X-Git-Url: https://git.cyclocoop.org/%27.WWW_URL.%27admin/?a=blobdiff_plain;f=includes%2Fdb%2FDatabase.php;h=e68a8f25f204ef7ce28e0c57bd5eccbabfdc2eb3;hb=272f45a8a0a682d903bc521c04cbf0f4bc1e099e;hp=ced73790847dd1653e0ae031747b3e3877781451;hpb=c24cbb13dfa35274adc1a6ab11a5ebf38fe5c7d9;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/db/Database.php b/includes/db/Database.php index ced7379084..e68a8f25f2 100644 --- a/includes/db/Database.php +++ b/includes/db/Database.php @@ -1,5 +1,4 @@ srvCache = ObjectCache::getLocalServerInstance( 'hash' ); + + $server = $params['host']; + $user = $params['user']; + $password = $params['password']; + $dbName = $params['dbname']; + $flags = $params['flags']; + $tablePrefix = $params['tablePrefix']; + $schema = $params['schema']; + $foreign = $params['foreign']; + + $this->cliMode = isset( $params['cliMode'] ) + ? $params['cliMode'] + : ( PHP_SAPI === 'cli' ); + + $this->mFlags = $flags; + if ( $this->mFlags & DBO_DEFAULT ) { + if ( $this->cliMode ) { + $this->mFlags &= ~DBO_TRX; + } else { + $this->mFlags |= DBO_TRX; + } + } + + $this->mSessionVars = $params['variables']; + + /** Get the default table prefix*/ + if ( $tablePrefix === 'get from global' ) { + $this->mTablePrefix = $wgDBprefix; + } else { + $this->mTablePrefix = $tablePrefix; + } + + /** Get the database schema*/ + if ( $schema === 'get from global' ) { + $this->mSchema = $wgDBmwschema; + } else { + $this->mSchema = $schema; + } + + $this->mForeign = $foreign; + + $this->profiler = isset( $params['profiler'] ) + ? $params['profiler'] + : Profiler::instance(); // @TODO: remove global state + $this->trxProfiler = isset( $params['trxProfiler'] ) + ? $params['trxProfiler'] + : new TransactionProfiler(); + $this->connLogger = isset( $params['connLogger'] ) + ? $params['connLogger'] + : new \Psr\Log\NullLogger(); + $this->queryLogger = isset( $params['queryLogger'] ) + ? $params['queryLogger'] + : new \Psr\Log\NullLogger(); + + if ( $user ) { + $this->open( $server, $user, $password, $dbName ); + } + } + + /** + * Given a DB type, construct the name of the appropriate child class of + * IDatabase. This is designed to replace all of the manual stuff like: + * $class = 'Database' . ucfirst( strtolower( $dbType ) ); + * as well as validate against the canonical list of DB types we have + * + * This factory function is mostly useful for when you need to connect to a + * database other than the MediaWiki default (such as for external auth, + * an extension, et cetera). Do not use this to connect to the MediaWiki + * database. Example uses in core: + * @see LoadBalancer::reallyOpenConnection() + * @see ForeignDBRepo::getMasterDB() + * @see WebInstallerDBConnect::execute() + * + * @since 1.18 + * + * @param string $dbType A possible DB type + * @param array $p An array of options to pass to the constructor. + * Valid options are: host, user, password, dbname, flags, tablePrefix, schema, driver + * @return IDatabase|null If the database driver or extension cannot be found + * @throws MWException + */ + final public static function factory( $dbType, $p = [] ) { + global $wgCommandLineMode; + + $canonicalDBTypes = [ + 'mysql' => [ 'mysqli', 'mysql' ], + 'postgres' => [], + 'sqlite' => [], + 'oracle' => [], + 'mssql' => [], + ]; + + $driver = false; + $dbType = strtolower( $dbType ); + if ( isset( $canonicalDBTypes[$dbType] ) && $canonicalDBTypes[$dbType] ) { + $possibleDrivers = $canonicalDBTypes[$dbType]; + if ( !empty( $p['driver'] ) ) { + if ( in_array( $p['driver'], $possibleDrivers ) ) { + $driver = $p['driver']; + } else { + throw new InvalidArgumentException( __METHOD__ . + " type '$dbType' does not support driver '{$p['driver']}'" ); + } + } else { + foreach ( $possibleDrivers as $posDriver ) { + if ( extension_loaded( $posDriver ) ) { + $driver = $posDriver; + break; + } + } + } + } else { + $driver = $dbType; + } + if ( $driver === false ) { + throw new MWException( __METHOD__ . + " no viable database extension found for type '$dbType'" ); + } + + // Determine schema defaults. Currently Microsoft SQL Server uses $wgDBmwschema, + // and everything else doesn't use a schema (e.g. null) + // Although postgres and oracle support schemas, we don't use them (yet) + // to maintain backwards compatibility + $defaultSchemas = [ + 'mssql' => 'get from global', + ]; + + $class = 'Database' . ucfirst( $driver ); + if ( class_exists( $class ) && is_subclass_of( $class, 'IDatabase' ) ) { + // Resolve some defaults for b/c + $p['host'] = isset( $p['host'] ) ? $p['host'] : false; + $p['user'] = isset( $p['user'] ) ? $p['user'] : false; + $p['password'] = isset( $p['password'] ) ? $p['password'] : false; + $p['dbname'] = isset( $p['dbname'] ) ? $p['dbname'] : false; + $p['flags'] = isset( $p['flags'] ) ? $p['flags'] : 0; + $p['variables'] = isset( $p['variables'] ) ? $p['variables'] : []; + $p['tablePrefix'] = isset( $p['tablePrefix'] ) ? $p['tablePrefix'] : 'get from global'; + if ( !isset( $p['schema'] ) ) { + $p['schema'] = isset( $defaultSchemas[$dbType] ) ? $defaultSchemas[$dbType] : null; + } + $p['foreign'] = isset( $p['foreign'] ) ? $p['foreign'] : false; + $p['cliMode'] = $wgCommandLineMode; + + $conn = new $class( $p ); + if ( isset( $p['connLogger'] ) ) { + $conn->connLogger = $p['connLogger']; + } + if ( isset( $p['queryLogger'] ) ) { + $conn->queryLogger = $p['queryLogger']; + } + } else { + $conn = null; + } + + return $conn; + } + + public function setLogger( LoggerInterface $logger ) { + $this->queryLogger = $logger; + } + public function getServerInfo() { return $this->getServerVersion(); } @@ -312,12 +494,6 @@ abstract class DatabaseBase implements IDatabase { } } - /** - * Set a lazy-connecting DB handle to the master DB (for replication status purposes) - * - * @param IDatabase $conn - * @since 1.27 - */ public function setLazyMasterHandle( IDatabase $conn ) { $this->lazyMasterHandle = $conn; } @@ -331,13 +507,6 @@ abstract class DatabaseBase implements IDatabase { return $this->lazyMasterHandle; } - /** - * @return TransactionProfiler - */ - protected function getTransactionProfiler() { - return $this->trxProfiler; - } - /** * @param TransactionProfiler $profiler * @since 1.27 @@ -559,176 +728,16 @@ abstract class DatabaseBase implements IDatabase { */ abstract function strencode( $s ); - /** - * Constructor. - * - * FIXME: It is possible to construct a Database object with no associated - * connection object, by specifying no parameters to __construct(). This - * feature is deprecated and should be removed. - * - * DatabaseBase subclasses should not be constructed directly in external - * code. DatabaseBase::factory() should be used instead. - * - * @param array $params Parameters passed from DatabaseBase::factory() - */ - function __construct( array $params ) { - global $wgDBprefix, $wgDBmwschema; - - $this->srvCache = ObjectCache::getLocalServerInstance( 'hash' ); - - $server = $params['host']; - $user = $params['user']; - $password = $params['password']; - $dbName = $params['dbname']; - $flags = $params['flags']; - $tablePrefix = $params['tablePrefix']; - $schema = $params['schema']; - $foreign = $params['foreign']; - - $this->cliMode = isset( $params['cliMode'] ) - ? $params['cliMode'] - : ( PHP_SAPI === 'cli' ); - - $this->mFlags = $flags; - if ( $this->mFlags & DBO_DEFAULT ) { - if ( $this->cliMode ) { - $this->mFlags &= ~DBO_TRX; - } else { - $this->mFlags |= DBO_TRX; - } - } - - $this->mSessionVars = $params['variables']; - - /** Get the default table prefix*/ - if ( $tablePrefix === 'get from global' ) { - $this->mTablePrefix = $wgDBprefix; - } else { - $this->mTablePrefix = $tablePrefix; - } - - /** Get the database schema*/ - if ( $schema === 'get from global' ) { - $this->mSchema = $wgDBmwschema; - } else { - $this->mSchema = $schema; - } - - $this->mForeign = $foreign; - - $this->profiler = isset( $params['profiler'] ) - ? $params['profiler'] - : Profiler::instance(); // @TODO: remove global state - $this->trxProfiler = isset( $params['trxProfiler'] ) - ? $params['trxProfiler'] - : new TransactionProfiler(); - - if ( $user ) { - $this->open( $server, $user, $password, $dbName ); - } - - } - /** * 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 __sleep() { - throw new MWException( 'Database serialization may cause problems, since ' . + throw new RuntimeException( 'Database serialization may cause problems, since ' . 'the connection is not restored on wakeup.' ); } - /** - * 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: - * $class = 'Database' . ucfirst( strtolower( $dbType ) ); - * as well as validate against the canonical list of DB types we have - * - * This factory function is mostly useful for when you need to connect to a - * database other than the MediaWiki default (such as for external auth, - * an extension, et cetera). Do not use this to connect to the MediaWiki - * database. Example uses in core: - * @see LoadBalancer::reallyOpenConnection() - * @see ForeignDBRepo::getMasterDB() - * @see WebInstallerDBConnect::execute() - * - * @since 1.18 - * - * @param string $dbType A possible DB type - * @param array $p An array of options to pass to the constructor. - * Valid options are: host, user, password, dbname, flags, tablePrefix, schema, driver - * @throws MWException If the database driver or extension cannot be found - * @return DatabaseBase|null DatabaseBase subclass or null - */ - final public static function factory( $dbType, $p = [] ) { - global $wgCommandLineMode; - - $canonicalDBTypes = [ - 'mysql' => [ 'mysqli', 'mysql' ], - 'postgres' => [], - 'sqlite' => [], - 'oracle' => [], - 'mssql' => [], - ]; - - $driver = false; - $dbType = strtolower( $dbType ); - if ( isset( $canonicalDBTypes[$dbType] ) && $canonicalDBTypes[$dbType] ) { - $possibleDrivers = $canonicalDBTypes[$dbType]; - if ( !empty( $p['driver'] ) ) { - if ( in_array( $p['driver'], $possibleDrivers ) ) { - $driver = $p['driver']; - } else { - throw new MWException( __METHOD__ . - " cannot construct Database with type '$dbType' and driver '{$p['driver']}'" ); - } - } else { - foreach ( $possibleDrivers as $posDriver ) { - if ( extension_loaded( $posDriver ) ) { - $driver = $posDriver; - break; - } - } - } - } else { - $driver = $dbType; - } - if ( $driver === false ) { - throw new MWException( __METHOD__ . - " no viable database extension found for type '$dbType'" ); - } - - // Determine schema defaults. Currently Microsoft SQL Server uses $wgDBmwschema, - // and everything else doesn't use a schema (e.g. null) - // Although postgres and oracle support schemas, we don't use them (yet) - // to maintain backwards compatibility - $defaultSchemas = [ - 'mssql' => 'get from global', - ]; - - $class = 'Database' . ucfirst( $driver ); - if ( class_exists( $class ) && is_subclass_of( $class, 'DatabaseBase' ) ) { - // Resolve some defaults for b/c - $p['host'] = isset( $p['host'] ) ? $p['host'] : false; - $p['user'] = isset( $p['user'] ) ? $p['user'] : false; - $p['password'] = isset( $p['password'] ) ? $p['password'] : false; - $p['dbname'] = isset( $p['dbname'] ) ? $p['dbname'] : false; - $p['flags'] = isset( $p['flags'] ) ? $p['flags'] : 0; - $p['variables'] = isset( $p['variables'] ) ? $p['variables'] : []; - $p['tablePrefix'] = isset( $p['tablePrefix'] ) ? $p['tablePrefix'] : 'get from global'; - if ( !isset( $p['schema'] ) ) { - $p['schema'] = isset( $defaultSchemas[$dbType] ) ? $defaultSchemas[$dbType] : null; - } - $p['foreign'] = isset( $p['foreign'] ) ? $p['foreign'] : false; - $p['cliMode'] = $wgCommandLineMode; - - return new $class( $p ); - } else { - return null; - } - } - protected function installErrorHandler() { $this->mPHPError = false; $this->htmlErrors = ini_set( 'html_errors', '0' ); @@ -792,7 +801,7 @@ abstract class DatabaseBase implements IDatabase { $closed = $this->closeConnection(); $this->mConn = false; } elseif ( $this->mTrxIdleCallbacks || $this->mTrxEndCallbacks ) { // sanity - throw new MWException( "Transaction callbacks still pending." ); + throw new RuntimeException( "Transaction callbacks still pending." ); } else { $closed = true; } @@ -914,12 +923,12 @@ abstract class DatabaseBase implements IDatabase { # Keep track of whether the transaction has write queries pending if ( $this->mTrxLevel && !$this->mTrxDoneWrites && $isWrite ) { $this->mTrxDoneWrites = true; - $this->getTransactionProfiler()->transactionWritingIn( + $this->trxProfiler->transactionWritingIn( $this->mServer, $this->mDBname, $this->mTrxShortId ); } if ( $this->debug() ) { - wfDebugLog( 'queries', sprintf( "%s: %s", $this->mDBname, $commentedSql ) ); + $this->queryLogger->debug( "{$this->mDBname} {$commentedSql}" ); } # Avoid fatals if close() was called @@ -936,11 +945,10 @@ abstract class DatabaseBase implements IDatabase { $lastErrno = $this->lastErrno(); # Update state tracking to reflect transaction loss due to disconnection $this->handleTransactionLoss(); - wfDebug( "Connection lost, reconnecting...\n" ); if ( $this->reconnect() ) { - wfDebug( "Reconnected\n" ); $msg = __METHOD__ . ": lost connection to {$this->getServer()}; reconnected"; - wfDebugLog( 'DBPerformance', "$msg:\n" . wfBacktrace( true ) ); + $this->connLogger->warning( $msg ); + $this->queryLogger->warning( "$msg:\n" . wfBacktrace( true ) ); if ( !$recoverable ) { # Callers may catch the exception and continue to use the DB @@ -950,7 +958,8 @@ abstract class DatabaseBase implements IDatabase { $ret = $this->doProfiledQuery( $sql, $commentedSql, $isWrite, $fname ); } } else { - wfDebug( "Failed\n" ); + $msg = __METHOD__ . ": lost connection to {$this->getServer()} permanently"; + $this->connLogger->error( $msg ); } } @@ -980,9 +989,9 @@ abstract class DatabaseBase implements IDatabase { # generalizeSQL() will probably cut down the query to reasonable # logging size most of the time. The substr is really just a sanity check. if ( $isMaster ) { - $queryProf = 'query-m: ' . substr( DatabaseBase::generalizeSQL( $sql ), 0, 255 ); + $queryProf = 'query-m: ' . substr( self::generalizeSQL( $sql ), 0, 255 ); } else { - $queryProf = 'query: ' . substr( DatabaseBase::generalizeSQL( $sql ), 0, 255 ); + $queryProf = 'query: ' . substr( self::generalizeSQL( $sql ), 0, 255 ); } # Include query transaction state @@ -1008,7 +1017,7 @@ abstract class DatabaseBase implements IDatabase { $this->mRTTEstimate = $queryRuntime; } - $this->getTransactionProfiler()->recordQueryCompletion( + $this->trxProfiler->recordQueryCompletion( $queryProf, $startTime, $isWrite, $this->affectedRows() ); MWDebug::query( $sql, $fname, $isMaster, $queryRuntime ); @@ -1085,7 +1094,7 @@ abstract class DatabaseBase implements IDatabase { public function reportQueryError( $error, $errno, $sql, $fname, $tempIgnore = false ) { if ( $this->ignoreErrors() || $tempIgnore ) { - wfDebug( "SQL ERROR (ignored): $error\n" ); + $this->queryLogger->debug( "SQL ERROR (ignored): $error\n" ); } else { $sql1line = mb_substr( str_replace( "\n", "\\n", $sql ), 0, 5 * 1024 ); wfLogDBError( @@ -1098,7 +1107,7 @@ abstract class DatabaseBase implements IDatabase { 'fname' => $fname, ] ) ); - wfDebug( "SQL ERROR: " . $error . "\n" ); + $this->queryLogger->debug( "SQL ERROR: " . $error . "\n" ); throw new DBQueryError( $this, $error, $errno, $sql, $fname ); } } @@ -1117,7 +1126,7 @@ abstract class DatabaseBase implements IDatabase { * * @return array */ - protected function prepare( $sql, $func = 'DatabaseBase::prepare' ) { + protected function prepare( $sql, $func = __METHOD__ ) { /* MySQL doesn't support prepared statements (yet), so just * pack up the query for reference. We'll manually replace * the bits later. @@ -1344,8 +1353,13 @@ abstract class DatabaseBase implements IDatabase { } else { $useIndex = ''; } + if ( isset( $options['IGNORE INDEX'] ) && is_string( $options['IGNORE INDEX'] ) ) { + $ignoreIndex = $this->ignoreIndexClause( $options['IGNORE INDEX'] ); + } else { + $ignoreIndex = ''; + } - return [ $startOpts, $useIndex, $preLimitTail, $postLimitTail ]; + return [ $startOpts, $useIndex, $preLimitTail, $postLimitTail, $ignoreIndex ]; } /** @@ -1413,31 +1427,34 @@ abstract class DatabaseBase implements IDatabase { $useIndexes = ( isset( $options['USE INDEX'] ) && is_array( $options['USE INDEX'] ) ) ? $options['USE INDEX'] : []; + $ignoreIndexes = ( isset( $options['IGNORE INDEX'] ) && is_array( $options['IGNORE INDEX'] ) ) + ? $options['IGNORE INDEX'] + : []; if ( is_array( $table ) ) { $from = ' FROM ' . - $this->tableNamesWithUseIndexOrJOIN( $table, $useIndexes, $join_conds ); + $this->tableNamesWithIndexClauseOrJOIN( $table, $useIndexes, $ignoreIndexes, $join_conds ); } elseif ( $table != '' ) { if ( $table[0] == ' ' ) { $from = ' FROM ' . $table; } else { $from = ' FROM ' . - $this->tableNamesWithUseIndexOrJOIN( [ $table ], $useIndexes, [] ); + $this->tableNamesWithIndexClauseOrJOIN( [ $table ], $useIndexes, $ignoreIndexes, [] ); } } else { $from = ''; } - list( $startOpts, $useIndex, $preLimitTail, $postLimitTail ) = + list( $startOpts, $useIndex, $preLimitTail, $postLimitTail, $ignoreIndex ) = $this->makeSelectOptions( $options ); if ( !empty( $conds ) ) { if ( is_array( $conds ) ) { $conds = $this->makeList( $conds, LIST_AND ); } - $sql = "SELECT $startOpts $vars $from $useIndex WHERE $conds $preLimitTail"; + $sql = "SELECT $startOpts $vars $from $useIndex $ignoreIndex WHERE $conds $preLimitTail"; } else { - $sql = "SELECT $startOpts $vars $from $useIndex $preLimitTail"; + $sql = "SELECT $startOpts $vars $from $useIndex $ignoreIndex $preLimitTail"; } if ( isset( $options['LIMIT'] ) ) { @@ -1682,7 +1699,7 @@ abstract class DatabaseBase implements IDatabase { public function makeList( $a, $mode = LIST_COMMA ) { if ( !is_array( $a ) ) { - throw new DBUnexpectedError( $this, 'DatabaseBase::makeList called with incorrect parameters' ); + throw new DBUnexpectedError( $this, __METHOD__ . ' called with incorrect parameters' ); } $first = true; @@ -1713,7 +1730,7 @@ abstract class DatabaseBase implements IDatabase { unset( $value[$nullKey] ); } if ( count( $value ) == 0 && !$includeNull ) { - throw new MWException( __METHOD__ . ": empty input for field $field" ); + throw new InvalidArgumentException( __METHOD__ . ": empty input for field $field" ); } elseif ( count( $value ) == 0 ) { // only check if $field is null $list .= "$field IS NULL"; @@ -2048,19 +2065,21 @@ abstract class DatabaseBase implements IDatabase { /** * Get the aliased table name clause for a FROM clause - * which might have a JOIN and/or USE INDEX clause + * which might have a JOIN and/or USE INDEX or IGNORE INDEX clause * * @param array $tables ( [alias] => table ) * @param array $use_index Same as for select() + * @param array $ignore_index Same as for select() * @param array $join_conds Same as for select() * @return string */ - protected function tableNamesWithUseIndexOrJOIN( - $tables, $use_index = [], $join_conds = [] + protected function tableNamesWithIndexClauseOrJOIN( + $tables, $use_index = [], $ignore_index = [], $join_conds = [] ) { $ret = []; $retJOIN = []; $use_index = (array)$use_index; + $ignore_index = (array)$ignore_index; $join_conds = (array)$join_conds; foreach ( $tables as $alias => $table ) { @@ -2079,6 +2098,12 @@ abstract class DatabaseBase implements IDatabase { $tableClause .= ' ' . $use; } } + if ( isset( $ignore_index[$alias] ) ) { // has IGNORE INDEX? + $ignore = $this->ignoreIndexClause( implode( ',', (array)$ignore_index[$alias] ) ); + if ( $ignore != '' ) { + $tableClause .= ' ' . $ignore; + } + } $on = $this->makeList( (array)$conds, LIST_AND ); if ( $on != '' ) { $tableClause .= ' ON (' . $on . ')'; @@ -2092,6 +2117,14 @@ abstract class DatabaseBase implements IDatabase { implode( ',', (array)$use_index[$alias] ) ); + $ret[] = $tableClause; + } elseif ( isset( $ignore_index[$alias] ) ) { + // Is there an INDEX clause for this table? + $tableClause = $this->tableNameWithAlias( $table, $alias ); + $tableClause .= ' ' . $this->ignoreIndexClause( + implode( ',', (array)$ignore_index[$alias] ) + ); + $ret[] = $tableClause; } else { $tableClause = $this->tableNameWithAlias( $table, $alias ); @@ -2224,6 +2257,20 @@ abstract class DatabaseBase implements IDatabase { return ''; } + /** + * IGNORE 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 + * indexes on a given table. So don't bother overriding this unless you're + * MySQL. + * @param string $index + * @return string + */ + public function ignoreIndexClause( $index ) { + return ''; + } + public function replace( $table, $uniqueIndexes, $rows, $fname = __METHOD__ ) { $quotedTable = $this->tableName( $table ); @@ -2364,8 +2411,7 @@ abstract class DatabaseBase implements IDatabase { $fname = __METHOD__ ) { if ( !$conds ) { - throw new DBUnexpectedError( $this, - 'DatabaseBase::deleteJoin() called with empty $conds' ); + throw new DBUnexpectedError( $this, __METHOD__ . ' called with empty $conds' ); } $delTable = $this->tableName( $delTable ); @@ -2389,7 +2435,7 @@ abstract class DatabaseBase implements IDatabase { public function textFieldSize( $table, $field ) { $table = $this->tableName( $table ); $sql = "SHOW COLUMNS FROM $table LIKE \"$field\";"; - $res = $this->query( $sql, 'DatabaseBase::textFieldSize' ); + $res = $this->query( $sql, __METHOD__ ); $row = $this->fetchObject( $res ); $m = []; @@ -2417,7 +2463,7 @@ abstract class DatabaseBase implements IDatabase { public function delete( $table, $conds, $fname = __METHOD__ ) { if ( !$conds ) { - throw new DBUnexpectedError( $this, 'DatabaseBase::delete() called with no conditions' ); + throw new DBUnexpectedError( $this, __METHOD__ . ' called with no conditions' ); } $table = $this->tableName( $table ); @@ -2488,7 +2534,8 @@ abstract class DatabaseBase implements IDatabase { $selectOptions = [ $selectOptions ]; } - list( $startOpts, $useIndex, $tailOpts ) = $this->makeSelectOptions( $selectOptions ); + list( $startOpts, $useIndex, $tailOpts, $ignoreIndex ) = $this->makeSelectOptions( + $selectOptions ); if ( is_array( $srcTable ) ) { $srcTable = implode( ',', array_map( [ &$this, 'tableName' ], $srcTable ) ); @@ -2498,7 +2545,7 @@ abstract class DatabaseBase implements IDatabase { $sql = "INSERT $insertOptions INTO $destTable (" . implode( ',', array_keys( $varMap ) ) . ')' . " SELECT $startOpts " . implode( ',', $varMap ) . - " FROM $srcTable $useIndex "; + " FROM $srcTable $useIndex $ignoreIndex "; if ( $conds != '*' ) { if ( is_array( $conds ) ) { @@ -2971,11 +3018,11 @@ abstract class DatabaseBase implements IDatabase { $this->assertOpen(); $this->runOnTransactionPreCommitCallbacks(); - $writeTime = $this->pendingWriteQueryDuration(); + $writeTime = $this->pendingWriteQueryDuration( self::ESTIMATE_DB_APPLY ); $this->doCommit( $fname ); if ( $this->mTrxDoneWrites ) { $this->mDoneWrites = microtime( true ); - $this->getTransactionProfiler()->transactionWritingOut( + $this->trxProfiler->transactionWritingOut( $this->mServer, $this->mDBname, $this->mTrxShortId, $writeTime ); } @@ -3019,7 +3066,7 @@ abstract class DatabaseBase implements IDatabase { $this->doRollback( $fname ); $this->mTrxAtomicLevels = []; if ( $this->mTrxDoneWrites ) { - $this->getTransactionProfiler()->transactionWritingOut( + $this->trxProfiler->transactionWritingOut( $this->mServer, $this->mDBname, $this->mTrxShortId ); } @@ -3078,12 +3125,11 @@ abstract class DatabaseBase implements IDatabase { public function duplicateTableStructure( $oldName, $newName, $temporary = false, $fname = __METHOD__ ) { - throw new MWException( - 'DatabaseBase::duplicateTableStructure is not implemented in descendant class' ); + throw new RuntimeException( __METHOD__ . ' is not implemented in descendant class' ); } function listTables( $prefix = null, $fname = __METHOD__ ) { - throw new MWException( 'DatabaseBase::listTables is not implemented in descendant class' ); + throw new RuntimeException( __METHOD__ . ' is not implemented in descendant class' ); } /** @@ -3107,7 +3153,7 @@ abstract class DatabaseBase implements IDatabase { * @since 1.22 */ public function listViews( $prefix = null, $fname = __METHOD__ ) { - throw new MWException( 'DatabaseBase::listViews is not implemented in descendant class' ); + throw new RuntimeException( __METHOD__ . ' is not implemented in descendant class' ); } /** @@ -3119,7 +3165,7 @@ abstract class DatabaseBase implements IDatabase { * @since 1.22 */ public function isView( $name ) { - throw new MWException( 'DatabaseBase::isView is not implemented in descendant class' ); + throw new RuntimeException( __METHOD__ . ' is not implemented in descendant class' ); } public function timestamp( $ts = 0 ) { @@ -3314,7 +3360,7 @@ abstract class DatabaseBase implements IDatabase { MediaWiki\restoreWarnings(); if ( false === $fp ) { - throw new MWException( "Could not open \"{$filename}\".\n" ); + throw new RuntimeException( "Could not open \"{$filename}\".\n" ); } if ( !$fname ) {