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