MDL-35129 implement new export_table_recordset()
authorPetr Škoda <commits@skodak.org>
Sat, 15 Sep 2012 08:49:48 +0000 (10:49 +0200)
committerPetr Škoda <commits@skodak.org>
Sat, 15 Sep 2012 10:03:24 +0000 (12:03 +0200)
This is necessary because MySQL get_recordset_sql() stores the data in memory, unfortunately the alternative resultset iteration blocks concurrent access - that is why we need new method for export.

lib/dml/moodle_database.php
lib/dml/mysqli_native_moodle_database.php
lib/dml/tests/dml_test.php
lib/dtl/database_exporter.php

index 9531782..7a81253 100644 (file)
@@ -1101,6 +1101,20 @@ abstract class moodle_database {
      */
     public abstract function get_recordset_sql($sql, array $params=null, $limitfrom=0, $limitnum=0);
 
+    /**
+     * Get all records from a table.
+     *
+     * This method works around potential memory problems and may improve performance,
+     * this method may block access to table until the recordset is closed.
+     *
+     * @param string $table Name of database table.
+     * @return moodle_recordset A moodle_recordset instance {@link function get_recordset}.
+     * @throws dml_exception A DML specific exception is thrown for any errors.
+     */
+    public function export_table_recordset($table) {
+        return $this->get_recordset($table, array());
+    }
+
     /**
      * Get a number of records as an array of objects where all the given conditions met.
      *
index 1a238ba..21404d7 100644 (file)
@@ -905,6 +905,27 @@ class mysqli_native_moodle_database extends moodle_database {
         return $this->create_recordset($result);
     }
 
+    /**
+     * Get all records from a table.
+     *
+     * This method works around potential memory problems and may improve performance,
+     * this method may block access to table until the recordset is closed.
+     *
+     * @param string $table Name of database table.
+     * @return moodle_recordset A moodle_recordset instance {@link function get_recordset}.
+     * @throws dml_exception A DML specific exception is thrown for any errors.
+     */
+    public function export_table_recordset($table) {
+        $sql = $this->fix_table_names("SELECT * FROM {{$table}}");
+
+        $this->query_start($sql, array(), SQL_QUERY_SELECT);
+        // MYSQLI_STORE_RESULT may eat all memory for large tables, unfortunately MYSQLI_USE_RESULT blocks other queries.
+        $result = $this->mysqli->query($sql, MYSQLI_USE_RESULT);
+        $this->query_end($result);
+
+        return $this->create_recordset($result);
+    }
+
     protected function create_recordset($result) {
         return new mysqli_native_moodle_recordset($result);
     }
index 2bb7270..e056b53 100644 (file)
@@ -1300,6 +1300,36 @@ class dml_testcase extends database_driver_testcase {
         // note: fetching nulls, empties, LOBs already tested by test_insert_record() no needed here
     }
 
+    public function test_export_table_recordset() {
+        $DB = $this->tdb;
+        $dbman = $DB->get_manager();
+
+        $table = $this->get_test_table();
+        $tablename = $table->getName();
+
+        $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
+        $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
+        $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
+        $dbman->create_table($table);
+
+        $ids = array();
+        $ids[] = $DB->insert_record($tablename, array('course' => 3));
+        $ids[] = $DB->insert_record($tablename, array('course' => 5));
+        $ids[] = $DB->insert_record($tablename, array('course' => 4));
+        $ids[] = $DB->insert_record($tablename, array('course' => 3));
+        $ids[] = $DB->insert_record($tablename, array('course' => 2));
+        $ids[] = $DB->insert_record($tablename, array('course' => 1));
+        $ids[] = $DB->insert_record($tablename, array('course' => 0));
+
+        $rs = $DB->export_table_recordset($tablename);
+        $rids = array();
+        foreach ($rs as $record) {
+            $rids[] = $record->id;
+        }
+        $rs->close();
+        $this->assertEquals($ids, $rids, '', 0, 0, true);
+    }
+
     public function test_get_records() {
         $DB = $this->tdb;
         $dbman = $DB->get_manager();
index 5d3c121..4d9b7bb 100644 (file)
@@ -144,7 +144,7 @@ abstract class database_exporter {
         $tables = $this->schema->getTables();
         $this->begin_database_export($CFG->version, $CFG->release, date('c'), $description);
         foreach ($tables as $table) {
-            $rs = $this->mdb->get_recordset_sql('SELECT * FROM {'.$table->getName().'}');
+            $rs = $this->mdb->export_table_recordset($table->getName());
             if (!$rs) {
                 throw new ddl_table_missing_exception($table->getName());
             }