MediaWiki REL1_35
MysqlUpdater.php
Go to the documentation of this file.
1<?php
26
35 protected function getCoreUpdateList() {
36 return [
37 // 1.2
38 [ 'addField', 'ipblocks', 'ipb_id', 'patch-ipblocks.sql' ],
39 [ 'addField', 'ipblocks', 'ipb_expiry', 'patch-ipb_expiry.sql' ],
40 [ 'doInterwikiUpdate' ],
41 [ 'doIndexUpdate' ],
42 [ 'addField', 'recentchanges', 'rc_type', 'patch-rc_type.sql' ],
43 [ 'addIndex', 'recentchanges', 'new_name_timestamp', 'patch-rc-newindex.sql' ],
44
45 // 1.3
46 [ 'addField', 'user', 'user_real_name', 'patch-user-realname.sql' ],
47 [ 'addTable', 'querycache', 'patch-querycache.sql' ],
48 [ 'addTable', 'objectcache', 'patch-objectcache.sql' ],
49 [ 'addTable', 'categorylinks', 'patch-categorylinks.sql' ],
50 [ 'doOldLinksUpdate' ],
51 [ 'doFixAncientImagelinks' ],
52 [ 'addField', 'recentchanges', 'rc_ip', 'patch-rc_ip.sql' ],
53
54 // 1.4
55 [ 'addIndex', 'image', 'PRIMARY', 'patch-image_name_primary.sql' ],
56 [ 'addField', 'recentchanges', 'rc_id', 'patch-rc_id.sql' ],
57 [ 'addField', 'recentchanges', 'rc_patrolled', 'patch-rc-patrol.sql' ],
58 [ 'addTable', 'logging', 'patch-logging.sql' ],
59 [ 'addField', 'user', 'user_token', 'patch-user_token.sql' ],
60 [ 'addField', 'watchlist', 'wl_notificationtimestamp', 'patch-email-notification.sql' ],
61 [ 'doWatchlistUpdate' ],
62 [ 'dropField', 'user', 'user_emailauthenticationtimestamp',
63 'patch-email-authentication.sql' ],
64
65 // 1.5
66 [ 'doSchemaRestructuring' ],
67 [ 'addField', 'logging', 'log_params', 'patch-log_params.sql' ],
68 [ 'checkBin', 'logging', 'log_title', 'patch-logging-title.sql', ],
69 [ 'addField', 'archive', 'ar_rev_id', 'patch-archive-rev_id.sql' ],
70 [ 'addField', 'page', 'page_len', 'patch-page_len.sql' ],
71 [ 'dropField', 'revision', 'inverse_timestamp', 'patch-inverse_timestamp.sql' ],
72 [ 'ifTableNotExists', 'content',
73 'addField', 'revision', 'rev_text_id', 'patch-rev_text_id.sql' ],
74 [ 'addField', 'revision', 'rev_deleted', 'patch-rev_deleted.sql' ],
75 [ 'addField', 'image', 'img_width', 'patch-img_width.sql' ],
76 [ 'addField', 'image', 'img_metadata', 'patch-img_metadata.sql' ],
77 [ 'addField', 'user', 'user_email_token', 'patch-user_email_token.sql' ],
78 [ 'ifTableNotExists', 'content',
79 'addField', 'archive', 'ar_text_id', 'patch-archive-text_id.sql' ],
80 [ 'doNamespaceSize' ],
81 [ 'addField', 'image', 'img_media_type', 'patch-img_media_type.sql' ],
82 [ 'doPagelinksUpdate' ],
83 [ 'dropField', 'image', 'img_type', 'patch-drop_img_type.sql' ],
84 [ 'doUserUniqueUpdate' ],
85 [ 'doUserGroupsUpdate' ],
86 [ 'addField', 'site_stats', 'ss_total_pages', 'patch-ss_total_articles.sql' ],
87 [ 'addTable', 'user_newtalk', 'patch-usernewtalk.sql' ],
88 [ 'addField', 'interwiki', 'iw_trans', 'patch-interwiki-trans.sql' ],
89
90 // 1.6
91 [ 'doWatchlistNull' ],
92 [ 'addIndex', 'logging', 'times', 'patch-logging-times-index.sql' ],
93 [ 'addField', 'ipblocks', 'ipb_range_start', 'patch-ipb_range_start.sql' ],
94 [ 'doPageRandomUpdate' ],
95 [ 'addField', 'user', 'user_registration', 'patch-user_registration.sql' ],
96 [ 'doTemplatelinksUpdate' ],
97 [ 'addTable', 'externallinks', 'patch-externallinks.sql' ],
98 [ 'addTable', 'job', 'patch-job.sql' ],
99 [ 'addField', 'site_stats', 'ss_images', 'patch-ss_images.sql' ],
100 [ 'addTable', 'langlinks', 'patch-langlinks.sql' ],
101 [ 'addTable', 'querycache_info', 'patch-querycacheinfo.sql' ],
102 [ 'addTable', 'filearchive', 'patch-filearchive.sql' ],
103 [ 'addField', 'ipblocks', 'ipb_anon_only', 'patch-ipb_anon_only.sql' ],
104 [ 'ifTableNotExists', 'actor', 'addIndex', 'recentchanges', 'rc_ns_usertext',
105 'patch-recentchanges-utindex.sql' ],
106 [ 'ifTableNotExists', 'actor', 'addIndex', 'recentchanges', 'rc_user_text',
107 'patch-rc_user_text-index.sql' ],
108
109 // 1.9
110 [ 'addField', 'user', 'user_newpass_time', 'patch-user_newpass_time.sql' ],
111 [ 'addTable', 'redirect', 'patch-redirect.sql' ],
112 [ 'addTable', 'querycachetwo', 'patch-querycachetwo.sql' ],
113 [ 'addField', 'ipblocks', 'ipb_enable_autoblock', 'patch-ipb_optional_autoblock.sql' ],
114 [ 'doBacklinkingIndicesUpdate' ],
115 [ 'addField', 'recentchanges', 'rc_old_len', 'patch-rc_len.sql' ],
116 [ 'addField', 'user', 'user_editcount', 'patch-user_editcount.sql' ],
117
118 // 1.10
119 [ 'doRestrictionsUpdate' ],
120 [ 'addField', 'logging', 'log_id', 'patch-log_id.sql' ],
121 [ 'addField', 'revision', 'rev_parent_id', 'patch-rev_parent_id.sql' ],
122 [ 'addField', 'page_restrictions', 'pr_id', 'patch-page_restrictions_sortkey.sql' ],
123 [ 'addField', 'revision', 'rev_len', 'patch-rev_len.sql' ],
124 [ 'addField', 'recentchanges', 'rc_deleted', 'patch-rc_deleted.sql' ],
125 [ 'addField', 'logging', 'log_deleted', 'patch-log_deleted.sql' ],
126 [ 'addField', 'archive', 'ar_deleted', 'patch-ar_deleted.sql' ],
127 [ 'addField', 'ipblocks', 'ipb_deleted', 'patch-ipb_deleted.sql' ],
128 [ 'addField', 'filearchive', 'fa_deleted', 'patch-fa_deleted.sql' ],
129 [ 'addField', 'archive', 'ar_len', 'patch-ar_len.sql' ],
130
131 // 1.11
132 [ 'addField', 'ipblocks', 'ipb_block_email', 'patch-ipb_emailban.sql' ],
133 [ 'doCategorylinksIndicesUpdate' ],
134 [ 'addField', 'oldimage', 'oi_metadata', 'patch-oi_metadata.sql' ],
135 [ 'ifTableNotExists', 'actor', 'addIndex', 'archive', 'usertext_timestamp',
136 'patch-archive-user-index.sql' ],
137 [ 'ifTableNotExists', 'actor', 'addIndex', 'image', 'img_usertext_timestamp',
138 'patch-image-user-index.sql' ],
139 [ 'ifTableNotExists', 'actor', 'addIndex', 'oldimage', 'oi_usertext_timestamp',
140 'patch-oldimage-user-index.sql' ],
141 [ 'addField', 'archive', 'ar_page_id', 'patch-archive-page_id.sql' ],
142 [ 'addField', 'image', 'img_sha1', 'patch-img_sha1.sql' ],
143
144 // 1.12
145 [ 'addTable', 'protected_titles', 'patch-protected_titles.sql' ],
146
147 // 1.13
148 [ 'ifTableNotExists', 'actor', 'addField', 'ipblocks', 'ipb_by_text', 'patch-ipb_by_text.sql' ],
149 [ 'addTable', 'page_props', 'patch-page_props.sql' ],
150 [ 'addTable', 'updatelog', 'patch-updatelog.sql' ],
151 [ 'addTable', 'category', 'patch-category.sql' ],
152 [ 'doCategoryPopulation' ],
153 [ 'addField', 'archive', 'ar_parent_id', 'patch-ar_parent_id.sql' ],
154 [ 'addField', 'user_newtalk', 'user_last_timestamp', 'patch-user_last_timestamp.sql' ],
155 [ 'doPopulateParentId' ],
156 [ 'checkBin', 'protected_titles', 'pt_title', 'patch-pt_title-encoding.sql', ],
157 [ 'ifTableNotExists', 'actor', 'doFilearchiveIndicesUpdate' ],
158
159 // 1.14
160 [ 'addField', 'site_stats', 'ss_active_users', 'patch-ss_active_users.sql' ],
161 [ 'doActiveUsersInit' ],
162 [ 'addField', 'ipblocks', 'ipb_allow_usertalk', 'patch-ipb_allow_usertalk.sql' ],
163
164 // 1.15
165 [ 'addTable', 'change_tag', 'patch-change_tag.sql' ],
166
167 // 1.16
168 [ 'addTable', 'user_properties', 'patch-user_properties.sql' ],
169 [ 'addTable', 'log_search', 'patch-log_search.sql' ],
170 [ 'ifTableNotExists', 'actor',
171 'addField', 'logging', 'log_user_text', 'patch-log_user_text.sql' ],
172 # listed separately from the previous update because 1.16 was released without this update
173 [ 'ifTableNotExists', 'actor', 'doLogUsertextPopulation' ],
174 [ 'doLogSearchPopulation' ],
175 [ 'addTable', 'l10n_cache', 'patch-l10n_cache.sql' ],
176 [ 'dropIndex', 'change_tag', 'ct_rc_id', 'patch-change_tag-indexes.sql' ],
177 [ 'addField', 'redirect', 'rd_interwiki', 'patch-rd_interwiki.sql' ],
178 [ 'doUpdateMimeMinorField' ],
179
180 // 1.17
181 [ 'addTable', 'iwlinks', 'patch-iwlinks.sql' ],
182 [ 'addIndex', 'iwlinks', 'iwl_prefix_title_from', 'patch-rename-iwl_prefix.sql' ],
183 [ 'addField', 'updatelog', 'ul_value', 'patch-ul_value.sql' ],
184 [ 'addField', 'interwiki', 'iw_api', 'patch-iw_api_and_wikiid.sql' ],
185 [ 'dropIndex', 'iwlinks', 'iwl_prefix', 'patch-kill-iwl_prefix.sql' ],
186 [ 'addField', 'categorylinks', 'cl_collation', 'patch-categorylinks-better-collation.sql' ],
187 [ 'doClFieldsUpdate' ],
188 [ 'addTable', 'module_deps', 'patch-module_deps.sql' ],
189 [ 'dropIndex', 'archive', 'ar_page_revid', 'patch-archive_kill_ar_page_revid.sql' ],
190 [ 'addIndexIfNoneExist',
191 'archive', [ 'ar_revid', 'ar_revid_uniq' ], 'patch-archive_ar_revid.sql' ],
192
193 // 1.18
194 [ 'doUserNewTalkTimestampNotNull' ],
195 [ 'addIndex', 'user', 'user_email', 'patch-user_email_index.sql' ],
196 [ 'modifyField', 'user_properties', 'up_property', 'patch-up_property.sql' ],
197 [ 'addTable', 'uploadstash', 'patch-uploadstash.sql' ],
198 [ 'addTable', 'user_former_groups', 'patch-user_former_groups.sql' ],
199
200 // 1.19
201 [ 'addField', 'revision', 'rev_sha1', 'patch-rev_sha1.sql' ],
202 [ 'doMigrateUserOptions' ],
203 [ 'dropField', 'user', 'user_options', 'patch-drop-user_options.sql' ],
204 [ 'addField', 'archive', 'ar_sha1', 'patch-ar_sha1.sql' ],
205 [ 'addIndex', 'page', 'page_redirect_namespace_len',
206 'patch-page_redirect_namespace_len.sql' ],
207 [ 'addField', 'uploadstash', 'us_chunk_inx', 'patch-uploadstash_chunk.sql' ],
208 [ 'addfield', 'job', 'job_timestamp', 'patch-jobs-add-timestamp.sql' ],
209
210 // 1.20
211 [ 'ifFieldExists', 'revision', 'rev_user',
212 'addIndex', 'revision', 'page_user_timestamp', 'patch-revision-user-page-index.sql' ],
213 [ 'addField', 'ipblocks', 'ipb_parent_block_id', 'patch-ipb-parent-block-id.sql' ],
214 [ 'addIndex', 'ipblocks', 'ipb_parent_block_id', 'patch-ipb-parent-block-id-index.sql' ],
215 [ 'dropField', 'category', 'cat_hidden', 'patch-cat_hidden.sql' ],
216
217 // 1.21
218 [ 'ifFieldExists', 'revision', 'rev_text_id',
219 'addField', 'revision', 'rev_content_format', 'patch-revision-rev_content_format.sql' ],
220 [ 'ifFieldExists', 'revision', 'rev_text_id',
221 'addField', 'revision', 'rev_content_model', 'patch-revision-rev_content_model.sql' ],
222 [ 'ifFieldExists', 'archive', 'ar_text_id',
223 'addField', 'archive', 'ar_content_format', 'patch-archive-ar_content_format.sql' ],
224 [ 'ifFieldExists', 'archive', 'ar_text_id',
225 'addField', 'archive', 'ar_content_model', 'patch-archive-ar_content_model.sql' ],
226 [ 'addField', 'page', 'page_content_model', 'patch-page-page_content_model.sql' ],
227 [ 'dropField', 'site_stats', 'ss_admins', 'patch-drop-ss_admins.sql' ],
228 [ 'dropField', 'recentchanges', 'rc_moved_to_title', 'patch-rc_moved.sql' ],
229 [ 'addTable', 'sites', 'patch-sites.sql' ],
230 [ 'addField', 'filearchive', 'fa_sha1', 'patch-fa_sha1.sql' ],
231 [ 'addField', 'job', 'job_token', 'patch-job_token.sql' ],
232 [ 'addField', 'job', 'job_attempts', 'patch-job_attempts.sql' ],
233 [ 'addField', 'uploadstash', 'us_props', 'patch-uploadstash-us_props.sql' ],
234 [ 'modifyField', 'user_groups', 'ug_group', 'patch-ug_group-length-increase-255.sql' ],
235 [ 'modifyField', 'user_former_groups', 'ufg_group',
236 'patch-ufg_group-length-increase-255.sql' ],
237 [ 'addIndex', 'page_props', 'pp_propname_page',
238 'patch-page_props-propname-page-index.sql' ],
239 [ 'addIndex', 'image', 'img_media_mime', 'patch-img_media_mime-index.sql' ],
240
241 // 1.22
242 [ 'doIwlinksIndexNonUnique' ],
243 [ 'addIndex', 'iwlinks', 'iwl_prefix_from_title',
244 'patch-iwlinks-from-title-index.sql' ],
245 [ 'addField', 'archive', 'ar_id', 'patch-archive-ar_id.sql' ],
246 [ 'addField', 'externallinks', 'el_id', 'patch-externallinks-el_id.sql' ],
247
248 // 1.23
249 [ 'addField', 'recentchanges', 'rc_source', 'patch-rc_source.sql' ],
250 [ 'ifTableNotExists', 'actor', 'addIndex', 'logging', 'log_user_text_type_time',
251 'patch-logging_user_text_type_time_index.sql' ],
252 [ 'ifTableNotExists', 'actor', 'addIndex', 'logging', 'log_user_text_time',
253 'patch-logging_user_text_time_index.sql' ],
254 [ 'addField', 'page', 'page_links_updated', 'patch-page_links_updated.sql' ],
255 [ 'addField', 'user', 'user_password_expires', 'patch-user_password_expire.sql' ],
256
257 // 1.24
258 [ 'addField', 'page_props', 'pp_sortkey', 'patch-pp_sortkey.sql' ],
259 [ 'dropField', 'recentchanges', 'rc_cur_time', 'patch-drop-rc_cur_time.sql' ],
260 [ 'addIndex', 'watchlist', 'wl_user_notificationtimestamp',
261 'patch-watchlist-user-notificationtimestamp-index.sql' ],
262 [ 'addField', 'page', 'page_lang', 'patch-page_lang.sql' ],
263 [ 'addField', 'pagelinks', 'pl_from_namespace', 'patch-pl_from_namespace.sql' ],
264 [ 'addField', 'templatelinks', 'tl_from_namespace', 'patch-tl_from_namespace.sql' ],
265 [ 'addField', 'imagelinks', 'il_from_namespace', 'patch-il_from_namespace.sql' ],
266 [ 'modifyField', 'image', 'img_major_mime',
267 'patch-img_major_mime-chemical.sql' ],
268 [ 'modifyField', 'oldimage', 'oi_major_mime',
269 'patch-oi_major_mime-chemical.sql' ],
270 [ 'modifyField', 'filearchive', 'fa_major_mime',
271 'patch-fa_major_mime-chemical.sql' ],
272
273 // 1.25
274 // note this patch covers other _comment and _description fields too
275 [ 'doExtendCommentLengths' ],
276
277 // 1.26
278 [ 'dropTable', 'hitcounter' ],
279 [ 'dropField', 'site_stats', 'ss_total_views', 'patch-drop-ss_total_views.sql' ],
280 [ 'dropField', 'page', 'page_counter', 'patch-drop-page_counter.sql' ],
281
282 // 1.27
283 [ 'dropTable', 'msg_resource_links' ],
284 [ 'dropTable', 'msg_resource' ],
285 [ 'addTable', 'bot_passwords', 'patch-bot_passwords.sql' ],
286 [ 'addField', 'watchlist', 'wl_id', 'patch-watchlist-wl_id.sql' ],
287 [ 'dropIndex', 'categorylinks', 'cl_collation', 'patch-kill-cl_collation_index.sql' ],
288 [ 'addIndex', 'categorylinks', 'cl_collation_ext',
289 'patch-add-cl_collation_ext_index.sql' ],
290 [ 'doCollationUpdate' ],
291
292 // 1.28
293 [ 'addIndex', 'recentchanges', 'rc_name_type_patrolled_timestamp',
294 'patch-add-rc_name_type_patrolled_timestamp_index.sql' ],
295 [ 'doRevisionPageRevIndexNonUnique' ],
296 [ 'doNonUniquePlTlIl' ],
297 [ 'addField', 'change_tag', 'ct_id', 'patch-change_tag-ct_id.sql' ],
298 [ 'modifyField', 'recentchanges', 'rc_ip', 'patch-rc_ip_modify.sql' ],
299 [ 'ifTableNotExists', 'actor', 'addIndex', 'archive', 'usertext_timestamp',
300 'patch-rename-ar_usertext_timestamp.sql' ],
301
302 // 1.29
303 [ 'addField', 'externallinks', 'el_index_60', 'patch-externallinks-el_index_60.sql' ],
304 [ 'dropIndex', 'user_groups', 'ug_user_group', 'patch-user_groups-primary-key.sql' ],
305 [ 'addField', 'user_groups', 'ug_expiry', 'patch-user_groups-ug_expiry.sql' ],
306 [ 'ifTableNotExists', 'actor',
307 'addIndex', 'image', 'img_user_timestamp', 'patch-image-user-index-2.sql' ],
308
309 // 1.30
310 [ 'modifyField', 'image', 'img_media_type', 'patch-add-3d.sql' ],
311 [ 'addTable', 'ip_changes', 'patch-ip_changes.sql' ],
312 [ 'renameIndex', 'categorylinks', 'cl_from', 'PRIMARY', false,
313 'patch-categorylinks-fix-pk.sql' ],
314 [ 'renameIndex', 'templatelinks', 'tl_from', 'PRIMARY', false,
315 'patch-templatelinks-fix-pk.sql' ],
316 [ 'renameIndex', 'pagelinks', 'pl_from', 'PRIMARY', false, 'patch-pagelinks-fix-pk.sql' ],
317 [ 'renameIndex', 'text', 'old_id', 'PRIMARY', false, 'patch-text-fix-pk.sql' ],
318 [ 'renameIndex', 'imagelinks', 'il_from', 'PRIMARY', false, 'patch-imagelinks-fix-pk.sql' ],
319 [ 'renameIndex', 'iwlinks', 'iwl_from', 'PRIMARY', false, 'patch-iwlinks-fix-pk.sql' ],
320 [ 'renameIndex', 'langlinks', 'll_from', 'PRIMARY', false, 'patch-langlinks-fix-pk.sql' ],
321 [ 'renameIndex', 'log_search', 'ls_field_val', 'PRIMARY', false, 'patch-log_search-fix-pk.sql' ],
322 [ 'renameIndex', 'module_deps', 'md_module_skin', 'PRIMARY', false,
323 'patch-module_deps-fix-pk.sql' ],
324 [ 'renameIndex', 'objectcache', 'keyname', 'PRIMARY', false, 'patch-objectcache-fix-pk.sql' ],
325 [ 'renameIndex', 'querycache_info', 'qci_type', 'PRIMARY', false,
326 'patch-querycache_info-fix-pk.sql' ],
327 [ 'renameIndex', 'site_stats', 'ss_row_id', 'PRIMARY', false, 'patch-site_stats-fix-pk.sql' ],
328 [ 'renameIndex', 'user_former_groups', 'ufg_user_group', 'PRIMARY', false,
329 'patch-user_former_groups-fix-pk.sql' ],
330 [ 'renameIndex', 'user_properties', 'user_properties_user_property', 'PRIMARY', false,
331 'patch-user_properties-fix-pk.sql' ],
332 [ 'addTable', 'comment', 'patch-comment-table.sql' ],
333 [ 'addTable', 'revision_comment_temp', 'patch-revision_comment_temp-table.sql' ],
334 // image_comment_temp is no longer needed when upgrading to MW 1.31 or newer,
335 // as it is dropped later in the update process as part of 'migrateImageCommentTemp'.
336 // File kept on disk and the updater entry here for historical purposes.
337 // [ 'addTable', 'image_comment_temp', 'patch-image_comment_temp-table.sql' ],
338 [ 'addField', 'archive', 'ar_comment_id', 'patch-archive-ar_comment_id.sql' ],
339 [ 'addField', 'filearchive', 'fa_description_id', 'patch-filearchive-fa_description_id.sql' ],
340 [ 'modifyField', 'image', 'img_description', 'patch-image-img_description-default.sql' ],
341 [ 'addField', 'ipblocks', 'ipb_reason_id', 'patch-ipblocks-ipb_reason_id.sql' ],
342 [ 'addField', 'logging', 'log_comment_id', 'patch-logging-log_comment_id.sql' ],
343 [ 'addField', 'oldimage', 'oi_description_id', 'patch-oldimage-oi_description_id.sql' ],
344 [ 'addField', 'protected_titles', 'pt_reason_id', 'patch-protected_titles-pt_reason_id.sql' ],
345 [ 'addField', 'recentchanges', 'rc_comment_id', 'patch-recentchanges-rc_comment_id.sql' ],
346 [ 'modifyField', 'revision', 'rev_comment', 'patch-revision-rev_comment-default.sql' ],
347
348 // This field was added in 1.31, but is put here so it can be used by 'migrateComments'
349 [ 'addField', 'image', 'img_description_id', 'patch-image-img_description_id.sql' ],
350
351 [ 'migrateComments' ],
352 [ 'renameIndex', 'l10n_cache', 'lc_lang_key', 'PRIMARY', false,
353 'patch-l10n_cache-primary-key.sql' ],
354 [ 'doUnsignedSyncronisation' ],
355
356 // 1.31
357 [ 'addTable', 'slots', 'patch-slots.sql' ],
358 [ 'addField', 'slots', 'slot_origin', 'patch-slot-origin.sql' ],
359 [ 'addTable', 'content', 'patch-content.sql' ],
360 [ 'addTable', 'slot_roles', 'patch-slot_roles.sql' ],
361 [ 'addTable', 'content_models', 'patch-content_models.sql' ],
362 [ 'migrateArchiveText' ],
363 [ 'addTable', 'actor', 'patch-actor-table.sql' ],
364 [ 'addTable', 'revision_actor_temp', 'patch-revision_actor_temp-table.sql' ],
365 [ 'addField', 'archive', 'ar_actor', 'patch-archive-ar_actor.sql' ],
366 [ 'addField', 'ipblocks', 'ipb_by_actor', 'patch-ipblocks-ipb_by_actor.sql' ],
367 [ 'addField', 'image', 'img_actor', 'patch-image-img_actor.sql' ],
368 [ 'addField', 'oldimage', 'oi_actor', 'patch-oldimage-oi_actor.sql' ],
369 [ 'addField', 'filearchive', 'fa_actor', 'patch-filearchive-fa_actor.sql' ],
370 [ 'addField', 'recentchanges', 'rc_actor', 'patch-recentchanges-rc_actor.sql' ],
371 [ 'addField', 'logging', 'log_actor', 'patch-logging-log_actor.sql' ],
372 [ 'migrateActors' ],
373 [ 'modifyField', 'revision', 'rev_text_id', 'patch-rev_text_id-default.sql' ],
374 [ 'modifyTable', 'site_stats', 'patch-site_stats-modify.sql' ],
375 [ 'populateArchiveRevId' ],
376 [ 'addIndex', 'recentchanges', 'rc_namespace_title_timestamp',
377 'patch-recentchanges-nttindex.sql' ],
378
379 // 1.32
380 [ 'addTable', 'change_tag_def', 'patch-change_tag_def.sql' ],
381 [ 'populateExternallinksIndex60' ],
382 [ 'modifyfield', 'externallinks', 'el_index_60',
383 'patch-externallinks-el_index_60-drop-default.sql' ],
384 [ 'runMaintenance', DeduplicateArchiveRevId::class, 'maintenance/deduplicateArchiveRevId.php' ],
385 [ 'addField', 'change_tag', 'ct_tag_id', 'patch-change_tag-tag_id.sql' ],
386 [ 'addIndex', 'archive', 'ar_revid_uniq', 'patch-archive-ar_rev_id-unique.sql' ],
387 [ 'populateContentTables' ],
388 [ 'addIndex', 'logging', 'log_type_action', 'patch-logging-log-type-action-index.sql' ],
389 [ 'dropIndex', 'logging', 'type_action', 'patch-logging-drop-type-action-index.sql' ],
390 [ 'renameIndex', 'interwiki', 'iw_prefix', 'PRIMARY', false, 'patch-interwiki-fix-pk.sql' ],
391 [ 'renameIndex', 'page_props', 'pp_page_propname', 'PRIMARY', false,
392 'patch-page_props-fix-pk.sql' ],
393 [ 'renameIndex', 'protected_titles', 'pt_namespace_title', 'PRIMARY', false,
394 'patch-protected_titles-fix-pk.sql' ],
395 [ 'renameIndex', 'site_identifiers', 'site_ids_type', 'PRIMARY', false,
396 'patch-site_identifiers-fix-pk.sql' ],
397 [ 'addIndex', 'recentchanges', 'rc_this_oldid', 'patch-recentchanges-rc_this_oldid-index.sql' ],
398 [ 'dropTable', 'transcache' ],
399 [ 'runMaintenance', PopulateChangeTagDef::class, 'maintenance/populateChangeTagDef.php' ],
400 [ 'addIndex', 'change_tag', 'change_tag_rc_tag_id',
401 'patch-change_tag-change_tag_rc_tag_id.sql' ],
402 [ 'addField', 'ipblocks', 'ipb_sitewide', 'patch-ipb_sitewide.sql' ],
403 [ 'addTable', 'ipblocks_restrictions', 'patch-ipblocks_restrictions-table.sql' ],
404 [ 'migrateImageCommentTemp' ],
405
406 // 1.33
407 [ 'dropField', 'change_tag', 'ct_tag', 'patch-drop-ct_tag.sql' ],
408 [ 'dropTable', 'valid_tag' ],
409 [ 'dropTable', 'tag_summary' ],
410 [ 'dropField', 'archive', 'ar_comment', 'patch-archive-drop-ar_comment.sql' ],
411 [ 'dropField', 'ipblocks', 'ipb_reason', 'patch-ipblocks-drop-ipb_reason.sql' ],
412 [ 'dropField', 'image', 'img_description', 'patch-image-drop-img_description.sql' ],
413 [ 'dropField', 'oldimage', 'oi_description', 'patch-oldimage-drop-oi_description.sql' ],
414 [ 'dropField', 'filearchive', 'fa_description', 'patch-filearchive-drop-fa_description.sql' ],
415 [ 'dropField', 'recentchanges', 'rc_comment', 'patch-recentchanges-drop-rc_comment.sql' ],
416 [ 'dropField', 'logging', 'log_comment', 'patch-logging-drop-log_comment.sql' ],
417 [ 'dropField', 'protected_titles', 'pt_reason', 'patch-protected_titles-drop-pt_reason.sql' ],
418 [ 'modifyTable', 'job', 'patch-job-params-mediumblob.sql' ],
419
420 // 1.34
421 [ 'dropIndex', 'archive', 'ar_usertext_timestamp',
422 'patch-drop-archive-ar_usertext_timestamp.sql' ],
423 [ 'dropIndex', 'archive', 'usertext_timestamp', 'patch-drop-archive-usertext_timestamp.sql' ],
424 [ 'dropField', 'archive', 'ar_user', 'patch-drop-archive-user-fields.sql' ],
425 [ 'dropField', 'ipblocks', 'ip_by', 'patch-drop-ipblocks-user-fields.sql' ],
426 [ 'dropIndex', 'image', 'img_user_timestamp', 'patch-drop-image-img_user_timestamp.sql' ],
427 [ 'dropField', 'image', 'img_user', 'patch-drop-image-user-fields.sql' ],
428 [ 'dropField', 'oldimage', 'oi_user', 'patch-drop-oldimage-user-fields.sql' ],
429 [ 'dropField', 'filearchive', 'fa_user', 'patch-drop-filearchive-user-fields.sql' ],
430 [ 'dropField', 'recentchanges', 'rc_user', 'patch-drop-recentchanges-user-fields.sql' ],
431 [ 'dropField', 'logging', 'log_user', 'patch-drop-logging-user-fields.sql' ],
432 [ 'addIndex', 'user_newtalk', 'un_user_ip', 'patch-rename-mysql-user_newtalk-indexes.sql' ],
433
434 // 1.35
435 [ 'addTable', 'watchlist_expiry', 'patch-watchlist_expiry.sql' ],
436 [ 'modifyField', 'page', 'page_restrictions', 'patch-page_restrictions-null.sql' ],
437 [ 'renameIndex', 'ipblocks', 'ipb_address', 'ipb_address_unique', false,
438 'patch-ipblocks-rename-ipb_address.sql' ],
439 [ 'addField', 'revision', 'rev_actor', 'patch-revision-actor-comment-MCR.sql' ],
440 [ 'dropField', 'archive', 'ar_text_id', 'patch-archive-MCR.sql' ],
441 [ 'doLanguageLinksLengthSync' ],
442 [ 'doFixIpbAddressUniqueIndex' ],
443 [ 'modifyField', 'actor', 'actor_name', 'patch-actor-actor_name-varbinary.sql' ],
444 [ 'modifyField', 'sites', 'site_global_key', 'patch-sites-site_global_key.sql' ],
445 [ 'modifyField', 'iwlinks', 'iwl_prefix', 'patch-extend-iwlinks-iwl_prefix.sql' ],
446 ];
447 }
448
458 protected function checkBin( $table, $field, $patchFile ) {
459 if ( !$this->doTable( $table ) ) {
460 return true;
461 }
462
464 $fieldInfo = $this->db->fieldInfo( $table, $field );
465 if ( $fieldInfo->isBinary() ) {
466 $this->output( "...$table table has correct $field encoding.\n" );
467 } else {
468 $this->applyPatch( $patchFile, false, "Fixing $field encoding on $table table" );
469 }
470 }
471
480 protected function indexHasField( $table, $index, $field ) {
481 $info = $this->db->indexInfo( $table, $index, __METHOD__ );
482 if ( $info ) {
483 foreach ( $info as $row ) {
484 if ( $row->Column_name == $field ) {
485 $this->output( "...index $index on table $table includes field $field.\n" );
486 return true;
487 }
488 }
489 }
490 $this->output( "...index $index on table $table has no field $field; added.\n" );
491
492 return false;
493 }
494
498 protected function doInterwikiUpdate() {
499 global $IP;
500
501 if ( !$this->doTable( 'interwiki' ) ) {
502 return;
503 }
504
505 if ( $this->db->tableExists( "interwiki", __METHOD__ ) ) {
506 $this->output( "...already have interwiki table\n" );
507
508 return;
509 }
510
511 $this->applyPatch( 'patch-interwiki.sql', false, 'Creating interwiki table' );
512 $this->applyPatch(
513 "$IP/maintenance/interwiki.sql",
514 true,
515 'Adding default interwiki definitions'
516 );
517 }
518
522 protected function doIndexUpdate() {
523 $meta = $this->db->fieldInfo( 'recentchanges', 'rc_timestamp' );
524 if ( $meta === false ) {
525 throw new MWException( 'Missing rc_timestamp field of recentchanges table. Should not happen.' );
526 }
527 if ( $meta->isMultipleKey() ) {
528 $this->output( "...indexes seem up to 20031107 standards.\n" );
529
530 return;
531 }
532
533 $this->applyPatch( 'patch-indexes.sql', true, "Updating indexes to 20031107" );
534 }
535
536 protected function doOldLinksUpdate() {
537 $cl = $this->maintenance->runChild( ConvertLinks::class );
538 $cl->execute();
539 }
540
541 protected function doFixAncientImagelinks() {
542 $info = $this->db->fieldInfo( 'imagelinks', 'il_from' );
543 if ( !$info || $info->type() !== 'string' ) {
544 $this->output( "...il_from OK\n" );
545
546 return;
547 }
548
549 $applied = $this->applyPatch(
550 'patch-fix-il_from.sql',
551 false,
552 'Fixing ancient broken imagelinks table.'
553 );
554
555 if ( $applied ) {
556 $this->output( "NOTE: you will have to run maintenance/refreshLinks.php after this." );
557 }
558 }
559
563 protected function doWatchlistUpdate() {
565
566 $sql = $this->db->unionQueries(
567 [
568 // Missing talk page rows (corresponding subject page row exists)
569 $this->db->selectSQLText(
570 [ 'wlsubject' => 'watchlist', 'wltalk' => 'watchlist' ],
571 [
572 'wl_user' => 'wlsubject.wl_user',
573 'wl_namespace' => 'wlsubject.wl_namespace | 1',
574 'wl_title' => 'wlsubject.wl_title',
575 'wl_notificationtimestamp' => 'wlsubject.wl_notificationtimestamp'
576 ],
577 [ 'NOT (wlsubject.wl_namespace & 1)', 'wltalk.wl_namespace IS NULL' ],
578 __METHOD__,
579 [],
580 [
581 'wltalk' => [ 'LEFT JOIN', [
582 'wltalk.wl_user = wlsubject.wl_user',
583 'wltalk.wl_namespace = (wlsubject.wl_namespace | 1)',
584 'wltalk.wl_title = wlsubject.wl_title'
585 ] ]
586 ]
587 ),
588 // Missing subject page rows (corresponding talk page row exists)
589 $this->db->selectSQLText(
590 [ 'wltalk' => 'watchlist', 'wlsubject' => 'watchlist' ],
591 [
592 'wl_user' => 'wltalk.wl_user',
593 'wl_namespace' => 'wltalk.wl_namespace & ~1',
594 'wl_title' => 'wltalk.wl_title',
595 'wl_notificationtimestamp' => 'wltalk.wl_notificationtimestamp'
596 ],
597 [ 'wltalk.wl_namespace & 1', 'wlsubject.wl_namespace IS NULL' ],
598 __METHOD__,
599 [],
600 [
601 'wlsubject' => [ 'LEFT JOIN', [
602 'wlsubject.wl_user = wltalk.wl_user',
603 'wlsubject.wl_namespace = (wltalk.wl_namespace & ~1)',
604 'wlsubject.wl_title = wltalk.wl_title'
605 ] ]
606 ]
607 )
608 ],
609 true // use a non-distinct UNION to avoid overhead
610 );
611
612 $res = $this->db->query( $sql, __METHOD__ );
613
614 if ( !$res->numRows() ) {
615 $this->output( "...watchlist talk page rows already present.\n" );
616 return;
617 }
618
619 $this->output( "Adding missing corresponding talk/subject watchlist page rows... " );
620
621 $rowBatch = [];
622 foreach ( $res as $row ) {
623 $rowBatch[] = (array)$row;
624 if ( count( $rowBatch ) >= $wgUpdateRowsPerQuery ) {
625 $this->db->insert( 'watchlist', $rowBatch, __METHOD__, [ 'IGNORE' ] );
626 $rowBatch = [];
627 }
628 }
629 $this->db->insert( 'watchlist', $rowBatch, __METHOD__, [ 'IGNORE' ] );
630
631 $this->output( "done.\n" );
632 }
633
634 protected function doSchemaRestructuring() {
635 if ( $this->db->tableExists( 'page', __METHOD__ ) ) {
636 $this->output( "...page table already exists.\n" );
637
638 return;
639 }
640
641 $this->output( "...converting from cur/old to page/revision/text DB structure.\n" );
642 $this->output( wfTimestamp( TS_DB ) );
643 $this->output( "......checking for duplicate entries.\n" );
644
645 list( $cur, $old, $page, $revision, $text ) = $this->db->tableNamesN(
646 'cur',
647 'old',
648 'page',
649 'revision',
650 'text'
651 );
652
653 $rows = $this->db->query( "
654 SELECT cur_title, cur_namespace, COUNT(cur_namespace) AS c
655 FROM $cur
656 GROUP BY cur_title, cur_namespace
657 HAVING c>1",
658 __METHOD__
659 );
660
661 if ( $rows->numRows() > 0 ) {
662 $this->output( wfTimestamp( TS_DB ) );
663 $this->output( "......<b>Found duplicate entries</b>\n" );
664 $this->output( sprintf( "<b> %-60s %3s %5s</b>\n", 'Title', 'NS', 'Count' ) );
665 $duplicate = [];
666 foreach ( $rows as $row ) {
667 if ( !isset( $duplicate[$row->cur_namespace] ) ) {
668 $duplicate[$row->cur_namespace] = [];
669 }
670
671 $duplicate[$row->cur_namespace][] = $row->cur_title;
672 $this->output( sprintf(
673 " %-60s %3s %5s\n",
674 $row->cur_title, $row->cur_namespace,
675 $row->c
676 ) );
677 }
678 $sql = "SELECT cur_title, cur_namespace, cur_id, cur_timestamp FROM $cur WHERE ";
679 $dupeTitles = [];
680 foreach ( $duplicate as $ns => $titles ) {
681 $dupeTitles[] = "( cur_namespace = {$ns} AND cur_title in ("
682 . $this->db->makeList( $titles ) . ") ) \n";
683 }
684 $sql .= $this->db->makeList( $dupeTitles, IDatabase::LIST_OR );
685 # By sorting descending, the most recent entry will be the first in the list.
686 # All following entries will be deleted by the next while-loop.
687 $sql .= 'ORDER BY cur_namespace, cur_title, cur_timestamp DESC';
688
689 $rows = $this->db->query( $sql, __METHOD__ );
690
691 $prev_title = $prev_namespace = false;
692 $deleteId = [];
693
694 foreach ( $rows as $row ) {
695 if ( $prev_title == $row->cur_title && $prev_namespace == $row->cur_namespace ) {
696 $deleteId[] = (int)$row->cur_id;
697 }
698 $prev_title = $row->cur_title;
699 $prev_namespace = $row->cur_namespace;
700 }
701 $sql = "DELETE FROM $cur WHERE cur_id IN ( " . implode( ',', $deleteId ) . ')';
702 $this->db->query( $sql, __METHOD__ );
703 $this->output( wfTimestamp( TS_DB ) );
704 $this->output( "......<b>Deleted</b> " . $this->db->affectedRows() . " records.\n" );
705 }
706
707 $this->output( wfTimestamp( TS_DB ) );
708 $this->output( "......Creating tables.\n" );
709 $this->db->query( "CREATE TABLE $page (
710 page_id int(8) unsigned NOT NULL auto_increment,
711 page_namespace int NOT NULL,
712 page_title varchar(255) binary NOT NULL,
713 page_restrictions tinyblob NOT NULL,
714 page_is_redirect tinyint(1) unsigned NOT NULL default '0',
715 page_is_new tinyint(1) unsigned NOT NULL default '0',
716 page_random real unsigned NOT NULL,
717 page_touched char(14) binary NOT NULL default '',
718 page_latest int(8) unsigned NOT NULL,
719 page_len int(8) unsigned NOT NULL,
720
721 PRIMARY KEY page_id (page_id),
722 UNIQUE INDEX name_title (page_namespace,page_title),
723 INDEX (page_random),
724 INDEX (page_len)
725 ) ENGINE=InnoDB", __METHOD__ );
726 $this->db->query( "CREATE TABLE $revision (
727 rev_id int(8) unsigned NOT NULL auto_increment,
728 rev_page int(8) unsigned NOT NULL,
729 rev_comment tinyblob NOT NULL,
730 rev_user int(5) unsigned NOT NULL default '0',
731 rev_user_text varchar(255) binary NOT NULL default '',
732 rev_timestamp char(14) binary NOT NULL default '',
733 rev_minor_edit tinyint(1) unsigned NOT NULL default '0',
734 rev_deleted tinyint(1) unsigned NOT NULL default '0',
735 rev_len int(8) unsigned,
736 rev_parent_id int(8) unsigned default NULL,
737 PRIMARY KEY rev_page_id (rev_page, rev_id),
738 UNIQUE INDEX rev_id (rev_id),
739 INDEX rev_timestamp (rev_timestamp),
740 INDEX page_timestamp (rev_page,rev_timestamp),
741 INDEX user_timestamp (rev_user,rev_timestamp),
742 INDEX usertext_timestamp (rev_user_text,rev_timestamp)
743 ) ENGINE=InnoDB", __METHOD__ );
744
745 $this->output( wfTimestamp( TS_DB ) );
746 $this->output( "......Locking tables.\n" );
747 $this->db->query(
748 "LOCK TABLES $page WRITE, $revision WRITE, $old WRITE, $cur WRITE",
749 __METHOD__
750 );
751
752 $maxold = intval( $this->db->selectField( 'old', 'max(old_id)', '', __METHOD__ ) );
753 $this->output( wfTimestamp( TS_DB ) );
754 $this->output( "......maxold is {$maxold}\n" );
755
756 $this->output( wfTimestamp( TS_DB ) );
759 // Create HistoryBlobCurStub entries.
760 // Text will be pulled from the leftover 'cur' table at runtime.
761 $this->output( "......Moving metadata from cur; using blob references to text in cur table.\n" );
762 $cur_text = "concat('O:18:\"historyblobcurstub\":1:{s:6:\"mCurId\";i:',cur_id,';}')";
763 $cur_flags = "'object'";
764 } else {
765 // Copy all cur text in immediately: this may take longer but avoids
766 // having to keep an extra table around.
767 $this->output( "......Moving text from cur.\n" );
768 $cur_text = 'cur_text';
769 $cur_flags = "''";
770 }
771 $this->db->query(
772 "INSERT INTO $old (old_namespace, old_title, old_text, old_comment, old_user,
773 old_user_text, old_timestamp, old_minor_edit, old_flags)
774 SELECT cur_namespace, cur_title, $cur_text, cur_comment, cur_user, cur_user_text,
775 cur_timestamp, cur_minor_edit, $cur_flags
776 FROM $cur",
777 __METHOD__
778 );
779
780 $this->output( wfTimestamp( TS_DB ) );
781 $this->output( "......Setting up revision table.\n" );
782 $this->db->query(
783 "INSERT INTO $revision (rev_id, rev_page, rev_comment, rev_user,
784 rev_user_text, rev_timestamp, rev_minor_edit)
785 SELECT old_id, cur_id, old_comment, old_user, old_user_text,
786 old_timestamp, old_minor_edit
787 FROM $old,$cur WHERE old_namespace=cur_namespace AND old_title=cur_title",
788 __METHOD__
789 );
790
791 $this->output( wfTimestamp( TS_DB ) );
792 $this->output( "......Setting up page table.\n" );
793 $this->db->query(
794 "INSERT INTO $page (page_id, page_namespace, page_title,
795 page_restrictions, page_is_redirect, page_is_new, page_random,
796 page_touched, page_latest, page_len)
797 SELECT cur_id, cur_namespace, cur_title, cur_restrictions,
798 cur_is_redirect, cur_is_new, cur_random, cur_touched, rev_id, LENGTH(cur_text)
799 FROM $cur,$revision
800 WHERE cur_id=rev_page AND rev_timestamp=cur_timestamp AND rev_id > {$maxold}",
801 __METHOD__
802 );
803
804 $this->output( wfTimestamp( TS_DB ) );
805 $this->output( "......Unlocking tables.\n" );
806 $this->db->query( "UNLOCK TABLES", __METHOD__ );
807
808 $this->output( wfTimestamp( TS_DB ) );
809 $this->output( "......Renaming old.\n" );
810 $this->db->query( "ALTER TABLE $old RENAME TO $text", __METHOD__ );
811
812 $this->output( wfTimestamp( TS_DB ) );
813 $this->output( "...done.\n" );
814 }
815
816 protected function doNamespaceSize() {
817 $tables = [
818 'page' => 'page',
819 'archive' => 'ar',
820 'recentchanges' => 'rc',
821 'watchlist' => 'wl',
822 'querycache' => 'qc',
823 'logging' => 'log',
824 ];
825 foreach ( $tables as $table => $prefix ) {
826 $field = $prefix . '_namespace';
827
828 $tablename = $this->db->tableName( $table );
829 $result = $this->db->query( "SHOW COLUMNS FROM $tablename LIKE '$field'", __METHOD__ );
830 $info = $this->db->fetchObject( $result );
831
832 if ( substr( $info->Type, 0, 3 ) == 'int' ) {
833 $this->output( "...$field is already a full int ($info->Type).\n" );
834 } else {
835 $this->output( "Promoting $field from $info->Type to int... " );
836 $this->db->query( "ALTER TABLE $tablename MODIFY $field int NOT NULL", __METHOD__ );
837 $this->output( "done.\n" );
838 }
839 }
840 }
841
842 protected function doPagelinksUpdate() {
843 if ( $this->db->tableExists( 'pagelinks', __METHOD__ ) ) {
844 $this->output( "...already have pagelinks table.\n" );
845
846 return;
847 }
848
849 $this->applyPatch(
850 'patch-pagelinks.sql',
851 false,
852 'Converting links and brokenlinks tables to pagelinks'
853 );
854
855 foreach (
856 MediaWikiServices::getInstance()->getContentLanguage()->getNamespaces() as $ns => $name
857 ) {
858 if ( $ns == 0 ) {
859 continue;
860 }
861
862 $this->output( "Cleaning up broken links for namespace $ns... " );
863 $this->db->update( 'pagelinks',
864 [
865 'pl_namespace' => $ns,
866 "pl_title = TRIM(LEADING {$this->db->addQuotes( "$name:" )} FROM pl_title)",
867 ],
868 [
869 'pl_namespace' => 0,
870 'pl_title' . $this->db->buildLike( "$name:", $this->db->anyString() ),
871 ],
872 __METHOD__
873 );
874 $this->output( "done.\n" );
875 }
876 }
877
878 protected function doUserUniqueUpdate() {
879 if ( !$this->doTable( 'user' ) ) {
880 return true;
881 }
882
883 $duper = new UserDupes( $this->db, [ $this, 'output' ] );
884 if ( $duper->hasUniqueIndex() ) {
885 $this->output( "...already have unique user_name index.\n" );
886
887 return;
888 }
889
890 if ( !$duper->clearDupes() ) {
891 $this->output( "WARNING: This next step will probably fail due to unfixed duplicates...\n" );
892 }
893 $this->applyPatch( 'patch-user_nameindex.sql', false, "Adding unique index on user_name" );
894 }
895
896 protected function doUserGroupsUpdate() {
897 if ( !$this->doTable( 'user_groups' ) ) {
898 return true;
899 }
900
901 if ( $this->db->tableExists( 'user_groups', __METHOD__ ) ) {
902 $info = $this->db->fieldInfo( 'user_groups', 'ug_group' );
903 if ( $info->type() == 'int' ) {
904 $oldug = $this->db->tableName( 'user_groups' );
905 $newug = $this->db->tableName( 'user_groups_bogus' );
906 $this->output( "user_groups table exists but is in bogus intermediate " .
907 "format. Renaming to $newug... " );
908 $this->db->query( "ALTER TABLE $oldug RENAME TO $newug", __METHOD__ );
909 $this->output( "done.\n" );
910
911 $this->applyPatch( 'patch-user_groups.sql', false, "Re-adding fresh user_groups table" );
912
913 $this->output( "***\n" );
914 $this->output( "*** WARNING: You will need to manually fix up user " .
915 "permissions in the user_groups\n" );
916 $this->output( "*** table. Old 1.5 alpha versions did some pretty funky stuff...\n" );
917 $this->output( "***\n" );
918 } else {
919 $this->output( "...user_groups table exists and is in current format.\n" );
920 }
921
922 return;
923 }
924
925 $this->applyPatch( 'patch-user_groups.sql', false, "Adding user_groups table" );
926
927 if ( !$this->db->tableExists( 'user_rights', __METHOD__ ) ) {
928 if ( $this->db->fieldExists( 'user', 'user_rights', __METHOD__ ) ) {
929 $this->applyPatch(
930 'patch-user_rights.sql',
931 false,
932 'Upgrading from a 1.3 or older database? Breaking out user_rights for conversion'
933 );
934 } else {
935 $this->output( "*** WARNING: couldn't locate user_rights table or field for upgrade.\n" );
936 $this->output( "*** You may need to manually configure some sysops by manipulating\n" );
937 $this->output( "*** the user_groups table.\n" );
938
939 return;
940 }
941 }
942
943 $this->output( "Converting user_rights table to user_groups... " );
944 $result = $this->db->select( 'user_rights',
945 [ 'ur_user', 'ur_rights' ],
946 [ "ur_rights != ''" ],
947 __METHOD__ );
948
949 foreach ( $result as $row ) {
950 $groups = array_unique(
951 array_map( 'trim',
952 explode( ',', $row->ur_rights ) ) );
953
954 foreach ( $groups as $group ) {
955 $this->db->insert( 'user_groups',
956 [
957 'ug_user' => $row->ur_user,
958 'ug_group' => $group ],
959 __METHOD__ );
960 }
961 }
962 $this->output( "done.\n" );
963 }
964
969 protected function doWatchlistNull() {
970 $info = $this->db->fieldInfo( 'watchlist', 'wl_notificationtimestamp' );
971 if ( !$info ) {
972 return;
973 }
974 if ( $info->isNullable() ) {
975 $this->output( "...wl_notificationtimestamp is already nullable.\n" );
976
977 return;
978 }
979
980 $this->applyPatch(
981 'patch-watchlist-null.sql',
982 false,
983 'Making wl_notificationtimestamp nullable'
984 );
985 }
986
992 protected function doPageRandomUpdate() {
993 $page = $this->db->tableName( 'page' );
994 $this->db->query( "UPDATE $page SET page_random = RAND() WHERE page_random = 0", __METHOD__ );
995 $rows = $this->db->affectedRows();
996
997 if ( $rows ) {
998 $this->output( "Set page_random to a random value on $rows rows where it was set to 0\n" );
999 } else {
1000 $this->output( "...no page_random rows needed to be set\n" );
1001 }
1002 }
1003
1004 protected function doTemplatelinksUpdate() {
1005 if ( $this->db->tableExists( 'templatelinks', __METHOD__ ) ) {
1006 $this->output( "...templatelinks table already exists\n" );
1007
1008 return;
1009 }
1010
1011 $this->applyPatch( 'patch-templatelinks.sql', false, "Creating templatelinks table" );
1012
1013 $this->output( "Populating...\n" );
1014 $services = MediaWikiServices::getInstance();
1015 if ( $services->getDBLoadBalancer()->getServerCount() > 1 ) {
1016 // Slow, replication-friendly update
1017 $res = $this->db->select( 'pagelinks', [ 'pl_from', 'pl_namespace', 'pl_title' ],
1018 [ 'pl_namespace' => NS_TEMPLATE ], __METHOD__ );
1019 $count = 0;
1020 foreach ( $res as $row ) {
1021 $count = ( $count + 1 ) % 100;
1022 if ( $count == 0 ) {
1023 $lbFactory = $services->getDBLoadBalancerFactory();
1024 $lbFactory->waitForReplication( [
1025 'domain' => $lbFactory->getLocalDomainID(),
1027 ] );
1028 }
1029 $this->db->insert( 'templatelinks',
1030 [
1031 'tl_from' => $row->pl_from,
1032 'tl_namespace' => $row->pl_namespace,
1033 'tl_title' => $row->pl_title,
1034 ], __METHOD__
1035 );
1036 }
1037 } else {
1038 // Fast update
1039 $this->db->insertSelect( 'templatelinks', 'pagelinks',
1040 [
1041 'tl_from' => 'pl_from',
1042 'tl_namespace' => 'pl_namespace',
1043 'tl_title' => 'pl_title'
1044 ], [
1045 'pl_namespace' => 10
1046 ], __METHOD__,
1047 [ 'NO_AUTO_COLUMNS' ] // There's no "tl_id" auto-increment field
1048 );
1049 }
1050 $this->output( "Done. Please run maintenance/refreshLinks.php for a more " .
1051 "thorough templatelinks update.\n" );
1052 }
1053
1054 protected function doBacklinkingIndicesUpdate() {
1055 if ( !$this->indexHasField( 'pagelinks', 'pl_namespace', 'pl_from' ) ||
1056 !$this->indexHasField( 'templatelinks', 'tl_namespace', 'tl_from' ) ||
1057 !$this->indexHasField( 'imagelinks', 'il_to', 'il_from' )
1058 ) {
1059 $this->applyPatch( 'patch-backlinkindexes.sql', false, "Updating backlinking indices" );
1060 }
1061 }
1062
1068 protected function doRestrictionsUpdate() {
1069 if ( $this->db->tableExists( 'page_restrictions', __METHOD__ ) ) {
1070 $this->output( "...page_restrictions table already exists.\n" );
1071
1072 return;
1073 }
1074
1075 $this->applyPatch(
1076 'patch-page_restrictions.sql',
1077 false,
1078 'Creating page_restrictions table (1/2)'
1079 );
1080 $this->applyPatch(
1081 'patch-page_restrictions_sortkey.sql',
1082 false,
1083 'Creating page_restrictions table (2/2)'
1084 );
1085 $this->output( "done.\n" );
1086
1087 $this->output( "Migrating old restrictions to new table...\n" );
1088 $task = $this->maintenance->runChild( UpdateRestrictions::class );
1089 $task->execute();
1090 }
1091
1092 protected function doCategorylinksIndicesUpdate() {
1093 if ( !$this->indexHasField( 'categorylinks', 'cl_sortkey', 'cl_from' ) ) {
1094 $this->applyPatch( 'patch-categorylinksindex.sql', false, "Updating categorylinks Indices" );
1095 }
1096 }
1097
1098 protected function doCategoryPopulation() {
1099 if ( $this->updateRowExists( 'populate category' ) ) {
1100 $this->output( "...category table already populated.\n" );
1101
1102 return;
1103 }
1104
1105 $this->output(
1106 "Populating category table, printing progress markers. " .
1107 "For large databases, you\n" .
1108 "may want to hit Ctrl-C and do this manually with maintenance/\n" .
1109 "populateCategory.php.\n"
1110 );
1111 $task = $this->maintenance->runChild( PopulateCategory::class );
1112 $task->execute();
1113 $this->output( "Done populating category table.\n" );
1114 }
1115
1116 protected function doPopulateParentId() {
1117 if ( !$this->updateRowExists( 'populate rev_parent_id' ) ) {
1118 $this->output(
1119 "Populating rev_parent_id fields, printing progress markers. For large\n" .
1120 "databases, you may want to hit Ctrl-C and do this manually with\n" .
1121 "maintenance/populateParentId.php.\n" );
1122
1123 $task = $this->maintenance->runChild( PopulateParentId::class );
1124 $task->execute();
1125 }
1126 }
1127
1128 protected function doFilearchiveIndicesUpdate() {
1129 $info = $this->db->indexInfo( 'filearchive', 'fa_user_timestamp', __METHOD__ );
1130 if ( !$info ) {
1131 $this->applyPatch( 'patch-filearchive-user-index.sql', false, "Updating filearchive indices" );
1132 }
1133
1134 return true;
1135 }
1136
1137 protected function doNonUniquePlTlIl() {
1138 $info = $this->db->indexInfo( 'pagelinks', 'pl_namespace', __METHOD__ );
1139 if ( is_array( $info ) && $info[0]->Non_unique ) {
1140 $this->output( "...pl_namespace, tl_namespace, il_to indices are already non-UNIQUE.\n" );
1141
1142 return true;
1143 }
1144 if ( $this->skipSchema ) {
1145 $this->output( "...skipping schema change (making pl_namespace, tl_namespace " .
1146 "and il_to indices non-UNIQUE).\n" );
1147
1148 return false;
1149 }
1150
1151 return $this->applyPatch(
1152 'patch-pl-tl-il-nonunique.sql',
1153 false,
1154 'Making pl_namespace, tl_namespace and il_to indices non-UNIQUE'
1155 );
1156 }
1157
1158 protected function doUpdateMimeMinorField() {
1159 if ( $this->updateRowExists( 'mime_minor_length' ) ) {
1160 $this->output( "...*_mime_minor fields are already long enough.\n" );
1161
1162 return;
1163 }
1164
1165 $this->applyPatch(
1166 'patch-mime_minor_length.sql',
1167 false,
1168 'Altering all *_mime_minor fields to 100 bytes in size'
1169 );
1170 }
1171
1172 protected function doClFieldsUpdate() {
1173 if ( $this->updateRowExists( 'cl_fields_update' ) ) {
1174 $this->output( "...categorylinks up-to-date.\n" );
1175
1176 return;
1177 }
1178
1179 $this->applyPatch(
1180 'patch-categorylinks-better-collation2.sql',
1181 false,
1182 'Updating categorylinks (again)'
1183 );
1184 }
1185
1186 protected function doLanguageLinksLengthSync() {
1187 $sync = [
1188 [ 'table' => 'l10n_cache', 'field' => 'lc_lang' ],
1189 [ 'table' => 'langlinks', 'field' => 'll_lang' ],
1190 [ 'table' => 'sites', 'field' => 'site_language' ],
1191 ];
1192
1193 foreach ( $sync as $s ) {
1194 $table = $this->db->tableName( $s['table'] );
1195 $field = $s['field'];
1196 $res = $this->db->query( "SHOW COLUMNS FROM $table LIKE '$field'", __METHOD__ );
1197 $row = $this->db->fetchObject( $res );
1198
1199 if ( $row && $row->Type !== "varbinary(35)" ) {
1200 $this->applyPatch(
1201 "patch-{$s['table']}-$field-35.sql",
1202 false,
1203 "Updating length of $field in $table"
1204 );
1205 } else {
1206 $this->output( "...$field is up-to-date.\n" );
1207 }
1208 }
1209 }
1210
1211 protected function doFixIpbAddressUniqueIndex() {
1212 if ( !$this->doTable( 'ipblocks' ) ) {
1213 return;
1214 }
1215
1216 if ( !$this->indexHasField( 'ipblocks', 'ipb_address_unique', 'ipb_anon_only' ) ) {
1217 $this->output( "...ipb_address_unique index up-to-date.\n" );
1218 return;
1219 }
1220
1221 $this->applyPatch(
1222 'patch-ipblocks-fix-ipb_address_unique.sql',
1223 false,
1224 'Removing ipb_anon_only column from ipb_address_unique index'
1225 );
1226 }
1227
1228 protected function doUserNewTalkTimestampNotNull() {
1229 if ( !$this->doTable( 'user_newtalk' ) ) {
1230 return true;
1231 }
1232
1233 $info = $this->db->fieldInfo( 'user_newtalk', 'user_last_timestamp' );
1234 if ( $info === false ) {
1235 return;
1236 }
1237 if ( $info->isNullable() ) {
1238 $this->output( "...user_last_timestamp is already nullable.\n" );
1239
1240 return;
1241 }
1242
1243 $this->applyPatch(
1244 'patch-user-newtalk-timestamp-null.sql',
1245 false,
1246 'Making user_last_timestamp nullable'
1247 );
1248 }
1249
1250 protected function doIwlinksIndexNonUnique() {
1251 $info = $this->db->indexInfo( 'iwlinks', 'iwl_prefix_title_from', __METHOD__ );
1252 if ( is_array( $info ) && $info[0]->Non_unique ) {
1253 $this->output( "...iwl_prefix_title_from index is already non-UNIQUE.\n" );
1254
1255 return true;
1256 }
1257 if ( $this->skipSchema ) {
1258 $this->output( "...skipping schema change (making iwl_prefix_title_from index non-UNIQUE).\n" );
1259
1260 return false;
1261 }
1262
1263 return $this->applyPatch(
1264 'patch-iwl_prefix_title_from-non-unique.sql',
1265 false,
1266 'Making iwl_prefix_title_from index non-UNIQUE'
1267 );
1268 }
1269
1270 protected function doUnsignedSyncronisation() {
1271 $sync = [
1272 [ 'table' => 'bot_passwords', 'field' => 'bp_user' ],
1273 [ 'table' => 'change_tag', 'field' => 'ct_log_id' ],
1274 [ 'table' => 'change_tag', 'field' => 'ct_rev_id' ],
1275 [ 'table' => 'page_restrictions', 'field' => 'pr_user' ],
1276 [ 'table' => 'user_newtalk', 'field' => 'user_id' ],
1277 [ 'table' => 'user_properties', 'field' => 'up_user' ],
1278 ];
1279
1280 foreach ( $sync as $s ) {
1281 if ( !$this->doTable( $s['table'] ) ) {
1282 continue;
1283 }
1284
1285 $info = $this->db->fieldInfo( $s['table'], $s['field'] );
1286 if ( $info === false ) {
1287 continue;
1288 }
1289 $fullName = "{$s['table']}.{$s['field']}";
1290 if ( $info->isUnsigned() ) {
1291 $this->output( "...$fullName is already unsigned int.\n" );
1292
1293 continue;
1294 }
1295
1296 $this->applyPatch(
1297 "patch-{$s['table']}-{$s['field']}-unsigned.sql",
1298 false,
1299 "Making $fullName into an unsigned int"
1300 );
1301 }
1302
1303 return true;
1304 }
1305
1306 protected function doRevisionPageRevIndexNonUnique() {
1307 if ( !$this->doTable( 'revision' ) ) {
1308 return true;
1309 } elseif ( !$this->db->indexExists( 'revision', 'rev_page_id', __METHOD__ ) ) {
1310 $this->output( "...rev_page_id index not found on revision.\n" );
1311 return true;
1312 }
1313
1314 if ( !$this->db->indexUnique( 'revision', 'rev_page_id', __METHOD__ ) ) {
1315 $this->output( "...rev_page_id index already non-unique.\n" );
1316 return true;
1317 }
1318
1319 return $this->applyPatch(
1320 'patch-revision-page-rev-index-nonunique.sql',
1321 false,
1322 'Making rev_page_id index non-unique'
1323 );
1324 }
1325
1326 protected function doExtendCommentLengths() {
1327 $table = $this->db->tableName( 'revision' );
1328 $res = $this->db->query( "SHOW COLUMNS FROM $table LIKE 'rev_comment'", __METHOD__ );
1329 $row = $this->db->fetchObject( $res );
1330
1331 if ( $row && ( $row->Type !== "varbinary(767)" || $row->Default !== "" ) ) {
1332 $this->applyPatch(
1333 'patch-editsummary-length.sql',
1334 false,
1335 'Extending edit summary lengths (and setting defaults)'
1336 );
1337 } else {
1338 $this->output( "...comment fields are up to date.\n" );
1339 }
1340 }
1341
1342 public function getSchemaVars() {
1343 global $wgDBTableOptions;
1344
1345 $vars = [];
1346 $vars['wgDBTableOptions'] = str_replace( 'TYPE', 'ENGINE', $wgDBTableOptions );
1347 $vars['wgDBTableOptions'] = str_replace(
1348 'CHARSET=mysql4',
1349 'CHARSET=binary',
1350 $vars['wgDBTableOptions']
1351 );
1352
1353 return $vars;
1354 }
1355}
$wgLegacySchemaConversion
If set to true, the MediaWiki 1.4 to 1.5 schema conversion will create stub reference rows in the tex...
$wgDBTableOptions
MySQL table options to use during installation or update.
$wgUpdateRowsPerQuery
Number of rows to update per query.
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
$IP
Definition WebStart.php:49
Class for handling database updates.
updateRowExists( $key)
Helper function: check if the given key is present in the updatelog table.
output( $str)
Output some text.
applyPatch( $path, $isFullPath=false, $msg=null)
Applies a SQL patch.
doTable( $name)
Returns whether updates should be executed on the database table $name.
MediaWiki exception.
MediaWikiServices is the service locator for the application scope of MediaWiki.
doWatchlistNull()
Make sure wl_notificationtimestamp can be NULL, and update old broken items.
doInterwikiUpdate()
Check that interwiki table exists; if it doesn't source it.
getSchemaVars()
Get appropriate schema variables in the current database connection.
doRestrictionsUpdate()
Adding page_restrictions table, obsoleting page.page_restrictions.
checkBin( $table, $field, $patchFile)
MW 1.4 betas were missing the 'binary' marker from logging.log_title, which caused a MySQL collation ...
doIndexUpdate()
Check that proper indexes are in place.
getCoreUpdateList()
Get an array of updates to perform on the database.
doRevisionPageRevIndexNonUnique()
indexHasField( $table, $index, $field)
Check whether an index contain a field.
doPageRandomUpdate()
Set page_random field to a random value where it is equals to 0.
doWatchlistUpdate()
Check if we need to add talk page rows to the watchlist.
doUserNewTalkTimestampNotNull()
Look for duplicate user table entries and optionally prune them.
Definition userDupes.inc:37
const NS_TEMPLATE
Definition Defines.php:80
Basic database interface for live and lazy-loaded relation database handles.
Definition IDatabase.php:38