]> scripts.mit.edu Git - autoinstalls/mediawiki.git/blobdiff - includes/db/DatabaseMysql.php
MediaWiki 1.17.0
[autoinstalls/mediawiki.git] / includes / db / DatabaseMysql.php
index ea7ef5b977d557588d4cd64afdaf9bba484bd3a3..ed276ec5e297f61f36da49754bed7cc4d9a92103 100644 (file)
@@ -1,4 +1,11 @@
 <?php
+/**
+ * This is the MySQL database abstraction layer.
+ *
+ * @file
+ * @ingroup Database
+ */
+
 /**
  * Database abstraction object for mySQL
  * Inherit all methods and properties of Database::Database()
@@ -24,11 +31,8 @@ class DatabaseMysql extends DatabaseBase {
                global $wgAllDBsAreLocalhost;
                wfProfileIn( __METHOD__ );
 
-               # Test for missing mysql.so
-               # First try to load it
-               if (!@extension_loaded('mysql')) {
-                       @dl('mysql.so');
-               }
+               # Load mysql.so if we don't have it
+               wfDl( 'mysql' );
 
                # Fail now
                # Otherwise we get a suppressed fatal error, which is very hard to track down
@@ -48,8 +52,6 @@ class DatabaseMysql extends DatabaseBase {
                $this->mPassword = $password;
                $this->mDBname = $dbName;
 
-               $success = false;
-
                wfProfileIn("dbconnect-$server");
 
                # The kernel's default SYN retransmission period is far too slow for us,
@@ -72,10 +74,10 @@ class DatabaseMysql extends DatabaseBase {
                                # Create a new connection...
                                $this->mConn = mysql_connect( $realServer, $user, $password, true );
                        }
-                       if ($this->mConn === false) {
+                       #if ( $this->mConn === false ) {
                                #$iplus = $i + 1;
-                               #wfLogDBError("Connect loop error $iplus of $max ($server): " . mysql_errno() . " - " . mysql_error()."\n"); 
-                       }
+                               #wfLogDBError("Connect loop error $iplus of $max ($server): " . mysql_errno() . " - " . mysql_error()."\n");
+                       #}
                }
                $phpError = $this->restoreErrorHandler();
                # Always log connection errors
@@ -88,9 +90,8 @@ class DatabaseMysql extends DatabaseBase {
                        wfDebug( "DB connection error\n" );
                        wfDebug( "Server: $server, User: $user, Password: " .
                                substr( $password, 0, 3 ) . "..., error: " . mysql_error() . "\n" );
-                       $success = false;
                }
-               
+
                wfProfileOut("dbconnect-$server");
 
                if ( $dbName != '' && $this->mConn !== false ) {
@@ -114,9 +115,15 @@ class DatabaseMysql extends DatabaseBase {
                                global $wgDBmysql5;
                                if( $wgDBmysql5 ) {
                                        $this->query( 'SET NAMES utf8', __METHOD__ );
+                               } else {
+                                       $this->query( 'SET NAMES binary', __METHOD__ );
+                               }
+                               // Set SQL mode, default is turning them all off, can be overridden or skipped with null
+                               global $wgSQLMode;
+                               if ( is_string( $wgSQLMode ) ) {
+                                       $mode = $this->addQuotes( $wgSQLMode );
+                                       $this->query( "SET sql_mode = $mode", __METHOD__ );
                                }
-                               // Turn off strict mode
-                               $this->query( "SET sql_mode = ''", __METHOD__ );
                        }
 
                        // Turn off strict mode if it is on
@@ -233,34 +240,35 @@ class DatabaseMysql extends DatabaseBase {
        }
 
        function affectedRows() { return mysql_affected_rows( $this->mConn ); }
-       
+
        /**
         * Estimate rows in dataset
         * Returns estimated count, based on EXPLAIN output
         * Takes same arguments as Database::select()
         */
