MDL-23682 new MySQL engine diagnostics + InnoDB is now used instead of MyISAM in...
authorPetr Skoda <skodak@moodle.org>
Wed, 18 Aug 2010 16:47:00 +0000 (16:47 +0000)
committerPetr Skoda <skodak@moodle.org>
Wed, 18 Aug 2010 16:47:00 +0000 (16:47 +0000)
admin/index.php
lang/en/error.php
lib/ddl/mysql_sql_generator.php
lib/dml/moodle_database.php
lib/dml/mysqli_native_moodle_database.php

index b50c0e1..409b531 100644 (file)
@@ -373,6 +373,12 @@ if ($showbloglevelupgrade) {
     echo $OUTPUT->box(get_string('bloglevelupgradenotice', 'admin'), 'generalbox adminwarning');
 }
 
+// diagnose DB, especially the sloppy MyISAM tables
+$diagnose = $DB->diagnose();
+if ($diagnose !== NULL) {
+    echo $OUTPUT->box($diagnose, 'generalbox adminwarning');
+}
+
 // Alert if we are currently in maintenance mode
 if (!empty($CFG->maintenance_enabled)) {
     echo $OUTPUT->box(get_string('sitemaintenancewarning2', 'admin', "$CFG->wwwroot/$CFG->admin/settings.php?section=maintenancemode"), 'generalbox adminwarning');
index cd9e000..40903ff 100755 (executable)
@@ -341,6 +341,7 @@ $string['multiplerecordsfound'] = 'Multiple records found, only one record expec
 $string['multiplerestorenotallow'] = 'Multiple restore execution not allowed!';
 $string['mustbeloggedin'] = 'You must be logged in to do this';
 $string['mustbeteacher'] = 'You must be a teacher to look at this page';
+$string['myisamproblem'] = 'Database tables are using MyISAM database engine, it is recommended to use ACID compliant engine with full transaction support such as InnoDB.';
 $string['needcopy'] = 'You need to copy something first!';
 $string['needcoursecategroyid'] = 'Either course id or category must be specified';
 $string['needphpext'] = 'You need to add {$a} support to your PHP installation';
index d0a4d7d..d4f8b82 100644 (file)
@@ -96,6 +96,33 @@ class mysql_sql_generator extends sql_generator {
         return array("ALTER TABLE $this->prefix$tablename AUTO_INCREMENT = $value");
     }
 
+    /**
+     * Given one correct xmldb_table, returns the SQL statements
+     * to create it (inside one array)
+     */
+    public function getCreateTableSQL($xmldb_table) {
+        // first find out if want some special db engine
+        $engine = null;
+        if (method_exists($this->mdb, 'get_dbengine')) {
+            $engine = $this->mdb->get_dbengine();
+        }
+
+        $sqlarr = parent::getCreateTableSQL($xmldb_table);
+
+        if (!$engine) {
+            // we rely on database defaults
+            return $sqlarr;
+        }
+
+        // let's inject the engine into SQL
+        foreach ($sqlarr as $i=>$sql) {
+            if (strpos($sql, 'CREATE TABLE ') === 0) {
+                $sqlarr[$i] .= " ENGINE = $engine";
+            }
+        }
+
+        return $sqlarr;
+    }
 
     /**
      * Given one correct xmldb_table, returns the SQL statements
@@ -103,7 +130,7 @@ class mysql_sql_generator extends sql_generator {
      */
     public function getCreateTempTableSQL($xmldb_table) {
         $this->temptables->add_temptable($xmldb_table->getName());
-        $sqlarr = $this->getCreateTableSQL($xmldb_table);
+        $sqlarr = parent::getCreateTableSQL($xmldb_table); // we do not want the engine hack included in create table SQL
         $sqlarr = preg_replace('/^CREATE TABLE (.*)/s', 'CREATE TEMPORARY TABLE $1', $sqlarr);
         return $sqlarr;
     }
index 371cb75..979e9d1 100644 (file)
@@ -236,6 +236,16 @@ abstract class moodle_database {
         return $cfg;
     }
 
+    /**
+     * Diagnose database and tables, this function is used
+     * to verify database and driver settings, db engine types, etc.
+     *
+     * @return string null means everything ok, string means problem found.
+     */
+    public function diagnose() {
+        return null;
+    }
+
     /**
      * Connect to db
      * Must be called before other methods.
index 6e6dcfa..6a0ff48 100644 (file)
@@ -116,6 +116,71 @@ class mysqli_native_moodle_database extends moodle_database {
         return 'native';
     }
 
+    /**
+     * Returns the current MySQL db engine.
+     *
+     * This is an ugly workaround for MySQL default engine problems,
+     * Moodle is designed to work best on ACID compliant databases
+     * with full transaction support. Do not use MyISAM.
+     *
+     * @return string or null MySQL engine name
+     */
+    public function get_dbengine() {
+        if (isset($this->dboptions['dbengine'])) {
+            return $this->dboptions['dbengine'];
+        }
+
+        $engine = null;
+
+        if (!$this->external) {
+            // look for current engine of our config table (the first table that gets created),
+            // so that we create all tables with the same engine
+            $sql = "SELECT engine FROM INFORMATION_SCHEMA.TABLES WHERE table_schema = DATABASE() AND table_name = '{$this->prefix}config'";
+            $this->query_start($sql, NULL, SQL_QUERY_AUX);
+            $result = $this->mysqli->query($sql);
+            $this->query_end($result);
+            if ($rec = $result->fetch_assoc()) {
+                $engine = $rec['engine'];
+            }
+            $result->close();
+        }
+
+        if ($engine) {
+            return $engine;
+        }
+
+        // get the default database engine
+        $sql = "SELECT @@storage_engine";
+        $this->query_start($sql, NULL, SQL_QUERY_AUX);
+        $result = $this->mysqli->query($sql);
+        $this->query_end($result);
+        if ($rec = $result->fetch_assoc()) {
+            $engine = $rec['@@storage_engine'];
+        }
+        $result->close();
+
+        if (!$this->external and $engine === 'MyISAM') {
+            // we really do not want MyISAM for Moodle, InnoDB or XtraDB is a reasonable defaults if supported
+            $sql = "SHOW STORAGE ENGINES";
+            $this->query_start($sql, NULL, SQL_QUERY_AUX);
+            $result = $this->mysqli->query($sql);
+            $this->query_end($result);
+            while ($res = $result->fetch_assoc()) {
+                if ($res['Engine'] === 'InnoDB' and $res['Support'] === 'YES') {
+                    $engine = 'InnoDB';
+                    break;
+                }
+                if ($res['Engine'] === 'XtraDB' and $res['Support'] === 'YES') {
+                    $engine = 'XtraDB';
+                    break;
+                }
+            }
+            $result->close();
+        }
+
+        return $engine;
+    }
+
     /**
      * Returns localised database type name
      * Note: can be used before connect()
@@ -143,6 +208,36 @@ class mysqli_native_moodle_database extends moodle_database {
         return get_string('databasesettingssub_mysqli', 'install');
     }
 
+    /**
+     * Diagnose database and tables, this function is used
+     * to verify database and driver settings, db engine types, etc.
+     *
+     * @return string null means everything ok, string means problem found.
+     */
+    public function diagnose() {
+        $sloppymyisamfound = false;
+        $prefix = str_replace('_', '\\_', $this->prefix);
+        $sql = "SHOW TABLE STATUS WHERE Name LIKE BINARY '$prefix%'";
+        $this->query_start($sql, null, SQL_QUERY_AUX);
+        $result = $this->mysqli->query($sql);
+        $this->query_end($result);
+        if ($result) {
+            while ($arr = $result->fetch_assoc()) {
+                if ($arr['Engine'] === 'MyISAM') {
+                    $sloppymyisamfound = true;
+                    break;
+                }
+            }
+            $result->close();
+        }
+
+        if ($sloppymyisamfound) {
+            return get_string('myisamproblem', 'error');
+        } else {
+            return null;
+        }
+    }
+
     /**
      * Connect to db
      * Must be called before other methods.
@@ -1021,6 +1116,8 @@ class mysqli_native_moodle_database extends moodle_database {
      *
      * MyISAM does not support support transactions.
      *
+     * You can override this via the dbtransactions option.
+     *
      * @return bool
      */
     protected function transactions_supported() {
@@ -1028,20 +1125,20 @@ class mysqli_native_moodle_database extends moodle_database {
             return $this->transactions_supported;
         }
 
-        // Only will accept transactions if using InnoDB storage engine (more engines can be added easily BDB, Falcon...)
+        // this is all just guessing, might be better to just specify it in config.php
+        if (isset($this->dboptions['dbtransactions'])) {
+            $this->transactions_supported = $this->dboptions['dbtransactions'];
+            return $this->transactions_supported;
+        }
+
         $this->transactions_supported = false;
 
-        $sql = "SELECT @@storage_engine";
-        $this->query_start($sql, NULL, SQL_QUERY_AUX);
-        $result = $this->mysqli->query($sql);
-        $this->query_end($result);
+        $engine = $this->get_dbengine();
 
-        if ($rec = $result->fetch_assoc()) {
-            if (in_array($rec['@@storage_engine'], array('InnoDB'))) {
-                $this->transactions_supported = true;
-            }
+        // Only will accept transactions if using compatible storage engine (more engines can be added easily BDB, Falcon...)
+        if (in_array($engine, array('InnoDB', 'INNOBASE', 'BDB', 'XtraDB', 'Aria', 'Falcon'))) {
+            $this->transactions_supported = true;
         }
-        $result->close();
 
         return $this->transactions_supported;
     }