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' ],
121 [
'migrateImagelinks' ],
122 [
'modifyPrimaryKey',
'imagelinks', [
'il_from',
'il_target_id' ],
'patch-imagelinks-pk.sql' ],
123 [
'addMissingTalkPageWatchlistLabels' ],
124 [
'dropField',
'imagelinks',
'il_to',
'patch-imagelinks-drop-il_to.sql' ],
131 'filearchive-fa_major_mime-patch-fa_major_mime-chemical.sql',
132 'image-img_major_mime-patch-img_major_mime-chemical.sql',
133 'oldimage-oi_major_mime-patch-oi_major_mime-chemical.sql',
134 'user_groups-ug_group-patch-ug_group-length-increase-255.sql',
135 'user_former_groups-ufg_group-patch-ufg_group-length-increase-255.sql',
136 'user_properties-up_property-patch-up_property.sql',
146SELECT attname, attnum FROM pg_namespace, pg_class, pg_attribute
147 WHERE pg_class.relnamespace = pg_namespace.oid
148 AND attrelid=pg_class.oid AND attnum > 0
149 AND relname=%s AND nspname=%s
151 $res = $this->db->query(
153 $this->db->addQuotes( $table ),
154 $this->db->addQuotes( $this->db->getCoreSchema() )
163 foreach ( $res as $r ) {
181SELECT indkey, indrelid FROM pg_namespace, pg_class, pg_index
183 AND pg_class.relnamespace = pg_namespace.oid
185 AND indexrelid=pg_class.oid
187 $res = $this->db->query(
190 $this->db->addQuotes( $this->db->getCoreSchema() ),
191 $this->db->addQuotes( $idx )
198 $r = $res->fetchRow();
204 $relid = intval( $r[1] );
205 $indkeys = explode(
' ', $indkey );
208 foreach ( $indkeys as $rid ) {
210SELECT attname FROM pg_class, pg_attribute
211 WHERE attrelid=$relid
213 AND attrelid=pg_class.oid
215 $r2 = $this->db->query( sprintf( $query, $rid ), __METHOD__ );
219 $row2 = $r2->fetchRow();
223 $colnames[] = $row2[0];
235SELECT confdeltype FROM pg_constraint, pg_namespace
236 WHERE connamespace=pg_namespace.oid
240 $r = $this->db->query(
243 $this->db->addQuotes( $this->db->getCoreSchema() ),
244 $this->db->addQuotes( $fkey )
248 $row = $r->fetchRow();
249 return $row ? $row[0] :
null;
259SELECT definition FROM pg_rules
260 WHERE schemaname = %s
264 $r = $this->db->query(
267 $this->db->addQuotes( $this->db->getCoreSchema() ),
268 $this->db->addQuotes( $table ),
269 $this->db->addQuotes( $rule )
273 $row = $r->fetchRow();
288 if ( !$this->db->sequenceExists( $ns ) ) {
289 $this->
output(
"Creating sequence $ns\n" );
290 if ( $pkey !==
false ) {
291 $table = $this->db->addIdentifierQuotes( $table );
292 $this->db->query(
"CREATE SEQUENCE $ns OWNED BY $table.$pkey", __METHOD__ );
293 $this->
setDefault( $table, $pkey,
'"nextval"(\'"' . $ns .
'"\'::"regclass")' );
295 $this->db->query(
"CREATE SEQUENCE $ns", __METHOD__ );
305 if ( $this->db->sequenceExists( $ns ) ) {
306 $this->
output(
"Dropping sequence $ns\n" );
307 $this->db->query(
"DROP SEQUENCE $ns CASCADE", __METHOD__ );
316 if ( $this->db->sequenceExists( $new ) ) {
317 $this->
output(
"...sequence $new already exists.\n" );
321 if ( $this->db->sequenceExists( $old ) ) {
322 $this->
output(
"Renaming sequence $old to $new\n" );
323 $this->db->query(
"ALTER SEQUENCE $old RENAME TO $new", __METHOD__ );
333 if ( $this->db->sequenceExists( $seq ) ) {
334 $this->
output(
"Setting sequence $seq owner to $table.$pkey\n" );
335 $table = $this->db->addIdentifierQuotes( $table );
336 $this->db->query(
"ALTER SEQUENCE $seq OWNED BY $table.$pkey", __METHOD__ );
346 $updateMsg =
"Renaming table $old to $new";
351 if ( $this->db->tableExists( $old, __METHOD__ ) ) {
352 $this->
output(
"$updateMsg\n" );
353 $old = $this->db->addIdentifierQuotes( $old );
354 $new = $this->db->addIdentifierQuotes( $new );
355 $this->db->query(
"ALTER TABLE $old RENAME TO $new", __METHOD__ );
356 if ( $patch !==
false ) {
372 $table, $old, $new, $skipBothIndexExistWarning =
false, $a =
false, $b =
false
375 "Renaming index $old to $new in table $table"
381 if ( !$this->db->tableExists( $table, __METHOD__ ) ) {
382 $this->
output(
"...skipping: '$table' table doesn't exist yet.\n" );
388 if ( $this->db->indexExists( $table, $new, __METHOD__ ) ) {
389 $this->
output(
"...index $new already set on $table table.\n" );
390 if ( !$skipBothIndexExistWarning
391 && $this->db->indexExists( $table, $old, __METHOD__ )
393 $this->
output(
"...WARNING: $old still exists, despite it has been " .
394 "renamed into $new (which also exists).\n" .
395 " $old should be manually removed if not needed anymore.\n" );
402 if ( !$this->db->indexExists( $table, $old, __METHOD__ ) ) {
403 $this->
output(
"...skipping: index $old doesn't exist.\n" );
408 $this->db->query(
"ALTER INDEX $old RENAME TO $new", __METHOD__ );
417 $updateMsg =
"Dropping column '$table.$field'";
422 $fi = $this->db->fieldInfo( $table, $field );
423 if ( $fi ===
null ) {
424 $this->
output(
"...$table table does not contain $field field.\n" );
426 $this->
output(
"$updateMsg\n" );
427 $table = $this->db->addIdentifierQuotes( $table );
428 $this->db->query(
"ALTER TABLE $table DROP COLUMN $field", __METHOD__ );
438 $updateMsg =
"Adding column '$table.$field'";
443 $fi = $this->db->fieldInfo( $table, $field );
444 if ( $fi !==
null ) {
445 $this->
output(
"...column '$table.$field' already exists\n" );
447 $this->
output(
"$updateMsg'\n" );
448 $table = $this->db->addIdentifierQuotes( $table );
449 $this->db->query(
"ALTER TABLE $table ADD $field $type", __METHOD__ );
459 protected function changeField( $table, $field, $newtype, $default ) {
464 if ( !$this->db->tableExists( $table, __METHOD__ ) ) {
465 $this->
output(
"...$table table does not exist, skipping default change.\n" );
468 $fi = $this->db->fieldInfo( $table, $field );
469 if ( $fi ===
null ) {
470 $this->
output(
"...ERROR: expected column $table.$field to exist\n" );
474 if ( $fi->type() === strtolower( $newtype ) ) {
475 $this->
output(
"...column '$table.$field' is already of type '$newtype'\n" );
477 $this->
output(
"Changing column type of '$table.$field' from '{$fi->type()}' to '$newtype'\n" );
478 $table = $this->db->addIdentifierQuotes( $table );
479 $sql =
"ALTER TABLE $table ALTER $field TYPE $newtype";
480 if ( $default !==
'' ) {
482 if ( preg_match(
'/DEFAULT (.+)/', $default, $res ) ) {
483 $sqldef =
"ALTER TABLE $table ALTER $field SET DEFAULT $res[1]";
484 $this->db->query( $sqldef, __METHOD__ );
485 $default = preg_replace(
'/\s*DEFAULT .+/',
'', $default );
487 $sql .=
" USING $default";
489 $this->db->query( $sql, __METHOD__ );
501 "Changing column type of '$table.$field' to '$newtype'"
506 # # For a cache table, empty it if the field needs to be changed, because the old contents
507 # # may be corrupted. If the column is already the desired type, refrain from purging.
508 $fi = $this->db->fieldInfo( $table, $field );
509 if ( $fi ===
null ) {
510 $this->
output(
"...ERROR: expected column $table.$field to exist\n" );
514 if ( $fi->type() === $newtype ) {
515 $this->
output(
"...column '$table.$field' is already of type '$newtype'\n" );
517 $this->
output(
"Purging data from cache table '$table'\n" );
518 $table = $this->db->addIdentifierQuotes( $table );
519 $this->db->query(
"DELETE from $table", __METHOD__ );
520 $this->
output(
"Changing column type of '$table.$field' from '{$fi->type()}' to '$newtype'\n" );
521 $sql =
"ALTER TABLE $table ALTER $field TYPE $newtype";
522 if ( strlen( $default ) ) {
524 if ( preg_match(
'/DEFAULT (.+)/', $default, $res ) ) {
525 $sqldef =
"ALTER TABLE $table ALTER $field SET DEFAULT $res[1]";
526 $this->db->query( $sqldef, __METHOD__ );
527 $default = preg_replace(
'/\s*DEFAULT .+/',
'', $default );
529 $sql .=
" USING $default";
531 $this->db->query( $sql, __METHOD__ );
541 $updateMsg =
"Changing '$table.$field' default value";
546 if ( !$this->db->tableExists( $table, __METHOD__ ) ) {
547 $this->
output(
"...$table table does not exist, skipping default change.\n" );
550 $info = $this->db->fieldInfo( $table, $field );
551 if ( $info && $info->defaultValue() !== $default ) {
552 $this->
output(
"$updateMsg\n" );
553 $table = $this->db->addIdentifierQuotes( $table );
554 $this->db->query(
"ALTER TABLE $table ALTER $field SET DEFAULT "
555 . $this->db->addQuotes( $default ), __METHOD__ );
566 $updateMsg =
"Dropping '$table.$field' default value";
571 $info = $this->db->fieldInfo( $table, $field );
572 if ( $info && $info->defaultValue() !==
false ) {
573 $this->
output(
"$updateMsg\n" );
574 $table = $this->db->addIdentifierQuotes( $table );
575 $this->db->query(
"ALTER TABLE $table ALTER $field DROP DEFAULT", __METHOD__ );
586 $updateMsg =
"Changing field '$table.$field' to ";
587 if ( $null ===
'NOT NULL' ) {
588 $updateMsg .=
'not allow NULLs';
590 $updateMsg .=
'allow NULLs';
596 $fi = $this->db->fieldInfo( $table, $field );
597 if ( $fi ===
null ) {
600 if ( $fi->isNullable() ) {
601 # # It's NULL - does it need to be NOT NULL?
602 if ( $null ===
'NOT NULL' ) {
603 $this->
output(
"$updateMsg\n" );
604 $table = $this->db->addIdentifierQuotes( $table );
606 $this->db->query(
"UPDATE $table SET $field = DEFAULT WHERE $field IS NULL", __METHOD__ );
608 $this->db->query(
"ALTER TABLE $table ALTER $field SET NOT NULL", __METHOD__ );
610 $this->
output(
"...column '$table.$field' is already set as NULL\n" );
613 # # It's NOT NULL - does it need to be NULL?
614 if ( $null ===
'NULL' ) {
615 $this->
output(
"$updateMsg\n" );
616 $table = $this->db->addIdentifierQuotes( $table );
617 $this->db->query(
"ALTER TABLE $table ALTER $field DROP NOT NULL", __METHOD__ );
619 $this->
output(
"...column '$table.$field' is already set as NOT NULL\n" );
630 protected function addPgIndex( $table, $index, $type, $unique =
false ) {
631 $updateMsg =
"Creating index '$index' on table '$table' $type";
636 if ( !$this->db->tableExists( $table, __METHOD__ ) ) {
637 $this->
output(
"...$table table does not exist, skipping index.\n" );
638 } elseif ( $this->db->indexExists( $table, $index, __METHOD__ ) ) {
639 $this->
output(
"...index '$index' on table '$table' already exists\n" );
641 $this->
output(
"$updateMsg\n" );
642 $table = $this->db->addIdentifierQuotes( $table );
643 $unique = $unique ?
'UNIQUE' :
'';
644 $this->db->query(
"CREATE $unique INDEX $index ON $table $type", __METHOD__ );
654 $updateMsg =
"Creating index '$index' on table '$table'";
659 if ( $this->db->indexExists( $table, $index, __METHOD__ ) ) {
660 $this->
output(
"...index '$index' on table '$table' already exists\n" );
661 } elseif ( preg_match(
'/^\(/', $type ) ) {
662 $this->
output(
"Creating index '$index' on table '$table'\n" );
663 $table = $this->db->addIdentifierQuotes( $table );
664 $this->db->query(
"CREATE INDEX $index ON $table $type", __METHOD__ );
675 $updateMsg =
"Dropping foreign key constraint on '$table.$field'";
680 if ( !$this->db->tableExists( $table, __METHOD__ ) ) {
681 $this->
output(
"...$table table does not exist, skipping constraint change.\n" );
684 $fi = $this->db->fieldInfo( $table, $field );
685 if ( $fi ===
null ) {
686 $this->
output(
"WARNING! Column '$table.$field' does not exist but it should! " .
687 "Please report this.\n" );
691 if ( $this->
dropConstraint( $table, $field,
'foreignkey', $fi->conname() ) ) {
692 $this->
output(
"$updateMsg\n" );
694 $this->
output(
"...foreign key constraint on '$table.$field' already does not exist\n" );
704 $updateMsg =
"Altering column '$table.$field' to be DEFERRABLE INITIALLY DEFERRED";
709 $fi = $this->db->fieldInfo( $table, $field );
710 if ( $fi ===
null ) {
711 $this->
output(
"WARNING! Column '$table.$field' does not exist but it should! " .
712 "Please report this.\n" );
716 if ( $fi->is_deferred() && $fi->is_deferrable() ) {
719 $this->
output(
"$updateMsg\n" );
721 $conname = $fi->conname();
722 $conclause =
"CONSTRAINT \"$conname\"";
724 if ( !$this->
dropConstraint( $table, $field,
'foreignkey', $conname ) ) {
725 $this->
output(
"Column '$table.$field' does not have a foreign key " .
726 "constraint, will be added\n" );
731 "ALTER TABLE $table ADD $conclause " .
732 "FOREIGN KEY ($field) REFERENCES $clause DEFERRABLE INITIALLY DEFERRED";
733 $this->db->query( $command, __METHOD__ );
741 $updateMsg =
"Dropping obsolete index '$index'";
746 if ( $this->db->indexExists( $table, $index, __METHOD__ ) ) {
747 $this->
output(
"$updateMsg\n" );
748 $this->db->query(
"DROP INDEX \"" . $index .
"\"", __METHOD__ );
757 protected function checkIndex( $index, $should_be, $good_def ) {
759 "Checking if index '$index' is up to date"
764 $pu = $this->db->indexAttributes( $index );
765 if ( $pu && $pu != $should_be ) {
766 $this->
output(
"Dropping obsolete version of index '$index'\n" );
767 $this->db->query(
"DROP INDEX \"" . $index .
"\"", __METHOD__ );
770 $this->
output(
"...no need to drop index '$index'\n" );
774 $this->
output(
"Creating index '$index'\n" );
775 $this->db->query( $good_def, __METHOD__ );
777 $this->
output(
"...index '$index' exists\n" );
792 $result = $this->db->query(
793 "SELECT a.attname as column " .
795 "JOIN pg_attribute a ON a.attrelid = i.indrelid " .
796 "AND a.attnum = ANY(i.indkey) " .
797 "WHERE i.indrelid = '\"$table\"'::regclass " .
798 "AND i.indisprimary",
801 $currentColumns = [];
802 foreach ( $result as $row ) {
803 $currentColumns[] = $row->column;
806 if ( $currentColumns == $shouldBe ) {
807 $this->
output(
"...no need to change primary key of '$table'\n" );
813 $table = $this->db->addIdentifierQuotes( $table );
815 "ALTER TABLE $table" .
816 " ADD PRIMARY KEY (" . implode(
',', $shouldBe ) .
');',
837 if ( $conname ===
null ) {
838 if ( $type ==
'primary' ) {
839 $conname =
"{$table}_pkey";
841 $map = [
'unique' =>
'key',
'check' =>
'check',
'foreignkey' =>
'fkey' ];
842 $conname =
"{$table}_{$field}_{$map[$type]}";
846 if ( $this->db->constraintExists( $table, $conname ) ) {
847 $table = $this->db->addIdentifierQuotes( $table );
849 "ALTER TABLE $table DROP CONSTRAINT $conname;",
866 $updateKey =
'searchindex-pk-titlelength';
871 $primaryIndexExists = $this->db->indexExists(
'searchindex',
'searchindex_pkey', __METHOD__ );
872 if ( $this->
updateRowExists( $updateKey ) || ( $primaryIndexExists ) ) {
873 $this->
outputApplied(
"...searchindex table has already been migrated.\n" );
880 $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.