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