Completed
Branch master (939199)
by
unknown
39:35
created

includes/installer/MysqlUpdater.php (3 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * MySQL-specific updater.
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 Deployment
22
 */
23
24
/**
25
 * Mysql update list and mysql-specific update functions.
26
 *
27
 * @ingroup Deployment
28
 * @since 1.17
29
 */
30
class MysqlUpdater extends DatabaseUpdater {
31
	protected function getCoreUpdateList() {
32
		return [
33
			[ 'disableContentHandlerUseDB' ],
34
35
			// 1.2
36
			[ 'addField', 'ipblocks', 'ipb_id', 'patch-ipblocks.sql' ],
37
			[ 'addField', 'ipblocks', 'ipb_expiry', 'patch-ipb_expiry.sql' ],
38
			[ 'doInterwikiUpdate' ],
39
			[ 'doIndexUpdate' ],
40
			[ 'addField', 'recentchanges', 'rc_type', 'patch-rc_type.sql' ],
41
			[ 'addIndex', 'recentchanges', 'new_name_timestamp', 'patch-rc-newindex.sql' ],
42
43
			// 1.3
44
			[ 'addField', 'user', 'user_real_name', 'patch-user-realname.sql' ],
45
			[ 'addTable', 'querycache', 'patch-querycache.sql' ],
46
			[ 'addTable', 'objectcache', 'patch-objectcache.sql' ],
47
			[ 'addTable', 'categorylinks', 'patch-categorylinks.sql' ],
48
			[ 'doOldLinksUpdate' ],
49
			[ 'doFixAncientImagelinks' ],
50
			[ 'addField', 'recentchanges', 'rc_ip', 'patch-rc_ip.sql' ],
51
52
			// 1.4
53
			[ 'addIndex', 'image', 'PRIMARY', 'patch-image_name_primary.sql' ],
54
			[ 'addField', 'recentchanges', 'rc_id', 'patch-rc_id.sql' ],
55
			[ 'addField', 'recentchanges', 'rc_patrolled', 'patch-rc-patrol.sql' ],
56
			[ 'addTable', 'logging', 'patch-logging.sql' ],
57
			[ 'addField', 'user', 'user_token', 'patch-user_token.sql' ],
58
			[ 'addField', 'watchlist', 'wl_notificationtimestamp', 'patch-email-notification.sql' ],
59
			[ 'doWatchlistUpdate' ],
60
			[ 'dropField', 'user', 'user_emailauthenticationtimestamp',
61
				'patch-email-authentication.sql' ],
62
63
			// 1.5
64
			[ 'doSchemaRestructuring' ],
65
			[ 'addField', 'logging', 'log_params', 'patch-log_params.sql' ],
66
			[ 'checkBin', 'logging', 'log_title', 'patch-logging-title.sql', ],
67
			[ 'addField', 'archive', 'ar_rev_id', 'patch-archive-rev_id.sql' ],
68
			[ 'addField', 'page', 'page_len', 'patch-page_len.sql' ],
69
			[ 'dropField', 'revision', 'inverse_timestamp', 'patch-inverse_timestamp.sql' ],
70
			[ 'addField', 'revision', 'rev_text_id', 'patch-rev_text_id.sql' ],
71
			[ 'addField', 'revision', 'rev_deleted', 'patch-rev_deleted.sql' ],
72
			[ 'addField', 'image', 'img_width', 'patch-img_width.sql' ],
73
			[ 'addField', 'image', 'img_metadata', 'patch-img_metadata.sql' ],
74
			[ 'addField', 'user', 'user_email_token', 'patch-user_email_token.sql' ],
75
			[ 'addField', 'archive', 'ar_text_id', 'patch-archive-text_id.sql' ],
76
			[ 'doNamespaceSize' ],
77
			[ 'addField', 'image', 'img_media_type', 'patch-img_media_type.sql' ],
78
			[ 'doPagelinksUpdate' ],
79
			[ 'dropField', 'image', 'img_type', 'patch-drop_img_type.sql' ],
80
			[ 'doUserUniqueUpdate' ],
81
			[ 'doUserGroupsUpdate' ],
82
			[ 'addField', 'site_stats', 'ss_total_pages', 'patch-ss_total_articles.sql' ],
83
			[ 'addTable', 'user_newtalk', 'patch-usernewtalk2.sql' ],
84
			[ 'addTable', 'transcache', 'patch-transcache.sql' ],
85
			[ 'addField', 'interwiki', 'iw_trans', 'patch-interwiki-trans.sql' ],
86
87
			// 1.6
88
			[ 'doWatchlistNull' ],
89
			[ 'addIndex', 'logging', 'times', 'patch-logging-times-index.sql' ],
90
			[ 'addField', 'ipblocks', 'ipb_range_start', 'patch-ipb_range_start.sql' ],
91
			[ 'doPageRandomUpdate' ],
92
			[ 'addField', 'user', 'user_registration', 'patch-user_registration.sql' ],
93
			[ 'doTemplatelinksUpdate' ],
94
			[ 'addTable', 'externallinks', 'patch-externallinks.sql' ],
95
			[ 'addTable', 'job', 'patch-job.sql' ],
96
			[ 'addField', 'site_stats', 'ss_images', 'patch-ss_images.sql' ],
97
			[ 'addTable', 'langlinks', 'patch-langlinks.sql' ],
98
			[ 'addTable', 'querycache_info', 'patch-querycacheinfo.sql' ],
99
			[ 'addTable', 'filearchive', 'patch-filearchive.sql' ],
100
			[ 'addField', 'ipblocks', 'ipb_anon_only', 'patch-ipb_anon_only.sql' ],
101
			[ 'addIndex', 'recentchanges', 'rc_ns_usertext', 'patch-recentchanges-utindex.sql' ],
102
			[ 'addIndex', 'recentchanges', 'rc_user_text', 'patch-rc_user_text-index.sql' ],
103
104
			// 1.9
105
			[ 'addField', 'user', 'user_newpass_time', 'patch-user_newpass_time.sql' ],
106
			[ 'addTable', 'redirect', 'patch-redirect.sql' ],
107
			[ 'addTable', 'querycachetwo', 'patch-querycachetwo.sql' ],
108
			[ 'addField', 'ipblocks', 'ipb_enable_autoblock', 'patch-ipb_optional_autoblock.sql' ],
109
			[ 'doBacklinkingIndicesUpdate' ],
110
			[ 'addField', 'recentchanges', 'rc_old_len', 'patch-rc_len.sql' ],
111
			[ 'addField', 'user', 'user_editcount', 'patch-user_editcount.sql' ],
112
113
			// 1.10
114
			[ 'doRestrictionsUpdate' ],
115
			[ 'addField', 'logging', 'log_id', 'patch-log_id.sql' ],
116
			[ 'addField', 'revision', 'rev_parent_id', 'patch-rev_parent_id.sql' ],
117
			[ 'addField', 'page_restrictions', 'pr_id', 'patch-page_restrictions_sortkey.sql' ],
118
			[ 'addField', 'revision', 'rev_len', 'patch-rev_len.sql' ],
119
			[ 'addField', 'recentchanges', 'rc_deleted', 'patch-rc_deleted.sql' ],
120
			[ 'addField', 'logging', 'log_deleted', 'patch-log_deleted.sql' ],
121
			[ 'addField', 'archive', 'ar_deleted', 'patch-ar_deleted.sql' ],
122
			[ 'addField', 'ipblocks', 'ipb_deleted', 'patch-ipb_deleted.sql' ],
123
			[ 'addField', 'filearchive', 'fa_deleted', 'patch-fa_deleted.sql' ],
124
			[ 'addField', 'archive', 'ar_len', 'patch-ar_len.sql' ],
125
126
			// 1.11
127
			[ 'addField', 'ipblocks', 'ipb_block_email', 'patch-ipb_emailban.sql' ],
128
			[ 'doCategorylinksIndicesUpdate' ],
129
			[ 'addField', 'oldimage', 'oi_metadata', 'patch-oi_metadata.sql' ],
130
			[ 'addIndex', 'archive', 'usertext_timestamp', 'patch-archive-user-index.sql' ],
131
			[ 'addIndex', 'image', 'img_usertext_timestamp', 'patch-image-user-index.sql' ],
132
			[ 'addIndex', 'oldimage', 'oi_usertext_timestamp', 'patch-oldimage-user-index.sql' ],
133
			[ 'addField', 'archive', 'ar_page_id', 'patch-archive-page_id.sql' ],
134
			[ 'addField', 'image', 'img_sha1', 'patch-img_sha1.sql' ],
135
136
			// 1.12
137
			[ 'addTable', 'protected_titles', 'patch-protected_titles.sql' ],
138
139
			// 1.13
140
			[ 'addField', 'ipblocks', 'ipb_by_text', 'patch-ipb_by_text.sql' ],
141
			[ 'addTable', 'page_props', 'patch-page_props.sql' ],
142
			[ 'addTable', 'updatelog', 'patch-updatelog.sql' ],
143
			[ 'addTable', 'category', 'patch-category.sql' ],
144
			[ 'doCategoryPopulation' ],
145
			[ 'addField', 'archive', 'ar_parent_id', 'patch-ar_parent_id.sql' ],
146
			[ 'addField', 'user_newtalk', 'user_last_timestamp', 'patch-user_last_timestamp.sql' ],
147
			[ 'doPopulateParentId' ],
148
			[ 'checkBin', 'protected_titles', 'pt_title', 'patch-pt_title-encoding.sql', ],
149
			[ 'doMaybeProfilingMemoryUpdate' ],
150
			[ 'doFilearchiveIndicesUpdate' ],
151
152
			// 1.14
153
			[ 'addField', 'site_stats', 'ss_active_users', 'patch-ss_active_users.sql' ],
154
			[ 'doActiveUsersInit' ],
155
			[ 'addField', 'ipblocks', 'ipb_allow_usertalk', 'patch-ipb_allow_usertalk.sql' ],
156
157
			// 1.15
158
			[ 'addTable', 'change_tag', 'patch-change_tag.sql' ],
159
			[ 'addTable', 'tag_summary', 'patch-tag_summary.sql' ],
160
			[ 'addTable', 'valid_tag', 'patch-valid_tag.sql' ],
161
162
			// 1.16
163
			[ 'addTable', 'user_properties', 'patch-user_properties.sql' ],
164
			[ 'addTable', 'log_search', 'patch-log_search.sql' ],
165
			[ 'addField', 'logging', 'log_user_text', 'patch-log_user_text.sql' ],
166
			# listed separately from the previous update because 1.16 was released without this update
167
			[ 'doLogUsertextPopulation' ],
168
			[ 'doLogSearchPopulation' ],
169
			[ 'addTable', 'l10n_cache', 'patch-l10n_cache.sql' ],
170
			[ 'addIndex', 'log_search', 'ls_field_val', 'patch-log_search-rename-index.sql' ],
171
			[ 'addIndex', 'change_tag', 'change_tag_rc_tag', 'patch-change_tag-indexes.sql' ],
172
			[ 'addField', 'redirect', 'rd_interwiki', 'patch-rd_interwiki.sql' ],
173
			[ 'doUpdateTranscacheField' ],
174
			[ 'doUpdateMimeMinorField' ],
175
176
			// 1.17
177
			[ 'addTable', 'iwlinks', 'patch-iwlinks.sql' ],
178
			[ 'addIndex', 'iwlinks', 'iwl_prefix_title_from', 'patch-rename-iwl_prefix.sql' ],
179
			[ 'addField', 'updatelog', 'ul_value', 'patch-ul_value.sql' ],
180
			[ 'addField', 'interwiki', 'iw_api', 'patch-iw_api_and_wikiid.sql' ],
181
			[ 'dropIndex', 'iwlinks', 'iwl_prefix', 'patch-kill-iwl_prefix.sql' ],
182
			[ 'addField', 'categorylinks', 'cl_collation', 'patch-categorylinks-better-collation.sql' ],
183
			[ 'doClFieldsUpdate' ],
184
			[ 'addTable', 'module_deps', 'patch-module_deps.sql' ],
185
			[ 'dropIndex', 'archive', 'ar_page_revid', 'patch-archive_kill_ar_page_revid.sql' ],
186
			[ 'addIndex', 'archive', 'ar_revid', 'patch-archive_ar_revid.sql' ],
187
			[ 'doLangLinksLengthUpdate' ],
188
189
			// 1.18
190
			[ 'doUserNewTalkTimestampNotNull' ],
191
			[ 'addIndex', 'user', 'user_email', 'patch-user_email_index.sql' ],
192
			[ 'modifyField', 'user_properties', 'up_property', 'patch-up_property.sql' ],
193
			[ 'addTable', 'uploadstash', 'patch-uploadstash.sql' ],
194
			[ 'addTable', 'user_former_groups', 'patch-user_former_groups.sql' ],
195
196
			// 1.19
197
			[ 'addIndex', 'logging', 'type_action', 'patch-logging-type-action-index.sql' ],
198
			[ 'addField', 'revision', 'rev_sha1', 'patch-rev_sha1.sql' ],
199
			[ 'doMigrateUserOptions' ],
200
			[ 'dropField', 'user', 'user_options', 'patch-drop-user_options.sql' ],
201
			[ 'addField', 'archive', 'ar_sha1', 'patch-ar_sha1.sql' ],
202
			[ 'addIndex', 'page', 'page_redirect_namespace_len',
203
				'patch-page_redirect_namespace_len.sql' ],
204
			[ 'addField', 'uploadstash', 'us_chunk_inx', 'patch-uploadstash_chunk.sql' ],
205
			[ 'addfield', 'job', 'job_timestamp', 'patch-jobs-add-timestamp.sql' ],
206
207
			// 1.20
208
			[ 'addIndex', 'revision', 'page_user_timestamp', 'patch-revision-user-page-index.sql' ],
209
			[ 'addField', 'ipblocks', 'ipb_parent_block_id', 'patch-ipb-parent-block-id.sql' ],
210
			[ 'addIndex', 'ipblocks', 'ipb_parent_block_id', 'patch-ipb-parent-block-id-index.sql' ],
211
			[ 'dropField', 'category', 'cat_hidden', 'patch-cat_hidden.sql' ],
212
213
			// 1.21
214
			[ 'addField', 'revision', 'rev_content_format', 'patch-revision-rev_content_format.sql' ],
215
			[ 'addField', 'revision', 'rev_content_model', 'patch-revision-rev_content_model.sql' ],
216
			[ 'addField', 'archive', 'ar_content_format', 'patch-archive-ar_content_format.sql' ],
217
			[ 'addField', 'archive', 'ar_content_model', 'patch-archive-ar_content_model.sql' ],
218
			[ 'addField', 'page', 'page_content_model', 'patch-page-page_content_model.sql' ],
219
			[ 'enableContentHandlerUseDB' ],
220
			[ 'dropField', 'site_stats', 'ss_admins', 'patch-drop-ss_admins.sql' ],
221
			[ 'dropField', 'recentchanges', 'rc_moved_to_title', 'patch-rc_moved.sql' ],
222
			[ 'addTable', 'sites', 'patch-sites.sql' ],
223
			[ 'addField', 'filearchive', 'fa_sha1', 'patch-fa_sha1.sql' ],
224
			[ 'addField', 'job', 'job_token', 'patch-job_token.sql' ],
225
			[ 'addField', 'job', 'job_attempts', 'patch-job_attempts.sql' ],
226
			[ 'doEnableProfiling' ],
227
			[ 'addField', 'uploadstash', 'us_props', 'patch-uploadstash-us_props.sql' ],
228
			[ 'modifyField', 'user_groups', 'ug_group', 'patch-ug_group-length-increase-255.sql' ],
229
			[ 'modifyField', 'user_former_groups', 'ufg_group',
230
				'patch-ufg_group-length-increase-255.sql' ],
231
			[ 'addIndex', 'page_props', 'pp_propname_page',
232
				'patch-page_props-propname-page-index.sql' ],
233
			[ 'addIndex', 'image', 'img_media_mime', 'patch-img_media_mime-index.sql' ],
234
235
			// 1.22
236
			[ 'doIwlinksIndexNonUnique' ],
237
			[ 'addIndex', 'iwlinks', 'iwl_prefix_from_title',
238
				'patch-iwlinks-from-title-index.sql' ],
239
			[ 'addField', 'archive', 'ar_id', 'patch-archive-ar_id.sql' ],
240
			[ 'addField', 'externallinks', 'el_id', 'patch-externallinks-el_id.sql' ],
241
242
			// 1.23
243
			[ 'addField', 'recentchanges', 'rc_source', 'patch-rc_source.sql' ],
244
			[ 'addIndex', 'logging', 'log_user_text_type_time',
245
				'patch-logging_user_text_type_time_index.sql' ],
246
			[ 'addIndex', 'logging', 'log_user_text_time', 'patch-logging_user_text_time_index.sql' ],
247
			[ 'addField', 'page', 'page_links_updated', 'patch-page_links_updated.sql' ],
248
			[ 'addField', 'user', 'user_password_expires', 'patch-user_password_expire.sql' ],
249
250
			// 1.24
251
			[ 'addField', 'page_props', 'pp_sortkey', 'patch-pp_sortkey.sql' ],
252
			[ 'dropField', 'recentchanges', 'rc_cur_time', 'patch-drop-rc_cur_time.sql' ],
253
			[ 'addIndex', 'watchlist', 'wl_user_notificationtimestamp',
254
				'patch-watchlist-user-notificationtimestamp-index.sql' ],
255
			[ 'addField', 'page', 'page_lang', 'patch-page_lang.sql' ],
256
			[ 'addField', 'pagelinks', 'pl_from_namespace', 'patch-pl_from_namespace.sql' ],
257
			[ 'addField', 'templatelinks', 'tl_from_namespace', 'patch-tl_from_namespace.sql' ],
258
			[ 'addField', 'imagelinks', 'il_from_namespace', 'patch-il_from_namespace.sql' ],
259
			[ 'modifyField', 'image', 'img_major_mime',
260
				'patch-img_major_mime-chemical.sql' ],
261
			[ 'modifyField', 'oldimage', 'oi_major_mime',
262
				'patch-oi_major_mime-chemical.sql' ],
263
			[ 'modifyField', 'filearchive', 'fa_major_mime',
264
				'patch-fa_major_mime-chemical.sql' ],
265
266
			// 1.25
267
			[ 'doUserNewTalkUseridUnsigned' ],
268
			// note this patch covers other _comment and _description fields too
269
			[ 'modifyField', 'recentchanges', 'rc_comment', 'patch-editsummary-length.sql' ],
270
271
			// 1.26
272
			[ 'dropTable', 'hitcounter' ],
273
			[ 'dropField', 'site_stats', 'ss_total_views', 'patch-drop-ss_total_views.sql' ],
274
			[ 'dropField', 'page', 'page_counter', 'patch-drop-page_counter.sql' ],
275
276
			// 1.27
277
			[ 'dropTable', 'msg_resource_links' ],
278
			[ 'dropTable', 'msg_resource' ],
279
			[ 'addTable', 'bot_passwords', 'patch-bot_passwords.sql' ],
280
			[ 'addField', 'watchlist', 'wl_id', 'patch-watchlist-wl_id.sql' ],
281
			[ 'dropIndex', 'categorylinks', 'cl_collation', 'patch-kill-cl_collation_index.sql' ],
282
			[ 'addIndex', 'categorylinks', 'cl_collation_ext',
283
				'patch-add-cl_collation_ext_index.sql' ],
284
			[ 'doCollationUpdate' ],
285
286
			// 1.28
287
			[ 'addIndex', 'recentchanges', 'rc_name_type_patrolled_timestamp',
288
				'patch-add-rc_name_type_patrolled_timestamp_index.sql' ],
289
			[ 'doRevisionPageRevIndexNonUnique' ],
290
			[ 'doNonUniquePlTlIl' ],
291
			[ 'addField', 'change_tag', 'ct_id', 'patch-change_tag-ct_id.sql' ],
292
			[ 'addField', 'tag_summary', 'ts_id', 'patch-tag_summary-ts_id.sql' ],
293
			[ 'modifyField', 'recentchanges', 'rc_ip', 'patch-rc_ip_modify.sql' ],
294
		];
295
	}
296
297
	/**
298
	 * 1.4 betas were missing the 'binary' marker from logging.log_title,
299
	 * which causes a collation mismatch error on joins in MySQL 4.1.
300
	 *
301
	 * @param string $table Table name
302
	 * @param string $field Field name to check
303
	 * @param string $patchFile Path to the patch to correct the field
304
	 * @return bool
305
	 */
306
	protected function checkBin( $table, $field, $patchFile ) {
307
		if ( !$this->doTable( $table ) ) {
308
			return true;
309
		}
310
311
		/** @var MySQLField $fieldInfo */
312
		$fieldInfo = $this->db->fieldInfo( $table, $field );
313
		if ( $fieldInfo->isBinary() ) {
314
			$this->output( "...$table table has correct $field encoding.\n" );
315
		} else {
316
			$this->applyPatch( $patchFile, false, "Fixing $field encoding on $table table" );
317
		}
318
	}
319
320
	/**
321
	 * Check whether an index contain a field
322
	 *
323
	 * @param string $table Table name
324
	 * @param string $index Index name to check
325
	 * @param string $field Field that should be in the index
326
	 * @return bool
327
	 */
328
	protected function indexHasField( $table, $index, $field ) {
329
		if ( !$this->doTable( $table ) ) {
330
			return true;
331
		}
332
333
		$info = $this->db->indexInfo( $table, $index, __METHOD__ );
334
		if ( $info ) {
335
			foreach ( $info as $row ) {
336
				if ( $row->Column_name == $field ) {
337
					$this->output( "...index $index on table $table includes field $field.\n" );
338
339
					return true;
340
				}
341
			}
342
		}
343
		$this->output( "...index $index on table $table has no field $field; added.\n" );
344
345
		return false;
346
	}
347
348
	/**
349
	 * Check that interwiki table exists; if it doesn't source it
350
	 */
351
	protected function doInterwikiUpdate() {
352
		global $IP;
353
354
		if ( !$this->doTable( 'interwiki' ) ) {
355
			return true;
356
		}
357
358
		if ( $this->db->tableExists( "interwiki", __METHOD__ ) ) {
359
			$this->output( "...already have interwiki table\n" );
360
361
			return;
362
		}
363
364
		$this->applyPatch( 'patch-interwiki.sql', false, 'Creating interwiki table' );
365
		$this->applyPatch(
366
			"$IP/maintenance/interwiki.sql",
367
			true,
368
			'Adding default interwiki definitions'
369
		);
370
	}
371
372
	/**
373
	 * Check that proper indexes are in place
374
	 */
375
	protected function doIndexUpdate() {
376
		$meta = $this->db->fieldInfo( 'recentchanges', 'rc_timestamp' );
377
		if ( $meta === false ) {
378
			throw new MWException( 'Missing rc_timestamp field of recentchanges table. Should not happen.' );
379
		}
380
		if ( $meta->isMultipleKey() ) {
381
			$this->output( "...indexes seem up to 20031107 standards.\n" );
382
383
			return;
384
		}
385
386
		$this->applyPatch( 'patch-indexes.sql', true, "Updating indexes to 20031107" );
387
	}
388
389
	protected function doOldLinksUpdate() {
390
		$cl = $this->maintenance->runChild( 'ConvertLinks' );
0 ignored issues
show
The property maintenance does not seem to exist. Did you mean postDatabaseUpdateMaintenance?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
391
		$cl->execute();
392
	}
393
394
	protected function doFixAncientImagelinks() {
395
		$info = $this->db->fieldInfo( 'imagelinks', 'il_from' );
396
		if ( !$info || $info->type() !== 'string' ) {
397
			$this->output( "...il_from OK\n" );
398
399
			return;
400
		}
401
402
		$applied = $this->applyPatch(
403
			'patch-fix-il_from.sql',
404
			false,
405
			'Fixing ancient broken imagelinks table.'
406
		);
407
408
		if ( $applied ) {
409
			$this->output( "NOTE: you will have to run maintenance/refreshLinks.php after this." );
410
		}
411
	}
412
413
	/**
414
	 * Check if we need to add talk page rows to the watchlist
415
	 */
416
	function doWatchlistUpdate() {
417
		$talk = $this->db->selectField( 'watchlist', 'count(*)', 'wl_namespace & 1', __METHOD__ );
418
		$nontalk = $this->db->selectField(
419
			'watchlist',
420
			'count(*)',
421
			'NOT (wl_namespace & 1)',
422
			__METHOD__
423
		);
424
		if ( $talk == $nontalk ) {
425
			$this->output( "...watchlist talk page rows already present.\n" );
426
427
			return;
428
		}
429
430
		$this->output( "Adding missing watchlist talk page rows... " );
431
		$this->db->insertSelect( 'watchlist', 'watchlist',
432
			[
433
				'wl_user' => 'wl_user',
434
				'wl_namespace' => 'wl_namespace | 1',
435
				'wl_title' => 'wl_title',
436
				'wl_notificationtimestamp' => 'wl_notificationtimestamp'
437
			], [ 'NOT (wl_namespace & 1)' ], __METHOD__, 'IGNORE' );
438
		$this->output( "done.\n" );
439
440
		$this->output( "Adding missing watchlist subject page rows... " );
441
		$this->db->insertSelect( 'watchlist', 'watchlist',
442
			[
443
				'wl_user' => 'wl_user',
444
				'wl_namespace' => 'wl_namespace & ~1',
445
				'wl_title' => 'wl_title',
446
				'wl_notificationtimestamp' => 'wl_notificationtimestamp'
447
			], [ 'wl_namespace & 1' ], __METHOD__, 'IGNORE' );
448
		$this->output( "done.\n" );
449
	}
450
451
	function doSchemaRestructuring() {
452
		if ( $this->db->tableExists( 'page', __METHOD__ ) ) {
453
			$this->output( "...page table already exists.\n" );
454
455
			return;
456
		}
457
458
		$this->output( "...converting from cur/old to page/revision/text DB structure.\n" );
459
		$this->output( wfTimestamp( TS_DB ) );
460
		$this->output( "......checking for duplicate entries.\n" );
461
462
		list( $cur, $old, $page, $revision, $text ) = $this->db->tableNamesN(
463
			'cur',
464
			'old',
465
			'page',
466
			'revision',
467
			'text'
468
		);
469
470
		$rows = $this->db->query( "
471
			SELECT cur_title, cur_namespace, COUNT(cur_namespace) AS c
472
			FROM $cur
473
			GROUP BY cur_title, cur_namespace
474
			HAVING c>1",
475
			__METHOD__
476
		);
477
478
		if ( $rows->numRows() > 0 ) {
479
			$this->output( wfTimestamp( TS_DB ) );
480
			$this->output( "......<b>Found duplicate entries</b>\n" );
481
			$this->output( sprintf( "<b>      %-60s %3s %5s</b>\n", 'Title', 'NS', 'Count' ) );
482
			$duplicate = [];
483
			foreach ( $rows as $row ) {
484
				if ( !isset( $duplicate[$row->cur_namespace] ) ) {
485
					$duplicate[$row->cur_namespace] = [];
486
				}
487
488
				$duplicate[$row->cur_namespace][] = $row->cur_title;
489
				$this->output( sprintf(
490
					"      %-60s %3s %5s\n",
491
					$row->cur_title, $row->cur_namespace,
492
					$row->c
493
				) );
494
			}
495
			$sql = "SELECT cur_title, cur_namespace, cur_id, cur_timestamp FROM $cur WHERE ";
496
			$firstCond = true;
497
			foreach ( $duplicate as $ns => $titles ) {
498
				if ( $firstCond ) {
499
					$firstCond = false;
500
				} else {
501
					$sql .= ' OR ';
502
				}
503
				$sql .= "( cur_namespace = {$ns} AND cur_title in (";
504
				$first = true;
505
				foreach ( $titles as $t ) {
506
					if ( $first ) {
507
						$sql .= $this->db->addQuotes( $t );
508
						$first = false;
509
					} else {
510
						$sql .= ', ' . $this->db->addQuotes( $t );
511
					}
512
				}
513
				$sql .= ") ) \n";
514
			}
515
			# By sorting descending, the most recent entry will be the first in the list.
516
			# All following entries will be deleted by the next while-loop.
517
			$sql .= 'ORDER BY cur_namespace, cur_title, cur_timestamp DESC';
518
519
			$rows = $this->db->query( $sql, __METHOD__ );
520
521
			$prev_title = $prev_namespace = false;
522
			$deleteId = [];
523
524
			foreach ( $rows as $row ) {
525
				if ( $prev_title == $row->cur_title && $prev_namespace == $row->cur_namespace ) {
526
					$deleteId[] = $row->cur_id;
527
				}
528
				$prev_title = $row->cur_title;
529
				$prev_namespace = $row->cur_namespace;
530
			}
531
			$sql = "DELETE FROM $cur WHERE cur_id IN ( " . implode( ',', $deleteId ) . ')';
532
			$this->db->query( $sql, __METHOD__ );
533
			$this->output( wfTimestamp( TS_DB ) );
534
			$this->output( "......<b>Deleted</b> " . $this->db->affectedRows() . " records.\n" );
535
		}
536
537
		$this->output( wfTimestamp( TS_DB ) );
538
		$this->output( "......Creating tables.\n" );
539
		$this->db->query( "CREATE TABLE $page (
540
			page_id int(8) unsigned NOT NULL auto_increment,
541
			page_namespace int NOT NULL,
542
			page_title varchar(255) binary NOT NULL,
543
			page_restrictions tinyblob NOT NULL,
544
			page_is_redirect tinyint(1) unsigned NOT NULL default '0',
545
			page_is_new tinyint(1) unsigned NOT NULL default '0',
546
			page_random real unsigned NOT NULL,
547
			page_touched char(14) binary NOT NULL default '',
548
			page_latest int(8) unsigned NOT NULL,
549
			page_len int(8) unsigned NOT NULL,
550
551
			PRIMARY KEY page_id (page_id),
552
			UNIQUE INDEX name_title (page_namespace,page_title),
553
			INDEX (page_random),
554
			INDEX (page_len)
555
			) ENGINE=InnoDB", __METHOD__ );
556
		$this->db->query( "CREATE TABLE $revision (
557
			rev_id int(8) unsigned NOT NULL auto_increment,
558
			rev_page int(8) unsigned NOT NULL,
559
			rev_comment tinyblob NOT NULL,
560
			rev_user int(5) unsigned NOT NULL default '0',
561
			rev_user_text varchar(255) binary NOT NULL default '',
562
			rev_timestamp char(14) binary NOT NULL default '',
563
			rev_minor_edit tinyint(1) unsigned NOT NULL default '0',
564
			rev_deleted tinyint(1) unsigned NOT NULL default '0',
565
			rev_len int(8) unsigned,
566
			rev_parent_id int(8) unsigned default NULL,
567
			PRIMARY KEY rev_page_id (rev_page, rev_id),
568
			UNIQUE INDEX rev_id (rev_id),
569
			INDEX rev_timestamp (rev_timestamp),
570
			INDEX page_timestamp (rev_page,rev_timestamp),
571
			INDEX user_timestamp (rev_user,rev_timestamp),
572
			INDEX usertext_timestamp (rev_user_text,rev_timestamp)
573
			) ENGINE=InnoDB", __METHOD__ );
574
575
		$this->output( wfTimestamp( TS_DB ) );
576
		$this->output( "......Locking tables.\n" );
577
		$this->db->query(
578
			"LOCK TABLES $page WRITE, $revision WRITE, $old WRITE, $cur WRITE",
579
			__METHOD__
580
		);
581
582
		$maxold = intval( $this->db->selectField( 'old', 'max(old_id)', '', __METHOD__ ) );
583
		$this->output( wfTimestamp( TS_DB ) );
584
		$this->output( "......maxold is {$maxold}\n" );
585
586
		$this->output( wfTimestamp( TS_DB ) );
587
		global $wgLegacySchemaConversion;
588
		if ( $wgLegacySchemaConversion ) {
589
			// Create HistoryBlobCurStub entries.
590
			// Text will be pulled from the leftover 'cur' table at runtime.
591
			$this->output( "......Moving metadata from cur; using blob references to text in cur table.\n" );
592
			$cur_text = "concat('O:18:\"historyblobcurstub\":1:{s:6:\"mCurId\";i:',cur_id,';}')";
593
			$cur_flags = "'object'";
594
		} else {
595
			// Copy all cur text in immediately: this may take longer but avoids
596
			// having to keep an extra table around.
597
			$this->output( "......Moving text from cur.\n" );
598
			$cur_text = 'cur_text';
599
			$cur_flags = "''";
600
		}
601
		$this->db->query(
602
			"INSERT INTO $old (old_namespace, old_title, old_text, old_comment, old_user,
603
				old_user_text, old_timestamp, old_minor_edit, old_flags)
604
			SELECT cur_namespace, cur_title, $cur_text, cur_comment, cur_user, cur_user_text,
605
				cur_timestamp, cur_minor_edit, $cur_flags
606
			FROM $cur",
607
			__METHOD__
608
		);
609
610
		$this->output( wfTimestamp( TS_DB ) );
611
		$this->output( "......Setting up revision table.\n" );
612
		$this->db->query(
613
			"INSERT INTO $revision (rev_id, rev_page, rev_comment, rev_user,
614
				rev_user_text, rev_timestamp, rev_minor_edit)
615
			SELECT old_id, cur_id, old_comment, old_user, old_user_text,
616
				old_timestamp, old_minor_edit
617
			FROM $old,$cur WHERE old_namespace=cur_namespace AND old_title=cur_title",
618
			__METHOD__
619
		);
620
621
		$this->output( wfTimestamp( TS_DB ) );
622
		$this->output( "......Setting up page table.\n" );
623
		$this->db->query(
624
			"INSERT INTO $page (page_id, page_namespace, page_title,
625
				page_restrictions, page_is_redirect, page_is_new, page_random,
626
				page_touched, page_latest, page_len)
627
			SELECT cur_id, cur_namespace, cur_title, cur_restrictions,
628
				cur_is_redirect, cur_is_new, cur_random, cur_touched, rev_id, LENGTH(cur_text)
629
			FROM $cur,$revision
630
			WHERE cur_id=rev_page AND rev_timestamp=cur_timestamp AND rev_id > {$maxold}",
631
			__METHOD__
632
		);
