X-Git-Url: https://git.heureux-cyclage.org/?a=blobdiff_plain;f=includes%2Flibs%2Frdbms%2Fdatabase%2FDatabase.php;h=723a4a607b7b70eea48cd87f123caf174897780b;hb=4427b84407e03275bdb62ca58a0fde14f3dc7be6;hp=9e91592a52c71b1df2d73a6ffeda77d25f0ebfc5;hpb=bf1d5b2ab367c9723300ec0fedaf565bb8eed8e5;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/libs/rdbms/database/Database.php b/includes/libs/rdbms/database/Database.php index 9e91592a52..723a4a607b 100644 --- a/includes/libs/rdbms/database/Database.php +++ b/includes/libs/rdbms/database/Database.php @@ -1802,8 +1802,33 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware } # Split database and table into proper variables. - # We reverse the explode so that database.table and table both output - # the correct table. + list( $database, $schema, $prefix, $table ) = $this->qualifiedTableComponents( $name ); + + # Quote $table and apply the prefix if not quoted. + # $tableName might be empty if this is called from Database::replaceVars() + $tableName = "{$prefix}{$table}"; + if ( $format === 'quoted' + && !$this->isQuotedIdentifier( $tableName ) + && $tableName !== '' + ) { + $tableName = $this->addIdentifierQuotes( $tableName ); + } + + # Quote $schema and $database and merge them with the table name if needed + $tableName = $this->prependDatabaseOrSchema( $schema, $tableName, $format ); + $tableName = $this->prependDatabaseOrSchema( $database, $tableName, $format ); + + return $tableName; + } + + /** + * Get the table components needed for a query given the currently selected database + * + * @param string $name Table name in the form of db.schema.table, db.table, or table + * @return array (DB name or "" for default, schema name, table prefix, table name) + */ + protected function qualifiedTableComponents( $name ) { + # We reverse the explode so that database.table and table both output the correct table. $dbDetails = explode( '.', $name, 3 ); if ( count( $dbDetails ) == 3 ) { list( $database, $schema, $table ) = $dbDetails; @@ -1833,21 +1858,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware } } - # Quote $table and apply the prefix if not quoted. - # $tableName might be empty if this is called from Database::replaceVars() - $tableName = "{$prefix}{$table}"; - if ( $format === 'quoted' - && !$this->isQuotedIdentifier( $tableName ) - && $tableName !== '' - ) { - $tableName = $this->addIdentifierQuotes( $tableName ); - } - - # Quote $schema and $database and merge them with the table name if needed - $tableName = $this->prependDatabaseOrSchema( $schema, $tableName, $format ); - $tableName = $this->prependDatabaseOrSchema( $database, $tableName, $format ); - - return $tableName; + return [ $database, $schema, $prefix, $table ]; } /** @@ -2470,6 +2481,77 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware return '(' . implode( $glue, $sqls ) . ')'; } + public function unionConditionPermutations( + $table, $vars, array $permute_conds, $extra_conds = '', $fname = __METHOD__, + $options = [], $join_conds = [] + ) { + // First, build the Cartesian product of $permute_conds + $conds = [ [] ]; + foreach ( $permute_conds as $field => $values ) { + if ( !$values ) { + // Skip empty $values + continue; + } + $values = array_unique( $values ); // For sanity + $newConds = []; + foreach ( $conds as $cond ) { + foreach ( $values as $value ) { + $cond[$field] = $value; + $newConds[] = $cond; // Arrays are by-value, not by-reference, so this works + } + } + $conds = $newConds; + } + + $extra_conds = $extra_conds === '' ? [] : (array)$extra_conds; + + // If there's just one condition and no subordering, hand off to + // selectSQLText directly. + if ( count( $conds ) === 1 && + ( !isset( $options['INNER ORDER BY'] ) || !$this->unionSupportsOrderAndLimit() ) + ) { + return $this->selectSQLText( + $table, $vars, $conds[0] + $extra_conds, $fname, $options, $join_conds + ); + } + + // Otherwise, we need to pull out the order and limit to apply after + // the union. Then build the SQL queries for each set of conditions in + // $conds. Then union them together (using UNION ALL, because the + // product *should* already be distinct). + $orderBy = $this->makeOrderBy( $options ); + $limit = isset( $options['LIMIT'] ) ? $options['LIMIT'] : null; + $offset = isset( $options['OFFSET'] ) ? $options['OFFSET'] : false; + $all = empty( $options['NOTALL'] ) && !in_array( 'NOTALL', $options ); + if ( !$this->unionSupportsOrderAndLimit() ) { + unset( $options['ORDER BY'], $options['LIMIT'], $options['OFFSET'] ); + } else { + if ( array_key_exists( 'INNER ORDER BY', $options ) ) { + $options['ORDER BY'] = $options['INNER ORDER BY']; + } + if ( $limit !== null && is_numeric( $offset ) && $offset != 0 ) { + // We need to increase the limit by the offset rather than + // using the offset directly, otherwise it'll skip incorrectly + // in the subqueries. + $options['LIMIT'] = $limit + $offset; + unset( $options['OFFSET'] ); + } + } + + $sqls = []; + foreach ( $conds as $cond ) { + $sqls[] = $this->selectSQLText( + $table, $vars, $cond + $extra_conds, $fname, $options, $join_conds + ); + } + $sql = $this->unionQueries( $sqls, $all ) . $orderBy; + if ( $limit !== null ) { + $sql = $this->limitResult( $sql, $limit, $offset ); + } + + return $sql; + } + public function conditional( $cond, $trueVal, $falseVal ) { if ( is_array( $cond ) ) { $cond = $this->makeList( $cond, self::LIST_AND );