Fix PostgreSQL updater to produce 1.19 schema

* PostgresField now reports column default value
* DatabasePostgres::indexAttributes reports index
  details
* Perform schema update in one transaction

With this change we can sucessfully update
MediaWiki 1.7.3 schema to trunk^Wmaster

Patch set 2: Rebased against master. PLEASE check carefully
to make sure I got those conflicted files right.

Conflicts:

	includes/db/DatabasePostgres.php
	includes/installer/PostgresUpdater.php

Change-Id: Iebb6855e8f6f44470bbb1dc5ab9ac1abb513adfe
This commit is contained in:
saper 2012-03-22 03:42:44 +01:00 committed by Chad Horohoe
parent f0ecaf0af1
commit f752cf8042
12 changed files with 180 additions and 59 deletions

View file

@ -19,6 +19,8 @@ class PostgresField implements Field {
$q = <<<SQL
SELECT
attnotnull, attlen, conname AS conname,
atthasdef,
adsrc,
COALESCE(condeferred, 'f') AS deferred,
COALESCE(condeferrable, 'f') AS deferrable,
CASE WHEN typname = 'int2' THEN 'smallint'
@ -315,6 +317,7 @@ class DatabasePostgres extends DatabaseBase {
}
protected function doQuery( $sql ) {
global $wgDebugDBTransactions;
if ( function_exists( 'mb_convert_encoding' ) ) {
$sql = mb_convert_encoding( $sql, 'UTF-8' );
}
@ -707,19 +710,14 @@ class DatabasePostgres extends DatabaseBase {
# Replace reserved words with better ones
switch( $name ) {
case 'user':
return $this->realTableName( 'mwuser', $format );
return 'mwuser';
case 'text':
return $this->realTableName( 'pagecontent', $format );
return 'pagecontent';
default:
return $this->realTableName( $name, $format );
return parent::tableName( $name, $format );
}
}
/* Don't cheat on installer */
function realTableName( $name, $format = 'quoted' ) {
return parent::tableName( $name, $format );
}
/**
* Return the next in a sequence, save the value for retrieval via insertId()
* @return null
@ -994,7 +992,7 @@ class DatabasePostgres extends DatabaseBase {
if ( !$schema ) {
$schema = $this->getCoreSchema();
}
$table = $this->realTableName( $table, 'raw' );
$table = $this->tableName( $table, 'raw' );
$etable = $this->addQuotes( $table );
$eschema = $this->addQuotes( $schema );
$SQL = "SELECT 1 FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n "

View file

@ -272,6 +272,7 @@ abstract class DatabaseUpdater {
public function doUpdates( $what = array( 'core', 'extensions', 'purge', 'stats' ) ) {
global $wgLocalisationCacheConf, $wgVersion;
$this->db->begin( __METHOD__ );
$what = array_flip( $what );
if ( isset( $what['core'] ) ) {
$this->runUpdates( $this->getCoreUpdateList(), false );
@ -294,6 +295,7 @@ abstract class DatabaseUpdater {
$this->rebuildLocalisationCache();
}
}
$this->db->commit( __METHOD__ );
}
/**

View file

@ -541,7 +541,7 @@ class PostgresInstaller extends DatabaseInstaller {
*/
$conn = $status->value;
if( $conn->tableExists( 'user' ) ) {
if( $conn->tableExists( 'archive' ) ) {
$status->warning( 'config-install-tables-exist' );
$this->enableLB();
return $status;

View file

@ -29,8 +29,11 @@ class PostgresUpdater extends DatabaseUpdater {
return array(
# rename tables 1.7.3
# r15791 Change reserved word table names "user" and "text"
array( 'renameTable', 'user', 'mwuser'),
array( 'renameTable', 'text', 'pagecontent'),
array( 'renameTable', 'user', 'mwuser' ),
array( 'renameTable', 'text', 'pagecontent' ),
array( 'renameIndex', 'mwuser', 'user_pkey', 'mwuser_pkey'),
array( 'renameIndex', 'mwuser', 'user_user_name_key', 'mwuser_user_name_key' ),
array( 'renameIndex', 'pagecontent','text_pkey', 'pagecontent_pkey' ),
# new sequences
array( 'addSequence', 'logging_log_id_seq' ),
@ -40,12 +43,19 @@ class PostgresUpdater extends DatabaseUpdater {
array( 'renameSequence', 'ipblocks_ipb_id_val', 'ipblocks_ipb_id_seq' ),
array( 'renameSequence', 'rev_rev_id_val', 'revision_rev_id_seq' ),
array( 'renameSequence', 'text_old_id_val', 'text_old_id_seq' ),
array( 'renameSequence', 'category_id_seq', 'category_cat_id_seq' ),
array( 'renameSequence', 'rc_rc_id_seq', 'recentchanges_rc_id_seq' ),
array( 'renameSequence', 'log_log_id_seq', 'logging_log_id_seq' ),
array( 'renameSequence', 'pr_id_val', 'page_restrictions_pr_id_seq' ),
array( 'renameSequence', 'us_id_seq', 'uploadstash_us_id_seq' ),
# since r58263
array( 'renameSequence', 'category_id_seq', 'category_cat_id_seq'),
# new sequences if not renamed above
array( 'addSequence', 'logging', false, 'logging_log_id_seq' ),
array( 'addSequence', 'page_restrictions', false, 'page_restrictions_pr_id_seq' ),
array( 'addSequence', 'filearchive', 'fa_id', 'filearchive_fa_id_seq' ),
# new tables
array( 'addTable', 'category', 'patch-category.sql' ),
array( 'addTable', 'page', 'patch-page.sql' ),
@ -69,11 +79,13 @@ class PostgresUpdater extends DatabaseUpdater {
array( 'addTable', 'uploadstash', 'patch-uploadstash.sql' ),
array( 'addTable', 'user_former_groups','patch-user_former_groups.sql' ),
array( 'addTable', 'config', 'patch-config.sql' ),
array( 'addTable', 'external_user','patch-external_user.sql' ),
# Needed before new field
array( 'convertArchive2' ),
# new fields
array( 'addPgField', 'updatelog', 'ul_value', 'TEXT' ),
array( 'addPgField', 'archive', 'ar_deleted', 'SMALLINT NOT NULL DEFAULT 0' ),
array( 'addPgField', 'archive', 'ar_len', 'INTEGER' ),
array( 'addPgField', 'archive', 'ar_page_id', 'INTEGER' ),
@ -177,6 +189,11 @@ class PostgresUpdater extends DatabaseUpdater {
array( 'changeNullableField', 'oldimage', 'oi_timestamp', 'NULL' ),
array( 'changeNullableField', 'oldimage', 'oi_major_mime', 'NULL' ),
array( 'changeNullableField', 'oldimage', 'oi_minor_mime', 'NULL' ),
array( 'setDefault', 'image', 'img_metadata', '\'\x\'::bytea'),
array( 'changeNullableField', 'image', 'img_metadata', 'NOT NULL'),
array( 'setDefault', 'filearchive', 'fa_metadata', '\'\x\'::bytea'),
array( 'changeNullableField', 'filearchive', 'fa_metadata', 'NOT NULL'),
array( 'changeNullableField', 'recentchanges', 'rc_cur_id', 'NULL' ),
array( 'checkOiDeleted' ),
@ -195,12 +212,78 @@ class PostgresUpdater extends DatabaseUpdater {
array( 'addPgIndex', 'iwlinks', 'iwl_prefix_title_from', '(iwl_prefix, iwl_title, iwl_from)' ),
array( 'addPgIndex', 'job', 'job_timestamp_idx', '(job_timestamp)' ),
array( 'checkIndex', 'pagelink_unique', array(
array('pl_from', 'int4_ops', 'btree', 0),
array('pl_namespace', 'int2_ops', 'btree', 0),
array('pl_title', 'text_ops', 'btree', 0),
),
'CREATE UNIQUE INDEX pagelink_unique ON pagelinks (pl_from,pl_namespace,pl_title)' ),
array( 'checkIndex', 'cl_sortkey', array(
array('cl_to', 'text_ops', 'btree', 0),
array('cl_sortkey', 'text_ops', 'btree', 0),
array('cl_from', 'int4_ops', 'btree', 0),
),
'CREATE INDEX cl_sortkey ON "categorylinks" USING "btree" ("cl_to", "cl_sortkey", "cl_from")' ),
array( 'checkIndex', 'logging_times', array(
array('log_timestamp', 'timestamptz_ops', 'btree', 0),
),
'CREATE INDEX "logging_times" ON "logging" USING "btree" ("log_timestamp")' ),
array( 'dropIndex', 'oldimage', 'oi_name' ),
array( 'checkIndex', 'oi_name_archive_name', array(
array('oi_name', 'text_ops', 'btree', 0),
array('oi_archive_name', 'text_ops', 'btree', 0),
),
'CREATE INDEX "oi_name_archive_name" ON "oldimage" USING "btree" ("oi_name", "oi_archive_name")' ),
array( 'checkIndex', 'oi_name_timestamp', array(
array('oi_name', 'text_ops', 'btree', 0),
array('oi_timestamp', 'timestamptz_ops', 'btree', 0),
),
'CREATE INDEX "oi_name_timestamp" ON "oldimage" USING "btree" ("oi_name", "oi_timestamp")' ),
array( 'checkIndex', 'page_main_title', array(
array('page_title', 'text_pattern_ops', 'btree', 0),
),
'CREATE INDEX "page_main_title" ON "page" USING "btree" ("page_title" "text_pattern_ops") WHERE ("page_namespace" = 0)' ),
array( 'checkIndex', 'page_mediawiki_title', array(
array('page_title', 'text_pattern_ops', 'btree', 0),
),
'CREATE INDEX "page_mediawiki_title" ON "page" USING "btree" ("page_title" "text_pattern_ops") WHERE ("page_namespace" = 8)' ),
array( 'checkIndex', 'page_project_title', array(
array('page_title', 'text_pattern_ops', 'btree', 0),
),
'CREATE INDEX "page_project_title" ON "page" USING "btree" ("page_title" "text_pattern_ops") WHERE ("page_namespace" = 4)' ),
array( 'checkIndex', 'page_talk_title', array(
array('page_title', 'text_pattern_ops', 'btree', 0),
),
'CREATE INDEX "page_talk_title" ON "page" USING "btree" ("page_title" "text_pattern_ops") WHERE ("page_namespace" = 1)' ),
array( 'checkIndex', 'page_user_title', array(
array('page_title', 'text_pattern_ops', 'btree', 0),
),
'CREATE INDEX "page_user_title" ON "page" USING "btree" ("page_title" "text_pattern_ops") WHERE ("page_namespace" = 2)' ),
array( 'checkIndex', 'page_utalk_title', array(
array('page_title', 'text_pattern_ops', 'btree', 0),
),
'CREATE INDEX "page_utalk_title" ON "page" USING "btree" ("page_title" "text_pattern_ops") WHERE ("page_namespace" = 3)' ),
array( 'checkIndex', 'ts2_page_text', array(
array('textvector', 'tsvector_ops', 'gist', 0),
),
'CREATE INDEX "ts2_page_text" ON "pagecontent" USING "gist" ("textvector")' ),
array( 'checkIndex', 'ts2_page_title', array(
array('titlevector', 'tsvector_ops', 'gist', 0),
),
'CREATE INDEX "ts2_page_title" ON "page" USING "gist" ("titlevector")' ),
array( 'checkOiNameConstraint' ),
array( 'checkPageDeletedTrigger' ),
array( 'checkRcCurIdNullable' ),
array( 'checkPagelinkUniqueIndex' ),
array( 'checkRevUserFkey' ),
array( 'checkIpbAdress' ),
array( 'dropIndex', 'ipblocks', 'ipb_address'),
array( 'checkIndex', 'ipb_address_unique', array(
array('ipb_address', 'text_ops', 'btree', 0),
array('ipb_user', 'int4_ops', 'btree', 0),
array('ipb_auto', 'int2_ops', 'btree', 0),
array('ipb_anon_only', 'int2_ops', 'btree', 0),
),
'CREATE UNIQUE INDEX ipb_address_unique ON ipblocks (ipb_address,ipb_user,ipb_auto,ipb_anon_only)' ),
array( 'checkIwlPrefix' ),
# All FK columns should be deferred
@ -232,6 +315,8 @@ class PostgresUpdater extends DatabaseUpdater {
array( 'changeFkeyDeferrable', 'user_properties', 'up_user', 'mwuser(user_id) ON DELETE CASCADE' ),
array( 'changeFkeyDeferrable', 'watchlist', 'wl_user', 'mwuser(user_id) ON DELETE CASCADE' ),
# r81574
array( 'addInterwikiType' ),
# end
array( 'tsearchFixes' ),
);
@ -392,10 +477,13 @@ END;
return $d;
}
protected function addSequence( $ns ) {
protected function addSequence( $table, $pkey, $ns ) {
if ( !$this->db->sequenceExists( $ns ) ) {
$this->output( "Creating sequence $ns\n" );
$this->db->query( "CREATE SEQUENCE $ns" );
if( $pkey !== false ) {
$this->setDefault( $table, $pkey, '"nextval"(\'"' . $ns . '"\'::"regclass")' );
}
}
}
@ -410,12 +498,22 @@ END;
}
}
protected function renameTable( $old, $new ) {
protected function renameTable( $old, $new, $patch = false ) {
if ( $this->db->tableExists( $old ) ) {
$this->output( "Renaming table $old to $new\n" );
$old = $this->db->realTableName( $old, "quoted" );
$new = $this->db->realTableName( $new, "quoted" );
$this->db->query( "ALTER TABLE $old RENAME TO $new" );
if( $patch !== false ) {
$this->applyPatch( $patch );
}
}
}
protected function renameIndex( $table, $old, $new ) {
if ( $this->db->indexExists( $table, $old ) ) {
$this->output( "Renaming index $old to $new\n" );
$this->db->query( "ALTER INDEX $old RENAME TO $new" );
}
}
@ -451,13 +549,20 @@ END;
}
$sql .= " USING $default";
}
$this->db->begin( __METHOD__ );
$this->db->query( $sql );
$this->db->commit( __METHOD__ );
}
}
protected function changeNullableField( $table, $field, $null ) {
protected function setDefault( $table, $field, $default ) {
$info = $this->db->fieldInfo( $table, $field );
if ( $info->defaultValue() !== $default ) {
$this->output( "Changing '$table.$field' default value\n" );
$this->db->query( "ALTER TABLE $table ALTER $field SET DEFAULT " . $default );
}
}
protected function changeNullableField( $table, $field, $null) {
$fi = $this->db->fieldInfo( $table, $field );
if ( is_null( $fi ) ) {
$this->output( "...ERROR: expected column $table.$field to exist\n" );
@ -581,31 +686,28 @@ END;
}
}
protected function checkRcCurIdNullable(){
$fi = $this->db->fieldInfo( 'recentchanges', 'rc_cur_id' );
if ( !$fi->isNullable() ) {
$this->output( "Removing NOT NULL constraint from 'recentchanges.rc_cur_id'\n" );
$this->applyPatch( 'patch-rc_cur_id-not-null.sql' );
} else {
$this->output( "...column 'recentchanges.rc_cur_id' has a NOT NULL constraint\n" );
protected function dropIndex( $table, $index ) {
if ( $this->db->indexExists( $table, $index ) ) {
$this->output( "Dropping obsolete index '$index'\n" );
$this->db->query( "DROP INDEX \"". $index ."\"" );
}
}
protected function checkPagelinkUniqueIndex() {
$pu = $this->describeIndex( 'pagelink_unique' );
if ( !is_null( $pu ) && ( $pu[0] != 'pl_from' || $pu[1] != 'pl_namespace' || $pu[2] != 'pl_title' ) ) {
$this->output( "Dropping obsolete version of index 'pagelink_unique index'\n" );
$this->db->query( 'DROP INDEX pagelink_unique' );
$pu = null;
protected function checkIndex( $index, $should_be, $good_def ) {
$pu = $this->db->indexAttributes( $index );
if ( !empty( $pu ) && $pu != $should_be ) {
$this->output( "Dropping obsolete version of index '$index'\n" );
$this->db->query( "DROP INDEX \"". $index ."\"" );
$pu = array();
} else {
$this->output( "...obsolete version of index 'pagelink_unique index' does not exist\n" );
$this->output( "...no need to drop index '$index'\n" );
}
if ( is_null( $pu ) ) {
$this->output( "Creating index 'pagelink_unique index'\n" );
$this->db->query( 'CREATE UNIQUE INDEX pagelink_unique ON pagelinks (pl_from,pl_namespace,pl_title)' );
if ( empty( $pu ) ) {
$this->output( "Creating index '$index'\n" );
$this->db->query( $good_def );
} else {
$this->output( "...index 'pagelink_unique_index' already exists\n" );
$this->output( "...index '$index' exists\n" );
}
}
@ -618,19 +720,6 @@ END;
}
}
protected function checkIpbAdress() {
if ( $this->db->indexExists( 'ipblocks', 'ipb_address' ) ) {
$this->output( "Removing deprecated index 'ipb_address'...\n" );
$this->db->query( 'DROP INDEX ipb_address' );
}
if ( $this->db->indexExists( 'ipblocks', 'ipb_address_unique' ) ) {
$this->output( "...have ipb_address_unique\n" );
} else {
$this->output( "Adding ipb_address_unique index\n" );
$this->applyPatch( 'patch-ipb_address_unique.sql' );
}
}
protected function checkIwlPrefix() {
if ( $this->db->indexExists( 'iwlinks', 'iwl_prefix' ) ) {
$this->output( "Replacing index 'iwl_prefix' with 'iwl_prefix_from_title'...\n" );
@ -638,6 +727,11 @@ END;
}
}
protected function addInterwikiType() {
$this->output( "Refreshing add_interwiki()...\n" );
$this->applyPatch( 'patch-add_interwiki.sql' );
}
protected function tsearchFixes() {
# Tweak the page_title tsearch2 trigger to filter out slashes
# This is create or replace, so harmless to call if not needed

View file

@ -0,0 +1,14 @@
DROP FUNCTION IF EXISTS add_interwiki(TEXT,INT,CHARACTER) CASCADE;
CREATE OR REPLACE FUNCTION "add_interwiki" (TEXT,INT,SMALLINT) RETURNS INT LANGUAGE SQL AS
$mw$
INSERT INTO interwiki (iw_prefix, iw_url, iw_local) VALUES ($1,$2,$3);
SELECT 1;
$mw$;
DROP FUNCTION IF EXISTS add_interwiki(TEXT,INT,CHARACTER) CASCADE;
CREATE OR REPLACE FUNCTION "add_interwiki" (TEXT,INT,SMALLINT) RETURNS INT LANGUAGE SQL AS
$mw$
INSERT INTO interwiki (iw_prefix, iw_url, iw_local) VALUES ($1,$2,$3);
SELECT 1;
$mw$;

View file

@ -1,8 +1,8 @@
CREATE SEQUENCE category_id_seq;
CREATE SEQUENCE category_cat_id_seq;
CREATE TABLE category (
cat_id INTEGER NOT NULL PRIMARY KEY DEFAULT nextval('category_id_seq'),
cat_id INTEGER NOT NULL PRIMARY KEY DEFAULT nextval('category_cat_id_seq'),
cat_title TEXT NOT NULL,
cat_pages INTEGER NOT NULL DEFAULT 0,
cat_subcats INTEGER NOT NULL DEFAULT 0,

View file

@ -0,0 +1,14 @@
CREATE TABLE external_user (
eu_local_id INTEGER NOT NULL PRIMARY KEY,
eu_external_id TEXT
);
CREATE UNIQUE INDEX eu_external_id ON external_user (eu_external_id);
CREATE TABLE external_user (
eu_local_id INTEGER NOT NULL PRIMARY KEY,
eu_external_id TEXT
);
CREATE UNIQUE INDEX eu_external_id ON external_user (eu_external_id);

View file

@ -1 +0,0 @@
CREATE UNIQUE INDEX ipb_address_unique ON ipblocks (ipb_address,ipb_user,ipb_auto,ipb_anon_only);

View file

@ -5,5 +5,5 @@ CREATE TABLE log_search (
ls_log_id INTEGER NOT NULL DEFAULT 0
);
ALTER TABLE log_search ADD CONSTRAINT log_search_pk PRIMARY KEY(ls_field, ls_value, ls_log_id);
ALTER TABLE log_search ADD CONSTRAINT log_search_pkey PRIMARY KEY(ls_field, ls_value, ls_log_id);
CREATE INDEX ls_log_id ON log_search (ls_log_id);

View file

@ -4,4 +4,4 @@ CREATE TABLE module_deps (
md_deps TEXT NOT NULL
);
CREATE UNIQUE INDEX md_module_skin_idx ON module_deps (md_module, md_skin);
CREATE UNIQUE INDEX md_module_skin ON module_deps (md_module, md_skin);

View file

@ -5,4 +5,4 @@ CREATE TABLE msg_resource (
mr_timestamp TIMESTAMPTZ NOT NULL
);
CREATE UNIQUE INDEX mr_resource_lang_idx ON msg_resource (mr_resource, mr_lang);
CREATE UNIQUE INDEX mr_resource_lang ON msg_resource (mr_resource, mr_lang);

View file

@ -3,4 +3,4 @@ CREATE TABLE msg_resource_links (
mrl_message TEXT NOT NULL
);
CREATE UNIQUE INDEX mrl_message_resource_idx ON msg_resource_links (mrl_message, mrl_resource);
CREATE UNIQUE INDEX mrl_message_resource ON msg_resource_links (mrl_message, mrl_resource);