diff --git a/lib/dml/adodb_moodle_database.php b/lib/dml/adodb_moodle_database.php deleted file mode 100644 index d4b6ac0b446..00000000000 --- 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 a3188e6fa86..00000000000 --- 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 50c453729a3..ca376eb499c 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 666348ace0f..00000000000 --- 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 b37cba369a9..00000000000 --- 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 fac9a8ecd01..c524021a974 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 edbe5393751..00000000000 --- 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 3126a162ab7..00000000000 --- 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 1aa094b83a4..280ba2ae1d8 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 7f668469e97..00000000000 --- 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 b5fee63f9ee..00000000000 --- 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 b2e9094a958..9753cd52674 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 972be93017d..00000000000 --- 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 72348fd96ba..bcd79a70e27 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 ee0242b0f1e..98becbc6289 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 73c03e5abc6..00000000000 --- 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; - } -}