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

includes/installer/PostgresUpdater.php (2 issues)

Upgrade to new PHP Analysis Engine

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

1
<?php
2
/**
3
 * PostgreSQL-specific updater.
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation; either version 2 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License along
16
 * with this program; if not, write to the Free Software Foundation, Inc.,
17
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18
 * http://www.gnu.org/copyleft/gpl.html
19
 *
20
 * @file
21
 * @ingroup Deployment
22
 */
23
24
/**
25
 * Class for handling updates to Postgres databases.
26
 *
27
 * @ingroup Deployment
28
 * @since 1.17
29
 */
30
class PostgresUpdater extends DatabaseUpdater {
31
32
	/**
33
	 * @var DatabasePostgres
34
	 */
35
	protected $db;
36
37
	/**
38
	 * @todo FIXME: Postgres should use sequential updates like Mysql, Sqlite
39
	 * and everybody else. It never got refactored like it should've.
40
	 * @return array
41
	 */
42
	protected function getCoreUpdateList() {
43
		return [
44
			# rename tables 1.7.3
45
			# r15791 Change reserved word table names "user" and "text"
46
			[ 'renameTable', 'user', 'mwuser' ],
47
			[ 'renameTable', 'text', 'pagecontent' ],
48
			[ 'renameIndex', 'mwuser', 'user_pkey', 'mwuser_pkey' ],
49
			[ 'renameIndex', 'mwuser', 'user_user_name_key', 'mwuser_user_name_key' ],
50
			[ 'renameIndex', 'pagecontent', 'text_pkey', 'pagecontent_pkey' ],
51
52
			# renamed sequences
53
			[ 'renameSequence', 'ipblocks_ipb_id_val', 'ipblocks_ipb_id_seq' ],
54
			[ 'renameSequence', 'rev_rev_id_val', 'revision_rev_id_seq' ],
55
			[ 'renameSequence', 'text_old_id_val', 'text_old_id_seq' ],
56
			[ 'renameSequence', 'rc_rc_id_seq', 'recentchanges_rc_id_seq' ],
57
			[ 'renameSequence', 'log_log_id_seq', 'logging_log_id_seq' ],
58
			[ 'renameSequence', 'pr_id_val', 'page_restrictions_pr_id_seq' ],
59
			[ 'renameSequence', 'us_id_seq', 'uploadstash_us_id_seq' ],
60
61
			# since r58263
62
			[ 'renameSequence', 'category_id_seq', 'category_cat_id_seq' ],
63
64
			# new sequences if not renamed above
65
			[ 'addSequence', 'logging', false, 'logging_log_id_seq' ],
66
			[ 'addSequence', 'page_restrictions', false, 'page_restrictions_pr_id_seq' ],
67
			[ 'addSequence', 'filearchive', 'fa_id', 'filearchive_fa_id_seq' ],
68
			[ 'addSequence', 'archive', false, 'archive_ar_id_seq' ],
69
			[ 'addSequence', 'externallinks', false, 'externallinks_el_id_seq' ],
70
			[ 'addSequence', 'watchlist', false, 'watchlist_wl_id_seq' ],
71
			[ 'addSequence', 'change_tag', false, 'change_tag_ct_id_seq' ],
72
			[ 'addSequence', 'tag_summary', false, 'tag_summary_ts_id_seq' ],
73
74
			# new tables
75
			[ 'addTable', 'category', 'patch-category.sql' ],
76
			[ 'addTable', 'page', 'patch-page.sql' ],
77
			[ 'addTable', 'querycachetwo', 'patch-querycachetwo.sql' ],
78
			[ 'addTable', 'page_props', 'patch-page_props.sql' ],
79
			[ 'addTable', 'page_restrictions', 'patch-page_restrictions.sql' ],
80
			[ 'addTable', 'profiling', 'patch-profiling.sql' ],
81
			[ 'addTable', 'protected_titles', 'patch-protected_titles.sql' ],
82
			[ 'addTable', 'redirect', 'patch-redirect.sql' ],
83
			[ 'addTable', 'updatelog', 'patch-updatelog.sql' ],
84
			[ 'addTable', 'change_tag', 'patch-change_tag.sql' ],
85
			[ 'addTable', 'tag_summary', 'patch-tag_summary.sql' ],
86
			[ 'addTable', 'valid_tag', 'patch-valid_tag.sql' ],
87
			[ 'addTable', 'user_properties', 'patch-user_properties.sql' ],
88
			[ 'addTable', 'log_search', 'patch-log_search.sql' ],
89
			[ 'addTable', 'l10n_cache', 'patch-l10n_cache.sql' ],
90
			[ 'addTable', 'iwlinks', 'patch-iwlinks.sql' ],
91
			[ 'addTable', 'module_deps', 'patch-module_deps.sql' ],
92
			[ 'addTable', 'uploadstash', 'patch-uploadstash.sql' ],
93
			[ 'addTable', 'user_former_groups', 'patch-user_former_groups.sql' ],
94
			[ 'addTable', 'sites', 'patch-sites.sql' ],
95
			[ 'addTable', 'bot_passwords', 'patch-bot_passwords.sql' ],
96
97
			# Needed before new field
98
			[ 'convertArchive2' ],
99
100
			# new fields
101
			[ 'addPgField', 'updatelog', 'ul_value', 'TEXT' ],
102
			[ 'addPgField', 'archive', 'ar_deleted', 'SMALLINT NOT NULL DEFAULT 0' ],
103
			[ 'addPgField', 'archive', 'ar_len', 'INTEGER' ],
104
			[ 'addPgField', 'archive', 'ar_page_id', 'INTEGER' ],
105
			[ 'addPgField', 'archive', 'ar_parent_id', 'INTEGER' ],
106
			[ 'addPgField', 'archive', 'ar_content_model', 'TEXT' ],
107
			[ 'addPgField', 'archive', 'ar_content_format', 'TEXT' ],
108
			[ 'addPgField', 'categorylinks', 'cl_sortkey_prefix', "TEXT NOT NULL DEFAULT ''" ],
109
			[ 'addPgField', 'categorylinks', 'cl_collation', "TEXT NOT NULL DEFAULT 0" ],
110
			[ 'addPgField', 'categorylinks', 'cl_type', "TEXT NOT NULL DEFAULT 'page'" ],
111
			[ 'addPgField', 'image', 'img_sha1', "TEXT NOT NULL DEFAULT ''" ],
112
			[ 'addPgField', 'ipblocks', 'ipb_allow_usertalk', 'SMALLINT NOT NULL DEFAULT 0' ],
113
			[ 'addPgField', 'ipblocks', 'ipb_anon_only', 'SMALLINT NOT NULL DEFAULT 0' ],
114
			[ 'addPgField', 'ipblocks', 'ipb_by_text', "TEXT NOT NULL DEFAULT ''" ],
115
			[ 'addPgField', 'ipblocks', 'ipb_block_email', 'SMALLINT NOT NULL DEFAULT 0' ],
116
			[ 'addPgField', 'ipblocks', 'ipb_create_account', 'SMALLINT NOT NULL DEFAULT 1' ],
117
			[ 'addPgField', 'ipblocks', 'ipb_deleted', 'SMALLINT NOT NULL DEFAULT 0' ],
118
			[ 'addPgField', 'ipblocks', 'ipb_enable_autoblock', 'SMALLINT NOT NULL DEFAULT 1' ],
119
			[ 'addPgField', 'ipblocks', 'ipb_parent_block_id',
120
				'INTEGER DEFAULT NULL REFERENCES ipblocks(ipb_id) ' .
121
				'ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED' ],
122
			[ 'addPgField', 'filearchive', 'fa_deleted', 'SMALLINT NOT NULL DEFAULT 0' ],
123
			[ 'addPgField', 'filearchive', 'fa_sha1', "TEXT NOT NULL DEFAULT ''" ],
124
			[ 'addPgField', 'logging', 'log_deleted', 'SMALLINT NOT NULL DEFAULT 0' ],
125
			[ 'addPgField', 'logging', 'log_id',
126
				"INTEGER NOT NULL PRIMARY KEY DEFAULT nextval('logging_log_id_seq')" ],
127
			[ 'addPgField', 'logging', 'log_params', 'TEXT' ],
128
			[ 'addPgField', 'mwuser', 'user_editcount', 'INTEGER' ],
129
			[ 'addPgField', 'mwuser', 'user_newpass_time', 'TIMESTAMPTZ' ],
130
			[ 'addPgField', 'oldimage', 'oi_deleted', 'SMALLINT NOT NULL DEFAULT 0' ],
131
			[ 'addPgField', 'oldimage', 'oi_major_mime', "TEXT NOT NULL DEFAULT 'unknown'" ],
132
			[ 'addPgField', 'oldimage', 'oi_media_type', 'TEXT' ],
133
			[ 'addPgField', 'oldimage', 'oi_metadata', "BYTEA NOT NULL DEFAULT ''" ],
134
			[ 'addPgField', 'oldimage', 'oi_minor_mime', "TEXT NOT NULL DEFAULT 'unknown'" ],
135
			[ 'addPgField', 'oldimage', 'oi_sha1', "TEXT NOT NULL DEFAULT ''" ],
136
			[ 'addPgField', 'page', 'page_content_model', 'TEXT' ],
137
			[ 'addPgField', 'page_restrictions', 'pr_id',
138
				"INTEGER NOT NULL UNIQUE DEFAULT nextval('page_restrictions_pr_id_seq')" ],
139
			[ 'addPgField', 'profiling', 'pf_memory', 'NUMERIC(18,10) NOT NULL DEFAULT 0' ],
140
			[ 'addPgField', 'recentchanges', 'rc_deleted', 'SMALLINT NOT NULL DEFAULT 0' ],
141
			[ 'addPgField', 'recentchanges', 'rc_log_action', 'TEXT' ],
142
			[ 'addPgField', 'recentchanges', 'rc_log_type', 'TEXT' ],
143
			[ 'addPgField', 'recentchanges', 'rc_logid', 'INTEGER NOT NULL DEFAULT 0' ],
144
			[ 'addPgField', 'recentchanges', 'rc_new_len', 'INTEGER' ],
145
			[ 'addPgField', 'recentchanges', 'rc_old_len', 'INTEGER' ],
146
			[ 'addPgField', 'recentchanges', 'rc_params', 'TEXT' ],
147
			[ 'addPgField', 'redirect', 'rd_interwiki', 'TEXT NULL' ],
148
			[ 'addPgField', 'redirect', 'rd_fragment', 'TEXT NULL' ],
149
			[ 'addPgField', 'revision', 'rev_deleted', 'SMALLINT NOT NULL DEFAULT 0' ],
150
			[ 'addPgField', 'revision', 'rev_len', 'INTEGER' ],
151
			[ 'addPgField', 'revision', 'rev_parent_id', 'INTEGER DEFAULT NULL' ],
152
			[ 'addPgField', 'revision', 'rev_content_model', 'TEXT' ],
153
			[ 'addPgField', 'revision', 'rev_content_format', 'TEXT' ],
154
			[ 'addPgField', 'site_stats', 'ss_active_users', "INTEGER DEFAULT '-1'" ],
155
			[ 'addPgField', 'user_newtalk', 'user_last_timestamp', 'TIMESTAMPTZ' ],
156
			[ 'addPgField', 'logging', 'log_user_text', "TEXT NOT NULL DEFAULT ''" ],
157
			[ 'addPgField', 'logging', 'log_page', 'INTEGER' ],
158
			[ 'addPgField', 'interwiki', 'iw_api', "TEXT NOT NULL DEFAULT ''" ],
159
			[ 'addPgField', 'interwiki', 'iw_wikiid', "TEXT NOT NULL DEFAULT ''" ],
160
			[ 'addPgField', 'revision', 'rev_sha1', "TEXT NOT NULL DEFAULT ''" ],
161
			[ 'addPgField', 'archive', 'ar_sha1', "TEXT NOT NULL DEFAULT ''" ],
162
			[ 'addPgField', 'uploadstash', 'us_chunk_inx', "INTEGER NULL" ],
163
			[ 'addPgField', 'job', 'job_timestamp', "TIMESTAMPTZ" ],
164
			[ 'addPgField', 'job', 'job_random', "INTEGER NOT NULL DEFAULT 0" ],
165
			[ 'addPgField', 'job', 'job_attempts', "INTEGER NOT NULL DEFAULT 0" ],
166
			[ 'addPgField', 'job', 'job_token', "TEXT NOT NULL DEFAULT ''" ],
167
			[ 'addPgField', 'job', 'job_token_timestamp', "TIMESTAMPTZ" ],
168
			[ 'addPgField', 'job', 'job_sha1', "TEXT NOT NULL DEFAULT ''" ],
169
			[ 'addPgField', 'archive', 'ar_id',
170
				"INTEGER NOT NULL PRIMARY KEY DEFAULT nextval('archive_ar_id_seq')" ],
171
			[ 'addPgField', 'externallinks', 'el_id',
172
				"INTEGER NOT NULL PRIMARY KEY DEFAULT nextval('externallinks_el_id_seq')" ],
173
			[ 'addPgField', 'uploadstash', 'us_props', "BYTEA" ],
174
175
			# type changes
176
			[ 'changeField', 'archive', 'ar_deleted', 'smallint', '' ],
177
			[ 'changeField', 'archive', 'ar_minor_edit', 'smallint',
178
				'ar_minor_edit::smallint DEFAULT 0' ],
179
			[ 'changeField', 'filearchive', 'fa_deleted', 'smallint', '' ],
180
			[ 'changeField', 'filearchive', 'fa_height', 'integer', '' ],
181
			[ 'changeField', 'filearchive', 'fa_metadata', 'bytea', "decode(fa_metadata,'escape')" ],
182
			[ 'changeField', 'filearchive', 'fa_size', 'integer', '' ],
183
			[ 'changeField', 'filearchive', 'fa_width', 'integer', '' ],
184
			[ 'changeField', 'filearchive', 'fa_storage_group', 'text', '' ],
185
			[ 'changeField', 'filearchive', 'fa_storage_key', 'text', '' ],
186
			[ 'changeField', 'image', 'img_metadata', 'bytea', "decode(img_metadata,'escape')" ],
187
			[ 'changeField', 'image', 'img_size', 'integer', '' ],
188
			[ 'changeField', 'image', 'img_width', 'integer', '' ],
189
			[ 'changeField', 'image', 'img_height', 'integer', '' ],
190
			[ 'changeField', 'interwiki', 'iw_local', 'smallint', 'iw_local::smallint' ],
191
			[ 'changeField', 'interwiki', 'iw_trans', 'smallint', 'iw_trans::smallint DEFAULT 0' ],
192
			[ 'changeField', 'ipblocks', 'ipb_auto', 'smallint', 'ipb_auto::smallint DEFAULT 0' ],
193
			[ 'changeField', 'ipblocks', 'ipb_anon_only', 'smallint',
194
				"CASE WHEN ipb_anon_only=' ' THEN 0 ELSE ipb_anon_only::smallint END DEFAULT 0" ],
195
			[ 'changeField', 'ipblocks', 'ipb_create_account', 'smallint',
196
				"CASE WHEN ipb_create_account=' ' THEN 0 ELSE ipb_create_account::smallint END DEFAULT 1" ],
197
			[ 'changeField', 'ipblocks', 'ipb_enable_autoblock', 'smallint',
198
				"CASE WHEN ipb_enable_autoblock=' ' THEN 0 ELSE ipb_enable_autoblock::smallint END DEFAULT 1" ],
199
			[ 'changeField', 'ipblocks', 'ipb_block_email', 'smallint',
200
				"CASE WHEN ipb_block_email=' ' THEN 0 ELSE ipb_block_email::smallint END DEFAULT 0" ],
201
			[ 'changeField', 'ipblocks', 'ipb_address', 'text', 'ipb_address::text' ],
202
			[ 'changeField', 'ipblocks', 'ipb_deleted', 'smallint', 'ipb_deleted::smallint DEFAULT 0' ],
203
			[ 'changeField', 'mwuser', 'user_token', 'text', '' ],
204
			[ 'changeField', 'mwuser', 'user_email_token', 'text', '' ],
205
			[ 'changeField', 'objectcache', 'keyname', 'text', '' ],
206
			[ 'changeField', 'oldimage', 'oi_height', 'integer', '' ],
207
			[ 'changeField', 'oldimage', 'oi_metadata', 'bytea', "decode(img_metadata,'escape')" ],
208
			[ 'changeField', 'oldimage', 'oi_size', 'integer', '' ],
209
			[ 'changeField', 'oldimage', 'oi_width', 'integer', '' ],
210
			[ 'changeField', 'page', 'page_is_redirect', 'smallint',
211
				'page_is_redirect::smallint DEFAULT 0' ],
212
			[ 'changeField', 'page', 'page_is_new', 'smallint', 'page_is_new::smallint DEFAULT 0' ],
213
			[ 'changeField', 'querycache', 'qc_value', 'integer', '' ],
214
			[ 'changeField', 'querycachetwo', 'qcc_value', 'integer', '' ],
215
			[ 'changeField', 'recentchanges', 'rc_bot', 'smallint', 'rc_bot::smallint DEFAULT 0' ],
216
			[ 'changeField', 'recentchanges', 'rc_deleted', 'smallint', '' ],
217
			[ 'changeField', 'recentchanges', 'rc_minor', 'smallint', 'rc_minor::smallint DEFAULT 0' ],
218
			[ 'changeField', 'recentchanges', 'rc_new', 'smallint', 'rc_new::smallint DEFAULT 0' ],
219
			[ 'changeField', 'recentchanges', 'rc_type', 'smallint', 'rc_type::smallint DEFAULT 0' ],
220
			[ 'changeField', 'recentchanges', 'rc_patrolled', 'smallint',
221
				'rc_patrolled::smallint DEFAULT 0' ],
222
			[ 'changeField', 'revision', 'rev_deleted', 'smallint', 'rev_deleted::smallint DEFAULT 0' ],
223
			[ 'changeField', 'revision', 'rev_minor_edit', 'smallint',
224
				'rev_minor_edit::smallint DEFAULT 0' ],
225
			[ 'changeField', 'templatelinks', 'tl_namespace', 'smallint', 'tl_namespace::smallint' ],
226
			[ 'changeField', 'user_newtalk', 'user_ip', 'text', 'host(user_ip)' ],
227
			[ 'changeField', 'uploadstash', 'us_image_bits', 'smallint', '' ],
228
			[ 'changeField', 'profiling', 'pf_time', 'float', '' ],
229
			[ 'changeField', 'profiling', 'pf_memory', 'float', '' ],
230
231
			# null changes
232
			[ 'changeNullableField', 'oldimage', 'oi_bits', 'NULL' ],
233
			[ 'changeNullableField', 'oldimage', 'oi_timestamp', 'NULL' ],
234
			[ 'changeNullableField', 'oldimage', 'oi_major_mime', 'NULL' ],
235
			[ 'changeNullableField', 'oldimage', 'oi_minor_mime', 'NULL' ],
236
			[ 'changeNullableField', 'image', 'img_metadata', 'NOT NULL' ],
237
			[ 'changeNullableField', 'filearchive', 'fa_metadata', 'NOT NULL' ],
238
			[ 'changeNullableField', 'recentchanges', 'rc_cur_id', 'NULL' ],
239
			[ 'changeNullableField', 'recentchanges', 'rc_cur_time', 'NULL' ],
240
241
			[ 'checkOiDeleted' ],
242
243
			# New indexes
244
			[ 'addPgIndex', 'archive', 'archive_user_text', '(ar_user_text)' ],
245
			[ 'addPgIndex', 'image', 'img_sha1', '(img_sha1)' ],
246
			[ 'addPgIndex', 'ipblocks', 'ipb_parent_block_id', '(ipb_parent_block_id)' ],
247
			[ 'addPgIndex', 'oldimage', 'oi_sha1', '(oi_sha1)' ],
248
			[ 'addPgIndex', 'page', 'page_mediawiki_title', '(page_title) WHERE page_namespace = 8' ],
249
			[ 'addPgIndex', 'pagelinks', 'pagelinks_title', '(pl_title)' ],
250
			[ 'addPgIndex', 'page_props', 'pp_propname_page', '(pp_propname, pp_page)' ],
251
			[ 'addPgIndex', 'revision', 'rev_text_id_idx', '(rev_text_id)' ],
252
			[ 'addPgIndex', 'recentchanges', 'rc_timestamp_bot', '(rc_timestamp) WHERE rc_bot = 0' ],
253
			[ 'addPgIndex', 'templatelinks', 'templatelinks_from', '(tl_from)' ],
254
			[ 'addPgIndex', 'watchlist', 'wl_user', '(wl_user)' ],
255
			[ 'addPgIndex', 'watchlist', 'wl_user_notificationtimestamp',
256
				'(wl_user, wl_notificationtimestamp)' ],
257
			[ 'addPgIndex', 'logging', 'logging_user_type_time',
258
				'(log_user, log_type, log_timestamp)' ],
259
			[ 'addPgIndex', 'logging', 'logging_page_id_time', '(log_page,log_timestamp)' ],
260
			[ 'addPgIndex', 'iwlinks', 'iwl_prefix_from_title', '(iwl_prefix, iwl_from, iwl_title)' ],
261
			[ 'addPgIndex', 'iwlinks', 'iwl_prefix_title_from', '(iwl_prefix, iwl_title, iwl_from)' ],
262
			[ 'addPgIndex', 'job', 'job_timestamp_idx', '(job_timestamp)' ],
263
			[ 'addPgIndex', 'job', 'job_sha1', '(job_sha1)' ],
264
			[ 'addPgIndex', 'job', 'job_cmd_token', '(job_cmd, job_token, job_random)' ],
265
			[ 'addPgIndex', 'job', 'job_cmd_token_id', '(job_cmd, job_token, job_id)' ],
266
			[ 'addPgIndex', 'filearchive', 'fa_sha1', '(fa_sha1)' ],
267
			[ 'addPgIndex', 'logging', 'logging_user_text_type_time',
268
				'(log_user_text, log_type, log_timestamp)' ],
269
			[ 'addPgIndex', 'logging', 'logging_user_text_time', '(log_user_text, log_timestamp)' ],
270
271
			[ 'checkIndex', 'pagelink_unique', [
272
				[ 'pl_from', 'int4_ops', 'btree', 0 ],
273
				[ 'pl_namespace', 'int2_ops', 'btree', 0 ],
274
				[ 'pl_title', 'text_ops', 'btree', 0 ],
275
			],
276
				'CREATE UNIQUE INDEX pagelink_unique ON pagelinks (pl_from,pl_namespace,pl_title)' ],
277
			[ 'checkIndex', 'cl_sortkey', [
278
				[ 'cl_to', 'text_ops', 'btree', 0 ],
279
				[ 'cl_sortkey', 'text_ops', 'btree', 0 ],
280
				[ 'cl_from', 'int4_ops', 'btree', 0 ],
281
			],
282
				'CREATE INDEX cl_sortkey ON "categorylinks" ' .
283
					'USING "btree" ("cl_to", "cl_sortkey", "cl_from")' ],
284
			[ 'checkIndex', 'iwl_prefix_title_from', [
285
				[ 'iwl_prefix', 'text_ops', 'btree', 0 ],
286
				[ 'iwl_title', 'text_ops', 'btree', 0 ],
287
				[ 'iwl_from', 'int4_ops', 'btree', 0 ],
288
			],
289
			'CREATE INDEX iwl_prefix_title_from ON "iwlinks" ' .
290
				'USING "btree" ("iwl_prefix", "iwl_title", "iwl_from")' ],
291
			[ 'checkIndex', 'logging_times', [
292
				[ 'log_timestamp', 'timestamptz_ops', 'btree', 0 ],
293
			],
294
			'CREATE INDEX "logging_times" ON "logging" USING "btree" ("log_timestamp")' ],
295
			[ 'dropIndex', 'oldimage', 'oi_name' ],
296
			[ 'checkIndex', 'oi_name_archive_name', [
297
				[ 'oi_name', 'text_ops', 'btree', 0 ],
298
				[ 'oi_archive_name', 'text_ops', 'btree', 0 ],
299
			],
300
			'CREATE INDEX "oi_name_archive_name" ON "oldimage" ' .
301
				'USING "btree" ("oi_name", "oi_archive_name")' ],
302
			[ 'checkIndex', 'oi_name_timestamp', [
303
				[ 'oi_name', 'text_ops', 'btree', 0 ],
304
				[ 'oi_timestamp', 'timestamptz_ops', 'btree', 0 ],
305
			],
306
			'CREATE INDEX "oi_name_timestamp" ON "oldimage" ' .
307
				'USING "btree" ("oi_name", "oi_timestamp")' ],
308
			[ 'checkIndex', 'page_main_title', [
309
				[ 'page_title', 'text_pattern_ops', 'btree', 0 ],
310
			],
311
			'CREATE INDEX "page_main_title" ON "page" ' .
312
				'USING "btree" ("page_title" "text_pattern_ops") WHERE ("page_namespace" = 0)' ],
313
			[ 'checkIndex', 'page_mediawiki_title', [
314
				[ 'page_title', 'text_pattern_ops', 'btree', 0 ],
315
			],
316
			'CREATE INDEX "page_mediawiki_title" ON "page" ' .
317
				'USING "btree" ("page_title" "text_pattern_ops") WHERE ("page_namespace" = 8)' ],
318
			[ 'checkIndex', 'page_project_title', [
319
				[ 'page_title', 'text_pattern_ops', 'btree', 0 ],
320
			],
321
			'CREATE INDEX "page_project_title" ON "page" ' .
322
				'USING "btree" ("page_title" "text_pattern_ops") ' .
323
				'WHERE ("page_namespace" = 4)' ],
324
			[ 'checkIndex', 'page_talk_title', [
325
				[ 'page_title', 'text_pattern_ops', 'btree', 0 ],
326
			],
327
			'CREATE INDEX "page_talk_title" ON "page" ' .
328
				'USING "btree" ("page_title" "text_pattern_ops") ' .
329
				'WHERE ("page_namespace" = 1)' ],
330
			[ 'checkIndex', 'page_user_title', [
331
				[ 'page_title', 'text_pattern_ops', 'btree', 0 ],
332
			],
333
			'CREATE INDEX "page_user_title" ON "page" ' .
334
				'USING "btree" ("page_title" "text_pattern_ops") WHERE ' .
335
				'("page_namespace" = 2)' ],
336
			[ 'checkIndex', 'page_utalk_title', [
337
				[ 'page_title', 'text_pattern_ops', 'btree', 0 ],
338
			],
339
			'CREATE INDEX "page_utalk_title" ON "page" ' .
340
				'USING "btree" ("page_title" "text_pattern_ops") ' .
341
				'WHERE ("page_namespace" = 3)' ],
342
			[ 'checkIndex', 'ts2_page_text', [
343
				[ 'textvector', 'tsvector_ops', 'gist', 0 ],
344
			],
345
			'CREATE INDEX "ts2_page_text" ON "pagecontent" USING "gist" ("textvector")' ],
346
			[ 'checkIndex', 'ts2_page_title', [
347
				[ 'titlevector', 'tsvector_ops', 'gist', 0 ],
348
			],
349
			'CREATE INDEX "ts2_page_title" ON "page" USING "gist" ("titlevector")' ],
350
351
			[ 'checkOiNameConstraint' ],
352
			[ 'checkPageDeletedTrigger' ],
353
			[ 'checkRevUserFkey' ],
354
			[ 'dropIndex', 'ipblocks', 'ipb_address' ],
355
			[ 'checkIndex', 'ipb_address_unique', [
356
				[ 'ipb_address', 'text_ops', 'btree', 0 ],
357
				[ 'ipb_user', 'int4_ops', 'btree', 0 ],
358
				[ 'ipb_auto', 'int2_ops', 'btree', 0 ],
359
				[ 'ipb_anon_only', 'int2_ops', 'btree', 0 ],
360
			],
361
			'CREATE UNIQUE INDEX ipb_address_unique ' .
362
				'ON ipblocks (ipb_address,ipb_user,ipb_auto,ipb_anon_only)' ],
363
364
			[ 'checkIwlPrefix' ],
365
366
			# All FK columns should be deferred
367
			[ 'changeFkeyDeferrable', 'archive', 'ar_user', 'mwuser(user_id) ON DELETE SET NULL' ],
368
			[ 'changeFkeyDeferrable', 'categorylinks', 'cl_from', 'page(page_id) ON DELETE CASCADE' ],
369
			[ 'changeFkeyDeferrable', 'externallinks', 'el_from', 'page(page_id) ON DELETE CASCADE' ],
370
			[ 'changeFkeyDeferrable', 'filearchive', 'fa_deleted_user',
371
				'mwuser(user_id) ON DELETE SET NULL' ],
372
			[ 'changeFkeyDeferrable', 'filearchive', 'fa_user', 'mwuser(user_id) ON DELETE SET NULL' ],
373
			[ 'changeFkeyDeferrable', 'image', 'img_user', 'mwuser(user_id) ON DELETE SET NULL' ],
374
			[ 'changeFkeyDeferrable', 'imagelinks', 'il_from', 'page(page_id) ON DELETE CASCADE' ],
375
			[ 'changeFkeyDeferrable', 'ipblocks', 'ipb_by', 'mwuser(user_id) ON DELETE CASCADE' ],
376
			[ 'changeFkeyDeferrable', 'ipblocks', 'ipb_user', 'mwuser(user_id) ON DELETE SET NULL' ],
377
			[ 'changeFkeyDeferrable', 'ipblocks', 'ipb_parent_block_id',
378
				'ipblocks(ipb_id) ON DELETE SET NULL' ],
379
			[ 'changeFkeyDeferrable', 'langlinks', 'll_from', 'page(page_id) ON DELETE CASCADE' ],
380
			[ 'changeFkeyDeferrable', 'logging', 'log_user', 'mwuser(user_id) ON DELETE SET NULL' ],
381
			[ 'changeFkeyDeferrable', 'oldimage', 'oi_name',
382
				'image(img_name) ON DELETE CASCADE ON UPDATE CASCADE' ],
383
			[ 'changeFkeyDeferrable', 'oldimage', 'oi_user', 'mwuser(user_id) ON DELETE SET NULL' ],
384
			[ 'changeFkeyDeferrable', 'pagelinks', 'pl_from', 'page(page_id) ON DELETE CASCADE' ],
385
			[ 'changeFkeyDeferrable', 'page_props', 'pp_page', 'page (page_id) ON DELETE CASCADE' ],
386
			[ 'changeFkeyDeferrable', 'page_restrictions', 'pr_page',
387
				'page(page_id) ON DELETE CASCADE' ],
388
			[ 'changeFkeyDeferrable', 'protected_titles', 'pt_user',
389
				'mwuser(user_id) ON DELETE SET NULL' ],
390
			[ 'changeFkeyDeferrable', 'recentchanges', 'rc_user',
391
				'mwuser(user_id) ON DELETE SET NULL' ],
392
			[ 'changeFkeyDeferrable', 'redirect', 'rd_from', 'page(page_id) ON DELETE CASCADE' ],
393
			[ 'changeFkeyDeferrable', 'revision', 'rev_page', 'page (page_id) ON DELETE CASCADE' ],
394
			[ 'changeFkeyDeferrable', 'revision', 'rev_user', 'mwuser(user_id) ON DELETE RESTRICT' ],
395
			[ 'changeFkeyDeferrable', 'templatelinks', 'tl_from', 'page(page_id) ON DELETE CASCADE' ],
396
			[ 'changeFkeyDeferrable', 'user_groups', 'ug_user', 'mwuser(user_id) ON DELETE CASCADE' ],
397
			[ 'changeFkeyDeferrable', 'user_newtalk', 'user_id', 'mwuser(user_id) ON DELETE CASCADE' ],
398
			[ 'changeFkeyDeferrable', 'user_properties', 'up_user',
399
				'mwuser(user_id) ON DELETE CASCADE' ],
400
			[ 'changeFkeyDeferrable', 'watchlist', 'wl_user', 'mwuser(user_id) ON DELETE CASCADE' ],
401
402
			# r81574
403
			[ 'addInterwikiType' ],
404
			# end
405
			[ 'tsearchFixes' ],
406
407
			// 1.23
408
			[ 'addPgField', 'recentchanges', 'rc_source', "TEXT NOT NULL DEFAULT ''" ],
409
			[ 'addPgField', 'page', 'page_links_updated', "TIMESTAMPTZ NULL" ],
410
			[ 'addPgField', 'mwuser', 'user_password_expires', 'TIMESTAMPTZ NULL' ],
411
			[ 'changeFieldPurgeTable', 'l10n_cache', 'lc_value', 'bytea',
412
				"replace(lc_value,'\','\\\\')::bytea" ],
413
			// 1.23.9
414
			[ 'rebuildTextSearch' ],
415
416
			// 1.24
417
			[ 'addPgField', 'page_props', 'pp_sortkey', 'float NULL' ],
418
			[ 'addPgIndex', 'page_props', 'pp_propname_sortkey_page',
419
					'( pp_propname, pp_sortkey, pp_page ) WHERE ( pp_sortkey IS NOT NULL )' ],
420
			[ 'addPgField', 'page', 'page_lang', 'TEXT default NULL' ],
421
			[ 'addPgField', 'pagelinks', 'pl_from_namespace', 'INTEGER NOT NULL DEFAULT 0' ],
422
			[ 'addPgField', 'templatelinks', 'tl_from_namespace', 'INTEGER NOT NULL DEFAULT 0' ],
423
			[ 'addPgField', 'imagelinks', 'il_from_namespace', 'INTEGER NOT NULL DEFAULT 0' ],
424
425
			// 1.25
426
			[ 'dropTable', 'hitcounter' ],
427
			[ 'dropField', 'site_stats', 'ss_total_views', 'patch-drop-ss_total_views.sql' ],
428
			[ 'dropField', 'page', 'page_counter', 'patch-drop-page_counter.sql' ],
429
			[ 'dropFkey', 'recentchanges', 'rc_cur_id' ],
430
431
			// 1.27
432
			[ 'dropTable', 'msg_resource_links' ],
433
			[ 'dropTable', 'msg_resource' ],
434
			[
435
				'addPgField', 'watchlist', 'wl_id',
436
				"INTEGER NOT NULL PRIMARY KEY DEFAULT nextval('watchlist_wl_id_seq')"
437
			],
438
439
			// 1.28
440
			[ 'addPgIndex', 'recentchanges', 'rc_name_type_patrolled_timestamp',
441
				'( rc_namespace, rc_type, rc_patrolled, rc_timestamp )' ],
442
			[ 'addPgField', 'change_tag', 'ct_id',
443
				"INTEGER NOT NULL PRIMARY KEY DEFAULT nextval('change_tag_ct_id_seq')" ],
444
			[ 'addPgField', 'tag_summary', 'ts_id',
445
				"INTEGER NOT NULL PRIMARY KEY DEFAULT nextval('tag_summary_ts_id_seq')" ],
446
		];
447
	}
448
449
	protected function getOldGlobalUpdates() {
450
		global $wgExtNewTables, $wgExtPGNewFields, $wgExtPGAlteredFields, $wgExtNewIndexes;
451
452
		$updates = [];
453
454
		# Add missing extension tables
455 View Code Duplication
		foreach ( $wgExtNewTables as $tableRecord ) {
456
			$updates[] = [
457
				'addTable', $tableRecord[0], $tableRecord[1], true
458
			];
459
		}
460
461
		# Add missing extension fields
462
		foreach ( $wgExtPGNewFields as $fieldRecord ) {
463
			$updates[] = [
464
				'addPgField', $fieldRecord[0], $fieldRecord[1],
465
				$fieldRecord[2]
466
			];
467
		}
468
469
		# Change altered columns
470
		foreach ( $wgExtPGAlteredFields as $fieldRecord ) {
471
			$updates[] = [
472
				'changeField', $fieldRecord[0], $fieldRecord[1],
473
				$fieldRecord[2]
474
			];
475
		}
476
477
		# Add missing extension indexes
478 View Code Duplication
		foreach ( $wgExtNewIndexes as $fieldRecord ) {
479
			$updates[] = [
480
				'addPgExtIndex', $fieldRecord[0], $fieldRecord[1],
481
				$fieldRecord[2]
482
			];
483
		}
484
485
		return $updates;
486
	}
487
488
	protected function describeTable( $table ) {
489
		$q = <<<END
490
SELECT attname, attnum FROM pg_namespace, pg_class, pg_attribute
491
	WHERE pg_class.relnamespace = pg_namespace.oid
492
	  AND attrelid=pg_class.oid AND attnum > 0
493
	  AND relname=%s AND nspname=%s
494
END;
495
		$res = $this->db->query( sprintf( $q,
496
			$this->db->addQuotes( $table ),
497
			$this->db->addQuotes( $this->db->getCoreSchema() ) ) );
498
		if ( !$res ) {
499
			return null;
500
		}
501
502
		$cols = [];
503
		foreach ( $res as $r ) {
504
			$cols[] = [
505
				"name" => $r[0],
506
				"ord" => $r[1],
507
			];
508
		}
509
510
		return $cols;
511
	}
512
513
	function describeIndex( $idx ) {
514
		// first fetch the key (which is a list of columns ords) and
515
		// the table the index applies to (an oid)
516
		$q = <<<END
517
SELECT indkey, indrelid FROM pg_namespace, pg_class, pg_index
518
	WHERE nspname=%s
519
	  AND pg_class.relnamespace = pg_namespace.oid
520
	  AND relname=%s
521
	  AND indexrelid=pg_class.oid
522
END;
523
		$res = $this->db->query(
524
			sprintf(
525
				$q,
526
				$this->db->addQuotes( $this->db->getCoreSchema() ),
527
				$this->db->addQuotes( $idx )
528
			)
529
		);
530
		if ( !$res ) {
531
			return null;
532
		}
533
		$r = $this->db->fetchRow( $res );
534
		if ( !$r ) {
535
			return null;
536
		}
537
538
		$indkey = $r[0];
539
		$relid = intval( $r[1] );
540
		$indkeys = explode( ' ', $indkey );
541
542
		$colnames = [];
543
		foreach ( $indkeys as $rid ) {
544
			$query = <<<END
545
SELECT attname FROM pg_class, pg_attribute
546
	WHERE attrelid=$relid
547
	  AND attnum=%d
548
	  AND attrelid=pg_class.oid
549
END;
550
			$r2 = $this->db->query( sprintf( $query, $rid ) );
551
			if ( !$r2 ) {
552
				return null;
553
			}
554
			$row2 = $this->db->fetchRow( $r2 );
555
			if ( !$row2 ) {
556
				return null;
557
			}
558
			$colnames[] = $row2[0];
559
		}
560
561
		return $colnames;
562
	}
563
564
	function fkeyDeltype( $fkey ) {
565
		$q = <<<END
566
SELECT confdeltype FROM pg_constraint, pg_namespace
567
	WHERE connamespace=pg_namespace.oid
568
	  AND nspname=%s
569
	  AND conname=%s;
570
END;
571
		$r = $this->db->query(
572
			sprintf(
573
				$q,
574
				$this->db->addQuotes( $this->db->getCoreSchema() ),
575
				$this->db->addQuotes( $fkey )
576
			)
577
		);
578
		$row = $this->db->fetchRow( $r );
579
		if ( !$row ) {
580
			return null;
581
		}
582
583
		return $row[0];
584
	}
585
586
	function ruleDef( $table, $rule ) {
587
		$q = <<<END
588
SELECT definition FROM pg_rules
589
	WHERE schemaname = %s
590
	  AND tablename = %s
591
	  AND rulename = %s
592
END;
593
		$r = $this->db->query(
594
			sprintf(
595
				$q,
596
				$this->db->addQuotes( $this->db->getCoreSchema() ),
597
				$this->db->addQuotes( $table ),
598
				$this->db->addQuotes( $rule )
599
			)
600
		);
601
		$row = $this->db->fetchRow( $r );
602
		if ( !$row ) {
603
			return null;
604
		}
605
		$d = $row[0];
606
607
		return $d;
608
	}
609
610
	protected function addSequence( $table, $pkey, $ns ) {
611
		if ( !$this->db->sequenceExists( $ns ) ) {
612
			$this->output( "Creating sequence $ns\n" );
613
			$this->db->query( "CREATE SEQUENCE $ns" );
614
			if ( $pkey !== false ) {
615
				$this->setDefault( $table, $pkey, '"nextval"(\'"' . $ns . '"\'::"regclass")' );
616
			}
617
		}
618
	}
619
620
	protected function renameSequence( $old, $new ) {
621
		if ( $this->db->sequenceExists( $new ) ) {
622
			$this->output( "...sequence $new already exists.\n" );
623
624
			return;
625
		}
626
		if ( $this->db->sequenceExists( $old ) ) {
627
			$this->output( "Renaming sequence $old to $new\n" );
628
			$this->db->query( "ALTER SEQUENCE $old RENAME TO $new" );
629
		}
630
	}
631
632
	protected function renameTable( $old, $new, $patch = false ) {
633
		if ( $this->db->tableExists( $old ) ) {
634
			$this->output( "Renaming table $old to $new\n" );
635
			$old = $this->db->realTableName( $old, "quoted" );
636
			$new = $this->db->realTableName( $new, "quoted" );
637
			$this->db->query( "ALTER TABLE $old RENAME TO $new" );
638
			if ( $patch !== false ) {
639
				$this->applyPatch( $patch );
640
			}
641
		}
642
	}
643
644
	protected function renameIndex(
645
		$table, $old, $new, $skipBothIndexExistWarning = false, $a = false, $b = false
646
	) {
647
		// First requirement: the table must exist
648
		if ( !$this->db->tableExists( $table, __METHOD__ ) ) {
649
			$this->output( "...skipping: '$table' table doesn't exist yet.\n" );
650
651
			return;
652
		}
653
654
		// Second requirement: the new index must be missing
655 View Code Duplication
		if ( $this->db->indexExists( $table, $new, __METHOD__ ) ) {
656
			$this->output( "...index $new already set on $table table.\n" );
657
			if ( !$skipBothIndexExistWarning
658
				&& $this->db->indexExists( $table, $old, __METHOD__ )
659
			) {
660
				$this->output( "...WARNING: $old still exists, despite it has been " .
661
					"renamed into $new (which also exists).\n" .
662
					"            $old should be manually removed if not needed anymore.\n" );
663
			}
664
665
			return;
666
		}
667
668
		// Third requirement: the old index must exist
669
		if ( !$this->db->indexExists( $table, $old, __METHOD__ ) ) {
670
			$this->output( "...skipping: index $old doesn't exist.\n" );
671
672
			return;
673
		}
674
675
		$this->db->query( "ALTER INDEX $old RENAME TO $new" );
676
	}
677
678
	protected function addPgField( $table, $field, $type ) {
679
		$fi = $this->db->fieldInfo( $table, $field );
680
		if ( !is_null( $fi ) ) {
681
			$this->output( "...column '$table.$field' already exists\n" );
682
683
			return;
684
		} else {
685
			$this->output( "Adding column '$table.$field'\n" );
686
			$this->db->query( "ALTER TABLE $table ADD $field $type" );
687
		}
688
	}
689
690
	protected function changeField( $table, $field, $newtype, $default ) {
691
		$fi = $this->db->fieldInfo( $table, $field );
692
		if ( is_null( $fi ) ) {
693
			$this->output( "...ERROR: expected column $table.$field to exist\n" );
694
			exit( 1 );
695
		}
696
697
		if ( $fi->type() === $newtype ) {
698
			$this->output( "...column '$table.$field' is already of type '$newtype'\n" );
699
		} else {
700
			$this->output( "Changing column type of '$table.$field' from '{$fi->type()}' to '$newtype'\n" );
701
			$sql = "ALTER TABLE $table ALTER $field TYPE $newtype";
702 View Code Duplication
			if ( strlen( $default ) ) {
703
				$res = [];
704
				if ( preg_match( '/DEFAULT (.+)/', $default, $res ) ) {
705
					$sqldef = "ALTER TABLE $table ALTER $field SET DEFAULT $res[1]";
706
					$this->db->query( $sqldef );
707
					$default = preg_replace( '/\s*DEFAULT .+/', '', $default );
708
				}
709
				$sql .= " USING $default";
710
			}
711
			$this->db->query( $sql );
712
		}
713
	}
714
715
	protected function changeFieldPurgeTable( $table, $field, $newtype, $default ) {
716
		# # For a cache table, empty it if the field needs to be changed, because the old contents
717
		# # may be corrupted.  If the column is already the desired type, refrain from purging.
718
		$fi = $this->db->fieldInfo( $table, $field );
719
		if ( is_null( $fi ) ) {
720
			$this->output( "...ERROR: expected column $table.$field to exist\n" );
721
			exit( 1 );
0 ignored issues
show
Coding Style Compatibility introduced by
The method changeFieldPurgeTable() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
722
		}
723
724
		if ( $fi->type() === $newtype ) {
725
			$this->output( "...column '$table.$field' is already of type '$newtype'\n" );
726
		} else {
727
			$this->output( "Purging data from cache table '$table'\n" );
728
			$this->db->query( "DELETE from $table" );
729
			$this->output( "Changing column type of '$table.$field' from '{$fi->type()}' to '$newtype'\n" );
730
			$sql = "ALTER TABLE $table ALTER $field TYPE $newtype";
731 View Code Duplication
			if ( strlen( $default ) ) {
732
				$res = [];
733
				if ( preg_match( '/DEFAULT (.+)/', $default, $res ) ) {
734
					$sqldef = "ALTER TABLE $table ALTER $field SET DEFAULT $res[1]";
735
					$this->db->query( $sqldef );
736
					$default = preg_replace( '/\s*DEFAULT .+/', '', $default );
737
				}
738
				$sql .= " USING $default";
739
			}
740
			$this->db->query( $sql );
741
		}
742
	}
743
744
	protected function setDefault( $table, $field, $default ) {
745
746
		$info = $this->db->fieldInfo( $table, $field );
747 View Code Duplication
		if ( $info->defaultValue() !== $default ) {
748
			$this->output( "Changing '$table.$field' default value\n" );
749
			$this->db->query( "ALTER TABLE $table ALTER $field SET DEFAULT " . $default );
750
		}
751
	}
752
753
	protected function changeNullableField( $table, $field, $null ) {
754
		$fi = $this->db->fieldInfo( $table, $field );
755
		if ( is_null( $fi ) ) {
756
			$this->output( "...ERROR: expected column $table.$field to exist\n" );
757
			exit( 1 );
758
		}
759
		if ( $fi->isNullable() ) {
760
			# # It's NULL - does it need to be NOT NULL?
761 View Code Duplication
			if ( 'NOT NULL' === $null ) {
762
				$this->output( "Changing '$table.$field' to not allow NULLs\n" );
763
				$this->db->query( "ALTER TABLE $table ALTER $field SET NOT NULL" );
764
			} else {
765
				$this->output( "...column '$table.$field' is already set as NULL\n" );
766
			}
767
		} else {
768
			# # It's NOT NULL - does it need to be NULL?
769 View Code Duplication
			if ( 'NULL' === $null ) {
770
				$this->output( "Changing '$table.$field' to allow NULLs\n" );
771
				$this->db->query( "ALTER TABLE $table ALTER $field DROP NOT NULL" );
772
			} else {
773
				$this->output( "...column '$table.$field' is already set as NOT NULL\n" );
774
			}
775
		}
776
	}
777
778
	public function addPgIndex( $table, $index, $type ) {
779
		if ( $this->db->indexExists( $table, $index ) ) {
780
			$this->output( "...index '$index' on table '$table' already exists\n" );
781
		} else {
782
			$this->output( "Creating index '$index' on table '$table' $type\n" );
783
			$this->db->query( "CREATE INDEX $index ON $table $type" );
784
		}
785
	}
786
787
	public function addPgExtIndex( $table, $index, $type ) {
788
		if ( $this->db->indexExists( $table, $index ) ) {
789
			$this->output( "...index '$index' on table '$table' already exists\n" );
790
		} else {
791
			if ( preg_match( '/^\(/', $type ) ) {
792
				$this->output( "Creating index '$index' on table '$table'\n" );
793
				$this->db->query( "CREATE INDEX $index ON $table $type" );
794
			} else {
795
				$this->applyPatch( $type, true, "Creating index '$index' on table '$table'" );
796
			}
797
		}
798
	}
799
800
	protected function dropFkey( $table, $field ) {
801
		$fi = $this->db->fieldInfo( $table, $field );
802
		if ( is_null( $fi ) ) {
803
			$this->output( "WARNING! Column '$table.$field' does not exist but it should! " .
804
				"Please report this.\n" );
805
			return;
806
		}
807
		$conname = $fi->conname();
808 View Code Duplication
		if ( $fi->conname() ) {
809
			$this->output( "Dropping foreign key constraint on '$table.$field'\n" );
810
			$conclause = "CONSTRAINT \"$conname\"";
0 ignored issues
show
$conclause is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
811
			$command = "ALTER TABLE $table DROP CONSTRAINT $conname";
812
			$this->db->query( $command );
813
		} else {
814
			$this->output( "...foreign key constraint on '$table.$field' already does not exist\n" );
815
		};
816
	}
817
818
	protected function changeFkeyDeferrable( $table, $field, $clause ) {
819
		$fi = $this->db->fieldInfo( $table, $field );
820
		if ( is_null( $fi ) ) {
821
			$this->output( "WARNING! Column '$table.$field' does not exist but it should! " .
822
				"Please report this.\n" );
823
824
			return;
825
		}
826
		if ( $fi->is_deferred() && $fi->is_deferrable() ) {
827
			return;
828
		}
829
		$this->output( "Altering column '$table.$field' to be DEFERRABLE INITIALLY DEFERRED\n" );
830
		$conname = $fi->conname();
831 View Code Duplication
		if ( $fi->conname() ) {
832
			$conclause = "CONSTRAINT \"$conname\"";
833
			$command = "ALTER TABLE $table DROP CONSTRAINT $conname";
834
			$this->db->query( $command );
835
		} else {
836
			$this->output( "Column '$table.$field' does not have a foreign key " .
837
				"constraint, will be added\n" );
838
			$conclause = "";
839
		}
840
		$command =
841
			"ALTER TABLE $table ADD $conclause " .
842
			"FOREIGN KEY ($field) REFERENCES $clause DEFERRABLE INITIALLY DEFERRED";
843
		$this->db->query( $command );
844
	}
845
846
	protected function convertArchive2() {
847
		if ( $this->db->tableExists( "archive2" ) ) {
848
			if ( $this->db->ruleExists( 'archive', 'archive_insert' ) ) {
849
				$this->output( "Dropping rule 'archive_insert'\n" );
850
				$this->db->query( 'DROP RULE archive_insert ON archive' );
851
			}
852
			if ( $this->db->ruleExists( 'archive', 'archive_delete' ) ) {
853
				$this->output( "Dropping rule 'archive_delete'\n" );
854
				$this->db->query( 'DROP RULE archive_delete ON archive' );
855
			}
856
			$this->applyPatch(
857
				'patch-remove-archive2.sql',
858
				false,
859
				"Converting 'archive2' back to normal archive table"
860
			);
861
		} else {
862
			$this->output( "...obsolete table 'archive2' does not exist\n" );
863
		}
864
	}
865
866
	protected function checkOiDeleted() {
867
		if ( $this->db->fieldInfo( 'oldimage', 'oi_deleted' )->type() !== 'smallint' ) {
868
			$this->output( "Changing 'oldimage.oi_deleted' to type 'smallint'\n" );
869
			$this->db->query( "ALTER TABLE oldimage ALTER oi_deleted DROP DEFAULT" );
870
			$this->db->query(
871
				"ALTER TABLE oldimage ALTER oi_deleted TYPE SMALLINT USING (oi_deleted::smallint)" );
872
			$this->db->query( "ALTER TABLE oldimage ALTER oi_deleted SET DEFAULT 0" );
873
		} else {
874
			$this->output( "...column 'oldimage.oi_deleted' is already of type 'smallint'\n" );
875
		}
876
	}
877
878
	protected function checkOiNameConstraint() {
879
		if ( $this->db->hasConstraint( "oldimage_oi_name_fkey_cascaded" ) ) {
880
			$this->output( "...table 'oldimage' has correct cascading delete/update " .
881
				"foreign key to image\n" );
882
		} else {
883
			if ( $this->db->hasConstraint( "oldimage_oi_name_fkey" ) ) {
884
				$this->db->query(
885
					"ALTER TABLE oldimage DROP CONSTRAINT oldimage_oi_name_fkey" );
886
			}
887
			if ( $this->db->hasConstraint( "oldimage_oi_name_fkey_cascade" ) ) {
888
				$this->db->query(
889
					"ALTER TABLE oldimage DROP CONSTRAINT oldimage_oi_name_fkey_cascade" );
890
			}
891
			$this->output( "Making foreign key on table 'oldimage' (to image) a cascade delete/update\n" );
892
			$this->db->query(
893
				"ALTER TABLE oldimage ADD CONSTRAINT oldimage_oi_name_fkey_cascaded " .
894
				"FOREIGN KEY (oi_name) REFERENCES image(img_name) " .
895
				"ON DELETE CASCADE ON UPDATE CASCADE" );
896
		}
897
	}
898
899
	protected function checkPageDeletedTrigger() {
900
		if ( !$this->db->triggerExists( 'page', 'page_deleted' ) ) {
901
			$this->applyPatch(
902
				'patch-page_deleted.sql',
903
				false,
904
				"Adding function and trigger 'page_deleted' to table 'page'"
905
			);
906
		} else {
907
			$this->output( "...table 'page' has 'page_deleted' trigger\n" );
908
		}
909
	}
910
911
	protected function dropIndex( $table, $index, $patch = '', $fullpath = false ) {
912
		if ( $this->db->indexExists( $table, $index ) ) {
913
			$this->output( "Dropping obsolete index '$index'\n" );
914
			$this->db->query( "DROP INDEX \"" . $index . "\"" );
915
		}
916
	}
917
918
	protected function checkIndex( $index, $should_be, $good_def ) {
919
		$pu = $this->db->indexAttributes( $index );
920
		if ( !empty( $pu ) && $pu != $should_be ) {
921
			$this->output( "Dropping obsolete version of index '$index'\n" );
922
			$this->db->query( "DROP INDEX \"" . $index . "\"" );
923
			$pu = [];
924
		} else {
925
			$this->output( "...no need to drop index '$index'\n" );
926
		}
927
928
		if ( empty( $pu ) ) {
929
			$this->output( "Creating index '$index'\n" );
930
			$this->db->query( $good_def );
931
		} else {
932
			$this->output( "...index '$index' exists\n" );
933
		}
934
	}
935
936
	protected function checkRevUserFkey() {
937
		if ( $this->fkeyDeltype( 'revision_rev_user_fkey' ) == 'r' ) {
938
			$this->output( "...constraint 'revision_rev_user_fkey' is ON DELETE RESTRICT\n" );
939
		} else {
940
			$this->applyPatch(
941
				'patch-revision_rev_user_fkey.sql',
942
				false,
943
				"Changing constraint 'revision_rev_user_fkey' to ON DELETE RESTRICT"
944
			);
945
		}
946
	}
947
948
	protected function checkIwlPrefix() {
949
		if ( $this->db->indexExists( 'iwlinks', 'iwl_prefix' ) ) {
950
			$this->applyPatch(
951
				'patch-rename-iwl_prefix.sql',
952
				false,
953
				"Replacing index 'iwl_prefix' with 'iwl_prefix_title_from'"
954
			);
955
		}
956
	}
957
958
	protected function addInterwikiType() {
959
		$this->applyPatch( 'patch-add_interwiki.sql', false, "Refreshing add_interwiki()" );
960
	}
961
962
	protected function tsearchFixes() {
963
		# Tweak the page_title tsearch2 trigger to filter out slashes
964
		# This is create or replace, so harmless to call if not needed
965
		$this->applyPatch( 'patch-ts2pagetitle.sql', false, "Refreshing ts2_page_title()" );
966
967
		# If the server is 8.3 or higher, rewrite the tsearch2 triggers
968
		# in case they have the old 'default' versions
969
		# Gather version numbers in case we need them
970
		if ( $this->db->getServerVersion() >= 8.3 ) {
971
			$this->applyPatch( 'patch-tsearch2funcs.sql', false, "Rewriting tsearch2 triggers" );
972
		}
973
	}
974
975
	protected function rebuildTextSearch() {
976
		if ( $this->updateRowExists( 'patch-textsearch_bug66650.sql' ) ) {
977
			$this->output( "...bug 66650 already fixed or not applicable.\n" );
978
			return;
979
		};
980
		$this->applyPatch( 'patch-textsearch_bug66650.sql', false,
981
			'Rebuilding text search for bug 66650' );
982
	}
983
}
984