X-Git-Url: http://git.cyclocoop.org/%22%20.%20generer_url_ecrire%28%22auteur_infos%22%2C%20%22id_auteur=%24id%22%29%20.%20%22?a=blobdiff_plain;f=includes%2Fdb%2FDatabase.php;h=8caacf7dc5a425edf17af6be9e83e931b5a113c3;hb=ebbea3c974312c56e93799fc93ee3f35130703f9;hp=8b56ab0ba8b12eae79c44b986cf98b0bf7a06385;hpb=af154bea2bd58112674c4dd71acc396152ab3653;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/db/Database.php b/includes/db/Database.php index 8b56ab0ba8..8caacf7dc5 100644 --- a/includes/db/Database.php +++ b/includes/db/Database.php @@ -184,7 +184,7 @@ interface DatabaseType { * * @return string: wikitext of a link to the server software's web site */ - static function getSoftwareLink(); + function getSoftwareLink(); /** * A string describing the current software version, like from @@ -204,11 +204,17 @@ interface DatabaseType { function getServerInfo(); } +/** + * Interface for classes that implement or wrap DatabaseBase + * @ingroup Database + */ +interface IDatabase {} + /** * Database abstraction object * @ingroup Database */ -abstract class DatabaseBase implements DatabaseType { +abstract class DatabaseBase implements IDatabase, DatabaseType { /** Number of times to re-try an operation in case of deadlock */ const DEADLOCK_TRIES = 4; /** Minimum time to wait before retry, in microseconds */ @@ -229,9 +235,9 @@ abstract class DatabaseBase implements DatabaseType { 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; @@ -937,6 +943,7 @@ abstract class DatabaseBase implements DatabaseType { # Keep track of whether the transaction has write queries pending if ( $this->mTrxLevel && !$this->mTrxDoneWrites && $this->isWriteQuery( $sql ) ) { $this->mTrxDoneWrites = true; + Profiler::instance()->transactionWritingIn( $this->mServer, $this->mDBname ); } if ( $this->debug() ) { @@ -1244,7 +1251,7 @@ abstract class DatabaseBase implements DatabaseType { $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 = ''; @@ -1466,28 +1473,26 @@ abstract class DatabaseBase implements DatabaseType { } $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 ) ) { @@ -2266,11 +2271,11 @@ abstract class DatabaseBase implements DatabaseType { } // 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 ) ); } /** @@ -2535,6 +2540,92 @@ abstract class DatabaseBase implements DatabaseType { 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. * @@ -2632,7 +2723,10 @@ abstract class DatabaseBase implements DatabaseType { $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 ); @@ -2972,10 +3066,10 @@ abstract class DatabaseBase implements DatabaseType { * 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(); @@ -2990,10 +3084,10 @@ abstract class DatabaseBase implements DatabaseType { * 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 { @@ -3085,6 +3179,9 @@ abstract class DatabaseBase implements DatabaseType { $this->runOnTransactionPreCommitCallbacks(); $this->doCommit( $fname ); + if ( $this->mTrxDoneWrites ) { + Profiler::instance()->transactionWritingOut( $this->mServer, $this->mDBname ); + } $this->runOnTransactionIdleCallbacks(); } @@ -3134,6 +3231,9 @@ abstract class DatabaseBase implements DatabaseType { $this->runOnTransactionPreCommitCallbacks(); $this->doCommit( $fname ); + if ( $this->mTrxDoneWrites ) { + Profiler::instance()->transactionWritingOut( $this->mServer, $this->mDBname ); + } $this->runOnTransactionIdleCallbacks(); } @@ -3165,6 +3265,9 @@ abstract class DatabaseBase implements DatabaseType { $this->doRollback( $fname ); $this->mTrxIdleCallbacks = array(); // cancel $this->mTrxPreCommitCallbacks = array(); // cancel + if ( $this->mTrxDoneWrites ) { + Profiler::instance()->transactionWritingOut( $this->mServer, $this->mDBname ); + } } /**