Page MenuHomeWickedGov Phorge

PostgresUpdater.php
No OneTemporary

Size
24 KB
Referenced Files
None
Subscribers
None

PostgresUpdater.php

<?php
/**
* PostgreSQL-specific updater.
*
* @license GPL-2.0-or-later
* @file
* @ingroup Installer
*/
namespace MediaWiki\Installer;
use FixInconsistentRedirects;
use FixWrongPasswordPrefixes;
use MediaWiki\Maintenance\FixAutoblockLogTitles;
use MigrateExternallinks;
use MigrateRevisionActorTemp;
use MigrateRevisionCommentTemp;
use PopulateUserIsTemp;
use UpdateRestrictions;
use Wikimedia\Rdbms\DatabasePostgres;
/**
* Class for handling updates to Postgres databases.
*
* @ingroup Installer
* @since 1.17
*/
class PostgresUpdater extends DatabaseUpdater {
/**
* @var DatabasePostgres
*/
protected $db;
/**
* @return array
*/
protected function getCoreUpdateList() {
return [
// 1.39
[ 'addTable', 'user_autocreate_serial', 'patch-user_autocreate_serial.sql' ],
[ 'runMaintenance', MigrateRevisionActorTemp::class ],
[ 'dropTable', 'revision_actor_temp' ],
[ 'runMaintenance', UpdateRestrictions::class ],
[ 'dropPgField', 'page', 'page_restrictions' ],
[ 'migrateTemplatelinks' ],
[ 'changeNullableField', 'templatelinks', 'tl_target_id', 'NOT NULL', true ],
[ 'changePrimaryKey', 'templatelinks', [ 'tl_from', 'tl_target_id' ] ],
[ 'dropField', 'templatelinks', 'tl_title', 'patch-templatelinks-drop-tl_title.sql' ],
// 1.40
[ 'addField', 'externallinks', 'el_to_path', 'patch-externallinks-el_to_path.sql' ],
// 1.41
[ 'addField', 'user', 'user_is_temp', 'patch-user-user_is_temp.sql' ],
[ 'runMaintenance', MigrateRevisionCommentTemp::class ],
[ 'dropTable', 'revision_comment_temp' ],
[ 'runMaintenance', MigrateExternallinks::class ],
[ 'modifyField', 'externallinks', 'el_to', 'patch-externallinks-el_to_default.sql' ],
[ 'addField', 'pagelinks', 'pl_target_id', 'patch-pagelinks-target_id.sql' ],
[ 'dropField', 'externallinks', 'el_to', 'patch-externallinks-drop-el_to.sql' ],
[ 'runMaintenance', FixInconsistentRedirects::class ],
[ 'modifyField', 'image', 'img_size', 'patch-image-img_size_to_bigint.sql' ],
[ 'modifyField', 'filearchive', 'fa_size', 'patch-filearchive-fa_size_to_bigint.sql' ],
[ 'modifyField', 'oldimage', 'oi_size', 'patch-oldimage-oi_size_to_bigint.sql' ],
[ 'modifyField', 'uploadstash', 'us_size', 'patch-uploadstash-us_size_to_bigint.sql' ],
// 1.42
[ 'addField', 'user_autocreate_serial', 'uas_year', 'patch-user_autocreate_serial-uas_year.sql' ],
[ 'addTable', 'block_target', 'patch-block_target.sql' ],
[ 'dropIndex', 'categorylinks', 'cl_collation_ext', 'patch-drop-cl_collation_ext.sql' ],
[ 'runMaintenance', PopulateUserIsTemp::class ],
// Attempt a rename from `site_groups` to `site_group` prior to dropping the `sites` indexes,
// due to a typo in the original index-renaming database update from MW 1.36. (T374042)
[ 'renameIndex', 'sites', 'sites_group', 'site_group' ],
[ 'dropIndex', 'sites', 'site_type', 'patch-sites-drop_indexes.sql' ],
// Re-attempt to drop most of the dropped `sites`-table indexes:
// If a Postgres wiki previously ran `update.php` on MW 1.42 or above, the script would have errored after
// reaching the "DROP INDEX site_group;" line of `patch-sites-drop_indexes.sql` (due to a previous
// index-renaming typo from MW 1.36).
// However, as the `site_type` index itself _would_ have been successfully dropped, the `.sql` file listed
// in the `dropIndex` line above will not be re-applied on any future runs of `update.php`.
// Therefore, to ensure that the remaining indexes are definitely dropped on these wikis, we need to
// separately attempt to drop them again. (T374042; T374042#11017896)
[ 'dropIndex', 'sites', 'site_group', 'patch-sites-drop_site_group_index.sql' ],
[ 'dropIndex', 'sites', 'site_source', 'patch-sites-drop_site_source_index.sql' ],
[ 'dropIndex', 'sites', 'site_language', 'patch-sites-drop_site_language_index.sql' ],
[ 'dropIndex', 'sites', 'site_protocol', 'patch-sites-drop_site_protocol_index.sql' ],
[ 'dropIndex', 'sites', 'site_domain', 'patch-sites-drop_site_domain_index.sql' ],
[ 'dropIndex', 'sites', 'site_forward', 'patch-sites-drop_site_forward_index.sql' ],
[ 'dropIndex', 'iwlinks', 'iwl_prefix_from_title', 'patch-iwlinks-drop-iwl_prefix_from_title.sql' ],
// 1.43
[ 'migratePagelinks' ],
[ 'dropDefault', 'revision', 'rev_actor' ],
[ 'dropDefault', 'revision', 'rev_comment_id' ],
[ 'changeField', 'revision', 'rev_id', 'BIGINT', '' ],
[ 'changeField', 'revision', 'rev_parent_id', 'BIGINT', '' ],
[ 'changeField', 'recentchanges', 'rc_id', 'BIGINT', '' ],
[ 'changeField', 'change_tag', 'ct_rc_id', 'BIGINT', '' ],
[ 'runMaintenance', \MigrateBlocks::class ],
[ 'dropTable', 'ipblocks' ],
[ 'dropField', 'pagelinks', 'pl_title', 'patch-pagelinks-drop-pl_title.sql' ],
[ 'addPostDatabaseUpdateMaintenance', FixAutoblockLogTitles::class ],
[ 'migrateSearchindex' ],
// 1.44
[ 'addTable', 'file', 'patch-file.sql' ],
[ 'addField', 'categorylinks', 'cl_target_id', 'patch-categorylinks-target_id.sql' ],
[ 'addTable', 'collation', 'patch-collation.sql' ],
[ 'dropTable', 'module_deps' ],
// 1.45
[ 'addTable', 'existencelinks', 'patch-existencelinks.sql' ],
[ 'runMaintenance', FixWrongPasswordPrefixes::class ],
[ 'addIndex', 'categorylinks', 'cl_timestamp_id', 'patch-categorylinks-cl_timestamp_id.sql' ],
[ 'migrateCategorylinks' ],
[ 'normalizeCollation' ],
[ 'modifyPrimaryKey', 'categorylinks', [ 'cl_from', 'cl_target_id' ], 'patch-categorylinks-pk.sql' ],
[ 'addIndex', 'recentchanges', 'rc_source_name_timestamp',
'patch-recentchanges-rc_source_name_timestamp.sql' ],
[ 'addIndex', 'recentchanges', 'rc_name_source_patrolled_timestamp',
'patch-recentchanges-rc_name_source_patrolled_timestamp.sql' ],
[ 'dropField', 'recentchanges', 'rc_new', 'patch-recentchanges-drop-rc_new.sql' ],
[ 'dropField', 'categorylinks', 'cl_to', 'patch-categorylinks-drop-cl_to-cl_collation.sql' ],
];
}
/** @inheritDoc */
protected function getInitialUpdateKeys() {
return [
'filearchive-fa_major_mime-patch-fa_major_mime-chemical.sql',
'image-img_major_mime-patch-img_major_mime-chemical.sql',
'oldimage-oi_major_mime-patch-oi_major_mime-chemical.sql',
'user_groups-ug_group-patch-ug_group-length-increase-255.sql',
'user_former_groups-ufg_group-patch-ufg_group-length-increase-255.sql',
'user_properties-up_property-patch-up_property.sql',
];
}
/**
* @param string $table
* @return array[]|null
*/
protected function describeTable( $table ) {
$q = <<<END
SELECT attname, attnum FROM pg_namespace, pg_class, pg_attribute
WHERE pg_class.relnamespace = pg_namespace.oid
AND attrelid=pg_class.oid AND attnum > 0
AND relname=%s AND nspname=%s
END;
$res = $this->db->query(
sprintf( $q,
$this->db->addQuotes( $table ),
$this->db->addQuotes( $this->db->getCoreSchema() )
),
__METHOD__
);
if ( !$res ) {
return null;
}
$cols = [];
foreach ( $res as $r ) {
$cols[] = [
"name" => $r[0],
"ord" => $r[1],
];
}
return $cols;
}
/**
* @param string $idx
* @return array|null
*/
protected function describeIndex( $idx ) {
// first fetch the key (which is a list of columns ords) and
// the table the index applies to (an oid)
$q = <<<END
SELECT indkey, indrelid FROM pg_namespace, pg_class, pg_index
WHERE nspname=%s
AND pg_class.relnamespace = pg_namespace.oid
AND relname=%s
AND indexrelid=pg_class.oid
END;
$res = $this->db->query(
sprintf(
$q,
$this->db->addQuotes( $this->db->getCoreSchema() ),
$this->db->addQuotes( $idx )
),
__METHOD__
);
if ( !$res ) {
return null;
}
$r = $res->fetchRow();
if ( !$r ) {
return null;
}
$indkey = $r[0];
$relid = intval( $r[1] );
$indkeys = explode( ' ', $indkey );
$colnames = [];
foreach ( $indkeys as $rid ) {
$query = <<<END
SELECT attname FROM pg_class, pg_attribute
WHERE attrelid=$relid
AND attnum=%d
AND attrelid=pg_class.oid
END;
$r2 = $this->db->query( sprintf( $query, $rid ), __METHOD__ );
if ( !$r2 ) {
return null;
}
$row2 = $r2->fetchRow();
if ( !$row2 ) {
return null;
}
$colnames[] = $row2[0];
}
return $colnames;
}
/**
* @param string $fkey
* @return string|null
*/
protected function fkeyDeltype( $fkey ) {
$q = <<<END
SELECT confdeltype FROM pg_constraint, pg_namespace
WHERE connamespace=pg_namespace.oid
AND nspname=%s
AND conname=%s;
END;
$r = $this->db->query(
sprintf(
$q,
$this->db->addQuotes( $this->db->getCoreSchema() ),
$this->db->addQuotes( $fkey )
),
__METHOD__
);
$row = $r->fetchRow();
return $row ? $row[0] : null;
}
/**
* @param string $table
* @param string $rule
* @return string|null
*/
protected function ruleDef( $table, $rule ) {
$q = <<<END
SELECT definition FROM pg_rules
WHERE schemaname = %s
AND tablename = %s
AND rulename = %s
END;
$r = $this->db->query(
sprintf(
$q,
$this->db->addQuotes( $this->db->getCoreSchema() ),
$this->db->addQuotes( $table ),
$this->db->addQuotes( $rule )
),
__METHOD__
);
$row = $r->fetchRow();
if ( !$row ) {
return null;
}
$d = $row[0];
return $d;
}
/**
* @param string $table
* @param string $pkey
* @param string $ns
*/
protected function addSequence( $table, $pkey, $ns ) {
if ( !$this->db->sequenceExists( $ns ) ) {
$this->output( "Creating sequence $ns\n" );
if ( $pkey !== false ) {
$table = $this->db->addIdentifierQuotes( $table );
$this->db->query( "CREATE SEQUENCE $ns OWNED BY $table.$pkey", __METHOD__ );
$this->setDefault( $table, $pkey, '"nextval"(\'"' . $ns . '"\'::"regclass")' );
} else {
$this->db->query( "CREATE SEQUENCE $ns", __METHOD__ );
}
}
}
/**
* @param string $table
* @param string $ns
*/
protected function dropSequence( $table, $ns ) {
if ( $this->db->sequenceExists( $ns ) ) {
$this->output( "Dropping sequence $ns\n" );
$this->db->query( "DROP SEQUENCE $ns CASCADE", __METHOD__ );
}
}
/**
* @param string $old
* @param string $new
*/
protected function renameSequence( $old, $new ) {
if ( $this->db->sequenceExists( $new ) ) {
$this->output( "...sequence $new already exists.\n" );
return;
}
if ( $this->db->sequenceExists( $old ) ) {
$this->output( "Renaming sequence $old to $new\n" );
$this->db->query( "ALTER SEQUENCE $old RENAME TO $new", __METHOD__ );
}
}
/**
* @param string $table
* @param string $pkey
* @param string $seq
*/
protected function setSequenceOwner( $table, $pkey, $seq ) {
if ( $this->db->sequenceExists( $seq ) ) {
$this->output( "Setting sequence $seq owner to $table.$pkey\n" );
$table = $this->db->addIdentifierQuotes( $table );
$this->db->query( "ALTER SEQUENCE $seq OWNED BY $table.$pkey", __METHOD__ );
}
}
/**
* @param string $old
* @param string $new
* @param string|false $patch
*/
protected function renameTable( $old, $new, $patch = false ) {
if ( $this->db->tableExists( $old, __METHOD__ ) ) {
$this->output( "Renaming table $old to $new\n" );
$old = $this->db->addIdentifierQuotes( $old );
$new = $this->db->addIdentifierQuotes( $new );
$this->db->query( "ALTER TABLE $old RENAME TO $new", __METHOD__ );
if ( $patch !== false ) {
$this->applyPatch( $patch );
}
}
}
/**
* @param string $table
* @param string $old
* @param string $new
* @param bool $skipBothIndexExistWarning
* @param string|false $a
* @param bool $b
* @return bool
*/
protected function renameIndex(
$table, $old, $new, $skipBothIndexExistWarning = false, $a = false, $b = false
) {
// First requirement: the table must exist
if ( !$this->db->tableExists( $table, __METHOD__ ) ) {
$this->output( "...skipping: '$table' table doesn't exist yet.\n" );
return true;
}
// Second requirement: the new index must be missing
if ( $this->db->indexExists( $table, $new, __METHOD__ ) ) {
$this->output( "...index $new already set on $table table.\n" );
if ( !$skipBothIndexExistWarning
&& $this->db->indexExists( $table, $old, __METHOD__ )
) {
$this->output( "...WARNING: $old still exists, despite it has been " .
"renamed into $new (which also exists).\n" .
" $old should be manually removed if not needed anymore.\n" );
}
return true;
}
// Third requirement: the old index must exist
if ( !$this->db->indexExists( $table, $old, __METHOD__ ) ) {
$this->output( "...skipping: index $old doesn't exist.\n" );
return true;
}
$this->db->query( "ALTER INDEX $old RENAME TO $new", __METHOD__ );
return true;
}
/**
* @param string $table
* @param string $field
*/
protected function dropPgField( $table, $field ) {
$fi = $this->db->fieldInfo( $table, $field );
if ( $fi === null ) {
$this->output( "...$table table does not contain $field field.\n" );
} else {
$this->output( "Dropping column '$table.$field'\n" );
$table = $this->db->addIdentifierQuotes( $table );
$this->db->query( "ALTER TABLE $table DROP COLUMN $field", __METHOD__ );
}
}
/**
* @param string $table
* @param string $field
* @param string $type
*/
protected function addPgField( $table, $field, $type ) {
$fi = $this->db->fieldInfo( $table, $field );
if ( $fi !== null ) {
$this->output( "...column '$table.$field' already exists\n" );
} else {
$this->output( "Adding column '$table.$field'\n" );
$table = $this->db->addIdentifierQuotes( $table );
$this->db->query( "ALTER TABLE $table ADD $field $type", __METHOD__ );
}
}
/**
* @param string $table
* @param string $field
* @param string $newtype
* @param string $default
*/
protected function changeField( $table, $field, $newtype, $default ) {
if ( !$this->db->tableExists( $table, __METHOD__ ) ) {
$this->output( "...$table table does not exist, skipping default change.\n" );
return;
}
$fi = $this->db->fieldInfo( $table, $field );
if ( $fi === null ) {
$this->output( "...ERROR: expected column $table.$field to exist\n" );
exit( 1 );
}
if ( $fi->type() === strtolower( $newtype ) ) {
$this->output( "...column '$table.$field' is already of type '$newtype'\n" );
} else {
$this->output( "Changing column type of '$table.$field' from '{$fi->type()}' to '$newtype'\n" );
$table = $this->db->addIdentifierQuotes( $table );
$sql = "ALTER TABLE $table ALTER $field TYPE $newtype";
if ( $default !== '' ) {
$res = [];
if ( preg_match( '/DEFAULT (.+)/', $default, $res ) ) {
$sqldef = "ALTER TABLE $table ALTER $field SET DEFAULT $res[1]";
$this->db->query( $sqldef, __METHOD__ );
$default = preg_replace( '/\s*DEFAULT .+/', '', $default );
}
$sql .= " USING $default";
}
$this->db->query( $sql, __METHOD__ );
}
}
/**
* @param string $table
* @param string $field
* @param string $newtype
* @param string $default
*/
protected function changeFieldPurgeTable( $table, $field, $newtype, $default ) {
# # For a cache table, empty it if the field needs to be changed, because the old contents
# # may be corrupted. If the column is already the desired type, refrain from purging.
$fi = $this->db->fieldInfo( $table, $field );
if ( $fi === null ) {
$this->output( "...ERROR: expected column $table.$field to exist\n" );
exit( 1 );
}
if ( $fi->type() === $newtype ) {
$this->output( "...column '$table.$field' is already of type '$newtype'\n" );
} else {
$this->output( "Purging data from cache table '$table'\n" );
$table = $this->db->addIdentifierQuotes( $table );
$this->db->query( "DELETE from $table", __METHOD__ );
$this->output( "Changing column type of '$table.$field' from '{$fi->type()}' to '$newtype'\n" );
$sql = "ALTER TABLE $table ALTER $field TYPE $newtype";
if ( strlen( $default ) ) {
$res = [];
if ( preg_match( '/DEFAULT (.+)/', $default, $res ) ) {
$sqldef = "ALTER TABLE $table ALTER $field SET DEFAULT $res[1]";
$this->db->query( $sqldef, __METHOD__ );
$default = preg_replace( '/\s*DEFAULT .+/', '', $default );
}
$sql .= " USING $default";
}
$this->db->query( $sql, __METHOD__ );
}
}
/**
* @param string $table
* @param string $field
* @param string $default
*/
protected function setDefault( $table, $field, $default ) {
if ( !$this->db->tableExists( $table, __METHOD__ ) ) {
$this->output( "...$table table does not exist, skipping default change.\n" );
return;
}
$info = $this->db->fieldInfo( $table, $field );
if ( $info && $info->defaultValue() !== $default ) {
$this->output( "Changing '$table.$field' default value\n" );
$table = $this->db->addIdentifierQuotes( $table );
$this->db->query( "ALTER TABLE $table ALTER $field SET DEFAULT "
. $this->db->addQuotes( $default ), __METHOD__ );
}
}
/**
* Drop a default value from a field
* @since 1.32
* @param string $table
* @param string $field
*/
protected function dropDefault( $table, $field ) {
$info = $this->db->fieldInfo( $table, $field );
if ( $info && $info->defaultValue() !== false ) {
$this->output( "Removing '$table.$field' default value\n" );
$table = $this->db->addIdentifierQuotes( $table );
$this->db->query( "ALTER TABLE $table ALTER $field DROP DEFAULT", __METHOD__ );
}
}
/**
* @param string $table
* @param string $field
* @param string $null
* @param bool $update
*/
protected function changeNullableField( $table, $field, $null, $update = false ) {
$fi = $this->db->fieldInfo( $table, $field );
if ( $fi === null ) {
return;
}
if ( $fi->isNullable() ) {
# # It's NULL - does it need to be NOT NULL?
if ( $null === 'NOT NULL' ) {
$this->output( "Changing '$table.$field' to not allow NULLs\n" );
$table = $this->db->addIdentifierQuotes( $table );
if ( $update ) {
$this->db->query( "UPDATE $table SET $field = DEFAULT WHERE $field IS NULL", __METHOD__ );
}
$this->db->query( "ALTER TABLE $table ALTER $field SET NOT NULL", __METHOD__ );
} else {
$this->output( "...column '$table.$field' is already set as NULL\n" );
}
} else {
# # It's NOT NULL - does it need to be NULL?
if ( $null === 'NULL' ) {
$this->output( "Changing '$table.$field' to allow NULLs\n" );
$table = $this->db->addIdentifierQuotes( $table );
$this->db->query( "ALTER TABLE $table ALTER $field DROP NOT NULL", __METHOD__ );
} else {
$this->output( "...column '$table.$field' is already set as NOT NULL\n" );
}
}
}
/**
* @param string $table
* @param string $index
* @param string $type
* @param bool $unique
*/
protected function addPgIndex( $table, $index, $type, $unique = false ) {
if ( !$this->db->tableExists( $table, __METHOD__ ) ) {
$this->output( "...$table table does not exist, skipping index.\n" );
} elseif ( $this->db->indexExists( $table, $index, __METHOD__ ) ) {
$this->output( "...index '$index' on table '$table' already exists\n" );
} else {
$this->output( "Creating index '$index' on table '$table' $type\n" );
$table = $this->db->addIdentifierQuotes( $table );
$unique = $unique ? 'UNIQUE' : '';
$this->db->query( "CREATE $unique INDEX $index ON $table $type", __METHOD__ );
}
}
/**
* @param string $table
* @param string $index
* @param string $type
*/
protected function addPgExtIndex( $table, $index, $type ) {
if ( $this->db->indexExists( $table, $index, __METHOD__ ) ) {
$this->output( "...index '$index' on table '$table' already exists\n" );
} elseif ( preg_match( '/^\(/', $type ) ) {
$this->output( "Creating index '$index' on table '$table'\n" );
$table = $this->db->addIdentifierQuotes( $table );
$this->db->query( "CREATE INDEX $index ON $table $type", __METHOD__ );
} else {
$this->applyPatch( $type, true, "Creating index '$index' on table '$table'" );
}
}
/**
* @param string $table
* @param string $field
*/
protected function dropFkey( $table, $field ) {
if ( !$this->db->tableExists( $table, __METHOD__ ) ) {
$this->output( "...$table table does not exist, skipping constraint change.\n" );
return;
}
$fi = $this->db->fieldInfo( $table, $field );
if ( $fi === null ) {
$this->output( "WARNING! Column '$table.$field' does not exist but it should! " .
"Please report this.\n" );
return;
}
if ( $this->dropConstraint( $table, $field, 'foreignkey', $fi->conname() ) ) {
$this->output( "Dropping foreign key constraint on '$table.$field'\n" );
} else {
$this->output( "...foreign key constraint on '$table.$field' already does not exist\n" );
}
}
/**
* @param string $table
* @param string $field
* @param string $clause
*/
protected function changeFkeyDeferrable( $table, $field, $clause ) {
$fi = $this->db->fieldInfo( $table, $field );
if ( $fi === null ) {
$this->output( "WARNING! Column '$table.$field' does not exist but it should! " .
"Please report this.\n" );
return;
}
if ( $fi->is_deferred() && $fi->is_deferrable() ) {
return;
}
$this->output( "Altering column '$table.$field' to be DEFERRABLE INITIALLY DEFERRED\n" );
$conname = $fi->conname();
$conclause = "CONSTRAINT \"$conname\"";
if ( !$this->dropConstraint( $table, $field, 'foreignkey', $conname ) ) {
$this->output( "Column '$table.$field' does not have a foreign key " .
"constraint, will be added\n" );
$conclause = "";
}
$command =
"ALTER TABLE $table ADD $conclause " .
"FOREIGN KEY ($field) REFERENCES $clause DEFERRABLE INITIALLY DEFERRED";
$this->db->query( $command, __METHOD__ );
}
/**
* @param string $table
* @param string $index
*/
protected function dropPgIndex( $table, $index ) {
if ( $this->db->indexExists( $table, $index, __METHOD__ ) ) {
$this->output( "Dropping obsolete index '$index'\n" );
$this->db->query( "DROP INDEX \"" . $index . "\"", __METHOD__ );
}
}
/**
* @param string $index
* @param array $should_be
* @param string $good_def
*/
protected function checkIndex( $index, $should_be, $good_def ) {
$pu = $this->db->indexAttributes( $index );
if ( $pu && $pu != $should_be ) {
$this->output( "Dropping obsolete version of index '$index'\n" );
$this->db->query( "DROP INDEX \"" . $index . "\"", __METHOD__ );
$pu = [];
} else {
$this->output( "...no need to drop index '$index'\n" );
}
if ( !$pu ) {
$this->output( "Creating index '$index'\n" );
$this->db->query( $good_def, __METHOD__ );
} else {
$this->output( "...index '$index' exists\n" );
}
}
/**
* @param string $table
* @param array $shouldBe
* @param string|null $constraintName
*/
protected function changePrimaryKey( $table, $shouldBe, $constraintName = null ) {
// https://wiki.postgresql.org/wiki/Retrieve_primary_key_columns
$result = $this->db->query(
"SELECT a.attname as column " .
"FROM pg_index i " .
"JOIN pg_attribute a ON a.attrelid = i.indrelid " .
"AND a.attnum = ANY(i.indkey) " .
"WHERE i.indrelid = '\"$table\"'::regclass " .
"AND i.indisprimary",
__METHOD__
);
$currentColumns = [];
foreach ( $result as $row ) {
$currentColumns[] = $row->column;
}
if ( $currentColumns == $shouldBe ) {
$this->output( "...no need to change primary key of '$table'\n" );
return;
}
$this->dropConstraint( $table, '', 'primary', $constraintName );
$table = $this->db->addIdentifierQuotes( $table );
$this->db->query(
"ALTER TABLE $table" .
" ADD PRIMARY KEY (" . implode( ',', $shouldBe ) . ');',
__METHOD__
);
}
/**
* Drop generic constraint. If the constraint was created with a custom name,
* then the name must be queried and supplied as $conname, otherwise standard
* system suffixes and format would be assumed.
*
* @param string $table
* @param string $field
* @param string $type
* @param string|null $conname
* @return bool
*/
protected function dropConstraint( $table, $field, $type, $conname = null ) {
if ( $conname === null ) {
if ( $type == 'primary' ) {
$conname = "{$table}_pkey";
} else {
$map = [ 'unique' => 'key', 'check' => 'check', 'foreignkey' => 'fkey' ];
$conname = "{$table}_{$field}_{$map[$type]}";
}
}
if ( $this->db->constraintExists( $table, $conname ) ) {
$table = $this->db->addIdentifierQuotes( $table );
$this->db->query(
"ALTER TABLE $table DROP CONSTRAINT $conname;",
__METHOD__
);
return true;
}
return false;
}
/**
* Replaces unique index with primary key,modifies si_title length
*
* @since 1.43
* @return void
*/
protected function migrateSearchindex() {
$updateKey = 'searchindex-pk-titlelength';
if ( !$this->tableExists( 'searchindex' ) ) {
return;
}
$primaryIndexExists = $this->db->indexExists( 'searchindex', 'searchindex_pkey' );
if ( $this->updateRowExists( $updateKey ) || ( $primaryIndexExists ) ) {
$this->output( "...searchindex table has already been migrated.\n" );
if ( !$this->updateRowExists( $updateKey ) ) {
$this->insertUpdateRow( $updateKey );
}
return;
}
$apply = $this->applyPatch( 'patch-searchindex-pk-titlelength.sql', false, '...migrating searchindex table' );
if ( $apply ) {
$this->insertUpdateRow( $updateKey );
}
}
}

File Metadata

Mime Type
text/x-php
Expires
Sat, May 16, 12:38 (1 d, 4 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
e3/23/4a1b0cfbdd8ababc2178ca95e05e
Default Alt Text
PostgresUpdater.php (24 KB)

Event Timeline