633
634
		$this->output( wfTimestamp( TS_DB ) );
635
		$this->output( "......Unlocking tables.\n" );
636
		$this->db->query( "UNLOCK TABLES", __METHOD__ );
637
638
		$this->output( wfTimestamp( TS_DB ) );
639
		$this->output( "......Renaming old.\n" );
640
		$this->db->query( "ALTER TABLE $old RENAME TO $text", __METHOD__ );
641
642
		$this->output( wfTimestamp( TS_DB ) );
643
		$this->output( "...done.\n" );
644
	}
645
646
	protected function doNamespaceSize() {
647
		$tables = [
648
			'page' => 'page',
649
			'archive' => 'ar',
650
			'recentchanges' => 'rc',
651
			'watchlist' => 'wl',
652
			'querycache' => 'qc',
653
			'logging' => 'log',
654
		];
655
		foreach ( $tables as $table => $prefix ) {
656
			$field = $prefix . '_namespace';
657
658
			$tablename = $this->db->tableName( $table );
659
			$result = $this->db->query( "SHOW COLUMNS FROM $tablename LIKE '$field'", __METHOD__ );
660
			$info = $this->db->fetchObject( $result );
661
662
			if ( substr( $info->Type, 0, 3 ) == 'int' ) {
663
				$this->output( "...$field is already a full int ($info->Type).\n" );
664
			} else {
665
				$this->output( "Promoting $field from $info->Type to int... " );
666
				$this->db->query( "ALTER TABLE $tablename MODIFY $field int NOT NULL", __METHOD__ );
667
				$this->output( "done.\n" );
668
			}
669
		}
670
	}
