Add transaction methods to complement getDB().
This makes it easy to grep for direct begin()/commit()
calls to IDatabase by having script use their own
wrapper. Maintenance scripts are one of the few places
that can (and need to) use begin/commit instead of the
start/end atomic methods.
Eventually, there should be almost no direct callers
and those methods can be made stricter about throwing
errors on nested calls.
Change-Id: Ibbfc7a77c0d2a55f7fc2261087f6c3a19061e0aa
/**
* Used by getDB() / setDB()
- * @var DatabaseBase
+ * @var IDatabase
*/
private $mDb = null;
public function purgeRedundantText( $delete = true ) {
# Data should come off the master, wrapped in a transaction
$dbw = $this->getDB( DB_MASTER );
- $dbw->begin( __METHOD__ );
+ $this->beginTransaction( $dbw, __METHOD__ );
# Get "active" text records from the revisions table
$this->output( 'Searching for active text records in revisions table...' );
}
# Done
- $dbw->commit( __METHOD__ );
+ $this->commitTransaction( $dbw, __METHOD__ );
}
/**
* If not set, wfGetDB() will be used.
* This function has the same parameters as wfGetDB()
*
- * @return DatabaseBase
+ * @param integer $db DB index (DB_SLAVE/DB_MASTER)
+ * @param array $groups; default: empty array
+ * @param string|bool $wiki; default: current wiki
+ * @return IDatabase
*/
protected function getDB( $db, $groups = array(), $wiki = false ) {
if ( is_null( $this->mDb ) ) {
/**
* Sets database object to be returned by getDB().
*
- * @param DatabaseBase $db Database object to be used
+ * @param IDatabase $db Database object to be used
*/
- public function setDB( $db ) {
+ public function setDB( IDatabase $db ) {
$this->mDb = $db;
}
+ /**
+ * Begin a transcation on a DB
+ *
+ * This method makes it clear that begin() is called from a maintenance script,
+ * which has outermost scope. This is safe, unlike $dbw->begin() called in other places.
+ *
+ * @param IDatabase $dbw
+ * @param string $fname Caller name
+ * @since 1.27
+ */
+ protected function beginTransaction( IDatabase $dbw, $fname ) {
+ $dbw->begin( $fname );
+ }
+
+ /**
+ * Commit a transcation on a DB
+ *
+ * This method makes it clear that commit() is called from a maintenance script,
+ * which has outermost scope. This is safe, unlike $dbw->commit() called in other places.
+ *
+ * @param IDatabase $dbw
+ * @param string $fname Caller name
+ * @since 1.27
+ */
+ protected function commitTransaction( IDatabase $dbw, $fname ) {
+ $dbw->commit( $fname );
+ }
+
+ /**
+ * Rollback a transcation on a DB
+ *
+ * This method makes it clear that rollback() is called from a maintenance script,
+ * which has outermost scope. This is safe, unlike $dbw->rollback() called in other places.
+ *
+ * @param IDatabase $dbw
+ * @param string $fname Caller name
+ * @since 1.27
+ */
+ protected function rollbackTransaction( IDatabase $dbw, $fname ) {
+ $dbw->rollback( $fname );
+ }
+
/**
* Lock the search index
* @param DatabaseBase &$db
} else {
$this->output( "renaming $path to $finalPath\n" );
// @todo FIXME: Should this use File::move()?
- $db->begin( __METHOD__ );
+ $this->beginTransaction( $db, __METHOD__ );
$db->update( 'image',
array( 'img_name' => $final ),
array( 'img_name' => $orig ),
if ( !file_exists( $dir ) ) {
if ( !wfMkdirParents( $dir, null, __METHOD__ ) ) {
$this->output( "RENAME FAILED, COULD NOT CREATE $dir" );
- $db->rollback( __METHOD__ );
+ $this->rollbackTransaction( $db, __METHOD__ );
return;
}
}
if ( rename( $path, $finalPath ) ) {
- $db->commit( __METHOD__ );
+ $this->commitTransaction( $db, __METHOD__ );
} else {
$this->error( "RENAME FAILED" );
- $db->rollback( __METHOD__ );
+ $this->rollbackTransaction( $db, __METHOD__ );
}
}
}
public function execute() {
global $wgHiddenPrefs;
- $dbw = wfGetDB( DB_MASTER );
- $dbw->begin( __METHOD__ );
+ $dbw = $this->getDB( DB_MASTER );
+ $this->beginTransaction( $dbw, __METHOD__ );
foreach ( $wgHiddenPrefs as $item ) {
$dbw->delete(
'user_properties',
__METHOD__
);
};
- $dbw->commit( __METHOD__ );
+ $this->commitTransaction( $dbw, __METHOD__ );
$this->output( "Finished!\n" );
}
}
$this->output( "False match\n" );
} else {
$dbw = wfGetDB( DB_MASTER );
- $dbw->begin( __METHOD__ );
+ $this->beginTransaction( $dbw, __METHOD__ );
$page = WikiPage::factory( $title );
if ( $rev ) {
// Revert to this revision
wfMessage( 'spam_blanking', $domain )->inContentLanguage()->text()
);
}
- $dbw->commit( __METHOD__ );
+ $this->commitTransaction( $dbw, __METHOD__ );
}
}
}
# Data should come off the master, wrapped in a transaction
$dbw = $this->getDB( DB_MASTER );
- $dbw->begin( __METHOD__ );
+ $this->beginTransaction( $dbw, __METHOD__ );
$repo = RepoGroup::singleton()->getLocalRepo();
# Get "active" revisions from the filearchive table
$dbw->delete( 'filearchive', array( 'fa_id' => $id ), __METHOD__ );
}
- $dbw->commit( __METHOD__ );
+ $this->commitTransaction( $dbw, __METHOD__ );
$this->output( "Done! [$count file(s)]\n" );
}
}
# Data should come off the master, wrapped in a transaction
$dbw = wfGetDB( DB_MASTER );
- $dbw->begin( __METHOD__ );
+ $this->beginTransaction( $dbw, __METHOD__ );
$pageConds = array();
$revConds = array();
# This bit's done
# Purge redundant text records
- $dbw->commit( __METHOD__ );
+ $this->commitTransaction( $dbw, __METHOD__ );
if ( $delete ) {
$this->purgeRedundantText( true );
}
$report = $this->hasOption( 'report' );
$dbw = wfGetDB( DB_MASTER );
- $dbw->begin( __METHOD__ );
+ $this->beginTransaction( $dbw, __METHOD__ );
list( $page, $revision ) = $dbw->tableNamesN( 'page', 'revision' );
# Find all the orphaned revisions
# Nothing to do?
if ( $report || $count == 0 ) {
- $dbw->commit( __METHOD__ );
+ $this->commitTransaction( $dbw, __METHOD__ );
exit( 0 );
}
$this->output( "done.\n" );
# Close the transaction and call the script to purge unused text records
- $dbw->commit( __METHOD__ );
+ $this->commitTransaction( $dbw, __METHOD__ );
$this->purgeRedundantText( true );
}
$db = wfGetDB( DB_MASTER );
while ( 1 ) {
wfWaitForSlaves();
- $db->commit( __METHOD__ );
+ $this->commitTransaction( $db, __METHOD__ );
$q = $db->limitResult( "DELETE /* deleteSelfExternals */ FROM externallinks WHERE el_to"
. $db->buildLike( $wgServer . '/', $db->anyString() ), $this->mBatchSize );
$this->output( "Deleting a batch\n" );
$affected = 0;
$this->output( "Doing users $blockStart to $blockEnd\n" );
- $dbw->begin( __METHOD__ );
+ $this->beginTransaction( $dbw, __METHOD__ );
$dbw->update( 'user_groups',
array( 'ug_group' => $newGroup ),
array( 'ug_group' => $oldGroup,
__METHOD__
);
$affected += $dbw->affectedRows();
- $dbw->commit( __METHOD__ );
+ $this->commitTransaction( $dbw, __METHOD__ );
// Clear cache for the affected users (bug 40340)
if ( $affected > 0 ) {
}
$this->output( $source->getPrefixedText() . ' --> ' . $dest->getPrefixedText() );
- $dbw->begin( __METHOD__ );
+ $this->beginTransaction( $dbw, __METHOD__ );
$mp = new MovePage( $source, $dest );
$status = $mp->move( $wgUser, $reason, !$noredirects );
if ( !$status->isOK() ) {
$this->output( "\nFAILED: " . $status->getWikiText() );
}
- $dbw->commit( __METHOD__ );
+ $this->commitTransaction( $dbw, __METHOD__ );
$this->output( "\n" );
if ( $interval ) {
$delete = $this->getOption( 'delete', false );
$all = $this->getOption( 'all', false );
$dbw = wfGetDB( DB_MASTER );
- $dbw->begin( __METHOD__ );
+ $this->beginTransaction( $dbw, __METHOD__ );
$tbl_pag = $dbw->tableName( 'page' );
$tbl_rev = $dbw->tableName( 'revision' );
// I already have the id & revs
if ( $delete ) {
$dbw->query( "DELETE FROM $tbl_pag WHERE page_id = $id" );
- $dbw->commit( __METHOD__ );
+ $this->commitTransaction( $dbw, __METHOD__ );
// Delete revisions as appropriate
$child = $this->runChild( 'NukePage', 'nukePage.php' );
$child->deleteRevisions( $revs );
$this->output( "skip: " . $title->getPrefixedText() . "\n" );
}
}
- $dbw->commit( __METHOD__ );
+ $this->commitTransaction( $dbw, __METHOD__ );
if ( $n_deleted > 0 ) {
# update statistics - better to decrement existing count, or just count
$delete = $this->getOption( 'delete', false );
$dbw = wfGetDB( DB_MASTER );
- $dbw->begin( __METHOD__ );
+ $this->beginTransaction( $dbw, __METHOD__ );
$tbl_pag = $dbw->tableName( 'page' );
$tbl_rec = $dbw->tableName( 'recentchanges' );
$this->output( "done.\n" );
}
- $dbw->commit( __METHOD__ );
+ $this->commitTransaction( $dbw, __METHOD__ );
# Delete revisions as appropriate
if ( $delete && $count ) {
}
} else {
$this->output( "not found in database.\n" );
- $dbw->commit( __METHOD__ );
+ $this->commitTransaction( $dbw, __METHOD__ );
}
}
public function deleteRevisions( $ids ) {
$dbw = wfGetDB( DB_MASTER );
- $dbw->begin( __METHOD__ );
+ $this->beginTransaction( $dbw, __METHOD__ );
$tbl_rev = $dbw->tableName( 'revision' );
$set = implode( ', ', $ids );
$dbw->query( "DELETE FROM $tbl_rev WHERE rev_id IN ( $set )" );
- $dbw->commit( __METHOD__ );
+ $this->commitTransaction( $dbw, __METHOD__ );
}
}
$res = $db->select( array( 'logging', 'user' ),
array( 'log_id', 'user_name' ), $cond, __METHOD__ );
- $db->begin( __METHOD__ );
+ $this->beginTransaction( $db, __METHOD__ );
foreach ( $res as $row ) {
$db->update( 'logging', array( 'log_user_text' => $row->user_name ),
array( 'log_id' => $row->log_id ), __METHOD__ );
}
- $db->commit( __METHOD__ );
+ $this->commitTransaction( $db, __METHOD__ );
$blockStart += $this->mBatchSize;
$blockEnd += $this->mBatchSize;
wfWaitForSlaves();
__METHOD__
);
- $db->begin( __METHOD__ );
+ $this->beginTransaction( $db, __METHOD__ );
# Go through and update rev_len from these rows.
foreach ( $res as $row ) {
if ( $this->upgradeRow( $row, $table, $idCol, $prefix ) ) {
$count++;
}
}
- $db->commit( __METHOD__ );
+ $this->commitTransaction( $db, __METHOD__ );
$blockStart += $this->mBatchSize;
$blockEnd += $this->mBatchSize;
AND $idCol IS NOT NULL AND {$prefix}_sha1 = ''";
$res = $db->select( $table, '*', $cond, __METHOD__ );
- $db->begin( __METHOD__ );
+ $this->beginTransaction( $db, __METHOD__ );
foreach ( $res as $row ) {
if ( $this->upgradeRow( $row, $table, $idCol, $prefix ) ) {
$count++;
}
}
- $db->commit( __METHOD__ );
+ $this->commitTransaction( $db, __METHOD__ );
$blockStart += $this->mBatchSize;
$blockEnd += $this->mBatchSize;
array( 'ar_rev_id IS NULL', 'ar_sha1' => '' ), __METHOD__ );
$updateSize = 0;
- $db->begin( __METHOD__ );
+ $this->beginTransaction( $db, __METHOD__ );
foreach ( $res as $row ) {
if ( $this->upgradeLegacyArchiveRow( $row ) ) {
++$count;
}
if ( ++$updateSize >= 100 ) {
$updateSize = 0;
- $db->commit( __METHOD__ );
+ $this->commitTransaction( $db, __METHOD__ );
$this->output( "Commited row with ar_timestamp={$row->ar_timestamp}\n" );
wfWaitForSlaves();
- $db->begin( __METHOD__ );
+ $this->beginTransaction( $db, __METHOD__ );
}
}
- $db->commit( __METHOD__ );
+ $this->commitTransaction( $db, __METHOD__ );
return $count;
}
*/
private function doReassignEdits( &$from, &$to, $rc = false, $report = false ) {
$dbw = wfGetDB( DB_MASTER );
- $dbw->begin( __METHOD__ );
+ $this->beginTransaction( $dbw, __METHOD__ );
# Count things
$this->output( "Checking current edits..." );
}
}
- $dbw->commit( __METHOD__ );
+ $this->commitTransaction( $dbw, __METHOD__ );
return (int)$total;
}
array( 'ORDER BY' => 'page_id ASC', 'USE INDEX' => 'PRIMARY' )
);
- $dbw->begin( __METHOD__ ); // for any changes
+ $this->beginTransaction( $dbw, __METHOD__ ); // for any changes
foreach ( $res as $row ) {
$rebuilt = false;
$wgRequestTime = microtime( true ); # bug 22852
$this->output( "Page {$row->page_id} not cacheable\n" );
}
}
- $dbw->commit( __METHOD__ ); // commit any changes (just for sanity)
+ $this->commitTransaction( $dbw, __METHOD__ ); // commit any changes (just for sanity)
$blockStart += $this->mBatchSize;
$blockEnd += $this->mBatchSize;
$chunk = new ConcatenatedGzipHistoryBlob();
$stubs = array();
- $dbw->begin( __METHOD__ );
+ $this->beginTransaction( $dbw, __METHOD__ );
$usedChunk = false;
$primaryOldid = $revs[$i]->rev_text_id;
}
# Done, next
$this->output( "/" );
- $dbw->commit( __METHOD__ );
+ $this->commitTransaction( $dbw, __METHOD__ );
$i += $thisChunkSize;
wfWaitForSlaves();
}
if ( !$dryRun ) {
// Reset the text row to point to the original copy
- $dbw->begin( __METHOD__ );
+ $this->beginTransaction( $dbw, __METHOD__ );
$dbw->update(
'text',
// SET
),
__METHOD__
);
- $dbw->commit( __METHOD__ );
+ $this->commitTransaction( $dbw, __METHOD__ );
$this->waitForSlaves();
}
exit( 1 );
}
$dbw = wfGetDB( DB_MASTER );
- $dbw->begin( __METHOD__ );
+ $this->beginTransaction( $dbw, __METHOD__ );
$dbw->update( 'text',
array( // set
'old_text' => $url,
array( 'bt_text_id' => $textId ),
__METHOD__
);
- $dbw->commit( __METHOD__ );
+ $this->commitTransaction( $dbw, __METHOD__ );
}
/**
* We do a locking read to prevent closer-run race conditions.
*/
$dbw = wfGetDB( DB_MASTER );
- $dbw->begin( __METHOD__ );
+ $this->beginTransaction( $dbw, __METHOD__ );
$res = $dbw->select( 'blob_tracking',
array( 'bt_text_id', 'bt_moved' ),
array( 'bt_text_id' => array_keys( $this->referrers ) ),
$targetDB->commit( __METHOD__ );
// Critical section here: interruption at this point causes blob duplication
// Reversing the order of the commits would cause data loss instead
- $dbw->commit( __METHOD__ );
+ $this->commitTransaction( $dbw, __METHOD__ );
// Write the new URLs to the text table and set the moved flag
if ( !$this->parent->copyOnly ) {
$this->output( " processing..." );
if ( !$dryRun ) {
- $dbw->begin( __METHOD__ );
+ $this->beginTransaction( $dbw, __METHOD__ );
}
foreach ( $res as $row ) {
$title = Title::newFromRow( $row );
}
}
if ( !$dryRun ) {
- $dbw->commit( __METHOD__ );
+ $this->commitTransaction( $dbw, __METHOD__ );
}
$count += $res->numRows();
$minUserId = 0;
do {
- $dbw->begin();
+ $this->beginTransaction( $dbw, __METHOD__ );
$res = $dbw->select( 'user',
array( 'user_id', 'user_name', 'user_password' ),
$minUserId = $row->user_id;
}
- $dbw->commit();
+ $this->commitTransaction( $dbw, __METHOD__ );
// Clear memcached so old passwords are wiped out
foreach ( $updateUsers as $user ) {