X-Git-Url: https://git.heureux-cyclage.org/?p=lhc%2Fweb%2Fwiklou.git;a=blobdiff_plain;f=includes%2Flibs%2Frdbms%2Fdatabase%2FDatabase.php;h=b8b44e698eeed170c44f597c9788df0fcc081723;hp=559c28b7adc8c51d2c26a34a91b9c9c084d54103;hb=36395150104588f2afea866c330b683e4329fa48;hpb=d89e006b74ab057868f4bfe7b9e682eb02c538a2 diff --git a/includes/libs/rdbms/database/Database.php b/includes/libs/rdbms/database/Database.php index 559c28b7ad..b8b44e698e 100644 --- a/includes/libs/rdbms/database/Database.php +++ b/includes/libs/rdbms/database/Database.php @@ -2481,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 ); @@ -2593,10 +2664,13 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware } final public function onTransactionPreCommitOrIdle( callable $callback, $fname = __METHOD__ ) { - if ( $this->mTrxLevel ) { + if ( $this->mTrxLevel || $this->getFlag( self::DBO_TRX ) ) { + // As long as DBO_TRX is set, writes will accumulate until the load balancer issues + // an implicit commit of all peer databases. This is true even if a transaction has + // not yet been triggered by writes; make sure $callback runs *after* any such writes. $this->mTrxPreCommitCallbacks[] = [ $callback, $fname ]; } else { - // If no transaction is active, then make one for this callback + // No transaction is active nor will start implicitly, so make one for this callback $this->startAtomic( __METHOD__ ); try { call_user_func( $callback );