Merge branch 'w13_MDL-37742_m25_dirtyoracle' of git://github.com/skodak/moodle
authorEloy Lafuente (stronk7) <stronk7@moodle.org>
Tue, 26 Mar 2013 22:04:48 +0000 (23:04 +0100)
committerEloy Lafuente (stronk7) <stronk7@moodle.org>
Tue, 26 Mar 2013 22:04:48 +0000 (23:04 +0100)
Conflicts:
lib/upgrade.txt

15 files changed:
blocks/rss_client/edit_form.php
cohort/lib.php
enrol/database/lib.php
enrol/manual/locallib.php
enrol/manual/tests/lib_test.php
enrol/meta/locallib.php
lib/accesslib.php
lib/blocklib.php
lib/datalib.php
lib/dml/moodle_database.php
lib/dml/oci_native_moodle_database.php
lib/dml/oci_native_moodle_package.sql
lib/dml/tests/dml_test.php
lib/upgrade.txt
user/filters/text.php

index 922734a..e696c46 100644 (file)
@@ -54,7 +54,7 @@ class block_rss_client_edit_form extends block_edit_form {
                 FROM {block_rss_client}
                 WHERE userid = ? OR shared = 1
                 ORDER BY CASE WHEN preferredtitle = ? THEN ' . $DB->sql_compare_text('title', 64) . ' ELSE preferredtitle END ',
-                array($DB->sql_empty(), $USER->id, $DB->sql_empty()));
+                array('', $USER->id, ''));
         if ($rssfeeds) {
             $select = $mform->addElement('select', 'config_rssid', get_string('choosefeedlabel', 'block_rss_client'), $rssfeeds);
             $select->setMultiple(true);
index 19441a5..61d56c9 100644 (file)
@@ -40,7 +40,6 @@ function cohort_add_cohort($cohort) {
         $cohort->idnumber = NULL;
     }
     if (!isset($cohort->description)) {
-        // sql_empty() does not belong here, this crazy Oracle hack is implemented in insert_record()!
         $cohort->description = '';
     }
     if (!isset($cohort->descriptionformat)) {
index 5a88859..fc57b58 100644 (file)
@@ -407,7 +407,7 @@ class enrol_database_plugin extends enrol_plugin {
             $localnotempty = "";
             if ($localcoursefield !== 'id') {
                 $localnotempty =  "AND c.$localcoursefield <> :lcfe";
-                $params['lcfe'] = $DB->sql_empty();
+                $params['lcfe'] = '';
             }
             $sql = "SELECT c.id, c.visible, c.$localcoursefield AS mapping, c.shortname
                       FROM {course} c
index 80631b2..5c3e345 100644 (file)
@@ -411,7 +411,7 @@ function enrol_manual_migrate_plugin_enrolments($enrol) {
         }
 
         // First delete potential role duplicates.
-        $params = array('id'=>$e->id, 'component'=>'enrol_'.$enrol, 'empty'=>$DB->sql_empty());
+        $params = array('id'=>$e->id, 'component'=>'enrol_'.$enrol, 'empty'=>'');
         $sql = "SELECT ra.id
                   FROM {role_assignments} ra
                   JOIN {role_assignments} mra ON (mra.contextid = ra.contextid AND mra.userid = ra.userid AND mra.roleid = ra.roleid AND mra.component = :empty AND mra.itemid = 0)
@@ -425,7 +425,7 @@ function enrol_manual_migrate_plugin_enrolments($enrol) {
         $sql = "UPDATE {role_assignments}
                    SET itemid = 0, component = :empty
                  WHERE itemid = :id AND component = :component";
-        $params = array('empty'=>$DB->sql_empty(), 'id'=>$e->id, 'component'=>'enrol_'.$enrol);
+        $params = array('empty'=>'', 'id'=>$e->id, 'component'=>'enrol_'.$enrol);
         $DB->execute($sql, $params);
 
         // Delete potential enrol duplicates.
index 9212b33..77ffc6b 100644 (file)
@@ -151,11 +151,11 @@ class enrol_manual_lib_testcase extends advanced_testcase {
         $this->assertTrue($DB->record_exists('user_enrolments', array('enrolid'=>$maninstance1->id, 'userid'=>$user4->id, 'status'=>ENROL_USER_ACTIVE)));
         $this->assertEquals(4, $DB->count_records('user_enrolments', array('enrolid'=>$maninstance1->id)));
         $this->assertEquals(0, $DB->count_records('user_enrolments', array('enrolid'=>$xxxinstance1->id)));
-        $this->assertTrue($DB->record_exists('role_assignments', array('itemid'=>0, 'component'=>$DB->sql_empty(), 'userid'=>$user1->id, 'roleid'=>$studentrole->id, 'contextid'=>$context1->id)));
-        $this->assertTrue($DB->record_exists('role_assignments', array('itemid'=>0, 'component'=>$DB->sql_empty(), 'userid'=>$user1->id, 'roleid'=>$teacherrole->id, 'contextid'=>$context1->id)));
-        $this->assertTrue($DB->record_exists('role_assignments', array('itemid'=>0, 'component'=>$DB->sql_empty(), 'userid'=>$user2->id, 'roleid'=>$studentrole->id, 'contextid'=>$context1->id)));
-        $this->assertTrue($DB->record_exists('role_assignments', array('itemid'=>0, 'component'=>$DB->sql_empty(), 'userid'=>$user3->id, 'roleid'=>$teacherrole->id, 'contextid'=>$context1->id)));
-        $this->assertTrue($DB->record_exists('role_assignments', array('itemid'=>0, 'component'=>$DB->sql_empty(), 'userid'=>$user4->id, 'roleid'=>$studentrole->id, 'contextid'=>$context1->id)));
+        $this->assertTrue($DB->record_exists('role_assignments', array('itemid'=>0, 'component'=>'', 'userid'=>$user1->id, 'roleid'=>$studentrole->id, 'contextid'=>$context1->id)));
+        $this->assertTrue($DB->record_exists('role_assignments', array('itemid'=>0, 'component'=>'', 'userid'=>$user1->id, 'roleid'=>$teacherrole->id, 'contextid'=>$context1->id)));
+        $this->assertTrue($DB->record_exists('role_assignments', array('itemid'=>0, 'component'=>'', 'userid'=>$user2->id, 'roleid'=>$studentrole->id, 'contextid'=>$context1->id)));
+        $this->assertTrue($DB->record_exists('role_assignments', array('itemid'=>0, 'component'=>'', 'userid'=>$user3->id, 'roleid'=>$teacherrole->id, 'contextid'=>$context1->id)));
+        $this->assertTrue($DB->record_exists('role_assignments', array('itemid'=>0, 'component'=>'', 'userid'=>$user4->id, 'roleid'=>$studentrole->id, 'contextid'=>$context1->id)));
         $this->assertEquals(5, $DB->count_records('role_assignments', array('contextid'=>$context1->id)));
 
 
@@ -173,7 +173,7 @@ class enrol_manual_lib_testcase extends advanced_testcase {
         $this->assertTrue($DB->record_exists('user_enrolments', array('enrolid'=>$maninstance3->id, 'userid'=>$user2->id, 'status'=>ENROL_USER_SUSPENDED)));
         $this->assertEquals(2, $DB->count_records('user_enrolments', array('enrolid'=>$maninstance3->id)));
         $this->assertEquals(0, $DB->count_records('user_enrolments', array('enrolid'=>$xxxinstance3->id)));
-        $this->assertTrue($DB->record_exists('role_assignments', array('itemid'=>0, 'component'=>$DB->sql_empty(), 'userid'=>$user1->id, 'roleid'=>$studentrole->id, 'contextid'=>$context3->id)));
+        $this->assertTrue($DB->record_exists('role_assignments', array('itemid'=>0, 'component'=>'', 'userid'=>$user1->id, 'roleid'=>$studentrole->id, 'contextid'=>$context3->id)));
         $this->assertEquals(1, $DB->count_records('role_assignments', array('contextid'=>$context3->id)));
 
 
@@ -187,10 +187,10 @@ class enrol_manual_lib_testcase extends advanced_testcase {
         $this->assertEquals(3, $DB->count_records('user_enrolments', array('enrolid'=>$maninstance4->id)));
         $this->assertEquals(0, $DB->count_records('user_enrolments', array('enrolid'=>$xxxinstance4->id)));
         $this->assertEquals(0, $DB->count_records('user_enrolments', array('enrolid'=>$xxxinstance4b->id)));
-        $this->assertTrue($DB->record_exists('role_assignments', array('itemid'=>0, 'component'=>$DB->sql_empty(), 'userid'=>$user1->id, 'roleid'=>$studentrole->id, 'contextid'=>$context4->id)));
-        $this->assertTrue($DB->record_exists('role_assignments', array('itemid'=>0, 'component'=>$DB->sql_empty(), 'userid'=>$user1->id, 'roleid'=>$teacherrole->id, 'contextid'=>$context4->id)));
-        $this->assertTrue($DB->record_exists('role_assignments', array('itemid'=>0, 'component'=>$DB->sql_empty(), 'userid'=>$user2->id, 'roleid'=>$studentrole->id, 'contextid'=>$context4->id)));
-        $this->assertTrue($DB->record_exists('role_assignments', array('itemid'=>0, 'component'=>$DB->sql_empty(), 'userid'=>$user4->id, 'roleid'=>$teacherrole->id, 'contextid'=>$context4->id)));
+        $this->assertTrue($DB->record_exists('role_assignments', array('itemid'=>0, 'component'=>'', 'userid'=>$user1->id, 'roleid'=>$studentrole->id, 'contextid'=>$context4->id)));
+        $this->assertTrue($DB->record_exists('role_assignments', array('itemid'=>0, 'component'=>'', 'userid'=>$user1->id, 'roleid'=>$teacherrole->id, 'contextid'=>$context4->id)));
+        $this->assertTrue($DB->record_exists('role_assignments', array('itemid'=>0, 'component'=>'', 'userid'=>$user2->id, 'roleid'=>$studentrole->id, 'contextid'=>$context4->id)));
+        $this->assertTrue($DB->record_exists('role_assignments', array('itemid'=>0, 'component'=>'', 'userid'=>$user4->id, 'roleid'=>$teacherrole->id, 'contextid'=>$context4->id)));
         $this->assertEquals(4, $DB->count_records('role_assignments', array('contextid'=>$context4->id)));
 
 
index fa3125c..3d16b0e 100644 (file)
@@ -569,7 +569,7 @@ function enrol_meta_sync($courseid = NULL, $verbose = false) {
         }
         $enabled[$k] = 'enrol_'.$v;
     }
-    $enabled[] = $DB->sql_empty(); // manual assignments are replicated too
+    $enabled[] = ''; // manual assignments are replicated too
 
     $onecourse = $courseid ? "AND e.courseid = :courseid" : "";
     list($enabled, $params) = $DB->get_in_or_equal($enabled, SQL_PARAMS_NAMED, 'e');
index 4b7432d..7d17974 100644 (file)
@@ -1634,8 +1634,6 @@ function role_assign($roleid, $userid, $contextid, $component = '', $itemid = 0,
     }
 
     // Check for existing entry
-    // TODO: Revisit this sql_empty() use once Oracle bindings are improved. MDL-29765
-    $component = ($component === '') ? $DB->sql_empty() : $component;
     $ras = $DB->get_records('role_assignments', array('roleid'=>$roleid, 'contextid'=>$context->id, 'userid'=>$userid, 'component'=>$component, 'itemid'=>$itemid), 'id');
 
     if ($ras) {
@@ -1748,10 +1746,6 @@ function role_unassign_all(array $params, $subcontexts = false, $includemanual =
         }
     }
 
-    // TODO: Revisit this sql_empty() use once Oracle bindings are improved. MDL-29765
-    if (isset($params['component'])) {
-        $params['component'] = ($params['component'] === '') ? $DB->sql_empty() : $params['component'];
-    }
     $ras = $DB->get_records('role_assignments', $params);
     foreach($ras as $ra) {
         $DB->delete_records('role_assignments', array('id'=>$ra->id));
index f374414..f96ae31 100644 (file)
@@ -587,8 +587,8 @@ class block_manager {
             'pagetype' => $this->page->pagetype,
         );
         if ($this->page->subpage === '') {
-            $params['subpage1'] = $DB->sql_empty();
-            $params['subpage2'] = $DB->sql_empty();
+            $params['subpage1'] = '';
+            $params['subpage2'] = '';
         }
         $sql = "SELECT
                     bi.id,
index 2668776..9d25748 100644 (file)
@@ -853,7 +853,8 @@ function get_courses_search($searchterms, $sort, $page, $recordsperpage, &$total
     if ($DB->get_dbfamily() == 'oracle') {
         $concat = $DB->sql_concat('c.summary', "' '", 'c.fullname', "' '", 'c.idnumber', "' '", 'c.shortname');
     } else {
-        $concat = $DB->sql_concat("COALESCE(c.summary, '". $DB->sql_empty() ."')", "' '", 'c.fullname', "' '", 'c.idnumber', "' '", 'c.shortname');
+        $concat = $DB->sql_concat("COALESCE(c.summary, :empty)", "' '", 'c.fullname', "' '", 'c.idnumber', "' '", 'c.shortname');
+        $params['empty'] = '';
     }
 
     foreach ($searchterms as $searchterm) {
index 3dc9c55..c7753a5 100644 (file)
@@ -2012,12 +2012,14 @@ abstract class moodle_database {
     }
 
     /**
-     * Returns the empty string char used by every supported DB. To be used when
-     * we are searching for that values in our queries. Only Oracle uses this
-     * for now (will be out, once we migrate to proper NULLs if that days arrives)
+     * This used to return empty string replacement character.
+     *
+     * @deprecated use bound parameter with empty string instead
+     *
      * @return string An empty string.
      */
     function sql_empty() {
+        debugging("sql_empty() is deprecated, please use empty string '' as sql parameter value instead", DEBUG_DEVELOPER);
         return '';
     }
 
@@ -2036,9 +2038,13 @@ abstract class moodle_database {
      *
      *     ... AND fieldname = '';
      *
-     * are being used. Final result should be:
+     * are being used. Final result for text fields should be:
+     *
+     *     ... AND ' . sql_isempty('tablename', 'fieldname', true/false, true);
      *
-     *     ... AND ' . sql_isempty('tablename', 'fieldname', true/false, true/false);
+     * and for varchar fields result should be:
+     *
+     *    ... AND fieldname = :empty; "; $params['empty'] = '';
      *
      * (see parameters description below)
      *
@@ -2066,10 +2072,14 @@ abstract class moodle_database {
      *
      *     ... AND fieldname != '';
      *
-     * are being used. Final result should be:
+     * are being used. Final result for text fields should be:
      *
      *     ... AND ' . sql_isnotempty('tablename', 'fieldname', true/false, true/false);
      *
+     * and for varchar fields result should be:
+     *
+     *    ... AND fieldname != :empty; "; $params['empty'] = '';
+     *
      * (see parameters description below)
      *
      * @param string $tablename Name of the table (without prefix). This is not used for now but can be
index eaafb54..012dc23 100644 (file)
@@ -52,9 +52,7 @@ class oci_native_moodle_database extends moodle_database {
     /** @var To store unique_session_id. Needed for temp tables unique naming.*/
     private $unique_session_id;
     /** @var To cache locks support along the connection life.*/
-    private $dblocks_supported = null;
-    /** @var To cache bitwise operations support along the connection life.*/
-    private $bitwise_supported = null;
+    private $oci_package_installed = null;
 
     /**
      * Detects if all needed PHP stuff installed.
@@ -129,7 +127,7 @@ class oci_native_moodle_database extends moodle_database {
      * @return string null means everything ok, string means problem found.
      */
     public function diagnose() {
-        if (!$this->bitwise_supported() or !$this->session_lock_supported()) {
+        if (!$this->oci_package_installed()) {
             return 'Oracle PL/SQL Moodle support packages are not installed! Database administrator has to execute /lib/dml/oci_native_moodle_package.sql script.';
         }
         return null;
@@ -758,10 +756,27 @@ class oci_native_moodle_database extends moodle_database {
      */
     private function oracle_dirty_hack ($table, $field, $value) {
 
+        // General bound parameter, just hack the spaces and pray it will work.
+        if (!$table) {
+            if ($value === '') {
+                return ' ';
+            } else if (is_bool($value)) {
+                return (int)$value;
+            } else {
+                return $value;
+            }
+        }
+
         // Get metadata
         $columns = $this->get_columns($table);
         if (!isset($columns[$field])) {
-            return $value;
+            if ($value === '') {
+                return ' ';
+            } else if (is_bool($value)) {
+                return (int)$value;
+            } else {
+                return $value;
+            }
         }
         $column = $columns[$field];
 
@@ -778,7 +793,7 @@ class oci_native_moodle_database extends moodle_database {
         // In the opposite, when retrieving records from Oracle, we'll decode " " back to
         // empty strings to allow everything to work properly. DIRTY HACK.
 
-        // !! These paragraphs explain the rationale about the change for Moodle 2.0:
+        // !! These paragraphs explain the rationale about the change for Moodle 2.5:
         //
         // Before Moodle 2.0, we only used to apply this DIRTY HACK to NOT NULL columns, as
         // stated above, but it causes one problem in NULL columns where both empty strings
@@ -791,19 +806,17 @@ class oci_native_moodle_database extends moodle_database {
         // to rely in NULL/empty/content contents without problems, until now that wasn't
         // possible at all.
         //
-        // No breakage with old data is expected as long as at the time of writing this
-        // (20090922) all the current uses of both sql_empty() and sql_isempty() has been
-        // revised in 2.0 and all them were being performed against NOT NULL columns,
-        // where nothing has changed (the DIRTY HACK was already being applied).
+        // One space DIRTY HACK is now applied automatically for all query parameters
+        // and results. The only problem is string concatenation where the glue must
+        // be specified as "' '" sql fragment.
         //
         // !! Conclusions:
         //
-        // From Moodle 2.0 onwards, ALL empty strings in Oracle DBs will be stored as
+        // From Moodle 2.5 onwards, ALL empty strings in Oracle DBs will be stored as
         // 1-whitespace char, ALL NULLs as NULLs and, obviously, content as content. And
         // those 1-whitespace chars will be converted back to empty strings by all the
         // get_field/record/set() functions transparently and any SQL needing direct handling
-        // of empties will need to use the sql_empty() and sql_isempty() helper functions.
-        // MDL-17491.
+        // of empties will have to use placeholders or sql_isempty() helper function.
 
         // If the field isn't VARCHAR or CLOB, skip
         if ($column->meta_type != 'C' and $column->meta_type != 'X') {
@@ -1445,26 +1458,6 @@ class oci_native_moodle_database extends moodle_database {
         return ' FROM dual';
     }
 
-   protected function bitwise_supported() {
-        if (isset($this->bitwise_supported)) { // Use cached value if available
-            return $this->bitwise_supported;
-        }
-        $sql = "SELECT 1
-                FROM user_objects
-                WHERE object_type = 'PACKAGE BODY'
-                  AND object_name = 'MOODLE_BITS'
-                  AND status = 'VALID'";
-        $this->query_start($sql, null, SQL_QUERY_AUX);
-        $stmt = $this->parse_query($sql);
-        $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);
-        oci_free_statement($stmt);
-        $this->bitwise_supported = isset($records[0]) && reset($records[0]) ? true : false;
-        return $this->bitwise_supported;
-    }
-
     public function sql_bitand($int1, $int2) {
         return 'bitand((' . $int1 . '), (' . $int2 . '))';
     }
@@ -1474,18 +1467,18 @@ class oci_native_moodle_database extends moodle_database {
     }
 
     public function sql_bitor($int1, $int2) {
-        // Use the MOODLE_BITS package if available
-        if ($this->bitwise_supported()) {
-            return 'MOODLE_BITS.BITOR(' . $int1 . ', ' . $int2 . ')';
+        // Use the MOODLELIB package if available
+        if ($this->oci_package_installed()) {
+            return 'MOODLELIB.BITOR(' . $int1 . ', ' . $int2 . ')';
         }
         // fallback to PHP bool operations, can break if using placeholders
         return '((' . $int1 . ') + (' . $int2 . ') - ' . $this->sql_bitand($int1, $int2) . ')';
     }
 
     public function sql_bitxor($int1, $int2) {
-        // Use the MOODLE_BITS package if available
-        if ($this->bitwise_supported()) {
-            return 'MOODLE_BITS.BITXOR(' . $int1 . ', ' . $int2 . ')';
+        // Use the MOODLELIB package if available
+        if ($this->oci_package_installed()) {
+            return 'MOODLELIB.BITXOR(' . $int1 . ', ' . $int2 . ')';
         }
         // fallback to PHP bool operations, can break if using placeholders
         return '(' . $this->sql_bitor($int1, $int2) . ' - ' . $this->sql_bitand($int1, $int2) . ')';
@@ -1552,22 +1545,38 @@ class oci_native_moodle_database extends moodle_database {
         // NULLs and empty strings. So this function will cause some tests to fail. Hopefully
         // it's only a side case and it won't affect normal concatenation operations in Moodle.
         $arr = func_get_args();
+        if ($this->oci_package_installed()) {
+            foreach ($arr as $k => $v) {
+                if (strpos($v, "'") === 0) {
+                    continue;
+                }
+                $arr[$k] = "MOODLELIB.UNDO_DIRTY_HACK($v)";
+            }
+        }
         $s = implode(' || ', $arr);
         if ($s === '') {
-            return " '' ";
+            return " ' ' ";
         }
-        return " $s ";
+        return " MOODLELIB.DIRTY_HACK($s) ";
     }
 
     public function sql_concat_join($separator="' '", $elements=array()) {
-        for ($n=count($elements)-1; $n > 0 ; $n--) {
+        if ($this->oci_package_installed()) {
+            foreach ($elements as $k => $v) {
+                if (strpos($v, "'") === 0) {
+                    continue;
+                }
+                $elements[$k] = "MOODLELIB.UNDO_DIRTY_HACK($v)";
+            }
+        }
+        for ($n = count($elements)-1; $n > 0 ; $n--) {
             array_splice($elements, $n, 0, $separator);
         }
         $s = implode(' || ', $elements);
         if ($s === '') {
-            return " '' ";
+            return " ' ' ";
         }
-        return " $s ";
+        return " MOODLELIB.DIRTY_HACK($s) ";
     }
 
     /**
@@ -1577,36 +1586,41 @@ class oci_native_moodle_database extends moodle_database {
         return "INSTR(($haystack), ($needle))";
     }
 
+    /**
+     * Returns the SQL to know if one field is empty.
+     *
+     * @param string $tablename Name of the table (without prefix). Not used for now but can be
+     *                          necessary in the future if we want to use some introspection using
+     *                          meta information against the DB.
+     * @param string $fieldname Name of the field we are going to check
+     * @param bool $nullablefield For specifying if the field is nullable (true) or no (false) in the DB.
+     * @param bool $textfield For specifying if it is a text (also called clob) field (true) or a varchar one (false)
+     * @return string the sql code to be added to check for empty values
+     */
     public function sql_isempty($tablename, $fieldname, $nullablefield, $textfield) {
         if ($textfield) {
-            return " (".$this->sql_compare_text($fieldname)." = '".$this->sql_empty()."') ";
+            return " (".$this->sql_compare_text($fieldname)." = ' ') ";
         } else {
-            return " ($fieldname = '".$this->sql_empty()."') ";
+            return " ($fieldname = ' ') ";
         }
     }
 
-    /**
-     * Returns the empty string char used by every supported DB. To be used when
-     * we are searching for that values in our queries. Only Oracle uses this
-     * for now (will be out, once we migrate to proper NULLs if that days arrives)
-     * @return string A string with single whitespace.
-     */
-    public function sql_empty() {
-        return ' ';
-    }
-
     public function sql_order_by_text($fieldname, $numchars=32) {
         return 'dbms_lob.substr(' . $fieldname . ', ' . $numchars . ',1)';
     }
 
-    public function session_lock_supported() {
-        if (isset($this->dblocks_supported)) { // Use cached value if available
-            return $this->dblocks_supported;
+    /**
+     * Is the required OCI server package installed?
+     * @return bool
+     */
+    protected function oci_package_installed() {
+        if (isset($this->oci_package_installed)) { // Use cached value if available.
+            return $this->oci_package_installed;
         }
         $sql = "SELECT 1
                 FROM user_objects
                 WHERE object_type = 'PACKAGE BODY'
-                  AND object_name = 'MOODLE_LOCKS'
+                  AND object_name = 'MOODLELIB'
                   AND status = 'VALID'";
         $this->query_start($sql, null, SQL_QUERY_AUX);
         $stmt = $this->parse_query($sql);
@@ -1615,8 +1629,8 @@ class oci_native_moodle_database extends moodle_database {
         $records = null;
         oci_fetch_all($stmt, $records, 0, -1, OCI_FETCHSTATEMENT_BY_ROW);
         oci_free_statement($stmt);
-        $this->dblocks_supported = isset($records[0]) && reset($records[0]) ? true : false;
-        return $this->dblocks_supported;
+        $this->oci_package_installed = isset($records[0]) && reset($records[0]) ? true : false;
+        return $this->oci_package_installed;
     }
 
     /**
@@ -1626,13 +1640,13 @@ class oci_native_moodle_database extends moodle_database {
      * @return void
      */
     public function get_session_lock($rowid, $timeout) {
-        if (!$this->session_lock_supported()) {
+        if (!$this->oci_package_installed()) {
             return;
         }
         parent::get_session_lock($rowid, $timeout);
 
         $fullname = $this->dbname.'-'.$this->prefix.'-session-'.$rowid;
-        $sql = 'SELECT MOODLE_LOCKS.GET_LOCK(:lockname, :locktimeout) FROM DUAL';
+        $sql = 'SELECT MOODLELIB.GET_LOCK(:lockname, :locktimeout) FROM DUAL';
         $params = array('lockname' => $fullname , 'locktimeout' => $timeout);
         $this->query_start($sql, $params, SQL_QUERY_AUX);
         $stmt = $this->parse_query($sql);
@@ -1646,7 +1660,7 @@ class oci_native_moodle_database extends moodle_database {
     }
 
     public function release_session_lock($rowid) {
-        if (!$this->session_lock_supported()) {
+        if (!$this->oci_package_installed()) {
             return;
         }
         if (!$this->used_for_db_sessions) {
@@ -1657,7 +1671,7 @@ class oci_native_moodle_database extends moodle_database {
 
         $fullname = $this->dbname.'-'.$this->prefix.'-session-'.$rowid;
         $params = array('lockname' => $fullname);
-        $sql = 'SELECT MOODLE_LOCKS.RELEASE_LOCK(:lockname) FROM DUAL';
+        $sql = 'SELECT MOODLELIB.RELEASE_LOCK(:lockname) FROM DUAL';
         $this->query_start($sql, $params, SQL_QUERY_AUX);
         $stmt = $this->parse_query($sql);
         $this->bind_params($stmt, $params);
index 2585f71..fed15f1 100644 (file)
  * This sql script generates various PL/SQL packages needed to provide
  * cross-db compatibility in the Moodle 2.x DB API with some operations
  * not natively supported by Oracle, namely:
- *  - MOODLE_LOCKS: Application locks used by Moodle DB sessions. It uses
- *                  the DBMS_LOCK package so execution must be granted
- *                  to the Moodle DB user by SYS to work properly.
- *  - MOODLE_BITS: To provide cross-db bitwise operations to be used by the
- *                 sql_bitXXX() helper functions
+ *  - locking: Application locks used by Moodle DB sessions. It uses
+ *             the DBMS_LOCK package so execution must be granted
+ *             to the Moodle DB user by SYS to work properly.
+ *  - bit ops: To provide cross-db bitwise operations to be used by the
+ *             sql_bitXXX() helper functions
+ *  - one space hacks: One space empty string substitute hacks.
  */
 
-CREATE OR REPLACE PACKAGE MOODLE_BITS AS
+CREATE OR REPLACE PACKAGE MOODLELIB AS
 
 FUNCTION BITOR (value1 IN INTEGER, value2 IN INTEGER) RETURN INTEGER;
 FUNCTION BITXOR(value1 IN INTEGER, value2 IN INTEGER) RETURN INTEGER;
 
-END MOODLE_BITS;
+FUNCTION GET_HANDLE  (lock_name IN VARCHAR2) RETURN VARCHAR2;
+FUNCTION GET_LOCK    (lock_name IN VARCHAR2, lock_timeout IN INTEGER) RETURN INTEGER;
+FUNCTION RELEASE_LOCK(lock_name IN VARCHAR2) RETURN INTEGER;
+
+FUNCTION DIRTY_HACK(somestring IN VARCHAR2) RETURN VARCHAR2;
+FUNCTION UNDO_DIRTY_HACK(hackedstring IN VARCHAR2) RETURN VARCHAR2;
+
+END MOODLELIB;
 /
 
-CREATE OR REPLACE PACKAGE BODY MOODLE_BITS AS
+CREATE OR REPLACE PACKAGE BODY MOODLELIB AS
 
 FUNCTION BITOR(value1 IN INTEGER, value2 IN INTEGER) RETURN INTEGER IS
 
@@ -50,23 +58,9 @@ END BITOR;
 FUNCTION BITXOR(value1 IN INTEGER, value2 IN INTEGER) RETURN INTEGER IS
 
 BEGIN
-    RETURN MOODLE_BITS.BITOR(value1,value2) - BITAND(value1,value2);
+    RETURN MOODLELIB.BITOR(value1,value2) - BITAND(value1,value2);
 END BITXOR;
 
-END MOODLE_BITS;
-/
-
-CREATE OR REPLACE PACKAGE MOODLE_LOCKS AS
-
-FUNCTION GET_HANDLE  (lock_name IN VARCHAR2) RETURN VARCHAR2;
-FUNCTION GET_LOCK    (lock_name IN VARCHAR2, lock_timeout IN INTEGER) RETURN INTEGER;
-FUNCTION RELEASE_LOCK(lock_name IN VARCHAR2) RETURN INTEGER;
-
-END MOODLE_LOCKS;
-/
-
-CREATE OR REPLACE PACKAGE BODY MOODLE_LOCKS AS
-
 FUNCTION GET_HANDLE(lock_name IN VARCHAR2) RETURN VARCHAR2 IS
     PRAGMA AUTONOMOUS_TRANSACTION;
     lock_handle VARCHAR2(128);
@@ -107,5 +101,23 @@ BEGIN
     RETURN 1;
 END RELEASE_LOCK;
 
-END MOODLE_LOCKS;
-/
\ No newline at end of file
+FUNCTION DIRTY_HACK(somestring IN VARCHAR2) RETURN VARCHAR2 IS
+
+BEGIN
+    IF somestring = '' THEN
+      RETURN ' ';
+    END IF;
+    RETURN somestring;
+END DIRTY_HACK;
+
+FUNCTION UNDO_DIRTY_HACK(hackedstring IN VARCHAR2) RETURN VARCHAR2 IS
+
+BEGIN
+    IF hackedstring = ' ' THEN
+        RETURN '';
+    END IF;
+    RETURN hackedstring;
+END UNDO_DIRTY_HACK;
+
+END MOODLELIB;
+/
index 1594245..01dc05d 100644 (file)
@@ -3926,6 +3926,9 @@ class dml_testcase extends database_driver_testcase {
         $table = $this->get_test_table();
         $tablename = $table->getName();
 
+        $this->assertSame('', $DB->sql_empty()); // Since 2.5 the hack is applied automatically to all bound params.
+        $this->assertDebuggingCalled();
+
         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
         $table->add_field('namenotnull', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, 'default value');
@@ -3938,17 +3941,17 @@ class dml_testcase extends database_driver_testcase {
         $DB->insert_record($tablename, array('name'=>'lalala'));
         $DB->insert_record($tablename, array('name'=>0));
 
-        $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE name = '".$DB->sql_empty()."'");
+        $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE name = ?", array(''));
         $this->assertEquals(count($records), 1);
         $record = reset($records);
         $this->assertEquals($record->name, '');
 
-        $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE namenotnull = '".$DB->sql_empty()."'");
+        $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE namenotnull = ?", array(''));
         $this->assertEquals(count($records), 1);
         $record = reset($records);
         $this->assertEquals($record->namenotnull, '');
 
-        $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE namenotnullnodeflt = '".$DB->sql_empty()."'");
+        $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE namenotnullnodeflt = ?", array(''));
         $this->assertEquals(count($records), 4);
         $record = reset($records);
         $this->assertEquals($record->namenotnullnodeflt, '');
index c6bb4fe..2afe486 100644 (file)
@@ -43,6 +43,10 @@ information provided here is intended especially for developers.
   course_category_show(), get_course_category(), create_course_category(), get_all_subcategories(),
   get_child_categories(), get_categories()
 
+Database (DML) layer:
+* $DB->sql_empty() is deprecated, you have to use sql parameters with empty values instead,
+  please note hardcoding of empty strings in SQL queries breaks execution in Oracle database.
+
 YUI changes:
 * M.util.help_icon has been deprecated. Code should be updated to use moodle-core-popuphelp
   instead. To do so, remove any existing JS calls to M.util.help_icon from your PHP and ensure
index 8be8f1f..b5e9a14 100644 (file)
@@ -116,7 +116,7 @@ class user_filter_text extends user_filter_type {
                 break;
             case 5: // empty
                 $res = "$field = :$name";
-                $params[$name] = $DB->sql_empty();
+                $params[$name] = '';
                 break;
             default:
                 return '';