39 [
'addField',
'externallinks',
'el_to_path',
'patch-externallinks-el_to_path.sql' ],
42 [
'addField',
'user',
'user_is_temp',
'patch-user-user_is_temp.sql' ],
43 [
'runMaintenance', MigrateRevisionCommentTemp::class ],
44 [
'dropTable',
'revision_comment_temp' ],
45 [
'runMaintenance', MigrateExternallinks::class ],
46 [
'modifyField',
'externallinks',
'el_to',
'patch-externallinks-el_to_default.sql' ],
47 [
'addField',
'pagelinks',
'pl_target_id',
'patch-pagelinks-target_id.sql' ],
48 [
'dropField',
'externallinks',
'el_to',
'patch-externallinks-drop-el_to.sql' ],
49 [
'runMaintenance', FixInconsistentRedirects::class ],
50 [
'modifyField',
'image',
'img_size',
'patch-image-img_size_to_bigint.sql' ],
51 [
'modifyField',
'filearchive',
'fa_size',
'patch-filearchive-fa_size_to_bigint.sql' ],
52 [
'modifyField',
'oldimage',
'oi_size',
'patch-oldimage-oi_size_to_bigint.sql' ],
53 [
'modifyField',
'uploadstash',
'us_size',
'patch-uploadstash-us_size_to_bigint.sql' ],
56 [
'addField',
'user_autocreate_serial',
'uas_year',
'patch-user_autocreate_serial-uas_year.sql' ],
57 [
'addTable',
'block_target',
'patch-block_target.sql' ],
58 [
'dropIndex',
'categorylinks',
'cl_collation_ext',
'patch-drop-cl_collation_ext.sql' ],
59 [
'runMaintenance', PopulateUserIsTemp::class ],
62 [
'renameIndex',
'sites',
'sites_group',
'site_group' ],
63 [
'dropIndex',
'sites',
'site_type',
'patch-sites-drop_indexes.sql' ],
72 [
'dropIndex',
'sites',
'site_group',
'patch-sites-drop_site_group_index.sql' ],
73 [
'dropIndex',
'sites',
'site_source',
'patch-sites-drop_site_source_index.sql' ],
74 [
'dropIndex',
'sites',
'site_language',
'patch-sites-drop_site_language_index.sql' ],
75 [
'dropIndex',
'sites',
'site_protocol',
'patch-sites-drop_site_protocol_index.sql' ],
76 [
'dropIndex',
'sites',
'site_domain',
'patch-sites-drop_site_domain_index.sql' ],
77 [
'dropIndex',
'sites',
'site_forward',
'patch-sites-drop_site_forward_index.sql' ],
78 [
'dropIndex',
'iwlinks',
'iwl_prefix_from_title',
'patch-iwlinks-drop-iwl_prefix_from_title.sql' ],
81 [
'migratePagelinks' ],
82 [
'dropDefault',
'revision',
'rev_actor' ],
83 [
'dropDefault',
'revision',
'rev_comment_id' ],
84 [
'changeField',
'revision',
'rev_id',
'BIGINT',
'' ],
85 [
'changeField',
'revision',
'rev_parent_id',
'BIGINT',
'' ],
86 [
'changeField',
'recentchanges',
'rc_id',
'BIGINT',
'' ],
87 [
'changeField',
'change_tag',
'ct_rc_id',
'BIGINT',
'' ],
88 [
'runMaintenance', \MigrateBlocks::class ],
89 [
'dropTable',
'ipblocks' ],
90 [
'dropField',
'pagelinks',
'pl_title',
'patch-pagelinks-drop-pl_title.sql' ],
91 [
'addPostDatabaseUpdateMaintenance', FixAutoblockLogTitles::class ],
92 [
'migrateSearchindex' ],
95 [
'addTable',
'file',
'patch-file.sql' ],
96 [
'addField',
'categorylinks',
'cl_target_id',
'patch-categorylinks-target_id.sql' ],
97 [
'addTable',
'collation',
'patch-collation.sql' ],
98 [
'dropTable',
'module_deps' ],
101 [
'addTable',
'existencelinks',
'patch-existencelinks.sql' ],
102 [
'runMaintenance', FixWrongPasswordPrefixes::class ],
103 [
'addIndex',
'categorylinks',
'cl_timestamp_id',
'patch-categorylinks-cl_timestamp_id.sql' ],
104 [
'migrateCategorylinks' ],
105 [
'normalizeCollation' ],
106 [
'modifyPrimaryKey',
'categorylinks', [
'cl_from',
'cl_target_id' ],
'patch-categorylinks-pk.sql' ],
107 [
'addIndex',
'recentchanges',
'rc_source_name_timestamp',
108 'patch-recentchanges-rc_source_name_timestamp.sql' ],
109 [
'addIndex',
'recentchanges',
'rc_name_source_patrolled_timestamp',
110 'patch-recentchanges-rc_name_source_patrolled_timestamp.sql' ],
111 [
'dropField',
'recentchanges',
'rc_new',
'patch-recentchanges-drop-rc_new.sql' ],
112 [
'dropField',
'categorylinks',
'cl_to',
'patch-categorylinks-drop-cl_to-cl_collation.sql' ],
115 [
'addTable',
'watchlist_label',
'patch-watchlist_label.sql' ],
116 [
'dropField',
'recentchanges',
'rc_type',
'patch-recentchanges-drop-rc_type.sql' ],
117 [
'dropField',
'archive',
'ar_sha1',
'patch-archive-drop-ar_sha1.sql' ],
118 [
'dropField',
'revision',
'rev_sha1',
'patch-revision-drop-rev_sha1.sql' ],
119 [
'dropField',
'objectcache',
'modtoken',
'patch-objectcache-drop-modtoken.sql' ],
120 [
'addField',
'imagelinks',
'il_target_id',
'patch-imagelinks-add-il_target_id.sql' ],
127 'filearchive-fa_major_mime-patch-fa_major_mime-chemical.sql',
128 'image-img_major_mime-patch-img_major_mime-chemical.sql',
129 'oldimage-oi_major_mime-patch-oi_major_mime-chemical.sql',
130 'user_groups-ug_group-patch-ug_group-length-increase-255.sql',
131 'user_former_groups-ufg_group-patch-ufg_group-length-increase-255.sql',
132 'user_properties-up_property-patch-up_property.sql',
142SELECT attname, attnum FROM pg_namespace, pg_class, pg_attribute
143 WHERE pg_class.relnamespace = pg_namespace.oid
144 AND attrelid=pg_class.oid AND attnum > 0
145 AND relname=%s AND nspname=%s
147 $res = $this->db->query(
149 $this->db->addQuotes( $table ),
150 $this->db->addQuotes( $this->db->getCoreSchema() )
159 foreach ( $res as $r ) {
177SELECT indkey, indrelid FROM pg_namespace, pg_class, pg_index
179 AND pg_class.relnamespace = pg_namespace.oid
181 AND indexrelid=pg_class.oid
183 $res = $this->db->query(
186 $this->db->addQuotes( $this->db->getCoreSchema() ),
187 $this->db->addQuotes( $idx )
194 $r = $res->fetchRow();
200 $relid = intval( $r[1] );
201 $indkeys = explode(
' ', $indkey );
204 foreach ( $indkeys as $rid ) {
206SELECT attname FROM pg_class, pg_attribute
207 WHERE attrelid=$relid
209 AND attrelid=pg_class.oid
211 $r2 = $this->db->query( sprintf( $query, $rid ), __METHOD__ );
215 $row2 = $r2->fetchRow();
219 $colnames[] = $row2[0];
231SELECT confdeltype FROM pg_constraint, pg_namespace
232 WHERE connamespace=pg_namespace.oid
236 $r = $this->db->query(
239 $this->db->addQuotes( $this->db->getCoreSchema() ),
240 $this->db->addQuotes( $fkey )
244 $row = $r->fetchRow();
245 return $row ? $row[0] :
null;
255SELECT definition FROM pg_rules
256 WHERE schemaname = %s
260 $r = $this->db->query(
263 $this->db->addQuotes( $this->db->getCoreSchema() ),
264 $this->db->addQuotes( $table ),
265 $this->db->addQuotes( $rule )
269 $row = $r->fetchRow();
284 if ( !$this->db->sequenceExists( $ns ) ) {
285 $this->
output(
"Creating sequence $ns\n" );
286 if ( $pkey !==
false ) {
287 $table = $this->db->addIdentifierQuotes( $table );
288 $this->db->query(
"CREATE SEQUENCE $ns OWNED BY $table.$pkey", __METHOD__ );
289 $this->
setDefault( $table, $pkey,
'"nextval"(\'"' . $ns .
'"\'::"regclass")' );
291 $this->db->query(
"CREATE SEQUENCE $ns", __METHOD__ );
301 if ( $this->db->sequenceExists( $ns ) ) {
302 $this->
output(
"Dropping sequence $ns\n" );
303 $this->db->query(
"DROP SEQUENCE $ns CASCADE", __METHOD__ );
312 if ( $this->db->sequenceExists( $new ) ) {
313 $this->
output(
"...sequence $new already exists.\n" );
317 if ( $this->db->sequenceExists( $old ) ) {
318 $this->
output(
"Renaming sequence $old to $new\n" );
319 $this->db->query(
"ALTER SEQUENCE $old RENAME TO $new", __METHOD__ );
329 if ( $this->db->sequenceExists( $seq ) ) {
330 $this->
output(
"Setting sequence $seq owner to $table.$pkey\n" );
331 $table = $this->db->addIdentifierQuotes( $table );
332 $this->db->query(
"ALTER SEQUENCE $seq OWNED BY $table.$pkey", __METHOD__ );
342 if ( $this->db->tableExists( $old, __METHOD__ ) ) {
343 $this->
output(
"Renaming table $old to $new\n" );
344 $old = $this->db->addIdentifierQuotes( $old );
345 $new = $this->db->addIdentifierQuotes( $new );
346 $this->db->query(
"ALTER TABLE $old RENAME TO $new", __METHOD__ );
347 if ( $patch !==
false ) {
363 $table, $old, $new, $skipBothIndexExistWarning =
false, $a =
false, $b =
false
366 if ( !$this->db->tableExists( $table, __METHOD__ ) ) {
367 $this->
output(
"...skipping: '$table' table doesn't exist yet.\n" );
373 if ( $this->db->indexExists( $table, $new, __METHOD__ ) ) {
374 $this->
output(
"...index $new already set on $table table.\n" );
375 if ( !$skipBothIndexExistWarning
376 && $this->db->indexExists( $table, $old, __METHOD__ )
378 $this->
output(
"...WARNING: $old still exists, despite it has been " .
379 "renamed into $new (which also exists).\n" .
380 " $old should be manually removed if not needed anymore.\n" );
387 if ( !$this->db->indexExists( $table, $old, __METHOD__ ) ) {
388 $this->
output(
"...skipping: index $old doesn't exist.\n" );
393 $this->db->query(
"ALTER INDEX $old RENAME TO $new", __METHOD__ );
402 $fi = $this->db->fieldInfo( $table, $field );
403 if ( $fi ===
null ) {
404 $this->
output(
"...$table table does not contain $field field.\n" );
406 $this->
output(
"Dropping column '$table.$field'\n" );
407 $table = $this->db->addIdentifierQuotes( $table );
408 $this->db->query(
"ALTER TABLE $table DROP COLUMN $field", __METHOD__ );
418 $fi = $this->db->fieldInfo( $table, $field );
419 if ( $fi !==
null ) {
420 $this->
output(
"...column '$table.$field' already exists\n" );
422 $this->
output(
"Adding column '$table.$field'\n" );
423 $table = $this->db->addIdentifierQuotes( $table );
424 $this->db->query(
"ALTER TABLE $table ADD $field $type", __METHOD__ );
434 protected function changeField( $table, $field, $newtype, $default ) {
435 if ( !$this->db->tableExists( $table, __METHOD__ ) ) {
436 $this->
output(
"...$table table does not exist, skipping default change.\n" );
439 $fi = $this->db->fieldInfo( $table, $field );
440 if ( $fi ===
null ) {
441 $this->
output(
"...ERROR: expected column $table.$field to exist\n" );
445 if ( $fi->type() === strtolower( $newtype ) ) {
446 $this->
output(
"...column '$table.$field' is already of type '$newtype'\n" );
448 $this->
output(
"Changing column type of '$table.$field' from '{$fi->type()}' to '$newtype'\n" );
449 $table = $this->db->addIdentifierQuotes( $table );
450 $sql =
"ALTER TABLE $table ALTER $field TYPE $newtype";
451 if ( $default !==
'' ) {
453 if ( preg_match(
'/DEFAULT (.+)/', $default, $res ) ) {
454 $sqldef =
"ALTER TABLE $table ALTER $field SET DEFAULT $res[1]";
455 $this->db->query( $sqldef, __METHOD__ );
456 $default = preg_replace(
'/\s*DEFAULT .+/',
'', $default );
458 $sql .=
" USING $default";
460 $this->db->query( $sql, __METHOD__ );
471 # # For a cache table, empty it if the field needs to be changed, because the old contents
472 # # may be corrupted. If the column is already the desired type, refrain from purging.
473 $fi = $this->db->fieldInfo( $table, $field );
474 if ( $fi ===
null ) {
475 $this->
output(
"...ERROR: expected column $table.$field to exist\n" );
479 if ( $fi->type() === $newtype ) {
480 $this->
output(
"...column '$table.$field' is already of type '$newtype'\n" );
482 $this->
output(
"Purging data from cache table '$table'\n" );
483 $table = $this->db->addIdentifierQuotes( $table );
484 $this->db->query(
"DELETE from $table", __METHOD__ );
485 $this->
output(
"Changing column type of '$table.$field' from '{$fi->type()}' to '$newtype'\n" );
486 $sql =
"ALTER TABLE $table ALTER $field TYPE $newtype";
487 if ( strlen( $default ) ) {
489 if ( preg_match(
'/DEFAULT (.+)/', $default, $res ) ) {
490 $sqldef =
"ALTER TABLE $table ALTER $field SET DEFAULT $res[1]";
491 $this->db->query( $sqldef, __METHOD__ );
492 $default = preg_replace(
'/\s*DEFAULT .+/',
'', $default );
494 $sql .=
" USING $default";
496 $this->db->query( $sql, __METHOD__ );
506 if ( !$this->db->tableExists( $table, __METHOD__ ) ) {
507 $this->
output(
"...$table table does not exist, skipping default change.\n" );
510 $info = $this->db->fieldInfo( $table, $field );
511 if ( $info && $info->defaultValue() !== $default ) {
512 $this->
output(
"Changing '$table.$field' default value\n" );
513 $table = $this->db->addIdentifierQuotes( $table );
514 $this->db->query(
"ALTER TABLE $table ALTER $field SET DEFAULT "
515 . $this->db->addQuotes( $default ), __METHOD__ );
526 $info = $this->db->fieldInfo( $table, $field );
527 if ( $info && $info->defaultValue() !==
false ) {
528 $this->
output(
"Removing '$table.$field' default value\n" );
529 $table = $this->db->addIdentifierQuotes( $table );
530 $this->db->query(
"ALTER TABLE $table ALTER $field DROP DEFAULT", __METHOD__ );
541 $fi = $this->db->fieldInfo( $table, $field );
542 if ( $fi ===
null ) {
545 if ( $fi->isNullable() ) {
546 # # It's NULL - does it need to be NOT NULL?
547 if ( $null ===
'NOT NULL' ) {
548 $this->
output(
"Changing '$table.$field' to not allow NULLs\n" );
549 $table = $this->db->addIdentifierQuotes( $table );
551 $this->db->query(
"UPDATE $table SET $field = DEFAULT WHERE $field IS NULL", __METHOD__ );
553 $this->db->query(
"ALTER TABLE $table ALTER $field SET NOT NULL", __METHOD__ );
555 $this->
output(
"...column '$table.$field' is already set as NULL\n" );
558 # # It's NOT NULL - does it need to be NULL?
559 if ( $null ===
'NULL' ) {
560 $this->
output(
"Changing '$table.$field' to allow NULLs\n" );
561 $table = $this->db->addIdentifierQuotes( $table );
562 $this->db->query(
"ALTER TABLE $table ALTER $field DROP NOT NULL", __METHOD__ );
564 $this->
output(
"...column '$table.$field' is already set as NOT NULL\n" );
575 protected function addPgIndex( $table, $index, $type, $unique =
false ) {
576 if ( !$this->db->tableExists( $table, __METHOD__ ) ) {
577 $this->
output(
"...$table table does not exist, skipping index.\n" );
578 } elseif ( $this->db->indexExists( $table, $index, __METHOD__ ) ) {
579 $this->
output(
"...index '$index' on table '$table' already exists\n" );
581 $this->
output(
"Creating index '$index' on table '$table' $type\n" );
582 $table = $this->db->addIdentifierQuotes( $table );
583 $unique = $unique ?
'UNIQUE' :
'';
584 $this->db->query(
"CREATE $unique INDEX $index ON $table $type", __METHOD__ );
594 if ( $this->db->indexExists( $table, $index, __METHOD__ ) ) {
595 $this->
output(
"...index '$index' on table '$table' already exists\n" );
596 } elseif ( preg_match(
'/^\(/', $type ) ) {
597 $this->
output(
"Creating index '$index' on table '$table'\n" );
598 $table = $this->db->addIdentifierQuotes( $table );
599 $this->db->query(
"CREATE INDEX $index ON $table $type", __METHOD__ );
601 $this->
applyPatch( $type,
true,
"Creating index '$index' on table '$table'" );
610 if ( !$this->db->tableExists( $table, __METHOD__ ) ) {
611 $this->
output(
"...$table table does not exist, skipping constraint change.\n" );
614 $fi = $this->db->fieldInfo( $table, $field );
615 if ( $fi ===
null ) {
616 $this->
output(
"WARNING! Column '$table.$field' does not exist but it should! " .
617 "Please report this.\n" );
621 if ( $this->
dropConstraint( $table, $field,
'foreignkey', $fi->conname() ) ) {
622 $this->
output(
"Dropping foreign key constraint on '$table.$field'\n" );
624 $this->
output(
"...foreign key constraint on '$table.$field' already does not exist\n" );
634 $fi = $this->db->fieldInfo( $table, $field );
635 if ( $fi ===
null ) {
636 $this->
output(
"WARNING! Column '$table.$field' does not exist but it should! " .
637 "Please report this.\n" );
641 if ( $fi->is_deferred() && $fi->is_deferrable() ) {
644 $this->
output(
"Altering column '$table.$field' to be DEFERRABLE INITIALLY DEFERRED\n" );
646 $conname = $fi->conname();
647 $conclause =
"CONSTRAINT \"$conname\"";
649 if ( !$this->
dropConstraint( $table, $field,
'foreignkey', $conname ) ) {
650 $this->
output(
"Column '$table.$field' does not have a foreign key " .
651 "constraint, will be added\n" );
656 "ALTER TABLE $table ADD $conclause " .
657 "FOREIGN KEY ($field) REFERENCES $clause DEFERRABLE INITIALLY DEFERRED";
658 $this->db->query( $command, __METHOD__ );
666 if ( $this->db->indexExists( $table, $index, __METHOD__ ) ) {
667 $this->
output(
"Dropping obsolete index '$index'\n" );
668 $this->db->query(
"DROP INDEX \"" . $index .
"\"", __METHOD__ );
677 protected function checkIndex( $index, $should_be, $good_def ) {
678 $pu = $this->db->indexAttributes( $index );
679 if ( $pu && $pu != $should_be ) {
680 $this->
output(
"Dropping obsolete version of index '$index'\n" );
681 $this->db->query(
"DROP INDEX \"" . $index .
"\"", __METHOD__ );
684 $this->
output(
"...no need to drop index '$index'\n" );
688 $this->
output(
"Creating index '$index'\n" );
689 $this->db->query( $good_def, __METHOD__ );
691 $this->
output(
"...index '$index' exists\n" );
702 $result = $this->db->query(
703 "SELECT a.attname as column " .
705 "JOIN pg_attribute a ON a.attrelid = i.indrelid " .
706 "AND a.attnum = ANY(i.indkey) " .
707 "WHERE i.indrelid = '\"$table\"'::regclass " .
708 "AND i.indisprimary",
711 $currentColumns = [];
712 foreach ( $result as $row ) {
713 $currentColumns[] = $row->column;
716 if ( $currentColumns == $shouldBe ) {
717 $this->
output(
"...no need to change primary key of '$table'\n" );
723 $table = $this->db->addIdentifierQuotes( $table );
725 "ALTER TABLE $table" .
726 " ADD PRIMARY KEY (" . implode(
',', $shouldBe ) .
');',
743 if ( $conname ===
null ) {
744 if ( $type ==
'primary' ) {
745 $conname =
"{$table}_pkey";
747 $map = [
'unique' =>
'key',
'check' =>
'check',
'foreignkey' =>
'fkey' ];
748 $conname =
"{$table}_{$field}_{$map[$type]}";
752 if ( $this->db->constraintExists( $table, $conname ) ) {
753 $table = $this->db->addIdentifierQuotes( $table );
755 "ALTER TABLE $table DROP CONSTRAINT $conname;",
772 $updateKey =
'searchindex-pk-titlelength';
777 $primaryIndexExists = $this->db->indexExists(
'searchindex',
'searchindex_pkey', __METHOD__ );
778 if ( $this->
updateRowExists( $updateKey ) || ( $primaryIndexExists ) ) {
779 $this->
outputApplied(
"...searchindex table has already been migrated.\n" );
786 $apply = $this->
applyPatch(
'patch-searchindex-pk-titlelength.sql',
false,
'...migrating searchindex table' );
Fix redirect pages with missing or incomplete row in the redirect table.
An error in a previous version of MediaWiki caused B type passwords to be written with an :A: prefix ...
Class for handling updates to Postgres databases.
setSequenceOwner( $table, $pkey, $seq)
getInitialUpdateKeys()
Get an array of update keys to insert into the updatelog table after a new installation....
addPgExtIndex( $table, $index, $type)
checkIndex( $index, $should_be, $good_def)
dropPgIndex( $table, $index)
addSequence( $table, $pkey, $ns)
dropConstraint( $table, $field, $type, $conname=null)
Drop generic constraint.
addPgIndex( $table, $index, $type, $unique=false)
changeNullableField( $table, $field, $null, $update=false)
changeField( $table, $field, $newtype, $default)
changeFkeyDeferrable( $table, $field, $clause)
changeFieldPurgeTable( $table, $field, $newtype, $default)
renameIndex( $table, $old, $new, $skipBothIndexExistWarning=false, $a=false, $b=false)
changePrimaryKey( $table, $shouldBe, $constraintName=null)
dropSequence( $table, $ns)
setDefault( $table, $field, $default)
renameTable( $old, $new, $patch=false)
migrateSearchindex()
Replaces unique index with primary key,modifies si_title length.
dropPgField( $table, $field)
renameSequence( $old, $new)
addPgField( $table, $field, $type)
dropFkey( $table, $field)
dropDefault( $table, $field)
Drop a default value from a field.
Maintenance script that migrates externallinks data.
Maintenance script that Fills the user_is_temp column of the user table for users created before MW 1...
Postgres database abstraction layer.