e1a89941744609a1aefe9b71b8264f74e9174f5f
[moodle.git] / lib / dml / moodle_database.php
1 <?php
3 // This file is part of Moodle - http://moodle.org/
4 //
5 // Moodle is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // Moodle is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
19 /**
20  * Abstract database driver class.
21  *
22  * @package core
23  * @category dml
24  * @subpackage dml
25  * @copyright 2008 Petr Skoda (http://skodak.org)
26  * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
27  */
29 defined('MOODLE_INTERNAL') || die();
31 require_once($CFG->libdir.'/dml/database_column_info.php');
32 require_once($CFG->libdir.'/dml/moodle_recordset.php');
33 require_once($CFG->libdir.'/dml/moodle_transaction.php');
35 /// GLOBAL CONSTANTS /////////////////////////////////////////////////////////
37 /** SQL_PARAMS_NAMED - Bitmask, indicates :name type parameters are supported by db backend. */
38 define('SQL_PARAMS_NAMED', 1);
40 /** SQL_PARAMS_QM - Bitmask, indicates ? type parameters are supported by db backend. */
41 define('SQL_PARAMS_QM', 2);
43 /** SQL_PARAMS_DOLLAR - Bitmask, indicates $1, $2, ... type parameters are supported by db backend. */
44 define('SQL_PARAMS_DOLLAR', 4);
46 /** SQL_QUERY_SELECT - Normal select query, reading only. */
47 define('SQL_QUERY_SELECT', 1);
49 /** SQL_QUERY_INSERT - Insert select query, writing. */
50 define('SQL_QUERY_INSERT', 2);
52 /** SQL_QUERY_UPDATE - Update select query, writing. */
53 define('SQL_QUERY_UPDATE', 3);
55 /** SQL_QUERY_STRUCTURE - Query changing db structure, writing. */
56 define('SQL_QUERY_STRUCTURE', 4);
58 /** SQL_QUERY_AUX - Auxiliary query done by driver, setting connection config, getting table info, etc. */
59 define('SQL_QUERY_AUX', 5);
61 /**
62  * Abstract class representing moodle database interface.
63  * @See http://docs.moodle.org/dev/DML_functions
64  *
65  * @package    core
66  * @category   dml
67  * @copyright  2008 Petr Skoda (http://skodak.org)
68  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
69  */
70 abstract class moodle_database {
72     /** @var database_manager db manager which allows db structure modifications. */
73     protected $database_manager;
74     /** @var moodle_temptables temptables manager to provide cross-db support for temp tables. */
75     protected $temptables;
76     /** @var array Cache of column info. */
77     protected $columns = array(); // I wish we had a shared memory cache for this :-(
78     /** @var array Cache of table info. */
79     protected $tables  = null;
81     // db connection options
82     /** @var string db host name. */
83     protected $dbhost;
84     /** @var string db host user. */
85     protected $dbuser;
86     /** @var string db host password. */
87     protected $dbpass;
88     /** @var string db name. */
89     protected $dbname;
90     /** @var string Prefix added to table names. */
91     protected $prefix;
93     /** @var array Database or driver specific options, such as sockets or TCPIP db connections. */
94     protected $dboptions;
96     /** @var bool True means non-moodle external database used.*/
97     protected $external;
99     /** @var int The database reads (performance counter).*/
100     protected $reads = 0;
101     /** @var int The database writes (performance counter).*/
102     protected $writes = 0;
104     /** @var int Debug level. */
105     protected $debug  = 0;
107     /** @var string Last used query sql. */
108     protected $last_sql;
109     /** @var array Last query parameters. */
110     protected $last_params;
111     /** @var int Last query type. */
112     protected $last_type;
113     /** @var string Last extra info. */
114     protected $last_extrainfo;
115     /** @var float Last time in seconds with millisecond precision. */
116     protected $last_time;
117     /** @var bool Flag indicating logging of query in progress. This helps prevent infinite loops. */
118     private $loggingquery = false;
120     /** @var bool True if the db is used for db sessions. */
121     protected $used_for_db_sessions = false;
123     /** @var array Array containing open transactions. */
124     private $transactions = array();
125     /** @var bool Flag used to force rollback of all current transactions. */
126     private $force_rollback = false;
128     /**
129      * @var int internal temporary variable used to fix params.
130      * @see _fix_sql_params_dollar_callback()
131      */
132     private $fix_sql_params_i;
133     /**
134      * @var int internal temporary variable used to guarantee unique parameters in each request.
135      * @see get_in_or_equal()
136      */
137     private $inorequaluniqueindex = 1;
139     /**
140      * Constructor - Instantiates the database, specifying if it's external (connect to other systems) or not (Moodle DB).
141      *              Note that this affects the decision of whether prefix checks must be performed or not.
142      * @param bool $external True means that an external database is used.
143      */
144     public function __construct($external=false) {
145         $this->external  = $external;
146     }
148     /**
149      * Destructor - cleans up and flushes everything needed.
150      */
151     public function __destruct() {
152         $this->dispose();
153     }
155     /**
156      * Detects if all needed PHP stuff are installed for DB connectivity.
157      * Note: can be used before connect()
158      * @return mixed True if requirements are met, otherwise a string if something isn't installed.
159      */
160     public abstract function driver_installed();
162     /**
163      * Returns database table prefix
164      * Note: can be used before connect()
165      * @return string The prefix used in the database.
166      */
167     public function get_prefix() {
168         return $this->prefix;
169     }
171     /**
172      * Loads and returns a database instance with the specified type and library.
173      *
174      * The loaded class is within lib/dml directory and of the form: $type.'_'.$library.'_moodle_database'
175      *
176      * @param string $type Database driver's type. (eg: mysqli, pgsql, mssql, sqldrv, oci, etc.)
177      * @param string $library Database driver's library (native, pdo, etc.)
178      * @param bool $external True if this is an external database.
179      * @return moodle_database driver object or null if error. for example @see mysqli_native_moodle_databases
180      */
181     public static function get_driver_instance($type, $library, $external = false) {
182         global $CFG;
184         $classname = $type.'_'.$library.'_moodle_database';
185         $libfile   = "$CFG->libdir/dml/$classname.php";
187         if (!file_exists($libfile)) {
188             return null;
189         }
191         require_once($libfile);
192         return new $classname($external);
193     }
195     /**
196      * Returns the database family type. (This sort of describes the SQL 'dialect')
197      * Note: can be used before connect()
198      * @return string The db family name (mysql, postgres, mssql, oracle, etc.)
199      */
200     public abstract function get_dbfamily();
202     /**
203      * Returns a more specific database driver type
204      * Note: can be used before connect()
205      * @return string The db type mysqli, pgsql, oci, mssql, sqlsrv
206      */
207     protected abstract function get_dbtype();
209     /**
210      * Returns the general database library name
211      * Note: can be used before connect()
212      * @return string The db library type -  pdo, native etc.
213      */
214     protected abstract function get_dblibrary();
216     /**
217      * Returns the localised database type name
218      * Note: can be used before connect()
219      * @return string
220      */
221     public abstract function get_name();
223     /**
224      * Returns the localised database configuration help.
225      * Note: can be used before connect()
226      * @return string
227      */
228     public abstract function get_configuration_help();
230     /**
231      * Returns the localised database description
232      * Note: can be used before connect()
233      * @return string
234      */
235     public abstract function get_configuration_hints();
237     /**
238      * Returns the db related part of config.php
239      * @return stdClass
240      */
241     public function export_dbconfig() {
242         $cfg = new stdClass();
243         $cfg->dbtype    = $this->get_dbtype();
244         $cfg->dblibrary = $this->get_dblibrary();
245         $cfg->dbhost    = $this->dbhost;
246         $cfg->dbname    = $this->dbname;
247         $cfg->dbuser    = $this->dbuser;
248         $cfg->dbpass    = $this->dbpass;
249         $cfg->prefix    = $this->prefix;
250         if ($this->dboptions) {
251             $cfg->dboptions = $this->dboptions;
252         }
254         return $cfg;
255     }
257     /**
258      * Diagnose database and tables, this function is used
259      * to verify database and driver settings, db engine types, etc.
260      *
261      * @return string null means everything ok, string means problem found.
262      */
263     public function diagnose() {
264         return null;
265     }
267     /**
268      * Connects to the database.
269      * Must be called before other methods.
270      * @param string $dbhost The database host.
271      * @param string $dbuser The database user to connect as.
272      * @param string $dbpass The password to use when connecting to the database.
273      * @param string $dbname The name of the database being connected to.
274      * @param mixed $prefix string means moodle db prefix, false used for external databases where prefix not used
275      * @param array $dboptions driver specific options
276      * @return bool true
277      * @throws dml_connection_exception if error
278      */
279     public abstract function connect($dbhost, $dbuser, $dbpass, $dbname, $prefix, array $dboptions=null);
281     /**
282      * Store various database settings
283      * @param string $dbhost The database host.
284      * @param string $dbuser The database user to connect as.
285      * @param string $dbpass The password to use when connecting to the database.
286      * @param string $dbname The name of the database being connected to.
287      * @param mixed $prefix string means moodle db prefix, false used for external databases where prefix not used
288      * @param array $dboptions driver specific options
289      * @return void
290      */
291     protected function store_settings($dbhost, $dbuser, $dbpass, $dbname, $prefix, array $dboptions=null) {
292         $this->dbhost    = $dbhost;
293         $this->dbuser    = $dbuser;
294         $this->dbpass    = $dbpass;
295         $this->dbname    = $dbname;
296         $this->prefix    = $prefix;
297         $this->dboptions = (array)$dboptions;
298     }
300     /**
301      * Attempt to create the database
302      * @param string $dbhost The database host.
303      * @param string $dbuser The database user to connect as.
304      * @param string $dbpass The password to use when connecting to the database.
305      * @param string $dbname The name of the database being connected to.
306      * @param array $dboptions An array of optional database options (eg: dbport)
307      *
308      * @return bool success True for successful connection. False otherwise.
309      */
310     public function create_database($dbhost, $dbuser, $dbpass, $dbname, array $dboptions=null) {
311         return false;
312     }
314     /**
315      * Closes the database connection and releases all resources
316      * and memory (especially circular memory references).
317      * Do NOT use connect() again, create a new instance if needed.
318      * @return void
319      */
320     public function dispose() {
321         if ($this->transactions) {
322             // this should not happen, it usually indicates wrong catching of exceptions,
323             // because all transactions should be finished manually or in default exception handler.
324             // unfortunately we can not access global $CFG any more and can not print debug,
325             // the diagnostic info should be printed in footer instead
326             $lowesttransaction = end($this->transactions);
327             $backtrace = $lowesttransaction->get_backtrace();
329             error_log('Potential coding error - active database transaction detected when disposing database:'."\n".format_backtrace($backtrace, true));
330             $this->force_transaction_rollback();
331         }
332         if ($this->used_for_db_sessions) {
333             // this is needed because we need to save session to db before closing it
334             session_get_instance()->write_close();
335             $this->used_for_db_sessions = false;
336         }
337         if ($this->temptables) {
338             $this->temptables->dispose();
339             $this->temptables = null;
340         }
341         if ($this->database_manager) {
342             $this->database_manager->dispose();
343             $this->database_manager = null;
344         }
345         $this->columns = array();
346         $this->tables  = null;
347     }
349     /**
350      * This should be called before each db query.
351      * @param string $sql The query string.
352      * @param array $params An array of parameters.
353      * @param int $type The type of query. ( SQL_QUERY_SELECT | SQL_QUERY_AUX | SQL_QUERY_INSERT | SQL_QUERY_UPDATE | SQL_QUERY_STRUCTURE )
354      * @param mixed $extrainfo This is here for any driver specific extra information.
355      * @return void
356      */
357     protected function query_start($sql, array $params=null, $type, $extrainfo=null) {
358         if ($this->loggingquery) {
359             return;
360         }
361         $this->last_sql       = $sql;
362         $this->last_params    = $params;
363         $this->last_type      = $type;
364         $this->last_extrainfo = $extrainfo;
365         $this->last_time      = microtime(true);
367         switch ($type) {
368             case SQL_QUERY_SELECT:
369             case SQL_QUERY_AUX:
370                 $this->reads++;
371                 break;
372             case SQL_QUERY_INSERT:
373             case SQL_QUERY_UPDATE:
374             case SQL_QUERY_STRUCTURE:
375                 $this->writes++;
376         }
378         $this->print_debug($sql, $params);
379     }
381     /**
382      * This should be called immediately after each db query. It does a clean up of resources.
383      * It also throws exceptions if the sql that ran produced errors.
384      * @param mixed $result The db specific result obtained from running a query.
385      * @throws dml_read_exception | dml_write_exception | ddl_change_structure_exception
386      * @return void
387      */
388     protected function query_end($result) {
389         if ($this->loggingquery) {
390             return;
391         }
392         if ($result !== false) {
393             $this->query_log();
394             // free memory
395             $this->last_sql    = null;
396             $this->last_params = null;
397             return;
398         }
400         // remember current info, log queries may alter it
401         $type   = $this->last_type;
402         $sql    = $this->last_sql;
403         $params = $this->last_params;
404         $time   = microtime(true) - $this->last_time;
405         $error  = $this->get_last_error();
407         $this->query_log($error);
409         switch ($type) {
410             case SQL_QUERY_SELECT:
411             case SQL_QUERY_AUX:
412                 throw new dml_read_exception($error, $sql, $params);
413             case SQL_QUERY_INSERT:
414             case SQL_QUERY_UPDATE:
415                 throw new dml_write_exception($error, $sql, $params);
416             case SQL_QUERY_STRUCTURE:
417                 $this->get_manager(); // includes ddl exceptions classes ;-)
418                 throw new ddl_change_structure_exception($error, $sql);
419         }
420     }
422     /**
423      * This logs the last query based on 'logall', 'logslow' and 'logerrors' options configured via $CFG->dboptions .
424      * @param mixed string error or false if not error
425      * @return void
426      */
427     public function query_log($error=false) {
428         $logall    = !empty($this->dboptions['logall']);
429         $logslow   = !empty($this->dboptions['logslow']) ? $this->dboptions['logslow'] : false;
430         $logerrors = !empty($this->dboptions['logerrors']);
431         $iserror   = ($error !== false);
433         $time = microtime(true) - $this->last_time;
435         if ($logall or ($logslow and ($logslow < ($time+0.00001))) or ($iserror and $logerrors)) {
436             $this->loggingquery = true;
437             try {
438                 $backtrace = debug_backtrace();
439                 if ($backtrace) {
440                     //remove query_log()
441                     array_shift($backtrace);
442                 }
443                 if ($backtrace) {
444                     //remove query_end()
445                     array_shift($backtrace);
446                 }
447                 $log = new stdClass();
448                 $log->qtype      = $this->last_type;
449                 $log->sqltext    = $this->last_sql;
450                 $log->sqlparams  = var_export((array)$this->last_params, true);
451                 $log->error      = (int)$iserror;
452                 $log->info       = $iserror ? $error : null;
453                 $log->backtrace  = format_backtrace($backtrace, true);
454                 $log->exectime   = $time;
455                 $log->timelogged = time();
456                 $this->insert_record('log_queries', $log);
457             } catch (Exception $ignored) {
458             }
459             $this->loggingquery = false;
460         }
461     }
463     /**
464      * Returns database server info array
465      * @return array Array containing 'description' and 'version' atleast.
466      */
467     public abstract function get_server_info();
469     /**
470      * Returns supported query parameter types
471      * @return int bitmask of accepted SQL_PARAMS_*
472      */
473     protected abstract function allowed_param_types();
475     /**
476      * Returns the last error reported by the database engine.
477      * @return string The error message.
478      */
479     public abstract function get_last_error();
481     /**
482      * Prints sql debug info
483      * @param string $sql The query which is being debugged.
484      * @param array $params The query parameters. (optional)
485      * @param mixed $obj The library specific object. (optional)
486      * @return void
487      */
488     protected function print_debug($sql, array $params=null, $obj=null) {
489         if (!$this->get_debug()) {
490             return;
491         }
492         if (CLI_SCRIPT) {
493             echo "--------------------------------\n";
494             echo $sql."\n";
495             if (!is_null($params)) {
496                 echo "[".var_export($params, true)."]\n";
497             }
498             echo "--------------------------------\n";
499         } else {
500             echo "<hr />\n";
501             echo s($sql)."\n";
502             if (!is_null($params)) {
503                 echo "[".s(var_export($params, true))."]\n";
504             }
505             echo "<hr />\n";
506         }
507     }
509     /**
510      * Returns the SQL WHERE conditions.
511      * @param string $table The table name that these conditions will be validated against.
512      * @param array $conditions The conditions to build the where clause. (must not contain numeric indexes)
513      * @throws dml_exception
514      * @return array An array list containing sql 'where' part and 'params'.
515      */
516     protected function where_clause($table, array $conditions=null) {
517         // We accept nulls in conditions
518         $conditions = is_null($conditions) ? array() : $conditions;
519         // Some checks performed under debugging only
520         if (debugging()) {
521             $columns = $this->get_columns($table);
522             if (empty($columns)) {
523                 // no supported columns means most probably table does not exist
524                 throw new dml_exception('ddltablenotexist', $table);
525             }
526             foreach ($conditions as $key=>$value) {
527                 if (!isset($columns[$key])) {
528                     $a = new stdClass();
529                     $a->fieldname = $key;
530                     $a->tablename = $table;
531                     throw new dml_exception('ddlfieldnotexist', $a);
532                 }
533                 $column = $columns[$key];
534                 if ($column->meta_type == 'X') {
535                     //ok so the column is a text column. sorry no text columns in the where clause conditions
536                     throw new dml_exception('textconditionsnotallowed', $conditions);
537                 }
538             }
539         }
541         $allowed_types = $this->allowed_param_types();
542         if (empty($conditions)) {
543             return array('', array());
544         }
545         $where = array();
546         $params = array();
548         foreach ($conditions as $key=>$value) {
549             if (is_int($key)) {
550                 throw new dml_exception('invalidnumkey');
551             }
552             if (is_null($value)) {
553                 $where[] = "$key IS NULL";
554             } else {
555                 if ($allowed_types & SQL_PARAMS_NAMED) {
556                     // Need to verify key names because they can contain, originally,
557                     // spaces and other forbidden chars when using sql_xxx() functions and friends.
558                     $normkey = trim(preg_replace('/[^a-zA-Z0-9_-]/', '_', $key), '-_');
559                     if ($normkey !== $key) {
560                         debugging('Invalid key found in the conditions array.');
561                     }
562                     $where[] = "$key = :$normkey";
563                     $params[$normkey] = $value;
564                 } else {
565                     $where[] = "$key = ?";
566                     $params[] = $value;
567                 }
568             }
569         }
570         $where = implode(" AND ", $where);
571         return array($where, $params);
572     }
574     /**
575      * Returns SQL WHERE conditions for the ..._list group of methods.
576      *
577      * @param string $field the name of a field.
578      * @param array $values the values field might take.
579      * @return array An array containing sql 'where' part and 'params'
580      */
581     protected function where_clause_list($field, array $values) {
582         $params = array();
583         $select = array();
584         $values = (array)$values;
585         foreach ($values as $value) {
586             if (is_bool($value)) {
587                 $value = (int)$value;
588             }
589             if (is_null($value)) {
590                 $select[] = "$field IS NULL";
591             } else {
592                 $select[] = "$field = ?";
593                 $params[] = $value;
594             }
595         }
596         $select = implode(" OR ", $select);
597         return array($select, $params);
598     }
600     /**
601      * Constructs 'IN()' or '=' sql fragment
602      * @param mixed $items A single value or array of values for the expression.
603      * @param int $type Parameter bounding type : SQL_PARAMS_QM or SQL_PARAMS_NAMED.
604      * @param string $prefix Named parameter placeholder prefix (a unique counter value is appended to each parameter name).
605      * @param bool $equal True means we want to equate to the constructed expression, false means we don't want to equate to it.
606      * @param mixed $onemptyitems This defines the behavior when the array of items provided is empty. Defaults to false,
607      *              meaning throw exceptions. Other values will become part of the returned SQL fragment.
608      * @throws coding_exception | dml_exception
609      * @return array A list containing the constructed sql fragment and an array of parameters.
610      */
611     public function get_in_or_equal($items, $type=SQL_PARAMS_QM, $prefix='param', $equal=true, $onemptyitems=false) {
613         // default behavior, throw exception on empty array
614         if (is_array($items) and empty($items) and $onemptyitems === false) {
615             throw new coding_exception('moodle_database::get_in_or_equal() does not accept empty arrays');
616         }
617         // handle $onemptyitems on empty array of items
618         if (is_array($items) and empty($items)) {
619             if (is_null($onemptyitems)) {             // Special case, NULL value
620                 $sql = $equal ? ' IS NULL' : ' IS NOT NULL';
621                 return (array($sql, array()));
622             } else {
623                 $items = array($onemptyitems);        // Rest of cases, prepare $items for std processing
624             }
625         }
627         if ($type == SQL_PARAMS_QM) {
628             if (!is_array($items) or count($items) == 1) {
629                 $sql = $equal ? '= ?' : '<> ?';
630                 $items = (array)$items;
631                 $params = array_values($items);
632             } else {
633                 if ($equal) {
634                     $sql = 'IN ('.implode(',', array_fill(0, count($items), '?')).')';
635                 } else {
636                     $sql = 'NOT IN ('.implode(',', array_fill(0, count($items), '?')).')';
637                 }
638                 $params = array_values($items);
639             }
641         } else if ($type == SQL_PARAMS_NAMED) {
642             if (empty($prefix)) {
643                 $prefix = 'param';
644             }
646             if (!is_array($items)){
647                 $param = $prefix.$this->inorequaluniqueindex++;
648                 $sql = $equal ? "= :$param" : "<> :$param";
649                 $params = array($param=>$items);
650             } else if (count($items) == 1) {
651                 $param = $prefix.$this->inorequaluniqueindex++;
652                 $sql = $equal ? "= :$param" : "<> :$param";
653                 $item = reset($items);
654                 $params = array($param=>$item);
655             } else {
656                 $params = array();
657                 $sql = array();
658                 foreach ($items as $item) {
659                     $param = $prefix.$this->inorequaluniqueindex++;
660                     $params[$param] = $item;
661                     $sql[] = ':'.$param;
662                 }
663                 if ($equal) {
664                     $sql = 'IN ('.implode(',', $sql).')';
665                 } else {
666                     $sql = 'NOT IN ('.implode(',', $sql).')';
667                 }
668             }
670         } else {
671             throw new dml_exception('typenotimplement');
672         }
673         return array($sql, $params);
674     }
676     /**
677      * Converts short table name {tablename} to the real prefixed table name in given sql.
678      * @param string $sql The sql to be operated on.
679      * @return string The sql with tablenames being prefixed with $CFG->prefix
680      */
681     protected function fix_table_names($sql) {
682         return preg_replace('/\{([a-z][a-z0-9_]*)\}/', $this->prefix.'$1', $sql);
683     }
685     /**
686      * Internal private utitlity function used to fix parameters.
687      * Used with preg_replace_callback()
688      * @param array $match Refer to preg_replace_callback usage for description.
689      * @see preg_replace_callback()
690      */
691     private function _fix_sql_params_dollar_callback($match) {
692         $this->fix_sql_params_i++;
693         return "\$".$this->fix_sql_params_i;
694     }
696     /**
697      * Normalizes sql query parameters and verifies parameters.
698      * @param string $sql The query or part of it.
699      * @param array $params The query parameters.
700      * @return array (sql, params, type of params)
701      */
702     public function fix_sql_params($sql, array $params=null) {
703         $params = (array)$params; // mke null array if needed
704         $allowed_types = $this->allowed_param_types();
706         // convert table names
707         $sql = $this->fix_table_names($sql);
709         // cast booleans to 1/0 int
710         foreach ($params as $key => $value) {
711             $params[$key] = is_bool($value) ? (int)$value : $value;
712         }
714         // NICOLAS C: Fixed regexp for negative backwards lookahead of double colons. Thanks for Sam Marshall's help
715         $named_count = preg_match_all('/(?<!:):[a-z][a-z0-9_]*/', $sql, $named_matches); // :: used in pgsql casts
716         $dollar_count = preg_match_all('/\$[1-9][0-9]*/', $sql, $dollar_matches);
717         $q_count     = substr_count($sql, '?');
719         $count = 0;
721         if ($named_count) {
722             $type = SQL_PARAMS_NAMED;
723             $count = $named_count;
725         }
726         if ($dollar_count) {
727             if ($count) {
728                 throw new dml_exception('mixedtypesqlparam');
729             }
730             $type = SQL_PARAMS_DOLLAR;
731             $count = $dollar_count;
733         }
734         if ($q_count) {
735             if ($count) {
736                 throw new dml_exception('mixedtypesqlparam');
737             }
738             $type = SQL_PARAMS_QM;
739             $count = $q_count;
741         }
743         if (!$count) {
744              // ignore params
745             if ($allowed_types & SQL_PARAMS_NAMED) {
746                 return array($sql, array(), SQL_PARAMS_NAMED);
747             } else if ($allowed_types & SQL_PARAMS_QM) {
748                 return array($sql, array(), SQL_PARAMS_QM);
749             } else {
750                 return array($sql, array(), SQL_PARAMS_DOLLAR);
751             }
752         }
754         if ($count > count($params)) {
755             $a = new stdClass;
756             $a->expected = $count;
757             $a->actual = count($params);
758             throw new dml_exception('invalidqueryparam', $a);
759         }
761         $target_type = $allowed_types;
763         if ($type & $allowed_types) { // bitwise AND
764             if ($count == count($params)) {
765                 if ($type == SQL_PARAMS_QM) {
766                     return array($sql, array_values($params), SQL_PARAMS_QM); // 0-based array required
767                 } else {
768                     //better do the validation of names below
769                 }
770             }
771             // needs some fixing or validation - there might be more params than needed
772             $target_type = $type;
773         }
775         if ($type == SQL_PARAMS_NAMED) {
776             $finalparams = array();
777             foreach ($named_matches[0] as $key) {
778                 $key = trim($key, ':');
779                 if (!array_key_exists($key, $params)) {
780                     throw new dml_exception('missingkeyinsql', $key, '');
781                 }
782                 if (strlen($key) > 30) {
783                     throw new coding_exception(
784                             "Placeholder names must be 30 characters or shorter. '" .
785                             $key . "' is too long.", $sql);
786                 }
787                 $finalparams[$key] = $params[$key];
788             }
789             if ($count != count($finalparams)) {
790                 throw new dml_exception('duplicateparaminsql');
791             }
793             if ($target_type & SQL_PARAMS_QM) {
794                 $sql = preg_replace('/(?<!:):[a-z][a-z0-9_]*/', '?', $sql);
795                 return array($sql, array_values($finalparams), SQL_PARAMS_QM); // 0-based required
796             } else if ($target_type & SQL_PARAMS_NAMED) {
797                 return array($sql, $finalparams, SQL_PARAMS_NAMED);
798             } else {  // $type & SQL_PARAMS_DOLLAR
799                 //lambda-style functions eat memory - we use globals instead :-(
800                 $this->fix_sql_params_i = 0;
801                 $sql = preg_replace_callback('/(?<!:):[a-z][a-z0-9_]*/', array($this, '_fix_sql_params_dollar_callback'), $sql);
802                 return array($sql, array_values($finalparams), SQL_PARAMS_DOLLAR); // 0-based required
803             }
805         } else if ($type == SQL_PARAMS_DOLLAR) {
806             if ($target_type & SQL_PARAMS_DOLLAR) {
807                 return array($sql, array_values($params), SQL_PARAMS_DOLLAR); // 0-based required
808             } else if ($target_type & SQL_PARAMS_QM) {
809                 $sql = preg_replace('/\$[0-9]+/', '?', $sql);
810                 return array($sql, array_values($params), SQL_PARAMS_QM); // 0-based required
811             } else { //$target_type & SQL_PARAMS_NAMED
812                 $sql = preg_replace('/\$([0-9]+)/', ':param\\1', $sql);
813                 $finalparams = array();
814                 foreach ($params as $key=>$param) {
815                     $key++;
816                     $finalparams['param'.$key] = $param;
817                 }
818                 return array($sql, $finalparams, SQL_PARAMS_NAMED);
819             }
821         } else { // $type == SQL_PARAMS_QM
822             if (count($params) != $count) {
823                 $params = array_slice($params, 0, $count);
824             }
826             if ($target_type & SQL_PARAMS_QM) {
827                 return array($sql, array_values($params), SQL_PARAMS_QM); // 0-based required
828             } else if ($target_type & SQL_PARAMS_NAMED) {
829                 $finalparams = array();
830                 $pname = 'param0';
831                 $parts = explode('?', $sql);
832                 $sql = array_shift($parts);
833                 foreach ($parts as $part) {
834                     $param = array_shift($params);
835                     $pname++;
836                     $sql .= ':'.$pname.$part;
837                     $finalparams[$pname] = $param;
838                 }
839                 return array($sql, $finalparams, SQL_PARAMS_NAMED);
840             } else {  // $type & SQL_PARAMS_DOLLAR
841                 //lambda-style functions eat memory - we use globals instead :-(
842                 $this->fix_sql_params_i = 0;
843                 $sql = preg_replace_callback('/\?/', array($this, '_fix_sql_params_dollar_callback'), $sql);
844                 return array($sql, array_values($params), SQL_PARAMS_DOLLAR); // 0-based required
845             }
846         }
847     }
849     /**
850      * Return tables in database WITHOUT current prefix.
851      * @param bool $usecache if true, returns list of cached tables.
852      * @return array of table names in lowercase and without prefix
853      */
854     public abstract function get_tables($usecache=true);
856     /**
857      * Return table indexes - everything lowercased.
858      * @param string $table The table we want to get indexes from.
859      * @return array An associative array of indexes containing 'unique' flag and 'columns' being indexed
860      */
861     public abstract function get_indexes($table);
863     /**
864      * Returns detailed information about columns in table. This information is cached internally.
865      * @param string $table The table's name.
866      * @param bool $usecache Flag to use internal cacheing. The default is true.
867      * @return array of database_column_info objects indexed with column names
868      */
869     public abstract function get_columns($table, $usecache=true);
871     /**
872      * Normalise values based on varying RDBMS's dependencies (booleans, LOBs...)
873      *
874      * @param database_column_info $column column metadata corresponding with the value we are going to normalise
875      * @param mixed $value value we are going to normalise
876      * @return mixed the normalised value
877      */
878     protected abstract function normalise_value($column, $value);
880     /**
881      * Resets the internal column details cache
882      * @return void
883      */
884     public function reset_caches() {
885         $this->columns = array();
886         $this->tables  = null;
887     }
889     /**
890      * Returns the sql generator used for db manipulation.
891      * Used mostly in upgrade.php scripts.
892      * @return database_manager The instance used to perform ddl operations. @see lib/ddl/database_manager.php
893      */
894     public function get_manager() {
895         global $CFG;
897         if (!$this->database_manager) {
898             require_once($CFG->libdir.'/ddllib.php');
900             $classname = $this->get_dbfamily().'_sql_generator';
901             require_once("$CFG->libdir/ddl/$classname.php");
902             $generator = new $classname($this, $this->temptables);
904             $this->database_manager = new database_manager($this, $generator);
905         }
906         return $this->database_manager;
907     }
909     /**
910      * Attempts to change db encoding to UTF-8 encoding if possible.
911      * @return bool True is successful.
912      */
913     public function change_db_encoding() {
914         return false;
915     }
917     /**
918      * Checks to see if the database is in unicode mode?
919      * @return bool
920      */
921     public function setup_is_unicodedb() {
922         return true;
923     }
925     /**
926      * Enable/disable very detailed debugging.
927      * @param bool $state
928      * @return void
929      */
930     public function set_debug($state) {
931         $this->debug = $state;
932     }
934     /**
935      * Returns debug status
936      * @return bool $state
937      */
938     public function get_debug() {
939         return $this->debug;
940     }
942     /**
943      * Enable/disable detailed sql logging
944      * @param bool $state
945      */
946     public function set_logging($state) {
947         // adodb sql logging shares one table without prefix per db - this is no longer acceptable :-(
948         // we must create one table shared by all drivers
949     }
951     /**
952      * Do NOT use in code, this is for use by database_manager only!
953      * @param string $sql query
954      * @return bool true
955      * @throws dml_exception A DML specific exception is thrown for any errors.
956      */
957     public abstract function change_database_structure($sql);
959     /**
960      * Executes a general sql query. Should be used only when no other method suitable.
961      * Do NOT use this to make changes in db structure, use database_manager::execute_sql() instead!
962      * @param string $sql query
963      * @param array $params query parameters
964      * @return bool true
965      * @throws dml_exception A DML specific exception is thrown for any errors.
966      */
967     public abstract function execute($sql, array $params=null);
969     /**
970      * Get a number of records as a moodle_recordset where all the given conditions met.
971      *
972      * Selects records from the table $table.
973      *
974      * If specified, only records meeting $conditions.
975      *
976      * If specified, the results will be sorted as specified by $sort. This
977      * is added to the SQL as "ORDER BY $sort". Example values of $sort
978      * might be "time ASC" or "time DESC".
979      *
980      * If $fields is specified, only those fields are returned.
981      *
982      * Since this method is a little less readable, use of it should be restricted to
983      * code where it's possible there might be large datasets being returned.  For known
984      * small datasets use get_records - it leads to simpler code.
985      *
986      * If you only want some of the records, specify $limitfrom and $limitnum.
987      * The query will skip the first $limitfrom records (according to the sort
988      * order) and then return the next $limitnum records. If either of $limitfrom
989      * or $limitnum is specified, both must be present.
990      *
991      * The return value is a moodle_recordset
992      * if the query succeeds. If an error occurs, false is returned.
993      *
994      * @param string $table the table to query.
995      * @param array $conditions optional array $fieldname=>requestedvalue with AND in between
996      * @param string $sort an order to sort the results in (optional, a valid SQL ORDER BY parameter).
997      * @param string $fields a comma separated list of fields to return (optional, by default all fields are returned).
998      * @param int $limitfrom return a subset of records, starting at this point (optional).
999      * @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set).
1000      * @return moodle_recordset A moodle_recordset instance
1001      * @throws dml_exception A DML specific exception is thrown for any errors.
1002      */
1003     public function get_recordset($table, array $conditions=null, $sort='', $fields='*', $limitfrom=0, $limitnum=0) {
1004         list($select, $params) = $this->where_clause($table, $conditions);
1005         return $this->get_recordset_select($table, $select, $params, $sort, $fields, $limitfrom, $limitnum);
1006     }
1008     /**
1009      * Get a number of records as a moodle_recordset where one field match one list of values.
1010      *
1011      * Only records where $field takes one of the values $values are returned.
1012      * $values must be an array of values.
1013      *
1014      * Other arguments and the return type as for @see function get_recordset.
1015      *
1016      * @param string $table the table to query.
1017      * @param string $field a field to check (optional).
1018      * @param array $values array of values the field must have
1019      * @param string $sort an order to sort the results in (optional, a valid SQL ORDER BY parameter).
1020      * @param string $fields a comma separated list of fields to return (optional, by default all fields are returned).
1021      * @param int $limitfrom return a subset of records, starting at this point (optional).
1022      * @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set).
1023      * @return moodle_recordset A moodle_recordset instance.
1024      * @throws dml_exception A DML specific exception is thrown for any errors.
1025      */
1026     public function get_recordset_list($table, $field, array $values, $sort='', $fields='*', $limitfrom=0, $limitnum=0) {
1027         list($select, $params) = $this->where_clause_list($field, $values);
1028         if (empty($select)) {
1029             $select = '1 = 2'; /// Fake condition, won't return rows ever. MDL-17645
1030             $params = array();
1031         }
1032         return $this->get_recordset_select($table, $select, $params, $sort, $fields, $limitfrom, $limitnum);
1033     }
1035     /**
1036      * Get a number of records as a moodle_recordset which match a particular WHERE clause.
1037      *
1038      * If given, $select is used as the SELECT parameter in the SQL query,
1039      * otherwise all records from the table are returned.
1040      *
1041      * Other arguments and the return type as for @see function get_recordset.
1042      *
1043      * @param string $table the table to query.
1044      * @param string $select A fragment of SQL to be used in a where clause in the SQL call.
1045      * @param array $params array of sql parameters
1046      * @param string $sort an order to sort the results in (optional, a valid SQL ORDER BY parameter).
1047      * @param string $fields a comma separated list of fields to return (optional, by default all fields are returned).
1048      * @param int $limitfrom return a subset of records, starting at this point (optional).
1049      * @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set).
1050      * @return moodle_recordset A moodle_recordset instance.
1051      * @throws dml_exception A DML specific exception is thrown for any errors.
1052      */
1053     public function get_recordset_select($table, $select, array $params=null, $sort='', $fields='*', $limitfrom=0, $limitnum=0) {
1054         $sql = "SELECT $fields FROM {".$table."}";
1055         if ($select) {
1056             $sql .= " WHERE $select";
1057         }
1058         if ($sort) {
1059             $sql .= " ORDER BY $sort";
1060         }
1061         return $this->get_recordset_sql($sql, $params, $limitfrom, $limitnum);
1062     }
1064     /**
1065      * Get a number of records as a moodle_recordset using a SQL statement.
1066      *
1067      * Since this method is a little less readable, use of it should be restricted to
1068      * code where it's possible there might be large datasets being returned.  For known
1069      * small datasets use get_records_sql - it leads to simpler code.
1070      *
1071      * The return type is as for @see function get_recordset.
1072      *
1073      * @param string $sql the SQL select query to execute.
1074      * @param array $params array of sql parameters
1075      * @param int $limitfrom return a subset of records, starting at this point (optional).
1076      * @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set).
1077      * @return moodle_recordset A moodle_recordset instance.
1078      * @throws dml_exception A DML specific exception is thrown for any errors.
1079      */
1080     public abstract function get_recordset_sql($sql, array $params=null, $limitfrom=0, $limitnum=0);
1082     /**
1083      * Get a number of records as an array of objects where all the given conditions met.
1084      *
1085      * If the query succeeds and returns at least one record, the
1086      * return value is an array of objects, one object for each
1087      * record found. The array key is the value from the first
1088      * column of the result set. The object associated with that key
1089      * has a member variable for each column of the results.
1090      *
1091      * @param string $table the table to query.
1092      * @param array $conditions optional array $fieldname=>requestedvalue with AND in between
1093      * @param string $sort an order to sort the results in (optional, a valid SQL ORDER BY parameter).
1094      * @param string $fields a comma separated list of fields to return (optional, by default
1095      *   all fields are returned). The first field will be used as key for the
1096      *   array so must be a unique field such as 'id'.
1097      * @param int $limitfrom return a subset of records, starting at this point (optional).
1098      * @param int $limitnum return a subset comprising this many records in total (optional, required if $limitfrom is set).
1099      * @return array An array of Objects indexed by first column.
1100      * @throws dml_exception A DML specific exception is thrown for any errors.
1101      */
1102     public function get_records($table, array $conditions=null, $sort='', $fields='*', $limitfrom=0, $limitnum=0) {
1103         list($select, $params) = $this->where_clause($table, $conditions);
1104         return $this->get_records_select($table, $select, $params, $sort, $fields, $limitfrom, $limitnum);
1105     }
1107     /**
1108      * Get a number of records as an array of objects where one field match one list of values.
1109      *
1110      * Return value as for @see function get_records.
1111      *
1112      * @param string $table The database table to be checked against.
1113      * @param string $field The field to search
1114      * @param array $values An array of values
1115      * @param string $sort Sort order (as valid SQL sort parameter)
1116      * @param string $fields A comma separated list of fields to be returned from the chosen table. If specified,
1117      *   the first field should be a unique one such as 'id' since it will be used as a key in the associative
1118      *   array.
1119      * @return array An array of objects indexed by first column
1120      * @throws dml_exception A DML specific exception is thrown for any errors.
1121      */
1122     public function get_records_list($table, $field, array $values, $sort='', $fields='*', $limitfrom=0, $limitnum=0) {
1123         list($select, $params) = $this->where_clause_list($field, $values);
1124         if (empty($select)) {
1125             // nothing to return
1126             return array();
1127         }
1128         return $this->get_records_select($table, $select, $params, $sort, $fields, $limitfrom, $limitnum);
1129     }
1131     /**
1132      * Get a number of records as an array of objects which match a particular WHERE clause.
1133      *
1134      * Return value as for @see function get_records.
1135      *
1136      * @param string $table The table to query.
1137      * @param string $select A fragment of SQL to be used in a where clause in the SQL call.
1138      * @param array $params An array of sql parameters
1139      * @param string $sort An order to sort the results in (optional, a valid SQL ORDER BY parameter).
1140      * @param string $fields A comma separated list of fields to return
1141      *   (optional, by default all fields are returned). The first field will be used as key for the
1142      *   array so must be a unique field such as 'id'.
1143      * @param int $limitfrom return a subset of records, starting at this point (optional).
1144      * @param int $limitnum return a subset comprising this many records in total (optional, required if $limitfrom is set).
1145      * @return array of objects indexed by first column
1146      * @throws dml_exception A DML specific exception is thrown for any errors.
1147      */
1148     public function get_records_select($table, $select, array $params=null, $sort='', $fields='*', $limitfrom=0, $limitnum=0) {
1149         if ($select) {
1150             $select = "WHERE $select";
1151         }
1152         if ($sort) {
1153             $sort = " ORDER BY $sort";
1154         }
1155         return $this->get_records_sql("SELECT $fields FROM {" . $table . "} $select $sort", $params, $limitfrom, $limitnum);
1156     }
1158     /**
1159      * Get a number of records as an array of objects using a SQL statement.
1160      *
1161      * Return value as for @see function get_records.
1162      *
1163      * @param string $sql the SQL select query to execute. The first column of this SELECT statement
1164      *   must be a unique value (usually the 'id' field), as it will be used as the key of the
1165      *   returned array.
1166      * @param array $params array of sql parameters
1167      * @param int $limitfrom return a subset of records, starting at this point (optional).
1168      * @param int $limitnum return a subset comprising this many records in total (optional, required if $limitfrom is set).
1169      * @return array of objects indexed by first column
1170      * @throws dml_exception A DML specific exception is thrown for any errors.
1171      */
1172     public abstract function get_records_sql($sql, array $params=null, $limitfrom=0, $limitnum=0);
1174     /**
1175      * Get the first two columns from a number of records as an associative array where all the given conditions met.
1176      *
1177      * Arguments as for @see function get_recordset.
1178      *
1179      * If no errors occur the return value
1180      * is an associative whose keys come from the first field of each record,
1181      * and whose values are the corresponding second fields.
1182      * False is returned if an error occurs.
1183      *
1184      * @param string $table the table to query.
1185      * @param array $conditions optional array $fieldname=>requestedvalue with AND in between
1186      * @param string $sort an order to sort the results in (optional, a valid SQL ORDER BY parameter).
1187      * @param string $fields a comma separated list of fields to return - the number of fields should be 2!
1188      * @param int $limitfrom return a subset of records, starting at this point (optional).
1189      * @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set).
1190      * @return array an associative array
1191      * @throws dml_exception A DML specific exception is thrown for any errors.
1192      */
1193     public function get_records_menu($table, array $conditions=null, $sort='', $fields='*', $limitfrom=0, $limitnum=0) {
1194         $menu = array();
1195         if ($records = $this->get_records($table, $conditions, $sort, $fields, $limitfrom, $limitnum)) {
1196             foreach ($records as $record) {
1197                 $record = (array)$record;
1198                 $key   = array_shift($record);
1199                 $value = array_shift($record);
1200                 $menu[$key] = $value;
1201             }
1202         }
1203         return $menu;
1204     }
1206     /**
1207      * Get the first two columns from a number of records as an associative array which match a particular WHERE clause.
1208      *
1209      * Arguments as for @see function get_recordset_select.
1210      * Return value as for @see function get_records_menu.
1211      *
1212      * @param string $table The database table to be checked against.
1213      * @param string $select A fragment of SQL to be used in a where clause in the SQL call.
1214      * @param array $params array of sql parameters
1215      * @param string $sort Sort order (optional) - a valid SQL order parameter
1216      * @param string $fields A comma separated list of fields to be returned from the chosen table - the number of fields should be 2!
1217      * @param int $limitfrom return a subset of records, starting at this point (optional).
1218      * @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set).
1219      * @return array an associative array
1220      * @throws dml_exception A DML specific exception is thrown for any errors.
1221      */
1222     public function get_records_select_menu($table, $select, array $params=null, $sort='', $fields='*', $limitfrom=0, $limitnum=0) {
1223         $menu = array();
1224         if ($records = $this->get_records_select($table, $select, $params, $sort, $fields, $limitfrom, $limitnum)) {
1225             foreach ($records as $record) {
1226                 $record = (array)$record;
1227                 $key   = array_shift($record);
1228                 $value = array_shift($record);
1229                 $menu[$key] = $value;
1230             }
1231         }
1232         return $menu;
1233     }
1235     /**
1236      * Get the first two columns from a number of records as an associative array using a SQL statement.
1237      *
1238      * Arguments as for @see function get_recordset_sql.
1239      * Return value as for @see function get_records_menu.
1240      *
1241      * @param string $sql The SQL string you wish to be executed.
1242      * @param array $params array of sql parameters
1243      * @param int $limitfrom return a subset of records, starting at this point (optional).
1244      * @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set).
1245      * @return array an associative array
1246      * @throws dml_exception A DML specific exception is thrown for any errors.
1247      */
1248     public function get_records_sql_menu($sql, array $params=null, $limitfrom=0, $limitnum=0) {
1249         $menu = array();
1250         if ($records = $this->get_records_sql($sql, $params, $limitfrom, $limitnum)) {
1251             foreach ($records as $record) {
1252                 $record = (array)$record;
1253                 $key   = array_shift($record);
1254                 $value = array_shift($record);
1255                 $menu[$key] = $value;
1256             }
1257         }
1258         return $menu;
1259     }
1261     /**
1262      * Get a single database record as an object where all the given conditions met.
1263      *
1264      * @param string $table The table to select from.
1265      * @param array $conditions optional array $fieldname=>requestedvalue with AND in between
1266      * @param string $fields A comma separated list of fields to be returned from the chosen table.
1267      * @param int $strictness IGNORE_MISSING means compatible mode, false returned if record not found, debug message if more found;
1268      *                        IGNORE_MULTIPLE means return first, ignore multiple records found(not recommended);
1269      *                        MUST_EXIST means we will throw an exception if no record or multiple records found.
1270      *
1271      * @todo MDL-30407 MUST_EXIST option should not throw a dml_exception, it should throw a different exception as it's a requested check.
1272      * @return mixed a fieldset object containing the first matching record, false or exception if error not found depending on mode
1273      * @throws dml_exception A DML specific exception is thrown for any errors.
1274      */
1275     public function get_record($table, array $conditions, $fields='*', $strictness=IGNORE_MISSING) {
1276         list($select, $params) = $this->where_clause($table, $conditions);
1277         return $this->get_record_select($table, $select, $params, $fields, $strictness);
1278     }
1280     /**
1281      * Get a single database record as an object which match a particular WHERE clause.
1282      *
1283      * @param string $table The database table to be checked against.
1284      * @param string $select A fragment of SQL to be used in a where clause in the SQL call.
1285      * @param array $params array of sql parameters
1286      * @param int $strictness IGNORE_MISSING means compatible mode, false returned if record not found, debug message if more found;
1287      *                        IGNORE_MULTIPLE means return first, ignore multiple records found(not recommended);
1288      *                        MUST_EXIST means throw exception if no record or multiple records found
1289      * @return mixed a fieldset object containing the first matching record, false or exception if error not found depending on mode
1290      * @throws dml_exception A DML specific exception is thrown for any errors.
1291      */
1292     public function get_record_select($table, $select, array $params=null, $fields='*', $strictness=IGNORE_MISSING) {
1293         if ($select) {
1294             $select = "WHERE $select";
1295         }
1296         try {
1297             return $this->get_record_sql("SELECT $fields FROM {" . $table . "} $select", $params, $strictness);
1298         } catch (dml_missing_record_exception $e) {
1299             // create new exception which will contain correct table name
1300             throw new dml_missing_record_exception($table, $e->sql, $e->params);
1301         }
1302     }
1304     /**
1305      * Get a single database record as an object using a SQL statement.
1306      *
1307      * The SQL statement should normally only return one record.
1308      * It is recommended to use get_records_sql() if more matches possible!
1309      *
1310      * @param string $sql The SQL string you wish to be executed, should normally only return one record.
1311      * @param array $params array of sql parameters
1312      * @param int $strictness IGNORE_MISSING means compatible mode, false returned if record not found, debug message if more found;
1313      *                        IGNORE_MULTIPLE means return first, ignore multiple records found(not recommended);
1314      *                        MUST_EXIST means throw exception if no record or multiple records found
1315      * @return mixed a fieldset object containing the first matching record, false or exception if error not found depending on mode
1316      * @throws dml_exception A DML specific exception is thrown for any errors.
1317      */
1318     public function get_record_sql($sql, array $params=null, $strictness=IGNORE_MISSING) {
1319         $strictness = (int)$strictness; // we support true/false for BC reasons too
1320         if ($strictness == IGNORE_MULTIPLE) {
1321             $count = 1;
1322         } else {
1323             $count = 0;
1324         }
1325         if (!$records = $this->get_records_sql($sql, $params, 0, $count)) {
1326             // not found
1327             if ($strictness == MUST_EXIST) {
1328                 throw new dml_missing_record_exception('', $sql, $params);
1329             }
1330             return false;
1331         }
1333         if (count($records) > 1) {
1334             if ($strictness == MUST_EXIST) {
1335                 throw new dml_multiple_records_exception($sql, $params);
1336             }
1337             debugging('Error: mdb->get_record() found more than one record!');
1338         }
1340         $return = reset($records);
1341         return $return;
1342     }
1344     /**
1345      * Get a single field value from a table record where all the given conditions met.
1346      *
1347      * @param string $table the table to query.
1348      * @param string $return the field to return the value of.
1349      * @param array $conditions optional array $fieldname=>requestedvalue with AND in between
1350      * @param int $strictness IGNORE_MISSING means compatible mode, false returned if record not found, debug message if more found;
1351      *                        IGNORE_MULTIPLE means return first, ignore multiple records found(not recommended);
1352      *                        MUST_EXIST means throw exception if no record or multiple records found
1353      * @return mixed the specified value false if not found
1354      * @throws dml_exception A DML specific exception is thrown for any errors.
1355      */
1356     public function get_field($table, $return, array $conditions, $strictness=IGNORE_MISSING) {
1357         list($select, $params) = $this->where_clause($table, $conditions);
1358         return $this->get_field_select($table, $return, $select, $params, $strictness);
1359     }
1361     /**
1362      * Get a single field value from a table record which match a particular WHERE clause.
1363      *
1364      * @param string $table the table to query.
1365      * @param string $return the field to return the value of.
1366      * @param string $select A fragment of SQL to be used in a where clause returning one row with one column
1367      * @param array $params array of sql parameters
1368      * @param int $strictness IGNORE_MISSING means compatible mode, false returned if record not found, debug message if more found;
1369      *                        IGNORE_MULTIPLE means return first, ignore multiple records found(not recommended);
1370      *                        MUST_EXIST means throw exception if no record or multiple records found
1371      * @return mixed the specified value false if not found
1372      * @throws dml_exception A DML specific exception is thrown for any errors.
1373      */
1374     public function get_field_select($table, $return, $select, array $params=null, $strictness=IGNORE_MISSING) {
1375         if ($select) {
1376             $select = "WHERE $select";
1377         }
1378         try {
1379             return $this->get_field_sql("SELECT $return FROM {" . $table . "} $select", $params, $strictness);
1380         } catch (dml_missing_record_exception $e) {
1381             // create new exception which will contain correct table name
1382             throw new dml_missing_record_exception($table, $e->sql, $e->params);
1383         }
1384     }
1386     /**
1387      * Get a single field value (first field) using a SQL statement.
1388      *
1389      * @param string $table the table to query.
1390      * @param string $return the field to return the value of.
1391      * @param string $sql The SQL query returning one row with one column
1392      * @param array $params array of sql parameters
1393      * @param int $strictness IGNORE_MISSING means compatible mode, false returned if record not found, debug message if more found;
1394      *                        IGNORE_MULTIPLE means return first, ignore multiple records found(not recommended);
1395      *                        MUST_EXIST means throw exception if no record or multiple records found
1396      * @return mixed the specified value false if not found
1397      * @throws dml_exception A DML specific exception is thrown for any errors.
1398      */
1399     public function get_field_sql($sql, array $params=null, $strictness=IGNORE_MISSING) {
1400         if (!$record = $this->get_record_sql($sql, $params, $strictness)) {
1401             return false;
1402         }
1404         $record = (array)$record;
1405         return reset($record); // first column
1406     }
1408     /**
1409      * Selects records and return values of chosen field as an array which match a particular WHERE clause.
1410      *
1411      * @param string $table the table to query.
1412      * @param string $return the field we are intered in
1413      * @param string $select A fragment of SQL to be used in a where clause in the SQL call.
1414      * @param array $params array of sql parameters
1415      * @return array of values
1416      * @throws dml_exception A DML specific exception is thrown for any errors.
1417      */
1418     public function get_fieldset_select($table, $return, $select, array $params=null) {
1419         if ($select) {
1420             $select = "WHERE $select";
1421         }
1422         return $this->get_fieldset_sql("SELECT $return FROM {" . $table . "} $select", $params);
1423     }
1425     /**
1426      * Selects records and return values (first field) as an array using a SQL statement.
1427      *
1428      * @param string $sql The SQL query
1429      * @param array $params array of sql parameters
1430      * @return array of values
1431      * @throws dml_exception A DML specific exception is thrown for any errors.
1432      */
1433     public abstract function get_fieldset_sql($sql, array $params=null);
1435     /**
1436      * Insert new record into database, as fast as possible, no safety checks, lobs not supported.
1437      * @param string $table name
1438      * @param mixed $params data record as object or array
1439      * @param bool $returnid Returns id of inserted record.
1440      * @param bool $bulk true means repeated inserts expected
1441      * @param bool $customsequence true if 'id' included in $params, disables $returnid
1442      * @return bool|int true or new id
1443      * @throws dml_exception A DML specific exception is thrown for any errors.
1444      */
1445     public abstract function insert_record_raw($table, $params, $returnid=true, $bulk=false, $customsequence=false);
1447     /**
1448      * Insert a record into a table and return the "id" field if required.
1449      *
1450      * Some conversions and safety checks are carried out. Lobs are supported.
1451      * If the return ID isn't required, then this just reports success as true/false.
1452      * $data is an object containing needed data
1453      * @param string $table The database table to be inserted into
1454      * @param object $data A data object with values for one or more fields in the record
1455      * @param bool $returnid Should the id of the newly created record entry be returned? If this option is not requested then true/false is returned.
1456      * @return bool|int true or new id
1457      * @throws dml_exception A DML specific exception is thrown for any errors.
1458      */
1459     public abstract function insert_record($table, $dataobject, $returnid=true, $bulk=false);
1461     /**
1462      * Import a record into a table, id field is required.
1463      * Safety checks are NOT carried out. Lobs are supported.
1464      *
1465      * @param string $table name of database table to be inserted into
1466      * @param object $dataobject A data object with values for one or more fields in the record
1467      * @return bool true
1468      * @throws dml_exception A DML specific exception is thrown for any errors.
1469      */
1470     public abstract function import_record($table, $dataobject);
1472     /**
1473      * Update record in database, as fast as possible, no safety checks, lobs not supported.
1474      * @param string $table name
1475      * @param mixed $params data record as object or array
1476      * @param bool $bulk True means repeated updates expected.
1477      * @return bool true
1478      * @throws dml_exception A DML specific exception is thrown for any errors.
1479      */
1480     public abstract function update_record_raw($table, $params, $bulk=false);
1482     /**
1483      * Update a record in a table
1484      *
1485      * $dataobject is an object containing needed data
1486      * Relies on $dataobject having a variable "id" to
1487      * specify the record to update
1488      *
1489      * @param string $table The database table to be checked against.
1490      * @param object $dataobject An object with contents equal to fieldname=>fieldvalue. Must have an entry for 'id' to map to the table specified.
1491      * @param bool $bulk True means repeated updates expected.
1492      * @return bool true
1493      * @throws dml_exception A DML specific exception is thrown for any errors.
1494      */
1495     public abstract function update_record($table, $dataobject, $bulk=false);
1498     /**
1499      * Set a single field in every table record where all the given conditions met.
1500      *
1501      * @param string $table The database table to be checked against.
1502      * @param string $newfield the field to set.
1503      * @param string $newvalue the value to set the field to.
1504      * @param array $conditions optional array $fieldname=>requestedvalue with AND in between
1505      * @return bool true
1506      * @throws dml_exception A DML specific exception is thrown for any errors.
1507      */
1508     public function set_field($table, $newfield, $newvalue, array $conditions=null) {
1509         list($select, $params) = $this->where_clause($table, $conditions);
1510         return $this->set_field_select($table, $newfield, $newvalue, $select, $params);
1511     }
1513     /**
1514      * Set a single field in every table record which match a particular WHERE clause.
1515      *
1516      * @param string $table The database table to be checked against.
1517      * @param string $newfield the field to set.
1518      * @param string $newvalue the value to set the field to.
1519      * @param string $select A fragment of SQL to be used in a where clause in the SQL call.
1520      * @param array $params array of sql parameters
1521      * @return bool true
1522      * @throws dml_exception A DML specific exception is thrown for any errors.
1523      */
1524     public abstract function set_field_select($table, $newfield, $newvalue, $select, array $params=null);
1527     /**
1528      * Count the records in a table where all the given conditions met.
1529      *
1530      * @param string $table The table to query.
1531      * @param array $conditions optional array $fieldname=>requestedvalue with AND in between
1532      * @return int The count of records returned from the specified criteria.
1533      * @throws dml_exception A DML specific exception is thrown for any errors.
1534      */
1535     public function count_records($table, array $conditions=null) {
1536         list($select, $params) = $this->where_clause($table, $conditions);
1537         return $this->count_records_select($table, $select, $params);
1538     }
1540     /**
1541      * Count the records in a table which match a particular WHERE clause.
1542      *
1543      * @param string $table The database table to be checked against.
1544      * @param string $select A fragment of SQL to be used in a WHERE clause in the SQL call.
1545      * @param array $params array of sql parameters
1546      * @param string $countitem The count string to be used in the SQL call. Default is COUNT('x').
1547      * @return int The count of records returned from the specified criteria.
1548      * @throws dml_exception A DML specific exception is thrown for any errors.
1549      */
1550     public function count_records_select($table, $select, array $params=null, $countitem="COUNT('x')") {
1551         if ($select) {
1552             $select = "WHERE $select";
1553         }
1554         return $this->count_records_sql("SELECT $countitem FROM {" . $table . "} $select", $params);
1555     }
1557     /**
1558      * Get the result of a SQL SELECT COUNT(...) query.
1559      *
1560      * Given a query that counts rows, return that count. (In fact,
1561      * given any query, return the first field of the first record
1562      * returned. However, this method should only be used for the
1563      * intended purpose.) If an error occurs, 0 is returned.
1564      *
1565      * @param string $sql The SQL string you wish to be executed.
1566      * @param array $params array of sql parameters
1567      * @return int the count
1568      * @throws dml_exception A DML specific exception is thrown for any errors.
1569      */
1570     public function count_records_sql($sql, array $params=null) {
1571         if ($count = $this->get_field_sql($sql, $params)) {
1572             return $count;
1573         } else {
1574             return 0;
1575         }
1576     }
1578     /**
1579      * Test whether a record exists in a table where all the given conditions met.
1580      *
1581      * The record to test is specified by giving up to three fields that must
1582      * equal the corresponding values.
1583      *
1584      * @param string $table The table to check.
1585      * @param array $conditions optional array $fieldname=>requestedvalue with AND in between
1586      * @return bool true if a matching record exists, else false.
1587      * @throws dml_exception A DML specific exception is thrown for any errors.
1588      */
1589     public function record_exists($table, array $conditions) {
1590         list($select, $params) = $this->where_clause($table, $conditions);
1591         return $this->record_exists_select($table, $select, $params);
1592     }
1594     /**
1595      * Test whether any records exists in a table which match a particular WHERE clause.
1596      *
1597      * @param string $table The database table to be checked against.
1598      * @param string $select A fragment of SQL to be used in a WHERE clause in the SQL call.
1599      * @param array $params array of sql parameters
1600      * @return bool true if a matching record exists, else false.
1601      * @throws dml_exception A DML specific exception is thrown for any errors.
1602      */
1603     public function record_exists_select($table, $select, array $params=null) {
1604         if ($select) {
1605             $select = "WHERE $select";
1606         }
1607         return $this->record_exists_sql("SELECT 'x' FROM {" . $table . "} $select", $params);
1608     }
1610     /**
1611      * Test whether a SQL SELECT statement returns any records.
1612      *
1613      * This function returns true if the SQL statement executes
1614      * without any errors and returns at least one record.
1615      *
1616      * @param string $sql The SQL statement to execute.
1617      * @param array $params array of sql parameters
1618      * @return bool true if the SQL executes without errors and returns at least one record.
1619      * @throws dml_exception A DML specific exception is thrown for any errors.
1620      */
1621     public function record_exists_sql($sql, array $params=null) {
1622         $mrs = $this->get_recordset_sql($sql, $params, 0, 1);
1623         $return = $mrs->valid();
1624         $mrs->close();
1625         return $return;
1626     }
1628     /**
1629      * Delete the records from a table where all the given conditions met.
1630      * If conditions not specified, table is truncated.
1631      *
1632      * @param string $table the table to delete from.
1633      * @param array $conditions optional array $fieldname=>requestedvalue with AND in between
1634      * @return bool true.
1635      * @throws dml_exception A DML specific exception is thrown for any errors.
1636      */
1637     public function delete_records($table, array $conditions=null) {
1638         // truncate is drop/create (DDL), not transactional safe,
1639         // so we don't use the shortcut within them. MDL-29198
1640         if (is_null($conditions) && empty($this->transactions)) {
1641             return $this->execute("TRUNCATE TABLE {".$table."}");
1642         }
1643         list($select, $params) = $this->where_clause($table, $conditions);
1644         return $this->delete_records_select($table, $select, $params);
1645     }
1647     /**
1648      * Delete the records from a table where one field match one list of values.
1649      *
1650      * @param string $table the table to delete from.
1651      * @param string $field The field to search
1652      * @param array $values array of values
1653      * @return bool true.
1654      * @throws dml_exception A DML specific exception is thrown for any errors.
1655      */
1656     public function delete_records_list($table, $field, array $values) {
1657         list($select, $params) = $this->where_clause_list($field, $values);
1658         if (empty($select)) {
1659             // nothing to delete
1660             return true;
1661         }
1662         return $this->delete_records_select($table, $select, $params);
1663     }
1665     /**
1666      * Delete one or more records from a table which match a particular WHERE clause.
1667      *
1668      * @param string $table The database table to be checked against.
1669      * @param string $select A fragment of SQL to be used in a where clause in the SQL call (used to define the selection criteria).
1670      * @param array $params array of sql parameters
1671      * @return bool true.
1672      * @throws dml_exception A DML specific exception is thrown for any errors.
1673      */
1674     public abstract function delete_records_select($table, $select, array $params=null);
1678 /// sql constructs
1679     /**
1680      * Returns the FROM clause required by some DBs in all SELECT statements.
1681      *
1682      * To be used in queries not having FROM clause to provide cross_db
1683      * Most DBs don't need it, hence the default is ''
1684      * @return string
1685      */
1686     public function sql_null_from_clause() {
1687         return '';
1688     }
1690     /**
1691      * Returns the SQL text to be used in order to perform one bitwise AND operation
1692      * between 2 integers.
1693      *
1694      * NOTE: The SQL result is a number and can not be used directly in
1695      *       SQL condition, please compare it to some number to get a bool!!
1696      *
1697      * @param int $int1 First integer in the operation.
1698      * @param int $int2 Second integer in the operation.
1699      * @return string The piece of SQL code to be used in your statement.
1700      */
1701     public function sql_bitand($int1, $int2) {
1702         return '((' . $int1 . ') & (' . $int2 . '))';
1703     }
1705     /**
1706      * Returns the SQL text to be used in order to perform one bitwise NOT operation
1707      * with 1 integer.
1708      *
1709      * @param int $int1 The operand integer in the operation.
1710      * @return string The piece of SQL code to be used in your statement.
1711      */
1712     public function sql_bitnot($int1) {
1713         return '(~(' . $int1 . '))';
1714     }
1716     /**
1717      * Returns the SQL text to be used in order to perform one bitwise OR operation
1718      * between 2 integers.
1719      *
1720      * NOTE: The SQL result is a number and can not be used directly in
1721      *       SQL condition, please compare it to some number to get a bool!!
1722      *
1723      * @param int $int1 The first operand integer in the operation.
1724      * @param int $int2 The second operand integer in the operation.
1725      * @return string The piece of SQL code to be used in your statement.
1726      */
1727     public function sql_bitor($int1, $int2) {
1728         return '((' . $int1 . ') | (' . $int2 . '))';
1729     }
1731     /**
1732      * Returns the SQL text to be used in order to perform one bitwise XOR operation
1733      * between 2 integers.
1734      *
1735      * NOTE: The SQL result is a number and can not be used directly in
1736      *       SQL condition, please compare it to some number to get a bool!!
1737      *
1738      * @param int $int1 The first operand integer in the operation.
1739      * @param int $int2 The second operand integer in the operation.
1740      * @return string The piece of SQL code to be used in your statement.
1741      */
1742     public function sql_bitxor($int1, $int2) {
1743         return '((' . $int1 . ') ^ (' . $int2 . '))';
1744     }
1746     /**
1747      * Returns the SQL text to be used in order to perform module '%'
1748      * operation - remainder after division
1749      *
1750      * @param int $int1 The first operand integer in the operation.
1751      * @param int $int2 The second operand integer in the operation.
1752      * @return string The piece of SQL code to be used in your statement.
1753      */
1754     public function sql_modulo($int1, $int2) {
1755         return '((' . $int1 . ') % (' . $int2 . '))';
1756     }
1758     /**
1759      * Returns the cross db correct CEIL (ceiling) expression applied to fieldname.
1760      * note: Most DBs use CEIL(), hence it's the default here.
1761      *
1762      * @param string $fieldname The field (or expression) we are going to ceil.
1763      * @return string The piece of SQL code to be used in your ceiling statement.
1764      */
1765     public function sql_ceil($fieldname) {
1766         return ' CEIL(' . $fieldname . ')';
1767     }
1769     /**
1770      * Returns the SQL to be used in order to CAST one CHAR column to INTEGER.
1771      *
1772      * Be aware that the CHAR column you're trying to cast contains really
1773      * int values or the RDBMS will throw an error!
1774      *
1775      * @param string $fieldname The name of the field to be casted.
1776      * @param bool $text Specifies if the original column is one TEXT (CLOB) column (true). Defaults to false.
1777      * @return string The piece of SQL code to be used in your statement.
1778      */
1779     public function sql_cast_char2int($fieldname, $text=false) {
1780         return ' ' . $fieldname . ' ';
1781     }
1783     /**
1784      * Returns the SQL to be used in order to CAST one CHAR column to REAL number.
1785      *
1786      * Be aware that the CHAR column you're trying to cast contains really
1787      * numbers or the RDBMS will throw an error!
1788      *
1789      * @param string $fieldname The name of the field to be casted.
1790      * @param bool $text Specifies if the original column is one TEXT (CLOB) column (true). Defaults to false.
1791      * @return string The piece of SQL code to be used in your statement.
1792      */
1793     public function sql_cast_char2real($fieldname, $text=false) {
1794         return ' ' . $fieldname . ' ';
1795     }
1797     /**
1798      * Returns the SQL to be used in order to an UNSIGNED INTEGER column to SIGNED.
1799      *
1800      * (Only MySQL needs this. MySQL things that 1 * -1 = 18446744073709551615
1801      * if the 1 comes from an unsigned column).
1802      *
1803      * @param string $fieldname The name of the field to be cast
1804      * @return string The piece of SQL code to be used in your statement.
1805      */
1806     public function sql_cast_2signed($fieldname) {
1807         return ' ' . $fieldname . ' ';
1808     }
1810     /**
1811      * Returns the SQL text to be used to compare one TEXT (clob) column with
1812      * one varchar column, because some RDBMS doesn't support such direct
1813      * comparisons.
1814      *
1815      * @param string $fieldname The name of the TEXT field we need to order by
1816      * @param int $numchars Number of chars to use for the ordering (defaults to 32).
1817      * @return string The piece of SQL code to be used in your statement.
1818      */
1819     public function sql_compare_text($fieldname, $numchars=32) {
1820         return $this->sql_order_by_text($fieldname, $numchars);
1821     }
1823     /**
1824      * Returns 'LIKE' part of a query.
1825      *
1826      * @param string $fieldname Usually the name of the table column.
1827      * @param string $param Usually the bound query parameter (?, :named).
1828      * @param bool $casesensitive Use case sensitive search when set to true (default).
1829      * @param bool $accentsensitive Use accent sensitive search when set to true (default). (not all databases support accent insensitive)
1830      * @param bool $notlike True means "NOT LIKE".
1831      * @param string $escapechar The escape char for '%' and '_'.
1832      * @return string The SQL code fragment.
1833      */
1834     public function sql_like($fieldname, $param, $casesensitive = true, $accentsensitive = true, $notlike = false, $escapechar = '\\') {
1835         if (strpos($param, '%') !== false) {
1836             debugging('Potential SQL injection detected, sql_like() expects bound parameters (? or :named)');
1837         }
1838         $LIKE = $notlike ? 'NOT LIKE' : 'LIKE';
1839         // by default ignore any sensitiveness - each database does it in a different way
1840         return "$fieldname $LIKE $param ESCAPE '$escapechar'";
1841     }
1843     /**
1844      * Escape sql LIKE special characters like '_' or '%'.
1845      * @param string $text The string containing characters needing escaping.
1846      * @param string $escapechar The desired escape character, defaults to '\\'.
1847      * @return string The escaped sql LIKE string.
1848      */
1849     public function sql_like_escape($text, $escapechar = '\\') {
1850         $text = str_replace('_', $escapechar.'_', $text);
1851         $text = str_replace('%', $escapechar.'%', $text);
1852         return $text;
1853     }
1855     /**
1856      * Returns the proper SQL to do LIKE in a case-insensitive way.
1857      *
1858      * Note the LIKE are case sensitive for Oracle. Oracle 10g is required to use
1859      * the case insensitive search using regexp_like() or NLS_COMP=LINGUISTIC :-(
1860      * See http://docs.moodle.org/en/XMLDB_Problems#Case-insensitive_searches
1861      *
1862      * @deprecated since Moodle 2.0 MDL-23925 - please do not use this function any more.
1863      * @todo MDL-31280 to remove deprecated functions prior to 2.3 release.
1864      * @return string Do not use this function! @see sql_like()
1865      */
1866     public function sql_ilike() {
1867         debugging('sql_ilike() is deprecated, please use sql_like() instead');
1868         return 'LIKE';
1869     }
1871     /**
1872      * Returns the proper SQL to do CONCAT between the elements(fieldnames) passed.
1873      *
1874      * This function accepts variable number of string parameters.
1875      * All strings/fieldnames will used in the SQL concatenate statement generated.
1876      *
1877      * @return string The SQL to concatenate strings passed in.
1878      * @uses func_get_args()  and thus parameters are unlimited OPTIONAL number of additional field names.
1879      */
1880     public abstract function sql_concat();
1882     /**
1883      * Returns the proper SQL to do CONCAT between the elements passed
1884      * with a given separator
1885      *
1886      * @param string $separator The separator desired for the SQL concatenating $elements.
1887      * @param array  $elements The array of strings to be concatenated.
1888      * @return string The SQL to concatenate the strings.
1889      */
1890     public abstract function sql_concat_join($separator="' '", $elements=array());
1892     /**
1893      * Returns the proper SQL (for the dbms in use) to concatenate $firstname and $lastname
1894      *
1895      * @todo MDL-31233 This may not be needed here.
1896      *
1897      * @param string $first User's first name (default:'firstname').
1898      * @param string $last User's last name (default:'lastname').
1899      * @return string The SQL to concatenate strings.
1900      */
1901     function sql_fullname($first='firstname', $last='lastname') {
1902         return $this->sql_concat($first, "' '", $last);
1903     }
1905     /**
1906      * Returns the SQL text to be used to order by one TEXT (clob) column, because
1907      * some RDBMS doesn't support direct ordering of such fields.
1908      *
1909      * Note that the use or queries being ordered by TEXT columns must be minimised,
1910      * because it's really slooooooow.
1911      *
1912      * @param string $fieldname The name of the TEXT field we need to order by.
1913      * @param string $numchars The number of chars to use for the ordering (defaults to 32).
1914      * @return string The piece of SQL code to be used in your statement.
1915      */
1916     public function sql_order_by_text($fieldname, $numchars=32) {
1917         return $fieldname;
1918     }
1920     /**
1921      * Returns the SQL text to be used to calculate the length in characters of one expression.
1922      * @param string $fieldname The fieldname/expression to calculate its length in characters.
1923      * @return string the piece of SQL code to be used in the statement.
1924      */
1925     public function sql_length($fieldname) {
1926         return ' LENGTH(' . $fieldname . ')';
1927     }
1929     /**
1930      * Returns the proper substr() SQL text used to extract substrings from DB
1931      * NOTE: this was originally returning only function name
1932      *
1933      * @param string $expr Some string field, no aggregates.
1934      * @param mixed $start Integer or expression evaluating to integer (1 based value; first char has index 1)
1935      * @param mixed $length Optional integer or expression evaluating to integer.
1936      * @return string The sql substring extraction fragment.
1937      */
1938     public function sql_substr($expr, $start, $length=false) {
1939         if (count(func_get_args()) < 2) {
1940             throw new coding_exception('moodle_database::sql_substr() requires at least two parameters', 'Originally this function was only returning name of SQL substring function, it now requires all parameters.');
1941         }
1942         if ($length === false) {
1943             return "SUBSTR($expr, $start)";
1944         } else {
1945             return "SUBSTR($expr, $start, $length)";
1946         }
1947     }
1949     /**
1950      * Returns the SQL for returning searching one string for the location of another.
1951      *
1952      * Note, there is no guarantee which order $needle, $haystack will be in
1953      * the resulting SQL so when using this method, and both arguments contain
1954      * placeholders, you should use named placeholders.
1955      *
1956      * @param string $needle the SQL expression that will be searched for.
1957      * @param string $haystack the SQL expression that will be searched in.
1958      * @return string The required searching SQL part.
1959      */
1960     public function sql_position($needle, $haystack) {
1961         // Implementation using standard SQL.
1962         return "POSITION(($needle) IN ($haystack))";
1963     }
1965     /**
1966      * Returns the empty string char used by every supported DB. To be used when
1967      * we are searching for that values in our queries. Only Oracle uses this
1968      * for now (will be out, once we migrate to proper NULLs if that days arrives)
1969      * @return string An empty string.
1970      */
1971     function sql_empty() {
1972         return '';
1973     }
1975     /**
1976      * Returns the proper SQL to know if one field is empty.
1977      *
1978      * Note that the function behavior strongly relies on the
1979      * parameters passed describing the field so, please,  be accurate
1980      * when specifying them.
1981      *
1982      * Also, note that this function is not suitable to look for
1983      * fields having NULL contents at all. It's all for empty values!
1984      *
1985      * This function should be applied in all the places where conditions of
1986      * the type:
1987      *
1988      *     ... AND fieldname = '';
1989      *
1990      * are being used. Final result should be:
1991      *
1992      *     ... AND ' . sql_isempty('tablename', 'fieldname', true/false, true/false);
1993      *
1994      * (see parameters description below)
1995      *
1996      * @param string $tablename Name of the table (without prefix). Not used for now but can be
1997      *                          necessary in the future if we want to use some introspection using
1998      *                          meta information against the DB. /// TODO ///
1999      * @param string $fieldname Name of the field we are going to check
2000      * @param bool $nullablefield For specifying if the field is nullable (true) or no (false) in the DB.
2001      * @param bool $textfield For specifying if it is a text (also called clob) field (true) or a varchar one (false)
2002      * @return string the sql code to be added to check for empty values
2003      */
2004     public function sql_isempty($tablename, $fieldname, $nullablefield, $textfield) {
2005         return " ($fieldname = '') ";
2006     }
2008     /**
2009      * Returns the proper SQL to know if one field is not empty.
2010      *
2011      * Note that the function behavior strongly relies on the
2012      * parameters passed describing the field so, please,  be accurate
2013      * when specifying them.
2014      *
2015      * This function should be applied in all the places where conditions of
2016      * the type:
2017      *
2018      *     ... AND fieldname != '';
2019      *
2020      * are being used. Final result should be:
2021      *
2022      *     ... AND ' . sql_isnotempty('tablename', 'fieldname', true/false, true/false);
2023      *
2024      * (see parameters description below)
2025      *
2026      * @param string $tablename Name of the table (without prefix). This is not used for now but can be
2027      *                          necessary in the future if we want to use some introspection using
2028      *                          meta information against the DB.
2029      * @param string $fieldname The name of the field we are going to check.
2030      * @param bool $nullablefield Specifies if the field is nullable (true) or not (false) in the DB.
2031      * @param bool $textfield Specifies if it is a text (also called clob) field (true) or a varchar one (false).
2032      * @return string The sql code to be added to check for non empty values.
2033      */
2034     public function sql_isnotempty($tablename, $fieldname, $nullablefield, $textfield) {
2035         return ' ( NOT ' . $this->sql_isempty($tablename, $fieldname, $nullablefield, $textfield) . ') ';
2036     }
2038     /**
2039      * Returns true if this database driver supports regex syntax when searching.
2040      * @return bool True if supported.
2041      */
2042     public function sql_regex_supported() {
2043         return false;
2044     }
2046     /**
2047      * Returns the driver specific syntax (SQL part) for matching regex positively or negatively (inverted matching).
2048      * Eg: 'REGEXP':'NOT REGEXP' or '~*' : '!~*'
2049      * @param bool $positivematch
2050      * @return string or empty if not supported
2051      */
2052     public function sql_regex($positivematch=true) {
2053         return '';
2054     }
2056 /// transactions
2058     /**
2059      * Checks and returns true if transactions are supported.
2060      *
2061      * It is not responsible to run productions servers
2062      * on databases without transaction support ;-)
2063      *
2064      * Override in driver if needed.
2065      *
2066      * @return bool
2067      */
2068     protected function transactions_supported() {
2069         // protected for now, this might be changed to public if really necessary
2070         return true;
2071     }
2073     /**
2074      * Returns true if a transaction is in progress.
2075      * @return bool
2076      */
2077     public function is_transaction_started() {
2078         return !empty($this->transactions);
2079     }
2081     /**
2082      * This is a test that throws an exception if transaction in progress.
2083      * This test does not force rollback of active transactions.
2084      * @return void
2085      */
2086     public function transactions_forbidden() {
2087         if ($this->is_transaction_started()) {
2088             throw new dml_transaction_exception('This code can not be excecuted in transaction');
2089         }
2090     }
2092     /**
2093      * On DBs that support it, switch to transaction mode and begin a transaction
2094      * you'll need to ensure you call allow_commit() on the returned object
2095      * or your changes *will* be lost.
2096      *
2097      * this is _very_ useful for massive updates
2098      *
2099      * Delegated database transactions can be nested, but only one actual database
2100      * transaction is used for the outer-most delegated transaction. This method
2101      * returns a transaction object which you should keep until the end of the
2102      * delegated transaction. The actual database transaction will
2103      * only be committed if all the nested delegated transactions commit
2104      * successfully. If any part of the transaction rolls back then the whole
2105      * thing is rolled back.
2106      *
2107      * @return moodle_transaction
2108      */
2109     public function start_delegated_transaction() {
2110         $transaction = new moodle_transaction($this);
2111         $this->transactions[] = $transaction;
2112         if (count($this->transactions) == 1) {
2113             $this->begin_transaction();
2114         }
2115         return $transaction;
2116     }
2118     /**
2119      * Driver specific start of real database transaction,
2120      * this can not be used directly in code.
2121      * @return void
2122      */
2123     protected abstract function begin_transaction();
2125     /**
2126      * Indicates delegated transaction finished successfully.
2127      * The real database transaction is committed only if
2128      * all delegated transactions committed.
2129      * @return void
2130      * @throws dml_transaction_exception Creates and throws transaction related exceptions.
2131      */
2132     public function commit_delegated_transaction(moodle_transaction $transaction) {
2133         if ($transaction->is_disposed()) {
2134             throw new dml_transaction_exception('Transactions already disposed', $transaction);
2135         }
2136         // mark as disposed so that it can not be used again
2137         $transaction->dispose();
2139         if (empty($this->transactions)) {
2140             throw new dml_transaction_exception('Transaction not started', $transaction);
2141         }
2143         if ($this->force_rollback) {
2144             throw new dml_transaction_exception('Tried to commit transaction after lower level rollback', $transaction);
2145         }
2147         if ($transaction !== $this->transactions[count($this->transactions) - 1]) {
2148             // one incorrect commit at any level rollbacks everything
2149             $this->force_rollback = true;
2150             throw new dml_transaction_exception('Invalid transaction commit attempt', $transaction);
2151         }
2153         if (count($this->transactions) == 1) {
2154             // only commit the top most level
2155             $this->commit_transaction();
2156         }
2157         array_pop($this->transactions);
2158     }
2160     /**
2161      * Driver specific commit of real database transaction,
2162      * this can not be used directly in code.
2163      * @return void
2164      */
2165     protected abstract function commit_transaction();
2167     /**
2168      * Call when delegated transaction failed, this rolls back
2169      * all delegated transactions up to the top most level.
2170      *
2171      * In many cases you do not need to call this method manually,
2172      * because all open delegated transactions are rolled back
2173      * automatically if exceptions not caught.
2174      *
2175      * @param moodle_transaction $transaction An instance of a moodle_transaction.
2176      * @param Exception $e The related exception to this transaction rollback.
2177      * @return void This does not return, instead the exception passed in will be rethrown.
2178      */
2179     public function rollback_delegated_transaction(moodle_transaction $transaction, Exception $e) {
2180         if ($transaction->is_disposed()) {
2181             throw new dml_transaction_exception('Transactions already disposed', $transaction);
2182         }
2183         // mark as disposed so that it can not be used again
2184         $transaction->dispose();
2186         // one rollback at any level rollbacks everything
2187         $this->force_rollback = true;
2189         if (empty($this->transactions) or $transaction !== $this->transactions[count($this->transactions) - 1]) {
2190             // this may or may not be a coding problem, better just rethrow the exception,
2191             // because we do not want to loose the original $e
2192             throw $e;
2193         }
2195         if (count($this->transactions) == 1) {
2196             // only rollback the top most level
2197             $this->rollback_transaction();
2198         }
2199         array_pop($this->transactions);
2200         if (empty($this->transactions)) {
2201             // finally top most level rolled back
2202             $this->force_rollback = false;
2203         }
2204         throw $e;
2205     }
2207     /**
2208      * Driver specific abort of real database transaction,
2209      * this can not be used directly in code.
2210      * @return void
2211      */
2212     protected abstract function rollback_transaction();
2214     /**
2215      * Force rollback of all delegated transaction.
2216      * Does not throw any exceptions and does not log anything.
2217      *
2218      * This method should be used only from default exception handlers and other
2219      * core code.
2220      *
2221      * @return void
2222      */
2223     public function force_transaction_rollback() {
2224         if ($this->transactions) {
2225             try {
2226                 $this->rollback_transaction();
2227             } catch (dml_exception $e) {
2228                 // ignore any sql errors here, the connection might be broken
2229             }
2230         }
2232         // now enable transactions again
2233         $this->transactions = array(); // unfortunately all unfinished exceptions are kept in memory
2234         $this->force_rollback = false;
2235     }
2237 /// session locking
2238     /**
2239      * Is session lock supported in this driver?
2240      * @return bool
2241      */
2242     public function session_lock_supported() {
2243         return false;
2244     }
2246     /**
2247      * Obtains the session lock.
2248      * @param int $rowid The id of the row with session record.
2249      * @param int $timeout The maximum allowed time to wait for the lock in seconds.
2250      * @return void
2251      * @throws dml_exception A DML specific exception is thrown for any errors.
2252      */
2253     public function get_session_lock($rowid, $timeout) {
2254         $this->used_for_db_sessions = true;
2255     }
2257     /**
2258      * Releases the session lock.
2259      * @param int $rowid The id of the row with session record.
2260      * @return void
2261      * @throws dml_exception A DML specific exception is thrown for any errors.
2262      */
2263     public function release_session_lock($rowid) {
2264     }
2266 /// performance and logging
2267     /**
2268      * Returns the number of reads done by this database.
2269      * @return int Number of reads.
2270      */
2271     public function perf_get_reads() {
2272         return $this->reads;
2273     }
2275     /**
2276      * Returns the number of writes done by this database.
2277      * @return int Number of writes.
2278      */
2279     public function perf_get_writes() {
2280         return $this->writes;
2281     }
2283     /**
2284      * Returns the number of queries done by this database.
2285      * @return int Number of queries.
2286      */
2287     public function perf_get_queries() {
2288         return $this->writes + $this->reads;
2289     }