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' ],
127 [
'addField',
'site_stats',
'ss_temp_users',
'patch-site_stats-temp_users.sql' ],
134 'filearchive-fa_major_mime-patch-fa_major_mime-chemical.sql',
135 'image-img_major_mime-patch-img_major_mime-chemical.sql',
136 'oldimage-oi_major_mime-patch-oi_major_mime-chemical.sql',
137 'user_groups-ug_group-patch-ug_group-length-increase-255.sql',
138 'user_former_groups-ufg_group-patch-ufg_group-length-increase-255.sql',
139 'user_properties-up_property-patch-up_property.sql',
149SELECT attname, attnum FROM pg_namespace, pg_class, pg_attribute
150 WHERE pg_class.relnamespace = pg_namespace.oid
151 AND attrelid=pg_class.oid AND attnum > 0
152 AND relname=%s AND nspname=%s
154 $res = $this->db->query(
156 $this->db->addQuotes( $table ),
157 $this->db->addQuotes( $this->db->getCoreSchema() )
166 foreach ( $res as $r ) {
184SELECT indkey, indrelid FROM pg_namespace, pg_class, pg_index
186 AND pg_class.relnamespace = pg_namespace.oid
188 AND indexrelid=pg_class.oid
190 $res = $this->db->query(
193 $this->db->addQuotes( $this->db->getCoreSchema() ),
194 $this->db->addQuotes( $idx )
201 $r = $res->fetchRow();
207 $relid = intval( $r[1] );
208 $indkeys = explode(
' ', $indkey );
211 foreach ( $indkeys as $rid ) {
213SELECT attname FROM pg_class, pg_attribute
214 WHERE attrelid=$relid
216 AND attrelid=pg_class.oid
218 $r2 = $this->db->query( sprintf( $query, $rid ), __METHOD__ );
222 $row2 = $r2->fetchRow();
226 $colnames[] = $row2[0];
238SELECT confdeltype FROM pg_constraint, pg_namespace
239 WHERE connamespace=pg_namespace.oid
243 $r = $this->db->query(
246 $this->db->addQuotes( $this->db->getCoreSchema() ),
247 $this->db->addQuotes( $fkey )
251 $row = $r->fetchRow();
252 return $row ? $row[0] :
null;
262SELECT definition FROM pg_rules
263 WHERE schemaname = %s
267 $r = $this->db->query(
270 $this->db->addQuotes( $this->db->getCoreSchema() ),
271 $this->db->addQuotes( $table ),
272 $this->db->addQuotes( $rule )
276 $row = $r->fetchRow();
291 if ( !$this->db->sequenceExists( $ns ) ) {
292 $this->
output(
"Creating sequence $ns\n" );
293 if ( $pkey !==
false ) {
294 $table = $this->db->addIdentifierQuotes( $table );
295 $this->db->query(
"CREATE SEQUENCE $ns OWNED BY $table.$pkey", __METHOD__ );
296 $this->
setDefault( $table, $pkey,
'"nextval"(\'"' . $ns .
'"\'::"regclass")' );
298 $this->db->query(
"CREATE SEQUENCE $ns", __METHOD__ );
308 if ( $this->db->sequenceExists( $ns ) ) {
309 $this->
output(
"Dropping sequence $ns\n" );
310 $this->db->query(
"DROP SEQUENCE $ns CASCADE", __METHOD__ );
319 if ( $this->db->sequenceExists( $new ) ) {
320 $this->
output(
"...sequence $new already exists.\n" );
324 if ( $this->db->sequenceExists( $old ) ) {
325 $this->
output(
"Renaming sequence $old to $new\n" );
326 $this->db->query(
"ALTER SEQUENCE $old RENAME TO $new", __METHOD__ );
336 if ( $this->db->sequenceExists( $seq ) ) {
337 $this->
output(
"Setting sequence $seq owner to $table.$pkey\n" );
338 $table = $this->db->addIdentifierQuotes( $table );
339 $this->db->query(
"ALTER SEQUENCE $seq OWNED BY $table.$pkey", __METHOD__ );
349 $updateMsg =
"Renaming table $old to $new";
354 if ( $this->db->tableExists( $old, __METHOD__ ) ) {
355 $this->
output(
"$updateMsg\n" );
356 $old = $this->db->addIdentifierQuotes( $old );
357 $new = $this->db->addIdentifierQuotes( $new );
358 $this->db->query(
"ALTER TABLE $old RENAME TO $new", __METHOD__ );
359 if ( $patch !==
false ) {
375 $table, $old, $new, $skipBothIndexExistWarning =
false, $a =
false, $b =
false
378 "Renaming index $old to $new in table $table"
384 if ( !$this->db->tableExists( $table, __METHOD__ ) ) {
385 $this->
output(
"...skipping: '$table' table doesn't exist yet.\n" );
391 if ( $this->db->indexExists( $table, $new, __METHOD__ ) ) {
392 $this->
output(
"...index $new already set on $table table.\n" );
393 if ( !$skipBothIndexExistWarning
394 && $this->db->indexExists( $table, $old, __METHOD__ )
396 $this->
output(
"...WARNING: $old still exists, despite it has been " .
397 "renamed into $new (which also exists).\n" .
398 " $old should be manually removed if not needed anymore.\n" );
405 if ( !$this->db->indexExists( $table, $old, __METHOD__ ) ) {
406 $this->
output(
"...skipping: index $old doesn't exist.\n" );
411 $this->db->query(
"ALTER INDEX $old RENAME TO $new", __METHOD__ );
420 $updateMsg =
"Dropping column '$table.$field'";
425 $fi = $this->db->fieldInfo( $table, $field );
426 if ( $fi ===
null ) {
427 $this->
output(
"...$table table does not contain $field field.\n" );
429 $this->
output(
"$updateMsg\n" );
430 $table = $this->db->addIdentifierQuotes( $table );
431 $this->db->query(
"ALTER TABLE $table DROP COLUMN $field", __METHOD__ );
441 $updateMsg =
"Adding column '$table.$field'";
446 $fi = $this->db->fieldInfo( $table, $field );
447 if ( $fi !==
null ) {
448 $this->
output(
"...column '$table.$field' already exists\n" );
450 $this->
output(
"$updateMsg'\n" );
451 $table = $this->db->addIdentifierQuotes( $table );
452 $this->db->query(
"ALTER TABLE $table ADD $field $type", __METHOD__ );
462 protected function changeField( $table, $field, $newtype, $default ) {
467 if ( !$this->db->tableExists( $table, __METHOD__ ) ) {
468 $this->
output(
"...$table table does not exist, skipping default change.\n" );
471 $fi = $this->db->fieldInfo( $table, $field );
472 if ( $fi ===
null ) {
473 $this->
output(
"...ERROR: expected column $table.$field to exist\n" );
477 if ( $fi->type() === strtolower( $newtype ) ) {
478 $this->
output(
"...column '$table.$field' is already of type '$newtype'\n" );
480 $this->
output(
"Changing column type of '$table.$field' from '{$fi->type()}' to '$newtype'\n" );
481 $table = $this->db->addIdentifierQuotes( $table );
482 $sql =
"ALTER TABLE $table ALTER $field TYPE $newtype";
483 if ( $default !==
'' ) {
485 if ( preg_match(
'/DEFAULT (.+)/', $default, $res ) ) {
486 $sqldef =
"ALTER TABLE $table ALTER $field SET DEFAULT $res[1]";
487 $this->db->query( $sqldef, __METHOD__ );
488 $default = preg_replace(
'/\s*DEFAULT .+/',
'', $default );
490 $sql .=
" USING $default";
492 $this->db->query( $sql, __METHOD__ );
504 "Changing column type of '$table.$field' to '$newtype'"
509 # # For a cache table, empty it if the field needs to be changed, because the old contents
510 # # may be corrupted. If the column is already the desired type, refrain from purging.
511 $fi = $this->db->fieldInfo( $table, $field );
512 if ( $fi ===
null ) {
513 $this->
output(
"...ERROR: expected column $table.$field to exist\n" );
517 if ( $fi->type() === $newtype ) {
518 $this->
output(
"...column '$table.$field' is already of type '$newtype'\n" );
520 $this->
output(
"Purging data from cache table '$table'\n" );
521 $table = $this->db->addIdentifierQuotes( $table );
522 $this->db->query(
"DELETE from $table", __METHOD__ );
523 $this->
output(
"Changing column type of '$table.$field' from '{$fi->type()}' to '$newtype'\n" );
524 $sql =
"ALTER TABLE $table ALTER $field TYPE $newtype";
525 if ( strlen( $default ) ) {
527 if ( preg_match(
'/DEFAULT (.+)/', $default, $res ) ) {
528 $sqldef =
"ALTER TABLE $table ALTER $field SET DEFAULT $res[1]";
529 $this->db->query( $sqldef, __METHOD__ );
530 $default = preg_replace(
'/\s*DEFAULT .+/',
'', $default );
532 $sql .=
" USING $default";
534 $this->db->query( $sql, __METHOD__ );
544 $updateMsg =
"Changing '$table.$field' default value";
549 if ( !$this->db->tableExists( $table, __METHOD__ ) ) {
550 $this->
output(
"...$table table does not exist, skipping default change.\n" );
553 $info = $this->db->fieldInfo( $table, $field );
554 if ( $info && $info->defaultValue() !== $default ) {
555 $this->
output(
"$updateMsg\n" );
556 $table = $this->db->addIdentifierQuotes( $table );
557 $this->db->query(
"ALTER TABLE $table ALTER $field SET DEFAULT "
558 . $this->db->addQuotes( $default ), __METHOD__ );
569 $updateMsg =
"Dropping '$table.$field' default value";
574 $info = $this->db->fieldInfo( $table, $field );
575 if ( $info && $info->defaultValue() !==
false ) {
576 $this->
output(
"$updateMsg\n" );
577 $table = $this->db->addIdentifierQuotes( $table );
578 $this->db->query(
"ALTER TABLE $table ALTER $field DROP DEFAULT", __METHOD__ );
589 $updateMsg =
"Changing field '$table.$field' to ";
590 if ( $null ===
'NOT NULL' ) {
591 $updateMsg .=
'not allow NULLs';
593 $updateMsg .=
'allow NULLs';
599 $fi = $this->db->fieldInfo( $table, $field );
600 if ( $fi ===
null ) {
603 if ( $fi->isNullable() ) {
604 # # It's NULL - does it need to be NOT NULL?
605 if ( $null ===
'NOT NULL' ) {
606 $this->
output(
"$updateMsg\n" );
607 $table = $this->db->addIdentifierQuotes( $table );
609 $this->db->query(
"UPDATE $table SET $field = DEFAULT WHERE $field IS NULL", __METHOD__ );
611 $this->db->query(
"ALTER TABLE $table ALTER $field SET NOT NULL", __METHOD__ );
613 $this->
output(
"...column '$table.$field' is already set as NULL\n" );
616 # # It's NOT NULL - does it need to be NULL?
617 if ( $null ===
'NULL' ) {
618 $this->
output(
"$updateMsg\n" );
619 $table = $this->db->addIdentifierQuotes( $table );
620 $this->db->query(
"ALTER TABLE $table ALTER $field DROP NOT NULL", __METHOD__ );
622 $this->
output(
"...column '$table.$field' is already set as NOT NULL\n" );
633 protected function addPgIndex( $table, $index, $type, $unique =
false ) {
634 $updateMsg =
"Creating index '$index' on table '$table' $type";
639 if ( !$this->db->tableExists( $table, __METHOD__ ) ) {
640 $this->
output(
"...$table table does not exist, skipping index.\n" );
641 } elseif ( $this->db->indexExists( $table, $index, __METHOD__ ) ) {
642 $this->
output(
"...index '$index' on table '$table' already exists\n" );
644 $this->
output(
"$updateMsg\n" );
645 $table = $this->db->addIdentifierQuotes( $table );
646 $unique = $unique ?
'UNIQUE' :
'';
647 $this->db->query(
"CREATE $unique INDEX $index ON $table $type", __METHOD__ );
657 $updateMsg =
"Creating index '$index' on table '$table'";
662 if ( $this->db->indexExists( $table, $index, __METHOD__ ) ) {
663 $this->
output(
"...index '$index' on table '$table' already exists\n" );
664 } elseif ( preg_match(
'/^\(/', $type ) ) {
665 $this->
output(
"Creating index '$index' on table '$table'\n" );
666 $table = $this->db->addIdentifierQuotes( $table );
667 $this->db->query(
"CREATE INDEX $index ON $table $type", __METHOD__ );
678 $updateMsg =
"Dropping foreign key constraint on '$table.$field'";
683 if ( !$this->db->tableExists( $table, __METHOD__ ) ) {
684 $this->
output(
"...$table table does not exist, skipping constraint change.\n" );
687 $fi = $this->db->fieldInfo( $table, $field );
688 if ( $fi ===
null ) {
689 $this->
output(
"WARNING! Column '$table.$field' does not exist but it should! " .
690 "Please report this.\n" );
694 if ( $this->
dropConstraint( $table, $field,
'foreignkey', $fi->conname() ) ) {
695 $this->
output(
"$updateMsg\n" );
697 $this->
output(
"...foreign key constraint on '$table.$field' already does not exist\n" );
707 $updateMsg =
"Altering column '$table.$field' to be DEFERRABLE INITIALLY DEFERRED";
712 $fi = $this->db->fieldInfo( $table, $field );
713 if ( $fi ===
null ) {
714 $this->
output(
"WARNING! Column '$table.$field' does not exist but it should! " .
715 "Please report this.\n" );
719 if ( $fi->is_deferred() && $fi->is_deferrable() ) {
722 $this->
output(
"$updateMsg\n" );
724 $conname = $fi->conname();
725 $conclause =
"CONSTRAINT \"$conname\"";
727 if ( !$this->
dropConstraint( $table, $field,
'foreignkey', $conname ) ) {
728 $this->
output(
"Column '$table.$field' does not have a foreign key " .
729 "constraint, will be added\n" );
734 "ALTER TABLE $table ADD $conclause " .
735 "FOREIGN KEY ($field) REFERENCES $clause DEFERRABLE INITIALLY DEFERRED";
736 $this->db->query( $command, __METHOD__ );
744 $updateMsg =
"Dropping obsolete index '$index'";
749 if ( $this->db->indexExists( $table, $index, __METHOD__ ) ) {
750 $this->
output(
"$updateMsg\n" );
751 $this->db->query(
"DROP INDEX \"" . $index .
"\"", __METHOD__ );
760 protected function checkIndex( $index, $should_be, $good_def ) {
762 "Checking if index '$index' is up to date"
767 $pu = $this->db->indexAttributes( $index );
768 if ( $pu && $pu != $should_be ) {
769 $this->
output(
"Dropping obsolete version of index '$index'\n" );
770 $this->db->query(
"DROP INDEX \"" . $index .
"\"", __METHOD__ );
773 $this->
output(
"...no need to drop index '$index'\n" );
777 $this->
output(
"Creating index '$index'\n" );
778 $this->db->query( $good_def, __METHOD__ );
780 $this->
output(
"...index '$index' exists\n" );
795 $result = $this->db->query(
796 "SELECT a.attname as column " .
798 "JOIN pg_attribute a ON a.attrelid = i.indrelid " .
799 "AND a.attnum = ANY(i.indkey) " .
800 "WHERE i.indrelid = '\"$table\"'::regclass " .
801 "AND i.indisprimary",
804 $currentColumns = [];
805 foreach ( $result as $row ) {
806 $currentColumns[] = $row->column;
809 if ( $currentColumns == $shouldBe ) {
810 $this->
output(
"...no need to change primary key of '$table'\n" );
816 $table = $this->db->addIdentifierQuotes( $table );
818 "ALTER TABLE $table" .
819 " ADD PRIMARY KEY (" . implode(
',', $shouldBe ) .
');',
840 if ( $conname ===
null ) {
841 if ( $type ==
'primary' ) {
842 $conname =
"{$table}_pkey";
844 $map = [
'unique' =>
'key',
'check' =>
'check',
'foreignkey' =>
'fkey' ];
845 $conname =
"{$table}_{$field}_{$map[$type]}";
849 if ( $this->db->constraintExists( $table, $conname ) ) {
850 $table = $this->db->addIdentifierQuotes( $table );
852 "ALTER TABLE $table DROP CONSTRAINT $conname;",
869 $updateKey =
'searchindex-pk-titlelength';
874 $primaryIndexExists = $this->db->indexExists(
'searchindex',
'searchindex_pkey', __METHOD__ );
875 if ( $this->
updateRowExists( $updateKey ) || ( $primaryIndexExists ) ) {
876 $this->
outputApplied(
"...searchindex table has already been migrated.\n" );
883 $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.