671
672
	protected function doPagelinksUpdate() {
673
		if ( $this->db->tableExists( 'pagelinks', __METHOD__ ) ) {
674
			$this->output( "...already have pagelinks table.\n" );
675
676
			return;
677
		}
678
679
		$this->applyPatch(
680
			'patch-pagelinks.sql',
681
			false,
682
			'Converting links and brokenlinks tables to pagelinks'
683
		);
684
685
		global $wgContLang;
686
		foreach ( $wgContLang->getNamespaces() as $ns => $name ) {
687
			if ( $ns == 0 ) {
688
				continue;
689
			}
690
691
			$this->output( "Cleaning up broken links for namespace $ns... " );
692
			$this->db->update( 'pagelinks',
693
				[
694
					'pl_namespace' => $ns,
695
					"pl_title = TRIM(LEADING {$this->db->addQuotes( "$name:" )} FROM pl_title)",
696
				],
697
				[
698
					'pl_namespace' => 0,
699
					'pl_title' . $this->db->buildLike( "$name:", $this->db->anyString() ),
700
				],
701
				__METHOD__
702
			);
703
			$this->output( "done.\n" );
704
		}
705
	}
706
707
	protected function doUserUniqueUpdate() {
708
		if ( !$this->doTable( 'user' ) ) {
709
			return true;
710
		}
711
712
		$duper = new UserDupes( $this->db, [ $this, 'output' ] );
713
		if ( $duper->hasUniqueIndex() ) {
714
			$this->output( "...already have unique user_name index.\n" );
715
716
			return;
717
		}
718
719
		if ( !$duper->clearDupes() ) {
720
			$this->output( "WARNING: This next step will probably fail due to unfixed duplicates...\n" );
721
		}
722
		$this->applyPatch( 'patch-user_nameindex.sql', false, "Adding unique index on user_name" );
723
	}
