Merge "Upgrade wikimedia/remex-html to 2.0.1" into REL1_31
[lhc/web/wiklou.git] / maintenance / populateArchiveRevId.php
1 <?php
2 /**
3 * Populate ar_rev_id in pre-1.5 rows
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * http://www.gnu.org/copyleft/gpl.html
19 *
20 * @file
21 * @ingroup Maintenance
22 */
23
24 use Wikimedia\Rdbms\IDatabase;
25
26 require_once __DIR__ . '/Maintenance.php';
27
28 /**
29 * Maintenance script that populares archive.ar_rev_id in old rows
30 *
31 * @ingroup Maintenance
32 * @since 1.31
33 */
34 class PopulateArchiveRevId extends LoggedUpdateMaintenance {
35 public function __construct() {
36 parent::__construct();
37 $this->addDescription( 'Populate ar_rev_id in pre-1.5 rows' );
38 $this->setBatchSize( 100 );
39 }
40
41 protected function getUpdateKey() {
42 return __CLASS__;
43 }
44
45 protected function doDBUpdates() {
46 $this->output( "Populating ar_rev_id...\n" );
47 $dbw = $this->getDB( DB_MASTER );
48
49 // Quick exit if there are no rows needing updates.
50 $any = $dbw->selectField(
51 'archive',
52 'ar_id',
53 [ 'ar_rev_id' => null ],
54 __METHOD__
55 );
56 if ( !$any ) {
57 $this->output( "Completed ar_rev_id population, 0 rows updated.\n" );
58 return true;
59 }
60
61 $rev = $this->makeDummyRevisionRow( $dbw );
62 $count = 0;
63 while ( true ) {
64 wfWaitForSlaves();
65
66 $arIds = $dbw->selectFieldValues(
67 'archive',
68 'ar_id',
69 [ 'ar_rev_id' => null ],
70 __METHOD__,
71 [ 'LIMIT' => $this->getBatchSize(), 'ORDER BY' => [ 'ar_id' ] ]
72 );
73 if ( !$arIds ) {
74 $this->output( "Completed ar_rev_id population, $count rows updated.\n" );
75 return true;
76 }
77
78 try {
79 $updates = $dbw->doAtomicSection( __METHOD__, function ( $dbw, $fname ) use ( $arIds, $rev ) {
80 // Create new rev_ids by inserting dummy rows into revision and then deleting them.
81 $dbw->insert( 'revision', array_fill( 0, count( $arIds ), $rev ), $fname );
82 $revIds = $dbw->selectFieldValues(
83 'revision',
84 'rev_id',
85 [ 'rev_timestamp' => $rev['rev_timestamp'] ],
86 $fname
87 );
88 if ( !is_array( $revIds ) ) {
89 throw new UnexpectedValueException( 'Failed to insert dummy revisions' );
90 }
91 if ( count( $revIds ) !== count( $arIds ) ) {
92 throw new UnexpectedValueException(
93 'Tried to insert ' . count( $arIds ) . ' dummy revisions, but found '
94 . count( $revIds ) . ' matching rows.'
95 );
96 }
97 $dbw->delete( 'revision', [ 'rev_id' => $revIds ], $fname );
98
99 return array_combine( $arIds, $revIds );
100 } );
101 } catch ( UnexpectedValueException $ex ) {
102 $this->fatalError( $ex->getMessage() );
103 }
104
105 foreach ( $updates as $arId => $revId ) {
106 $dbw->update(
107 'archive',
108 [ 'ar_rev_id' => $revId ],
109 [ 'ar_id' => $arId, 'ar_rev_id' => null ],
110 __METHOD__
111 );
112 $count += $dbw->affectedRows();
113 }
114
115 $min = min( array_keys( $updates ) );
116 $max = max( array_keys( $updates ) );
117 $this->output( " ... $min-$max\n" );
118 }
119 }
120
121 /**
122 * Construct a dummy revision table row to use for reserving IDs
123 *
124 * The row will have a wildly unlikely timestamp, and possibly a generic
125 * user and comment, but will otherwise be derived from a revision on the
126 * wiki's main page.
127 *
128 * @param IDatabase $dbw
129 * @return array
130 */
131 private function makeDummyRevisionRow( IDatabase $dbw ) {
132 $ts = $dbw->timestamp( '11111111111111' );
133 $mainPage = Title::newMainPage();
134 if ( !$mainPage ) {
135 $this->fatalError( 'Main page does not exist' );
136 }
137 $pageId = $mainPage->getArticleId();
138 if ( !$pageId ) {
139 $this->fatalError( $mainPage->getPrefixedText() . ' has no ID' );
140 }
141 $rev = $dbw->selectRow(
142 'revision',
143 '*',
144 [ 'rev_page' => $pageId ],
145 __METHOD__,
146 [ 'ORDER BY' => 'rev_timestamp ASC' ]
147 );
148 if ( !$rev ) {
149 $this->fatalError( $mainPage->getPrefixedText() . ' has no revisions' );
150 }
151 unset( $rev->rev_id );
152 $rev = (array)$rev;
153 $rev['rev_timestamp'] = $ts;
154 if ( isset( $rev['rev_user'] ) ) {
155 $rev['rev_user'] = 0;
156 $rev['rev_user_text'] = '0.0.0.0';
157 }
158 if ( isset( $rev['rev_comment'] ) ) {
159 $rev['rev_comment'] = 'Dummy row';
160 }
161
162 $any = $dbw->selectField(
163 'revision',
164 'rev_id',
165 [ 'rev_timestamp' => $ts ],
166 __METHOD__
167 );
168 if ( $any ) {
169 $this->fatalError( "... Why does your database contain a revision dated $ts?" );
170 }
171
172 return $rev;
173 }
174 }
175
176 $maintClass = "PopulateArchiveRevId";
177 require_once RUN_MAINTENANCE_IF_MAIN;