From 53fc101b2227cdfc88303ec72b6034bc6e8256b8 Mon Sep 17 00:00:00 2001 From: "Ariel T. Glenn" Date: Mon, 4 Jul 2016 13:56:40 +0300 Subject: [PATCH] add IGNORE INDEX option to mysql database handler BUG: T29112 Change-Id: I9a5a4f7983ebd37b7889abb8433e5acc44b8f9ac --- includes/db/Database.php | 61 +++++++++++++++++++++++++------ includes/db/DatabaseMssql.php | 2 +- includes/db/DatabaseMysqlBase.php | 8 ++++ includes/db/DatabaseOracle.php | 13 +++++-- includes/db/DatabasePostgres.php | 9 +++-- 5 files changed, 74 insertions(+), 19 deletions(-) diff --git a/includes/db/Database.php b/includes/db/Database.php index 6bdcb24cdb..68df9cf710 100644 --- a/includes/db/Database.php +++ b/includes/db/Database.php @@ -1176,8 +1176,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 ]; } /** @@ -1245,31 +1250,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'] ) ) { @@ -1871,19 +1879,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 ) { @@ -1902,6 +1912,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 . ')'; @@ -1915,6 +1931,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 ); @@ -2047,6 +2071,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 ); @@ -2272,7 +2310,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 ) ); @@ -2282,7 +2321,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 ) ) { diff --git a/includes/db/DatabaseMssql.php b/includes/db/DatabaseMssql.php index 33f81623d9..66c348d526 100644 --- a/includes/db/DatabaseMssql.php +++ b/includes/db/DatabaseMssql.php @@ -1216,7 +1216,7 @@ class DatabaseMssql extends Database { } // we want this to be compatible with the output of parent::makeSelectOptions() - return [ $startOpts, '', $tailOpts, '' ]; + return [ $startOpts, '', $tailOpts, '', '' ]; } /** diff --git a/includes/db/DatabaseMysqlBase.php b/includes/db/DatabaseMysqlBase.php index 3ebc3ecce1..52dee019af 100644 --- a/includes/db/DatabaseMysqlBase.php +++ b/includes/db/DatabaseMysqlBase.php @@ -865,6 +865,14 @@ abstract class DatabaseMysqlBase extends Database { return "FORCE INDEX (" . $this->indexName( $index ) . ")"; } + /** + * @param string $index + * @return string + */ + function ignoreIndexClause( $index ) { + return "IGNORE INDEX (" . $this->indexName( $index ) . ")"; + } + /** * @return string */ diff --git a/includes/db/DatabaseOracle.php b/includes/db/DatabaseOracle.php index f9ba0507d4..c2bf2e7bd1 100644 --- a/includes/db/DatabaseOracle.php +++ b/includes/db/DatabaseOracle.php @@ -739,7 +739,8 @@ class DatabaseOracle extends Database { if ( !is_array( $selectOptions ) ) { $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 ) ); } else { @@ -761,7 +762,7 @@ class DatabaseOracle extends Database { $sql = "INSERT INTO $destTable (" . implode( ',', array_keys( $varMap ) ) . ')' . " SELECT $startOpts " . implode( ',', $varMap ) . - " FROM $srcTable $useIndex "; + " FROM $srcTable $useIndex $ignoreIndex "; if ( $conds != '*' ) { $sql .= ' WHERE ' . $this->makeList( $conds, LIST_AND ); } @@ -1375,7 +1376,13 @@ class DatabaseOracle extends Database { $useIndex = ''; } - return [ $startOpts, $useIndex, $preLimitTail, $postLimitTail ]; + if ( isset( $options['IGNORE INDEX'] ) && !is_array( $options['IGNORE INDEX'] ) ) { + $ignoreIndex = $this->ignoreIndexClause( $options['IGNORE INDEX'] ); + } else { + $ignoreIndex = ''; + } + + return [ $startOpts, $useIndex, $preLimitTail, $postLimitTail, $ignoreIndex ]; } public function delete( $table, $conds, $fname = __METHOD__ ) { diff --git a/includes/db/DatabasePostgres.php b/includes/db/DatabasePostgres.php index c9127ff599..151b4449df 100644 --- a/includes/db/DatabasePostgres.php +++ b/includes/db/DatabasePostgres.php @@ -927,7 +927,8 @@ __INDEXATTR__; if ( !is_array( $selectOptions ) ) { $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 ) ); } else { @@ -936,7 +937,7 @@ __INDEXATTR__; $sql = "INSERT INTO $destTable (" . implode( ',', array_keys( $varMap ) ) . ')' . " SELECT $startOpts " . implode( ',', $varMap ) . - " FROM $srcTable $useIndex"; + " FROM $srcTable $useIndex $ignoreIndex "; if ( $conds != '*' ) { $sql .= ' WHERE ' . $this->makeList( $conds, LIST_AND ); @@ -1482,7 +1483,7 @@ SQL; */ function makeSelectOptions( $options ) { $preLimitTail = $postLimitTail = ''; - $startOpts = $useIndex = ''; + $startOpts = $useIndex = $ignoreIndex = ''; $noKeyOptions = []; foreach ( $options as $key => $option ) { @@ -1512,7 +1513,7 @@ SQL; $startOpts .= 'DISTINCT'; } - return [ $startOpts, $useIndex, $preLimitTail, $postLimitTail ]; + return [ $startOpts, $useIndex, $preLimitTail, $postLimitTail, $ignoreIndex ]; } function getDBname() { -- 2.20.1