724
725
	protected function doUserGroupsUpdate() {
726
		if ( !$this->doTable( 'user_groups' ) ) {
727
			return true;
728
		}
729
730
		if ( $this->db->tableExists( 'user_groups', __METHOD__ ) ) {
731
			$info = $this->db->fieldInfo( 'user_groups', 'ug_group' );
732
			if ( $info->type() == 'int' ) {
733
				$oldug = $this->db->tableName( 'user_groups' );
734
				$newug = $this->db->tableName( 'user_groups_bogus' );
735
				$this->output( "user_groups table exists but is in bogus intermediate " .
736
					"format. Renaming to $newug... " );
737
				$this->db->query( "ALTER TABLE $oldug RENAME TO $newug", __METHOD__ );
738
				$this->output( "done.\n" );
739
740
				$this->applyPatch( 'patch-user_groups.sql', false, "Re-adding fresh user_groups table" );
741
742
				$this->output( "***\n" );
743
				$this->output( "*** WARNING: You will need to manually fix up user " .
744
					"permissions in the user_groups\n" );
745
				$this->output( "*** table. Old 1.5 alpha versions did some pretty funky stuff...\n" );
746
				$this->output( "***\n" );
747
			} else {
748
				$this->output( "...user_groups table exists and is in current format.\n" );
749
			}
750
751
			return;
752
		}
753
754
		$this->applyPatch( 'patch-user_groups.sql', false, "Adding user_groups table" );
755
756
		if ( !$this->db->tableExists( 'user_rights', __METHOD__ ) ) {
757
			if ( $this->db->fieldExists( 'user', 'user_rights', __METHOD__ ) ) {
758
				$this->applyPatch(
759
					'patch-user_rights.sql',
760
					false,
761
					'Upgrading from a 1.3 or older database? Breaking out user_rights for conversion'
762
				);
763
			} else {
764
				$this->output( "*** WARNING: couldn't locate user_rights table or field for upgrade.\n" );
765
				$this->output( "*** You may need to manually configure some sysops by manipulating\n" );
766
				$this->output( "*** the user_groups table.\n" );
767
768
				return;
769
			}
770
		}
771
772
		$this->output( "Converting user_rights table to user_groups... " );
773
		$result = $this->db->select( 'user_rights',
774
			[ 'ur_user', 'ur_rights' ],
775
			[ "ur_rights != ''" ],
776
			__METHOD__ );
777
778
		foreach ( $result as $row ) {
779
			$groups = array_unique(
780
				array_map( 'trim',
781
					explode( ',', $row->ur_rights ) ) );
782
783
			foreach ( $groups as $group ) {
784
				$this->db->insert( 'user_groups',
785
					[
786
						'ug_user' => $row->ur_user,
787
						'ug_group' => $group ],
788
					__METHOD__ );
789
			}
790
		}
791
		$this->output( "done.\n" );
792
	}
