]> scripts.mit.edu Git - autoinstalls/mediawiki.git/blob - maintenance/populateContentModel.php
MediaWiki 1.30.2-scripts
[autoinstalls/mediawiki.git] / maintenance / populateContentModel.php
1 <?php
2 /**
3  * Populate the page_content_model and {rev,ar}_content_{model,format} fields.
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 require_once __DIR__ . '/Maintenance.php';
25
26 use Wikimedia\Rdbms\IDatabase;
27 use MediaWiki\MediaWikiServices;
28
29 /**
30  * Usage:
31  *  populateContentModel.php --ns=1 --table=page
32  */
33 class PopulateContentModel extends Maintenance {
34         protected $wikiId;
35         /** @var WANObjectCache */
36         protected $wanCache;
37
38         public function __construct() {
39                 parent::__construct();
40                 $this->addDescription( 'Populate the various content_* fields' );
41                 $this->addOption( 'ns', 'Namespace to run in, or "all" for all namespaces', true, true );
42                 $this->addOption( 'table', 'Table to run in', true, true );
43                 $this->setBatchSize( 100 );
44         }
45
46         public function execute() {
47                 $dbw = $this->getDB( DB_MASTER );
48
49                 $this->wikiId = $dbw->getDomainID();
50                 $this->wanCache = MediaWikiServices::getInstance()->getMainWANObjectCache();
51
52                 $ns = $this->getOption( 'ns' );
53                 if ( !ctype_digit( $ns ) && $ns !== 'all' ) {
54                         $this->error( 'Invalid namespace', 1 );
55                 }
56                 $ns = $ns === 'all' ? 'all' : (int)$ns;
57                 $table = $this->getOption( 'table' );
58                 switch ( $table ) {
59                         case 'revision':
60                         case 'archive':
61                                 $this->populateRevisionOrArchive( $dbw, $table, $ns );
62                                 break;
63                         case 'page':
64                                 $this->populatePage( $dbw, $ns );
65                                 break;
66                         default:
67                                 $this->error( "Invalid table name: $table", 1 );
68                 }
69         }
70
71         protected function clearCache( $page_id, $rev_id ) {
72                 $contentModelKey = $this->wanCache->makeKey( 'page', 'content-model', $rev_id );
73                 $revisionKey =
74                         $this->wanCache->makeGlobalKey( 'revision', $this->wikiId, $page_id, $rev_id );
75
76                 // WikiPage content model cache
77                 $this->wanCache->delete( $contentModelKey );
78
79                 // Revision object cache, which contains a content model
80                 $this->wanCache->delete( $revisionKey );
81         }
82
83         private function updatePageRows( IDatabase $dbw, $pageIds, $model ) {
84                 $count = count( $pageIds );
85                 $this->output( "Setting $count rows to $model..." );
86                 $dbw->update(
87                         'page',
88                         [ 'page_content_model' => $model ],
89                         [ 'page_id' => $pageIds ],
90                         __METHOD__
91                 );
92                 wfWaitForSlaves();
93                 $this->output( "done.\n" );
94         }
95
96         protected function populatePage( IDatabase $dbw, $ns ) {
97                 $toSave = [];
98                 $lastId = 0;
99                 $nsCondition = $ns === 'all' ? [] : [ 'page_namespace' => $ns ];
100                 do {
101                         $rows = $dbw->select(
102                                 'page',
103                                 [ 'page_namespace', 'page_title', 'page_id' ],
104                                 [
105                                         'page_content_model' => null,
106                                         'page_id > ' . $dbw->addQuotes( $lastId ),
107                                 ] + $nsCondition,
108                                 __METHOD__,
109                                 [ 'LIMIT' => $this->mBatchSize, 'ORDER BY' => 'page_id ASC' ]
110                         );
111                         $this->output( "Fetched {$rows->numRows()} rows.\n" );
112                         foreach ( $rows as $row ) {
113                                 $title = Title::newFromRow( $row );
114                                 $model = ContentHandler::getDefaultModelFor( $title );
115                                 $toSave[$model][] = $row->page_id;
116                                 if ( count( $toSave[$model] ) >= $this->mBatchSize ) {
117                                         $this->updatePageRows( $dbw, $toSave[$model], $model );
118                                         unset( $toSave[$model] );
119                                 }
120                                 $lastId = $row->page_id;
121                         }
122                 } while ( $rows->numRows() >= $this->mBatchSize );
123                 foreach ( $toSave as $model => $pages ) {
124                         $this->updatePageRows( $dbw, $pages, $model );
125                 }
126         }
127
128         private function updateRevisionOrArchiveRows( IDatabase $dbw, $ids, $model, $table ) {
129                 $prefix = $table === 'archive' ? 'ar' : 'rev';
130                 $model_column = "{$prefix}_content_model";
131                 $format_column = "{$prefix}_content_format";
132                 $key = "{$prefix}_id";
133
134                 $count = count( $ids );
135                 $format = ContentHandler::getForModelID( $model )->getDefaultFormat();
136                 $this->output( "Setting $count rows to $model / $format..." );
137                 $dbw->update(
138                         $table,
139                         [ $model_column => $model, $format_column => $format ],
140                         [ $key => $ids ],
141                         __METHOD__
142                 );
143
144                 $this->output( "done.\n" );
145         }
146
147         protected function populateRevisionOrArchive( IDatabase $dbw, $table, $ns ) {
148                 $prefix = $table === 'archive' ? 'ar' : 'rev';
149                 $model_column = "{$prefix}_content_model";
150                 $format_column = "{$prefix}_content_format";
151                 $key = "{$prefix}_id";
152                 if ( $table === 'archive' ) {
153                         $selectTables = 'archive';
154                         $fields = [ 'ar_namespace', 'ar_title' ];
155                         $join_conds = [];
156                         $where = $ns === 'all' ? [] : [ 'ar_namespace' => $ns ];
157                         $page_id_column = 'ar_page_id';
158                         $rev_id_column = 'ar_rev_id';
159                 } else { // revision
160                         $selectTables = [ 'revision', 'page' ];
161                         $fields = [ 'page_title', 'page_namespace' ];
162                         $join_conds = [ 'page' => [ 'INNER JOIN', 'rev_page=page_id' ] ];
163                         $where = $ns === 'all' ? [] : [ 'page_namespace' => $ns ];
164                         $page_id_column = 'rev_page';
165                         $rev_id_column = 'rev_id';
166                 }
167
168                 $toSave = [];
169                 $idsToClear = [];
170                 $lastId = 0;
171                 do {
172                         $rows = $dbw->select(
173                                 $selectTables,
174                                 array_merge(
175                                         $fields,
176                                         [ $model_column, $format_column, $key, $page_id_column, $rev_id_column ]
177                                 ),
178                                 // @todo support populating format if model is already set
179                                 [
180                                         $model_column => null,
181                                         "$key > " . $dbw->addQuotes( $lastId ),
182                                 ] + $where,
183                                 __METHOD__,
184                                 [ 'LIMIT' => $this->mBatchSize, 'ORDER BY' => "$key ASC" ],
185                                 $join_conds
186                         );
187                         $this->output( "Fetched {$rows->numRows()} rows.\n" );
188                         foreach ( $rows as $row ) {
189                                 if ( $table === 'archive' ) {
190                                         $title = Title::makeTitle( $row->ar_namespace, $row->ar_title );
191                                 } else {
192                                         $title = Title::newFromRow( $row );
193                                 }
194                                 $lastId = $row->{$key};
195                                 try {
196                                         $handler = ContentHandler::getForTitle( $title );
197                                 } catch ( MWException $e ) {
198                                         $this->error( "Invalid content model for $title" );
199                                         continue;
200                                 }
201                                 $defaultModel = $handler->getModelID();
202                                 $defaultFormat = $handler->getDefaultFormat();
203                                 $dbModel = $row->{$model_column};
204                                 $dbFormat = $row->{$format_column};
205                                 $id = $row->{$key};
206                                 if ( $dbModel === null && $dbFormat === null ) {
207                                         // Set the defaults
208                                         $toSave[$defaultModel][] = $row->{$key};
209                                         $idsToClear[] = [
210                                                 'page_id' => $row->{$page_id_column},
211                                                 'rev_id' => $row->{$rev_id_column},
212                                         ];
213                                 } else { // $dbModel === null, $dbFormat set.
214                                         if ( $dbFormat === $defaultFormat ) {
215                                                 $toSave[$defaultModel][] = $row->{$key};
216                                                 $idsToClear[] = [
217                                                         'page_id' => $row->{$page_id_column},
218                                                         'rev_id' => $row->{$rev_id_column},
219                                                 ];
220                                         } else { // non-default format, just update now
221                                                 $this->output( "Updating model to match format for $table $id of $title... " );
222                                                 $dbw->update(
223                                                         $table,
224                                                         [ $model_column => $defaultModel ],
225                                                         [ $key => $id ],
226                                                         __METHOD__
227                                                 );
228                                                 wfWaitForSlaves();
229                                                 $this->clearCache( $row->{$page_id_column}, $row->{$rev_id_column} );
230                                                 $this->output( "done.\n" );
231                                                 continue;
232                                         }
233                                 }
234
235                                 if ( count( $toSave[$defaultModel] ) >= $this->mBatchSize ) {
236                                         $this->updateRevisionOrArchiveRows( $dbw, $toSave[$defaultModel], $defaultModel, $table );
237                                         unset( $toSave[$defaultModel] );
238                                 }
239                         }
240                 } while ( $rows->numRows() >= $this->mBatchSize );
241                 foreach ( $toSave as $model => $ids ) {
242                         $this->updateRevisionOrArchiveRows( $dbw, $ids, $model, $table );
243                 }
244
245                 foreach ( $idsToClear as $idPair ) {
246                         $this->clearCache( $idPair['page_id'], $idPair['rev_id'] );
247                 }
248         }
249 }
250
251 $maintClass = 'PopulateContentModel';
252 require_once RUN_MAINTENANCE_IF_MAIN;