Merge ".mailmap: Correct two contributor names"
[mediawiki.git] / maintenance / fixAutoblockLogTitles.php
blobd2fcca74cf6350b3ebb2cccfadcf1093b698bba5
1 <?php
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();
31 $totalRowsFixed = 0;
32 $lastProcessedLogId = 0;
33 do {
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()
37 ->select( 'log_id' )
38 ->from( 'logging' )
39 ->where( [
40 'log_type' => 'block',
41 'log_action' => 'unblock',
42 $dbr->expr( 'log_id', '>', $lastProcessedLogId ),
43 ] )
44 ->limit( $this->getBatchSize() )
45 ->orderBy( 'log_id', SelectQueryBuilder::SORT_ASC )
46 ->caller( __METHOD__ )
47 ->fetchFieldValues();
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' ] )
58 ->from( 'logging' )
59 ->where( $dbr->expr(
60 'log_title',
61 IExpression::LIKE,
62 new LikeValue( 'User:#', $dbr->anyString() )
63 ) )
64 ->andWhere( [ 'log_id' => $logIds ] )
65 ->limit( $this->getBatchSize() )
66 ->caller( __METHOD__ )
67 ->fetchResultSet();
69 foreach ( $matchingRows as $row ) {
70 $dbw->newUpdateQueryBuilder()
71 ->update( 'logging' )
72 ->set( [ 'log_title' => substr( $row->log_title, strlen( 'User:' ) ) ] )
73 ->where( [ 'log_id' => $row->log_id ] )
74 ->caller( __METHOD__ )
75 ->execute();
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" );
93 return true;
96 protected function getUpdateKey() {
97 return __CLASS__;
101 // @codeCoverageIgnoreStart
102 $maintClass = FixAutoblockLogTitles::class;
103 require_once RUN_MAINTENANCE_IF_MAIN;
104 // @codeCoverageIgnoreEnd