From: skodak Date: Thu, 8 Oct 2009 22:34:34 +0000 (+0000) Subject: MDL-19474 One year and a half ago we started designing and implementing our new datab... X-Git-Url: http://git.mjollnir.org/gw?a=commitdiff_plain;h=eb123debc6279c0e7fe92d7cb8979f4db651a46c;p=moodle.git MDL-19474 One year and a half ago we started designing and implementing our new databse abstraction layer which included brand new moodle native drivers. Today we are finally removing old legacy drivers that were still using adodb internally. From now on the adodb will be used only in some authentication and enrolment plugins, we are probably going to drop adodb there too later in 2.x. Thanks everybody who helped test and improve our new drivers, even bigger thanks to all the great developers who created ADOdb! --- diff --git a/lib/dml/adodb_moodle_database.php b/lib/dml/adodb_moodle_database.php deleted file mode 100644 index d4b6ac0b44..0000000000 --- a/lib/dml/adodb_moodle_database.php +++ /dev/null @@ -1,590 +0,0 @@ -. - - -/** - * General adodb database driver. - * - * TODO: delete before branching 2.0 - * - * @package moodlecore - * @subpackage DML - * @copyright 2008 Petr Skoda (http://skodak.org) - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -require_once($CFG->libdir.'/dml/moodle_database.php'); -require_once($CFG->libdir.'/dml/adodb_moodle_recordset.php'); - -/** - * Abstract moodle database class - */ -abstract class adodb_moodle_database extends moodle_database { - - protected $adodb; - - /** - * Returns general database library name - * Note: can be used before connect() - * @return string db type adodb, pdo, native - */ - protected function get_dblibrary() { - return 'adodb'; - } - - /** - * Returns localised database type name - * Note: can be used before connect() - * @return string - */ - public function get_name() { - $dbtype = $this->get_dbtype(); - return get_string($dbtype, 'install'); - } - - /** - * Returns localised database configuration help. - * Note: can be used before connect() - * @return string - */ - public function get_configuration_help() { - $dbtype = $this->get_dbtype(); - return get_string($dbtype.'help', 'install'); - } - - /** - * Adodb preconnection routines, ususally sets up needed defines; - */ - protected abstract function preconfigure_dbconnection(); - - public function connect($dbhost, $dbuser, $dbpass, $dbname, $prefix, array $dboptions=null) { - global $CFG; - - $driverstatus = $this->driver_installed(); - - if ($driverstatus !== true) { - throw new dml_exception('dbdriverproblem', $driverstatus); - } - - ob_start(); - - $this->store_settings($dbhost, $dbuser, $dbpass, $dbname, $prefix, $dboptions); - - $this->preconfigure_dbconnection(); - - require_once($CFG->libdir.'/adodb/adodb.inc.php'); - - $this->adodb = ADONewConnection($this->get_dbtype()); - - // See MDL-6760 for why this is necessary. In Moodle 1.8, once we start using NULLs properly, - // we probably want to change this value to ''. - $this->adodb->null2null = 'A long random string that will never, ever match something we want to insert into the database, I hope. \''; - - - if (!empty($this->dboptions['dbpersist'])) { // Use persistent connection - $connected = $this->adodb->PConnect($this->dbhost, $this->dbuser, $this->dbpass, $this->dbname); - } else { // Use single connection - $connected = $this->adodb->Connect($this->dbhost, $this->dbuser, $this->dbpass, $this->dbname); - } - - $dberr = ob_get_contents(); - ob_end_clean(); - - if (!$connected) { - throw new dml_connection_exception($dberr); - } - - $this->configure_dbconnection(); - - return true; - } - - /** - * Adodb post connection routines, usually sets up encoding,e tc. - */ - protected abstract function configure_dbconnection(); - - /** - * Close database connection and release all resources - * and memory (especially circular memory references). - * Do NOT use connect() again, create a new instance if needed. - */ - public function dispose() { - parent::dispose(); // Call parent dispose to write/close session and other common stuff before clossing conn - if ($this->adodb) { - $this->adodb->Close(); - } - } - - public function query_log($error=false) { - // no logging for obsoleted adodb - it would require a lot more fixing - return; - } - - /** - * Returns database server info array - * @return array - */ - public function get_server_info() { - $this->query_start("--adodb-ServerInfo", null, SQL_QUERY_AUX); - $info = $this->adodb->ServerInfo(); - $this->query_end(true); - return $info; - } - - /** - * Return tables in database WITHOUT current prefix - * @return array of table names in lowercase and without prefix - */ - public function get_tables($usecache=true) { - $this->query_start("--adodb-MetaTables", null, SQL_QUERY_AUX); - $metatables = $this->adodb->MetaTables(); - $this->query_end(true); - - $tables = array(); - - foreach ($metatables as $table) { - $table = strtolower($table); - if (empty($this->prefix) || strpos($table, $this->prefix) === 0) { - $tablename = substr($table, strlen($this->prefix)); - $tables[$tablename] = $tablename; - } - } - return $tables; - } - - /** - * Return table indexes - everything lowercased - * @return array of arrays - */ - public function get_indexes($table) { - $this->query_start("--adodb-MetaIndexes", null, SQL_QUERY_AUX); - $indexes = $this->adodb->MetaIndexes($this->prefix.$table); - $this->query_end(true); - - if (!$indexes) { - return array(); - } - $indexes = array_change_key_case($indexes, CASE_LOWER); - foreach ($indexes as $indexname => $index) { - $columns = $index['columns']; - /// column names always lowercase - $columns = array_map('strtolower', $columns); - $indexes[$indexname]['columns'] = $columns; - } - - return $indexes; - } - - public function get_columns($table, $usecache=true) { - if ($usecache and isset($this->columns[$table])) { - return $this->columns[$table]; - } - - $this->query_start("--adodb-MetaColumns", null, SQL_QUERY_AUX); - $columns = $this->adodb->MetaColumns($this->prefix.$table); - $this->query_end(true); - - if (!$columns) { - return array(); - } - - $this->columns[$table] = array(); - - foreach ($columns as $column) { - // colum names must be lowercase - $column->meta_type = substr($this->adodb->MetaType($column), 0 ,1); // only 1 character - if (!empty($column->enums)) { - // hack: fix the 'quotes' surrounding the values itroduced by adodb - foreach ($column->enums as $key=>$value) { - if (strpos($value, "'") === 0 and strlen($value) > 2) { - $column->enums[$key] = substr($value, 1, strlen($value)-2); - } - } - } - $this->columns[$table][$column->name] = new database_column_info($column); - } - - return $this->columns[$table]; - } - - public function get_last_error() { - return $this->adodb->ErrorMsg(); - } - - /** - * Do NOT use in code, to be used by database_manager only! - * @param string $sql query - * @return bool true - * @throws dml_exception if error - */ - public function change_database_structure($sql) { - $this->reset_caches(); - - $this->query_start($sql, null, SQL_QUERY_STRUCTURE); - $rs = $this->adodb->Execute($sql); - $this->query_end($rs); - - $rs->Close(); - - return true; - } - - /** - * Execute general sql query. Should be used only when no other method suitable. - * Do NOT use this to make changes in db structure, use database_manager::execute_sql() instead! - * @param string $sql query - * @param array $params query parameters - * @return bool true - * @throws dml_exception if error - */ - public function execute($sql, array $params=null) { - list($sql, $params, $type) = $this->fix_sql_params($sql, $params); - - if (strpos($sql, ';') !== false) { - throw new coding_exception('moodle_database::execute() Multiple sql statements found or bound parameters not used properly in query!'); - } - - $this->query_start($sql, $params, SQL_QUERY_UPDATE); - $rs = $this->adodb->Execute($sql, $params); - $this->query_end($rs); - - $rs->Close(); - - return true; - } - - /** - * Insert new record into database, as fast as possible, no safety checks, lobs not supported. - * @param string $table name - * @param mixed $params data record as object or array - * @param bool $returnit return it of inserted record - * @param bool $bulk true means repeated inserts expected - * @param bool $customsequence true if 'id' included in $params, disables $returnid - * @return mixed true or new id - * @throws dml_exception if error - */ - public function insert_record_raw($table, $params, $returnid=true, $bulk=false, $customsequence=false) { - if (!is_array($params)) { - $params = (array)$params; - } - - if ($customsequence) { - if (!isset($params['id'])) { - throw new coding_exception('moodle_database::insert_record_raw() id field must be specified if custom sequences used.'); - } - $returnid = false; - } else { - unset($params['id']); - } - - if (empty($params)) { - throw new coding_exception('moodle_database::insert_record_raw() no fields found.'); - } - - $fields = implode(',', array_keys($params)); - $qms = array_fill(0, count($params), '?'); - $qms = implode(',', $qms); - - $sql = "INSERT INTO {$this->prefix}$table ($fields) VALUES($qms)"; - - $this->query_start($sql, $params, SQL_QUERY_INSERT); - $rs = $this->adodb->Execute($sql, $params); - $this->query_end($rs); - $rs->Close(); - - if (!$returnid) { - return true; - } - - if (!$id = $this->adodb->Insert_ID()) { - throw new dml_write_exception('unknown error fetching inserted id'); - } - - return (int)$id; - } - - /** - * Update record in database, as fast as possible, no safety checks, lobs not supported. - * @param string $table name - * @param mixed $params data record as object or array - * @param bool true means repeated updates expected - * @return bool true - * @throws dml_exception if error - */ - public function update_record_raw($table, $params, $bulk=false) { - if (!is_array($params)) { - $params = (array)$params; - } - if (!isset($params['id'])) { - throw new coding_exception('moodle_database::update_record_raw() id field must be specified.'); - } - $id = $params['id']; - unset($params['id']); - - if (empty($params)) { - throw new coding_exception('moodle_database::update_record_raw() no fields found.'); - } - - $sets = array(); - foreach ($params as $field=>$value) { - $sets[] = "$field = ?"; - } - - $params[] = $id; // last ? in WHERE condition - - $sets = implode(',', $sets); - $sql = "UPDATE {$this->prefix}$table SET $sets WHERE id=?"; - - $this->query_start($sql, $params, SQL_QUERY_UPDATE); - $rs = $this->adodb->Execute($sql, $params); - $this->query_end($rs); - $rs->Close(); - - return true; - } - - /** - * Delete one or more records from a table - * - * @param string $table The database table to be checked against. - * @param string $select A fragment of SQL to be used in a where clause in the SQL call (used to define the selection criteria). - * @param array $params array of sql parameters - * @return bool true - * @throws dml_exception if error - */ - public function delete_records_select($table, $select, array $params=null) { - if ($select) { - $select = "WHERE $select"; - } - $sql = "DELETE FROM {$this->prefix}$table $select"; - - list($sql, $params, $type) = $this->fix_sql_params($sql, $params); - - $this->query_start($sql, $params, SQL_QUERY_UPDATE); - $rs = $this->adodb->Execute($sql, $params); - $this->query_end($rs); - $rs->Close(); - - return true; - } - - /** - * Get a number of records as an moodle_recordset. $sql must be a complete SQL query. - * Since this method is a little less readable, use of it should be restricted to - * code where it's possible there might be large datasets being returned. For known - * small datasets use get_records_sql - it leads to simpler code. - * - * The return type is as for @see function get_recordset. - * - * @param string $sql the SQL select query to execute. - * @param array $params array of sql parameters - * @param int $limitfrom return a subset of records, starting at this point (optional, required if $limitnum is set). - * @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set). - * @return object moodle_recordset instance - * @throws dml_exception if error - */ - public function get_recordset_sql($sql, array $params=null, $limitfrom=0, $limitnum=0) { - list($sql, $params, $type) = $this->fix_sql_params($sql, $params); - - if ($limitfrom || $limitnum) { - ///Special case, 0 must be -1 for ADOdb - $limitfrom = empty($limitfrom) ? -1 : $limitfrom; - $limitnum = empty($limitnum) ? -1 : $limitnum; - $this->query_start($sql." --LIMIT $limitfrom, $limitnum", $params, SQL_QUERY_SELECT); - $rs = $this->adodb->SelectLimit($sql, $limitnum, $limitfrom, $params); - $this->query_end($rs); - return $this->create_recordset($rs); - } - - $this->query_start($sql, $params, SQL_QUERY_SELECT); - $rs = $this->adodb->Execute($sql, $params); - $this->query_end($rs); - return $this->create_recordset($rs); - } - - protected function create_recordset($rs) { - return new adodb_moodle_recordset($rs); - } - - /** - * Get a number of records as an array of objects. - * - * Return value as for @see function get_records. - * - * @param string $sql the SQL select query to execute. The first column of this SELECT statement - * must be a unique value (usually the 'id' field), as it will be used as the key of the - * returned array. - * @param array $params array of sql parameters - * @param int $limitfrom return a subset of records, starting at this point (optional, required if $limitnum is set). - * @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set). - * @return array of objects indexed by first column - * @throws dml_exception if error - */ - public function get_records_sql($sql, array $params=null, $limitfrom=0, $limitnum=0) { - list($sql, $params, $type) = $this->fix_sql_params($sql, $params); - - $rs = null; - if ($limitfrom || $limitnum) { - ///Special case, 0 must be -1 for ADOdb - $limitfrom = empty($limitfrom) ? -1 : $limitfrom; - $limitnum = empty($limitnum) ? -1 : $limitnum; - $this->query_start($sql." --LIMIT $limitfrom, $limitnum", $params, SQL_QUERY_SELECT); - $rs = $this->adodb->SelectLimit($sql, $limitnum, $limitfrom, $params); - $this->query_end($rs); - } else { - $this->query_start($sql, $params, SQL_QUERY_SELECT); - $rs = $this->adodb->Execute($sql, $params); - $this->query_end($rs); - } - $return = $this->adodb_recordset_to_array($rs); - $rs->Close(); - - return $return; - } - - /** - * Selects rows and return values of first column as array. - * - * @param string $sql The SQL query - * @param array $params array of sql parameters - * @return mixed array of values - * @throws dml_exception if error - */ - public function get_fieldset_sql($sql, array $params=null) { - list($sql, $params, $type) = $this->fix_sql_params($sql, $params); - - $this->query_start($sql, $params, SQL_QUERY_SELECT); - $rs = $this->adodb->Execute($sql, $params); - $this->query_end($rs); - - $results = array(); - while (!$rs->EOF) { - $res = reset($rs->fields); - $results[] = $res; - $rs->MoveNext(); - } - $rs->Close(); - return $results; - } - - protected function adodb_recordset_to_array($rs) { - $debugging = debugging('', DEBUG_DEVELOPER); - - if ($rs->EOF) { - // BIIIG change here - return empty array() if nothing found (2.0) - return array(); - } - - $objects = array(); - /// First of all, we are going to get the name of the first column - /// to introduce it back after transforming the recordset to assoc array - /// See http://docs.moodle.org/en/XMLDB_Problems, fetch mode problem. - $firstcolumn = $rs->FetchField(0); - /// Get the whole associative array - if ($records = $rs->GetAssoc(true)) { - foreach ($records as $key => $record) { - $record = array($firstcolumn->name=>$key) + $record; /// Re-add the assoc field (as FIRST element since 2.0) - $objects[$key] = (object) $record; /// To object - } - if ($debugging) { - if (count($objects) != $rs->_numOfRows) { - debugging("Did you remember to make the first column something unique in your call to get_records? Duplicate values found in column '".$firstcolumn->name."'.", DEBUG_DEVELOPER); - } - } - return $objects; - /// Fallback in case we only have 1 field in the recordset. MDL-5877 - } else if ($rs->_numOfFields == 1 and $records = $rs->GetRows()) { - foreach ($records as $key => $record) { - if ($debugging && array_key_exists($record[$firstcolumn->name], $objects)) { - debugging("Did you remember to make the first column something unique in your call to get_records? Duplicate value '".$record[$firstcolumn->name]."' found in column '".$firstcolumn->name."'.", DEBUG_DEVELOPER); - } - $objects[$record[$firstcolumn->name]] = (object) $record; /// The key is the first column value (like Assoc) - } - return $objects; - } else { - // weird error? - return false; - } - } - - public function sql_concat() { - $args = func_get_args(); - return call_user_func_array(array($this->adodb, 'Concat'), $args); - } - - public function sql_concat_join($separator="' '", $elements=array()) { - // Intersperse $elements in the array. - // Add items to the array on the fly, walking it - // _backwards_ splicing the elements in. The loop definition - // should skip first and last positions. - for ($n=count($elements)-1; $n > 0 ; $n--) { - array_splice($elements, $n, 0, $separator); - } - return call_user_func_array(array($this->adodb, 'Concat'), $elements); - } - - public function begin_sql() { - if (!parent::begin_sql()) { - return false; - } - $this->adodb->BeginTrans(); - return true; - } - public function commit_sql() { - if (!parent::commit_sql()) { - return false; - } - $this->adodb->CommitTrans(); - return true; - } - public function rollback_sql() { - if (!parent::rollback_sql()) { - return false; - } - $this->adodb->RollbackTrans(); - return true; - } - - /** - * Very ugly hack which emulates bound parameters in mssql queries - * where params not supported (UpdateBlob) :-( - */ - protected function emulate_bound_params($sql, array $params=null) { - if (empty($params)) { - return $sql; - } - /// ok, we have verified sql statement with ? and correct number of params - $return = strtok($sql, '?'); - foreach ($params as $param) { - if (is_bool($param)) { - $return .= (int)$param; - } else if (is_null($param)) { - $return .= 'NULL'; - } else if (is_number($param)) { // we can not use is_numeric() because it eats leading zeros from strings like 0045646 - $return .= $param; - } else if (is_float($param)) { - $return .= $param; - } else { - $param = $this->adodb->qstr($param); - $return .= "$param"; - } - $return .= strtok('?'); - } - return $return; - } -} diff --git a/lib/dml/adodb_moodle_recordset.php b/lib/dml/adodb_moodle_recordset.php deleted file mode 100644 index a3188e6fa8..0000000000 --- a/lib/dml/adodb_moodle_recordset.php +++ /dev/null @@ -1,64 +0,0 @@ -. - - -/** - * General adodb recordset. - * - * TODO: delete before branching 2.0 - * - * @package moodlecore - * @subpackage DML - * @copyright 2008 Petr Skoda (http://skodak.org) - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -require_once($CFG->libdir.'/dml/moodle_recordset.php'); - -/** - * Adodb basic moodle recordset class - */ -class adodb_moodle_recordset extends moodle_recordset { - - protected $rs; ///ADOdb recordset - - public function __construct($rs) { - $this->rs = $rs; - } - - public function current() { - return (object)$this->rs->fields; - } - - public function key() { - /// return first column value as key - return reset($this->rs->fields); - } - - public function next() { - $this->rs->MoveNext(); - } - - public function valid() { - return !$this->rs->EOF; - } - - public function close() { - $this->rs->Close(); - $this->rs = null; - } -} diff --git a/lib/dml/moodle_database.php b/lib/dml/moodle_database.php index 50c453729a..ca376eb499 100644 --- a/lib/dml/moodle_database.php +++ b/lib/dml/moodle_database.php @@ -184,7 +184,7 @@ abstract class moodle_database { /** * Returns general database library name * Note: can be used before connect() - * @return string db type adodb, pdo, native + * @return string db type pdo, native */ protected abstract function get_dblibrary(); diff --git a/lib/dml/mssql_adodb_moodle_database.php b/lib/dml/mssql_adodb_moodle_database.php deleted file mode 100644 index 666348ace0..0000000000 --- a/lib/dml/mssql_adodb_moodle_database.php +++ /dev/null @@ -1,535 +0,0 @@ -. - - -/** - * MSSQL database class using adodb backend - * - * TODO: delete before branching 2.0 - * - * @package moodlecore - * @subpackage DML - * @copyright 2008 Petr Skoda (http://skodak.org) - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -require_once($CFG->libdir.'/dml/moodle_database.php'); -require_once($CFG->libdir.'/dml/adodb_moodle_database.php'); - -/** - * MSSQL database class using adodb backend - */ -class mssql_adodb_moodle_database extends adodb_moodle_database { - /** - * Ungly mssql hack needed for temp table names starting with '#' - */ - public $temptables; - - public function connect($dbhost, $dbuser, $dbpass, $dbname, $prefix, array $dboptions=null) { - if ($prefix == '' and !$this->external) { - //Enforce prefixes for everybody but mysql - throw new dml_exception('prefixcannotbeempty', $this->get_dbfamily()); - } - return parent::connect($dbhost, $dbuser, $dbpass, $dbname, $prefix, $dboptions); - } - - /** - * Detects if all needed PHP stuff installed. - * Do not connect to connect to db if this test fails. - * @return mixed true if ok, string if something - */ - public function driver_installed() { - if (!function_exists('mssql_connect')) { - return get_string('mssqlextensionisnotpresentinphp', 'install'); - } - return true; - } - - protected function preconfigure_dbconnection() { - if (!defined('ADODB_ASSOC_CASE')) { - define ('ADODB_ASSOC_CASE', 2); - } - } - - protected function configure_dbconnection() { - $this->adodb->SetFetchMode(ADODB_FETCH_ASSOC); - - /// No need to set charset. It must be specified in the driver conf - /// Allow quoted identifiers - $sql = "SET QUOTED_IDENTIFIER ON"; - $this->query_start($sql, null, SQL_QUERY_AUX); - $rs = $this->adodb->Execute($sql); - $this->query_end($rs); - /// Force ANSI nulls so the NULL check was done by IS NULL and NOT IS NULL - /// instead of equal(=) and distinct(<>) simbols - $sql = "SET ANSI_NULLS ON"; - $this->query_start($sql, null, SQL_QUERY_AUX); - $rs = $this->adodb->Execute($sql); - $this->query_end($rs); - - return true; - } - - /** - * Returns database family type - * @return string db family name (mysql, postgres, mssql, oracle, etc.) - */ - public function get_dbfamily() { - return 'mssql'; - } - - /** - * Returns database type - * @return string db type mysql, mysqli, postgres7 - */ - protected function get_dbtype() { - return 'mssql'; - } - - /** - * Returns localised database description - * Note: can be used before connect() - * @return string - */ - public function get_configuration_hints() { - $str = get_string('databasesettingssub_mssql', 'install'); - $str .= "

