]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blob - maintenance/populateRevisionSha1.php
MediaWiki 1.30.2
[autoinstallsdev/mediawiki.git] / maintenance / populateRevisionSha1.php
1 <?php
2 /**
3  * Fills the rev_sha1 and ar_sha1 columns of revision
4  * and archive tables for revisions created before MW 1.19.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  * http://www.gnu.org/copyleft/gpl.html
20  *
21  * @file
22  * @ingroup Maintenance
23  */
24
25 require_once __DIR__ . '/Maintenance.php';
26
27 /**
28  * Maintenance script that fills the rev_sha1 and ar_sha1 columns of revision
29  * and archive tables for revisions created before MW 1.19.
30  *
31  * @ingroup Maintenance
32  */
33 class PopulateRevisionSha1 extends LoggedUpdateMaintenance {
34         public function __construct() {
35                 parent::__construct();
36                 $this->addDescription( 'Populates the rev_sha1 and ar_sha1 fields' );
37                 $this->setBatchSize( 200 );
38         }
39
40         protected function getUpdateKey() {
41                 return 'populate rev_sha1';
42         }
43
44         protected function doDBUpdates() {
45                 $db = $this->getDB( DB_MASTER );
46
47                 if ( !$db->tableExists( 'revision' ) ) {
48                         $this->error( "revision table does not exist", true );
49                 } elseif ( !$db->tableExists( 'archive' ) ) {
50                         $this->error( "archive table does not exist", true );
51                 } elseif ( !$db->fieldExists( 'revision', 'rev_sha1', __METHOD__ ) ) {
52                         $this->output( "rev_sha1 column does not exist\n\n", true );
53
54                         return false;
55                 }
56
57                 $this->output( "Populating rev_sha1 column\n" );
58                 $rc = $this->doSha1Updates( 'revision', 'rev_id', 'rev' );
59
60                 $this->output( "Populating ar_sha1 column\n" );
61                 $ac = $this->doSha1Updates( 'archive', 'ar_rev_id', 'ar' );
62                 $this->output( "Populating ar_sha1 column legacy rows\n" );
63                 $ac += $this->doSha1LegacyUpdates();
64
65                 $this->output( "rev_sha1 and ar_sha1 population complete "
66                         . "[$rc revision rows, $ac archive rows].\n" );
67
68                 return true;
69         }
70
71         /**
72          * @param string $table
73          * @param string $idCol
74          * @param string $prefix
75          * @return int Rows changed
76          */
77         protected function doSha1Updates( $table, $idCol, $prefix ) {
78                 $db = $this->getDB( DB_MASTER );
79                 $start = $db->selectField( $table, "MIN($idCol)", false, __METHOD__ );
80                 $end = $db->selectField( $table, "MAX($idCol)", false, __METHOD__ );
81                 if ( !$start || !$end ) {
82                         $this->output( "...$table table seems to be empty.\n" );
83
84                         return 0;
85                 }
86
87                 $count = 0;
88                 # Do remaining chunk
89                 $end += $this->mBatchSize - 1;
90                 $blockStart = $start;
91                 $blockEnd = $start + $this->mBatchSize - 1;
92                 while ( $blockEnd <= $end ) {
93                         $this->output( "...doing $idCol from $blockStart to $blockEnd\n" );
94                         $cond = "$idCol BETWEEN $blockStart AND $blockEnd
95                                 AND $idCol IS NOT NULL AND {$prefix}_sha1 = ''";
96                         $res = $db->select( $table, '*', $cond, __METHOD__ );
97
98                         $this->beginTransaction( $db, __METHOD__ );
99                         foreach ( $res as $row ) {
100                                 if ( $this->upgradeRow( $row, $table, $idCol, $prefix ) ) {
101                                         $count++;
102                                 }
103                         }
104                         $this->commitTransaction( $db, __METHOD__ );
105
106                         $blockStart += $this->mBatchSize;
107                         $blockEnd += $this->mBatchSize;
108                         wfWaitForSlaves();
109                 }
110
111                 return $count;
112         }
113
114         /**
115          * @return int
116          */
117         protected function doSha1LegacyUpdates() {
118                 $count = 0;
119                 $db = $this->getDB( DB_MASTER );
120                 $res = $db->select( 'archive', '*',
121                         [ 'ar_rev_id IS NULL', 'ar_sha1' => '' ], __METHOD__ );
122
123                 $updateSize = 0;
124                 $this->beginTransaction( $db, __METHOD__ );
125                 foreach ( $res as $row ) {
126                         if ( $this->upgradeLegacyArchiveRow( $row ) ) {
127                                 ++$count;
128                         }
129                         if ( ++$updateSize >= 100 ) {
130                                 $updateSize = 0;
131                                 $this->commitTransaction( $db, __METHOD__ );
132                                 $this->output( "Commited row with ar_timestamp={$row->ar_timestamp}\n" );
133                                 wfWaitForSlaves();
134                                 $this->beginTransaction( $db, __METHOD__ );
135                         }
136                 }
137                 $this->commitTransaction( $db, __METHOD__ );
138
139                 return $count;
140         }
141
142         /**
143          * @param stdClass $row
144          * @param string $table
145          * @param string $idCol
146          * @param string $prefix
147          * @return bool
148          */
149         protected function upgradeRow( $row, $table, $idCol, $prefix ) {
150                 $db = $this->getDB( DB_MASTER );
151                 try {
152                         $rev = ( $table === 'archive' )
153                                 ? Revision::newFromArchiveRow( $row )
154                                 : new Revision( $row );
155                         $text = $rev->getSerializedData();
156                 } catch ( Exception $e ) {
157                         $this->output( "Data of revision with {$idCol}={$row->$idCol} unavailable!\n" );
158
159                         return false; // T24624?
160                 }
161                 if ( !is_string( $text ) ) {
162                         # This should not happen, but sometimes does (T22757)
163                         $this->output( "Data of revision with {$idCol}={$row->$idCol} unavailable!\n" );
164
165                         return false;
166                 } else {
167                         $db->update( $table,
168                                 [ "{$prefix}_sha1" => Revision::base36Sha1( $text ) ],
169                                 [ $idCol => $row->$idCol ],
170                                 __METHOD__
171                         );
172
173                         return true;
174                 }
175         }
176
177         /**
178          * @param stdClass $row
179          * @return bool
180          */
181         protected function upgradeLegacyArchiveRow( $row ) {
182                 $db = $this->getDB( DB_MASTER );
183                 try {
184                         $rev = Revision::newFromArchiveRow( $row );
185                 } catch ( Exception $e ) {
186                         $this->output( "Text of revision with timestamp {$row->ar_timestamp} unavailable!\n" );
187
188                         return false; // T24624?
189                 }
190                 $text = $rev->getSerializedData();
191                 if ( !is_string( $text ) ) {
192                         # This should not happen, but sometimes does (T22757)
193                         $this->output( "Data of revision with timestamp {$row->ar_timestamp} unavailable!\n" );
194
195                         return false;
196                 } else {
197                         # Archive table as no PK, but (NS,title,time) should be near unique.
198                         # Any duplicates on those should also have duplicated text anyway.
199                         $db->update( 'archive',
200                                 [ 'ar_sha1' => Revision::base36Sha1( $text ) ],
201                                 [
202                                         'ar_namespace' => $row->ar_namespace,
203                                         'ar_title' => $row->ar_title,
204                                         'ar_timestamp' => $row->ar_timestamp,
205                                         'ar_len' => $row->ar_len // extra sanity
206                                 ],
207                                 __METHOD__
208                         );
209
210                         return true;
211                 }
212         }
213 }
214
215 $maintClass = "PopulateRevisionSha1";
216 require_once RUN_MAINTENANCE_IF_MAIN;