-       public function estimateRowCount( $table, $vars='*', $conds='', $fname = 'Database::estimateRowCount', $options = array() ) {
+       public function estimateRowCount( $table, $vars='*', $conds='', $fname = 'DatabaseMysql::estimateRowCount', $options = array() ) {
                $options['EXPLAIN'] = true;
                $res = $this->select( $table, $vars, $conds, $fname, $options );
-               if ( $res === false )
+               if ( $res === false ) {
                        return false;
+               }
                if ( !$this->numRows( $res ) ) {
-                       $this->freeResult($res);
                        return 0;
                }
 
                $rows = 1;
-               while( $plan = $this->fetchObject( $res ) ) {
+               foreach ( $res as $plan ) {
                        $rows *= $plan->rows > 0 ? $plan->rows : 1; // avoid resetting to zero
                }
-
-               $this->freeResult($res);
-               return $rows;           
+               return $rows;
        }
 
        function fieldInfo( $table, $field ) {
                $table = $this->tableName( $table );
-               $res = $this->query( "SELECT * FROM $table LIMIT 1" );
+               $res = $this->query( "SELECT * FROM $table LIMIT 1", __METHOD__, true );
+               if ( !$res ) {
+                       return false;
+               }
                $n = mysql_num_fields( $res->result );
                for( $i = 0; $i < $n; $i++ ) {
                        $meta = mysql_fetch_field( $res->result, $i );
@@ -271,32 +279,108 @@ class DatabaseMysql extends DatabaseBase {
                return false;
        }
 
+       /**
+        * Get information about an index into an object
+        * Returns false if the index does not exist
+        */
+       function indexInfo( $table, $index, $fname = 'DatabaseMysql::indexInfo' ) {
+               # SHOW INDEX works in MySQL 3.23.58, but SHOW INDEXES does not.
+               # SHOW INDEX should work for 3.x and up:
+               # http://dev.mysql.com/doc/mysql/en/SHOW_INDEX.html
+               $table = $this->tableName( $table );
+               $index = $this->indexName( $index );
+               $sql = 'SHOW INDEX FROM ' . $table;
+               $res = $this->query( $sql, $fname );
+
+               if ( !$res ) {
+                       return null;
+               }
+
+               $result = array();
+
+               foreach ( $res as $row ) {
+                       if ( $row->Key_name == $index ) {
+                               $result[] = $row;
+                       }
+               }
+
+               return empty( $result ) ? false : $result;
+       }
+
        function selectDB( $db ) {
                $this->mDBname = $db;
                return mysql_select_db( $db, $this->mConn );
        }
 
        function strencode( $s ) {
-               return mysql_real_escape_string( $s, $this->mConn );
+               $sQuoted = mysql_real_escape_string( $s, $this->mConn );
+
+               if($sQuoted === false) {
+                       $this->ping();
+                       $sQuoted = mysql_real_escape_string( $s, $this->mConn );
+               }
+               return $sQuoted;
+       }
+
+       /**
+        * MySQL uses `backticks` for identifier quoting instead of the sql standard "double quotes".
+        */
+       public function addIdentifierQuotes( $s ) {
+               return "`" . $this->strencode( $s ) . "`";
        }
 
        function ping() {
-               if( !function_exists( 'mysql_ping' ) ) {
-                       wfDebug( "Tried to call mysql_ping but this is ancient PHP version. Faking it!\n" );
-                       return true;
-               }
                $ping = mysql_ping( $this->mConn );
                if ( $ping ) {
                        return true;
                }
 
-               // Need to reconnect manually in MySQL client 5.0.13+
-               if ( version_compare( mysql_get_client_info(), '5.0.13', '>=' ) ) {
-                       mysql_close( $this->mConn );
-                       $this->mOpened = false;
-                       $this->mConn = false;
-                       $this->open( $this->mServer, $this->mUser, $this->mPassword, $this->mDBname );
-                       return true;
+               mysql_close( $this->mConn );
+               $this->mOpened = false;
+               $this->mConn = false;
+               $this->open( $this->mServer, $this->mUser, $this->mPassword, $this->mDBname );
+               return true;
+       }
+
+       /**
+        * Returns slave lag.
+        * At the moment, this will only work if the DB user has the PROCESS privilege
+        * @result int
+        */
+       function getLag() {
+               if ( !is_null( $this->mFakeSlaveLag ) ) {
+                       wfDebug( "getLag: fake slave lagged {$this->mFakeSlaveLag} seconds\n" );
+                       return $this->mFakeSlaveLag;
+               }
+               $res = $this->query( 'SHOW PROCESSLIST', __METHOD__ );
+               if( !$res ) {
+                       return false;
+               }
+               # Find slave SQL thread
+               foreach( $res as $row ) {
+                       /* This should work for most situations - when default db
+                        * for thread is not specified, it had no events executed,
+                        * and therefore it doesn't know yet how lagged it is.
+                        *
+                        * Relay log I/O thread does not select databases.
+                        */
+                       if ( $row->User == 'system user' &&
+                               $row->State != 'Waiting for master to send event' &&
+                               $row->State != 'Connecting to master' &&
+                               $row->State != 'Queueing master event to the relay log' &&
+                               $row->State != 'Waiting for master update' &&
+                               $row->State != 'Requesting binlog dump' &&
+                               $row->State != 'Waiting to reconnect after a failed master event read' &&
+                               $row->State != 'Reconnecting after a failed master event read' &&
+                               $row->State != 'Registering slave on master'
+                               ) {
+                               # This is it, return the time (except -ve)
+                               if ( $row->Time > 0x7fffffff ) {
+                                       return false;
+                               } else {
+                                       return $row->Time;
+                               }
+                       }
                }
                return false;
        }
@@ -313,7 +397,7 @@ class DatabaseMysql extends DatabaseBase {
                return 'LOW_PRIORITY';
        }
 
-       function getSoftwareLink() {
+       public static function getSoftwareLink() {
                return '[http://www.mysql.com/ MySQL]';
        }
 
@@ -330,7 +414,6 @@ class DatabaseMysql extends DatabaseBase {
                $lockName = $this->addQuotes( $lockName );
                $result = $this->query( "SELECT GET_LOCK($lockName, $timeout) AS lockstatus", $method );
                $row = $this->fetchObject( $result );
-               $this->freeResult( $result );
 
                if( $row->lockstatus == 1 ) {
                        return true;
@@ -340,6 +423,9 @@ class DatabaseMysql extends DatabaseBase {
                }
        }
 
+       /**
+        * FROM MYSQL DOCS: http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html#function_release-lock
+        */
        public function unlock( $lockName, $method ) {
                $lockName = $this->addQuotes( $lockName );
                $result = $this->query( "SELECT RELEASE_LOCK($lockName) as lockstatus", $method );
@@ -351,8 +437,8 @@ class DatabaseMysql extends DatabaseBase {
                $items = array();
 
                foreach( $write as $table ) {
-                       $tbl = $this->tableName( $table ) . 
-                                       ( $lowPriority ? ' LOW_PRIORITY' : '' ) . 
+                       $tbl = $this->tableName( $table ) .
+                                       ( $lowPriority ? ' LOW_PRIORITY' : '' ) .
                                        ' WRITE';
                        $items[] = $tbl;
                }
@@ -367,6 +453,16 @@ class DatabaseMysql extends DatabaseBase {
                $this->query( "UNLOCK TABLES", $method );
        }
 
+       /**
+        * Get search engine class. All subclasses of this
+        * need to implement this if they wish to use searching.
+        *
+        * @return String
+        */
+       public function getSearchEngine() {
+               return 'SearchMySQL';
+       }
+
        public function setBigSelects( $value = true ) {
                if ( $value === 'default' ) {
                        if ( $this->mDefaultBigSelects === null ) {
@@ -382,7 +478,10 @@ class DatabaseMysql extends DatabaseBase {
                $this->query( "SET sql_big_selects=$encValue", __METHOD__ );
        }
 
-       
+       public function unixTimestamp( $field ) {
+               return "UNIX_TIMESTAMP($field)";
+       }
+
        /**
         * Determines if the last failure was due to a deadlock
         */
@@ -391,7 +490,7 @@ class DatabaseMysql extends DatabaseBase {
        }
 
        /**
-        * Determines if the last query error was something that should be dealt 
+        * Determines if the last query error was something that should be dealt
         * with by pinging the connection and reissuing the query
         */
        function wasErrorReissuable() {
@@ -402,7 +501,7 @@ class DatabaseMysql extends DatabaseBase {
         * Determines if the last failure was due to the database being read-only.
         */
        function wasReadOnlyError() {
-               return $this->lastErrno() == 1223 || 
+               return $this->lastErrno() == 1223 ||
                        ( $this->lastErrno() == 1290 && strpos( $this->lastError(), '--read-only' ) !== false );
        }
 
@@ -420,7 +519,7 @@ class DatabaseMysql extends DatabaseBase {
                        $res = $this->query( "SHOW CREATE TABLE $oldName" );
                        $row = $this->fetchRow( $res );
                        $oldQuery = $row[1];
-                       $query = preg_replace( '/CREATE TABLE `(.*?)`/', 
+                       $query = preg_replace( '/CREATE TABLE `(.*?)`/',
                                "CREATE $tmp TABLE `$newName`", $oldQuery );
                        if ($oldQuery === $query) {
                                # Couldn't do replacement
@@ -432,6 +531,11 @@ class DatabaseMysql extends DatabaseBase {
                $this->query( $query, $fname );
        }
 
+       protected function getDefaultSchemaVars() {
+               $vars = parent::getDefaultSchemaVars();
+               $vars['wgDBTableOptions'] = $GLOBALS['wgDBTableOptions'];
+               return $vars;
+       }
 }
 
 /**
@@ -439,6 +543,56 @@ class DatabaseMysql extends DatabaseBase {
  */
 class Database extends DatabaseMysql {}
 
+/**
+ * Utility class.
+ * @ingroup Database
+ */
+class MySQLField implements Field {
+       private $name, $tablename, $default, $max_length, $nullable,
+               $is_pk, $is_unique, $is_multiple, $is_key, $type;
+
+       function __construct ( $info ) {
+               $this->name = $info->name;
+               $this->tablename = $info->table;
+               $this->default = $info->def;
+               $this->max_length = $info->max_length;
+               $this->nullable = !$info->not_null;
+               $this->is_pk = $info->primary_key;
+               $this->is_unique = $info->unique_key;
+               $this->is_multiple = $info->multiple_key;
+               $this->is_key = ( $this->is_pk || $this->is_unique || $this->is_multiple );
+               $this->type = $info->type;
+       }
+
+       function name() {
+               return $this->name;
+       }
+
+       function tableName() {
+               return $this->tableName;
+       }
+
+       function type() {
+               return $this->type;
+       }
+
+       function isNullable() {
+               return $this->nullable;
+       }
+
+       function defaultValue() {
+               return $this->default;
+       }
+
+       function isKey() {
+               return $this->is_key;
+       }
+
+       function isMultipleKey() {
+               return $this->is_multiple;
+       }
+}
+
 class MySQLMasterPos {
        var $file, $pos;