793
794
	/**
795
	 * Make sure wl_notificationtimestamp can be NULL,
796
	 * and update old broken items.
797
	 */
798
	protected function doWatchlistNull() {
799
		$info = $this->db->fieldInfo( 'watchlist', 'wl_notificationtimestamp' );
800
		if ( !$info ) {
801
			return;
802
		}
803
		if ( $info->isNullable() ) {
804
			$this->output( "...wl_notificationtimestamp is already nullable.\n" );
805
806
			return;
807
		}
808
809
		$this->applyPatch(
810
			'patch-watchlist-null.sql',
811
			false,
812
			'Making wl_notificationtimestamp nullable'
813
		);
814
	}
815
816
	/**
817
	 * Set page_random field to a random value where it is equals to 0.
818
	 *
819
	 * @see bug 3946
820
	 */
821
	protected function doPageRandomUpdate() {
822
		$page = $this->db->tableName( 'page' );
823
		$this->db->query( "UPDATE $page SET page_random = RAND() WHERE page_random = 0", __METHOD__ );
824
		$rows = $this->db->affectedRows();
825
826
		if ( $rows ) {
827
			$this->output( "Set page_random to a random value on $rows rows where it was set to 0\n" );
828
		} else {
829
			$this->output( "...no page_random rows needed to be set\n" );
830
		}
831
	}
