Merge "Added result properties to action=paraminfo"
[lhc/web/wiklou.git] / includes / db / DatabaseMysql.php
index c7eada7..1d03073 100644 (file)
@@ -2,6 +2,21 @@
 /**
  * This is the MySQL database abstraction layer.
  *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
  * @file
  * @ingroup Database
  */
@@ -22,6 +37,10 @@ class DatabaseMysql extends DatabaseBase {
                return 'mysql';
        }
 
+       /**
+        * @param $sql string
+        * @return resource
+        */
        protected function doQuery( $sql ) {
                if( $this->bufferResults() ) {
                        $ret = mysql_query( $sql, $this->mConn );
@@ -31,6 +50,14 @@ class DatabaseMysql extends DatabaseBase {
                return $ret;
        }
 
+       /**
+        * @param $server string
+        * @param $user string
+        * @param $password string
+        * @param $dbName string
+        * @return bool
+        * @throws DBConnectionError
+        */
        function open( $server, $user, $password, $dbName ) {
                global $wgAllDBsAreLocalhost;
                wfProfileIn( __METHOD__ );
@@ -86,14 +113,14 @@ class DatabaseMysql extends DatabaseBase {
                $phpError = $this->restoreErrorHandler();
                # Always log connection errors
                if ( !$this->mConn ) {
-                       $error = $this->lastError();
+                       $error = $phpError;
                        if ( !$error ) {
-                               $error = $phpError;
+                               $error = $this->lastError();
                        }
                        wfLogDBError( "Error connecting to {$this->mServer}: $error\n" );
                        wfDebug( "DB connection error\n" );
                        wfDebug( "Server: $server, User: $user, Password: " .
-                               substr( $password, 0, 3 ) . "..., error: " . mysql_error() . "\n" );
+                               substr( $password, 0, 3 ) . "..., error: " . $error . "\n" );
                }
 
                wfProfileOut("dbconnect-$server");
@@ -114,22 +141,19 @@ class DatabaseMysql extends DatabaseBase {
                }
 
                if ( $success ) {
-                       $version = $this->getServerVersion();
-                       if ( version_compare( $version, '4.1' ) >= 0 ) {
-                               // Tell the server we're communicating with it in UTF-8.
-                               // This may engage various charset conversions.
-                               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__ );
-                               }
+                       // Tell the server we're communicating with it in UTF-8.
+                       // This may engage various charset conversions.
+                       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 if it is on
@@ -145,18 +169,14 @@ class DatabaseMysql extends DatabaseBase {
        /**
         * @return bool
         */
-       function close() {
-               $this->mOpened = false;
-               if ( $this->mConn ) {
-                       if ( $this->trxLevel() ) {
-                               $this->commit();
-                       }
-                       return mysql_close( $this->mConn );
-               } else {
-                       return true;
-               }
+       protected function closeConnection() {
+               return mysql_close( $this->mConn );
        }
 
+       /**
+        * @param $res ResultWrapper
+        * @throws DBUnexpectedError
+        */
        function freeResult( $res ) {
                if ( $res instanceof ResultWrapper ) {
                        $res = $res->result;
@@ -169,6 +189,11 @@ class DatabaseMysql extends DatabaseBase {
                }
        }
 
+       /**
+        * @param $res ResultWrapper
+        * @return object|stdClass
+        * @throws DBUnexpectedError
+        */
        function fetchObject( $res ) {
                if ( $res instanceof ResultWrapper ) {
                        $res = $res->result;
@@ -182,6 +207,11 @@ class DatabaseMysql extends DatabaseBase {
                return $row;
        }
 
+       /**
+        * @param $res ResultWrapper
+        * @return array
+        * @throws DBUnexpectedError
+        */
        function fetchRow( $res ) {
                if ( $res instanceof ResultWrapper ) {
                        $res = $res->result;
@@ -197,7 +227,7 @@ class DatabaseMysql extends DatabaseBase {
 
        /**
         * @throws DBUnexpectedError
-        * @param $res
+        * @param $res ResultWrapper
         * @return int
         */
        function numRows( $res ) {
@@ -214,7 +244,7 @@ class DatabaseMysql extends DatabaseBase {
        }
 
        /**
-        * @param $res
+        * @param $res ResultWrapper
         * @return int
         */
        function numFields( $res ) {
@@ -225,8 +255,8 @@ class DatabaseMysql extends DatabaseBase {
        }
 
        /**
-        * @param $res
-        * @param $n
+        * @param $res ResultWrapper
+        * @param $n string
         * @return string
         */
        function fieldName( $res, $n ) {
@@ -243,6 +273,11 @@ class DatabaseMysql extends DatabaseBase {
                return mysql_insert_id( $this->mConn );
        }
 
+       /**
+        * @param $res ResultWrapper
+        * @param $row
+        * @return bool
+        */
        function dataSeek( $res, $row ) {
                if ( $res instanceof ResultWrapper ) {
                        $res = $res->result;
@@ -250,6 +285,9 @@ class DatabaseMysql extends DatabaseBase {
                return mysql_data_seek( $res, $row );
        }
 
+       /**
+        * @return int
+        */
        function lastErrno() {
                if ( $this->mConn ) {
                        return mysql_errno( $this->mConn );
@@ -258,6 +296,9 @@ class DatabaseMysql extends DatabaseBase {
                }
        }
 
+       /**
+        * @return string
+        */
        function lastError() {
                if ( $this->mConn ) {
                        # Even if it's non-zero, it can still be invalid
@@ -283,6 +324,13 @@ class DatabaseMysql extends DatabaseBase {
                return mysql_affected_rows( $this->mConn );
        }
 
+       /**
+        * @param $table string
+        * @param $uniqueIndexes
+        * @param $rows array
+        * @param $fname string
+        * @return ResultWrapper
+        */
        function replace( $table, $uniqueIndexes, $rows, $fname = 'DatabaseMysql::replace' ) {
                return $this->nativeReplace( $table, $rows, $fname );
        }
@@ -292,6 +340,11 @@ class DatabaseMysql extends DatabaseBase {
         * Returns estimated count, based on EXPLAIN output
         * Takes same arguments as Database::select()
         *
+        * @param $table string|array
+        * @param $vars string|array
+        * @param $conds string|array
+        * @param $fname string
+        * @param $options string|array
         * @return int
         */
        public function estimateRowCount( $table, $vars='*', $conds='', $fname = 'DatabaseMysql::estimateRowCount', $options = array() ) {
@@ -336,7 +389,10 @@ class DatabaseMysql extends DatabaseBase {
         * Get information about an index into an object
         * Returns false if the index does not exist
         *
-        * @return false|array
+        * @param $table string
+        * @param $index string
+        * @param $fname string
+        * @return bool|array|null False or null on failure
         */
        function indexInfo( $table, $index, $fname = 'DatabaseMysql::indexInfo' ) {
                # SHOW INDEX works in MySQL 3.23.58, but SHOW INDEXES does not.
@@ -424,9 +480,7 @@ class DatabaseMysql extends DatabaseBase {
        /**
         * Returns slave lag.
         *
-        * On MySQL 4.1.9 and later, this will do a SHOW SLAVE STATUS. On earlier
-        * versions of MySQL, it uses SHOW PROCESSLIST, which requires the PROCESS
-        * privilege.
+        * This will do a SHOW SLAVE STATUS
         *
         * @return int
         */
@@ -436,11 +490,7 @@ class DatabaseMysql extends DatabaseBase {
                        return $this->mFakeSlaveLag;
                }
 
-               if ( version_compare( $this->getServerVersion(), '4.1.9', '>=' ) ) {
-                       return $this->getLagFromSlaveStatus();
-               } else {
-                       return $this->getLagFromProcesslist();
-               }
+               return $this->getLagFromSlaveStatus();
        }
 
        /**
@@ -463,9 +513,12 @@ class DatabaseMysql extends DatabaseBase {
        }
 
        /**
+        * @deprecated in 1.19, use getLagFromSlaveStatus
+        *
         * @return bool|int
         */
        function getLagFromProcesslist() {
+               wfDeprecated( __METHOD__, '1.19' );
                $res = $this->query( 'SHOW PROCESSLIST', __METHOD__ );
                if( !$res ) {
                        return false;
@@ -504,6 +557,7 @@ class DatabaseMysql extends DatabaseBase {
         *
         * @param $pos DBMasterPos object
         * @param $timeout Integer: the maximum number of seconds to wait for synchronisation
+        * @return bool|string
         */
        function masterPosWait( DBMasterPos $pos, $timeout ) {
                $fname = 'DatabaseBase::masterPosWait';
@@ -511,7 +565,7 @@ class DatabaseMysql extends DatabaseBase {
 
                # Commit any open transactions
                if ( $this->mTrxLevel ) {
-                       $this->commit();
+                       $this->commit( __METHOD__ );
                }
 
                if ( !is_null( $this->mFakeSlaveLag ) ) {
@@ -538,7 +592,7 @@ class DatabaseMysql extends DatabaseBase {
        /**
         * Get the position of the master from SHOW SLAVE STATUS
         *
-        * @return MySQLMasterPos|false
+        * @return MySQLMasterPos|bool
         */
        function getSlavePos() {
                if ( !is_null( $this->mFakeSlaveLag ) ) {
@@ -559,7 +613,7 @@ class DatabaseMysql extends DatabaseBase {
        /**
         * Get the position of the master from SHOW MASTER STATUS
         *
-        * @return MySQLMasterPos|false
+        * @return MySQLMasterPos|bool
         */
        function getMasterPos() {
                if ( $this->mFakeMaster ) {
@@ -605,15 +659,54 @@ class DatabaseMysql extends DatabaseBase {
                return '[http://www.mysql.com/ MySQL]';
        }
 
+       /**
+        * @return bool
+        */
        function standardSelectDistinct() {
                return false;
        }
 
-       public function setTimeout( $timeout ) {
-               $this->query( "SET net_read_timeout=$timeout" );
-               $this->query( "SET net_write_timeout=$timeout" );
+       /**
+        * @param $options array
+        */
+       public function setSessionOptions( array $options ) {
+               if ( isset( $options['connTimeout'] ) ) {
+                       $timeout = (int)$options['connTimeout'];
+                       $this->query( "SET net_read_timeout=$timeout" );
+                       $this->query( "SET net_write_timeout=$timeout" );
+               }
+       }
+
+       public function streamStatementEnd( &$sql, &$newLine ) {
+               if ( strtoupper( substr( $newLine, 0, 9 ) ) == 'DELIMITER' ) {
+                       preg_match( '/^DELIMITER\s+(\S+)/' , $newLine, $m );
+                       $this->delimiter = $m[1];
+                       $newLine = '';
+               }
+               return parent::streamStatementEnd( $sql, $newLine );
+       }
+
+       /**
+        * Check to see if a named lock is available. This is non-blocking.
+        *
+        * @param $lockName String: name of lock to poll
+        * @param $method String: name of method calling us
+        * @return Boolean
+        * @since 1.20
+        */
+       public function lockIsFree( $lockName, $method ) {
+               $lockName = $this->addQuotes( $lockName );
+               $result = $this->query( "SELECT IS_FREE_LOCK($lockName) AS lockstatus", $method );
+               $row = $this->fetchObject( $result );
+               return ( $row->lockstatus == 1 );
        }
 
+       /**
+        * @param $lockName string
+        * @param $method string
+        * @param $timeout int
+        * @return bool
+        */
        public function lock( $lockName, $method, $timeout = 5 ) {
                $lockName = $this->addQuotes( $lockName );
                $result = $this->query( "SELECT GET_LOCK($lockName, $timeout) AS lockstatus", $method );
@@ -629,14 +722,23 @@ class DatabaseMysql extends DatabaseBase {
 
        /**
         * FROM MYSQL DOCS: http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html#function_release-lock
+        * @param $lockName string
+        * @param $method string
+        * @return bool
         */
        public function unlock( $lockName, $method ) {
                $lockName = $this->addQuotes( $lockName );
                $result = $this->query( "SELECT RELEASE_LOCK($lockName) as lockstatus", $method );
                $row = $this->fetchObject( $result );
-               return $row->lockstatus;
+               return ( $row->lockstatus == 1 );
        }
 
+       /**
+        * @param $read array
+        * @param $write array
+        * @param $method string
+        * @param $lowPriority bool
+        */
        public function lockTables( $read, $write, $method, $lowPriority = true ) {
                $items = array();
 
@@ -653,6 +755,9 @@ class DatabaseMysql extends DatabaseBase {
                $this->query( $sql, $method );
        }
 
+       /**
+        * @param $method string
+        */
        public function unlockTables( $method ) {
                $this->query( "UNLOCK TABLES", $method );
        }
@@ -667,6 +772,10 @@ class DatabaseMysql extends DatabaseBase {
                return 'SearchMySQL';
        }
 
+       /**
+        * @param bool $value
+        * @return mixed
+        */
        public function setBigSelects( $value = true ) {
                if ( $value === 'default' ) {
                        if ( $this->mDefaultBigSelects === null ) {
@@ -684,6 +793,13 @@ class DatabaseMysql extends DatabaseBase {
 
        /**
         * DELETE where the condition is a join. MySql uses multi-table deletes.
+        * @param $delTable string
+        * @param $joinTable string
+        * @param $delVar string
+        * @param $joinVar string
+        * @param $conds array|string
+        * @param $fname bool
+        * @return bool|ResultWrapper
         */
        function deleteJoin( $delTable, $joinTable, $delVar, $joinVar, $conds, $fname = 'DatabaseBase::deleteJoin' ) {
                if ( !$conds ) {
@@ -701,6 +817,16 @@ class DatabaseMysql extends DatabaseBase {
                return $this->query( $sql, $fname );
        }
 
+       /**
+        * Determines how long the server has been up
+        *
+        * @return int
+        */
+       function getServerUptime() {
+               $vars = $this->getMysqlStatus( 'Uptime' );
+               return (int)$vars['Uptime'];
+       }
+
        /**
         * Determines if the last failure was due to a deadlock
         *
@@ -710,6 +836,15 @@ class DatabaseMysql extends DatabaseBase {
                return $this->lastErrno() == 1213;
        }
 
+       /**
+        * Determines if the last failure was due to a lock timeout
+        *
+        * @return bool
+        */
+       function wasLockTimeout() {
+               return $this->lastErrno() == 1205;
+       }
+
        /**
         * Determines if the last query error was something that should be dealt
         * with by pinging the connection and reissuing the query
@@ -730,39 +865,26 @@ class DatabaseMysql extends DatabaseBase {
                        ( $this->lastErrno() == 1290 && strpos( $this->lastError(), '--read-only' ) !== false );
        }
 
+       /**
+        * @param $oldName
+        * @param $newName
+        * @param $temporary bool
+        * @param $fname string
+        */
        function duplicateTableStructure( $oldName, $newName, $temporary = false, $fname = 'DatabaseMysql::duplicateTableStructure' ) {
                $tmp = $temporary ? 'TEMPORARY ' : '';
-               if ( strcmp( $this->getServerVersion(), '4.1' ) < 0 ) {
-                       # Hack for MySQL versions < 4.1, which don't support
-                       # "CREATE TABLE ... LIKE". Note that
-                       # "CREATE TEMPORARY TABLE ... SELECT * FROM ... LIMIT 0"
-                       # would not create the indexes we need....
-                       #
-                       # Note that we don't bother changing around the prefixes here be-
-                       # cause we know we're using MySQL anyway.
-
-                       $res = $this->query( 'SHOW CREATE TABLE ' . $this->addIdentifierQuotes( $oldName ) );
-                       $row = $this->fetchRow( $res );
-                       $oldQuery = $row[1];
-                       $query = preg_replace( '/CREATE TABLE `(.*?)`/',
-                               "CREATE $tmp TABLE " . $this->addIdentifierQuotes( $newName ), $oldQuery );
-                       if ($oldQuery === $query) {
-                               # Couldn't do replacement
-                               throw new MWException( "could not create temporary table $newName" );
-                       }
-               } else {
-                       $newName = $this->addIdentifierQuotes( $newName );
-                       $oldName = $this->addIdentifierQuotes( $oldName );
-                       $query = "CREATE $tmp TABLE $newName (LIKE $oldName)";
-               }
+               $newName = $this->addIdentifierQuotes( $newName );
+               $oldName = $this->addIdentifierQuotes( $oldName );
+               $query = "CREATE $tmp TABLE $newName (LIKE $oldName)";
                $this->query( $query, $fname );
        }
 
        /**
         * List all tables on the database
         *
-        * @param $prefix Only show tables with this prefix, e.g. mw_
+        * @param $prefix string Only show tables with this prefix, e.g. mw_
         * @param $fname String: calling function name
+        * @return array
         */
        function listTables( $prefix = null, $fname = 'DatabaseMysql::listTables' ) {
                $result = $this->query( "SHOW TABLES", $fname);
@@ -787,21 +909,26 @@ class DatabaseMysql extends DatabaseBase {
         * @return bool|ResultWrapper
         */
        public function dropTable( $tableName, $fName = 'DatabaseMysql::dropTable' ) {
-               if( !$this->tableExists( $tableName ) ) {
+               if( !$this->tableExists( $tableName, $fName ) ) {
                        return false;
                }
                return $this->query( "DROP TABLE IF EXISTS " . $this->tableName( $tableName ), $fName );
        }
 
+       /**
+        * @return array
+        */
        protected function getDefaultSchemaVars() {
                $vars = parent::getDefaultSchemaVars();
-               $vars['wgDBTableOptions'] = $GLOBALS['wgDBTableOptions'];
+               $vars['wgDBTableOptions'] = str_replace( 'TYPE', 'ENGINE', $GLOBALS['wgDBTableOptions'] );
+               $vars['wgDBTableOptions'] = str_replace( 'CHARSET=mysql4', 'CHARSET=binary', $vars['wgDBTableOptions'] );
                return $vars;
        }
 
        /**
         * Get status information from SHOW STATUS in an associative array
         *
+        * @param $which string
         * @return array
         */
        function getMysqlStatus( $which = "%" ) {
@@ -859,6 +986,9 @@ class MySQLField implements Field {
                return $this->tableName;
        }
 
+       /**
+        * @return string
+        */
        function type() {
                return $this->type;
        }