'; - $str .= get_string('moodledocslink', 'install') . '

'; - return $str; - } - - /** - * Converts short table name {tablename} to real table name - * @param string sql - * @return string sql - */ - protected function fix_table_names($sql) { - // look for temporary tables, they must start with # - if (preg_match_all('/\{([a-z][a-z0-9_]*)\}/', $sql, $matches)) { - foreach($matches[0] as $key=>$match) { - $name = $matches[1][$key]; - if (empty($this->temptables[$name])) { - $sql = str_replace($match, $this->prefix.$name, $sql); - } else { - $sql = str_replace($match, '#'.$this->prefix.$name, $sql); - } - } - } - return $sql; - } - - /** - * Returns supported query parameter types - * @return bitmask - */ - protected function allowed_param_types() { - return SQL_PARAMS_QM; - } - - public function sql_ceil($fieldname) { - return ' CEILING(' . $fieldname . ')'; - } - - public function sql_cast_char2int($fieldname, $text=false) { - if (!$text) { - return ' CAST(' . $fieldname . ' AS INT) '; - } else { - return ' CAST(' . $this->sql_compare_text($fieldname) . ' AS INT) '; - } - } - - public function sql_order_by_text($fieldname, $numchars=32) { - return ' CONVERT(varchar, ' . $fieldname . ', ' . $numchars . ')'; - } - - /** - * Returns the SQL text to be used to calculate the length in characters of one expression. - * @param string fieldname or expression to calculate its length in characters. - * @return string the piece of SQL code to be used in the statement. - */ - public function sql_length($fieldname) { - return ' LEN(' . $fieldname . ')'; - } - - /** - * Returns the SQL for returning searching one string for the location of another. - */ - public function sql_position($needle, $haystack) { - return "CHARINDEX(($needle), ($haystack))"; - } - - public function sql_isempty($tablename, $fieldname, $nullablefield, $textfield) { - if ($textfield) { - return $this->sql_compare_text($fieldname)." = '' "; - } else { - return " $fieldname = '' "; - } - } - - /** - * Returns the proper substr() function for each DB. - * NOTE: this was originally returning only function name - * - * @param string $expr some string field, no aggregates - * @param mixed $start integer or expresion evaluating to int - * @param mixed $length optional integer or expresion evaluating to int - * @return string sql fragment - */ - public function sql_substr($expr, $start, $length=false) { - if (count(func_get_args()) < 2) { - throw new coding_exception('moodle_database::sql_substr() requires at least two parameters', 'Originaly this function was only returning name of SQL substring function, it now requires all parameters.'); - } - if ($length === false) { - return "SUBSTRING($expr, $start, (LEN($expr) - $start + 1))"; - } else { - return "SUBSTRING($expr, $start, $length)"; - } - } - - /** - * Update a record in a table - * - * $dataobject is an object containing needed data - * Relies on $dataobject having a variable "id" to - * specify the record to update - * - * @param string $table The database table to be checked against. - * @param object $dataobject An object with contents equal to fieldname=>fieldvalue. Must have an entry for 'id' to map to the table specified. - * @param bool true means repeated updates expected - * @return bool true - * @throws dml_exception if error - */ - public function update_record($table, $dataobject, $bulk=false) { - if (!is_object($dataobject)) { - $dataobject = (object)$dataobject; - } - - $columns = $this->get_columns($table); - $cleaned = array(); - $blobs = array(); - - foreach ($dataobject as $field=>$value) { - if (!isset($columns[$field])) { /// Non-existing table field, skip it - continue; - } - $column = $columns[$field]; - - if (is_bool($value)) { /// Always, convert boolean to int - $value = (int)$value; - } - - if ($column->meta_type == 'B') { /// BLOBs (IMAGE) columns need to be updated apart - if (!is_null($value)) { /// If value not null, add it to the list of BLOBs to update later - $blobs[$field] = $value; - $value = null; /// Set the default value to be inserted in first instance - } - - } else if ($column->meta_type == 'X') { /// MSSQL doesn't cast from int to text, so if text column - if (is_numeric($value)) { /// and is numeric value - $value = (string)$value; /// cast to string - } - - } else if ($value === '') { - if ($column->meta_type == 'I' or $column->meta_type == 'F' or $column->meta_type == 'N') { - $value = 0; // prevent '' problems in numeric fields - } - } - $cleaned[$field] = $value; - } - - if (empty($blobs)) { /// Without BLOBs, execute the raw update and return - return $this->update_record_raw($table, $cleaned, $bulk); - } - - /// We have BLOBs to postprocess, execute the raw update and then update blobs - $this->update_record_raw($table, $cleaned, $bulk); - - foreach ($blobs as $key=>$value) { - $this->query_start('--adodb-UpdateBlob', null, SQL_QUERY_UPDATE); - $result = $this->adodb->UpdateBlob($this->prefix.$table, $key, $value, "id = {$dataobject->id}"); - $this->query_end($result); - } - - return true; - } - - /** - * Set a single field in every table row where the select statement evaluates to true. - * - * @param string $table The database table to be checked against. - * @param string $newfield the field to set. - * @param string $newvalue the value to set the field to. - * @param string $select A fragment of SQL to be used in a where clause in the SQL call. - * @param array $params array of sql parameters - * @return bool true - * @throws dml_exception if error - */ - public function set_field_select($table, $newfield, $newvalue, $select, array $params=null) { - - if (is_null($params)) { - $params = array(); - } - list($select, $params, $type) = $this->fix_sql_params($select, $params); - - $columns = $this->get_columns($table); - $column = $columns[$newfield]; - - if ($column->meta_type == 'B') { /// If the column is a BLOB (IMAGE) - /// Update BLOB column and return - $select = $this->emulate_bound_params($select, $params); // adodb does not use bound parameters for blob updates :-( - $this->query_start('--adodb-UpdateBlob', null, SQL_QUERY_UPDATE); - $result = $this->adodb->UpdateBlob($this->prefix.$table, $newfield, $newvalue, $select); - $this->query_end($result); - return true; - } - - /// Arrived here, normal update (without BLOBs) - - if (is_bool($newvalue)) { /// Always, convert boolean to int - $newvalue = (int)$newvalue; - } - - if (is_null($newvalue)) { - $newfield = "$newfield = NULL"; - } else { - if ($column->meta_type == 'X') { /// MSSQL doesn't cast from int to text, so if text column - if (is_numeric($newvalue)) { /// and is numeric value - $newvalue = (string)$newvalue; /// cast to string in PHP - } - - } else if ($newvalue === '') { - if ($column->meta_type == 'I' or $column->meta_type == 'F' or $column->meta_type == 'N') { - $newvalue = 0; // prevent '' problems in numeric fields - } - } - - $newfield = "$newfield = ?"; - array_unshift($params, $newvalue); // add as first param - } - $select = !empty($select) ? "WHERE $select" : ''; - $sql = "UPDATE {$this->prefix}$table SET $newfield $select"; - - $this->query_start($sql, $params, SQL_QUERY_UPDATE); - $rs = $this->adodb->Execute($sql, $params); - $this->query_end($rs); - $rs->Close(); - - return true; - } - - /** - * Insert a record into a table and return the "id" field if required, - * Some conversions and safety checks are carried out. Lobs are supported. - * If the return ID isn't required, then this just reports success as true/false. - * $data is an object containing needed data - * @param string $table The database table to be inserted into - * @param object $data A data object with values for one or more fields in the record - * @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. - * @param bool $bulk true means repeated inserts expected - * @return mixed true or new id - * @throws dml_exception if error - */ - public function insert_record($table, $dataobject, $returnid=true, $bulk=false) { - if (!is_object($dataobject)) { - $dataobject = (object)$dataobject; - } - - unset($dataobject->id); - - $columns = $this->get_columns($table); - $cleaned = array(); - $blobs = array(); - - foreach ($dataobject as $field=>$value) { - if (!isset($columns[$field])) { /// Non-existing table field, skip it - continue; - } - $column = $columns[$field]; - - if (is_bool($value)) { /// Always, convert boolean to int - $value = (int)$value; - } - - if ($column->meta_type == 'B') { /// BLOBs (IMAGE) columns need to be updated apart - if (!is_null($value)) { /// If value not null, add it to the list of BLOBs to update later - $blobs[$field] = $value; - $value = null; /// Set the default value to be inserted in first instance - } - - } else if ($column->meta_type == 'X') { /// MSSQL doesn't cast from int to text, so if text column - if (is_numeric($value)) { /// and is numeric value - $value = (string)$value; /// cast to string - } - - } else if ($value === '') { - if ($column->meta_type == 'I' or $column->meta_type == 'F' or $column->meta_type == 'N') { - $value = 0; // prevent '' problems in numeric fields - } - } - $cleaned[$field] = $value; - } - - if (empty($blobs)) { /// Without BLOBs, execute the raw insert and return - return $this->insert_record_raw($table, $cleaned, $returnid, $bulk); - } - - /// We have BLOBs to postprocess, insert the raw record fetching the id to be used later - $id = $this->insert_record_raw($table, $cleaned, true, $bulk); - - foreach ($blobs as $key=>$value) { - $this->query_start('--adodb-UpdateBlob', null, SQL_QUERY_UPDATE); - $result = $this->adodb->UpdateBlob($this->prefix.$table, $key, $value, "id = $id"); - $this->query_end($result); - } - - return ($returnid ? $id : true); - } - - /** - * Import a record into a table, id field is required. - * Basic safety checks only. Lobs are supported. - * @param string $table name of database table to be inserted into - * @param mixed $dataobject object or array with fields in the record - * @return bool true - * @throws dml_exception if error - */ - public function import_record($table, $dataobject) { - $dataobject = (object)$dataobject; - - $columns = $this->get_columns($table); - $cleaned = array(); - $blobs = array(); - - foreach ($dataobject as $field=>$value) { - if (!isset($columns[$field])) { // Non-existing table field, skip it - continue; - } - $column = $columns[$field]; - if ($column->meta_type == 'B') { // BLOBs (IMAGE) columns need to be updated apart - if (!is_null($value)) { // If value not null, add it to the list of BLOBs to update later - $blobs[$field] = $value; - $value = null; // Set the default value to be inserted in first instance - } - } else if ($column->meta_type == 'X') { // MSSQL doesn't cast from int to text, so if text column - if (is_numeric($value)) { // and is numeric value - $value = (string)$value; // cast to string - } - } - - $cleaned[$field] = $value; - } - - $this->insert_record_raw($table, $cleaned, false, true, true); - - if (empty($blobs)) { - return true; - } - - /// We have BLOBs to postprocess - - foreach ($blobs as $key=>$value) { - $this->query_start('--adodb-UpdateBlob', null, SQL_QUERY_UPDATE); - $result = $this->adodb->UpdateBlob($this->prefix.$table, $key, $value, "id = $id"); - $this->query_end($result); - } - - return true; - } - - public function get_columns($table, $usecache=true) { - if ($usecache and isset($this->columns[$table])) { - return $this->columns[$table]; - } - - $this->columns[$table] = array(); - - $tablename = strtoupper($this->prefix.$table); - - $sql = "SELECT column_name AS name, - data_type AS type, - numeric_precision AS max_length, - character_maximum_length AS char_max_length, - numeric_scale AS scale, - is_nullable AS is_nullable, - columnproperty(object_id(quotename(TABLE_SCHEMA) + '.' + - quotename(TABLE_NAME)), COLUMN_NAME, 'IsIdentity') AS auto_increment, - column_default AS default_value - FROM INFORMATION_SCHEMA.Columns - WHERE TABLE_NAME = '$tablename' - ORDER BY ordinal_position"; - - $this->query_start($sql, null, SQL_QUERY_AUX); - $rs = $this->adodb->Execute($sql); - $this->query_end($rs); - - $columns = $this->adodb_recordset_to_array($rs); - $rs->Close(); - - if (!$columns) { - return array(); - } - - $this->columns[$table] = array(); - - foreach ($columns as $column) { - $dict = NewDataDictionary($this->adodb); // use dictionary because mssql driver lacks proper MetaType() function - $column->meta_type = substr($dict->MetaType($column), 0 ,1); // only 1 character - $column->meta_type = $column->meta_type == 'F' ? 'N' : $column->meta_type; // floats are numbers for us - $column->meta_type = ($column->auto_increment && $column->meta_type == 'I') ? 'R' : $column->meta_type; // Proper 'R' - $column->max_length = $column->meta_type == 'C' ? $column->char_max_length : $column->max_length; //Pick correct for Chars - $column->max_length = ($column->meta_type == 'X' || $column->meta_type == 'B') ? -1 : $column->max_length; // -1 for xLOB - $column->auto_increment = $column->auto_increment ? true : false; - $column->not_null = $column->is_nullable == 'NO' ? true : false; // Process not_null - $column->has_default = !empty($column->default_value); // Calculate has_default - $column->default_value = preg_replace("/^[\(N]+[']?(.*?)[']?[\)]+$/", '\\1', $column->default_value); // Clean default - $this->columns[$table][$column->name] = new database_column_info($column); - } - - return $this->columns[$table]; - } - - public function get_indexes($table) { - $indexes = parent::get_indexes($table); // AdoDB fails here, returning PK index while it shouldn't - - // Going to look for the primary key to delete it from the list of indexes - $tablename = strtoupper($this->prefix.$table); - - $sql = "SELECT c.column_name - FROM INFORMATION_SCHEMA.key_column_usage c - JOIN INFORMATION_SCHEMA.table_constraints t ON t.constraint_name = c.constraint_name - WHERE t.table_name = '$tablename' - AND t.constraint_type = 'PRIMARY KEY' - ORDER BY t.constraint_name, c.ordinal_position"; - - $this->query_start($sql, null, SQL_QUERY_AUX); - $rs = $this->adodb->Execute($sql); - $this->query_end($rs); - - $columns = $this->adodb_recordset_to_array($rs); - $rs->Close(); - /// Mimic one index array structure for easier search - $columns = array_keys($columns); - $primary_key = array('unique' => 1, 'columns' => $columns); - if ($found = array_search($primary_key, $indexes)) { - unset($indexes[$found]); - } - return $indexes; - } -} diff --git a/lib/dml/mssql_n_adodb_moodle_database.php b/lib/dml/mssql_n_adodb_moodle_database.php deleted file mode 100644 index b37cba369a..0000000000 --- a/lib/dml/mssql_n_adodb_moodle_database.php +++ /dev/null @@ -1,62 +0,0 @@ -. - - -/** - * MSSQL_N database class using adodb backend - * - * TODO: delete before branching 2.0 - * - * @package moodlecore - * @subpackage DML - * @copyright 2008 Petr Skoda (http://skodak.org) - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -require_once($CFG->libdir.'/dml/moodle_database.php'); -require_once($CFG->libdir.'/dml/adodb_moodle_database.php'); -require_once($CFG->libdir.'/dml/mssql_adodb_moodle_database.php'); - -/** - * MSSQL_N database class using adodb backend - */ -class mssql_n_adodb_moodle_database extends mssql_adodb_moodle_database { - - /** - * Returns database type - * @return string db type mysql, mysqli, postgres7 - */ - protected function get_dbtype() { - return 'mssql_n'; - } - - /** - * Returns localised database description - * Note: can be used before connect() - * @return string - */ - public function get_configuration_hints() { - $str = get_string('databasesettingssub_mssql_n', 'install'); - $str .= "

