MediaWiki REL1_40
PostgresUpdater.php
Go to the documentation of this file.
1<?php
25
33
37 protected $db;
38
42 protected function getCoreUpdateList() {
43 return [
44 // 1.35 but must come first
45 [ 'addPgField', 'revision', 'rev_actor', 'INTEGER NOT NULL DEFAULT 0' ],
46 [ 'addPgIndex', 'revision', 'rev_actor_timestamp', '(rev_actor,rev_timestamp,rev_id)' ],
47 [ 'addPgIndex', 'revision', 'rev_page_actor_timestamp', '(rev_page,rev_actor,rev_timestamp)' ],
48
49 // Exception to the sequential updates. Renaming pagecontent and mwuser.
50 // Introduced in 1.36.
51 [ 'renameTable', 'pagecontent', 'text' ],
52 // Introduced in 1.37.
53 [ 'renameTable', 'mwuser', 'user' ],
54
55 // 1.35
56 [ 'addIndex', 'redirect', 'redirect_pkey', 'patch-redirect-pk.sql' ],
57 [ 'addTable', 'watchlist_expiry', 'patch-watchlist_expiry.sql' ],
58 [ 'setSequenceOwner', 'watchlist_expiry', 'we_item', 'watchlist_expiry_we_item_seq' ],
59 [ 'setDefault', 'user_newtalk', 'user_ip', '' ],
60 [ 'changeNullableField', 'user_newtalk', 'user_ip', 'NOT NULL', true ],
61 [ 'setDefault', 'user_newtalk', 'user_id', 0 ],
62 [ 'dropPgIndex', 'revision', 'rev_user_idx' ],
63 [ 'dropPgIndex', 'revision', 'rev_user_text_idx' ],
64 [ 'dropPgIndex', 'revision', 'rev_text_id_idx' ],
65 [ 'dropPgField', 'revision', 'rev_user' ],
66 [ 'dropPgField', 'revision', 'rev_user_text' ],
67 [ 'dropPgField', 'revision', 'rev_comment' ],
68 [ 'dropPgField', 'revision', 'rev_text_id' ],
69 [ 'dropPgField', 'revision', 'rev_content_model' ],
70 [ 'dropPgField', 'revision', 'rev_content_format' ],
71 [ 'addPgField', 'revision', 'rev_comment_id', 'INTEGER NOT NULL DEFAULT 0' ],
72 [ 'dropPgField', 'archive', 'ar_text_id' ],
73 [ 'dropPgField', 'archive', 'ar_content_model' ],
74 [ 'dropPgField', 'archive', 'ar_content_format' ],
75 [ 'changeField', 'updatelog', 'ul_key', 'varchar(255)', '' ],
76 [ 'changeField', 'updatelog', 'ul_value', 'TEXT', '' ],
77 [ 'changeField', 'site_identifiers', 'si_type', 'TEXT', '' ],
78 [ 'changeField', 'site_identifiers', 'si_key', 'TEXT', '' ],
79 [ 'changeField', 'actor', 'actor_id', 'BIGINT', '' ],
80 [ 'changeField', 'actor', 'actor_name', 'TEXT', '' ],
81 [ 'changeField', 'user_former_groups', 'ufg_group', 'TEXT', '' ],
82 [ 'dropFkey', 'user_former_groups', 'ufg_user' ],
83 [ 'checkIndex', 'ipb_address_unique', [
84 [ 'ipb_address', 'text_ops', 'btree', 0 ],
85 [ 'ipb_user', 'int4_ops', 'btree', 0 ],
86 [ 'ipb_auto', 'int2_ops', 'btree', 0 ],
87 ],
88 'CREATE UNIQUE INDEX ipb_address_unique ON ipblocks (ipb_address,ipb_user,ipb_auto)'
89 ],
90
91 // 1.36
92 [ 'setDefault', 'bot_passwords', 'bp_token', '' ],
93 [ 'changeField', 'comment', 'comment_id', 'BIGINT', '' ],
94 [ 'changeField', 'slots', 'slot_revision_id', 'BIGINT', '' ],
95 [ 'changeField', 'slots', 'slot_content_id', 'BIGINT', '' ],
96 [ 'changeField', 'slots', 'slot_origin', 'BIGINT', '' ],
97 [ 'changeField', 'site_stats', 'ss_total_edits', 'BIGINT', '' ],
98 [ 'changeField', 'site_stats', 'ss_good_articles', 'BIGINT', '' ],
99 [ 'changeField', 'site_stats', 'ss_total_pages', 'BIGINT', '' ],
100 [ 'changeField', 'site_stats', 'ss_users', 'BIGINT', '' ],
101 [ 'changeField', 'site_stats', 'ss_active_users', 'BIGINT', '' ],
102 [ 'changeField', 'site_stats', 'ss_images', 'BIGINT', '' ],
103 [ 'dropFkey', 'user_properties', 'up_user' ],
104 [ 'addIndex', 'user_properties', 'user_properties_pkey', 'patch-user_properties-pk.sql' ],
105 [ 'changeField', 'log_search', 'ls_value', 'VARCHAR(255)', '' ],
106 [ 'changeField', 'content', 'content_id', 'BIGINT', '' ],
107 [ 'changeField', 'l10n_cache', 'lc_value', 'TEXT', '' ],
108 [ 'changeField', 'l10n_cache', 'lc_key', 'VARCHAR(255)', '' ],
109 [ 'addIndex', 'l10n_cache', 'l10n_cache_pkey', 'patch-l10n_cache-pk.sql' ],
110 [ 'addIndex', 'module_deps', 'module_deps_pkey', 'patch-module_deps-pk.sql' ],
111 [ 'changeField', 'redirect', 'rd_namespace', 'INT', 'rd_namespace::INT DEFAULT 0' ],
112 [ 'setDefault', 'redirect', 'rd_title', '' ],
113 [ 'setDefault', 'redirect', 'rd_from', 0 ],
114 [ 'dropFkey', 'redirect', 'rd_from' ],
115 [ 'changeField', 'redirect', 'rd_interwiki', 'VARCHAR(32)', '' ],
116 [ 'dropFkey', 'pagelinks', 'pl_from' ],
117 [ 'changeField', 'pagelinks', 'pl_namespace', 'INT', 'pl_namespace::INT DEFAULT 0' ],
118 [ 'setDefault', 'pagelinks', 'pl_title', '' ],
119 [ 'addPgIndex', 'pagelinks', 'pl_namespace', '(pl_namespace,pl_title,pl_from)' ],
120 [ 'addPgIndex', 'pagelinks', 'pl_backlinks_namespace',
121 '(pl_from_namespace,pl_namespace,pl_title,pl_from)' ],
122 [ 'dropPgIndex', 'pagelinks', 'pagelink_unique' ],
123 [ 'dropPgIndex', 'pagelinks', 'pagelinks_title' ],
124 [ 'dropFkey', 'templatelinks', 'tl_from' ],
125 [ 'dropPgIndex', 'templatelinks', 'templatelinks_unique' ],
126 [ 'dropPgIndex', 'templatelinks', 'templatelinks_from' ],
127 [ 'dropFkey', 'imagelinks', 'il_from' ],
128 [ 'setDefault', 'imagelinks', 'il_to', '' ],
129 [ 'addPgIndex', 'imagelinks', 'il_to', '(il_to, il_from)' ],
130 [ 'addPgIndex', 'imagelinks', 'il_backlinks_namespace',
131 '(il_from_namespace, il_to, il_from)' ],
132 [ 'dropPgIndex', 'imagelinks', 'il_from' ],
133 [ 'dropFkey', 'langlinks', 'll_from' ],
134 [ 'addIndex', 'langlinks', 'langlinks_pkey', 'patch-langlinks-pk.sql' ],
135 [ 'renameIndex', 'langlinks', 'langlinks_lang_title', 'll_lang' ],
136 [ 'setDefault', 'langlinks', 'll_lang', '' ],
137 [ 'setDefault', 'langlinks', 'll_from', 0 ],
138 [ 'setDefault', 'langlinks', 'll_title', '' ],
139 [ 'changeNullableField', 'langlinks', 'll_lang', 'NOT NULL', true ],
140 [ 'changeNullableField', 'langlinks', 'll_title', 'NOT NULL', true ],
141 [ 'addIndex', 'iwlinks', 'iwlinks_pkey', 'patch-iwlinks-pk.sql' ],
142 [ 'renameIndex', 'category', 'category_title', 'cat_title' ],
143 [ 'renameIndex', 'category', 'category_pages', 'cat_pages' ],
144 [ 'dropSequence', 'watchlist_expiry', 'watchlist_expiry_we_item_seq' ],
145 [ 'changeField', 'change_tag_def', 'ctd_count', 'BIGINT', 'ctd_count::BIGINT DEFAULT 0' ],
146 [ 'dropDefault', 'change_tag_def', 'ctd_user_defined' ],
147 [ 'dropFkey', 'ipblocks_restrictions', 'ir_ipb_id' ],
148 [ 'setDefault', 'querycache', 'qc_value', 0 ],
149 [ 'changeField', 'querycache', 'qc_namespace', 'INT', 'qc_namespace::INT DEFAULT 0' ],
150 [ 'setDefault', 'querycache', 'qc_title', '' ],
151 [ 'renameIndex', 'querycache', 'querycache_type_value', 'qc_type' ],
152 [ 'renameIndex', 'querycachetwo', 'querycachetwo_type_value', 'qcc_type' ],
153 [ 'renameIndex', 'querycachetwo', 'querycachetwo_title', 'qcc_title' ],
154 [ 'renameIndex', 'querycachetwo', 'querycachetwo_titletwo', 'qcc_titletwo' ],
155 [ 'dropFkey', 'page_restrictions', 'pr_page' ],
156 [ 'addPgIndex', 'page_restrictions', 'pr_pagetype', '(pr_page, pr_type)', true ],
157 [ 'addPgIndex', 'page_restrictions', 'pr_typelevel', '(pr_type, pr_level)' ],
158 [ 'addPgIndex', 'page_restrictions', 'pr_level', '(pr_level)' ],
159 [ 'addPgIndex', 'page_restrictions', 'pr_cascade', '(pr_cascade)' ],
160 [ 'changePrimaryKey', 'page_restrictions', [ 'pr_id' ], 'page_restrictions_pk' ] ,
161 [ 'changeNullableField', 'page_restrictions', 'pr_page', 'NOT NULL', true ],
162 [ 'dropFkey', 'user_groups', 'ug_user' ],
163 [ 'setDefault', 'user_groups', 'ug_user', 0 ],
164 [ 'setDefault', 'user_groups', 'ug_group', '' ],
165 [ 'renameIndex', 'user_groups', 'user_groups_group', 'ug_group' ],
166 [ 'renameIndex', 'user_groups', 'user_groups_expiry', 'ug_expiry' ],
167 [ 'setDefault', 'querycache_info', 'qci_type', '' ],
168 [ 'setDefault', 'querycache_info', 'qci_timestamp', '1970-01-01 00:00:00+00' ],
169 [ 'changeNullableField', 'querycache_info', 'qci_type', 'NOT NULL', true ],
170 [ 'changeNullableField', 'querycache_info', 'qci_timestamp', 'NOT NULL', true ],
171 [ 'addIndex', 'querycache_info', 'querycache_info_pkey', 'patch-querycache_info-pk.sql' ],
172 [ 'setDefault', 'watchlist', 'wl_title', '' ],
173 [ 'changeField', 'watchlist', 'wl_namespace', 'INT', 'wl_namespace::INT DEFAULT 0' ],
174 [ 'dropFkey', 'watchlist', 'wl_user' ],
175 [ 'dropPgIndex', 'watchlist', 'wl_user_namespace_title' ],
176 [ 'addPgIndex', 'watchlist', 'namespace_title', '(wl_namespace, wl_title)' ],
177 [ 'checkIndex', 'wl_user', [
178 [ 'wl_user', 'text_ops', 'btree', 1 ],
179 [ 'wl_namespace', 'int4_ops', 'btree', 1 ],
180 [ 'wl_title', 'text_ops', 'btree', 1 ],
181 ],
182 'CREATE UNIQUE INDEX "wl_user" ON "watchlist" (wl_user, wl_namespace, wl_title)'
183 ],
184 [ 'changeField', 'sites', 'site_domain', 'VARCHAR(255)', '' ],
185 [ 'renameIndex', 'sites', 'site_global_key', 'sites_global_key' ],
186 [ 'renameIndex', 'sites', 'site_type', 'sites_type' ],
187 [ 'renameIndex', 'sites', 'site_group', 'sites_group' ],
188 [ 'renameIndex', 'sites', 'site_source', 'sites_source' ],
189 [ 'renameIndex', 'sites', 'site_language', 'sites_language' ],
190 [ 'renameIndex', 'sites', 'site_protocol', 'sites_protocol' ],
191 [ 'renameIndex', 'sites', 'site_domain', 'sites_domain' ],
192 [ 'renameIndex', 'sites', 'site_forward', 'sites_forward' ],
193 [ 'dropFkey', 'user_newtalk', 'user_id' ],
194 [ 'renameIndex', 'user_newtalk', 'user_newtalk_id', 'un_user_id' ],
195 [ 'renameIndex', 'user_newtalk', 'user_newtalk_ip', 'un_user_ip' ],
196 [ 'changeField', 'interwiki', 'iw_prefix', 'VARCHAR(32)', '' ],
197 [ 'changeField', 'interwiki', 'iw_wikiid', 'VARCHAR(64)', '' ],
198 [ 'dropFkey', 'protected_titles', 'pt_user' ],
199 [ 'changeNullableField', 'protected_titles', 'pt_user', 'NOT NULL', true ],
200 [ 'changeNullableField', 'protected_titles', 'pt_expiry', 'NOT NULL', true ],
201 [ 'changeField', 'protected_titles', 'pt_reason_id', 'BIGINT', '' ],
202 [ 'dropDefault', 'protected_titles', 'pt_create_perm' ],
203 [ 'dropFkey', 'externallinks', 'el_from' ],
204 [ 'setDefault', 'externallinks', 'el_from', 0 ],
205 [ 'changeField', 'externallinks', 'el_index_60', 'TEXT', '' ],
206 [ 'renameIndex', 'externallinks', 'externallinks_from_to', 'el_from' ],
207 [ 'renameIndex', 'externallinks', 'externallinks_index', 'el_index' ],
208 [ 'addPgIndex', 'externallinks', 'el_to', '(el_to, el_from)' ],
209 [ 'dropSequence', 'ip_changes', 'ip_changes_ipc_rev_id_seq' ],
210 [ 'changeField', 'ip_changes', 'ipc_hex', 'TEXT', "ipc_hex::TEXT DEFAULT ''" ],
211 [ 'setDefault', 'ip_changes', 'ipc_rev_id', 0 ],
212 [ 'changeField', 'revision_comment_temp', 'revcomment_comment_id', 'BIGINT', '' ],
213 [ 'renameIndex', 'watchlist', 'namespace_title', 'wl_namespace_title' ],
214 [ 'dropFkey', 'page_props', 'pp_page' ],
215 // page_props primary key change moved from the Schema SQL file to here in 1.36
216 [ 'changePrimaryKey', 'page_props', [ 'pp_page', 'pp_propname' ], 'page_props_pk' ],
217 [ 'setDefault','job', 'job_cmd', '' ],
218 [ 'changeField', 'job', 'job_namespace', 'INTEGER', '' ],
219 [ 'dropPgIndex', 'job', 'job_cmd_namespace_title' ],
220 [ 'addPgIndex', 'job', 'job_cmd', '(job_cmd, job_namespace, job_title, job_params)' ],
221 [ 'renameIndex', 'job', 'job_timestamp_idx', 'job_timestamp' ],
222 [ 'changeField', 'slot_roles', 'role_id', 'INTEGER', '' ],
223 [ 'changeField', 'content_models', 'model_id', 'INTEGER', '' ],
224 [ 'renameIndex', 'page', 'page_len_idx', 'page_len' ],
225 [ 'renameIndex', 'page', 'page_random_idx', 'page_random' ],
226 [ 'renameIndex', 'page', 'page_unique_name', 'page_name_title' ],
227 [ 'addPGIndex', 'page', 'page_redirect_namespace_len', '(page_is_redirect, page_namespace, page_len)' ],
228 [ 'dropFkey', 'categorylinks', 'cl_from' ],
229 [ 'setDefault','categorylinks', 'cl_from', 0 ],
230 [ 'setDefault','categorylinks', 'cl_to', '' ],
231 [ 'setDefault','categorylinks', 'cl_sortkey', '' ],
232 [ 'setDefault','categorylinks', 'cl_collation', '' ],
233 [ 'changeNullableField', 'categorylinks', 'cl_sortkey', 'NOT NULL', true ],
234 [ 'addIndex', 'categorylinks', 'categorylinks_pkey', 'patch-categorylinks-pk.sql' ],
235 [ 'addPgIndex', 'categorylinks', 'cl_timestamp', '(cl_to, cl_timestamp)' ],
236 [ 'addPgIndex', 'categorylinks', 'cl_collation_ext', '(cl_collation, cl_to, cl_type, cl_from)' ],
237 [ 'checkIndex', 'cl_sortkey', [
238 [ 'cl_to', 'text_ops', 'btree', 1 ],
239 [ 'cl_type', 'text_ops', 'btree', 1 ],
240 [ 'cl_sortkey', 'text_ops', 'btree', 1 ],
241 [ 'cl_from', 'text_ops', 'btree', 1 ],
242 ],
243 'CREATE INDEX cl_sortkey ON categorylinks (cl_to, cl_type, cl_sortkey, cl_from)'
244 ],
245 [ 'renameIndex', 'logging', 'logging_type_name', 'type_time' ],
246 [ 'renameIndex', 'logging', 'logging_actor_time_backwards', 'actor_time' ],
247 [ 'renameIndex', 'logging', 'logging_page_time', 'page_time' ],
248 [ 'renameIndex', 'logging', 'logging_times', 'times' ],
249 [ 'renameIndex', 'logging', 'logging_actor_type_time', 'log_actor_type_time' ],
250 [ 'renameIndex', 'logging', 'logging_page_id_time', 'log_page_id_time' ],
251 [ 'renameIndex', 'logging', 'logging_type_action', 'log_type_action' ],
252 [ 'changeNullableField', 'logging', 'log_params', 'NOT NULL', true ],
253 [ 'setDefault', 'logging', 'log_action', '' ],
254 [ 'setDefault', 'logging', 'log_type', '' ],
255 [ 'setDefault', 'logging', 'log_title', '' ],
256 [ 'setDefault', 'logging', 'log_timestamp', '1970-01-01 00:00:00+00' ],
257 [ 'changeField', 'logging', 'log_actor', 'BIGINT', '' ],
258 [ 'changeField', 'logging', 'log_comment_id', 'BIGINT', '' ],
259 [ 'changeField', 'logging', 'log_namespace', 'INT', 'log_namespace::INT DEFAULT 0' ],
260 [ 'dropPgIndex', 'logging', 'logging_actor_time' ],
261 [ 'changeField', 'uploadstash', 'us_key', 'VARCHAR(255)', '' ],
262 [ 'changeField', 'uploadstash', 'us_orig_path', 'VARCHAR(255)', '' ],
263 [ 'changeField', 'uploadstash', 'us_path', 'VARCHAR(255)', '' ],
264 [ 'changeField', 'uploadstash', 'us_source_type', 'VARCHAR(50)', '' ],
265 [ 'changeField', 'uploadstash', 'us_props', 'TEXT', '' ],
266 [ 'changeField', 'uploadstash', 'us_status', 'VARCHAR(50)', '' ],
267 [ 'changeField', 'uploadstash', 'us_sha1', 'VARCHAR(31)', '' ],
268 [ 'changeField', 'uploadstash', 'us_mime', 'VARCHAR(255)', '' ],
269 [ 'changeNullableField', 'uploadstash', 'us_key', 'NOT NULL', true ],
270 [ 'changeNullableField', 'uploadstash', 'us_user', 'NOT NULL', true ],
271 [ 'changeNullableField', 'uploadstash', 'us_orig_path', 'NOT NULL', true ],
272 [ 'changeNullableField', 'uploadstash', 'us_path', 'NOT NULL', true ],
273 [ 'changeNullableField', 'uploadstash', 'us_timestamp', 'NOT NULL', true ],
274 [ 'changeNullableField', 'uploadstash', 'us_status', 'NOT NULL', true ],
275 [ 'changeNullableField', 'uploadstash', 'us_size', 'NOT NULL', true ],
276 [ 'changeNullableField', 'uploadstash', 'us_sha1', 'NOT NULL', true ],
277 [ 'renameIndex', 'uploadstash', 'us_user_idx', 'us_user' ],
278 [ 'renameIndex', 'uploadstash', 'us_key_idx', 'us_key' ],
279 [ 'renameIndex', 'uploadstash', 'us_timestamp_idx', 'us_timestamp' ],
280 [ 'renameIndex', 'user_properties', 'user_properties_property', 'up_property' ],
281 [ 'renameIndex', 'sites', 'sites_global_key', 'site_global_key' ],
282 [ 'renameIndex', 'sites', 'sites_type', 'site_type' ],
283 [ 'renameIndex', 'sites', 'sites_group, ', 'site_group' ],
284 [ 'renameIndex', 'sites', 'sites_source', 'site_source' ],
285 [ 'renameIndex', 'sites', 'sites_language', 'site_language' ],
286 [ 'renameIndex', 'sites', 'sites_protocol', 'site_protocol' ],
287 [ 'renameIndex', 'sites', 'sites_domain', 'site_domain' ],
288 [ 'renameIndex', 'sites', 'sites_forward', 'site_forward' ],
289 [ 'renameIndex', 'logging', 'type_name', 'log_type_time' ],
290 [ 'renameIndex', 'logging', 'actor_time', 'log_actor_time' ],
291 [ 'renameIndex', 'logging', 'page_time', 'log_page_time' ],
292 [ 'renameIndex', 'logging', 'times', 'log_times' ],
293 [ 'setDefault', 'filearchive', 'fa_name', '' ],
294 [ 'setDefault', 'filearchive', 'fa_archive_name', '' ],
295 [ 'setDefault', 'filearchive', 'fa_storage_key', '' ],
296 [ 'dropFkey', 'filearchive', 'fa_deleted_user' ],
297 [ 'changeField', 'filearchive', 'fa_deleted_reason_id', 'BIGINT', '' ],
298 [ 'changeField', 'filearchive', 'fa_metadata', 'TEXT', '' ],
299 [ 'changeField', 'filearchive', 'fa_bits', 'INTEGER', '' ],
300 [ 'changeField', 'filearchive', 'fa_description_id', 'BIGINT', '' ],
301 [ 'changeField', 'filearchive', 'fa_actor', 'BIGINT', '' ],
302 [ 'renameIndex', 'filearchive', 'fa_name_time', 'fa_name' ],
303 [ 'renameIndex', 'filearchive', 'fa_dupe', 'fa_storage_group' ],
304 [ 'renameIndex', 'filearchive', 'fa_notime', 'fa_deleted_timestamp' ],
305 [ 'dropPgIndex', 'filearchive', 'fa_nouser' ],
306 [ 'addPgIndex', 'filearchive', 'fa_actor_timestamp', '(fa_actor, fa_timestamp)' ],
307 [ 'addPgIndex', 'ipblocks', 'ipb_expiry', '(ipb_expiry)' ],
308 [ 'addPgIndex', 'ipblocks', 'ipb_timestamp', '(ipb_timestamp)' ],
309 [ 'renameIndex', 'text', 'pagecontent_pkey', 'text_pkey' ],
310 [ 'changeNullableField', 'text', 'old_text', 'NOT NULL', true ],
311 [ 'changeNullableField', 'text', 'old_flags', 'NOT NULL', true ],
312 [ 'setDefault', 'oldimage', 'oi_name', '' ],
313 [ 'setDefault', 'oldimage', 'oi_archive_name', '' ],
314 [ 'setDefault', 'oldimage', 'oi_size', 0 ],
315 [ 'setDefault', 'oldimage', 'oi_width', 0 ],
316 [ 'setDefault', 'oldimage', 'oi_height', 0 ],
317 [ 'setDefault', 'oldimage', 'oi_bits', 0 ],
318 [ 'setDefault', 'oldimage', 'oi_name', '' ],
319 [ 'changeField', 'oldimage', 'oi_bits', 'INTEGER', '' ],
320 [ 'changeField', 'oldimage', 'oi_description_id', 'BIGINT', '' ],
321 [ 'changeField', 'oldimage', 'oi_actor', 'BIGINT', '' ],
322 [ 'changeField', 'oldimage', 'oi_metadata', 'TEXT', '' ],
323 [ 'dropDefault', 'oldimage', 'oi_metadata' ],
324 [ 'changeNullableField', 'oldimage', 'oi_minor_mime', 'NOT NULL', true ],
325 [ 'changeNullableField', 'oldimage', 'oi_minor_mime', 'NOT NULL', true ],
326 [ 'dropFkey', 'oldimage', 'oi_name' ],
327 [ 'addPgIndex', 'oldimage', 'oi_actor_timestamp', '(oi_actor, oi_timestamp)' ],
328 [ 'dropPgIndex', 'recentchanges', 'rc_timestamp_bot' ],
329 [ 'addPgIndex', 'recentchanges', 'rc_ns_actor', '(rc_namespace, rc_actor)' ],
330 [ 'addPgIndex', 'recentchanges', 'rc_actor', '(rc_actor, rc_timestamp)' ],
331 [ 'dropIndex', 'objectcache', 'keyname', 'patch-objectcache_keyname-pk.sql' ],
332 [ 'changeField', 'objectcache', 'value', 'TEXT', '' ],
333 [ 'changeNullableField', 'objectcache', 'value', 'NULL', true ],
334 [ 'dropFkey', 'ipblocks', 'ipb_user' ],
335 [ 'dropFkey', 'ipblocks', 'ipb_parent_block_id' ],
336 [ 'setDefault', 'ipblocks', 'ipb_user', 0 ],
337 [ 'changeNullableField', 'ipblocks', 'ipb_user', 'NOT NULL', true ],
338 [ 'changeNullableField', 'ipblocks', 'ipb_range_start', 'NOT NULL', true ],
339 [ 'changeNullableField', 'ipblocks', 'ipb_range_end', 'NOT NULL', true ],
340 [ 'changeField', 'ipblocks', 'ipb_by_actor', 'BIGINT', '' ],
341 [ 'changeField', 'ipblocks', 'ipb_reason_id', 'BIGINT', '' ],
342 [ 'renameIndex', 'archive', 'archive_name_title_timestamp', 'ar_name_title_timestamp' ],
343 [ 'dropPgIndex', 'archive', 'archive_actor' ],
344 [ 'addPgIndex', 'archive', 'ar_actor_timestamp', '(ar_actor,ar_timestamp)' ],
345 [ 'setDefault', 'image', 'img_name', '' ],
346 [ 'setDefault', 'image', 'img_size', 0 ],
347 [ 'setDefault', 'image', 'img_width', 0 ],
348 [ 'setDefault', 'image', 'img_height', 0 ],
349 [ 'setDefault', 'image', 'img_bits', 0 ],
350 [ 'changeField', 'image', 'img_bits', 'INTEGER', '' ],
351 [ 'changeField', 'image', 'img_description_id', 'BIGINT', '' ],
352 [ 'changeField', 'image', 'img_actor', 'BIGINT', '' ],
353 [ 'changeField', 'image', 'img_metadata', 'TEXT', '' ],
354 [ 'dropDefault', 'image', 'img_metadata' ],
355 [ 'changeNullableField', 'image', 'img_major_mime', 'NOT NULL', true ],
356 [ 'changeNullableField', 'image', 'img_minor_mime', 'NOT NULL', true ],
357 [ 'changeNullableField', 'image', 'img_timestamp', 'NOT NULL', true ],
358 [ 'renameIndex', 'image', 'img_size_idx', 'img_size' ],
359 [ 'renameIndex', 'image', 'img_timestamp_idx', 'img_timestamp' ],
360 [ 'addPgIndex', 'image', 'img_actor_timestamp', '(img_actor, img_timestamp)' ],
361 [ 'addPgIndex', 'image', 'img_media_mime', '(img_media_type, img_major_mime, img_minor_mime)' ],
362 [ 'renameIndex', 'site_identifiers', 'site_ids_site', 'si_site' ],
363 [ 'renameIndex', 'site_identifiers', 'site_ids_key', 'si_key' ],
364 [ 'changeField', 'recentchanges', 'rc_actor', 'BIGINT', '' ],
365 [ 'changeField', 'recentchanges', 'rc_comment_id', 'BIGINT', '' ],
366 [ 'changeField', 'recentchanges', 'rc_ip', 'TEXT', '' ],
367 [ 'changeField', 'recentchanges', 'rc_namespace', 'INTEGER', '' ],
368 [ 'setDefault', 'recentchanges', 'rc_title', '' ],
369 [ 'setDefault', 'recentchanges', 'rc_source', '' ],
370 [ 'setDefault', 'recentchanges', 'rc_ip', '' ],
371 [ 'setDefault', 'recentchanges', 'rc_namespace', 0 ],
372 [ 'setDefault', 'recentchanges', 'rc_cur_id', 0 ],
373 [ 'setDefault', 'recentchanges', 'rc_this_oldid', 0 ],
374 [ 'setDefault', 'recentchanges', 'rc_last_oldid', 0 ],
375 [ 'changeNullableField', 'recentchanges', 'rc_cur_id', 'NOT NULL', true ],
376 [ 'changeNullableField', 'recentchanges', 'rc_ip', 'NOT NULL', true ],
377 [ 'renameIndex', 'recentchanges', 'new_name_timestamp', 'rc_new_name_timestamp', false,
378 'patch-recentchanges-rc_new_name_timestamp.sql' ],
379 [ 'changeField', 'archive', 'ar_namespace', 'INTEGER', '' ],
380 [ 'setDefault', 'archive', 'ar_namespace', 0 ],
381 [ 'setDefault', 'archive', 'ar_title', '' ],
382 [ 'changeField', 'archive', 'ar_comment_id', 'BIGINT', '' ],
383 [ 'changeField', 'archive', 'ar_actor', 'BIGINT', '' ],
384 [ 'renameIndex', 'user', 'user_email_token_idx', 'user_email_token' ],
385 [ 'addPgIndex', 'user', 'user_email', '(user_email)' ],
386 [ 'addPgIndex', 'user', 'user_name', '(user_name)', true ],
387 [ 'changeField', 'page', 'page_namespace', 'INTEGER', '' ],
388 [ 'changeNullableField', 'page', 'page_touched', 'NOT NULL', true ],
389 [ 'changeField', 'page', 'page_random', 'FLOAT', '' ],
390 [ 'renameIndex', 'revision', 'revision_unique', 'rev_page_id' ],
391 [ 'renameIndex', 'revision', 'rev_timestamp_idx', 'rev_timestamp' ],
392 [ 'addPgIndex', 'revision', 'rev_page_timestamp', '(rev_page,rev_timestamp)' ],
393 [ 'changeNullableField', 'user', 'user_touched', 'NOT NULL', true ],
394
395 // 1.37
396 [ 'setDefault', 'user', 'user_name', '' ],
397 [ 'setDefault', 'user', 'user_token', '' ],
398 [ 'setDefault', 'user', 'user_real_name', '' ],
399 [ 'setDefault', 'user', 'user_email', '' ],
400 [ 'setDefault', 'user', 'user_newpassword', '' ],
401 [ 'setDefault', 'user', 'user_password', '' ],
402 [ 'changeNullableField', 'user', 'user_token', 'NOT NULL', true ],
403 [ 'changeNullableField', 'user', 'user_real_name', 'NOT NULL', true ],
404 [ 'changeNullableField', 'user', 'user_email', 'NOT NULL', true ],
405 [ 'changeNullableField', 'user', 'user_newpassword', 'NOT NULL', true ],
406 [ 'changeNullableField', 'user', 'user_password', 'NOT NULL', true ],
407 [ 'dropDefault', 'user', 'user_email' ],
408 [ 'dropDefault', 'user', 'user_newpassword' ],
409 [ 'dropDefault', 'user', 'user_password' ],
410 [ 'dropConstraint', 'user', 'user_name', 'unique' ],
411 [ 'addField', 'objectcache', 'modtoken', 'patch-objectcache-modtoken.sql' ],
412 [ 'dropFkey', 'revision', 'rev_page' ],
413 [ 'changeNullableField', 'revision', 'rev_page', 'NOT NULL', true ],
414 [ 'changeField', 'revision', 'rev_comment_id', 'BIGINT', 'rev_comment_id::BIGINT DEFAULT 0' ],
415 [ 'changeField', 'revision', 'rev_actor', 'BIGINT', 'rev_actor::BIGINT DEFAULT 0' ],
416 [ 'checkIndex', 'rev_page_id', [
417 [ 'rev_page', 'int4_ops', 'btree', 1 ],
418 [ 'rev_id', 'int4_ops', 'btree', 1 ],
419 ],
420 'CREATE INDEX rev_page_id ON revision (rev_page,rev_id)'
421 ],
422 [ 'addTable', 'searchindex', 'patch-searchindex-table.sql' ],
423 [ 'addPgIndex', 'oldimage', 'oi_timestamp', '(oi_timestamp)' ],
424 [ 'renameIndex', 'page', 'name_title', 'page_name_title' ],
425 [ 'renameIndex', 'change_tag', 'change_tag_rc_tag_id', 'ct_rc_tag_id' ],
426 [ 'renameIndex', 'change_tag', 'change_tag_log_tag_id', 'ct_log_tag_id' ],
427 [ 'renameIndex', 'change_tag', 'change_tag_rev_tag_id', 'ct_rev_tag_id' ],
428 [ 'renameIndex', 'change_tag', 'change_tag_tag_id_id', 'ct_tag_id_id' ],
429
430 // 1.38
431 [ 'doConvertDjvuMetadata' ],
432 [ 'dropPgField', 'page_restrictions', 'pr_user' ],
433 [ 'addTable', 'linktarget', 'patch-linktarget.sql' ],
434 [ 'dropIndex', 'revision', 'rev_page_id', 'patch-drop-rev_page_id.sql' ],
435 [ 'addField', 'templatelinks', 'tl_target_id', 'patch-templatelinks-target_id.sql' ],
436
437 // 1.39
438 [ 'addTable', 'user_autocreate_serial', 'patch-user_autocreate_serial.sql' ],
439 [ 'runMaintenance', MigrateRevisionActorTemp::class, 'maintenance/migrateRevisionActorTemp.php' ],
440 [ 'dropTable', 'revision_actor_temp' ],
441 [ 'runMaintenance', UpdateRestrictions::class, 'maintenance/updateRestrictions.php' ],
442 [ 'dropPgField', 'page', 'page_restrictions' ],
443 [ 'migrateTemplatelinks' ],
444 [ 'changeNullableField', 'templatelinks', 'tl_target_id', 'NOT NULL', true ],
445 [ 'changePrimaryKey', 'templatelinks', [ 'tl_from', 'tl_target_id' ] ],
446 [ 'dropField', 'templatelinks', 'tl_title', 'patch-templatelinks-drop-tl_title.sql' ],
447
448 // 1.40
449 [ 'addField', 'externallinks', 'el_to_path', 'patch-externallinks-el_to_path.sql' ],
450 ];
451 }
452
453 protected function describeTable( $table ) {
454 $q = <<<END
455SELECT attname, attnum FROM pg_namespace, pg_class, pg_attribute
456 WHERE pg_class.relnamespace = pg_namespace.oid
457 AND attrelid=pg_class.oid AND attnum > 0
458 AND relname=%s AND nspname=%s
459END;
460 $res = $this->db->query(
461 sprintf( $q,
462 $this->db->addQuotes( $table ),
463 $this->db->addQuotes( $this->db->getCoreSchema() )
464 ),
465 __METHOD__
466 );
467 if ( !$res ) {
468 return null;
469 }
470
471 $cols = [];
472 foreach ( $res as $r ) {
473 $cols[] = [
474 "name" => $r[0],
475 "ord" => $r[1],
476 ];
477 }
478
479 return $cols;
480 }
481
482 protected function describeIndex( $idx ) {
483 // first fetch the key (which is a list of columns ords) and
484 // the table the index applies to (an oid)
485 $q = <<<END
486SELECT indkey, indrelid FROM pg_namespace, pg_class, pg_index
487 WHERE nspname=%s
488 AND pg_class.relnamespace = pg_namespace.oid
489 AND relname=%s
490 AND indexrelid=pg_class.oid
491END;
492 $res = $this->db->query(
493 sprintf(
494 $q,
495 $this->db->addQuotes( $this->db->getCoreSchema() ),
496 $this->db->addQuotes( $idx )
497 ),
498 __METHOD__
499 );
500 if ( !$res ) {
501 return null;
502 }
503 $r = $res->fetchRow();
504 if ( !$r ) {
505 return null;
506 }
507
508 $indkey = $r[0];
509 $relid = intval( $r[1] );
510 $indkeys = explode( ' ', $indkey );
511
512 $colnames = [];
513 foreach ( $indkeys as $rid ) {
514 $query = <<<END
515SELECT attname FROM pg_class, pg_attribute
516 WHERE attrelid=$relid
517 AND attnum=%d
518 AND attrelid=pg_class.oid
519END;
520 $r2 = $this->db->query( sprintf( $query, $rid ), __METHOD__ );
521 if ( !$r2 ) {
522 return null;
523 }
524 $row2 = $r2->fetchRow();
525 if ( !$row2 ) {
526 return null;
527 }
528 $colnames[] = $row2[0];
529 }
530
531 return $colnames;
532 }
533
534 protected function fkeyDeltype( $fkey ) {
535 $q = <<<END
536SELECT confdeltype FROM pg_constraint, pg_namespace
537 WHERE connamespace=pg_namespace.oid
538 AND nspname=%s
539 AND conname=%s;
540END;
541 $r = $this->db->query(
542 sprintf(
543 $q,
544 $this->db->addQuotes( $this->db->getCoreSchema() ),
545 $this->db->addQuotes( $fkey )
546 ),
547 __METHOD__
548 );
549 $row = $r->fetchRow();
550 return $row ? $row[0] : null;
551 }
552
553 protected function ruleDef( $table, $rule ) {
554 $q = <<<END
555SELECT definition FROM pg_rules
556 WHERE schemaname = %s
557 AND tablename = %s
558 AND rulename = %s
559END;
560 $r = $this->db->query(
561 sprintf(
562 $q,
563 $this->db->addQuotes( $this->db->getCoreSchema() ),
564 $this->db->addQuotes( $table ),
565 $this->db->addQuotes( $rule )
566 ),
567 __METHOD__
568 );
569 $row = $r->fetchRow();
570 if ( !$row ) {
571 return null;
572 }
573 $d = $row[0];
574
575 return $d;
576 }
577
578 protected function addSequence( $table, $pkey, $ns ) {
579 if ( !$this->db->sequenceExists( $ns ) ) {
580 $this->output( "Creating sequence $ns\n" );
581 if ( $pkey !== false ) {
582 $table = $this->db->addIdentifierQuotes( $table );
583 $this->db->query( "CREATE SEQUENCE $ns OWNED BY $table.$pkey", __METHOD__ );
584 $this->setDefault( $table, $pkey, '"nextval"(\'"' . $ns . '"\'::"regclass")' );
585 } else {
586 $this->db->query( "CREATE SEQUENCE $ns", __METHOD__ );
587 }
588 }
589 }
590
591 protected function dropSequence( $table, $ns ) {
592 if ( $this->db->sequenceExists( $ns ) ) {
593 $this->output( "Dropping sequence $ns\n" );
594 $this->db->query( "DROP SEQUENCE $ns CASCADE", __METHOD__ );
595 }
596 }
597
598 protected function renameSequence( $old, $new ) {
599 if ( $this->db->sequenceExists( $new ) ) {
600 $this->output( "...sequence $new already exists.\n" );
601
602 return;
603 }
604 if ( $this->db->sequenceExists( $old ) ) {
605 $this->output( "Renaming sequence $old to $new\n" );
606 $this->db->query( "ALTER SEQUENCE $old RENAME TO $new", __METHOD__ );
607 }
608 }
609
610 protected function setSequenceOwner( $table, $pkey, $seq ) {
611 if ( $this->db->sequenceExists( $seq ) ) {
612 $this->output( "Setting sequence $seq owner to $table.$pkey\n" );
613 $table = $this->db->addIdentifierQuotes( $table );
614 $this->db->query( "ALTER SEQUENCE $seq OWNED BY $table.$pkey", __METHOD__ );
615 }
616 }
617
618 protected function renameTable( $old, $new, $patch = false ) {
619 if ( $this->db->tableExists( $old, __METHOD__ ) ) {
620 $this->output( "Renaming table $old to $new\n" );
621 $old = $this->db->addIdentifierQuotes( $old );
622 $new = $this->db->addIdentifierQuotes( $new );
623 $this->db->query( "ALTER TABLE $old RENAME TO $new", __METHOD__ );
624 if ( $patch !== false ) {
625 $this->applyPatch( $patch );
626 }
627 }
628 }
629
630 protected function renameIndex(
631 $table, $old, $new, $skipBothIndexExistWarning = false, $a = false, $b = false
632 ) {
633 // First requirement: the table must exist
634 if ( !$this->db->tableExists( $table, __METHOD__ ) ) {
635 $this->output( "...skipping: '$table' table doesn't exist yet.\n" );
636
637 return true;
638 }
639
640 // Second requirement: the new index must be missing
641 if ( $this->db->indexExists( $table, $new, __METHOD__ ) ) {
642 $this->output( "...index $new already set on $table table.\n" );
643 if ( !$skipBothIndexExistWarning
644 && $this->db->indexExists( $table, $old, __METHOD__ )
645 ) {
646 $this->output( "...WARNING: $old still exists, despite it has been " .
647 "renamed into $new (which also exists).\n" .
648 " $old should be manually removed if not needed anymore.\n" );
649 }
650
651 return true;
652 }
653
654 // Third requirement: the old index must exist
655 if ( !$this->db->indexExists( $table, $old, __METHOD__ ) ) {
656 $this->output( "...skipping: index $old doesn't exist.\n" );
657
658 return true;
659 }
660
661 $this->db->query( "ALTER INDEX $old RENAME TO $new", __METHOD__ );
662 return true;
663 }
664
665 protected function dropPgField( $table, $field ) {
666 $fi = $this->db->fieldInfo( $table, $field );
667 if ( $fi === null ) {
668 $this->output( "...$table table does not contain $field field.\n" );
669 } else {
670 $this->output( "Dropping column '$table.$field'\n" );
671 $table = $this->db->addIdentifierQuotes( $table );
672 $this->db->query( "ALTER TABLE $table DROP COLUMN $field", __METHOD__ );
673 }
674 }
675
676 protected function addPgField( $table, $field, $type ) {
677 $fi = $this->db->fieldInfo( $table, $field );
678 if ( $fi !== null ) {
679 $this->output( "...column '$table.$field' already exists\n" );
680 } else {
681 $this->output( "Adding column '$table.$field'\n" );
682 $table = $this->db->addIdentifierQuotes( $table );
683 $this->db->query( "ALTER TABLE $table ADD $field $type", __METHOD__ );
684 }
685 }
686
687 protected function changeField( $table, $field, $newtype, $default ) {
688 $fi = $this->db->fieldInfo( $table, $field );
689 if ( $fi === null ) {
690 $this->output( "...ERROR: expected column $table.$field to exist\n" );
691 exit( 1 );
692 }
693
694 if ( $fi->type() === strtolower( $newtype ) ) {
695 $this->output( "...column '$table.$field' is already of type '$newtype'\n" );
696 } else {
697 $this->output( "Changing column type of '$table.$field' from '{$fi->type()}' to '$newtype'\n" );
698 $table = $this->db->addIdentifierQuotes( $table );
699 $sql = "ALTER TABLE $table ALTER $field TYPE $newtype";
700 if ( strlen( $default ) ) {
701 $res = [];
702 if ( preg_match( '/DEFAULT (.+)/', $default, $res ) ) {
703 $sqldef = "ALTER TABLE $table ALTER $field SET DEFAULT $res[1]";
704 $this->db->query( $sqldef, __METHOD__ );
705 $default = preg_replace( '/\s*DEFAULT .+/', '', $default );
706 }
707 $sql .= " USING $default";
708 }
709 $this->db->query( $sql, __METHOD__ );
710 }
711 }
712
713 protected function changeFieldPurgeTable( $table, $field, $newtype, $default ) {
714 # # For a cache table, empty it if the field needs to be changed, because the old contents
715 # # may be corrupted. If the column is already the desired type, refrain from purging.
716 $fi = $this->db->fieldInfo( $table, $field );
717 if ( $fi === null ) {
718 $this->output( "...ERROR: expected column $table.$field to exist\n" );
719 exit( 1 );
720 }
721
722 if ( $fi->type() === $newtype ) {
723 $this->output( "...column '$table.$field' is already of type '$newtype'\n" );
724 } else {
725 $this->output( "Purging data from cache table '$table'\n" );
726 $table = $this->db->addIdentifierQuotes( $table );
727 $this->db->query( "DELETE from $table", __METHOD__ );
728 $this->output( "Changing column type of '$table.$field' from '{$fi->type()}' to '$newtype'\n" );
729 $sql = "ALTER TABLE $table ALTER $field TYPE $newtype";
730 if ( strlen( $default ) ) {
731 $res = [];
732 if ( preg_match( '/DEFAULT (.+)/', $default, $res ) ) {
733 $sqldef = "ALTER TABLE $table ALTER $field SET DEFAULT $res[1]";
734 $this->db->query( $sqldef, __METHOD__ );
735 $default = preg_replace( '/\s*DEFAULT .+/', '', $default );
736 }
737 $sql .= " USING $default";
738 }
739 $this->db->query( $sql, __METHOD__ );
740 }
741 }
742
743 protected function setDefault( $table, $field, $default ) {
744 $info = $this->db->fieldInfo( $table, $field );
745 if ( $info && $info->defaultValue() !== $default ) {
746 $this->output( "Changing '$table.$field' default value\n" );
747 $table = $this->db->addIdentifierQuotes( $table );
748 $this->db->query( "ALTER TABLE $table ALTER $field SET DEFAULT "
749 . $this->db->addQuotes( $default ), __METHOD__ );
750 }
751 }
752
759 protected function dropDefault( $table, $field ) {
760 $info = $this->db->fieldInfo( $table, $field );
761 if ( $info->defaultValue() !== false ) {
762 $this->output( "Removing '$table.$field' default value\n" );
763 $table = $this->db->addIdentifierQuotes( $table );
764 $this->db->query( "ALTER TABLE $table ALTER $field DROP DEFAULT", __METHOD__ );
765 }
766 }
767
768 protected function changeNullableField( $table, $field, $null, $update = false ) {
769 $fi = $this->db->fieldInfo( $table, $field );
770 if ( $fi === null ) {
771 return;
772 }
773 if ( $fi->isNullable() ) {
774 # # It's NULL - does it need to be NOT NULL?
775 if ( $null === 'NOT NULL' ) {
776 $this->output( "Changing '$table.$field' to not allow NULLs\n" );
777 $table = $this->db->addIdentifierQuotes( $table );
778 if ( $update ) {
779 $this->db->query( "UPDATE $table SET $field = DEFAULT WHERE $field IS NULL", __METHOD__ );
780 }
781 $this->db->query( "ALTER TABLE $table ALTER $field SET NOT NULL", __METHOD__ );
782 } else {
783 $this->output( "...column '$table.$field' is already set as NULL\n" );
784 }
785 } else {
786 # # It's NOT NULL - does it need to be NULL?
787 if ( $null === 'NULL' ) {
788 $this->output( "Changing '$table.$field' to allow NULLs\n" );
789 $table = $this->db->addIdentifierQuotes( $table );
790 $this->db->query( "ALTER TABLE $table ALTER $field DROP NOT NULL", __METHOD__ );
791 } else {
792 $this->output( "...column '$table.$field' is already set as NOT NULL\n" );
793 }
794 }
795 }
796
797 protected function addPgIndex( $table, $index, $type, $unique = false ) {
798 if ( $this->db->indexExists( $table, $index, __METHOD__ ) ) {
799 $this->output( "...index '$index' on table '$table' already exists\n" );
800 } else {
801 $this->output( "Creating index '$index' on table '$table' $type\n" );
802 $table = $this->db->addIdentifierQuotes( $table );
803 $unique = $unique ? 'UNIQUE' : '';
804 $this->db->query( "CREATE $unique INDEX $index ON $table $type", __METHOD__ );
805 }
806 }
807
808 protected function addPgExtIndex( $table, $index, $type ) {
809 if ( $this->db->indexExists( $table, $index, __METHOD__ ) ) {
810 $this->output( "...index '$index' on table '$table' already exists\n" );
811 } elseif ( preg_match( '/^\‍(/', $type ) ) {
812 $this->output( "Creating index '$index' on table '$table'\n" );
813 $table = $this->db->addIdentifierQuotes( $table );
814 $this->db->query( "CREATE INDEX $index ON $table $type", __METHOD__ );
815 } else {
816 $this->applyPatch( $type, true, "Creating index '$index' on table '$table'" );
817 }
818 }
819
826 protected function addPgEnumValue( $type, $value ) {
827 $row = $this->db->selectRow(
828 [
829 't' => 'pg_catalog.pg_type',
830 'n' => 'pg_catalog.pg_namespace',
831 'e' => 'pg_catalog.pg_enum',
832 ],
833 [ 't.typname', 't.typtype', 'e.enumlabel' ],
834 [
835 't.typname' => $type,
836 'n.nspname' => $this->db->getCoreSchema(),
837 ],
838 __METHOD__,
839 [],
840 [
841 'n' => [ 'JOIN', 't.typnamespace = n.oid' ],
842 'e' => [ 'LEFT JOIN', [ 'e.enumtypid = t.oid', 'e.enumlabel' => $value ] ],
843 ]
844 );
845
846 if ( !$row ) {
847 $this->output( "...Type $type does not exist, skipping modify enum.\n" );
848 } elseif ( $row->typtype !== 'e' ) {
849 $this->output( "...Type $type does not seem to be an enum, skipping modify enum.\n" );
850 } elseif ( $row->enumlabel === $value ) {
851 $this->output( "...Enum type $type already contains value '$value'.\n" );
852 } else {
853 $this->output( "...Adding value '$value' to enum type $type.\n" );
854 $etype = $this->db->addIdentifierQuotes( $type );
855 $evalue = $this->db->addQuotes( $value );
856 $this->db->query( "ALTER TYPE $etype ADD VALUE $evalue", __METHOD__ );
857 }
858 }
859
860 protected function dropFkey( $table, $field ) {
861 $fi = $this->db->fieldInfo( $table, $field );
862 if ( $fi === null ) {
863 $this->output( "WARNING! Column '$table.$field' does not exist but it should! " .
864 "Please report this.\n" );
865 return;
866 }
867
868 if ( $this->dropConstraint( $table, $field, 'foreignkey', $fi->conname() ) ) {
869 $this->output( "Dropping foreign key constraint on '$table.$field'\n" );
870 } else {
871 $this->output( "...foreign key constraint on '$table.$field' already does not exist\n" );
872 }
873 }
874
875 protected function changeFkeyDeferrable( $table, $field, $clause ) {
876 $fi = $this->db->fieldInfo( $table, $field );
877 if ( $fi === null ) {
878 $this->output( "WARNING! Column '$table.$field' does not exist but it should! " .
879 "Please report this.\n" );
880
881 return;
882 }
883 if ( $fi->is_deferred() && $fi->is_deferrable() ) {
884 return;
885 }
886 $this->output( "Altering column '$table.$field' to be DEFERRABLE INITIALLY DEFERRED\n" );
887
888 $conname = $fi->conname();
889 $conclause = "CONSTRAINT \"$conname\"";
890
891 if ( !$this->dropConstraint( $table, $field, 'foreignkey', $conname ) ) {
892 $this->output( "Column '$table.$field' does not have a foreign key " .
893 "constraint, will be added\n" );
894 $conclause = "";
895 }
896
897 $command =
898 "ALTER TABLE $table ADD $conclause " .
899 "FOREIGN KEY ($field) REFERENCES $clause DEFERRABLE INITIALLY DEFERRED";
900 $this->db->query( $command, __METHOD__ );
901 }
902
903 protected function dropPgIndex( $table, $index ) {
904 if ( $this->db->indexExists( $table, $index, __METHOD__ ) ) {
905 $this->output( "Dropping obsolete index '$index'\n" );
906 $this->db->query( "DROP INDEX \"" . $index . "\"", __METHOD__ );
907 }
908 }
909
910 protected function checkIndex( $index, $should_be, $good_def ) {
911 $pu = $this->db->indexAttributes( $index );
912 if ( !empty( $pu ) && $pu != $should_be ) {
913 $this->output( "Dropping obsolete version of index '$index'\n" );
914 $this->db->query( "DROP INDEX \"" . $index . "\"", __METHOD__ );
915 $pu = [];
916 } else {
917 $this->output( "...no need to drop index '$index'\n" );
918 }
919
920 if ( empty( $pu ) ) {
921 $this->output( "Creating index '$index'\n" );
922 $this->db->query( $good_def, __METHOD__ );
923 } else {
924 $this->output( "...index '$index' exists\n" );
925 }
926 }
927
928 protected function changePrimaryKey( $table, $shouldBe, $constraintName = null ) {
929 // https://wiki.postgresql.org/wiki/Retrieve_primary_key_columns
930 $result = $this->db->query(
931 "SELECT a.attname as column " .
932 "FROM pg_index i " .
933 "JOIN pg_attribute a ON a.attrelid = i.indrelid " .
934 "AND a.attnum = ANY(i.indkey) " .
935 "WHERE i.indrelid = '\"$table\"'::regclass " .
936 "AND i.indisprimary",
937 __METHOD__
938 );
939 $currentColumns = [];
940 foreach ( $result as $row ) {
941 $currentColumns[] = $row->column;
942 }
943
944 if ( $currentColumns == $shouldBe ) {
945 $this->output( "...no need to change primary key of '$table'\n" );
946 return true;
947 }
948
949 $this->dropConstraint( $table, '', 'primary', $constraintName );
950
951 $table = $this->db->addIdentifierQuotes( $table );
952 $this->db->query(
953 "ALTER TABLE $table" .
954 " ADD PRIMARY KEY (" . implode( ',', $shouldBe ) . ');',
955 __METHOD__
956 );
957 }
958
970 protected function dropConstraint( $table, $field, $type, $conname = null ) {
971 if ( $conname === null ) {
972 if ( $type == 'primary' ) {
973 $conname = "{$table}_pkey";
974 } else {
975 $map = [ 'unique' => 'key', 'check' => 'check', 'foreignkey' => 'fkey' ];
976 $conname = "{$table}_{$field}_{$map[$type]}";
977 }
978 }
979
980 if ( $this->db->constraintExists( $table, $conname ) ) {
981 $table = $this->db->addIdentifierQuotes( $table );
982 $this->db->query(
983 "ALTER TABLE $table DROP CONSTRAINT $conname;",
984 __METHOD__
985 );
986
987 return true;
988 }
989
990 return false;
991 }
992}
Class for handling database updates.
output( $str)
Output some text.
applyPatch( $path, $isFullPath=false, $msg=null)
Applies a SQL patch.
Class for handling updates to Postgres databases.
changeNullableField( $table, $field, $null, $update=false)
changePrimaryKey( $table, $shouldBe, $constraintName=null)
changeField( $table, $field, $newtype, $default)
renameTable( $old, $new, $patch=false)
renameIndex( $table, $old, $new, $skipBothIndexExistWarning=false, $a=false, $b=false)
Rename an index from an existing table.
dropDefault( $table, $field)
Drop a default value from a field.
ruleDef( $table, $rule)
dropSequence( $table, $ns)
addPgExtIndex( $table, $index, $type)
addSequence( $table, $pkey, $ns)
addPgEnumValue( $type, $value)
Add a value to an existing PostgreSQL enum type.
dropPgField( $table, $field)
renameSequence( $old, $new)
dropPgIndex( $table, $index)
setDefault( $table, $field, $default)
DatabasePostgres $db
addPgField( $table, $field, $type)
setSequenceOwner( $table, $pkey, $seq)
dropFkey( $table, $field)
dropConstraint( $table, $field, $type, $conname=null)
Drop generic constraint.
changeFkeyDeferrable( $table, $field, $clause)
checkIndex( $index, $should_be, $good_def)
changeFieldPurgeTable( $table, $field, $newtype, $default)
addPgIndex( $table, $index, $type, $unique=false)
Postgres database abstraction layer.
return true
Definition router.php:92