af9006b05e63c0d251fb46ed5ffea483998d7c7e
[lhc/web/wiklou.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 * @ingroup Maintenance
22 */
23
24 require_once( dirname( __FILE__ ) . '/Maintenance.php' );
25
26 class PopulateRevisionSha1 extends LoggedUpdateMaintenance {
27 public function __construct() {
28 parent::__construct();
29 $this->mDescription = "Populates the rev_sha1 and ar_sha1 fields";
30 $this->setBatchSize( 200 );
31 }
32
33 protected function getUpdateKey() {
34 return 'populate rev_sha1';
35 }
36
37 protected function doDBUpdates() {
38 $db = $this->getDB( DB_MASTER );
39
40 if ( !$db->tableExists( 'revision' ) ) {
41 $this->error( "revision table does not exist", true );
42 } elseif ( !$db->tableExists( 'archive' ) ) {
43 $this->error( "archive table does not exist", true );
44 }
45
46 $this->output( "Populating rev_sha1 column\n" );
47 $rc = $this->doSha1Updates( 'revision', 'rev_id', 'rev' );
48
49 $this->output( "Populating ar_sha1 column\n" );
50 $ac = $this->doSha1Updates( 'archive', 'ar_rev_id', 'ar' );
51 $this->output( "Populating ar_sha1 column legacy rows\n" );
52 $ac += $this->doSha1LegacyUpdates();
53
54 $this->output( "rev_sha1 and ar_sha1 population complete [$rc revision rows, $ac archive rows].\n" );
55 return true;
56 }
57
58 /**
59 * @param $table string
60 * @param $idCol
61 * @param $prefix string
62 * @return Integer Rows changed
63 */
64 protected function doSha1Updates( $table, $idCol, $prefix ) {
65 $db = $this->getDB( DB_MASTER );
66 $start = $db->selectField( $table, "MIN($idCol)", false, __METHOD__ );
67 $end = $db->selectField( $table, "MAX($idCol)", false, __METHOD__ );
68 if ( !$start || !$end ) {
69 $this->output( "...$table table seems to be empty.\n" );
70 return 0;
71 }
72
73 $count = 0;
74 # Do remaining chunk
75 $end += $this->mBatchSize - 1;
76 $blockStart = $start;
77 $blockEnd = $start + $this->mBatchSize - 1;
78 while ( $blockEnd <= $end ) {
79 $this->output( "...doing $idCol from $blockStart to $blockEnd\n" );
80 $cond = "$idCol BETWEEN $blockStart AND $blockEnd
81 AND $idCol IS NOT NULL AND {$prefix}_sha1 = ''";
82 $res = $db->select( $table, '*', $cond, __METHOD__ );
83
84 $db->begin( __METHOD__ );
85 foreach ( $res as $row ) {
86 if ( $this->upgradeRow( $row, $table, $idCol, $prefix ) ) {
87 $count++;
88 }
89 }
90 $db->commit( __METHOD__ );
91
92 $blockStart += $this->mBatchSize;
93 $blockEnd += $this->mBatchSize;
94 wfWaitForSlaves();
95 }
96 return $count;
97 }
98
99 /**
100 * @return int
101 */
102 protected function doSha1LegacyUpdates() {
103 $count = 0;
104 $db = $this->getDB( DB_MASTER );
105 $res = $db->select( 'archive', '*',
106 array( 'ar_rev_id IS NULL', 'ar_sha1' => '' ), __METHOD__ );
107
108 $updateSize = 0;
109 $db->begin( __METHOD__ );
110 foreach ( $res as $row ) {
111 if ( $this->upgradeLegacyArchiveRow( $row ) ) {
112 ++$count;
113 }
114 if ( ++$updateSize >= 100 ) {
115 $updateSize = 0;
116 $db->commit( __METHOD__ );
117 $this->output( "Commited row with ar_timestamp={$row->ar_timestamp}\n" );
118 wfWaitForSlaves();
119 $db->begin( __METHOD__ );
120 }
121 }
122 $db->commit( __METHOD__ );
123 return $count;
124 }
125
126 /**
127 * @param $row
128 * @param $table
129 * @param $idCol
130 * @param $prefix
131 * @return bool
132 */
133 protected function upgradeRow( $row, $table, $idCol, $prefix ) {
134 $db = $this->getDB( DB_MASTER );
135 try {
136 $rev = ( $table === 'archive' )
137 ? Revision::newFromArchiveRow( $row )
138 : new Revision( $row );
139 $text = $rev->getSerializedData();
140 } catch ( MWException $e ) {
141 $this->output( "Data of revision with {$idCol}={$row->$idCol} unavailable!\n" );
142 return false; // bug 22624?
143 }
144 if ( !is_string( $text ) ) {
145 # This should not happen, but sometimes does (bug 20757)
146 $this->output( "Data of revision with {$idCol}={$row->$idCol} unavailable!\n" );
147 return false;
148 } else {
149 $db->update( $table,
150 array( "{$prefix}_sha1" => Revision::base36Sha1( $text ) ),
151 array( $idCol => $row->$idCol ),
152 __METHOD__
153 );
154 return true;
155 }
156 }
157
158 /**
159 * @param $row
160 * @return bool
161 */
162 protected function upgradeLegacyArchiveRow( $row ) {
163 $db = $this->getDB( DB_MASTER );
164 try {
165 $rev = Revision::newFromArchiveRow( $row );
166 } catch ( MWException $e ) {
167 $this->output( "Text of revision with timestamp {$row->ar_timestamp} unavailable!\n" );
168 return false; // bug 22624?
169 }
170 $text = $rev->getSerializedData();
171 if ( !is_string( $text ) ) {
172 # This should not happen, but sometimes does (bug 20757)
173 $this->output( "Data of revision with timestamp {$row->ar_timestamp} unavailable!\n" );
174 return false;
175 } else {
176 # Archive table as no PK, but (NS,title,time) should be near unique.
177 # Any duplicates on those should also have duplicated text anyway.
178 $db->update( 'archive',
179 array( 'ar_sha1' => Revision::base36Sha1( $text ) ),
180 array(
181 'ar_namespace' => $row->ar_namespace,
182 'ar_title' => $row->ar_title,
183 'ar_timestamp' => $row->ar_timestamp,
184 'ar_len' => $row->ar_len // extra sanity
185 ),
186 __METHOD__
187 );
188 return true;
189 }
190 }
191 }
192
193 $maintClass = "PopulateRevisionSha1";
194 require_once( RUN_MAINTENANCE_IF_MAIN );