'; - $str .= get_string('moodledocslink', 'install') . '

'; - return $str; - } - -} diff --git a/lib/dml/mssql_native_moodle_database.php b/lib/dml/mssql_native_moodle_database.php index fac9a8ecd0..c524021a97 100644 --- a/lib/dml/mssql_native_moodle_database.php +++ b/lib/dml/mssql_native_moodle_database.php @@ -72,7 +72,7 @@ class mssql_native_moodle_database extends moodle_database { /** * Returns general database library name * Note: can be used before connect() - * @return string db type adodb, pdo, native + * @return string db type pdo, native */ protected function get_dblibrary() { return 'native'; diff --git a/lib/dml/mysql_adodb_moodle_database.php b/lib/dml/mysql_adodb_moodle_database.php deleted file mode 100644 index edbe539375..0000000000 --- a/lib/dml/mysql_adodb_moodle_database.php +++ /dev/null @@ -1,68 +0,0 @@ -. - - -/** - * Legacy MySQL database class using adodb backend. - * - * TODO: delete before branching 2.0 - * - * @package moodlecore - * @subpackage DML - * @copyright 2008 Petr Skoda (http://skodak.org) - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -require_once($CFG->libdir.'/dml/moodle_database.php'); -require_once($CFG->libdir.'/dml/adodb_moodle_database.php'); -require_once($CFG->libdir.'/dml/mysqli_adodb_moodle_database.php'); - -/** - * Legacy MySQL database class using adodb backend - */ -class mysql_adodb_moodle_database extends mysqli_adodb_moodle_database { - - /** - * Detects if all needed PHP stuff installed. - * Do not try to connect to db if this test fails. - * @return mixed true if ok, string if something - */ - public function driver_installed() { - if (!extension_loaded('mysql')) { - return get_string('mysqlextensionisnotpresentinphp', 'install'); - } - return true; - } - - /** - * Returns database type - * @return string db type mysql, mysqli, postgres7 - */ - protected function get_dbtype() { - return 'mysql'; - } - - /** - * Returns localised database description - * Note: can be used before connect() - * @return string - */ - public function get_configuration_hints() { - return get_string('databasesettingssub_mysql', 'install'); - } - -} diff --git a/lib/dml/mysqli_adodb_moodle_database.php b/lib/dml/mysqli_adodb_moodle_database.php deleted file mode 100644 index 3126a162ab..0000000000 --- a/lib/dml/mysqli_adodb_moodle_database.php +++ /dev/null @@ -1,347 +0,0 @@ -. - - -/** - * Legacy MySQL database class using adodb backend - * - * TODO: delete before branching 2.0 - * - * @package moodlecore - * @subpackage DML - * @copyright 2008 Petr Skoda (http://skodak.org) - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -require_once($CFG->libdir.'/dml/moodle_database.php'); -require_once($CFG->libdir.'/dml/adodb_moodle_database.php'); - -/** - * Legacy MySQL database class using adodb backend - */ -class mysqli_adodb_moodle_database extends adodb_moodle_database { - - /** - * Attempt to create the database - * @param string $dbhost - * @param string $dbuser - * @param string $dbpass - * @param string $dbname - * @return bool success - * @throws dml_exception if error - */ - public function create_database($dbhost, $dbuser, $dbpass, $dbname, array $dboptions=null) { - $this->adodb->database = ''; // reset database name cached by ADODB. Trick from MDL-9609 - if ($this->adodb->Connect($dbhost, $dbuser, $dbpass)) { /// Try to connect without DB - if ($this->adodb->Execute("CREATE DATABASE $dbname DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci")) { - $this->adodb->Disconnect(); - return true; - } else { - return false; - } - } - } - - /** - * Detects if all needed PHP stuff installed. - * Do not connect to connect to db if this test fails. - * @return mixed true if ok, string if something - */ - public function driver_installed() { - if (!extension_loaded('mysqli')) { - return get_string('mysqliextensionisnotpresentinphp', 'install'); - } - return true; - } - - protected function preconfigure_dbconnection() { - if (!defined('ADODB_ASSOC_CASE')) { - define ('ADODB_ASSOC_CASE', 2); - } - } - - protected function configure_dbconnection() { - $this->adodb->SetFetchMode(ADODB_FETCH_ASSOC); - - $sql = "SET NAMES 'utf8'"; - $this->query_start($sql, null, SQL_QUERY_AUX); - $result = $this->adodb->Execute($sql); - $this->query_end($result); - - return true; - } - - /** - * Returns database family type - * @return string db family name (mysql, postgres, mssql, oracle, etc.) - */ - public function get_dbfamily() { - return 'mysql'; - } - - /** - * Returns database type - * @return string db type mysql, mysqli, postgres7 - */ - protected function get_dbtype() { - return 'mysqli'; - } - - /** - * Returns localised database description - * Note: can be used before connect() - * @return string - */ - public function get_configuration_hints() { - return get_string('databasesettingssub_mysqli', 'install'); - } - - /** - * Returns supported query parameter types - * @return bitmask - */ - protected function allowed_param_types() { - return SQL_PARAMS_QM; - } - - /** - * This method will introspect inside DB to detect it it's a UTF-8 DB or no - * Used from setup.php to set correctly "set names" when the installation - * process is performed without the initial and beautiful installer - * @return bool true if db in unicode mode - */ - function setup_is_unicodedb() { - - $sql = "SHOW LOCAL VARIABLES LIKE 'character_set_database'"; - $this->query_start($sql, null, SQL_QUERY_AUX); - $rs = $this->adodb->Execute($sql); - $this->query_end($rs); - - if ($rs && !$rs->EOF) { - $records = $rs->GetAssoc(true); - $encoding = $records['character_set_database']['Value']; - if (strtoupper($encoding) == 'UTF8') { - return true; - } - } - return false; - } - - /** - /* Tries to change default db encoding to utf8, if empty db - * @return bool sucecss - * @throws dml_exception if error - */ - public function change_db_encoding() { - // try forcing utf8 collation, if mysql db and no tables present - $this->query_start("--adodb-MetaTables", null, SQL_QUERY_AUX); - // TODO: maybe add separate empty database test because this ignores tables without prefix - $metatables = $this->adodb->MetaTables(); - $this->query_end(true); - - if (!$metatables) { - $sql = "ALTER DATABASE $this->dbname CHARACTER SET utf8"; - $this->query_start($sql, null, SQL_QUERY_AUX); - $rs = $this->adodb->Execute($sql); - $this->query_end($rs); - if ($this->setup_is_unicodedb()) { - $this->configure_dbconnection(); - return true; - } else { - return false; - } - } else { - return false; - } - } - - /** - * Insert a record into a table and return the "id" field if required, - * Some conversions and safety checks are carried out. Lobs are supported. - * If the return ID isn't required, then this just reports success as true/false. - * $data is an object containing needed data - * @param string $table The database table to be inserted into - * @param object $data A data object with values for one or more fields in the record - * @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. - * @param bool $bulk true means repeated inserts expected - * @return true or new id - * @throws dml_exception if error - */ - public function insert_record($table, $dataobject, $returnid=true, $bulk=false) { - if (!is_object($dataobject)) { - $dataobject = (object)$dataobject; - } - - $columns = $this->get_columns($table); - - unset($dataobject->id); - $cleaned = array(); - - foreach ($dataobject as $field=>$value) { - if (!isset($columns[$field])) { - continue; - } - $column = $columns[$field]; - if (is_bool($value)) { - $value = (int)$value; // prevent "false" problems - } - if (!empty($column->enums)) { - // workaround for problem with wrong enums in mysql - if (is_null($value) and !$column->not_null) { - // ok - nulls allowed - } else { - if (!in_array((string)$value, $column->enums)) { - throw new dml_write_exception('Enum value '.s($value).' not allowed in field '.$field.' table '.$table.'.'); - } - } - } - $cleaned[$field] = $value; - } - - return $this->insert_record_raw($table, $cleaned, $returnid, $bulk); - } - - /** - * Update a record in a table - * - * $dataobject is an object containing needed data - * Relies on $dataobject having a variable "id" to - * specify the record to update - * - * @param string $table The database table to be checked against. - * @param object $dataobject An object with contents equal to fieldname=>fieldvalue. Must have an entry for 'id' to map to the table specified. - * @param bool true means repeated updates expected - * @return bool true - * @throws dml_exception if error - */ - public function update_record($table, $dataobject, $bulk=false) { - if (!is_object($dataobject)) { - $dataobject = (object)$dataobject; - } - - $columns = $this->get_columns($table); - $cleaned = array(); - - foreach ($dataobject as $field=>$value) { - if (!isset($columns[$field])) { - continue; - } - if (is_bool($value)) { - $value = (int)$value; // prevent "false" problems - } - $cleaned[$field] = $value; - } - - return $this->update_record_raw($table, $cleaned, $bulk); - } - - /** - * Set a single field in every table row where the select statement evaluates to true. - * - * @param string $table The database table to be checked against. - * @param string $newfield the field to set. - * @param string $newvalue the value to set the field to. - * @param string $select A fragment of SQL to be used in a where clause in the SQL call. - * @param array $params array of sql parameters - * @return bool true - * @throws dml_exception if error - */ - public function set_field_select($table, $newfield, $newvalue, $select, array $params=null) { - if ($select) { - $select = "WHERE $select"; - } - if (is_null($params)) { - $params = array(); - } - list($select, $params, $type) = $this->fix_sql_params($select, $params); - - if (is_bool($newvalue)) { - $newvalue = (int)$newvalue; // prevent "false" problems - } - if (is_null($newvalue)) { - $newfield = "$newfield = NULL"; - } else { - $newfield = "$newfield = ?"; - array_unshift($params, $newvalue); - } - $sql = "UPDATE {$this->prefix}$table SET $newfield $select"; - - $this->query_start($sql, $params, SQL_QUERY_UPDATE); - $rs = $this->adodb->Execute($sql, $params); - $this->query_end($rs); - - return true; - } - - - public function sql_cast_char2int($fieldname, $text=false) { - return ' CAST(' . $fieldname . ' AS SIGNED) '; - } - - /** - * Returns the SQL text to be used to calculate the length in characters of one expression. - * @param string fieldname or expression to calculate its length in characters. - * @return string the piece of SQL code to be used in the statement. - */ - public function sql_length($fieldname) { - return ' CHAR_LENGTH(' . $fieldname . ')'; - } - - /** - * Does this driver suppoer regex syntax when searching - */ - public function sql_regex_supported() { - return true; - } - - /** - * Return regex positive or negative match sql - * @param bool $positivematch - * @return string or empty if not supported - */ - public function sql_regex($positivematch=true) { - return $positivematch ? 'REGEXP' : 'NOT REGEXP'; - } - - public function sql_cast_2signed($fieldname) { - return ' CAST(' . $fieldname . ' AS SIGNED) '; - } - - /** - * Import a record into a table, id field is required. - * Basic safety checks only. Lobs are supported. - * @param string $table name of database table to be inserted into - * @param mixed $dataobject object or array with fields in the record - * @return bool true - * @throws dml_exception if error - */ - public function import_record($table, $dataobject) { - $dataobject = (object)$dataobject; - - $columns = $this->get_columns($table); - $cleaned = array(); - - foreach ($dataobject as $field=>$value) { - if (!isset($columns[$field])) { - continue; - } - $cleaned[$field] = $value; - } - - return $this->insert_record_raw($table, $cleaned, false, true, true); - } -} diff --git a/lib/dml/mysqli_native_moodle_database.php b/lib/dml/mysqli_native_moodle_database.php index 1aa094b83a..280ba2ae1d 100644 --- a/lib/dml/mysqli_native_moodle_database.php +++ b/lib/dml/mysqli_native_moodle_database.php @@ -107,7 +107,7 @@ class mysqli_native_moodle_database extends moodle_database { /** * Returns general database library name * Note: can be used before connect() - * @return string db type adodb, pdo, native + * @return string db type pdo, native */ protected function get_dblibrary() { return 'native'; diff --git a/lib/dml/oci8po_adodb_moodle_database.php b/lib/dml/oci8po_adodb_moodle_database.php deleted file mode 100644 index 7f668469e9..0000000000 --- a/lib/dml/oci8po_adodb_moodle_database.php +++ /dev/null @@ -1,718 +0,0 @@ -. - - -/** - * Oracle database class using adodb backend - * - * TODO: delete before branching 2.0 - * - * @package moodlecore - * @subpackage DML - * @copyright 2008 Petr Skoda (http://skodak.org) - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -require_once($CFG->libdir.'/dml/moodle_database.php'); -require_once($CFG->libdir.'/dml/adodb_moodle_database.php'); -require_once($CFG->libdir.'/dml/oci8po_adodb_moodle_recordset.php'); - -/** - * Oracle database class using adodb backend - */ -class oci8po_adodb_moodle_database extends adodb_moodle_database { - - public function connect($dbhost, $dbuser, $dbpass, $dbname, $prefix, array $dboptions=null) { - if ($prefix == '' and !$this->external) { - //Enforce prefixes for everybody but mysql - throw new dml_exception('prefixcannotbeempty', $this->get_dbfamily()); - } - if (!$this->external and strlen($prefix) > 2) { - //Max prefix length for Oracle is 2cc - $a = (object)array('dbfamily'=>'oracle', 'maxlength'=>2); - throw new dml_exception('prefixtoolong', $a); - } - return parent::connect($dbhost, $dbuser, $dbpass, $dbname, $prefix, $dboptions); - } - - /** - * Detects if all needed PHP stuff installed. - * Do not connect to connect to db if this test fails. - * @return mixed true if ok, string if something - */ - public function driver_installed() { - if (!extension_loaded('oci8')) { - return get_string('ociextensionisnotpresentinphp', 'install'); - } - return true; - } - - protected function preconfigure_dbconnection() { - if (!defined('ADODB_ASSOC_CASE')) { - define ('ADODB_ASSOC_CASE', 0); /// Use lowercase fieldnames for ADODB_FETCH_ASSOC - /// (only meaningful for oci8po, it's the default - /// for other DB drivers so this won't affect them) - } - /// Row prefetching uses a bit of memory but saves a ton - /// of network latency. With current AdoDB and PHP, only - /// Oracle uses this setting. - if (!defined('ADODB_PREFETCH_ROWS')) { - define ('ADODB_PREFETCH_ROWS', 1000); - } - } - - protected function configure_dbconnection() { - $this->adodb->SetFetchMode(ADODB_FETCH_ASSOC); - - /// No need to set charset. It must be specified by the NLS_LANG env. variable - /// Now set the decimal separator to DOT, Moodle & PHP will always send floats to - /// DB using DOTS. Manually introduced floats (if using other characters) must be - /// converted back to DOTs (like gradebook does) - $sql = "ALTER SESSION SET NLS_NUMERIC_CHARACTERS='.,'"; - $this->query_start($sql, null, SQL_QUERY_AUX); - $rs = $this->adodb->Execute($sql); - $this->query_end($rs); - - return true; - } - - /** - * Returns database family type - * @return string db family name (mysql, postgres, mssql, oracle, etc.) - */ - public function get_dbfamily() { - return 'oracle'; - } - - /** - * Returns database type - * @return string db type mysql, mysqli, postgres7 - */ - protected function get_dbtype() { - return 'oci8po'; - } - - /** - * Returns localised database description - * Note: can be used before connect() - * @return string - */ - public function get_configuration_hints() { - $str = get_string('databasesettingssub_oci8po', 'install'); - $str .= "

'; - $str .= get_string('moodledocslink', 'install') . '

'; - return $str; - } - - /** - * Returns supported query parameter types - * @return bitmask - */ - protected function allowed_param_types() { - return SQL_PARAMS_QM; - } - - /** - * This method will introspect inside DB to detect it it's a UTF-8 DB or no - * Used from setup.php to set correctly "set names" when the installation - * process is performed without the initial and beautiful installer - * @return bool true if db in unicode mode - */ - function setup_is_unicodedb() { - $sql = "SELECT parameter, value FROM nls_database_parameters where parameter = 'NLS_CHARACTERSET'"; - $this->query_start($sql, null, SQL_QUERY_AUX); - $rs = $this->adodb->Execute($sql); - $this->query_end($rs); - - if ($rs && !$rs->EOF) { - $encoding = $rs->fields['value']; - if (strtoupper($encoding) == 'AL32UTF8') { - return true; - } - } - return false; - } - - /** - * Selects rows and return values of first column as array. - * - * @param string $sql The SQL query - * @param array $params array of sql parameters - * @return mixed array of values - * @throws dml_exception if error - */ - public function get_fieldset_sql($sql, array $params=null) { - if ($result = parent::get_fieldset_sql($sql, $params)) { - array_walk($result, array('oci8po_adodb_moodle_database', 'onespace2empty')); - } - return $result; - } - - protected function create_recordset($rs) { - return new oci8po_adodb_moodle_recordset($rs); - } - - protected function adodb_recordset_to_array($rs) { - /// Really DIRTY HACK for Oracle - needed because it can not see difference from NULL and '' - /// this can not be removed even if we chane db defaults :-( - if ($result = parent::adodb_recordset_to_array($rs)) { - foreach ($result as $key=>$row) { - $row = (array)$row; - array_walk($row, array('oci8po_adodb_moodle_database', 'onespace2empty')); - $result[$key] = (object)$row; - } - } - - return $result; - } - - public function sql_bitand($int1, $int2) { - return 'bitand((' . $int1 . '), (' . $int2 . '))'; - } - - public function sql_bitnot($int1) { - return '((0 - (' . $int1 . ')) - 1)'; - } - - public function sql_bitor($int1, $int2) { - return '((' . $int1 . ') + (' . $int2 . ') - ' . $this->sql_bitand($int1, $int2) . ')'; - } - - public function sql_bitxor($int1, $int2) { - return '(' . $this->sql_bitor($int1, $int2) . ' - ' . $this->sql_bitand($int1, $int2) . ')'; - } - - /** - * Returns the SQL text to be used in order to perform module '%' - * opration - remainder after division - * - * @param integer int1 first integer in the operation - * @param integer int2 second integer in the operation - * @return string the piece of SQL code to be used in your statement. - */ - public function sql_modulo($int1, $int2) { - return 'MOD(' . $int1 . ', ' . $int2 . ')'; - } - - function sql_null_from_clause() { - return ' FROM dual'; - } - - public function sql_cast_char2int($fieldname, $text=false) { - if (!$text) { - return ' CAST(' . $fieldname . ' AS INT) '; - } else { - return ' CAST(' . $this->sql_compare_text($fieldname) . ' AS INT) '; - } - } - - public function sql_order_by_text($fieldname, $numchars=32) { - return 'dbms_lob.substr(' . $fieldname . ', ' . $numchars . ',1)'; - } - - /** - * Returns the SQL for returning searching one string for the location of another. - */ - public function sql_position($needle, $haystack) { - return "INSTR(($haystack), ($needle))"; - } - - public function sql_isempty($tablename, $fieldname, $nullablefield, $textfield) { - if ($nullablefield) { - return " $fieldname IS NULL "; /// empties in nullable fields are stored as - } else { /// NULLs - if ($textfield) { - return " ".$this->sql_compare_text($fieldname)." = ' ' "; /// oracle_dirty_hack inserts 1-whitespace - } else { /// in NOT NULL varchar and text columns so - return " $fieldname = ' ' "; /// we need to look for that in any situation - } - } - } - - function sql_empty() { - return ' '; - } - - /** - * Update a record in a table - * - * $dataobject is an object containing needed data - * Relies on $dataobject having a variable "id" to - * specify the record to update - * - * @param string $table The database table to be checked against. - * @param object $dataobject An object with contents equal to fieldname=>fieldvalue. Must have an entry for 'id' to map to the table specified. - * @param bool true means repeated updates expected - * @return bool true - * @throws dml_exception if error - */ - public function update_record($table, $dataobject, $bulk=false) { - if (!is_object($dataobject)) { - $dataobject = (object)$dataobject; - } - - $columns = $this->get_columns($table); - $cleaned = array(); - $clobs = array(); - $blobs = array(); - - foreach ($dataobject as $field=>$value) { - if (!isset($columns[$field])) { /// Non-existing table field, skip it - continue; - } - /// Apply Oracle dirty hack to value, to have "correct" empty values for Oracle - $value = $this->oracle_dirty_hack($table, $field, $value); - - /// Get column metadata - $column = $columns[$field]; - if ($column->meta_type == 'B') { /// BLOB columns need to be updated apart - if (!is_null($value)) { /// If value not null, add it to the list of BLOBs to update later - $blobs[$field] = $value; - continue; /// We don't want this column to be processed by update_record_raw() at all. - /// $value = 'empty_blob()'; /// Set the default value to be inserted in first instance. Not needed to initialize lob storage in updates - } - - } else if ($column->meta_type == 'X' && strlen($value) > 4000) { /// CLOB columns need to be updated apart (if lenght > 4000) - if (!is_null($value)) { /// If value not null, add it to the list of BLOBs to update later - $blobs[$field] = $value; - continue; /// We don't want this column to be processed by update_record_raw() at all. - /// $value = 'empty_clob()'; /// Set the default value to be inserted in first instance. Not needed to initialize lob storage in updates - } - - } else if (is_bool($value)) { - $value = (int)$value; // prevent "false" problems - - } else if ($value === '' || $value === ' ') { - if ($column->meta_type == 'I' or $column->meta_type == 'F' or $column->meta_type == 'N') { - $value = 0; // prevent '' problems in numeric fields - } - } - $cleaned[$field] = $value; - } - - - if (empty($blobs) && empty($clobs)) { /// Without BLOBs and CLOBs, execute the raw update and return - return $this->update_record_raw($table, $cleaned, $bulk); - } - - /// We have BLOBs or CLOBs to postprocess, execute the raw update and then update blobs - $this->update_record_raw($table, $cleaned, $bulk); - - foreach ($blobs as $key=>$value) { - $this->query_start('--adodb-UpdateBlob', null, SQL_QUERY_UPDATE); - $result = $this->adodb->UpdateBlob($this->prefix.$table, $key, $value, "id = {$dataobject->id}"); - $this->query_end($result); - } - - foreach ($clobs as $key=>$value) { - $this->query_start('--adodb-UpdateClob', null, SQL_QUERY_UPDATE); - $result = $this->adodb->UpdateClob($this->prefix.$table, $key, $value, "id = {$dataobject->id}"); - $this->query_end($result); - } - - return true; - } - - /** - * Insert a record into a table and return the "id" field if required, - * Some conversions and safety checks are carried out. Lobs are supported. - * If the return ID isn't required, then this just reports success as true/false. - * $data is an object containing needed data - * @param string $table The database table to be inserted into - * @param object $data A data object with values for one or more fields in the record - * @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. - * @param bool $bulk true means repeated inserts expected - * @return true or new id - * @throws dml_exception if error - */ - public function insert_record($table, $dataobject, $returnid=true, $bulk=false) { - if (!is_object($dataobject)) { - $dataobject = (object)$dataobject; - } - - unset($dataobject->id); - - $columns = $this->get_columns($table); - $cleaned = array(); - $blobs = array(); - $clobs = array(); - - foreach ($dataobject as $field=>$value) { - if (!isset($columns[$field])) { /// Non-existing table field, skip it - continue; - } - /// Apply Oracle dirty hack to value, to have "correct" empty values for Oracle - $value = $this->oracle_dirty_hack($table, $field, $value); - - /// Get column metadata - $column = $columns[$field]; - if ($column->meta_type == 'B') { /// BLOBs columns need to be updated apart - if (!is_null($value)) { /// If value not null, add it to the list of BLOBs to update later - $blobs[$field] = $value; - $value = 'empty_blob()'; /// Set the default value to be inserted (preparing lob storage for next update) - } - - } else if ($column->meta_type == 'X' && strlen($value) > 4000) { /// CLOB columns need to be updated apart (if lenght > 4000) - if (!is_null($value)) { /// If value not null, add it to the list of BLOBs to update later - $clobs[$field] = $value; - $value = 'empty_clob()'; /// Set the default value to be inserted (preparing lob storage for next update) - } - - } else if (is_bool($value)) { - $value = (int)$value; // prevent "false" problems - - } else if ($value === '' || $value === ' ') { - if ($column->meta_type == 'I' or $column->meta_type == 'F' or $column->meta_type == 'N') { - $value = 0; // prevent '' problems in numeric fields - } - } - $cleaned[$field] = $value; - } - - if (empty($blobs) && empty($clobs)) { /// Without BLOBs and CLOBs, execute the raw insert and return - return $this->insert_record_raw($table, $cleaned, $returnid, $bulk); - } - - /// We have BLOBs or CLOBs to postprocess, insert the raw record fetching the id to be used later - $id = $this->insert_record_raw($table, $cleaned, true, $bulk); - - foreach ($blobs as $key=>$value) { - $this->query_start('--adodb-UpdateBlob', null, SQL_QUERY_UPDATE); - $result = $this->adodb->UpdateBlob($this->prefix.$table, $key, $value, "id = $id"); - $this->query_end($result); - } - - foreach ($clobs as $key=>$value) { - $this->query_start('--adodb-UpdateClob', null, SQL_QUERY_UPDATE); - $result = $this->adodb->UpdateClob($this->prefix.$table, $key, $value, "id = $id"); - $this->query_end($result); - } - - return ($returnid ? $id : true); - } - - /** - * Set a single field in every table row where the select statement evaluates to true. - * - * @param string $table The database table to be checked against. - * @param string $newfield the field to set. - * @param string $newvalue the value to set the field to. - * @param string $select A fragment of SQL to be used in a where clause in the SQL call. - * @param array $params array of sql parameters - * @return bool true - * @throws dml_exception if error - */ - public function set_field_select($table, $newfield, $newvalue, $select, array $params=null) { - - if (is_null($params)) { - $params = array(); - } - list($select, $params, $type) = $this->fix_sql_params($select, $params); - - /// Apply Oracle dirty hack to value, to have "correct" empty values for Oracle - $newvalue = $this->oracle_dirty_hack($table, $newfield, $newvalue); - - /// Get column metadata - $columns = $this->get_columns($table); - $column = $columns[$newfield]; - - if ($column->meta_type == 'B') { /// If the column is a BLOB - /// Update BLOB column and return - $select = $this->emulate_bound_params($select, $params); // adodb does not use bound parameters for blob updates :-( - $this->query_start('--adodb-UpdateBlob', null, SQL_QUERY_UPDATE); - $result = $this->adodb->UpdateBlob($this->prefix.$table, $newfield, $newvalue, $select); - $this->query_end($result); - return true; - } - - if ($column->meta_type == 'X' && strlen($newvalue) > 4000) { /// If the column is a CLOB with lenght > 4000 - /// Update BLOB column and return - $select = $this->emulate_bound_params($select, $params); // adodb does not use bound parameters for blob updates :-( - $this->query_start('--adodb-UpdateClob', null, SQL_QUERY_UPDATE); - $result = $this->adodb->UpdateClob($this->prefix.$table, $newfield, $newvalue, $select); - $this->query_end($result); - return true; - } - - /// Arrived here, normal update (without BLOBs) - if (is_null($newvalue)) { - $newfield = "$newfield = NULL"; - } else { - if (is_bool($newvalue)) { - $newvalue = (int)$newvalue; // prevent "false" problems - } else if ($newvalue === '' || $newvalue === ' ') { - if ($column->meta_type == 'I' or $column->meta_type == 'F' or $column->meta_type == 'N') { - $newvalue = 0; // prevent '' problems in numeric fields - } - } - - $newfield = "$newfield = ?"; - array_unshift($params, $newvalue); // add as first param - } - $select = !empty($select) ? "WHERE $select" : ''; - $sql = "UPDATE {$this->prefix}$table SET $newfield $select"; - - $this->query_start($sql, $params, SQL_QUERY_UPDATE); - $rs = $rs = $this->adodb->Execute($sql, $params); - $this->query_end($rs); - - return true; - } - - /** - * Insert new record into database, as fast as possible, no safety checks, lobs not supported. - * (overloaded from adodb_moodle_database because of sequence numbers and empty_blob()/empty_clob()) - * @param string $table name - * @param mixed $params data record as object or array - * @param bool $returnit return it of inserted record - * @param bool $bulk true means repeated inserts expected - * @param bool $customsequence true if 'id' included in $params, disables $returnid - * @return true or new id - * @throws dml_exception if error - */ - public function insert_record_raw($table, $params, $returnid=true, $bulk=false, $customsequence=false) { - if (!is_array($params)) { - $params = (array)$params; - } - - if ($customsequence) { - if (!isset($params['id'])) { - throw new coding_exception('moodle_database::insert_record_raw() id field must be specified if custom sequences used.'); - } - $returnid = false; - } else { - unset($params['id']); - } - - if ($returnid) { - $dbman = $this->get_manager(); - $xmldb_table = new xmldb_table($table); - $this->query_start('--find_sequence_name', null, SQL_QUERY_AUX); - $seqname = $dbman->find_sequence_name($xmldb_table); - $this->query_end(true); - if (!$seqname) { - /// Fallback, seqname not found, something is wrong. Inform and use the alternative getNameForObject() method - $generator = $this->get_dbman()->generator; - $generator->setPrefix($this->getPrefix()); - $seqname = $generator->getNameForObject($table, 'id', 'seq'); - } - $xmldb_table = new xmldb_table($table); - $this->query_start('--adodb-GenID', null, SQL_QUERY_AUX); - $nextval = $this->adodb->GenID($seqname); - $this->query_end(true); - - if ($nextval) { - $params['id'] = (int)$nextval; - } - } - - if (empty($params)) { - throw new coding_exception('moodle_database::insert_record_raw() no fields found.'); - } - - $fields = implode(',', array_keys($params)); - $qms = array(); - /// Look for 'empty_clob() and empty_blob() params to replace question marks properly - /// Oracle requires those function calls on insert to prepare blob/clob storage, so we - /// specify them as SQL, deleting them from parameters - foreach ($params as $key=>$param) { - if ($param === 'empty_blob()') { - $qms[] = 'empty_blob()'; - unset($params[$key]); - } else if ($param === 'empty_clob()') { - $qms[] = 'empty_clob()'; - unset($params[$key]); - } else { - $qms[] = '?'; - } - } - $qms = implode(',', $qms); - - $sql = "INSERT INTO {$this->prefix}$table ($fields) VALUES($qms)"; - - $this->query_start($sql, $params, SQL_QUERY_INSERT); - $rs = $rs = $this->adodb->Execute($sql, $params); - $this->query_end($rs); - - if (!$returnid) { - return true; - } - if (!empty($params['id'])) { - return (int)$params['id']; - } - - throw new dml_write_exception('unknown error fetching inserted id'); - } - - /** - * This function is used to convert all the Oracle 1-space defaults to the empty string - * like a really DIRTY HACK to allow it to work better until all those NOT NULL DEFAULT '' - * fields will be out from Moodle. - * @param string the string to be converted to '' (empty string) if it's ' ' (one space) - * @param mixed the key of the array in case we are using this function from array_walk, - * defaults to null for other (direct) uses - * @return boolean always true (the converted variable is returned by reference) - */ - static function onespace2empty(&$item, $key=null) { - $item = $item == ' ' ? '' : $item; - return true; - } - - /** - * This function will handle all the records before being inserted/updated to DB for Oracle - * installations. This is because the "special feature" of Oracle where the empty string is - * equal to NULL and this presents a problem with all our currently NOT NULL default '' fields. - * - * Once Moodle DB will be free of this sort of false NOT NULLS, this hack could be removed safely - * - * Note that this function is 100% private and should be used, exclusively by DML functions - * in this file. Also, this is considered a DIRTY HACK to be removed when possible. (stronk7) - * - * This function is private and must not be used outside this driver at all - * - * @param $table string the table where the record is going to be inserted/updated (without prefix) - * @param $field string the field where the record is going to be inserted/updated - * @param $value mixed the value to be inserted/updated - */ - private function oracle_dirty_hack ($table, $field, $value) { - - /// Get metadata - $columns = $this->get_columns($table); - if (!isset($columns[$field])) { - return $value; - } - $column = $columns[$field]; - - /// For Oracle DB, empty strings are converted to NULLs in DB - /// and this breaks a lot of NOT NULL columns currenty Moodle. In the future it's - /// planned to move some of them to NULL, if they must accept empty values and this - /// piece of code will become less and less used. But, for now, we need it. - /// What we are going to do is to examine all the data being inserted and if it's - /// an empty string (NULL for Oracle) and the field is defined as NOT NULL, we'll modify - /// such data in the best form possible ("0" for booleans and numbers and " " for the - /// rest of strings. It isn't optimal, but the only way to do so. - /// In the oppsite, when retrieving records from Oracle, we'll decode " " back to - /// empty strings to allow everything to work properly. DIRTY HACK. - - /// If the field ins't VARCHAR or CLOB, skip - if ($column->meta_type != 'C' and $column->meta_type != 'X') { - return $value; - } - - /// If the field isn't NOT NULL, skip (it's nullable, so accept empty-null values) - if (!$column->not_null) { - return $value; - } - - /// If the value isn't empty, skip - if (!empty($value)) { - return $value; - } - - /// Now, we have one empty value, going to be inserted to one NOT NULL, VARCHAR2 or CLOB field - /// Try to get the best value to be inserted - - /// The '0' string doesn't need any transformation, skip - if ($value === '0') { - return $value; - } - - /// Transformations start - if (gettype($value) == 'boolean') { - return '0'; /// Transform false to '0' that evaluates the same for PHP - - } else if (gettype($value) == 'integer') { - return '0'; /// Transform 0 to '0' that evaluates the same for PHP - - } else if (gettype($value) == 'NULL') { - return '0'; /// Transform NULL to '0' that evaluates the same for PHP - - } else if ($value === '') { - return ' '; /// Transform '' to ' ' that DONT'T EVALUATE THE SAME - /// (we'll transform back again on get_records_XXX functions and others)!! - } - - /// Fail safe to original value - return $value; - } - - /** - * Import a record into a table, id field is required. - * Basic safety checks only. Lobs are supported. - * @param string $table name of database table to be inserted into - * @param mixed $dataobject object or array with fields in the record - * @return bool success - */ - public function import_record($table, $dataobject) { - $dataobject = (object)$dataobject; - - $columns = $this->get_columns($table); - $cleaned = array(); - $blobs = array(); - $clobs = array(); - - foreach ($dataobject as $field=>$value) { - if (!isset($columns[$field])) { /// Non-existing table field, skip it - continue; - } - /// Apply Oracle dirty hack to value, to have "correct" empty values for Oracle - $value = $this->oracle_dirty_hack($table, $field, $value); - - /// Get column metadata - $column = $columns[$field]; - if ($column->meta_type == 'B') { /// BLOBs columns need to be updated apart - if (!is_null($value)) { /// If value not null, add it to the list of BLOBs to update later - $blobs[$field] = $value; - $value = 'empty_blob()'; /// Set the default value to be inserted (preparing lob storage for next update) - } - - } else if ($column->meta_type == 'X' && strlen($value) > 4000) { /// CLOB columns need to be updated apart (if lenght > 4000) - if (!is_null($value)) { /// If value not null, add it to the list of BLOBs to update later - $clobs[$field] = $value; - $value = 'empty_clob()'; /// Set the default value to be inserted (preparing lob storage for next update) - } - } - - $cleaned[$field] = $value; - } - - $this->insert_record_raw($table, $cleaned, false, true, true); - - if (empty($blobs) and empty($clobs)) { - return true; - } - - /// We have BLOBs or CLOBs to postprocess - - foreach ($blobs as $key=>$value) { - $this->query_start('--adodb-UpdateBlob', null, SQL_QUERY_UPDATE); - $result = $this->adodb->UpdateBlob($this->prefix.$table, $key, $value, "id = $id"); - $this->query_end($result); - } - - foreach ($clobs as $key=>$value) { - $this->query_start('--adodb-UpdateClob', null, SQL_QUERY_UPDATE); - $result = $this->adodb->UpdateClob($this->prefix.$table, $key, $value, "id = $id"); - $this->query_end($result); - } - - return true; - } -} diff --git a/lib/dml/oci8po_adodb_moodle_recordset.php b/lib/dml/oci8po_adodb_moodle_recordset.php deleted file mode 100644 index b5fee63f9e..0000000000 --- a/lib/dml/oci8po_adodb_moodle_recordset.php +++ /dev/null @@ -1,44 +0,0 @@ -. - - -/** - * Oracle moodle recordest with special hacks - * - * TODO: delete before branching 2.0 - * - * @package moodlecore - * @subpackage DML - * @copyright 2008 Petr Skoda (http://skodak.org) - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -require_once($CFG->libdir.'/dml/adodb_moodle_recordset.php'); - -/** - * Oracle moodle recordest with special hacks - */ -class oci8po_adodb_moodle_recordset extends adodb_moodle_recordset { - - public function current() { - /// Really DIRTY HACK for Oracle - needed because it can not see difference from NULL and '' - /// this can not be removed even if we change db defaults :-( - $fields = $this->rs->fields; - array_walk($fields, array('oci8po_adodb_moodle_database', 'onespace2empty')); - return (object)$fields; - } -} diff --git a/lib/dml/oci_native_moodle_database.php b/lib/dml/oci_native_moodle_database.php index b2e9094a95..9753cd5267 100644 --- a/lib/dml/oci_native_moodle_database.php +++ b/lib/dml/oci_native_moodle_database.php @@ -83,7 +83,7 @@ class oci_native_moodle_database extends moodle_database { /** * Returns general database library name * Note: can be used before connect() - * @return string db type adodb, pdo, native + * @return string db type pdo, native */ protected function get_dblibrary() { return 'native'; diff --git a/lib/dml/odbc_mssql_adodb_moodle_database.php b/lib/dml/odbc_mssql_adodb_moodle_database.php deleted file mode 100644 index 972be93017..0000000000 --- a/lib/dml/odbc_mssql_adodb_moodle_database.php +++ /dev/null @@ -1,74 +0,0 @@ -. - - -/** - * Experimenta mssql odbc database class using adodb backend - * - * TODO: delete before branching 2.0 - * - * @package moodlecore - * @subpackage DML - * @copyright 2008 Petr Skoda (http://skodak.org) - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -require_once($CFG->libdir.'/dml/moodle_database.php'); -require_once($CFG->libdir.'/dml/adodb_moodle_database.php'); -require_once($CFG->libdir.'/dml/mssql_adodb_moodle_database.php'); - -/** - * Experimenta mssql odbc database class using adodb backend - */ -class odbc_mssql_adodb_moodle_database extends mssql_adodb_moodle_database { - - /** - * Detects if all needed PHP stuff installed. - * Do not connect to connect to db if this test fails. - * @return mixed true if ok, string if something - */ - public function driver_installed() { - if (!extension_loaded('odbc')) { - return get_string('odbcextensionisnotpresentinphp', 'install'); - } - return true; - } - - /** - * Returns database type - * @return string db type mysql, mysqli, postgres7 - */ - protected function get_dbtype() { - return 'odbc_mssql'; - } - - /** - * Returns localised database description - * Note: can be used before connect() - * @return string - */ - public function get_configuration_hints() { - $str = get_string('databasesettingssub_odbc_mssql', 'install'); - $str .= "

'; - $str .= get_string('moodledocslink', 'install') . '

'; - return $str; - } - -} diff --git a/lib/dml/pdo_moodle_database.php b/lib/dml/pdo_moodle_database.php index 72348fd96b..bcd79a70e2 100644 --- a/lib/dml/pdo_moodle_database.php +++ b/lib/dml/pdo_moodle_database.php @@ -101,7 +101,7 @@ abstract class pdo_moodle_database extends moodle_database { /** * Returns general database library name * Note: can be used before connect() - * @return string db type adodb, pdo, native + * @return string db type pdo, native */ protected function get_dblibrary() { return 'pdo'; diff --git a/lib/dml/pgsql_native_moodle_database.php b/lib/dml/pgsql_native_moodle_database.php index ee0242b0f1..98becbc628 100644 --- a/lib/dml/pgsql_native_moodle_database.php +++ b/lib/dml/pgsql_native_moodle_database.php @@ -71,7 +71,7 @@ class pgsql_native_moodle_database extends moodle_database { /** * Returns general database library name * Note: can be used before connect() - * @return string db type adodb, pdo, native + * @return string db type pdo, native */ protected function get_dblibrary() { return 'native'; diff --git a/lib/dml/postgres7_adodb_moodle_database.php b/lib/dml/postgres7_adodb_moodle_database.php deleted file mode 100644 index 73c03e5abc..0000000000 --- a/lib/dml/postgres7_adodb_moodle_database.php +++ /dev/null @@ -1,542 +0,0 @@ -. - - -/** - * Postgresql database class using adodb backend - * - * TODO: delete before branching 2.0 - * - * @package moodlecore - * @subpackage DML - * @copyright 2008 Petr Skoda (http://skodak.org) - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -require_once($CFG->libdir.'/dml/moodle_database.php'); -require_once($CFG->libdir.'/dml/adodb_moodle_database.php'); - -/** - * Postgresql database class using adodb backend - */ -class postgres7_adodb_moodle_database extends adodb_moodle_database { - - public function connect($dbhost, $dbuser, $dbpass, $dbname, $prefix, array $dboptions=null) { - if ($prefix == '' and !$this->external) { - //Enforce prefixes for everybody but mysql - throw new dml_exception('prefixcannotbeempty', $this->get_dbfamily()); - } - return parent::connect($dbhost, $dbuser, $dbpass, $dbname, $prefix, $dboptions); - } - - /** - * Detects if all needed PHP stuff installed. - * Do not connect to connect to db if this test fails. - * @return mixed true if ok, string if something - */ - public function driver_installed() { - if (!extension_loaded('pgsql')) { - return get_string('pgsqlextensionisnotpresentinphp', 'install'); - } - return true; - } - - protected function preconfigure_dbconnection() { - if (!defined('ADODB_ASSOC_CASE')) { - define ('ADODB_ASSOC_CASE', 2); - } - } - - protected function configure_dbconnection() { - $this->adodb->SetFetchMode(ADODB_FETCH_ASSOC); - - $sql = "SET NAMES 'utf8'"; - $this->query_start($sql, null, SQL_QUERY_AUX); - $result = $this->adodb->Execute($sql); - $this->query_end($result); - - return true; - } - - /** - * Returns database family type - * @return string db family name (mysql, postgres, mssql, oracle, etc.) - */ - public function get_dbfamily() { - return 'postgres'; - } - - /** - * Returns database type - * @return string db type mysql, mysqli, postgres7 - */ - protected function get_dbtype() { - return 'postgres7'; - } - - /** - * Returns localised database description - * Note: can be used before connect() - * @return string - */ - public function get_configuration_hints() { - return get_string('databasesettingssub_postgres7', 'install'); - } - - /** - * Returns db related part of config.php - * @return object - */ - public function export_dbconfig() { - $cfg = new stdClass(); - $cfg->dbtype = $this->get_dbtype(); - $cfg->dblibrary = $this->get_dblibrary(); - if ($this->dbhost == 'localhost' or $this->dbhost == '127.0.0.1') { - $cfg->dbhost = "user='{$this->dbuser}' password='{$this->dbpass}' dbname='{$this->dbname}'"; - $cfg->dbname = ''; - $cfg->dbuser = ''; - $cfg->dbpass = ''; - } else { - $cfg->dbhost = $this->dbhost; - $cfg->dbname = $this->dbname; - $cfg->dbuser = $this->dbuser; - $cfg->dbpass = $this->dbpass; - } - $cfg->prefix = $this->prefix; - if ($this->dboptions) { - $cfg->dboptions = $this->dboptions; - } - - return $cfg; - } - - /** - * Returns supported query parameter types - * @return bitmask - */ - protected function allowed_param_types() { - return SQL_PARAMS_QM; - } - - public function get_columns($table, $usecache=true) { - if ($usecache and isset($this->columns[$table])) { - return $this->columns[$table]; - } - - $this->query_start("--adodb-MetaColumns", null, SQL_QUERY_AUX); - $columns = $this->adodb->MetaColumns($this->prefix.$table); - $this->query_end(true); - - if (!$columns) { - return array(); - } - - $this->columns[$table] = array(); - - foreach ($columns as $column) { - // colum names must be lowercase - $column->meta_type = substr($this->adodb->MetaType($column), 0 ,1); // only 1 character - // Let's fix the wrong meta type retrieved because of default blobSize=100 in AdoDB - if ($column->type == 'varchar' && $column->meta_type == 'X') { - $column->meta_type = 'C'; - } - if ($column->has_default) { - if ($pos = strpos($column->default_value, '::')) { - if (strpos($column->default_value, "'") === 0) { - $column->default_value = substr($column->default_value, 1, $pos-2); - } else { - $column->default_value = substr($column->default_value, 0, $pos); - } - } - } else { - $column->default_value = null; - } - $this->columns[$table][$column->name] = new database_column_info($column); - } - - return $this->columns[$table]; - } - - /** - * This method will introspect inside DB to detect it it's a UTF-8 DB or no - * Used from setup.php to set correctly "set names" when the installation - * process is performed without the initial and beautiful installer - * @return bool true if db in unicode mode - */ - function setup_is_unicodedb() { - /// Get PostgreSQL server_encoding value - $sql = "SHOW server_encoding"; - $this->query_start($sql, null, SQL_QUERY_AUX); - $rs = $this->adodb->Execute($sql); - $this->query_end($rs); - - if ($rs && !$rs->EOF) { - $encoding = $rs->fields['server_encoding']; - if (strtoupper($encoding) == 'UNICODE' || strtoupper($encoding) == 'UTF8') { - return true; - } - } - return false; - } - - /** - * Insert new record into database, as fast as possible, no safety checks, lobs not supported. - * (overloaded from adodb_moodle_database because of sequence numbers - * @param string $table name - * @param mixed $params data record as object or array - * @param bool $returnit return it of inserted record - * @param bool $bulk true means repeated inserts expected - * @param bool $customsequence true if 'id' included in $params, disables $returnid - * @return true or new id - * @throws dml_exception if error - */ - public function insert_record_raw($table, $params, $returnid=true, $bulk=false, $customsequence=false) { - if (!is_array($params)) { - $params = (array)$params; - } - - if ($customsequence) { - if (!isset($params['id'])) { - throw new coding_exception('moodle_database::insert_record_raw() id field must be specified if custom sequences used.'); - } - $returnid = false; - - } else { - unset($params['id']); - /// Postgres doesn't have the concept of primary key built in - /// and will return the OID which isn't what we want. - /// The efficient and transaction-safe strategy is to - /// move the sequence forward first, and make the insert - /// with an explicit id. - if ($returnid) { - $seqname = "{$this->prefix}{$table}_id_seq"; - $this->query_start('--adodb-GenID', null, SQL_QUERY_AUX); - $nextval = $this->adodb->GenID($seqname); - $this->query_end(true); - if ($nextval) { - $params['id'] = (int)$nextval; - } - } - } - - if (empty($params)) { - throw new coding_exception('moodle_database::insert_record_raw() no fields found.'); - } - - $fields = implode(',', array_keys($params)); - $qms = array_fill(0, count($params), '?'); - $qms = implode(',', $qms); - - $sql = "INSERT INTO {$this->prefix}$table ($fields) VALUES($qms)"; - $this->query_start($sql, $params, SQL_QUERY_INSERT); - $rs = $this->adodb->Execute($sql, $params); - $this->query_end($rs); - - if (!$returnid) { - return true; - } - if (!empty($params['id'])) { - return (int)$params['id']; - } - - $oid = $this->adodb->Insert_ID(); - - // try to get the primary key based on id - $sql = "SELECT id FROM {$this->prefix}$table WHERE oid = $oid"; - $this->query_start($sql, $params, SQL_QUERY_AUX); - $rs = $this->adodb->Execute($sql, $params); - $this->query_end($rs); - - if ( $rs && ($rs->RecordCount() == 1) ) { - trigger_error("Retrieved id using oid on table $table because we could not find the sequence."); - return (integer)reset($rs->fields); - } - throw new dml_write_exception('unknown error fetching inserted id'); - } - - /** - * Insert a record into a table and return the "id" field if required, - * Some conversions and safety checks are carried out. Lobs are supported. - * If the return ID isn't required, then this just reports success as true/false. - * $data is an object containing needed data - * @param string $table The database table to be inserted into - * @param object $data A data object with values for one or more fields in the record - * @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. - * @param bool $bulk true means repeated inserts expected - * @return true or new id - * @throws dml_exception if error - */ - public function insert_record($table, $dataobject, $returnid=true, $bulk=false) { - //TODO: add support for blobs BYTEA - if (!is_object($dataobject)) { - $dataobject = (object)$dataobject; - } - - $columns = $this->get_columns($table); - unset($dataobject->id); - $cleaned = array(); - $blobs = array(); - - foreach ($dataobject as $field=>$value) { - if (!isset($columns[$field])) { - continue; - } - $column = $columns[$field]; - if ($column->meta_type == 'B') { - if (is_null($value)) { - $cleaned[$field] = null; - } else { - $blobs[$field] = $value; - $cleaned[$field] = '@#BLOB#@'; - } - continue; - - } else if (is_bool($value)) { - $value = (int)$value; // prevent false '' problems - - } else if ($value === '') { - if ($column->meta_type == 'I' or $column->meta_type == 'F' or $column->meta_type == 'N') { - $value = 0; // prevent '' problems in numeric fields - } - } - - $cleaned[$field] = $value; - } - - if (empty($blobs)) { - return $this->insert_record_raw($table, $cleaned, $returnid, $bulk); - } - - $id = $this->insert_record_raw($table, $cleaned, true, $bulk); - - foreach ($blobs as $key=>$value) { - $this->query_start('--adodb-UpdateBlob', null, SQL_QUERY_UPDATE); - $result = $this->adodb->UpdateBlob($this->prefix.$table, $key, $value, "id = $id", 'BLOB');// adodb does not use bound parameters for blob updates :-( - $this->query_end($result); - } - - return ($returnid ? $id : true); - } - - /** - * Update a record in a table - * - * $dataobject is an object containing needed data - * Relies on $dataobject having a variable "id" to - * specify the record to update - * - * @param string $table The database table to be checked against. - * @param object $dataobject An object with contents equal to fieldname=>fieldvalue. Must have an entry for 'id' to map to the table specified. - * @param bool true means repeated updates expected - * @return bool true - * @throws dml_exception if error - */ - public function update_record($table, $dataobject, $bulk=false) { - //TODO: add support for blobs BYTEA - if (!is_object($dataobject)) { - $dataobject = (object)$dataobject; - } - - $columns = $this->get_columns($table); - $cleaned = array(); - $blobs = array(); - - foreach ($dataobject as $field=>$value) { - if (!isset($columns[$field])) { - continue; - } - $column = $columns[$field]; - if ($column->meta_type == 'B') { - if (is_null($value)) { - $cleaned[$field] = null; - } else { - $blobs[$field] = $value; - $cleaned[$field] = '@#BLOB#@'; - } - continue; - - } else if (is_bool($value)) { - $value = (int)$value; // prevent "false" problems - - } else if ($value === '') { - if ($column->meta_type == 'I' or $column->meta_type == 'F' or $column->meta_type == 'N') { - $value = 0; // prevent '' problems in numeric fields - } - } - $cleaned[$field] = $value; - } - - $this->update_record_raw($table, $cleaned, $bulk); - - if (empty($blobs)) { - return true; - } - - $id = $dataobject->id; - - foreach ($blobs as $key=>$value) { - $this->query_start('--adodb-UpdateBlob', null, SQL_QUERY_UPDATE); - $result = $this->adodb->UpdateBlob($this->prefix.$table, $key, $value, "id = $id", 'BLOB');// adodb does not use bound parameters for blob updates :-( - $this->query_end($result); - } - - return true; - } - - /** - * Set a single field in every table row where the select statement evaluates to true. - * - * @param string $table The database table to be checked against. - * @param string $newfield the field to set. - * @param string $newvalue the value to set the field to. - * @param string $select A fragment of SQL to be used in a where clause in the SQL call. - * @param array $params array of sql parameters - * @return bool true - * @throws dml_exception if error - */ - public function set_field_select($table, $newfield, $newvalue, $select, array $params=null) { - $params = (array)$params; - list($select, $params, $type) = $this->fix_sql_params($select, $params); - - $columns = $this->get_columns($table); - $column = $columns[$newfield]; - - if ($column->meta_type == 'B') { - /// update blobs and return - $select = $this->emulate_bound_params($select, $params); // adodb does not use bound parameters for blob updates :-( - $this->query_start('--adodb-UpdateBlob', null, SQL_QUERY_UPDATE); - $result = $this->adodb->UpdateBlob($this->prefix.$table, $newfield, $newvalue, $select, 'BLOB'); - $this->query_end($result); - return true; - } - - if ($select) { - $select = "WHERE $select"; - } - - /// normal field update - if (is_null($newvalue)) { - $newfield = "$newfield = NULL"; - } else { - if (is_bool($newvalue)) { - $newvalue = (int)$newvalue; // prevent "false" problems - } else if ($newvalue === '') { - if ($column->meta_type == 'I' or $column->meta_type == 'F' or $column->meta_type == 'N') { - $newvalue = 0; // prevent '' problems in numeric fields - } - } - - $newfield = "$newfield = ?"; - array_unshift($params, $newvalue); // add as first param - } - $sql = "UPDATE {$this->prefix}$table SET $newfield $select"; - $this->query_start($sql, $params, SQL_QUERY_UPDATE); - $rs = $this->adodb->Execute($sql, $params); - $this->query_end($rs); - - return true; - } - - public function sql_ilike() { - return 'ILIKE'; - } - - public function sql_concat() { - $args = func_get_args(); - /// PostgreSQL requires at least one char element in the concat, let's add it - /// here (at the beginning of the array) until ADOdb fixes it - if (is_array($args)) { - array_unshift($args , "''"); - } - return call_user_func_array(array($this->adodb, 'Concat'), $args); - } - - public function sql_bitxor($int1, $int2) { - return '(' . $this->sql_bitor($int1, $int2) . ' - ' . $this->sql_bitand($int1, $int2) . ')'; - } - - public function sql_cast_char2int($fieldname, $text=false) { - return ' CAST(' . $fieldname . ' AS INT) '; - } - - public function sql_cast_char2real($fieldname, $text=false) { - return " $fieldname::real "; - } - - /** - * Does this driver suppoer regex syntax when searching - */ - public function sql_regex_supported() { - return true; - } - - /** - * Return regex positive or negative match sql - * @param bool $positivematch - * @return string or empty if not supported - */ - public function sql_regex($positivematch=true) { - return $positivematch ? '~*' : '!~*'; - } - - /** - * Import a record into a table, id field is required. - * Basic safety checks only. Lobs are supported. - * @param string $table name of database table to be inserted into - * @param mixed $dataobject object or array with fields in the record - * @return bool true - * @throws dml_exception if error - */ - public function import_record($table, $dataobject) { - $dataobject = (object)$dataobject; - - $columns = $this->get_columns($table); - $cleaned = array(); - $blobs = array(); - - foreach ($dataobject as $field=>$value) { - if (!isset($columns[$field])) { - continue; - } - $column = $columns[$field]; - if ($column->meta_type == 'B') { - if (!is_null($value)) { - $blobs[$field] = $value; - $cleaned[$field] = '@#BLOB#@'; - continue; - } - } - $cleaned[$field] = $value; - } - - $this->insert_record_raw($table, $cleaned, false, true, true); - - if (empty($blobs)) { - return true; - } - - /// We have BLOBs to postprocess - - foreach ($blobs as $key=>$value) { - $this->query_start('--adodb-UpdateBlob', null, SQL_QUERY_UPDATE); - $result = $this->adodb->UpdateBlob($this->prefix.$table, $key, $value, "id = $id", 'BLOB'); - $this->query_end($result); - } - - return true; - } -}