3 namespace MediaWiki\Maintenance
;
5 use Wikimedia\Rdbms\IExpression
;
6 use Wikimedia\Rdbms\LikeValue
;
7 use Wikimedia\Rdbms\SelectQueryBuilder
;
9 // @codeCoverageIgnoreStart
10 require_once __DIR__
. '/Maintenance.php';
11 // @codeCoverageIgnoreEnd
13 class FixAutoblockLogTitles
extends LoggedUpdateMaintenance
{
15 public function __construct() {
16 parent
::__construct();
17 $this->addDescription(
18 'Finds and fixes unblock log rows in the logging table where the log_title starts with the ' .
19 'prefix "User:#". These rows are broken because the target is an autoblock and code expects the "#" ' .
20 'character to be the first character in the title (T373929).'
22 $this->setBatchSize( 200 );
25 protected function doDBUpdates() {
26 $this->output( "Fixing log entries with log_title starting with 'User:#'\n" );
28 $dbr = $this->getReplicaDB();
29 $dbw = $this->getPrimaryDB();
32 $lastProcessedLogId = 0;
34 // Get a batch of "unblock" log entries from the logging table. The check for the log_title being broken
35 // needs to be performed in batches, as it is expensive when run on the whole logging table on large wikis.
36 $logIds = $dbr->newSelectQueryBuilder()
40 'log_type' => 'block',
41 'log_action' => 'unblock',
42 $dbr->expr( 'log_id', '>', $lastProcessedLogId ),
44 ->limit( $this->getBatchSize() )
45 ->orderBy( 'log_id', SelectQueryBuilder
::SORT_ASC
)
46 ->caller( __METHOD__
)
49 if ( count( $logIds ) ) {
50 $lastId = end( $logIds );
51 $firstId = reset( $logIds );
52 $this->output( "...Processing unblock rows with IDs $firstId to $lastId\n" );
54 // Apply the LIKE query to find the rows with broken log_title values on the batch of log IDs.
55 // If any rows are found, then fix the log_title value.
56 $matchingRows = $dbr->newSelectQueryBuilder()
57 ->select( [ 'log_id', 'log_title' ] )
62 new LikeValue( 'User:#', $dbr->anyString() )
64 ->andWhere( [ 'log_id' => $logIds ] )
65 ->limit( $this->getBatchSize() )
66 ->caller( __METHOD__
)
69 foreach ( $matchingRows as $row ) {
70 $dbw->newUpdateQueryBuilder()
72 ->set( [ 'log_title' => substr( $row->log_title
, strlen( 'User:' ) ) ] )
73 ->where( [ 'log_id' => $row->log_id
] )
74 ->caller( __METHOD__
)
76 $totalRowsFixed +
= $dbw->affectedRows();
79 $this->waitForReplication();
82 $lastProcessedLogId = end( $logIds );
83 } while ( count( $logIds ) );
85 // Say we are done. Only output the total rows fixed if we found rows to fix, otherwise it may be confusing
86 // to see that no rows were fixed (and might imply that there are still rows to fix).
87 $this->output( "done." );
88 if ( $totalRowsFixed ) {
89 $this->output( " Fixed $totalRowsFixed rows." );
91 $this->output( "\n" );
96 protected function getUpdateKey() {
101 // @codeCoverageIgnoreStart
102 $maintClass = FixAutoblockLogTitles
::class;
103 require_once RUN_MAINTENANCE_IF_MAIN
;
104 // @codeCoverageIgnoreEnd