832
833
	protected function doTemplatelinksUpdate() {
834
		if ( $this->db->tableExists( 'templatelinks', __METHOD__ ) ) {
835
			$this->output( "...templatelinks table already exists\n" );
836
837
			return;
838
		}
839
840
		$this->applyPatch( 'patch-templatelinks.sql', false, "Creating templatelinks table" );
841
842
		$this->output( "Populating...\n" );
843
		if ( wfGetLB()->getServerCount() > 1 ) {
844
			// Slow, replication-friendly update
845
			$res = $this->db->select( 'pagelinks', [ 'pl_from', 'pl_namespace', 'pl_title' ],
846
				[ 'pl_namespace' => NS_TEMPLATE ], __METHOD__ );
847
			$count = 0;
848
			foreach ( $res as $row ) {
849
				$count = ( $count + 1 ) % 100;
850
				if ( $count == 0 ) {
851
					wfGetLBFactory()->waitForReplication( [ 'wiki' => wfWikiID() ] );
852
				}
853
				$this->db->insert( 'templatelinks',
854
					[
855
						'tl_from' => $row->pl_from,
856
						'tl_namespace' => $row->pl_namespace,
857
						'tl_title' => $row->pl_title,
858
					], __METHOD__
859
				);
860
			}
861
		} else {
862
			// Fast update
863
			$this->db->insertSelect( 'templatelinks', 'pagelinks',
864
				[
865
					'tl_from' => 'pl_from',
866
					'tl_namespace' => 'pl_namespace',
867
					'tl_title' => 'pl_title'
868
				], [
869
					'pl_namespace' => 10
870
				], __METHOD__
871
			);
872
		}
873
		$this->output( "Done. Please run maintenance/refreshLinks.php for a more " .
874
			"thorough templatelinks update.\n" );
875
	}
