From: jenkins-bot Date: Wed, 26 Jun 2013 04:27:35 +0000 (+0000) Subject: Merge "database: Simplify selectSQLText() and fix USE INDEX bug" X-Git-Tag: 1.31.0-rc.0~19353 X-Git-Url: https://git.cyclocoop.org/%242?a=commitdiff_plain;h=0898fa993ca5d51e11adaf4dc57e3ef757bc8c69;hp=-c;p=lhc%2Fweb%2Fwiklou.git Merge "database: Simplify selectSQLText() and fix USE INDEX bug" --- 0898fa993ca5d51e11adaf4dc57e3ef757bc8c69 diff --combined includes/db/Database.php index e06d80d9d1,84dda41fbe..a86d6be981 --- a/includes/db/Database.php +++ b/includes/db/Database.php @@@ -229,9 -229,9 +229,9 @@@ abstract class DatabaseBase implements protected $mConn = null; protected $mOpened = false; - /** @var array of Closure */ + /** @var callable[] */ protected $mTrxIdleCallbacks = array(); - /** @var array of Closure */ + /** @var callable[] */ protected $mTrxPreCommitCallbacks = array(); protected $mTablePrefix; @@@ -1239,7 -1239,7 +1239,7 @@@ $startOpts .= ' SQL_NO_CACHE'; } - if ( isset( $options['USE INDEX'] ) && ! is_array( $options['USE INDEX'] ) ) { + if ( isset( $options['USE INDEX'] ) && is_string( $options['USE INDEX'] ) ) { $useIndex = $this->useIndexClause( $options['USE INDEX'] ); } else { $useIndex = ''; @@@ -1461,28 -1461,26 +1461,26 @@@ } $options = (array)$options; + $useIndexes = ( isset( $options['USE INDEX'] ) && is_array( $options['USE INDEX'] ) ) + ? $options['USE INDEX'] + : array(); if ( is_array( $table ) ) { - $useIndex = ( isset( $options['USE INDEX'] ) && is_array( $options['USE INDEX'] ) ) - ? $options['USE INDEX'] - : array(); - if ( count( $join_conds ) || count( $useIndex ) ) { - $from = ' FROM ' . - $this->tableNamesWithUseIndexOrJOIN( $table, $useIndex, $join_conds ); - } else { - $from = ' FROM ' . implode( ',', $this->tableNamesWithAlias( $table ) ); - } + $from = ' FROM ' . + $this->tableNamesWithUseIndexOrJOIN( $table, $useIndexes, $join_conds ); } elseif ( $table != '' ) { if ( $table[0] == ' ' ) { $from = ' FROM ' . $table; } else { - $from = ' FROM ' . $this->tableName( $table ); + $from = ' FROM ' . + $this->tableNamesWithUseIndexOrJOIN( array( $table ), $useIndexes, array() ); } } else { $from = ''; } - list( $startOpts, $useIndex, $preLimitTail, $postLimitTail ) = $this->makeSelectOptions( $options ); + list( $startOpts, $useIndex, $preLimitTail, $postLimitTail ) = + $this->makeSelectOptions( $options ); if ( !empty( $conds ) ) { if ( is_array( $conds ) ) { @@@ -2260,11 -2258,11 +2258,11 @@@ } // We can't separate explicit JOIN clauses with ',', use ' ' for those - $straightJoins = !empty( $ret ) ? implode( ',', $ret ) : ""; - $otherJoins = !empty( $retJOIN ) ? implode( ' ', $retJOIN ) : ""; + $implicitJoins = !empty( $ret ) ? implode( ',', $ret ) : ""; + $explicitJoins = !empty( $retJOIN ) ? implode( ' ', $retJOIN ) : ""; // Compile our final table clause - return implode( ' ', array( $straightJoins, $otherJoins ) ); + return implode( ' ', array( $implicitJoins, $explicitJoins ) ); } /** @@@ -2529,92 -2527,6 +2527,92 @@@ return $this->query( $sql, $fname ); } + /** + * INSERT ON DUPLICATE KEY UPDATE wrapper, upserts an array into a table. + * + * This updates any conflicting rows (according to the unique indexes) using + * the provided SET clause and inserts any remaining (non-conflicted) rows. + * + * $rows may be either: + * - A single associative array. The array keys are the field names, and + * the values are the values to insert. The values are treated as data + * and will be quoted appropriately. If NULL is inserted, this will be + * converted to a database NULL. + * - An array with numeric keys, holding a list of associative arrays. + * This causes a multi-row INSERT on DBMSs that support it. The keys in + * each subarray must be identical to each other, and in the same order. + * + * It may be more efficient to leave off unique indexes which are unlikely + * to collide. However if you do this, you run the risk of encountering + * errors which wouldn't have occurred in MySQL. + * + * Usually throws a DBQueryError on failure. If errors are explicitly ignored, + * returns success. + * + * @param string $table Table name. This will be passed through DatabaseBase::tableName(). + * @param array $rows A single row or list of rows to insert + * @param array $uniqueIndexes List of single field names or field name tuples + * @param array $set An array of values to SET. For each array element, + * the key gives the field name, and the value gives the data + * to set that field to. The data will be quoted by + * DatabaseBase::addQuotes(). + * @param string $fname Calling function name (use __METHOD__) for logs/profiling + * @param array $options of options + * + * @return bool + * @since 1.22 + */ + public function upsert( + $table, array $rows, array $uniqueIndexes, array $set, $fname = __METHOD__ + ) { + if ( !count( $rows ) ) { + return true; // nothing to do + } + $rows = is_array( reset( $rows ) ) ? $rows : array( $rows ); + + if ( count( $uniqueIndexes ) ) { + $clauses = array(); // list WHERE clauses that each identify a single row + foreach ( $rows as $row ) { + foreach ( $uniqueIndexes as $index ) { + $index = is_array( $index ) ? $index : array( $index ); // columns + $rowKey = array(); // unique key to this row + foreach ( $index as $column ) { + $rowKey[$column] = $row[$column]; + } + $clauses[] = $this->makeList( $rowKey, LIST_AND ); + } + } + $where = array( $this->makeList( $clauses, LIST_OR ) ); + } else { + $where = false; + } + + $useTrx = !$this->mTrxLevel; + if ( $useTrx ) { + $this->begin( $fname ); + } + try { + # Update any existing conflicting row(s) + if ( $where !== false ) { + $ok = $this->update( $table, $set, $where, $fname ); + } else { + $ok = true; + } + # Now insert any non-conflicting row(s) + $ok = $this->insert( $table, $rows, $fname, array( 'IGNORE' ) ) && $ok; + } catch ( Exception $e ) { + if ( $useTrx ) { + $this->rollback( $fname ); + } + throw $e; + } + if ( $useTrx ) { + $this->commit( $fname ); + } + + return $ok; + } + /** * DELETE where the condition is a join. * @@@ -2712,10 -2624,7 +2710,10 @@@ $sql = "DELETE FROM $table"; if ( $conds != '*' ) { - $sql .= ' WHERE ' . $this->makeList( $conds, LIST_AND ); + if ( is_array( $conds ) ) { + $conds = $this->makeList( $conds, LIST_AND ); + } + $sql .= ' WHERE ' . $conds; } return $this->query( $sql, $fname ); @@@ -3055,10 -2964,10 +3053,10 @@@ * after the database is updated so that the jobs will see the data when they actually run. * It can also be used for updates that easily cause deadlocks if locks are held too long. * - * @param Closure $callback + * @param callable $callback * @since 1.20 */ - final public function onTransactionIdle( Closure $callback ) { + final public function onTransactionIdle( $callback ) { $this->mTrxIdleCallbacks[] = $callback; if ( !$this->mTrxLevel ) { $this->runOnTransactionIdleCallbacks(); @@@ -3073,10 -2982,10 +3071,10 @@@ * This is useful for updates that easily cause deadlocks if locks are held too long * but where atomicity is strongly desired for these updates and some related updates. * - * @param Closure $callback + * @param callable $callback * @since 1.22 */ - final public function onTransactionPreCommitOrIdle( Closure $callback ) { + final public function onTransactionPreCommitOrIdle( $callback ) { if ( $this->mTrxLevel ) { $this->mTrxPreCommitCallbacks[] = $callback; } else {