Convert numerous DB queries to use QueryBuilders

Bug: T344971
Change-Id: Ia727b513a6bfcaa5a0b13977a6789aa879ad2f0b
This commit is contained in:
Reedy 2023-09-26 19:40:35 +01:00 committed by Amir Sarabadani
parent 59213059de
commit b98f33cdac
12 changed files with 274 additions and 264 deletions

View file

@ -499,7 +499,7 @@ class SelectQueryBuilder extends JoinGroupBase {
*
* @param string[]|string $fields The field or list of fields to order by.
* @param string|null $direction self::SORT_ASC or self::SORT_DESC.
* If this is null then $fields is assumed to optionally contain ASC or DESC
* If this is null, then $fields is assumed to optionally contain ASC or DESC
* after each field name.
* @return $this
*/

View file

@ -22,7 +22,6 @@
* @ingroup Maintenance
*/
use MediaWiki\User\ActorMigration;
use MediaWiki\User\User;
require_once __DIR__ . '/Maintenance.php';
@ -52,28 +51,32 @@ class FixUserRegistration extends Maintenance {
->orderBy( 'user_id' )
->limit( $this->getBatchSize() )
->caller( __METHOD__ )->fetchResultSet();
foreach ( $res as $row ) {
$id = $row->user_id;
$lastId = $id;
// Get first edit time
$actorQuery = ActorMigration::newMigration()
->getWhere( $dbw, 'rev_user', User::newFromId( $id ) );
$timestamp = $dbw->selectField(
[ 'revision' ] + $actorQuery['tables'],
'MIN(rev_timestamp)',
$actorQuery['conds'],
__METHOD__,
[],
$actorQuery['joins']
);
$actorStore = \MediaWiki\MediaWikiServices::getInstance()->getActorStore();
$userIdentity = $actorStore->getUserIdentityByUserId( $id );
if ( !$userIdentity ) {
continue;
}
$timestamp = $dbw->newSelectQueryBuilder()
->select( 'MIN(rev_timestamp)' )
->from( 'revision' )
->where( [ 'rev_actor' => $userIdentity->getId() ] )
->caller( __METHOD__ )->fetchField();
// Update
if ( $timestamp !== null ) {
$dbw->update(
'user',
[ 'user_registration' => $timestamp ],
[ 'user_id' => $id ],
__METHOD__
);
$dbw->newUpdateQueryBuilder()
->update( 'user' )
->set( [ 'user_registration' => $timestamp ] )
->where( [ 'user_id' => $id ] )
->caller( __METHOD__ )->execute();
$user = User::newFromId( $id );
$user->invalidateCache();
$this->output( "Set registration for #$id to $timestamp\n" );

View file

@ -24,7 +24,6 @@
require_once __DIR__ . '/Maintenance.php';
use MediaWiki\User\ActorMigration;
use MediaWiki\WikiMap\WikiMap;
class InitEditCount extends Maintenance {
@ -51,8 +50,6 @@ class InitEditCount extends Maintenance {
$backgroundMode = $lb->hasReplicaServers();
}
$actorQuery = ActorMigration::newMigration()->getJoin( 'rev_user' );
if ( $backgroundMode ) {
$this->output( "Using replication-friendly background mode...\n" );
@ -68,21 +65,21 @@ class InitEditCount extends Maintenance {
for ( $min = 0; $min <= $lastUser; $min += $chunkSize ) {
$max = $min + $chunkSize;
$revUser = $actorQuery['fields']['rev_user'];
$result = $dbr->select(
[ 'user', 'rev' => [ 'revision' ] + $actorQuery['tables'] ],
[ 'user_id', 'user_editcount' => "COUNT($revUser)" ],
"user_id > $min AND user_id <= $max",
__METHOD__,
[ 'GROUP BY' => 'user_id' ],
[ 'rev' => [ 'LEFT JOIN', "user_id = $revUser" ] ] + $actorQuery['joins']
);
$result = $dbr->newSelectQueryBuilder()
->select( [ 'user_id', 'user_editcount' => "COUNT(actor_rev_user.actor_user)" ] )
->from( 'user' )
->leftJoin( 'revision', 'rev', "user_id = actor_rev_user.actor_user" )
->join( 'actor', 'actor_rev_user', 'actor_rev_user.actor_id = rev_actor' )
->where( "user_id > $min AND user_id <= $max" )
->groupBy( 'user_id' )
->caller( __METHOD__ )->fetchResultSet();
foreach ( $result as $row ) {
$dbw->update( 'user',
[ 'user_editcount' => $row->user_editcount ],
[ 'user_id' => $row->user_id ],
__METHOD__ );
$dbw->newUpdateQueryBuilder()
->update( 'user' )
->set( [ 'user_editcount' => $row->user_editcount ] )
->where( [ 'user_id' => $row->user_id ] )
->caller( __METHOD__ )->execute();
++$migrated;
}
@ -101,14 +98,13 @@ class InitEditCount extends Maintenance {
$this->output( "Using single-query mode...\n" );
$user = $dbw->tableName( 'user' );
$subquery = $dbw->selectSQLText(
[ 'revision' ] + $actorQuery['tables'],
[ 'COUNT(*)' ],
[ 'user_id = ' . $actorQuery['fields']['rev_user'] ],
__METHOD__,
[],
$actorQuery['joins']
);
$subquery = $dbw->newSelectQueryBuilder()
->select( 'COUNT(*)' )
->from( 'revision' )
->join( 'actor', 'actor_rev_user', 'actor_rev_user.actor_id = rev_actor' )
->where( 'user_id = actor_rev_user.actor_user' )
->caller( __METHOD__ )->getSQL();
$dbw->query( "UPDATE $user SET user_editcount=($subquery)", __METHOD__ );
}

View file

@ -62,7 +62,7 @@ class PopulateRevisionLength extends LoggedUpdateMaintenance {
'revision',
'rev_id',
'rev',
$revisionStore->getQueryInfo()
$revisionStore->newSelectQueryBuilder( $this->getDB( DB_REPLICA ) )->joinComment()
);
$this->output( "Populating ar_len column\n" );
@ -70,7 +70,7 @@ class PopulateRevisionLength extends LoggedUpdateMaintenance {
'archive',
'ar_id',
'ar',
$revisionStore->getArchiveQueryInfo()
$revisionStore->newArchiveSelectQueryBuilder( $this->getDB( DB_REPLICA ) )->joinComment()
);
$this->output( "rev_len and ar_len population complete "
@ -83,10 +83,10 @@ class PopulateRevisionLength extends LoggedUpdateMaintenance {
* @param string $table
* @param string $idCol
* @param string $prefix
* @param array $queryInfo
* @param \Wikimedia\Rdbms\SelectQueryBuilder $queryBuilder should use a replica db
* @return int
*/
protected function doLenUpdates( $table, $idCol, $prefix, $queryInfo ) {
protected function doLenUpdates( $table, $idCol, $prefix, $queryBuilder ) {
$dbr = $this->getDB( DB_REPLICA );
$dbw = $this->getDB( DB_PRIMARY );
$batchSize = $this->getBatchSize();
@ -111,10 +111,9 @@ class PopulateRevisionLength extends LoggedUpdateMaintenance {
while ( $blockStart <= $end ) {
$this->output( "...doing $idCol from $blockStart to $blockEnd\n" );
$res = $dbr->select(
$queryInfo['tables'],
$queryInfo['fields'],
[
$res = $queryBuilder
->where( [
"$idCol >= $blockStart",
"$idCol <= $blockEnd",
$dbr->makeList( [
@ -125,11 +124,8 @@ class PopulateRevisionLength extends LoggedUpdateMaintenance {
"{$prefix}_sha1 != " . $dbr->addQuotes( 'phoiac9h4m842xq45sp7s6u21eteeq1' ),
], IDatabase::LIST_AND )
], IDatabase::LIST_OR )
],
__METHOD__,
[],
$queryInfo['joins']
);
] )
->caller( __METHOD__ )->fetchResultSet();
if ( $res->numRows() > 0 ) {
$this->beginTransaction( $dbw, __METHOD__ );
@ -175,11 +171,11 @@ class PopulateRevisionLength extends LoggedUpdateMaintenance {
}
# Update the row...
$dbw->update( $table,
[ "{$prefix}_len" => $revRecord->getSize() ],
[ $idCol => $row->$idCol ],
__METHOD__
);
$dbw->newUpdateQueryBuilder()
->update( $table )
->set( [ "{$prefix}_len" => $revRecord->getSize() ] )
->where( [ $idCol => $row->$idCol ] )
->caller( __METHOD__ )->execute();
return true;
}

View file

@ -57,12 +57,14 @@ class PopulateRevisionSha1 extends LoggedUpdateMaintenance {
$this->output( "Populating rev_sha1 column\n" );
$rc = $this->doSha1Updates( $revStore, 'revision', 'rev_id',
$revStore->getQueryInfo(), 'rev'
$revStore->newSelectQueryBuilder( $this->getDB( DB_PRIMARY ) )->joinComment(),
'rev'
);
$this->output( "Populating ar_sha1 column\n" );
$ac = $this->doSha1Updates( $revStore, 'archive', 'ar_rev_id',
$revStore->getArchiveQueryInfo(), 'ar'
$revStore->newArchiveSelectQueryBuilder( $this->getDB( DB_PRIMARY ) )->joinComment(),
'ar'
);
$this->output( "rev_sha1 and ar_sha1 population complete "
@ -75,11 +77,11 @@ class PopulateRevisionSha1 extends LoggedUpdateMaintenance {
* @param MediaWiki\Revision\RevisionStore $revStore
* @param string $table
* @param string $idCol
* @param array $queryInfo
* @param \Wikimedia\Rdbms\SelectQueryBuilder $queryBuilder should use a primary db
* @param string $prefix
* @return int Rows changed
*/
protected function doSha1Updates( $revStore, $table, $idCol, $queryInfo, $prefix ) {
protected function doSha1Updates( $revStore, $table, $idCol, $queryBuilder, $prefix ) {
$db = $this->getDB( DB_PRIMARY );
$batchSize = $this->getBatchSize();
$start = $db->newSelectQueryBuilder()
@ -106,9 +108,9 @@ class PopulateRevisionSha1 extends LoggedUpdateMaintenance {
$cond = "$idCol BETWEEN " . (int)$blockStart . " AND " . (int)$blockEnd .
" AND $idCol IS NOT NULL AND {$prefix}_sha1 = ''";
$res = $db->select(
$queryInfo['tables'], $queryInfo['fields'], $cond, __METHOD__, [], $queryInfo['joins']
);
$res = $queryBuilder->where( $cond )
->caller( __METHOD__ )->fetchResultSet();
$this->beginTransaction( $db, __METHOD__ );
foreach ( $res as $row ) {
@ -147,11 +149,11 @@ class PopulateRevisionSha1 extends LoggedUpdateMaintenance {
return false; // T24624? T22757?
}
$db->update( $table,
[ "{$prefix}_sha1" => $sha1 ],
[ $idCol => $row->$idCol ],
__METHOD__
);
$db->newUpdateQueryBuilder()
->update( $table )
->set( [ "{$prefix}_sha1" => $sha1 ] )
->where( [ $idCol => $row->$idCol ] )
->caller( __METHOD__ )->execute();
return true;
}

View file

@ -23,7 +23,6 @@
* @license GPL-2.0-or-later
*/
use MediaWiki\User\ActorMigration;
use MediaWiki\User\User;
use Wikimedia\IPUtils;
@ -75,7 +74,7 @@ class ReassignEdits extends Maintenance {
* @param User &$to User to assign edits to
* @param bool $updateRC Update the recent changes table
* @param bool $report Don't change things; just echo numbers
* @return int Number of entries changed, or that would be changed
* @return int The number of entries changed, or that would be changed
*/
private function doReassignEdits( &$from, &$to, $updateRC = false, $report = false ) {
$dbw = $this->getDB( DB_PRIMARY );
@ -85,15 +84,14 @@ class ReassignEdits extends Maintenance {
# Count things
$this->output( "Checking current edits..." );
$revQueryInfo = ActorMigration::newMigration()->getWhere( $dbw, 'rev_user', $from );
$revisionRows = $dbw->selectRowCount(
[ 'revision' ] + $revQueryInfo['tables'],
'*',
$revQueryInfo['conds'],
__METHOD__,
[],
$revQueryInfo['joins']
);
$revisionRows = $dbw->newSelectQueryBuilder()
->select( '*' )
->from( 'revision' )
->where( [ 'rev_actor' => $fromActorId ] )
->caller( __METHOD__ )
->fetchRowCount();
$this->output( "found {$revisionRows}.\n" );
$this->output( "Checking deleted edits..." );
@ -126,32 +124,37 @@ class ReassignEdits extends Maintenance {
if ( $revisionRows ) {
# Reassign edits
$this->output( "Reassigning current edits..." );
$dbw->update(
'revision',
[ 'rev_actor' => $toActorId ],
[ 'rev_actor' => $fromActorId ],
__METHOD__
);
$dbw->newUpdateQueryBuilder()
->update( 'revision' )
->set( [ 'rev_actor' => $toActorId ] )
->where( [ 'rev_actor' => $fromActorId ] )
->caller( __METHOD__ )->execute();
$this->output( "done.\n" );
}
if ( $archiveRows ) {
$this->output( "Reassigning deleted edits..." );
$dbw->update( 'archive',
[ 'ar_actor' => $toActorId ],
[ 'ar_actor' => $fromActorId ],
__METHOD__
);
$dbw->newUpdateQueryBuilder()
->update( 'archive' )
->set( [ 'ar_actor' => $toActorId ] )
->where( [ 'ar_actor' => $fromActorId ] )
->caller( __METHOD__ )->execute();
$this->output( "done.\n" );
}
# Update recent changes if required
if ( $recentChangesRows ) {
$this->output( "Updating recent changes..." );
$dbw->update( 'recentchanges',
[ 'rc_actor' => $toActorId ],
[ 'rc_actor' => $fromActorId ],
__METHOD__
);
$dbw->newUpdateQueryBuilder()
->update( 'recentchanges' )
->set( [ 'rc_actor' => $toActorId ] )
->where( [ 'rc_actor' => $fromActorId ] )
->caller( __METHOD__ )->execute();
$this->output( "done.\n" );
}
@ -159,14 +162,11 @@ class ReassignEdits extends Maintenance {
# ip_changes. No update needed, as $to cannot be an IP.
if ( !$from->isRegistered() ) {
$this->output( "Deleting ip_changes..." );
$dbw->delete(
'ip_changes',
[
'ipc_hex' => IPUtils::toHex( $from->getName() )
],
__METHOD__
);
$this->output( "done.\n" );
$dbw->newDeleteQueryBuilder()
->deleteFrom( 'ip_changes' )
->where( [ 'ipc_hex' => IPUtils::toHex( $from->getName() ) ] )
->caller( __METHOD__ )->execute();
}
}

View file

@ -25,8 +25,8 @@
require_once __DIR__ . '/Maintenance.php';
use MediaWiki\User\ActorMigration;
use Wikimedia\Rdbms\IDatabase;
use Wikimedia\Rdbms\SelectQueryBuilder;
/**
* Maintenance script that rebuilds recent changes from scratch.
@ -109,36 +109,46 @@ class RebuildRecentchanges extends Maintenance {
->andWhere( [ 'rc_timestamp < ' . $dbw->addQuotes( $dbw->timestamp( $this->cutoffTo ) ) ] )
->caller( __METHOD__ )->fetchFieldValues();
foreach ( array_chunk( $rcids, $this->getBatchSize() ) as $rcidBatch ) {
$dbw->delete( 'recentchanges', [ 'rc_id' => $rcidBatch ], __METHOD__ );
$dbw->newDeleteQueryBuilder()
->deleteFrom( 'recentchanges' )
->where( [ 'rc_id' => $rcidBatch ] )
->caller( __METHOD__ )->execute();
$this->waitForReplication();
}
$this->output( "Loading from page and revision tables...\n" );
$commentQuery = $commentStore->getJoin( 'rev_comment' );
$actorQuery = ActorMigration::newMigration()->getJoin( 'rev_user' );
$res = $dbw->select(
[ 'revision', 'page' ] + $commentQuery['tables'] + $actorQuery['tables'],
[
'rev_timestamp',
'rev_minor_edit',
'rev_id',
'rev_deleted',
'page_namespace',
'page_title',
'page_is_new',
'page_id'
] + $commentQuery['fields'] + $actorQuery['fields'],
[
'rev_timestamp > ' . $dbw->addQuotes( $dbw->timestamp( $this->cutoffFrom ) ),
'rev_timestamp < ' . $dbw->addQuotes( $dbw->timestamp( $this->cutoffTo ) )
],
__METHOD__,
[ 'ORDER BY' => 'rev_timestamp DESC' ],
[
'page' => [ 'JOIN', 'rev_page=page_id' ],
] + $commentQuery['joins'] + $actorQuery['joins']
);
$res = $dbw->newSelectQueryBuilder()
->select(
[
'rev_timestamp',
'rev_minor_edit',
'rev_id',
'rev_deleted',
'page_namespace',
'page_title',
'page_is_new',
'page_id',
'rev_comment_text' => 'comment_rev_comment.comment_text',
'rev_comment_data' => 'comment_rev_comment.comment_data',
'rev_comment_cid' => 'comment_rev_comment.comment_id',
'rev_user' => 'actor_rev_user.actor_user',
'rev_user_text' => 'actor_rev_user.actor_name',
'rev_actor' => 'rev_actor',
]
)
->from( 'revision' )
->join( 'page', null, 'rev_page=page_id' )
->join( 'comment', 'comment_rev_comment', 'comment_rev_comment.comment_id = rev_comment_id' )
->join( 'actor', 'actor_rev_user', 'actor_rev_user.actor_id = rev_actor' )
->where(
[
'rev_timestamp > ' . $dbw->addQuotes( $dbw->timestamp( $this->cutoffFrom ) ),
'rev_timestamp < ' . $dbw->addQuotes( $dbw->timestamp( $this->cutoffTo ) )
]
)
->orderBy( 'rev_timestamp', SelectQueryBuilder::SORT_DESC )
->caller( __METHOD__ )->fetchResultSet();
$this->output( "Inserting from page and revision tables...\n" );
$inserted = 0;
@ -165,12 +175,11 @@ class RebuildRecentchanges extends Maintenance {
);
$rcid = $dbw->insertId();
$dbw->update(
'change_tag',
[ 'ct_rc_id' => $rcid ],
[ 'ct_rev_id' => $row->rev_id ],
__METHOD__
);
$dbw->newUpdateQueryBuilder()
->update( 'change_tag' )
->set( [ 'ct_rc_id' => $rcid ] )
->where( [ 'ct_rev_id' => $row->rev_id ] )
->caller( __METHOD__ )->execute();
if ( ( ++$inserted % $this->getBatchSize() ) == 0 ) {
$this->waitForReplication();
@ -236,23 +245,23 @@ class RebuildRecentchanges extends Maintenance {
->where( [ 'rev_id' => $row->rc_this_oldid ] )
->caller( __METHOD__ )->fetchField();
$dbw->update(
'recentchanges',
[
$dbw->newUpdateQueryBuilder()
->update( 'recentchanges' )
->set( [
'rc_last_oldid' => $lastOldId,
'rc_new' => $new,
'rc_type' => $new ? RC_NEW : RC_EDIT,
'rc_source' => $new === 1 ? RecentChange::SRC_NEW : RecentChange::SRC_EDIT,
'rc_old_len' => $lastSize,
'rc_new_len' => $size,
],
[
] )
->where( [
'rc_cur_id' => $lastCurId,
'rc_this_oldid' => $row->rc_this_oldid,
'rc_timestamp' => $row->rc_timestamp // index usage
],
__METHOD__
);
// index usage
'rc_timestamp' => $row->rc_timestamp,
] )
->caller( __METHOD__ )->execute();
$lastOldId = intval( $row->rc_this_oldid );
$lastSize = $size;
@ -272,37 +281,44 @@ class RebuildRecentchanges extends Maintenance {
$dbw = $this->getDB( DB_PRIMARY );
$commentStore = $this->getServiceContainer()->getCommentStore();
$nonRCLogs = array_merge( array_keys( $wgLogRestrictions ),
$nonRCLogs = array_merge(
array_keys( $wgLogRestrictions ),
array_keys( $wgFilterLogTypes ),
[ 'create' ] );
[ 'create' ]
);
$this->output( "Loading from user and logging tables...\n" );
$commentQuery = $commentStore->getJoin( 'log_comment' );
$res = $dbw->select(
[ 'logging' ] + $commentQuery['tables'],
[
'log_timestamp',
'log_actor',
'log_namespace',
'log_title',
'log_page',
'log_type',
'log_action',
'log_id',
'log_params',
'log_deleted'
] + $commentQuery['fields'],
[
'log_timestamp > ' . $dbw->addQuotes( $dbw->timestamp( $this->cutoffFrom ) ),
'log_timestamp < ' . $dbw->addQuotes( $dbw->timestamp( $this->cutoffTo ) ),
// Some logs don't go in RC since they are private, or are included in the filterable log types.
'log_type' => array_diff( LogPage::validTypes(), $nonRCLogs ),
],
__METHOD__,
[ 'ORDER BY' => [ 'log_timestamp DESC', 'log_id DESC' ] ],
$commentQuery['joins']
);
$res = $dbw->newSelectQueryBuilder()
->select(
[
'log_timestamp',
'log_actor',
'log_namespace',
'log_title',
'log_page',
'log_type',
'log_action',
'log_id',
'log_params',
'log_deleted',
'log_comment_text' => 'comment_log_comment.comment_text',
'log_comment_data' => 'comment_log_comment.comment_data',
'log_comment_cid' => 'comment_log_comment.comment_id',
]
)
->from( 'logging' )
->join( 'comment', 'comment_log_comment', 'comment_log_comment.comment_id = log_comment_id' )
->where(
[
'log_timestamp > ' . $dbw->addQuotes( $dbw->timestamp( $this->cutoffFrom ) ),
'log_timestamp < ' . $dbw->addQuotes( $dbw->timestamp( $this->cutoffTo ) ),
// Some logs don't go in RC since they are private, or are included in the filterable log types.
'log_type' => array_diff( LogPage::validTypes(), $nonRCLogs ),
]
)
->orderBy( [ 'log_timestamp DESC', 'log_id DESC' ] )
->caller( __METHOD__ )->fetchResultSet();
$field = $dbw->fieldInfo( 'recentchanges', 'rc_cur_id' );
@ -337,12 +353,11 @@ class RebuildRecentchanges extends Maintenance {
);
$rcid = $dbw->insertId();
$dbw->update(
'change_tag',
[ 'ct_rc_id' => $rcid ],
[ 'ct_log_id' => $row->log_id ],
__METHOD__
);
$dbw->newUpdateQueryBuilder()
->update( 'change_tag' )
->set( [ 'ct_rc_id' => $rcid ] )
->where( [ 'ct_log_id' => $row->log_id ] )
->caller( __METHOD__ )->execute();
if ( ( ++$inserted % $this->getBatchSize() ) == 0 ) {
$this->waitForReplication();
@ -402,12 +417,11 @@ class RebuildRecentchanges extends Maintenance {
$rcids = $this->findRcIdsWithGroups( $dbw, $botgroups );
foreach ( array_chunk( $rcids, $this->getBatchSize() ) as $rcidBatch ) {
$dbw->update(
'recentchanges',
[ 'rc_bot' => 1 ],
[ 'rc_id' => $rcidBatch ],
__METHOD__
);
$dbw->newUpdateQueryBuilder()
->update( 'recentchanges' )
->set( [ 'rc_bot' => 1 ] )
->where( [ 'rc_id' => $rcidBatch ] )
->caller( __METHOD__ )->execute();
$this->waitForReplication();
}
}
@ -430,12 +444,11 @@ class RebuildRecentchanges extends Maintenance {
$rcids = $this->findRcIdsWithGroups( $dbw, $autopatrolgroups, $conds );
foreach ( array_chunk( $rcids, $this->getBatchSize() ) as $rcidBatch ) {
$dbw->update(
'recentchanges',
[ 'rc_patrolled' => 2 ],
[ 'rc_id' => $rcidBatch ],
__METHOD__
);
$dbw->newUpdateQueryBuilder()
->update( 'recentchanges' )
->set( [ 'rc_patrolled' => 2 ] )
->where( [ 'rc_id' => $rcidBatch ] )
->caller( __METHOD__ )->execute();
$this->waitForReplication();
}
}
@ -468,19 +481,17 @@ class RebuildRecentchanges extends Maintenance {
$log_id = $row->ls_log_id;
// Mark the logging row as having an associated rev id
$dbw->update(
'recentchanges',
/*SET*/ [ 'rc_this_oldid' => $rev_id ],
/*WHERE*/ [ 'rc_logid' => $log_id ],
__METHOD__
);
$dbw->newUpdateQueryBuilder()
->update( 'recentchanges' )
->set( [ 'rc_this_oldid' => $rev_id ] )
->where( [ 'rc_logid' => $log_id ] )
->caller( __METHOD__ )->execute();
// Delete the revision row
$dbw->delete(
'recentchanges',
/*WHERE*/ [ 'rc_this_oldid' => $rev_id, 'rc_logid' => 0 ],
__METHOD__
);
$dbw->newDeleteQueryBuilder()
->deleteFrom( 'recentchanges' )
->where( [ 'rc_this_oldid' => $rev_id, 'rc_logid' => 0 ] )
->caller( __METHOD__ )->execute();
if ( ( ++$updates % $this->getBatchSize() ) == 0 ) {
$this->waitForReplication();

View file

@ -23,7 +23,6 @@
*/
use MediaWiki\Title\Title;
use MediaWiki\User\ActorMigration;
use MediaWiki\User\User;
require_once __DIR__ . '/Maintenance.php';
@ -114,16 +113,14 @@ class RollbackEdits extends Maintenance {
private function getRollbackTitles( $user ) {
$dbr = $this->getDB( DB_REPLICA );
$titles = [];
$actorQuery = ActorMigration::newMigration()
->getWhere( $dbr, 'rev_user', User::newFromName( $user, false ) );
$results = $dbr->select(
[ 'page', 'revision' ] + $actorQuery['tables'],
[ 'page_namespace', 'page_title' ],
$actorQuery['conds'],
__METHOD__,
[],
[ 'revision' => [ 'JOIN', 'page_latest = rev_id' ] ] + $actorQuery['joins']
);
$results = $dbr->newSelectQueryBuilder()
->select( [ 'page_namespace', 'page_title' ] )
->from( 'page' )
->join( 'revision', null, 'page_latest = rev_id' )
->join( 'actor', null, 'rev_actor = actor_id' )
->where( [ 'actor_name' => $user ] )
->caller( __METHOD__ )->fetchResultSet();
foreach ( $results as $row ) {
$titles[] = Title::makeTitle( $row->page_namespace, $row->page_title );
}

View file

@ -25,7 +25,7 @@
require_once __DIR__ . '/Maintenance.php';
use Wikimedia\Rdbms\IDatabase;
use Wikimedia\Rdbms\Platform\ISQLPlatform;
/**
* Maintenance script to run a database query in batches and wait for replica DBs.
@ -71,14 +71,21 @@ class RunBatchedQuery extends Maintenance {
$this->output( "Batch $n: " );
$n++;
// Note that the update conditions do not rely on atomicity of the
// Note that the update conditions do not rely on the atomicity of the
// SELECT query in order to guarantee that all rows are updated. The
// results of the SELECT are merely a partitioning hint. Simultaneous
// updates merely result in the wrong number of rows being updated
// in a batch.
$res = $dbw->select( $table, $key, $selectConds, __METHOD__,
[ 'ORDER BY' => $key, 'LIMIT' => $batchSize ] );
$res = $dbw->newSelectQueryBuilder()
->select( $key )
->from( $table )
->where( $selectConds )
->orderBy( $key )
->limit( $batchSize )
->caller( __METHOD__ )
->fetchResultSet();
if ( $res->numRows() ) {
$res->seek( $res->numRows() - 1 );
$row = $res->fetchObject();
@ -95,7 +102,7 @@ class RunBatchedQuery extends Maintenance {
$query = "UPDATE " . $dbw->tableName( $table ) .
" SET " . $set .
" WHERE " . $dbw->makeList( $updateConds, IDatabase::LIST_AND );
" WHERE " . $dbw->makeList( $updateConds, ISQLPlatform::LIST_AND );
$dbw->query( $query, __METHOD__ );

View file

@ -258,7 +258,7 @@ class CompressOld extends Maintenance {
$pageConds[] = 'page_namespace<>0';
}
if ( $queryExtra ) {
$pageConds[] = $queryExtra;
$pageConds[] = $queryExtra;
}
*/
@ -268,11 +268,23 @@ class CompressOld extends Maintenance {
# Don't compress object type entities, because that might produce data loss when
# overwriting bulk storage concat rows. Don't compress external references, because
# the script doesn't yet delete rows from external storage.
$conds = [
'old_flags NOT ' . $dbr->buildLike( $dbr->anyString(), 'object', $dbr->anyString() )
. ' AND old_flags NOT '
. $dbr->buildLike( $dbr->anyString(), 'external', $dbr->anyString() )
];
$slotRoleStore = $this->getServiceContainer()->getSlotRoleStore();
$queryBuilderTemplate = $dbw->newSelectQueryBuilder()
->select( [ 'rev_id', 'old_id', 'old_flags', 'old_text' ] )
->forUpdate()
->from( 'revision' )
->join( 'slots', null, 'rev_id=slot_revision_id' )
->join( 'content', null, 'content_id=slot_content_id' )
->join( 'text', null, 'SUBSTRING(content_address, 4)=old_id' )
->where(
'old_flags NOT ' . $dbr->buildLike( $dbr->anyString(), 'object', $dbr->anyString() )
. ' AND old_flags NOT '
. $dbr->buildLike( $dbr->anyString(), 'external', $dbr->anyString() )
)
->andWhere( [
'slot_role_id' => $slotRoleStore->getId( SlotRecord::MAIN ),
'SUBSTRING(content_address, 1, 3)=' . $dbr->addQuotes( 'tt:' ),
] );
if ( $beginDate ) {
if ( !preg_match( '/^\d{14}$/', $beginDate ) ) {
@ -280,7 +292,7 @@ class CompressOld extends Maintenance {
return false;
}
$conds[] = "rev_timestamp>'" . $beginDate . "'";
$queryBuilderTemplate->andWhere( "rev_timestamp>'" . $beginDate . "'" );
}
if ( $endDate ) {
if ( !preg_match( '/^\d{14}$/', $endDate ) ) {
@ -288,27 +300,9 @@ class CompressOld extends Maintenance {
return false;
}
$conds[] = "rev_timestamp<'" . $endDate . "'";
$queryBuilderTemplate->andWhere( "rev_timestamp<'" . $endDate . "'" );
}
$slotRoleStore = $this->getServiceContainer()->getSlotRoleStore();
$tables = [ 'revision', 'slots', 'content', 'text' ];
$conds = array_merge( [
'rev_id=slot_revision_id',
'slot_role_id=' . $slotRoleStore->getId( SlotRecord::MAIN ),
'content_id=slot_content_id',
'SUBSTRING(content_address, 1, 3)=' . $dbr->addQuotes( 'tt:' ),
'SUBSTRING(content_address, 4)=old_id',
], $conds );
$fields = [ 'rev_id', 'old_id', 'old_flags', 'old_text' ];
$revLoadOptions = 'FOR UPDATE';
# Don't work with current revisions
# Don't lock the page table for update either -- TS 2006-04-04
# $tables[] = 'page';
# $conds[] = 'page_id=rev_page AND rev_id != page_latest';
for ( $pageId = $startId; $pageId <= $maxPageId; $pageId++ ) {
$this->waitForReplication();
@ -332,17 +326,17 @@ class CompressOld extends Maintenance {
$this->output( "$pageId\t" . $titleObj->getPrefixedDBkey() . " " );
# Load revisions
$revRes = $dbw->select( $tables, $fields,
array_merge( [
$queryBuilder = clone $queryBuilderTemplate;
$revRes = $queryBuilder->where(
[
'rev_page' => $pageRow->page_id,
# Don't operate on the current revision
# Use < instead of <> in case the current revision has changed
# since the page select, which wasn't locking
// Don't operate on the current revision
// Use < instead of <> in case the current revision has changed
// since the page select, which wasn't locking
'rev_timestamp < ' . (int)$pageRow->rev_timestamp
], $conds ),
__METHOD__,
$revLoadOptions
);
] )
->caller( __METHOD__ )->fetchResultSet();
$revs = [];
foreach ( $revRes as $revRow ) {
$revs[] = $revRow;

View file

@ -334,12 +334,11 @@ class RecompressTracked {
if ( $this->noCount ) {
$numPages = '[unknown]';
} else {
$numPages = $dbr->selectField( 'blob_tracking',
'COUNT(DISTINCT bt_page)',
# A condition is required so that this query uses the index
[ 'bt_moved' => 0 ],
__METHOD__
);
$numPages = $dbr->newSelectQueryBuilder()
->select( 'COUNT(DISTINCT bt_page)' )
->from( 'blob_tracking' )
->where( [ 'bt_moved' => 0 ] )
->caller( __METHOD__ )->fetchField();
}
if ( $this->copyOnly ) {
$this->info( "Copying pages..." );

View file

@ -1,7 +1,7 @@
<?php
use MediaWiki\SiteStats\SiteStats;
use Wikimedia\Rdbms\IDatabase;
use Wikimedia\Rdbms\Platform\ISQLPlatform;
/**
* @group Database
@ -42,10 +42,15 @@ class SiteStatsTest extends MediaWikiIntegrationTestCase {
* @covers MediaWiki\SiteStats\SiteStats
*/
public function testInit() {
$this->db->delete( 'site_stats', IDatabase::ALL_ROWS, __METHOD__ );
$this->db->delete( 'site_stats', ISQLPlatform::ALL_ROWS, __METHOD__ );
SiteStats::unload();
SiteStats::edits();
$this->assertNotFalse( $this->db->selectRow( 'site_stats', '1', IDatabase::ALL_ROWS, __METHOD__ ) );
$row = $this->db->newSelectQueryBuilder()
->select( '1' )
->from( 'site_stats' )
->caller( __METHOD__ )->fetchRow();
$this->assertNotFalse( $row );
}
}