876
877
	protected function doBacklinkingIndicesUpdate() {
878
		if ( !$this->indexHasField( 'pagelinks', 'pl_namespace', 'pl_from' ) ||
879
			!$this->indexHasField( 'templatelinks', 'tl_namespace', 'tl_from' ) ||
880
			!$this->indexHasField( 'imagelinks', 'il_to', 'il_from' )
881
		) {
882
			$this->applyPatch( 'patch-backlinkindexes.sql', false, "Updating backlinking indices" );
883
		}
884
	}
885
886
	/**
887
	 * Adding page_restrictions table, obsoleting page.page_restrictions.
888
	 * Migrating old restrictions to new table
889
	 * -- Andrew Garrett, January 2007.
890
	 */
891
	protected function doRestrictionsUpdate() {
892
		if ( $this->db->tableExists( 'page_restrictions', __METHOD__ ) ) {
893
			$this->output( "...page_restrictions table already exists.\n" );
894
895
			return;
896
		}
897
898
		$this->applyPatch(
899
			'patch-page_restrictions.sql',
900
			false,
901
			'Creating page_restrictions table (1/2)'
902
		);
903
		$this->applyPatch(
904
			'patch-page_restrictions_sortkey.sql',
905
			false,
906
			'Creating page_restrictions table (2/2)'
907
		);
908
		$this->output( "done.\n" );
909
910
		$this->output( "Migrating old restrictions to new table...\n" );
911
		$task = $this->maintenance->runChild( 'UpdateRestrictions' );
0 ignored issues
show
The property maintenance does not seem to exist. Did you mean postDatabaseUpdateMaintenance?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
912
		$task->execute();
913
	}
914
915
	protected function doCategorylinksIndicesUpdate() {
916
		if ( !$this->indexHasField( 'categorylinks', 'cl_sortkey', 'cl_from' ) ) {
917
			$this->applyPatch( 'patch-categorylinksindex.sql', false, "Updating categorylinks Indices" );
918
		}
919
	}
920
921 View Code Duplication
	protected function doCategoryPopulation() {
922
		if ( $this->updateRowExists( 'populate category' ) ) {
923
			$this->output( "...category table already populated.\n" );
924
925
			return;
926
		}
927
928
		$this->output(
929
			"Populating category table, printing progress markers. " .
930
			"For large databases, you\n" .
931
			"may want to hit Ctrl-C and do this manually with maintenance/\n" .
932
			"populateCategory.php.\n"
933
		);
934
		$task = $this->maintenance->runChild( 'PopulateCategory' );
0 ignored issues
show
The property maintenance does not seem to exist. Did you mean postDatabaseUpdateMaintenance?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
935
		$task->execute();
936
		$this->output( "Done populating category table.\n" );
937
	}
938
939
	protected function doPopulateParentId() {
940
		if ( !$this->updateRowExists( 'populate rev_parent_id' ) ) {
941
			$this->output(
942
				"Populating rev_parent_id fields, printing progress markers. For large\n" .
943
				"databases, you may want to hit Ctrl-C and do this manually with\n" .
944
				"maintenance/populateParentId.php.\n" );
945
946
			$task = $this->maintenance->runChild( 'PopulateParentId' );
947
			$task->execute();
948
		}
949
	}
