Moodle  2.2.1
http://www.collinsharper.com
C:/xampp/htdocs/moodle/lib/dml/moodle_database.php
Go to the documentation of this file.
00001 <?php
00002 
00003 // This file is part of Moodle - http://moodle.org/
00004 //
00005 // Moodle is free software: you can redistribute it and/or modify
00006 // it under the terms of the GNU General Public License as published by
00007 // the Free Software Foundation, either version 3 of the License, or
00008 // (at your option) any later version.
00009 //
00010 // Moodle is distributed in the hope that it will be useful,
00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013 // GNU General Public License for more details.
00014 //
00015 // You should have received a copy of the GNU General Public License
00016 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
00017 
00018 
00028 defined('MOODLE_INTERNAL') || die();
00029 
00030 require_once($CFG->libdir.'/dml/database_column_info.php');
00031 require_once($CFG->libdir.'/dml/moodle_recordset.php');
00032 require_once($CFG->libdir.'/dml/moodle_transaction.php');
00033 
00035 
00037 define('SQL_PARAMS_NAMED', 1);
00038 
00040 define('SQL_PARAMS_QM', 2);
00041 
00043 define('SQL_PARAMS_DOLLAR', 4);
00044 
00045 
00047 define('SQL_QUERY_SELECT', 1);
00048 
00050 define('SQL_QUERY_INSERT', 2);
00051 
00053 define('SQL_QUERY_UPDATE', 3);
00054 
00056 define('SQL_QUERY_STRUCTURE', 4);
00057 
00059 define('SQL_QUERY_AUX', 5);
00060 
00064 abstract class moodle_database {
00065 
00067     protected $database_manager;
00069     protected $temptables;
00071     protected $columns = array(); // I wish we had a shared memory cache for this :-(
00073     protected $tables  = null;
00074 
00075     // db connection options
00077     protected $dbhost;
00079     protected $dbuser;
00081     protected $dbpass;
00083     protected $dbname;
00085     protected $prefix;
00086 
00088     protected $dboptions;
00089 
00091     protected $external;
00092 
00094     protected $reads = 0;
00096     protected $writes = 0;
00097 
00099     protected $debug  = 0;
00100 
00102     protected $last_sql;
00104     protected $last_params;
00106     protected $last_type;
00108     protected $last_extrainfo;
00110     protected $last_time;
00112     private $loggingquery = false;
00113 
00115     protected $used_for_db_sessions = false;
00116 
00118     private $transactions = array();
00120     private $force_rollback = false;
00121 
00123     private $fix_sql_params_i;
00125     private $inorequaluniqueindex = 1; // guarantees unique parameters in each request
00126 
00132     public function __construct($external=false) {
00133         $this->external  = $external;
00134     }
00135 
00139     public function __destruct() {
00140         $this->dispose();
00141     }
00142 
00148     public abstract function driver_installed();
00149 
00155     public function get_prefix() {
00156         return $this->prefix;
00157     }
00158 
00166     public static function get_driver_instance($type, $library, $external = false) {
00167         global $CFG;
00168 
00169         $classname = $type.'_'.$library.'_moodle_database';
00170         $libfile   = "$CFG->libdir/dml/$classname.php";
00171 
00172         if (!file_exists($libfile)) {
00173             return null;
00174         }
00175 
00176         require_once($libfile);
00177         return new $classname($external);
00178     }
00179 
00185     public abstract function get_dbfamily();
00186 
00192     protected abstract function get_dbtype();
00193 
00199     protected abstract function get_dblibrary();
00200 
00206     public abstract function get_name();
00207 
00213     public abstract function get_configuration_help();
00214 
00220     public abstract function get_configuration_hints();
00221 
00226     public function export_dbconfig() {
00227         $cfg = new stdClass();
00228         $cfg->dbtype    = $this->get_dbtype();
00229         $cfg->dblibrary = $this->get_dblibrary();
00230         $cfg->dbhost    = $this->dbhost;
00231         $cfg->dbname    = $this->dbname;
00232         $cfg->dbuser    = $this->dbuser;
00233         $cfg->dbpass    = $this->dbpass;
00234         $cfg->prefix    = $this->prefix;
00235         if ($this->dboptions) {
00236             $cfg->dboptions = $this->dboptions;
00237         }
00238 
00239         return $cfg;
00240     }
00241 
00248     public function diagnose() {
00249         return null;
00250     }
00251 
00264     public abstract function connect($dbhost, $dbuser, $dbpass, $dbname, $prefix, array $dboptions=null);
00265 
00276     protected function store_settings($dbhost, $dbuser, $dbpass, $dbname, $prefix, array $dboptions=null) {
00277         $this->dbhost    = $dbhost;
00278         $this->dbuser    = $dbuser;
00279         $this->dbpass    = $dbpass;
00280         $this->dbname    = $dbname;
00281         $this->prefix    = $prefix;
00282         $this->dboptions = (array)$dboptions;
00283     }
00284 
00294     public function create_database($dbhost, $dbuser, $dbpass, $dbname, array $dboptions=null) {
00295         return false;
00296     }
00297 
00304     public function dispose() {
00305         if ($this->transactions) {
00306             // this should not happen, it usually indicates wrong catching of exceptions,
00307             // because all transactions should be finished manually or in default exception handler.
00308             // unfortunately we can not access global $CFG any more and can not print debug,
00309             // the diagnostic info should be printed in footer instead
00310             $lowesttransaction = end($this->transactions);
00311             $backtrace = $lowesttransaction->get_backtrace();
00312 
00313             error_log('Potential coding error - active database transaction detected when disposing database:'."\n".format_backtrace($backtrace, true));
00314             $this->force_transaction_rollback();
00315         }
00316         if ($this->used_for_db_sessions) {
00317             // this is needed because we need to save session to db before closing it
00318             session_get_instance()->write_close();
00319             $this->used_for_db_sessions = false;
00320         }
00321         if ($this->temptables) {
00322             $this->temptables->dispose();
00323             $this->temptables = null;
00324         }
00325         if ($this->database_manager) {
00326             $this->database_manager->dispose();
00327             $this->database_manager = null;
00328         }
00329         $this->columns = array();
00330         $this->tables  = null;
00331     }
00332 
00341     protected function query_start($sql, array $params=null, $type, $extrainfo=null) {
00342         if ($this->loggingquery) {
00343             return;
00344         }
00345         $this->last_sql       = $sql;
00346         $this->last_params    = $params;
00347         $this->last_type      = $type;
00348         $this->last_extrainfo = $extrainfo;
00349         $this->last_time      = microtime(true);
00350 
00351         switch ($type) {
00352             case SQL_QUERY_SELECT:
00353             case SQL_QUERY_AUX:
00354                 $this->reads++;
00355                 break;
00356             case SQL_QUERY_INSERT:
00357             case SQL_QUERY_UPDATE:
00358             case SQL_QUERY_STRUCTURE:
00359                 $this->writes++;
00360         }
00361 
00362         $this->print_debug($sql, $params);
00363     }
00364 
00370     protected function query_end($result) {
00371         if ($this->loggingquery) {
00372             return;
00373         }
00374         if ($result !== false) {
00375             $this->query_log();
00376             // free memory
00377             $this->last_sql    = null;
00378             $this->last_params = null;
00379             return;
00380         }
00381 
00382         // remember current info, log queries may alter it
00383         $type   = $this->last_type;
00384         $sql    = $this->last_sql;
00385         $params = $this->last_params;
00386         $time   = microtime(true) - $this->last_time;
00387         $error  = $this->get_last_error();
00388 
00389         $this->query_log($error);
00390 
00391         switch ($type) {
00392             case SQL_QUERY_SELECT:
00393             case SQL_QUERY_AUX:
00394                 throw new dml_read_exception($error, $sql, $params);
00395             case SQL_QUERY_INSERT:
00396             case SQL_QUERY_UPDATE:
00397                 throw new dml_write_exception($error, $sql, $params);
00398             case SQL_QUERY_STRUCTURE:
00399                 $this->get_manager(); // includes ddl exceptions classes ;-)
00400                 throw new ddl_change_structure_exception($error, $sql);
00401         }
00402     }
00403 
00409     public function query_log($error=false) {
00410         $logall    = !empty($this->dboptions['logall']);
00411         $logslow   = !empty($this->dboptions['logslow']) ? $this->dboptions['logslow'] : false;
00412         $logerrors = !empty($this->dboptions['logerrors']);
00413         $iserror   = ($error !== false);
00414 
00415         $time = microtime(true) - $this->last_time;
00416 
00417         if ($logall or ($logslow and ($logslow < ($time+0.00001))) or ($iserror and $logerrors)) {
00418             $this->loggingquery = true;
00419             try {
00420                 $backtrace = debug_backtrace();
00421                 if ($backtrace) {
00422                     //remove query_log()
00423                     array_shift($backtrace);
00424                 }
00425                 if ($backtrace) {
00426                     //remove query_end()
00427                     array_shift($backtrace);
00428                 }
00429                 $log = new stdClass();
00430                 $log->qtype      = $this->last_type;
00431                 $log->sqltext    = $this->last_sql;
00432                 $log->sqlparams  = var_export((array)$this->last_params, true);
00433                 $log->error      = (int)$iserror;
00434                 $log->info       = $iserror ? $error : null;
00435                 $log->backtrace  = format_backtrace($backtrace, true);
00436                 $log->exectime   = $time;
00437                 $log->timelogged = time();
00438                 $this->insert_record('log_queries', $log);
00439             } catch (Exception $ignored) {
00440             }
00441             $this->loggingquery = false;
00442         }
00443     }
00444 
00449     public abstract function get_server_info();
00450 
00455     protected abstract function allowed_param_types();
00456 
00461     public abstract function get_last_error();
00462 
00470     protected function print_debug($sql, array $params=null, $obj=null) {
00471         if (!$this->get_debug()) {
00472             return;
00473         }
00474         if (CLI_SCRIPT) {
00475             echo "--------------------------------\n";
00476             echo $sql."\n";
00477             if (!is_null($params)) {
00478                 echo "[".var_export($params, true)."]\n";
00479             }
00480             echo "--------------------------------\n";
00481         } else {
00482             echo "<hr />\n";
00483             echo s($sql)."\n";
00484             if (!is_null($params)) {
00485                 echo "[".s(var_export($params, true))."]\n";
00486             }
00487             echo "<hr />\n";
00488         }
00489     }
00490 
00497     protected function where_clause($table, array $conditions=null) {
00498         // We accept nulls in conditions
00499         $conditions = is_null($conditions) ? array() : $conditions;
00500         // Some checks performed under debugging only
00501         if (debugging()) {
00502             $columns = $this->get_columns($table);
00503             if (empty($columns)) {
00504                 // no supported columns means most probably table does not exist
00505                 throw new dml_exception('ddltablenotexist', $table);
00506             }
00507             foreach ($conditions as $key=>$value) {
00508                 if (!isset($columns[$key])) {
00509                     $a = new stdClass();
00510                     $a->fieldname = $key;
00511                     $a->tablename = $table;
00512                     throw new dml_exception('ddlfieldnotexist', $a);
00513                 }
00514                 $column = $columns[$key];
00515                 if ($column->meta_type == 'X') {
00516                     //ok so the column is a text column. sorry no text columns in the where clause conditions
00517                     throw new dml_exception('textconditionsnotallowed', $conditions);
00518                 }
00519             }
00520         }
00521 
00522         $allowed_types = $this->allowed_param_types();
00523         if (empty($conditions)) {
00524             return array('', array());
00525         }
00526         $where = array();
00527         $params = array();
00528 
00529         foreach ($conditions as $key=>$value) {
00530             if (is_int($key)) {
00531                 throw new dml_exception('invalidnumkey');
00532             }
00533             if (is_null($value)) {
00534                 $where[] = "$key IS NULL";
00535             } else {
00536                 if ($allowed_types & SQL_PARAMS_NAMED) {
00537                     // Need to verify key names because they can contain, originally,
00538                     // spaces and other forbidden chars when using sql_xxx() functions and friends.
00539                     $normkey = trim(preg_replace('/[^a-zA-Z0-9_-]/', '_', $key), '-_');
00540                     if ($normkey !== $key) {
00541                         debugging('Invalid key found in the conditions array.');
00542                     }
00543                     $where[] = "$key = :$normkey";
00544                     $params[$normkey] = $value;
00545                 } else {
00546                     $where[] = "$key = ?";
00547                     $params[] = $value;
00548                 }
00549             }
00550         }
00551         $where = implode(" AND ", $where);
00552         return array($where, $params);
00553     }
00554 
00562     protected function where_clause_list($field, array $values) {
00563         $params = array();
00564         $select = array();
00565         $values = (array)$values;
00566         foreach ($values as $value) {
00567             if (is_bool($value)) {
00568                 $value = (int)$value;
00569             }
00570             if (is_null($value)) {
00571                 $select[] = "$field IS NULL";
00572             } else {
00573                 $select[] = "$field = ?";
00574                 $params[] = $value;
00575             }
00576         }
00577         $select = implode(" OR ", $select);
00578         return array($select, $params);
00579     }
00580 
00591     public function get_in_or_equal($items, $type=SQL_PARAMS_QM, $prefix='param', $equal=true, $onemptyitems=false) {
00592 
00593         // default behavior, throw exception on empty array
00594         if (is_array($items) and empty($items) and $onemptyitems === false) {
00595             throw new coding_exception('moodle_database::get_in_or_equal() does not accept empty arrays');
00596         }
00597         // handle $onemptyitems on empty array of items
00598         if (is_array($items) and empty($items)) {
00599             if (is_null($onemptyitems)) {             // Special case, NULL value
00600                 $sql = $equal ? ' IS NULL' : ' IS NOT NULL';
00601                 return (array($sql, array()));
00602             } else {
00603                 $items = array($onemptyitems);        // Rest of cases, prepare $items for std processing
00604             }
00605         }
00606 
00607         if ($type == SQL_PARAMS_QM) {
00608             if (!is_array($items) or count($items) == 1) {
00609                 $sql = $equal ? '= ?' : '<> ?';
00610                 $items = (array)$items;
00611                 $params = array_values($items);
00612             } else {
00613                 if ($equal) {
00614                     $sql = 'IN ('.implode(',', array_fill(0, count($items), '?')).')';
00615                 } else {
00616                     $sql = 'NOT IN ('.implode(',', array_fill(0, count($items), '?')).')';
00617                 }
00618                 $params = array_values($items);
00619             }
00620 
00621         } else if ($type == SQL_PARAMS_NAMED) {
00622             if (empty($prefix)) {
00623                 $prefix = 'param';
00624             }
00625 
00626             if (!is_array($items)){
00627                 $param = $prefix.$this->inorequaluniqueindex++;
00628                 $sql = $equal ? "= :$param" : "<> :$param";
00629                 $params = array($param=>$items);
00630             } else if (count($items) == 1) {
00631                 $param = $prefix.$this->inorequaluniqueindex++;
00632                 $sql = $equal ? "= :$param" : "<> :$param";
00633                 $item = reset($items);
00634                 $params = array($param=>$item);
00635             } else {
00636                 $params = array();
00637                 $sql = array();
00638                 foreach ($items as $item) {
00639                     $param = $prefix.$this->inorequaluniqueindex++;
00640                     $params[$param] = $item;
00641                     $sql[] = ':'.$param;
00642                 }
00643                 if ($equal) {
00644                     $sql = 'IN ('.implode(',', $sql).')';
00645                 } else {
00646                     $sql = 'NOT IN ('.implode(',', $sql).')';
00647                 }
00648             }
00649 
00650         } else {
00651             throw new dml_exception('typenotimplement');
00652         }
00653         return array($sql, $params);
00654     }
00655 
00661     protected function fix_table_names($sql) {
00662         return preg_replace('/\{([a-z][a-z0-9_]*)\}/', $this->prefix.'$1', $sql);
00663     }
00664 
00666     private function _fix_sql_params_dollar_callback($match) {
00667         $this->fix_sql_params_i++;
00668         return "\$".$this->fix_sql_params_i;
00669     }
00670 
00677     public function fix_sql_params($sql, array $params=null) {
00678         $params = (array)$params; // mke null array if needed
00679         $allowed_types = $this->allowed_param_types();
00680 
00681         // convert table names
00682         $sql = $this->fix_table_names($sql);
00683 
00684         // cast booleans to 1/0 int
00685         foreach ($params as $key => $value) {
00686             $params[$key] = is_bool($value) ? (int)$value : $value;
00687         }
00688 
00689         // NICOLAS C: Fixed regexp for negative backwards lookahead of double colons. Thanks for Sam Marshall's help
00690         $named_count = preg_match_all('/(?<!:):[a-z][a-z0-9_]*/', $sql, $named_matches); // :: used in pgsql casts
00691         $dollar_count = preg_match_all('/\$[1-9][0-9]*/', $sql, $dollar_matches);
00692         $q_count     = substr_count($sql, '?');
00693 
00694         $count = 0;
00695 
00696         if ($named_count) {
00697             $type = SQL_PARAMS_NAMED;
00698             $count = $named_count;
00699 
00700         }
00701         if ($dollar_count) {
00702             if ($count) {
00703                 throw new dml_exception('mixedtypesqlparam');
00704             }
00705             $type = SQL_PARAMS_DOLLAR;
00706             $count = $dollar_count;
00707 
00708         }
00709         if ($q_count) {
00710             if ($count) {
00711                 throw new dml_exception('mixedtypesqlparam');
00712             }
00713             $type = SQL_PARAMS_QM;
00714             $count = $q_count;
00715 
00716         }
00717 
00718         if (!$count) {
00719              // ignore params
00720             if ($allowed_types & SQL_PARAMS_NAMED) {
00721                 return array($sql, array(), SQL_PARAMS_NAMED);
00722             } else if ($allowed_types & SQL_PARAMS_QM) {
00723                 return array($sql, array(), SQL_PARAMS_QM);
00724             } else {
00725                 return array($sql, array(), SQL_PARAMS_DOLLAR);
00726             }
00727         }
00728 
00729         if ($count > count($params)) {
00730             $a = new stdClass;
00731             $a->expected = $count;
00732             $a->actual = count($params);
00733             throw new dml_exception('invalidqueryparam', $a);
00734         }
00735 
00736         $target_type = $allowed_types;
00737 
00738         if ($type & $allowed_types) { // bitwise AND
00739             if ($count == count($params)) {
00740                 if ($type == SQL_PARAMS_QM) {
00741                     return array($sql, array_values($params), SQL_PARAMS_QM); // 0-based array required
00742                 } else {
00743                     //better do the validation of names below
00744                 }
00745             }
00746             // needs some fixing or validation - there might be more params than needed
00747             $target_type = $type;
00748         }
00749 
00750         if ($type == SQL_PARAMS_NAMED) {
00751             $finalparams = array();
00752             foreach ($named_matches[0] as $key) {
00753                 $key = trim($key, ':');
00754                 if (!array_key_exists($key, $params)) {
00755                     throw new dml_exception('missingkeyinsql', $key, '');
00756                 }
00757                 if (strlen($key) > 30) {
00758                     throw new coding_exception(
00759                             "Placeholder names must be 30 characters or shorter. '" .
00760                             $key . "' is too long.", $sql);
00761                 }
00762                 $finalparams[$key] = $params[$key];
00763             }
00764             if ($count != count($finalparams)) {
00765                 throw new dml_exception('duplicateparaminsql');
00766             }
00767 
00768             if ($target_type & SQL_PARAMS_QM) {
00769                 $sql = preg_replace('/(?<!:):[a-z][a-z0-9_]*/', '?', $sql);
00770                 return array($sql, array_values($finalparams), SQL_PARAMS_QM); // 0-based required
00771             } else if ($target_type & SQL_PARAMS_NAMED) {
00772                 return array($sql, $finalparams, SQL_PARAMS_NAMED);
00773             } else {  // $type & SQL_PARAMS_DOLLAR
00774                 //lambda-style functions eat memory - we use globals instead :-(
00775                 $this->fix_sql_params_i = 0;
00776                 $sql = preg_replace_callback('/(?<!:):[a-z][a-z0-9_]*/', array($this, '_fix_sql_params_dollar_callback'), $sql);
00777                 return array($sql, array_values($finalparams), SQL_PARAMS_DOLLAR); // 0-based required
00778             }
00779 
00780         } else if ($type == SQL_PARAMS_DOLLAR) {
00781             if ($target_type & SQL_PARAMS_DOLLAR) {
00782                 return array($sql, array_values($params), SQL_PARAMS_DOLLAR); // 0-based required
00783             } else if ($target_type & SQL_PARAMS_QM) {
00784                 $sql = preg_replace('/\$[0-9]+/', '?', $sql);
00785                 return array($sql, array_values($params), SQL_PARAMS_QM); // 0-based required
00786             } else { //$target_type & SQL_PARAMS_NAMED
00787                 $sql = preg_replace('/\$([0-9]+)/', ':param\\1', $sql);
00788                 $finalparams = array();
00789                 foreach ($params as $key=>$param) {
00790                     $key++;
00791                     $finalparams['param'.$key] = $param;
00792                 }
00793                 return array($sql, $finalparams, SQL_PARAMS_NAMED);
00794             }
00795 
00796         } else { // $type == SQL_PARAMS_QM
00797             if (count($params) != $count) {
00798                 $params = array_slice($params, 0, $count);
00799             }
00800 
00801             if ($target_type & SQL_PARAMS_QM) {
00802                 return array($sql, array_values($params), SQL_PARAMS_QM); // 0-based required
00803             } else if ($target_type & SQL_PARAMS_NAMED) {
00804                 $finalparams = array();
00805                 $pname = 'param0';
00806                 $parts = explode('?', $sql);
00807                 $sql = array_shift($parts);
00808                 foreach ($parts as $part) {
00809                     $param = array_shift($params);
00810                     $pname++;
00811                     $sql .= ':'.$pname.$part;
00812                     $finalparams[$pname] = $param;
00813                 }
00814                 return array($sql, $finalparams, SQL_PARAMS_NAMED);
00815             } else {  // $type & SQL_PARAMS_DOLLAR
00816                 //lambda-style functions eat memory - we use globals instead :-(
00817                 $this->fix_sql_params_i = 0;
00818                 $sql = preg_replace_callback('/\?/', array($this, '_fix_sql_params_dollar_callback'), $sql);
00819                 return array($sql, array_values($params), SQL_PARAMS_DOLLAR); // 0-based required
00820             }
00821         }
00822     }
00823 
00828     public abstract function get_tables($usecache=true);
00829 
00834     public abstract function get_indexes($table);
00835 
00842     public abstract function get_columns($table, $usecache=true);
00843 
00851     protected abstract function normalise_value($column, $value);
00852 
00858     public function reset_caches() {
00859         $this->columns = array();
00860         $this->tables  = null;
00861     }
00862 
00868     public function get_manager() {
00869         global $CFG;
00870 
00871         if (!$this->database_manager) {
00872             require_once($CFG->libdir.'/ddllib.php');
00873 
00874             $classname = $this->get_dbfamily().'_sql_generator';
00875             require_once("$CFG->libdir/ddl/$classname.php");
00876             $generator = new $classname($this, $this->temptables);
00877 
00878             $this->database_manager = new database_manager($this, $generator);
00879         }
00880         return $this->database_manager;
00881     }
00882 
00887     public function change_db_encoding() {
00888         return false;
00889     }
00890 
00895     public function setup_is_unicodedb() {
00896         return true;
00897     }
00898 
00904     public function set_debug($state) {
00905         $this->debug = $state;
00906     }
00907 
00912     public function get_debug() {
00913         return $this->debug;
00914     }
00915 
00920     public function set_logging($state) {
00921         // adodb sql logging shares one table without prefix per db - this is no longer acceptable :-(
00922         // we must create one table shared by all drivers
00923     }
00924 
00931     public abstract function change_database_structure($sql);
00932 
00941     public abstract function execute($sql, array $params=null);
00942 
00977     public function get_recordset($table, array $conditions=null, $sort='', $fields='*', $limitfrom=0, $limitnum=0) {
00978         list($select, $params) = $this->where_clause($table, $conditions);
00979         return $this->get_recordset_select($table, $select, $params, $sort, $fields, $limitfrom, $limitnum);
00980     }
00981 
01000     public function get_recordset_list($table, $field, array $values, $sort='', $fields='*', $limitfrom=0, $limitnum=0) {
01001         list($select, $params) = $this->where_clause_list($field, $values);
01002         if (empty($select)) {
01003             $select = '1 = 2'; 
01004             $params = array();
01005         }
01006         return $this->get_recordset_select($table, $select, $params, $sort, $fields, $limitfrom, $limitnum);
01007     }
01008 
01027     public function get_recordset_select($table, $select, array $params=null, $sort='', $fields='*', $limitfrom=0, $limitnum=0) {
01028         $sql = "SELECT $fields FROM {".$table."}";
01029         if ($select) {
01030             $sql .= " WHERE $select";
01031         }
01032         if ($sort) {
01033             $sql .= " ORDER BY $sort";
01034         }
01035         return $this->get_recordset_sql($sql, $params, $limitfrom, $limitnum);
01036     }
01037 
01054     public abstract function get_recordset_sql($sql, array $params=null, $limitfrom=0, $limitnum=0);
01055 
01076     public function get_records($table, array $conditions=null, $sort='', $fields='*', $limitfrom=0, $limitnum=0) {
01077         list($select, $params) = $this->where_clause($table, $conditions);
01078         return $this->get_records_select($table, $select, $params, $sort, $fields, $limitfrom, $limitnum);
01079     }
01080 
01096     public function get_records_list($table, $field, array $values, $sort='', $fields='*', $limitfrom=0, $limitnum=0) {
01097         list($select, $params) = $this->where_clause_list($field, $values);
01098         if (empty($select)) {
01099             // nothing to return
01100             return array();
01101         }
01102         return $this->get_records_select($table, $select, $params, $sort, $fields, $limitfrom, $limitnum);
01103     }
01104 
01122     public function get_records_select($table, $select, array $params=null, $sort='', $fields='*', $limitfrom=0, $limitnum=0) {
01123         if ($select) {
01124             $select = "WHERE $select";
01125         }
01126         if ($sort) {
01127             $sort = " ORDER BY $sort";
01128         }
01129         return $this->get_records_sql("SELECT $fields FROM {" . $table . "} $select $sort", $params, $limitfrom, $limitnum);
01130     }
01131 
01146     public abstract function get_records_sql($sql, array $params=null, $limitfrom=0, $limitnum=0);
01147 
01167     public function get_records_menu($table, array $conditions=null, $sort='', $fields='*', $limitfrom=0, $limitnum=0) {
01168         $menu = array();
01169         if ($records = $this->get_records($table, $conditions, $sort, $fields, $limitfrom, $limitnum)) {
01170             foreach ($records as $record) {
01171                 $record = (array)$record;
01172                 $key   = array_shift($record);
01173                 $value = array_shift($record);
01174                 $menu[$key] = $value;
01175             }
01176         }
01177         return $menu;
01178     }
01179 
01196     public function get_records_select_menu($table, $select, array $params=null, $sort='', $fields='*', $limitfrom=0, $limitnum=0) {
01197         $menu = array();
01198         if ($records = $this->get_records_select($table, $select, $params, $sort, $fields, $limitfrom, $limitnum)) {
01199             foreach ($records as $record) {
01200                 $record = (array)$record;
01201                 $key   = array_shift($record);
01202                 $value = array_shift($record);
01203                 $menu[$key] = $value;
01204             }
01205         }
01206         return $menu;
01207     }
01208 
01222     public function get_records_sql_menu($sql, array $params=null, $limitfrom=0, $limitnum=0) {
01223         $menu = array();
01224         if ($records = $this->get_records_sql($sql, $params, $limitfrom, $limitnum)) {
01225             foreach ($records as $record) {
01226                 $record = (array)$record;
01227                 $key   = array_shift($record);
01228                 $value = array_shift($record);
01229                 $menu[$key] = $value;
01230             }
01231         }
01232         return $menu;
01233     }
01234 
01247     public function get_record($table, array $conditions, $fields='*', $strictness=IGNORE_MISSING) {
01248         list($select, $params) = $this->where_clause($table, $conditions);
01249         return $this->get_record_select($table, $select, $params, $fields, $strictness);
01250     }
01251 
01264     public function get_record_select($table, $select, array $params=null, $fields='*', $strictness=IGNORE_MISSING) {
01265         if ($select) {
01266             $select = "WHERE $select";
01267         }
01268         try {
01269             return $this->get_record_sql("SELECT $fields FROM {" . $table . "} $select", $params, $strictness);
01270         } catch (dml_missing_record_exception $e) {
01271             // create new exception which will contain correct table name
01272             throw new dml_missing_record_exception($table, $e->sql, $e->params);
01273         }
01274     }
01275 
01290     public function get_record_sql($sql, array $params=null, $strictness=IGNORE_MISSING) {
01291         $strictness = (int)$strictness; // we support true/false for BC reasons too
01292         if ($strictness == IGNORE_MULTIPLE) {
01293             $count = 1;
01294         } else {
01295             $count = 0;
01296         }
01297         if (!$records = $this->get_records_sql($sql, $params, 0, $count)) {
01298             // not found
01299             if ($strictness == MUST_EXIST) {
01300                 throw new dml_missing_record_exception('', $sql, $params);
01301             }
01302             return false;
01303         }
01304 
01305         if (count($records) > 1) {
01306             if ($strictness == MUST_EXIST) {
01307                 throw new dml_multiple_records_exception($sql, $params);
01308             }
01309             debugging('Error: mdb->get_record() found more than one record!');
01310         }
01311 
01312         $return = reset($records);
01313         return $return;
01314     }
01315 
01328     public function get_field($table, $return, array $conditions, $strictness=IGNORE_MISSING) {
01329         list($select, $params) = $this->where_clause($table, $conditions);
01330         return $this->get_field_select($table, $return, $select, $params, $strictness);
01331     }
01332 
01346     public function get_field_select($table, $return, $select, array $params=null, $strictness=IGNORE_MISSING) {
01347         if ($select) {
01348             $select = "WHERE $select";
01349         }
01350         try {
01351             return $this->get_field_sql("SELECT $return FROM {" . $table . "} $select", $params, $strictness);
01352         } catch (dml_missing_record_exception $e) {
01353             // create new exception which will contain correct table name
01354             throw new dml_missing_record_exception($table, $e->sql, $e->params);
01355         }
01356     }
01357 
01371     public function get_field_sql($sql, array $params=null, $strictness=IGNORE_MISSING) {
01372         if (!$record = $this->get_record_sql($sql, $params, $strictness)) {
01373             return false;
01374         }
01375 
01376         $record = (array)$record;
01377         return reset($record); // first column
01378     }
01379 
01390     public function get_fieldset_select($table, $return, $select, array $params=null) {
01391         if ($select) {
01392             $select = "WHERE $select";
01393         }
01394         return $this->get_fieldset_sql("SELECT $return FROM {" . $table . "} $select", $params);
01395     }
01396 
01405     public abstract function get_fieldset_sql($sql, array $params=null);
01406 
01417     public abstract function insert_record_raw($table, $params, $returnid=true, $bulk=false, $customsequence=false);
01418 
01431     public abstract function insert_record($table, $dataobject, $returnid=true, $bulk=false);
01432 
01442     public abstract function import_record($table, $dataobject);
01443 
01452     public abstract function update_record_raw($table, $params, $bulk=false);
01453 
01467     public abstract function update_record($table, $dataobject, $bulk=false);
01468 
01469 
01480     public function set_field($table, $newfield, $newvalue, array $conditions=null) {
01481         list($select, $params) = $this->where_clause($table, $conditions);
01482         return $this->set_field_select($table, $newfield, $newvalue, $select, $params);
01483     }
01484 
01496     public abstract function set_field_select($table, $newfield, $newvalue, $select, array $params=null);
01497 
01498 
01507     public function count_records($table, array $conditions=null) {
01508         list($select, $params) = $this->where_clause($table, $conditions);
01509         return $this->count_records_select($table, $select, $params);
01510     }
01511 
01522     public function count_records_select($table, $select, array $params=null, $countitem="COUNT('x')") {
01523         if ($select) {
01524             $select = "WHERE $select";
01525         }
01526         return $this->count_records_sql("SELECT $countitem FROM {" . $table . "} $select", $params);
01527     }
01528 
01542     public function count_records_sql($sql, array $params=null) {
01543         if ($count = $this->get_field_sql($sql, $params)) {
01544             return $count;
01545         } else {
01546             return 0;
01547         }
01548     }
01549 
01561     public function record_exists($table, array $conditions) {
01562         list($select, $params) = $this->where_clause($table, $conditions);
01563         return $this->record_exists_select($table, $select, $params);
01564     }
01565 
01575     public function record_exists_select($table, $select, array $params=null) {
01576         if ($select) {
01577             $select = "WHERE $select";
01578         }
01579         return $this->record_exists_sql("SELECT 'x' FROM {" . $table . "} $select", $params);
01580     }
01581 
01593     public function record_exists_sql($sql, array $params=null) {
01594         $mrs = $this->get_recordset_sql($sql, $params, 0, 1);
01595         $return = $mrs->valid();
01596         $mrs->close();
01597         return $return;
01598     }
01599 
01609     public function delete_records($table, array $conditions=null) {
01610         // truncate is drop/create (DDL), not transactional safe,
01611         // so we don't use the shortcut within them. MDL-29198
01612         if (is_null($conditions) && empty($this->transactions)) {
01613             return $this->execute("TRUNCATE TABLE {".$table."}");
01614         }
01615         list($select, $params) = $this->where_clause($table, $conditions);
01616         return $this->delete_records_select($table, $select, $params);
01617     }
01618 
01628     public function delete_records_list($table, $field, array $values) {
01629         list($select, $params) = $this->where_clause_list($field, $values);
01630         if (empty($select)) {
01631             // nothing to delete
01632             return true;
01633         }
01634         return $this->delete_records_select($table, $select, $params);
01635     }
01636 
01646     public abstract function delete_records_select($table, $select, array $params=null);
01647 
01648 
01649 
01651 
01658     public function sql_null_from_clause() {
01659         return '';
01660     }
01661 
01673     public function sql_bitand($int1, $int2) {
01674         return '((' . $int1 . ') & (' . $int2 . '))';
01675     }
01676 
01684     public function sql_bitnot($int1) {
01685         return '(~(' . $int1 . '))';
01686     }
01687 
01699     public function sql_bitor($int1, $int2) {
01700         return '((' . $int1 . ') | (' . $int2 . '))';
01701     }
01702 
01714     public function sql_bitxor($int1, $int2) {
01715         return '((' . $int1 . ') ^ (' . $int2 . '))';
01716     }
01717 
01726     public function sql_modulo($int1, $int2) {
01727         return '((' . $int1 . ') % (' . $int2 . '))';
01728     }
01729 
01737     public function sql_ceil($fieldname) {
01738         return ' CEIL(' . $fieldname . ')';
01739     }
01740 
01751     public function sql_cast_char2int($fieldname, $text=false) {
01752         return ' ' . $fieldname . ' ';
01753     }
01754 
01765     public function sql_cast_char2real($fieldname, $text=false) {
01766         return ' ' . $fieldname . ' ';
01767     }
01768 
01778     public function sql_cast_2signed($fieldname) {
01779         return ' ' . $fieldname . ' ';
01780     }
01781 
01791     public function sql_compare_text($fieldname, $numchars=32) {
01792         return $this->sql_order_by_text($fieldname, $numchars);
01793     }
01794 
01806     public function sql_like($fieldname, $param, $casesensitive = true, $accentsensitive = true, $notlike = false, $escapechar = '\\') {
01807         if (strpos($param, '%') !== false) {
01808             debugging('Potential SQL injection detected, sql_like() expects bound parameters (? or :named)');
01809         }
01810         $LIKE = $notlike ? 'NOT LIKE' : 'LIKE';
01811         // by default ignore any sensitiveness - each database does it in a different way
01812         return "$fieldname $LIKE $param ESCAPE '$escapechar'";
01813     }
01814 
01821     public function sql_like_escape($text, $escapechar = '\\') {
01822         $text = str_replace('_', $escapechar.'_', $text);
01823         $text = str_replace('%', $escapechar.'%', $text);
01824         return $text;
01825     }
01826 
01837     public function sql_ilike() {
01838         debugging('sql_ilike() is deprecated, please use sql_like() instead');
01839         return 'LIKE';
01840     }
01841 
01850     public abstract function sql_concat();
01851 
01860     public abstract function sql_concat_join($separator="' '", $elements=array());
01861 
01871     function sql_fullname($first='firstname', $last='lastname') {
01872         return $this->sql_concat($first, "' '", $last);
01873     }
01874 
01886     public function sql_order_by_text($fieldname, $numchars=32) {
01887         return $fieldname;
01888     }
01889 
01895     public function sql_length($fieldname) {
01896         return ' LENGTH(' . $fieldname . ')';
01897     }
01898 
01908     public function sql_substr($expr, $start, $length=false) {
01909         if (count(func_get_args()) < 2) {
01910             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.');
01911         }
01912         if ($length === false) {
01913             return "SUBSTR($expr, $start)";
01914         } else {
01915             return "SUBSTR($expr, $start, $length)";
01916         }
01917     }
01918 
01928     public function sql_position($needle, $haystack) {
01929         // Implementation using standard SQL.
01930         return "POSITION(($needle) IN ($haystack))";
01931     }
01932 
01939     function sql_empty() {
01940         return '';
01941     }
01942 
01972     public function sql_isempty($tablename, $fieldname, $nullablefield, $textfield) {
01973         return " ($fieldname = '') ";
01974     }
01975 
02002     public function sql_isnotempty($tablename, $fieldname, $nullablefield, $textfield) {
02003         return ' ( NOT ' . $this->sql_isempty($tablename, $fieldname, $nullablefield, $textfield) . ') ';
02004     }
02005 
02010     public function sql_regex_supported() {
02011         return false;
02012     }
02013 
02019     public function sql_regex($positivematch=true) {
02020         return '';
02021     }
02022 
02024 
02034     protected function transactions_supported() {
02035         // protected for now, this might be changed to public if really necessary
02036         return true;
02037     }
02038 
02043     public function is_transaction_started() {
02044         return !empty($this->transactions);
02045     }
02046 
02052     public function transactions_forbidden() {
02053         if ($this->is_transaction_started()) {
02054             throw new dml_transaction_exception('This code can not be excecuted in transaction');
02055         }
02056     }
02057 
02075     public function start_delegated_transaction() {
02076         $transaction = new moodle_transaction($this);
02077         $this->transactions[] = $transaction;
02078         if (count($this->transactions) == 1) {
02079             $this->begin_transaction();
02080         }
02081         return $transaction;
02082     }
02083 
02089     protected abstract function begin_transaction();
02090 
02097     public function commit_delegated_transaction(moodle_transaction $transaction) {
02098         if ($transaction->is_disposed()) {
02099             throw new dml_transaction_exception('Transactions already disposed', $transaction);
02100         }
02101         // mark as disposed so that it can not be used again
02102         $transaction->dispose();
02103 
02104         if (empty($this->transactions)) {
02105             throw new dml_transaction_exception('Transaction not started', $transaction);
02106         }
02107 
02108         if ($this->force_rollback) {
02109             throw new dml_transaction_exception('Tried to commit transaction after lower level rollback', $transaction);
02110         }
02111 
02112         if ($transaction !== $this->transactions[count($this->transactions) - 1]) {
02113             // one incorrect commit at any level rollbacks everything
02114             $this->force_rollback = true;
02115             throw new dml_transaction_exception('Invalid transaction commit attempt', $transaction);
02116         }
02117 
02118         if (count($this->transactions) == 1) {
02119             // only commit the top most level
02120             $this->commit_transaction();
02121         }
02122         array_pop($this->transactions);
02123     }
02124 
02130     protected abstract function commit_transaction();
02131 
02144     public function rollback_delegated_transaction(moodle_transaction $transaction, Exception $e) {
02145         if ($transaction->is_disposed()) {
02146             throw new dml_transaction_exception('Transactions already disposed', $transaction);
02147         }
02148         // mark as disposed so that it can not be used again
02149         $transaction->dispose();
02150 
02151         // one rollback at any level rollbacks everything
02152         $this->force_rollback = true;
02153 
02154         if (empty($this->transactions) or $transaction !== $this->transactions[count($this->transactions) - 1]) {
02155             // this may or may not be a coding problem, better just rethrow the exception,
02156             // because we do not want to loose the original $e
02157             throw $e;
02158         }
02159 
02160         if (count($this->transactions) == 1) {
02161             // only rollback the top most level
02162             $this->rollback_transaction();
02163         }
02164         array_pop($this->transactions);
02165         if (empty($this->transactions)) {
02166             // finally top most level rolled back
02167             $this->force_rollback = false;
02168         }
02169         throw $e;
02170     }
02171 
02177     protected abstract function rollback_transaction();
02178 
02188     public function force_transaction_rollback() {
02189         if ($this->transactions) {
02190             try {
02191                 $this->rollback_transaction();
02192             } catch (dml_exception $e) {
02193                 // ignore any sql errors here, the connection might be broken
02194             }
02195         }
02196 
02197         // now enable transactions again
02198         $this->transactions = array(); // unfortunately all unfinished exceptions are kept in memory
02199         $this->force_rollback = false;
02200     }
02201 
02203 
02207     public function session_lock_supported() {
02208         return false;
02209     }
02210 
02217     public function get_session_lock($rowid, $timeout) {
02218         $this->used_for_db_sessions = true;
02219     }
02220 
02226     public function release_session_lock($rowid) {
02227     }
02228 
02230 
02234     public function perf_get_reads() {
02235         return $this->reads;
02236     }
02237 
02242     public function perf_get_writes() {
02243         return $this->writes;
02244     }
02245 
02250     public function perf_get_queries() {
02251         return $this->writes + $this->reads;
02252     }
02253 }
 All Data Structures Namespaces Files Functions Variables Enumerations