Bug 35162 - Change in Database api break extension MSSQLBackCompat
[lhc/web/wiklou.git] / includes / db / Database.php
index cff6690..5c03617 100644 (file)
@@ -47,7 +47,7 @@ interface DatabaseType {
         * Fields can be retrieved with $row->fieldname, with fields acting like
         * member variables.
         *
-        * @param $res SQL result object as returned from DatabaseBase::query(), etc.
+        * @param $res ResultWrapper|object as returned from DatabaseBase::query(), etc.
         * @return Row object
         * @throws DBUnexpectedError Thrown if the database returns an error
         */
@@ -226,6 +226,10 @@ abstract class DatabaseBase implements DatabaseType {
 
        protected $preparedArgs;
 
+       protected $htmlErrors;
+
+       protected $delimiter = ';';
+
 # ------------------------------------------------------------------------------
 # Accessors
 # ------------------------------------------------------------------------------
@@ -249,7 +253,7 @@ abstract class DatabaseBase implements DatabaseType {
         *   - false to disable debugging
         *   - omitted or null to do nothing
         *
-        * @return The previous value of the flag
+        * @return bool|null previous value of the flag
         */
        function debug( $debug = null ) {
                return wfSetBit( $this->mFlags, DBO_DEBUG, $debug );
@@ -275,7 +279,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @param $buffer null|bool
         *
-        * @return The previous value of the flag
+        * @return null|bool The previous value of the flag
         */
        function bufferResults( $buffer = null ) {
                if ( is_null( $buffer ) ) {
@@ -294,7 +298,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @param $ignoreErrors bool|null
         *
-        * @return The previous value of the flag.
+        * @return bool The previous value of the flag.
         */
        function ignoreErrors( $ignoreErrors = null ) {
                return wfSetBit( $this->mFlags, DBO_IGNORE, $ignoreErrors );
@@ -306,8 +310,8 @@ abstract class DatabaseBase implements DatabaseType {
         * Historically, transactions were allowed to be "nested". This is no
         * longer supported, so this function really only returns a boolean.
         *
-        * @param $level An integer (0 or 1), or omitted to leave it unchanged.
-        * @return The previous value
+        * @param $level int An integer (0 or 1), or omitted to leave it unchanged.
+        * @return int The previous value
         */
        function trxLevel( $level = null ) {
                return wfSetVar( $this->mTrxLevel, $level );
@@ -315,8 +319,8 @@ abstract class DatabaseBase implements DatabaseType {
 
        /**
         * Get/set the number of errors logged. Only useful when errors are ignored
-        * @param $count The count to set, or omitted to leave it unchanged.
-        * @return The error count
+        * @param $count int The count to set, or omitted to leave it unchanged.
+        * @return int The error count
         */
        function errorCount( $count = null ) {
                return wfSetVar( $this->mErrorCount, $count );
@@ -324,8 +328,8 @@ abstract class DatabaseBase implements DatabaseType {
 
        /**
         * Get/set the table prefix.
-        * @param $prefix The table prefix to set, or omitted to leave it unchanged.
-        * @return The previous table prefix.
+        * @param $prefix string The table prefix to set, or omitted to leave it unchanged.
+        * @return string The previous table prefix.
         */
        function tablePrefix( $prefix = null ) {
                return wfSetVar( $this->mTablePrefix, $prefix );
@@ -608,28 +612,45 @@ abstract class DatabaseBase implements DatabaseType {
                }
        }
 
-       /**
-        * Called by unserialize. Needed to reopen DB connection, which
-        * is not saved by serialize.
+       /**
+        * Called by serialize. Throw an exception when DB connection is serialized.
+        * This causes problems on some database engines because the connection is
+        * not restored on unserialize.
         */
-       public function __wakeup() {
-        if ( $this->isOpen() ) {
-                       $this->open( $this->mServer, $this->mUser,
-                                    $this->mPassword, $this->mDBname);
-               }
+       public function __sleep() {
+               throw new MWException( 'Database serialization may cause problems, since the connection is not restored on wakeup.' );
        }
 
        /**
         * Same as new DatabaseMysql( ... ), kept for backward compatibility
         * @deprecated since 1.17
         *
+        * @param $server
+        * @param $user
+        * @param $password
+        * @param $dbName
+        * @param $flags int
         * @return DatabaseMysql
         */
        static function newFromParams( $server, $user, $password, $dbName, $flags = 0 ) {
-               wfDeprecated( __METHOD__ );
+               wfDeprecated( __METHOD__, '1.17' );
                return new DatabaseMysql( $server, $user, $password, $dbName, $flags );
        }
 
+       /**
+        * Same as new factory( ... ), kept for backward compatibility
+        * @deprecated since 1.18
+        * @see Database::factory()
+        * @return DatabaseBase
+        */
+       public final static function newFromType( $dbType, $p = array() ) {
+               wfDeprecated( __METHOD__, '1.18' );
+               if ( isset( $p['tableprefix'] ) ) {
+                       $p['tablePrefix'] = $p['tableprefix'];
+               }
+               return self::factory( $dbType, $p );
+       }
+
        /**
         * Given a DB type, construct the name of the appropriate child class of
         * DatabaseBase. This is designed to replace all of the manual stuff like:
@@ -645,6 +666,8 @@ abstract class DatabaseBase implements DatabaseType {
         * @see ForeignDBRepo::getMasterDB()
         * @see WebInstaller_DBConnect::execute()
         *
+        * @since 1.18
+        *
         * @param $dbType String A possible DB type
         * @param $p Array An array of options to pass to the constructor.
         *    Valid options are: host, user, password, dbname, flags, tablePrefix
@@ -655,9 +678,9 @@ abstract class DatabaseBase implements DatabaseType {
                        'mysql', 'postgres', 'sqlite', 'oracle', 'mssql', 'ibm_db2'
                );
                $dbType = strtolower( $dbType );
+               $class = 'Database' . ucfirst( $dbType );
 
-               if( in_array( $dbType, $canonicalDBTypes ) ) {
-                       $class = 'Database' . ucfirst( $dbType );
+               if( in_array( $dbType, $canonicalDBTypes ) || ( class_exists( $class ) && is_subclass_of( $class, 'DatabaseBase' ) ) ) {
                        return new $class(
                                isset( $p['host'] ) ? $p['host'] : false,
                                isset( $p['user'] ) ? $p['user'] : false,
@@ -687,13 +710,17 @@ abstract class DatabaseBase implements DatabaseType {
                }
                if ( $this->mPHPError ) {
                        $error = preg_replace( '!\[<a.*</a>\]!', '', $this->mPHPError );
-                       $error = preg_replace( '!^.*?:(.*)$!', '$1', $error );
+                       $error = preg_replace( '!^.*?:\s?(.*)$!', '$1', $error );
                        return $error;
                } else {
                        return false;
                }
        }
 
+       /**
+        * @param $errno
+        * @param $errstr
+        */
        protected function connectionErrorHandler( $errno,  $errstr ) {
                $this->mPHPError = $errstr;
        }
@@ -704,11 +731,27 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @return Bool operation success. true if already closed.
         */
-       function close() {
-               # Stub, should probably be overridden
-               return true;
+       public function close() {
+               $this->mOpened = false;
+               if ( $this->mConn ) {
+                       if ( $this->trxLevel() ) {
+                               $this->commit( __METHOD__ );
+                       }
+                       $ret = $this->closeConnection();
+                       $this->mConn = false;
+                       return $ret;
+               } else {
+                       return true;
+               }
        }
 
+       /**
+        * Closes underlying database connection
+        * @since 1.20
+        * @return bool: Whether connection was closed successfully
+        */
+       protected abstract function closeConnection();
+
        /**
         * @param $error String: fallback error message, used if none is given by DB
         */
@@ -739,7 +782,7 @@ abstract class DatabaseBase implements DatabaseType {
         * @return bool
         */
        function isWriteQuery( $sql ) {
-               return !preg_match( '/^(?:SELECT|BEGIN|COMMIT|SET|SHOW|\(SELECT)\b/i', $sql );
+               return !preg_match( '/^(?:SELECT|BEGIN|ROLLBACK|COMMIT|SET|SHOW|\(SELECT)\b/i', $sql );
        }
 
        /**
@@ -809,8 +852,8 @@ abstract class DatabaseBase implements DatabaseType {
                        # that would delay transaction initializations to once connection
                        # is really used by application
                        $sqlstart = substr( $sql, 0, 10 ); // very much worth it, benchmark certified(tm)
-                       if ( strpos( $sqlstart, "SHOW " ) !== 0 and strpos( $sqlstart, "SET " ) !== 0 )
-                               $this->begin();
+                       if ( strpos( $sqlstart, "SHOW " ) !== 0 && strpos( $sqlstart, "SET " ) !== 0 )
+                               $this->begin( __METHOD__ . " ($fname)" );
                }
 
                if ( $this->debug() ) {
@@ -820,20 +863,21 @@ abstract class DatabaseBase implements DatabaseType {
                        $sqlx = substr( $commentedSql, 0, 500 );
                        $sqlx = strtr( $sqlx, "\t\n", '  ' );
 
-                       if ( $isMaster ) {
-                               wfDebug( "Query $cnt (master): $sqlx\n" );
-                       } else {
-                               wfDebug( "Query $cnt (slave): $sqlx\n" );
-                       }
+                       $master = $isMaster ? 'master' : 'slave';
+                       wfDebug( "Query {$this->mDBname} ($cnt) ($master): $sqlx\n" );
                }
 
                if ( istainted( $sql ) & TC_MYSQL ) {
                        throw new MWException( 'Tainted query found' );
                }
 
+               $queryId = MWDebug::query( $sql, $fname, $isMaster );
+
                # Do the query and handle errors
                $ret = $this->doQuery( $commentedSql );
 
+               MWDebug::queryTime( $queryId );
+
                # Try reconnecting if the connection was lost
                if ( false === $ret && $this->wasErrorReissuable() ) {
                        # Transaction is gone, like it or not
@@ -921,6 +965,7 @@ abstract class DatabaseBase implements DatabaseType {
 
        /**
         * Free a prepared query, generated by prepare().
+        * @param $prepared
         */
        function freePrepared( $prepared ) {
                /* No-op by default */
@@ -1065,7 +1110,7 @@ abstract class DatabaseBase implements DatabaseType {
         * @param $fname string The function name of the caller.
         * @param $options string|array The query options. See DatabaseBase::select() for details.
         *
-        * @return false|mixed The value from the field, or false on failure.
+        * @return bool|mixed The value from the field, or false on failure.
         */
        function selectField( $table, $var, $cond = '', $fname = 'DatabaseBase::selectField',
                $options = array() )
@@ -1245,9 +1290,6 @@ abstract class DatabaseBase implements DatabaseType {
         *     - If the value is an array, an IN(...) clause will be constructed,
         *       such that the field name may match any of the elements in the
         *       array. The elements of the array will be quoted.
-        *     - If the field name ends with "!", this is taken as a flag which
-        *       inverts the comparison, allowing NOT IN clauses to be constructed,
-        *       for example: array( 'user_id!' => array( 1, 2, 3 ) )
         *
         * Note that expressions are often DBMS-dependent in their syntax.
         * DBMS-independent wrappers are provided for constructing several types of
@@ -1346,7 +1388,7 @@ abstract class DatabaseBase implements DatabaseType {
         * @param $options string|array Query options
         * @param $join_conds string|array Join conditions
         *
-        * @return SQL query string.
+        * @return string SQL query string.
         * @see DatabaseBase::select()
         */
        function selectSQLText( $table, $vars, $conds = '', $fname = 'DatabaseBase::select', $options = array(), $join_conds = array() ) {
@@ -1407,12 +1449,12 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @param $table string|array Table name
         * @param $vars string|array Field names
-        * @param $conds|array Conditions
+        * @param $conds array Conditions
         * @param $fname string Caller function name
         * @param $options string|array Query options
         * @param $join_conds array|string Join conditions
         *
-        * @return ResultWrapper|bool
+        * @return object|bool
         */
        function selectRow( $table, $vars, $conds, $fname = 'DatabaseBase::selectRow',
                $options = array(), $join_conds = array() )
@@ -1533,21 +1575,24 @@ abstract class DatabaseBase implements DatabaseType {
         * Query whether a given table exists
         *
         * @param $table string
+        * @param $fname string
         *
         * @return bool
         */
-       function tableExists( $table ) {
+       function tableExists( $table, $fname = __METHOD__ ) {
                $table = $this->tableName( $table );
                $old = $this->ignoreErrors( true );
-               $res = $this->query( "SELECT 1 FROM $table LIMIT 1", __METHOD__ );
+               $res = $this->query( "SELECT 1 FROM $table LIMIT 1", $fname );
                $this->ignoreErrors( $old );
 
                return (bool)$res;
        }
 
        /**
-        * @todo document
         * mysql_field_type() wrapper
+        * @param $res
+        * @param $index
+        * @return string
         */
        function fieldType( $res, $index ) {
                if ( $res instanceof ResultWrapper ) {
@@ -2177,7 +2222,7 @@ abstract class DatabaseBase implements DatabaseType {
         * @return string
         */
        function quote_ident( $s ) {
-               wfDeprecated( __METHOD__ );
+               wfDeprecated( __METHOD__, '1.18' );
                return $this->addIdentifierQuotes( $s );
        }
 
@@ -2192,7 +2237,7 @@ abstract class DatabaseBase implements DatabaseType {
         * @return string
         */
        public function escapeLike( $s ) {
-               wfDeprecated( __METHOD__ );
+               wfDeprecated( __METHOD__, '1.17' );
                return $this->escapeLikeInternal( $s );
        }
 
@@ -2262,6 +2307,12 @@ abstract class DatabaseBase implements DatabaseType {
         * Returns an appropriately quoted sequence value for inserting a new row.
         * MySQL has autoincrement fields, so this is just NULL. But the PostgreSQL
         * subclass will return an integer, and save the value for insertId()
+        *
+        * Any implementation of this function should *not* involve reusing
+        * sequence numbers created for rolled-back transactions.
+        * See http://bugs.mysql.com/bug.php?id=30767 for details.
+        * @param $seqName string
+        * @return null
         */
        function nextSequenceValue( $seqName ) {
                return null;
@@ -2274,6 +2325,8 @@ abstract class DatabaseBase implements DatabaseType {
         * which index to pick.  Anyway, other databases might have different
         * indexes on a given table.  So don't bother overriding this unless you're
         * MySQL.
+        * @param $index
+        * @return string
         */
        function useIndexClause( $index ) {
                return '';
@@ -2352,9 +2405,9 @@ abstract class DatabaseBase implements DatabaseType {
         * REPLACE query wrapper for MySQL and SQLite, which have a native REPLACE
         * statement.
         *
-        * @param $table Table name
-        * @param $rows Rows to insert
-        * @param $fname Caller function name
+        * @param $table string Table name
+        * @param $rows array Rows to insert
+        * @param $fname string Caller function name
         *
         * @return ResultWrapper
         */
@@ -2536,7 +2589,10 @@ abstract class DatabaseBase implements DatabaseType {
                        " FROM $srcTable $useIndex ";
 
                if ( $conds != '*' ) {
-                       $sql .= ' WHERE ' . $this->makeList( $conds, LIST_AND );
+                       if ( is_array( $conds ) ) {
+                               $conds = $this->makeList( $conds, LIST_AND );
+                       }
+                       $sql .= " WHERE $conds";
                }
 
                $sql .= " $tailOpts";
@@ -2560,7 +2616,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @param $sql String SQL query we will append the limit too
         * @param $limit Integer the SQL limit
-        * @param $offset Integer|false the SQL offset (default false)
+        * @param $offset Integer|bool the SQL offset (default false)
         *
         * @return string
         */
@@ -2632,6 +2688,16 @@ abstract class DatabaseBase implements DatabaseType {
                return "REPLACE({$orig}, {$old}, {$new})";
        }
 
+       /**
+        * Determines how long the server has been up
+        * STUB
+        *
+        * @return int
+        */
+       function getServerUptime() {
+               return 0;
+       }
+
        /**
         * Determines if the last failure was due to a deadlock
         * STUB
@@ -2642,6 +2708,16 @@ abstract class DatabaseBase implements DatabaseType {
                return false;
        }
 
+       /**
+        * Determines if the last failure was due to a lock timeout
+        * STUB
+        *
+        * @return bool
+        */
+       function wasLockTimeout() {
+               return false;
+       }
+
        /**
         * Determines if the last query error was something that should be dealt
         * with by pinging the connection and reissuing the query.
@@ -2682,9 +2758,8 @@ abstract class DatabaseBase implements DatabaseType {
         * @return bool
         */
        function deadlockLoop() {
-               $myFname = 'DatabaseBase::deadlockLoop';
 
-               $this->begin();
+               $this->begin( __METHOD__ );
                $args = func_get_args();
                $function = array_shift( $args );
                $oldIgnore = $this->ignoreErrors( true );
@@ -2715,11 +2790,11 @@ abstract class DatabaseBase implements DatabaseType {
                $this->ignoreErrors( $oldIgnore );
 
                if ( $tries <= 0 ) {
-                       $this->rollback( $myFname );
+                       $this->rollback( __METHOD__ );
                        $this->reportQueryError( $error, $errno, $sql, $fname );
                        return false;
                } else {
-                       $this->commit( $myFname );
+                       $this->commit( __METHOD__ );
                        return $retVal;
                }
        }
@@ -2731,34 +2806,33 @@ abstract class DatabaseBase implements DatabaseType {
         * @param $timeout Integer: the maximum number of seconds to wait for
         *   synchronisation
         *
-        * @return An integer: zero if the slave was past that position already,
+        * @return integer: zero if the slave was past that position already,
         *   greater than zero if we waited for some period of time, less than
         *   zero if we timed out.
         */
        function masterPosWait( DBMasterPos $pos, $timeout ) {
-               $fname = 'DatabaseBase::masterPosWait';
-               wfProfileIn( $fname );
+               wfProfileIn( __METHOD__ );
 
                if ( !is_null( $this->mFakeSlaveLag ) ) {
                        $wait = intval( ( $pos->pos - microtime( true ) + $this->mFakeSlaveLag ) * 1e6 );
 
                        if ( $wait > $timeout * 1e6 ) {
                                wfDebug( "Fake slave timed out waiting for $pos ($wait us)\n" );
-                               wfProfileOut( $fname );
+                               wfProfileOut( __METHOD__ );
                                return -1;
                        } elseif ( $wait > 0 ) {
                                wfDebug( "Fake slave waiting $wait us\n" );
                                usleep( $wait );
-                               wfProfileOut( $fname );
+                               wfProfileOut( __METHOD__ );
                                return 1;
                        } else {
                                wfDebug( "Fake slave up to date ($wait us)\n" );
-                               wfProfileOut( $fname );
+                               wfProfileOut( __METHOD__ );
                                return 0;
                        }
                }
 
-               wfProfileOut( $fname );
+               wfProfileOut( __METHOD__ );
 
                # Real waits are implemented in the subclass.
                return 0;
@@ -2852,7 +2926,7 @@ abstract class DatabaseBase implements DatabaseType {
        /**
         * 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
         */
        function listTables( $prefix = null, $fname = 'DatabaseBase::listTables' ) {
@@ -2908,7 +2982,7 @@ abstract class DatabaseBase implements DatabaseType {
         *
         * @param $result bool|ResultWrapper
         *
-        * @param bool|ResultWrapper
+        * @return bool|ResultWrapper
         */
        function resultObject( $result ) {
                if ( empty( $result ) ) {
@@ -2952,7 +3026,7 @@ abstract class DatabaseBase implements DatabaseType {
         * installations. Most callers should use LoadBalancer::safeGetLag()
         * instead.
         *
-        * @return Database replication lag in seconds
+        * @return int Database replication lag in seconds
         */
        function getLag() {
                return intval( $this->mFakeSlaveLag );
@@ -2972,6 +3046,8 @@ abstract class DatabaseBase implements DatabaseType {
         * don't allow simple quoted strings to be inserted. To insert into such
         * a field, pass the data through this function before passing it to
         * DatabaseBase::insert().
+        * @param $b string
+        * @return string
         */
        function encodeBlob( $b ) {
                return $b;
@@ -2981,20 +3057,36 @@ abstract class DatabaseBase implements DatabaseType {
         * Some DBMSs return a special placeholder object representing blob fields
         * in result objects. Pass the object through this function to return the
         * original string.
+        * @param $b string
+        * @return string
         */
        function decodeBlob( $b ) {
                return $b;
        }
 
        /**
-        * Override database's default connection timeout.  May be useful for very
-        * long batch queries such as full-wiki dumps, where a single query reads
-        * out over hours or days.  May or may not be necessary for non-MySQL
-        * databases.  For most purposes, leaving it as a no-op should be fine.
+        * Override database's default connection timeout
         *
         * @param $timeout Integer in seconds
+        * @return void
+        * @deprecated since 1.19; use setSessionOptions()
         */
-       public function setTimeout( $timeout ) {}
+       public function setTimeout( $timeout ) {
+               wfDeprecated( __METHOD__, '1.19' );
+               $this->setSessionOptions( array( 'connTimeout' => $timeout ) );
+       }
+
+       /**
+        * Override database's default behavior. $options include:
+        *     'connTimeout' : Set the connection timeout value in seconds.
+        *                     May be useful for very long batch queries such as
+        *                     full-wiki dumps, where a single query reads out over
+        *                     hours or days.
+        *
+        * @param $options Array
+        * @return void
+        */
+       public function setSessionOptions( array $options ) {}
 
        /**
         * Read and execute SQL commands from a file.
@@ -3007,6 +3099,7 @@ abstract class DatabaseBase implements DatabaseType {
         * @param $resultCallback Callback: Optional function called for each MySQL result
         * @param $fname String: Calling function name or false if name should be
         *      generated dynamically using $filename
+        * @return bool|string
         */
        function sourceFile( $filename, $lineCallback = false, $resultCallback = false, $fname = false ) {
                wfSuppressWarnings();
@@ -3058,7 +3151,7 @@ abstract class DatabaseBase implements DatabaseType {
         * ones in $GLOBALS. If an array is set here, $GLOBALS will not be used at
         * all. If it's set to false, $GLOBALS will be used.
         *
-        * @param $vars False, or array mapping variable name to value.
+        * @param $vars bool|array mapping variable name to value.
         */
        function setSchemaVars( $vars ) {
                $this->mSchemaVars = $vars;
@@ -3074,23 +3167,22 @@ abstract class DatabaseBase implements DatabaseType {
         * @param $lineCallback Callback: Optional function called before reading each line
         * @param $resultCallback Callback: Optional function called for each MySQL result
         * @param $fname String: Calling function name
+        * @param $inputCallback Callback: Optional function called for each complete line (ended with ;) sent
+        * @return bool|string
         */
-       function sourceStream( $fp, $lineCallback = false, $resultCallback = false,
-               $fname = 'DatabaseBase::sourceStream' )
+       public function sourceStream( $fp, $lineCallback = false, $resultCallback = false,
+               $fname = 'DatabaseBase::sourceStream', $inputCallback = false )
        {
-               $cmd = "";
-               $done = false;
-               $dollarquote = false;
+               $cmd = '';
 
-               while ( ! feof( $fp ) ) {
+               while ( !feof( $fp ) ) {
                        if ( $lineCallback ) {
                                call_user_func( $lineCallback );
                        }
 
                        $line = trim( fgets( $fp ) );
-                       $sl = strlen( $line ) - 1;
 
-                       if ( $sl < 0 ) {
+                       if ( $line == '' ) {
                                continue;
                        }
 
@@ -3098,32 +3190,19 @@ abstract class DatabaseBase implements DatabaseType {
                                continue;
                        }
 
-                       # # Allow dollar quoting for function declarations
-                       if ( substr( $line, 0, 4 ) == '$mw$' ) {
-                               if ( $dollarquote ) {
-                                       $dollarquote = false;
-                                       $done = true;
-                               }
-                               else {
-                                       $dollarquote = true;
-                               }
-                       }
-                       elseif ( !$dollarquote ) {
-                               if ( ';' == $line[$sl] && ( $sl < 2 || ';' != $line[$sl - 1] ) ) {
-                                       $done = true;
-                                       $line = substr( $line, 0, $sl );
-                               }
-                       }
-
                        if ( $cmd != '' ) {
                                $cmd .= ' ';
                        }
 
+                       $done = $this->streamStatementEnd( $cmd, $line );
+
                        $cmd .= "$line\n";
 
-                       if ( $done ) {
-                               $cmd = str_replace( ';;', ";", $cmd );
+                       if ( $done || feof( $fp ) ) {
                                $cmd = $this->replaceVars( $cmd );
+                               if ( $inputCallback ) {
+                                       call_user_func( $inputCallback, $cmd );
+                               }
                                $res = $this->query( $cmd, $fname );
 
                                if ( $resultCallback ) {
@@ -3136,13 +3215,30 @@ abstract class DatabaseBase implements DatabaseType {
                                }
 
                                $cmd = '';
-                               $done = false;
                        }
                }
 
                return true;
        }
 
+       /**
+        * Called by sourceStream() to check if we've reached a statement end
+        *
+        * @param $sql String SQL assembled so far
+        * @param $newLine String New line about to be added to $sql
+        * @return Bool Whether $newLine contains end of the statement
+        */
+       public function streamStatementEnd( &$sql, &$newLine ) {
+               if ( $this->delimiter ) {
+                       $prev = $newLine;
+                       $newLine = preg_replace( '/' . preg_quote( $this->delimiter, '/' ) . '$/', '', $newLine );
+                       if ( $newLine != $prev ) {
+                               return true;
+                       }
+               }
+               return false;
+       }
+
        /**
         * Database independent variable replacement. Replaces a set of variables
         * in an SQL statement with their contents as given by $this->getSchemaVars().
@@ -3197,6 +3293,8 @@ abstract class DatabaseBase implements DatabaseType {
        /**
         * Get schema variables. If none have been set via setSchemaVars(), then
         * use some defaults from the current object.
+        *
+        * @return array
         */
        protected function getSchemaVars() {
                if ( $this->mSchemaVars ) {
@@ -3270,7 +3368,7 @@ abstract class DatabaseBase implements DatabaseType {
         * @param $lockName String: Name of lock to release
         * @param $method String: Name of method calling us
         *
-        * @return Returns 1 if the lock was released, 0 if the lock was not established
+        * @return int Returns 1 if the lock was released, 0 if the lock was not established
         * by this thread (in which case the lock is not released), and NULL if the named
         * lock did not exist
         */
@@ -3308,9 +3406,10 @@ abstract class DatabaseBase implements DatabaseType {
         * @param $tableName string
         * @param $fName string
         * @return bool|ResultWrapper
+        * @since 1.18
         */
        public function dropTable( $tableName, $fName = 'DatabaseBase::dropTable' ) {
-               if( !$this->tableExists( $tableName ) ) {
+               if( !$this->tableExists( $tableName, $fName ) ) {
                        return false;
                }
                $sql = "DROP TABLE " . $this->tableName( $tableName );
@@ -3342,17 +3441,28 @@ abstract class DatabaseBase implements DatabaseType {
        }
 
        /**
-        * Encode an expiry time
+        * Encode an expiry time into the DBMS dependent format
         *
         * @param $expiry String: timestamp for expiry, or the 'infinity' string
         * @return String
         */
        public function encodeExpiry( $expiry ) {
-               if ( $expiry == '' || $expiry == $this->getInfinity() ) {
-                       return $this->getInfinity();
-               } else {
-                       return $this->timestamp( $expiry );
-               }
+               return ( $expiry == '' || $expiry == 'infinity' || $expiry == $this->getInfinity() )
+                       ? $this->getInfinity()
+                       : $this->timestamp( $expiry );
+       }
+
+       /**
+        * Decode an expiry time into a DBMS independent format
+        *
+        * @param $expiry String: DB timestamp field value for expiry
+        * @param $format integer: TS_* constant, defaults to TS_MW
+        * @return String
+        */
+       public function decodeExpiry( $expiry, $format = TS_MW ) {
+               return ( $expiry == '' || $expiry == $this->getInfinity() )
+                       ? 'infinity'
+                       : wfTimestamp( $format, $expiry );
        }
 
        /**