950
951 View Code Duplication
	protected function doMaybeProfilingMemoryUpdate() {
952
		if ( !$this->doTable( 'profiling' ) ) {
953
			return true;
954
		}
955
956
		if ( !$this->db->tableExists( 'profiling', __METHOD__ ) ) {
957
			return true;
958
		} elseif ( $this->db->fieldExists( 'profiling', 'pf_memory', __METHOD__ ) ) {
959
			$this->output( "...profiling table has pf_memory field.\n" );
960
961
			return true;
962
		}
963
964
		return $this->applyPatch(
965
			'patch-profiling-memory.sql',
966
			false,
967
			'Adding pf_memory field to table profiling'
968
		);
969
	}
970
971
	protected function doFilearchiveIndicesUpdate() {
972
		$info = $this->db->indexInfo( 'filearchive', 'fa_user_timestamp', __METHOD__ );
973
		if ( !$info ) {
974
			$this->applyPatch( 'patch-filearchive-user-index.sql', false, "Updating filearchive indices" );
975
		}
976
977
		return true;
978
	}
979
980 View Code Duplication
	protected function doNonUniquePlTlIl() {
981
		$info = $this->db->indexInfo( 'pagelinks', 'pl_namespace' );
982
		if ( is_array( $info ) && $info[0]->Non_unique ) {
983
			$this->output( "...pl_namespace, tl_namespace, il_to indices are already non-UNIQUE.\n" );
984
985
			return true;
986
		}
987
		if ( $this->skipSchema ) {
988
			$this->output( "...skipping schema change (making pl_namespace, tl_namespace " .
989
				"and il_to indices non-UNIQUE).\n" );
990
991
			return false;
992
		}
993
994
		return $this->applyPatch(
995
			'patch-pl-tl-il-nonunique.sql',
996
			false,
997
			'Making pl_namespace, tl_namespace and il_to indices non-UNIQUE'
998
		);
999
	}
1000
1001
	protected function doUpdateMimeMinorField() {
1002
		if ( $this->updateRowExists( 'mime_minor_length' ) ) {
1003
			$this->output( "...*_mime_minor fields are already long enough.\n" );
1004
1005
			return;
1006
		}
1007
1008
		$this->applyPatch(
1009
			'patch-mime_minor_length.sql',
1010
			false,
1011
			'Altering all *_mime_minor fields to 100 bytes in size'
1012
		);
1013
	}
1014
1015
	protected function doClFieldsUpdate() {
1016
		if ( $this->updateRowExists( 'cl_fields_update' ) ) {
1017
			$this->output( "...categorylinks up-to-date.\n" );
1018
1019
			return;
1020
		}
1021
1022
		$this->applyPatch(
1023
			'patch-categorylinks-better-collation2.sql',
1024
			false,
1025
			'Updating categorylinks (again)'
1026
		);
1027
	}
1028
1029
	protected function doLangLinksLengthUpdate() {
1030
		$langlinks = $this->db->tableName( 'langlinks' );
1031
		$res = $this->db->query( "SHOW COLUMNS FROM $langlinks LIKE 'll_lang'" );
1032
		$row = $this->db->fetchObject( $res );
1033
1034
		if ( $row && $row->Type == "varbinary(10)" ) {
1035
			$this->applyPatch(
1036
				'patch-langlinks-ll_lang-20.sql',
1037
				false,
1038
				'Updating length of ll_lang in langlinks'
1039
			);
1040
		} else {
1041
			$this->output( "...ll_lang is up-to-date.\n" );
1042
		}
1043
	}
1044
1045 View Code Duplication
	protected function doUserNewTalkTimestampNotNull() {
1046
		if ( !$this->doTable( 'user_newtalk' ) ) {
1047
			return true;
1048
		}
1049
1050
		$info = $this->db->fieldInfo( 'user_newtalk', 'user_last_timestamp' );
1051
		if ( $info === false ) {
1052
			return;
1053
		}
1054
		if ( $info->isNullable() ) {
1055
			$this->output( "...user_last_timestamp is already nullable.\n" );
1056
1057
			return;
1058
		}
1059
1060
		$this->applyPatch(
1061
			'patch-user-newtalk-timestamp-null.sql',
1062
			false,
1063
			'Making user_last_timestamp nullable'
1064
		);
1065
	}
1066
1067 View Code Duplication
	protected function doIwlinksIndexNonUnique() {
1068
		$info = $this->db->indexInfo( 'iwlinks', 'iwl_prefix_title_from' );
1069
		if ( is_array( $info ) && $info[0]->Non_unique ) {
1070
			$this->output( "...iwl_prefix_title_from index is already non-UNIQUE.\n" );
1071
1072
			return true;
1073
		}
1074
		if ( $this->skipSchema ) {
1075
			$this->output( "...skipping schema change (making iwl_prefix_title_from index non-UNIQUE).\n" );
1076
1077
			return false;
1078
		}
1079
1080
		return $this->applyPatch(
1081
			'patch-iwl_prefix_title_from-non-unique.sql',
1082
			false,
1083
			'Making iwl_prefix_title_from index non-UNIQUE'
1084
		);
1085
	}
1086
1087 View Code Duplication
	protected function doUserNewTalkUseridUnsigned() {
1088
		if ( !$this->doTable( 'user_newtalk' ) ) {
1089
			return true;
1090
		}
1091
1092
		$info = $this->db->fieldInfo( 'user_newtalk', 'user_id' );
1093
		if ( $info === false ) {
1094
			return true;
1095
		}
1096
		if ( $info->isUnsigned() ) {
1097
			$this->output( "...user_id is already unsigned int.\n" );
1098
1099
			return true;
1100
		}
1101
1102
		return $this->applyPatch(
1103
			'patch-user-newtalk-userid-unsigned.sql',
1104
			false,
1105
			'Making user_id unsigned int'
1106
		);
1107
	}
1108
1109 View Code Duplication
	protected function doRevisionPageRevIndexNonUnique() {
1110
		if ( !$this->doTable( 'revision' ) ) {
1111
			return true;
1112
		} elseif ( !$this->db->indexExists( 'revision', 'rev_page_id' ) ) {
1113
			$this->output( "...rev_page_id index not found on revision.\n" );
1114
			return true;
1115
		}
1116
1117
		if ( !$this->db->indexUnique( 'revision', 'rev_page_id' ) ) {
1118
			$this->output( "...rev_page_id index already non-unique.\n" );
1119
			return true;
1120
		}
1121
1122
		return $this->applyPatch(
1123
			'patch-revision-page-rev-index-nonunique.sql',
1124
			false,
1125
			'Making rev_page_id index non-unique'
1126
		);
1127
	}
1128
1129
	public function getSchemaVars() {
1130
		global $wgDBTableOptions;
1131
1132
		$vars = [];
1133
		$vars['wgDBTableOptions'] = str_replace( 'TYPE', 'ENGINE', $wgDBTableOptions );
1134
		$vars['wgDBTableOptions'] = str_replace(
1135
			'CHARSET=mysql4',
1136
			'CHARSET=binary',
1137
			$vars['wgDBTableOptions']
1138
		);
1139
1140
		return $vars;
1141
	}
1142
}
1143