From: stronk7 Date: Mon, 21 Sep 2009 11:28:42 +0000 (+0000) Subject: MDL-17491 oci native driver: preparing to support transactions, oracle dirty hack... X-Git-Url: http://git.mjollnir.org/gw?a=commitdiff_plain;h=8089f8f67ec4f0d35edfc9c5d2fce54322d1360f;p=moodle.git MDL-17491 oci native driver: preparing to support transactions, oracle dirty hack and others. WIP --- diff --git a/lib/dml/oci_native_moodle_database.php b/lib/dml/oci_native_moodle_database.php index 5cda7c2488..15513e5f95 100644 --- a/lib/dml/oci_native_moodle_database.php +++ b/lib/dml/oci_native_moodle_database.php @@ -30,13 +30,17 @@ require_once($CFG->libdir.'/dml/oci_native_moodle_recordset.php'); /** * Native oci class representing moodle database interface. + * + * One complete reference for PHP + OCI: + * http://www.oracle.com/technology/tech/php/underground-php-oracle-manual.html */ class oci_native_moodle_database extends moodle_database { protected $oci = null; - protected $bytea_oid = null; private $last_stmt_error = null; // To store stmt errors and enable get_last_error() to detect them + private $commit_status = OCI_COMMIT_ON_SUCCESS; // Autocommit ON by default. Switching to OFF (OCI_DEFAULT) + // when playing with transactions /** * Detects if all needed PHP stuff installed. @@ -149,9 +153,9 @@ class oci_native_moodle_database extends moodle_database { ob_start(); if (empty($this->dboptions['dbpersit'])) { - $this->oci = oci_connect($this->dbuser, $this->dbpass, $this->dbname, 'UTF-8'); + $this->oci = oci_connect($this->dbuser, $this->dbpass, $this->dbname, 'AL32UTF8'); } else { - $this->oci = oci_pconnect($this->dbuser, $this->dbpass, $this->dbname, 'UTF-8'); + $this->oci = oci_pconnect($this->dbuser, $this->dbpass, $this->dbname, 'AL32UTF8'); } $dberr = ob_get_contents(); ob_end_clean(); @@ -269,7 +273,7 @@ class oci_native_moodle_database extends moodle_database { protected function parse_query($sql) { $stmt = oci_parse($this->oci, $sql); - if ($stmt === false) { + if ($stmt == false) { throw new dml_connection_exception('Can not parse sql query'); //TODO: maybe add better info } return $stmt; @@ -289,7 +293,8 @@ class oci_native_moodle_database extends moodle_database { AND TABLE_NAME LIKE '$prefix%' ESCAPE '\\'"; $this->query_start($sql, null, SQL_QUERY_AUX); $stmt = $this->parse_query($sql); - $result = oci_execute($stmt); + oci_num_fields($stmt); + $result = oci_execute($stmt, $this->commit_status); $this->query_end($result, $stmt); $records = null; oci_fetch_all($stmt, $records, 0, -1, OCI_ASSOC); @@ -322,7 +327,7 @@ class oci_native_moodle_database extends moodle_database { ORDER BY i.INDEX_NAME, c.COLUMN_POSITION"; $stmt = $this->parse_query($sql); - $result = oci_execute($stmt); + $result = oci_execute($stmt, $this->commit_status); $this->query_end($result, $stmt); $records = null; oci_fetch_all($stmt, $records, 0, -1, OCI_FETCHSTATEMENT_BY_ROW); @@ -367,7 +372,7 @@ class oci_native_moodle_database extends moodle_database { $this->query_start($sql, null, SQL_QUERY_AUX); $stmt = $this->parse_query($sql); - $result = oci_execute($stmt); + $result = oci_execute($stmt, $this->commit_status); $this->query_end($result, $stmt); $records = null; oci_fetch_all($stmt, $records, 0, -1, OCI_FETCHSTATEMENT_BY_ROW); @@ -534,6 +539,84 @@ class oci_native_moodle_database extends moodle_database { return $this->columns[$table]; } + /** + * 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; + } + /** * Is db in unicode mode? * @return bool @@ -544,7 +627,7 @@ class oci_native_moodle_database extends moodle_database { WHERE PARAMETER = 'NLS_CHARACTERSET'"; $this->query_start($sql, null, SQL_QUERY_AUX); $stmt = $this->parse_query($sql); - $result = oci_execute($stmt); + $result = oci_execute($stmt, $this->commit_status); $this->query_end($result, $stmt); $records = null; oci_fetch_all($stmt, $records, 0, -1, OCI_FETCHSTATEMENT_BY_COLUMN); @@ -564,14 +647,14 @@ class oci_native_moodle_database extends moodle_database { $this->query_start($sql, null, SQL_QUERY_STRUCTURE); $stmt = $this->parse_query($sql); - $result = oci_execute($stmt); + $result = oci_execute($stmt, $this->commit_status); $this->query_end($result, $stmt); oci_free_statement($stmt); return true; } - protected function bind_params($stmt, array $params=null, $tablename=null) { + protected function bind_params(&$stmt, array $params=null, $tablename=null) { $descriptors = array(); if ($params) { $columns = array(); @@ -656,7 +739,7 @@ class oci_native_moodle_database extends moodle_database { $this->query_start($sql, $params, SQL_QUERY_UPDATE); $stmt = $this->parse_query($sql); $this->bind_params($stmt, $params); - $result = oci_execute($stmt); + $result = oci_execute($stmt, $this->commit_status); $this->query_end($result, $stmt); oci_free_statement($stmt); @@ -719,33 +802,36 @@ class oci_native_moodle_database extends moodle_database { list($sql, $params, $type) = $this->fix_sql_params($sql, $params); if ($limitfrom and $limitnum) { - $sql = "SELECT oracle_o.* - FROM (SELECT oracle_i.*, rownum AS oracle_rownum - FROM ($sql) oracle_i - ) oracle_o - WHERE rownum <= :oracle_max AND oracle_rownum > :oracle_min"; + $rawsql = "SELECT oracle_o.* + FROM (SELECT oracle_i.*, rownum AS oracle_rownum + FROM ($sql) oracle_i + ) oracle_o + WHERE rownum <= :oracle_max AND oracle_rownum > :oracle_min"; $params['oracle_max'] = $limitfrom + $limitnum; $params['oracle_min'] = $limitfrom; } else if ($limitfrom and !$limitnum) { - $sql = "SELECT oracle_o.* - FROM (SELECT oracle_i.*, rownum AS oracle_rownum - FROM ($sql) oracle_i - ) oracle_o - WHERE oracle_rownum > :oracle_min"; + $rawsql = "SELECT oracle_o.* + FROM (SELECT oracle_i.*, rownum AS oracle_rownum + FROM ($sql) oracle_i + ) oracle_o + WHERE oracle_rownum > :oracle_min"; $params['oracle_min'] = $limitfrom; } else if (!$limitfrom and $limitnum) { - $sql = "SELECT * - FROM ($sql) - WHERE rownum <= :oracle_max"; + $rawsql = "SELECT * + FROM ($sql) + WHERE rownum <= :oracle_max"; $params['oracle_max'] = $limitnum; + + } else { // No limitfrom nor limitnum + $rawsql = $sql; } $this->query_start($sql, $params, SQL_QUERY_SELECT); - $stmt = $this->parse_query($sql); + $stmt = $this->parse_query($rawsql); $this->bind_params($stmt, $params); - $result = oci_execute($stmt); + $result = oci_execute($stmt, $this->commit_status); $this->query_end($result, $stmt); return $this->create_recordset($stmt); @@ -804,7 +890,7 @@ class oci_native_moodle_database extends moodle_database { $this->query_start($sql, $params, SQL_QUERY_SELECT); $stmt = $this->parse_query($sql); $this->bind_params($stmt, $params); - $result = oci_execute($stmt); + $result = oci_execute($stmt, $this->commit_status); $this->query_end($result, $stmt); $records = null; @@ -842,7 +928,7 @@ class oci_native_moodle_database extends moodle_database { $this->query_start($sql, $params, SQL_QUERY_SELECT); $stmt = $this->parse_query($sql); $this->bind_params($stmt, $params); - $result = oci_execute($stmt); + $result = oci_execute($stmt, $this->commit_status); $this->query_end($result, $stmt); $records = null; @@ -904,7 +990,7 @@ class oci_native_moodle_database extends moodle_database { if ($returning) { oci_bind_by_name($stmt, ":oracle_id", $id, -1, SQLT_LNG); } - $result = oci_execute($stmt); + $result = oci_execute($stmt, $this->commit_status); $this->free_descriptors($descriptors); $this->query_end($result, $stmt); oci_free_statement($stmt); @@ -937,10 +1023,12 @@ class oci_native_moodle_database extends moodle_database { $dataobject = (object)$dataobject; } - $columns = $this->get_columns($table); - unset($dataobject->id); + + $columns = $this->get_columns($table); $cleaned = array(); + $blobs = array(); + $clobs = array(); foreach ($dataobject as $field=>$value) { if (!isset($columns[$field])) { @@ -1014,7 +1102,7 @@ class oci_native_moodle_database extends moodle_database { $this->query_start($sql, $params, SQL_QUERY_UPDATE); $stmt = $this->parse_query($sql); $descriptors = $this->bind_params($stmt, $params, $table); - $result = oci_execute($stmt); + $result = oci_execute($stmt, $this->commit_status); $this->query_end($result, $stmt); $this->free_descriptors($descriptors); oci_free_statement($stmt); @@ -1088,7 +1176,7 @@ class oci_native_moodle_database extends moodle_database { $this->query_start($sql, $params, SQL_QUERY_UPDATE); $stmt = $this->parse_query($sql); $descriptors = $this->bind_params($stmt, $params, $table); - $result = oci_execute($stmt); + $result = oci_execute($stmt, $this->commit_status); $this->query_end($result, $stmt); $this->free_descriptors($descriptors); oci_free_statement($stmt); @@ -1116,7 +1204,7 @@ class oci_native_moodle_database extends moodle_database { $this->query_start($sql, $params, SQL_QUERY_UPDATE); $stmt = $this->parse_query($sql); $this->bind_params($stmt, $params); - $result = oci_execute($stmt); + $result = oci_execute($stmt, $this->commit_status); $this->query_end($result, $stmt); oci_free_statement($stmt); @@ -1207,7 +1295,7 @@ class oci_native_moodle_database extends moodle_database { } } - function sql_empty() { + public function sql_empty() { return ' '; } @@ -1230,7 +1318,7 @@ class oci_native_moodle_database extends moodle_database { $sql = "BEGIN"; $this->query_start($sql, NULL, SQL_QUERY_AUX); $stmt = $this->parse_query($sql); - $result = oci_execute($stmt); + $result = oci_execute($stmt, $this->commit_status); $this->query_end($result, $stmt); oci_free_statement($stmt); return true; @@ -1248,7 +1336,7 @@ class oci_native_moodle_database extends moodle_database { $sql = "COMMIT"; $this->query_start($sql, NULL, SQL_QUERY_AUX); $stmt = $this->parse_query($sql); - $result = oci_execute($stmt); + $result = oci_execute($stmt, $this->commit_status); $this->query_end($result, $stmt); oci_free_statement($stmt); return true; @@ -1266,7 +1354,7 @@ class oci_native_moodle_database extends moodle_database { $sql = "ROLLBACK"; $this->query_start($sql, NULL, SQL_QUERY_AUX); $stmt = $this->parse_query($sql); - $result = oci_execute($stmt); + $result = oci_execute($stmt, $this->commit_status); $this->query_end($result, $stmt); oci_free_statement($stmt); return true;