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