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