Followup to r77713, rename quote_ident to addIdentifierQuotes to follow naming conven...
[lhc/web/wiklou.git] / includes / db / Database.php
index b009906..7248bb9 100644 (file)
@@ -32,7 +32,6 @@ interface DatabaseType {
 
        /**
         * Open a connection to the database. Usually aborts on failure
-        * If the failFunction is set to a non-zero integer, returns success
         *
         * @param $server String: database server host
         * @param $user String: database user name
@@ -45,7 +44,7 @@ interface DatabaseType {
 
        /**
         * The DBMS-dependent part of query()
-        * @todo @fixme Make this private someday
+        * @todo Fixme: Make this private someday
         *
         * @param  $sql String: SQL query.
         * @return Result object to feed to fetchObject, fetchRow, ...; or false on failure
@@ -149,6 +148,15 @@ interface DatabaseType {
         */
        public function fieldInfo( $table, $field );
 
+       /**
+        * Get information about an index into an object
+        * @param $table string: Table name
+        * @param $index string: Index name
+        * @param $fname string: Calling function name
+        * @return Mixed: Database-specific index description class or false if the index does not exist
+        */
+       function indexInfo( $table, $index, $fname = 'Database::indexInfo' );
+
        /**
         * Get the number of rows affected by the last write query
         * @see http://www.php.net/mysql_affected_rows
@@ -167,7 +175,7 @@ interface DatabaseType {
 
        /**
         * Returns a wikitext link to the DB's website, e.g.,
-        *         return "[http://www.mysql.com/ MySQL]";
+        *     return "[http://www.mysql.com/ MySQL]";
         * Should at least contain plain text, if for some reason
         * your database has no website.
         *
@@ -210,7 +218,6 @@ abstract class DatabaseBase implements DatabaseType {
        protected $mServer, $mUser, $mPassword, $mConn = null, $mDBname;
        protected $mOpened = false;
 
-       protected $mFailFunction;
        protected $mTablePrefix;
        protected $mFlags;
        protected $mTrxLevel = 0;
@@ -235,14 +242,6 @@ abstract class DatabaseBase implements DatabaseType {
                return $this->getServerVersion();
        }
 
-       /**
-        * Fail function, takes a Database as a parameter
-        * Set to false for default, 1 for ignore errors
-        */
-       function failFunction( $function = null ) {
-               return wfSetVar( $this->mFailFunction, $function );
-       }
-
        /**
         * Boolean, controls output of large amounts of debug information
         */
@@ -419,13 +418,13 @@ abstract class DatabaseBase implements DatabaseType {
         * Set a flag for this connection
         *
         * @param $flag Integer: DBO_* constants from Defines.php:
-        *       - DBO_DEBUG: output some debug info (same as debug())
-        *       - DBO_NOBUFFER: don't buffer results (inverse of bufferResults())
-        *       - DBO_IGNORE: ignore errors (same as ignoreErrors())
-        *       - DBO_TRX: automatically start transactions
-        *       - DBO_DEFAULT: automatically sets DBO_TRX if not in command line mode
-        *               and removes it in command line mode
-        *       - DBO_PERSISTENT: use persistant database connection
+        *   - DBO_DEBUG: output some debug info (same as debug())
+        *   - DBO_NOBUFFER: don't buffer results (inverse of bufferResults())
+        *   - DBO_IGNORE: ignore errors (same as ignoreErrors())
+        *   - DBO_TRX: automatically start transactions
+        *   - DBO_DEFAULT: automatically sets DBO_TRX if not in command line mode
+        *       and removes it in command line mode
+        *   - DBO_PERSISTENT: use persistant database connection
         */
        function setFlag( $flag ) {
                $this->mFlags |= $flag;
@@ -487,12 +486,11 @@ abstract class DatabaseBase implements DatabaseType {
         * @param $user String: database user name
         * @param $password String: database user password
         * @param $dbName String: database name
-        * @param $failFunction
         * @param $flags
         * @param $tablePrefix String: database table prefixes. By default use the prefix gave in LocalSettings.php
         */
        function __construct( $server = false, $user = false, $password = false, $dbName = false,
-               $failFunction = false, $flags = 0, $tablePrefix = 'get from global'
+               $flags = 0, $tablePrefix = 'get from global'
        ) {
                global $wgOut, $wgDBprefix, $wgCommandLineMode;
 
@@ -500,8 +498,6 @@ abstract class DatabaseBase implements DatabaseType {
                if ( !isset( $wgOut ) ) {
                        $wgOut = null;
                }
-
-               $this->mFailFunction = $failFunction;
                $this->mFlags = $flags;
 
                if ( $this->mFlags & DBO_DEFAULT ) {
@@ -537,12 +533,11 @@ abstract class DatabaseBase implements DatabaseType {
         * @param $user String: database user name
         * @param $password String: database user password
         * @param $dbName String: database name
-        * @param failFunction
         * @param $flags
         */
-       static function newFromParams( $server, $user, $password, $dbName, $failFunction = false, $flags = 0 ) {
+       static function newFromParams( $server, $user, $password, $dbName, $flags = 0 ) {
                wfDeprecated( __METHOD__ );
-               return new DatabaseMysql( $server, $user, $password, $dbName, $failFunction, $flags );
+               return new DatabaseMysql( $server, $user, $password, $dbName, $flags );
        }
 
        protected function installErrorHandler() {
@@ -565,7 +560,7 @@ abstract class DatabaseBase implements DatabaseType {
                }
        }
 
-       protected function connectionErrorHandler( $errno,      $errstr ) {
+       protected function connectionErrorHandler( $errno,  $errstr ) {
                $this->mPHPError = $errstr;
        }
 
@@ -589,16 +584,8 @@ abstract class DatabaseBase implements DatabaseType {
                        $error = $myError;
                }
 
-               if ( $this->mFailFunction ) {
-                       # Legacy error handling method
-                       if ( !is_int( $this->mFailFunction ) ) {
-                               $ff = $this->mFailFunction;
-                               $ff( $this, $error );
-                       }
-               } else {
-                       # New method
-                       throw new DBConnectionError( $this, $error );
-               }
+               # New method
+               throw new DBConnectionError( $this, $error );
        }
 
        /**
@@ -612,13 +599,13 @@ abstract class DatabaseBase implements DatabaseType {
        /**
         * Usually aborts on failure.  If errors are explicitly ignored, returns success.
         *
-        * @param  $sql            String: SQL query
-        * @param  $fname          String: Name of the calling function, for profiling/SHOW PROCESSLIST
-        *         comment (you can use __METHOD__ or add some extra info)
-        * @param  $tempIgnore Boolean:   Whether to avoid throwing an exception on errors...
-        *         maybe best to catch the exception instead?
-        * @return true for a successful write query, ResultWrapper object for a successful read query,
-        *         or false on failure if $tempIgnore set
+        * @param  $sql        String: SQL query
+        * @param  $fname      String: Name of the calling function, for profiling/SHOW PROCESSLIST
+        *     comment (you can use __METHOD__ or add some extra info)
+        * @param  $tempIgnore Boolean:   Whether to avoid throwing an exception on errors...
+        *     maybe best to catch the exception instead?
+        * @return boolean or ResultWrapper. true for a successful write query, ResultWrapper object for a successful read query,
+        *     or false on failure if $tempIgnore set
         * @throws DBQueryError Thrown when the database returns an error of any kind
         */
        public function query( $sql, $fname = '', $tempIgnore = false ) {
@@ -654,7 +641,7 @@ abstract class DatabaseBase implements DatabaseType {
                # Add a comment for easy SHOW PROCESSLIST interpretation
                # if ( $fname ) {
                        global $wgUser;
-                       if ( is_object( $wgUser ) && !( $wgUser instanceof StubObject ) ) {
+                       if ( is_object( $wgUser ) && $wgUser->mDataLoaded ) {
                                $userName = $wgUser->getName();
                                if ( mb_strlen( $userName ) > 15 ) {
                                        $userName = mb_substr( $userName, 0, 15 ) . '...';
@@ -684,7 +671,7 @@ abstract class DatabaseBase implements DatabaseType {
 
                        $cnt++;
                        $sqlx = substr( $commentedSql, 0, 500 );
-                       $sqlx = strtr( $sqlx, "\t\n", '  ' );
+                       $sqlx = strtr( $sqlx, "\t\n", '  ' );
 
                        if ( $isMaster ) {
                                wfDebug( "Query $cnt (master): $sqlx\n" );
@@ -709,7 +696,7 @@ abstract class DatabaseBase implements DatabaseType {
                        if ( $this->ping() ) {
                                wfDebug( "Reconnected\n" );
                                $sqlx = substr( $commentedSql, 0, 500 );
-                               $sqlx = strtr( $sqlx, "\t\n", '  ' );
+                               $sqlx = strtr( $sqlx, "\t\n", '  ' );
                                global $wgRequestTime;
                                $elapsed = round( microtime( true ) - $wgRequestTime, 3 );
                                wfLogDBError( "Connection lost and reconnected after {$elapsed}s, query: $sqlx\n" );
@@ -762,7 +749,7 @@ abstract class DatabaseBase implements DatabaseType {
         * ? = scalar value, quoted as necessary
         * ! = raw SQL bit (a function for instance)
         * & = filename; reads the file and inserts as a blob
-        *         (we don't use this though...)
+        *     (we don't use this though...)
         */
        function prepare( $sql, $func = 'DatabaseBase::prepare' ) {
                /* MySQL doesn't support prepared statements (yet), so just
@@ -862,7 +849,7 @@ abstract class DatabaseBase implements DatabaseType {
         * @param $res Mixed: A SQL result
         */
        function freeResult( $res ) {
-               # Stub.  Might not really need to be overridden, since results should
+               # Stub.  Might not really need to be overridden, since results should
                # be freed by PHP when the variable goes out of scope anyway.
        }
 
@@ -916,7 +903,7 @@ abstract class DatabaseBase implements DatabaseType {
         * @private
         *
         * @param $options Array: associative array of options to be turned into
-        *                              an SQL query, valid keys are listed in the function.
+        *              an SQL query, valid keys are listed in the function.
         * @return Array
         */
        function makeSelectOptions( $options ) {
@@ -1007,13 +994,13 @@ abstract class DatabaseBase implements DatabaseType {
         * SELECT wrapper
         *
         * @param $table   Mixed:  Array or string, table name(s) (prefix auto-added)
-        * @param $vars    Mixed:  Array or string, field name(s) to be retrieved
+        * @param $vars    Mixed:  Array or string, field name(s) to be retrieved
         * @param $conds   Mixed:  Array or string, condition(s) for WHERE
         * @param $fname   String: Calling function name (use __METHOD__) for logs/profiling
         * @param $options Array:  Associative array of options (e.g. array('GROUP BY' => 'page_title')),
-        *                                                 see DatabaseBase::makeSelectOptions code for list of supported stuff
+        *                         see DatabaseBase::makeSelectOptions code for list of supported stuff
         * @param $join_conds Array: Associative array of table join conditions (optional)
-        *                                                       (e.g. array( 'page' => array('LEFT JOIN','page_latest=rev_id') )
+        *                           (e.g. array( 'page' => array('LEFT JOIN','page_latest=rev_id') )
         * @return mixed Database result resource (feed to DatabaseBase::fetchObject or whatever), or false on failure
         */
        function select( $table, $vars, $conds = '', $fname = 'DatabaseBase::select', $options = array(), $join_conds = array() ) {
@@ -1025,14 +1012,14 @@ abstract class DatabaseBase implements DatabaseType {
        /**
         * SELECT wrapper
         *
-        * @param $table   Mixed:  Array or string, table name(s) (prefix auto-added)
-        * @param $vars    Mixed:  Array or string, field name(s) to be retrieved
+        * @param $table   Mixed:  Array or string, table name(s) (prefix auto-added). Array keys are table aliases (optional)
+        * @param $vars    Mixed:  Array or string, field name(s) to be retrieved
         * @param $conds   Mixed:  Array or string, condition(s) for WHERE
         * @param $fname   String: Calling function name (use __METHOD__) for logs/profiling
         * @param $options Array:  Associative array of options (e.g. array('GROUP BY' => 'page_title')),
-        *                                                 see DatabaseBase::makeSelectOptions code for list of supported stuff
+        *                         see DatabaseBase::makeSelectOptions code for list of supported stuff
         * @param $join_conds Array: Associative array of table join conditions (optional)
-        *                                                       (e.g. array( 'page' => array('LEFT JOIN','page_latest=rev_id') )
+        *                           (e.g. array( 'page' => array('LEFT JOIN','page_latest=rev_id') )
         * @return string, the SQL text
         */
        function selectSQLText( $table, $vars, $conds = '', $fname = 'DatabaseBase::select', $options = array(), $join_conds = array() ) {
@@ -1048,7 +1035,7 @@ abstract class DatabaseBase implements DatabaseType {
                        if ( !empty( $join_conds ) || ( isset( $options['USE INDEX'] ) && is_array( @$options['USE INDEX'] ) ) ) {
                                $from = ' FROM ' . $this->tableNamesWithUseIndexOrJOIN( $table, @$options['USE INDEX'], $join_conds );
                        } else {
-                               $from = ' FROM ' . implode( ',', array_map( array( &$this, 'tableName' ), $table ) );
+                               $from = ' FROM ' . implode( ',', $this->tableNamesWithAlias( $table ) );
                        }
                } elseif ( $table != '' ) {
                        if ( $table { 0 } == ' ' ) {
@@ -1090,10 +1077,10 @@ abstract class DatabaseBase implements DatabaseType {
         * @param $table String: table name
         * @param $vars String: the selected variables
         * @param $conds Array: a condition map, terms are ANDed together.
-        *       Items with numeric keys are taken to be literal conditions
+        *   Items with numeric keys are taken to be literal conditions
         * Takes an array of selected variables, and a condition map, which is ANDed
         * e.g: selectRow( "page", array( "page_id" ), array( "page_namespace" =>
-        * NS_MAIN, "page_title" => "Astronomy" ) )       would return an object where
+        * NS_MAIN, "page_title" => "Astronomy" ) )   would return an object where
         * $obj- >page_id is the ID of the Astronomy article
         * @param $fname String: Calling function name
         * @param $options Array
@@ -1197,35 +1184,6 @@ abstract class DatabaseBase implements DatabaseType {
                }
        }
 
-
-       /**
-        * Get information about an index into an object
-        * Returns false if the index does not exist
-        */
-       function indexInfo( $table, $index, $fname = 'DatabaseBase::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();
-
-               while ( $row = $this->fetchObject( $res ) ) {
-                       if ( $row->Key_name == $index ) {
-                               $result[] = $row;
-                       }
-               }
-
-               return empty( $result ) ? false : $result;
-       }
-
        /**
         * Query whether a given table exists
         */
@@ -1350,9 +1308,9 @@ abstract class DatabaseBase implements DatabaseType {
         * @param $values Array:  An array of values to SET
         * @param $conds  Array:  An array of conditions (WHERE). Use '*' to update all rows.
         * @param $fname  String: The Class::Function calling this function
-        *                                                (for the log)
+        *                        (for the log)
         * @param $options Array: An array of UPDATE options, can be one or
-        *                                                more of IGNORE, LOW_PRIORITY
+        *                        more of IGNORE, LOW_PRIORITY
         * @return Boolean
         */
        function update( $table, $values, $conds, $fname = 'DatabaseBase::update', $options = array() ) {
@@ -1370,11 +1328,11 @@ abstract class DatabaseBase implements DatabaseType {
        /**
         * Makes an encoded list of strings from an array
         * $mode:
-        *                LIST_COMMA             - comma separated, no field names
-        *                LIST_AND                       - ANDed WHERE clause (without the WHERE)
-        *                LIST_OR                        - ORed WHERE clause (without the WHERE)
-        *                LIST_SET                       - comma separated with field names, like a SET clause
-        *                LIST_NAMES             - comma separated field names
+        *        LIST_COMMA         - comma separated, no field names
+        *        LIST_AND           - ANDed WHERE clause (without the WHERE)
+        *        LIST_OR            - ORed WHERE clause (without the WHERE)
+        *        LIST_SET           - comma separated with field names, like a SET clause
+        *        LIST_NAMES         - comma separated field names
         */
        function makeList( $a, $mode = LIST_COMMA ) {
                if ( !is_array( $a ) ) {
@@ -1482,7 +1440,7 @@ abstract class DatabaseBase implements DatabaseType {
         * @return bool Success or failure
         */
        function selectDB( $db ) {
-               # Stub.  Shouldn't cause serious problems if it's not overridden, but
+               # Stub.  Shouldn't cause serious problems if it's not overridden, but
                # if your database engine supports a concept similar to MySQL's
                # databases you may as well.
                return true;
@@ -1562,7 +1520,7 @@ abstract class DatabaseBase implements DatabaseType {
                 && is_array( $wgSharedTables )
                 && in_array( $table, $wgSharedTables ) ) { # A shared table is selected
                        $database = $wgSharedDB;
-                       $prefix   = isset( $wgSharedPrefix ) ? $wgSharedPrefix : $prefix;
+                       $prefix   = isset( $wgSharedPrefix ) ? $wgSharedPrefix : $prefix;
                }
 
                # Quote the $database and $table and apply the prefix if not quoted.
@@ -1584,7 +1542,7 @@ abstract class DatabaseBase implements DatabaseType {
         * Example:
         * extract($dbr->tableNames('user','watchlist'));
         * $sql = "SELECT wl_namespace,wl_title FROM $watchlist,$user
-        *                 WHERE wl_user=user_id AND wl_user=$nameWithQuotes";
+        *         WHERE wl_user=user_id AND wl_user=$nameWithQuotes";
         */
        public function tableNames() {
                $inArray = func_get_args();
@@ -1604,7 +1562,7 @@ abstract class DatabaseBase implements DatabaseType {
         * Example:
         * list( $user, $watchlist ) = $dbr->tableNamesN('user','watchlist');
         * $sql = "SELECT wl_namespace,wl_title FROM $watchlist,$user
-        *                 WHERE wl_user=user_id AND wl_user=$nameWithQuotes";
+        *         WHERE wl_user=user_id AND wl_user=$nameWithQuotes";
         */
        public function tableNamesN() {
                $inArray = func_get_args();
@@ -1617,6 +1575,39 @@ abstract class DatabaseBase implements DatabaseType {
                return $retVal;
        }
 
+       /**
+        * Get an aliased table name
+        * e.g. tableName AS newTableName
+        *
+        * @param $name string Table name, see tableName()
+        * @param $alias string Alias (optional)
+        * @return string SQL name for aliased table. Will not alias a table to its own name
+        */
+       public function tableNameWithAlias( $name, $alias = false ) {
+               if ( !$alias || $alias == $name ) {
+                       return $this->tableName( $name );
+               } else {
+                       return $this->tableName( $name ) . ' `' . $alias . '`';
+               }
+       }
+
+       /**
+        * Gets an array of aliased table names
+        *
+        * @param $tables array( [alias] => table )
+        * @return array of strings, see tableNameWithAlias()
+        */
+       public function tableNamesWithAlias( $tables ) {
+               $retval = array();
+               foreach ( $tables as $alias => $table ) {
+                       if ( is_numeric( $alias ) ) {
+                               $alias = $table;
+                       }
+                       $retval[] = $this->tableNameWithAlias( $table, $alias );
+               }
+               return $retval;
+       }
+
        /**
         * @private
         */
@@ -1626,35 +1617,37 @@ abstract class DatabaseBase implements DatabaseType {
                $use_index_safe = is_array( $use_index ) ? $use_index : array();
                $join_conds_safe = is_array( $join_conds ) ? $join_conds : array();
 
-               foreach ( $tables as $table ) {
+               foreach ( $tables as $alias => $table ) {
+                       if ( !is_string( $alias ) ) {
+                               // No alias? Set it equal to the table name
+                               $alias = $table;
+                       }
                        // Is there a JOIN and INDEX clause for this table?
-                       if ( isset( $join_conds_safe[$table] ) && isset( $use_index_safe[$table] ) ) {
-                               $tableClause = $join_conds_safe[$table][0] . ' ' . $this->tableName( $table );
-                               $tableClause .= ' ' . $this->useIndexClause( implode( ',', (array)$use_index_safe[$table] ) );
-                               $on = $this->makeList( (array)$join_conds_safe[$table][1], LIST_AND );
-
+                       if ( isset( $join_conds_safe[$alias] ) && isset( $use_index_safe[$alias] ) ) {
+                               $tableClause = $join_conds_safe[$alias][0] . ' ' . $this->tableNameWithAlias( $table, $alias );
+                               $tableClause .= ' ' . $this->useIndexClause( implode( ',', (array)$use_index_safe[$alias] ) );
+                               $on = $this->makeList( (array)$join_conds_safe[$alias][1], LIST_AND );
                                if ( $on != '' ) {
                                        $tableClause .= ' ON (' . $on . ')';
                                }
 
                                $retJOIN[] = $tableClause;
                        // Is there an INDEX clause?
-                       } else if ( isset( $use_index_safe[$table] ) ) {
-                               $tableClause = $this->tableName( $table );
-                               $tableClause .= ' ' . $this->useIndexClause( implode( ',', (array)$use_index_safe[$table] ) );
+                       } else if ( isset( $use_index_safe[$alias] ) ) {
+                               $tableClause = $this->tableNameWithAlias( $table, $alias );
+                               $tableClause .= ' ' . $this->useIndexClause( implode( ',', (array)$use_index_safe[$alias] ) );
                                $ret[] = $tableClause;
                        // Is there a JOIN clause?
-                       } else if ( isset( $join_conds_safe[$table] ) ) {
-                               $tableClause = $join_conds_safe[$table][0] . ' ' . $this->tableName( $table );
-                               $on = $this->makeList( (array)$join_conds_safe[$table][1], LIST_AND );
-
+                       } else if ( isset( $join_conds_safe[$alias] ) ) {
+                               $tableClause = $join_conds_safe[$alias][0] . ' ' . $this->tableNameWithAlias( $table, $alias );
+                               $on = $this->makeList( (array)$join_conds_safe[$alias][1], LIST_AND );
                                if ( $on != '' ) {
                                        $tableClause .= ' ON (' . $on . ')';
                                }
 
                                $retJOIN[] = $tableClause;
                        } else {
-                               $tableClause = $this->tableName( $table );
+                               $tableClause = $this->tableNameWithAlias( $table, $alias );
                                $ret[] = $tableClause;
                        }
                }
@@ -1701,6 +1694,26 @@ abstract class DatabaseBase implements DatabaseType {
                }
        }
 
+       /**
+        * Quotes a string using `backticks` for things like database, table, and field
+        * names, other databases which use something other than backticks can replace
+        * this with something else
+        */      
+       public function addIdentifierQuotes( $s ) {
+               return "`" . $this->strencode( $s ) . "`";
+       }
+
+       /**
+        * Backwards compatibility, identifier quoting originated in DatabasePostgres
+        * which used quote_ident which does not follow our naming conventions
+        * was renamed to addIdentifierQuotes.
+        * @deprecated use addIdentifierQuotes
+        */
+       function quote_ident( $s ) {
+               wfDeprecated( __METHOD__ );
+               return $this->addIdentifierQuotes( $s );
+       }
+
        /**
         * Escape string for safe LIKE usage.
         * WARNING: you should almost never use this function directly,
@@ -1776,10 +1789,10 @@ abstract class DatabaseBase implements DatabaseType {
        }
 
        /**
-        * USE INDEX clause.  Unlikely to be useful for anything but MySQL.      This
+        * USE INDEX clause.  Unlikely to be useful for anything but MySQL.  This
         * is only needed because a) MySQL must be as efficient as possible due to
         * its use on Wikipedia, and b) MySQL 4.0 is kind of dumb sometimes about
-        * which index to pick.  Anyway, other databases might have different
+        * 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.
         */
@@ -1933,7 +1946,7 @@ abstract class DatabaseBase implements DatabaseType {
                list( $startOpts, $useIndex, $tailOpts ) = $this->makeSelectOptions( $selectOptions );
 
                if ( is_array( $srcTable ) ) {
-                       $srcTable =      implode( ',', array_map( array( &$this, 'tableName' ), $srcTable ) );
+                       $srcTable =  implode( ',', array_map( array( &$this, 'tableName' ), $srcTable ) );
                } else {
                        $srcTable = $this->tableName( $srcTable );
                }
@@ -1959,7 +1972,7 @@ abstract class DatabaseBase implements DatabaseType {
         * If the result of the query is not ordered, then the rows to be returned
         * are theoretically arbitrary.
         *
-        * $sql is expected to be a SELECT, if that makes a difference.  For
+        * $sql is expected to be a SELECT, if that makes a difference.  For
         * UPDATE, limitResultForUpdate should be used.
         *
         * The version provided by default works in MySQL and SQLite.  It will very
@@ -2006,7 +2019,7 @@ abstract class DatabaseBase implements DatabaseType {
        }
 
        /**
-        * Returns an SQL expression for a simple conditional.  This doesn't need
+        * Returns an SQL expression for a simple conditional.  This doesn't need
         * to be overridden unless CASE isn't supported in your DBMS.
         *
         * @param $cond String: SQL expression which will result in a boolean value
@@ -2030,6 +2043,16 @@ abstract class DatabaseBase implements DatabaseType {
                return "REPLACE({$orig}, {$old}, {$new})";
        }
 
+       /**
+        * Convert a field to an unix timestamp
+        *
+        * @param $field String: field name
+        * @return String: SQL statement
+        */
+       public function unixTimestamp( $field ) {
+               return "EXTRACT(epoch FROM $field)";
+       }
+
        /**
         * Determines if the last failure was due to a deadlock
         * STUB
@@ -2063,7 +2086,7 @@ abstract class DatabaseBase implements DatabaseType {
         * will be rolled back and the callback function will be called again.
         *
         * Usage:
-        *       $dbw->deadlockLoop( callback, ... );
+        *   $dbw->deadlockLoop( callback, ... );
         *
         * Extra arguments are passed through to the specified callback function.
         *
@@ -2312,7 +2335,7 @@ abstract class DatabaseBase implements DatabaseType {
         * @return bool Success or failure
         */
        function ping() {
-               # Stub.  Not essential to override.
+               # Stub.  Not essential to override.
                return true;
        }
 
@@ -2332,7 +2355,7 @@ abstract class DatabaseBase implements DatabaseType {
                $res = $this->query( "SHOW STATUS LIKE '{$which}'" );
                $status = array();
 
-               while ( $row = $this->fetchObject( $res ) ) {
+               foreach ( $res as $row ) {
                        $status[$row->Variable_name] = $row->Value;
                }
 
@@ -2355,9 +2378,9 @@ abstract class DatabaseBase implements DatabaseType {
        }
 
        /**
-        * Override database's default connection timeout.      May be useful for very
+        * 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
+        * 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.
         *
         * @param $timeout Integer in seconds
@@ -2371,7 +2394,7 @@ 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 or false if name should be generated dynamically
-        *              using $filename
+        *              using $filename
         */
        function sourceFile( $filename, $lineCallback = false, $resultCallback = false, $fname = false ) {
                $fp = fopen( $filename, 'r' );
@@ -2412,11 +2435,12 @@ abstract class DatabaseBase implements DatabaseType {
         * @param $patch String The name of the patch, like patch-something.sql
         * @return String Full path to patch file
         */
-       public static function patchPath( $patch ) {
-               global $wgDBtype, $IP;
+       public function patchPath( $patch ) {
+               global $IP;
 
-               if ( file_exists( "$IP/maintenance/$wgDBtype/archives/$patch" ) ) {
-                       return "$IP/maintenance/$wgDBtype/archives/$patch";
+               $dbType = $this->getType();
+               if ( file_exists( "$IP/maintenance/$dbType/archives/$patch" ) ) {
+                       return "$IP/maintenance/$dbType/archives/$patch";
                } else {
                        return "$IP/maintenance/archives/$patch";
                }
@@ -2496,6 +2520,32 @@ abstract class DatabaseBase implements DatabaseType {
                return true;
        }
 
+       /**
+        * Database independent variable replacement, replaces a set of named variables
+        * in a sql statement with the contents of their global variables.
+        * Supports '{$var}' `{$var}` and / *$var* / (without the spaces) style variables
+        * 
+        * '{$var}' should be used for text and is passed through the database's addQuotes method
+        * `{$var}` should be used for identifiers (eg: table and database names), it is passed through
+        *          the database's addIdentifierQuotes method which can be overridden if the database
+        *          uses something other than backticks.
+        * / *$var* / is just encoded, besides traditional dbprefix and tableoptions it's use should be avoided
+        * 
+        * @param $ins String: SQL statement to replace variables in
+        * @param $varnames Array: Array of global variable names to replace
+        * @return String The new SQL statement with variables replaced
+        */
+       protected function replaceGlobalVars( $ins, $varnames ) {
+               foreach ( $varnames as $var ) {
+                       if ( isset( $GLOBALS[$var] ) ) {
+                               $ins = str_replace( '\'{$' . $var . '}\'', $this->addQuotes( $GLOBALS[$var] ), $ins ); // replace '{$var}'
+                               $ins = str_replace( '`{$' . $var . '}`', $this->addIdentifierQuotes( $GLOBALS[$var] ), $ins ); // replace `{$var}`
+                               $ins = str_replace( '/*$' . $var . '*/', $this->strencode( $GLOBALS[$var] ) , $ins ); // replace /*$var*/
+                       }
+               }
+               return $ins;
+       }
+
        /**
         * Replace variables in sourced SQL
         */
@@ -2506,15 +2556,7 @@ abstract class DatabaseBase implements DatabaseType {
                        'wgDBadminuser', 'wgDBadminpassword', 'wgDBTableOptions',
                );
 
-               // Ordinary variables
-               foreach ( $varnames as $var ) {
-                       if ( isset( $GLOBALS[$var] ) ) {
-                               $val = addslashes( $GLOBALS[$var] ); // FIXME: safety check?
-                               $ins = str_replace( '{$' . $var . '}', $val, $ins );
-                               $ins = str_replace( '/*$' . $var . '*/`', '`' . $val, $ins );
-                               $ins = str_replace( '/*$' . $var . '*/', $val, $ins );
-                       }
-               }
+               $ins = $this->replaceGlobalVars( $ins, $varnames );
 
                // Table prefixes
                $ins = preg_replace_callback( '!/\*(?:\$wgDBprefix|_)\*/([a-zA-Z_0-9]*)!',
@@ -2667,57 +2709,33 @@ class Blob {
 }
 
 /**
- * Utility class.
+ * Base for all database-specific classes representing information about database fields
  * @ingroup Database
  */
-class MySQLField {
-       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 defaultValue() {
-               return $this->default;
-       }
-
-       function maxLength() {
-               return $this->max_length;
-       }
-
-       function nullable() {
-               return $this->nullable;
-       }
+interface Field {
+       /**
+        * Field name
+        * @return string
+        */
+       function name();
 
-       function isKey() {
-               return $this->is_key;
-       }
+       /**
+        * Name of table this field belongs to
+        * @return string
+        */
+       function tableName();
 
-       function isMultipleKey() {
-               return $this->is_multiple;
-       }
+       /**
+        * Database type
+        * @return string
+        */
+       function type();
 
-       function type() {
-               return $this->type;
-       }
+       /**
+        * Whether this field can store NULL values
+        * @return bool
+        */
+       function isNullable();
 }
 
 /******************************************************************************
@@ -2860,7 +2878,7 @@ class DBConnectionError extends DBError {
        }
 
        function searchForm() {
-               global $wgSitename, $wgServer, $wgLang, $wgInputEncoding;
+               global $wgSitename, $wgServer, $wgLang;
 
                $usegoogle = "You can try searching via Google in the meantime.";
                $outofdate = "Note that their indexes of our content may be out of date.";
@@ -2874,20 +2892,23 @@ class DBConnectionError extends DBError {
 
                $search = htmlspecialchars( @$_REQUEST['search'] );
 
+               $server = htmlspecialchars( $wgServer );
+               $sitename = htmlspecialchars( $wgSitename );
+
                $trygoogle = <<<EOT
 <div style="margin: 1.5em">$usegoogle<br />
 <small>$outofdate</small></div>
 <!-- SiteSearch Google -->
 <form method="get" action="http://www.google.com/search" id="googlesearch">
-       <input type="hidden" name="domains" value="$wgServer" />
+       <input type="hidden" name="domains" value="$server" />
        <input type="hidden" name="num" value="50" />
-       <input type="hidden" name="ie" value="$wgInputEncoding" />
-       <input type="hidden" name="oe" value="$wgInputEncoding" />
+       <input type="hidden" name="ie" value="UTF-8" />
+       <input type="hidden" name="oe" value="UTF-8" />
 
        <input type="text" name="q" size="31" maxlength="255" value="$search" />
        <input type="submit" name="btnG" value="$googlesearch" />
   <div>
-       <input type="radio" name="sitesearch" id="gwiki" value="$wgServer" checked="checked" /><label for="gwiki">$wgSitename</label>
+       <input type="radio" name="sitesearch" id="gwiki" value="$server" checked="checked" /><label for="gwiki">$sitename</label>
        <input type="radio" name="sitesearch" id="gWWW" value="" /><label for="gWWW">WWW</label>
   </div>
 </form>
@@ -2906,7 +2927,7 @@ EOT;
                $mainpage = 'Main Page';
 
                if ( $wgLang instanceof Language ) {
-                       $mainpage        = htmlspecialchars( $wgLang->getMessage( 'mainpage' ) );
+                       $mainpage    = htmlspecialchars( $wgLang->getMessage( 'mainpage' ) );
                }
 
                if ( $wgTitle ) {
@@ -2937,7 +2958,7 @@ class DBQueryError extends DBError {
        public $error, $errno, $sql, $fname;
 
        function __construct( DatabaseBase &$db, $error, $errno, $sql, $fname ) {
-               $message = "A database error has occurred.      Did you forget to run maintenance/update.php after upgrading?  See: http://www.mediawiki.org/wiki/Manual:Upgrading#Run_the_update_script\n" .
+               $message = "A database error has occurred.  Did you forget to run maintenance/update.php after upgrading?  See: http://www.mediawiki.org/wiki/Manual:Upgrading#Run_the_update_script\n" .
                  "Query: $sql\n" .
                  "Function: $fname\n" .
                  "Error: $errno $error\n";
@@ -3118,9 +3139,9 @@ class ResultWrapper implements Iterator {
  * doesn't go anywhere near an actual database.
  */
 class FakeResultWrapper extends ResultWrapper {
-       var $result             = array();
-       var $db                 = null; // And it's going to stay that way :D
-       var $pos                = 0;
+       var $result     = array();
+       var $db         = null; // And it's going to stay that way :D
+       var $pos        = 0;
        var $currentRow = null;
 
        function __construct( $array ) {