MediaWiki  master
fixMergeHistoryCorruption.php
Go to the documentation of this file.
1 <?php
22 require_once __DIR__ . '/Maintenance.php';
23 
40 
41  public function __construct() {
42  parent::__construct();
43  $this->addDescription( 'Delete pages corrupted by MergeHistory' );
44  $this->addOption( 'ns', 'Namespace to restrict the query', false, true );
45  $this->addOption( 'dry-run', 'Run in dry-mode' );
46  $this->addOption( 'delete', 'Actually delete the found rows' );
47  }
48 
49  public function execute() {
50  $dbr = $this->getDB( DB_REPLICA );
51  $dbw = $this->getDB( DB_PRIMARY );
52 
53  $dryRun = true;
54  if ( $this->hasOption( 'dry-run' ) && $this->hasOption( 'delete' ) ) {
55  $this->fatalError( 'Cannot do both --dry-run and --delete.' );
56  } elseif ( $this->hasOption( 'delete' ) ) {
57  $dryRun = false;
58  } elseif ( !$this->hasOption( 'dry-run' ) ) {
59  $this->fatalError( 'Either --dry-run or --delete must be specified.' );
60  }
61 
62  $conds = [ 'page_id<>rev_page' ];
63  if ( $this->hasOption( 'ns' ) ) {
64  $conds['page_namespace'] = (int)$this->getOption( 'ns' );
65  }
66 
67  $res = $dbr->newSelectQueryBuilder()
68  ->from( 'page' )
69  ->join( 'revision', null, 'page_latest=rev_id' )
70  ->fields( [ 'page_namespace', 'page_title', 'page_id' ] )
71  ->where( $conds )
72  ->caller( __METHOD__ )
73  ->fetchResultSet();
74 
75  $count = $res->numRows();
76 
77  if ( !$count ) {
78  $this->output( "Nothing was found, no page matches the criteria.\n" );
79  return;
80  }
81 
82  $numDeleted = 0;
83  $numUpdated = 0;
84 
85  foreach ( $res as $row ) {
86  $title = Title::makeTitleSafe( $row->page_namespace, $row->page_title );
87  if ( !$title ) {
88  $this->output( "Skipping invalid title with page_id: $row->page_id\n" );
89  continue;
90  }
91  $titleText = $title->getPrefixedDBkey();
92 
93  // Check if there are any revisions that have this $row->page_id as their
94  // rev_page and select the largest which should be the newest revision.
95  $revId = $dbr->selectField(
96  'revision',
97  'MAX(rev_id)',
98  [ 'rev_page' => $row->page_id ],
99  __METHOD__
100  );
101 
102  if ( !$revId ) {
103  if ( $dryRun ) {
104  $this->output( "Would delete $titleText with page_id: $row->page_id\n" );
105  } else {
106  $this->output( "Deleting $titleText with page_id: $row->page_id\n" );
107  $dbw->delete( 'page', [ 'page_id' => $row->page_id ], __METHOD__ );
108  }
109  $numDeleted++;
110  } else {
111  if ( $dryRun ) {
112  $this->output( "Would update page_id $row->page_id to page_latest $revId\n" );
113  } else {
114  $this->output( "Updating page_id $row->page_id to page_latest $revId\n" );
115  $dbw->update(
116  'page',
117  [ 'page_latest' => $revId ],
118  [ 'page_id' => $row->page_id ],
119  __METHOD__
120  );
121  }
122  $numUpdated++;
123  }
124  }
125 
126  if ( !$dryRun ) {
127  $this->output( "Updated $numUpdated row(s), deleted $numDeleted row(s)\n" );
128  }
129  }
130 }
131 
132 $maintClass = FixMergeHistoryCorruption::class;
133 require_once RUN_MAINTENANCE_IF_MAIN;
Maintenance script that clears rows of pages corrupted by MergeHistory, those pages 'exist' but have ...
Abstract maintenance class for quickly writing and churning out maintenance scripts with minimal effo...
Definition: Maintenance.php:66
getDB( $db, $groups=[], $dbDomain=false)
Returns a database to be used by current maintenance script.
output( $out, $channel=null)
Throw some output to the user.
hasOption( $name)
Checks to see if a particular option was set.
addDescription( $text)
Set the description text.
addOption( $name, $description, $required=false, $withArg=false, $shortName=false, $multiOccurrence=false)
Add a parameter to the script.
getOption( $name, $default=null)
Get an option, or return the default.
fatalError( $msg, $exitCode=1)
Output a message and terminate the current script.
static makeTitleSafe( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:664
const DB_REPLICA
Definition: defines.php:26
const DB_PRIMARY
Definition: defines.php:28