2010-08-15 18:55:08 +00:00
< ? php
2010-08-21 18:20:09 +00:00
/**
* PostgreSQL - specific updater .
*
* @ file
* @ ingroup Deployment
*/
2010-12-01 20:15:45 +00:00
2010-08-15 18:55:08 +00:00
/**
* Class for handling updates to Postgres databases .
*
* @ ingroup Deployment
* @ since 1.17
*/
2010-08-21 20:23:42 +00:00
class PostgresUpdater extends DatabaseUpdater {
2010-08-22 08:07:26 +00:00
2011-11-20 17:54:47 +00:00
/**
* @ var DatabasePostgres
*/
protected $db ;
2010-08-22 08:07:26 +00:00
/**
* @ todo FIXME : Postgres should use sequential updates like Mysql , Sqlite
* and everybody else . It never got refactored like it should ' ve .
2012-02-09 21:35:05 +00:00
* @ return array
2010-08-22 08:07:26 +00:00
*/
2010-08-15 18:55:08 +00:00
protected function getCoreUpdateList () {
2010-08-22 08:07:26 +00:00
return array (
# new sequences
array ( 'addSequence' , 'logging_log_id_seq' ),
array ( 'addSequence' , 'page_restrictions_pr_id_seq' ),
# renamed sequences
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' ),
2011-11-16 19:54:38 +00:00
array ( 'renameSequence' , 'us_id_seq' , 'uploadstash_us_id_seq' ),
2010-08-22 08:07:26 +00:00
# new tables
array ( 'addTable' , 'category' , 'patch-category.sql' ),
2010-09-13 14:26:38 +00:00
array ( 'addTable' , 'page' , 'patch-page.sql' ),
2010-08-22 08:07:26 +00:00
array ( 'addTable' , 'querycachetwo' , 'patch-querycachetwo.sql' ),
array ( 'addTable' , 'page_props' , 'patch-page_props.sql' ),
array ( 'addTable' , 'page_restrictions' , 'patch-page_restrictions.sql' ),
array ( 'addTable' , 'profiling' , 'patch-profiling.sql' ),
array ( 'addTable' , 'protected_titles' , 'patch-protected_titles.sql' ),
array ( 'addTable' , 'redirect' , 'patch-redirect.sql' ),
array ( 'addTable' , 'updatelog' , 'patch-updatelog.sql' ),
array ( 'addTable' , 'change_tag' , 'patch-change_tag.sql' ),
2010-12-30 18:53:48 +00:00
array ( 'addTable' , 'tag_summary' , 'patch-tag_summary.sql' ),
array ( 'addTable' , 'valid_tag' , 'patch-valid_tag.sql' ),
2010-08-22 08:07:26 +00:00
array ( 'addTable' , 'user_properties' , 'patch-user_properties.sql' ),
array ( 'addTable' , 'log_search' , 'patch-log_search.sql' ),
array ( 'addTable' , 'l10n_cache' , 'patch-l10n_cache.sql' ),
array ( 'addTable' , 'iwlinks' , 'patch-iwlinks.sql' ),
2010-09-04 04:10:23 +00:00
array ( 'addTable' , 'msg_resource' , 'patch-msg_resource.sql' ),
array ( 'addTable' , 'msg_resource_links' , 'patch-msg_resource_links.sql' ),
2010-09-04 04:14:51 +00:00
array ( 'addTable' , 'module_deps' , 'patch-module_deps.sql' ),
2011-10-24 19:55:01 +00:00
array ( 'addTable' , 'uploadstash' , 'patch-uploadstash.sql' ),
array ( 'addTable' , 'user_former_groups' , 'patch-user_former_groups.sql' ),
2010-08-22 08:07:26 +00:00
# Needed before new field
array ( 'convertArchive2' ),
# new fields
array ( 'addPgField' , 'archive' , 'ar_deleted' , 'SMALLINT NOT NULL DEFAULT 0' ),
array ( 'addPgField' , 'archive' , 'ar_len' , 'INTEGER' ),
array ( 'addPgField' , 'archive' , 'ar_page_id' , 'INTEGER' ),
array ( 'addPgField' , 'archive' , 'ar_parent_id' , 'INTEGER' ),
2010-09-08 17:51:42 +00:00
array ( 'addPgField' , 'categorylinks' , 'cl_sortkey_prefix' , " TEXT NOT NULL DEFAULT '' " ),
array ( 'addPgField' , 'categorylinks' , 'cl_collation' , " TEXT NOT NULL DEFAULT 0 " ),
array ( 'addPgField' , 'categorylinks' , 'cl_type' , " TEXT NOT NULL DEFAULT 'page' " ),
2010-08-22 08:07:26 +00:00
array ( 'addPgField' , 'image' , 'img_sha1' , " TEXT NOT NULL DEFAULT '' " ),
array ( 'addPgField' , 'ipblocks' , 'ipb_allow_usertalk' , 'SMALLINT NOT NULL DEFAULT 0' ),
array ( 'addPgField' , 'ipblocks' , 'ipb_anon_only' , 'SMALLINT NOT NULL DEFAULT 0' ),
array ( 'addPgField' , 'ipblocks' , 'ipb_by_text' , " TEXT NOT NULL DEFAULT '' " ),
array ( 'addPgField' , 'ipblocks' , 'ipb_block_email' , 'SMALLINT NOT NULL DEFAULT 0' ),
array ( 'addPgField' , 'ipblocks' , 'ipb_create_account' , 'SMALLINT NOT NULL DEFAULT 1' ),
array ( 'addPgField' , 'ipblocks' , 'ipb_deleted' , 'SMALLINT NOT NULL DEFAULT 0' ),
array ( 'addPgField' , 'ipblocks' , 'ipb_enable_autoblock' , 'SMALLINT NOT NULL DEFAULT 1' ),
array ( 'addPgField' , 'filearchive' , 'fa_deleted' , 'SMALLINT NOT NULL DEFAULT 0' ),
array ( 'addPgField' , 'logging' , 'log_deleted' , 'SMALLINT NOT NULL DEFAULT 0' ),
array ( 'addPgField' , 'logging' , 'log_id' , " INTEGER NOT NULL PRIMARY KEY DEFAULT nextval('logging_log_id_seq') " ),
array ( 'addPgField' , 'logging' , 'log_params' , 'TEXT' ),
array ( 'addPgField' , 'mwuser' , 'user_editcount' , 'INTEGER' ),
array ( 'addPgField' , 'mwuser' , 'user_newpass_time' , 'TIMESTAMPTZ' ),
array ( 'addPgField' , 'oldimage' , 'oi_deleted' , 'SMALLINT NOT NULL DEFAULT 0' ),
array ( 'addPgField' , 'oldimage' , 'oi_major_mime' , " TEXT NOT NULL DEFAULT 'unknown' " ),
array ( 'addPgField' , 'oldimage' , 'oi_media_type' , 'TEXT' ),
array ( 'addPgField' , 'oldimage' , 'oi_metadata' , " BYTEA NOT NULL DEFAULT '' " ),
array ( 'addPgField' , 'oldimage' , 'oi_minor_mime' , " TEXT NOT NULL DEFAULT 'unknown' " ),
array ( 'addPgField' , 'oldimage' , 'oi_sha1' , " TEXT NOT NULL DEFAULT '' " ),
array ( 'addPgField' , 'page_restrictions' , 'pr_id' , " INTEGER NOT NULL UNIQUE DEFAULT nextval('page_restrictions_pr_id_seq') " ),
array ( 'addPgField' , 'profiling' , 'pf_memory' , 'NUMERIC(18,10) NOT NULL DEFAULT 0' ),
array ( 'addPgField' , 'recentchanges' , 'rc_deleted' , 'SMALLINT NOT NULL DEFAULT 0' ),
array ( 'addPgField' , 'recentchanges' , 'rc_log_action' , 'TEXT' ),
array ( 'addPgField' , 'recentchanges' , 'rc_log_type' , 'TEXT' ),
array ( 'addPgField' , 'recentchanges' , 'rc_logid' , 'INTEGER NOT NULL DEFAULT 0' ),
array ( 'addPgField' , 'recentchanges' , 'rc_new_len' , 'INTEGER' ),
array ( 'addPgField' , 'recentchanges' , 'rc_old_len' , 'INTEGER' ),
array ( 'addPgField' , 'recentchanges' , 'rc_params' , 'TEXT' ),
array ( 'addPgField' , 'redirect' , 'rd_interwiki' , 'TEXT NULL' ),
array ( 'addPgField' , 'redirect' , 'rd_fragment' , 'TEXT NULL' ),
array ( 'addPgField' , 'revision' , 'rev_deleted' , 'SMALLINT NOT NULL DEFAULT 0' ),
array ( 'addPgField' , 'revision' , 'rev_len' , 'INTEGER' ),
array ( 'addPgField' , 'revision' , 'rev_parent_id' , 'INTEGER DEFAULT NULL' ),
array ( 'addPgField' , 'site_stats' , 'ss_active_users' , " INTEGER DEFAULT '-1' " ),
array ( 'addPgField' , 'user_newtalk' , 'user_last_timestamp' , 'TIMESTAMPTZ' ),
array ( 'addPgField' , 'logging' , 'log_user_text' , " TEXT NOT NULL DEFAULT '' " ),
array ( 'addPgField' , 'logging' , 'log_page' , 'INTEGER' ),
array ( 'addPgField' , 'interwiki' , 'iw_api' , " TEXT NOT NULL DEFAULT '' " ),
array ( 'addPgField' , 'interwiki' , 'iw_wikiid' , " TEXT NOT NULL DEFAULT '' " ),
2011-11-01 19:02:13 +00:00
array ( 'addPgField' , 'revision' , 'rev_sha1' , " TEXT NOT NULL DEFAULT '' " ),
array ( 'addPgField' , 'archive' , 'ar_sha1' , " TEXT NOT NULL DEFAULT '' " ),
2012-01-12 17:10:48 +00:00
array ( 'addPgField' , 'uploadstash' , 'us_chunk_inx' , " INTEGER NULL " ),
2012-01-12 22:11:12 +00:00
array ( 'addPgField' , 'job' , 'job_timestamp' , " TIMESTAMPTZ " ),
2010-08-22 08:07:26 +00:00
# type changes
array ( 'changeField' , 'archive' , 'ar_deleted' , 'smallint' , '' ),
array ( 'changeField' , 'archive' , 'ar_minor_edit' , 'smallint' , 'ar_minor_edit::smallint DEFAULT 0' ),
array ( 'changeField' , 'filearchive' , 'fa_deleted' , 'smallint' , '' ),
array ( 'changeField' , 'filearchive' , 'fa_height' , 'integer' , '' ),
array ( 'changeField' , 'filearchive' , 'fa_metadata' , 'bytea' , " decode(fa_metadata,'escape') " ),
array ( 'changeField' , 'filearchive' , 'fa_size' , 'integer' , '' ),
array ( 'changeField' , 'filearchive' , 'fa_width' , 'integer' , '' ),
array ( 'changeField' , 'filearchive' , 'fa_storage_group' , 'text' , '' ),
array ( 'changeField' , 'filearchive' , 'fa_storage_key' , 'text' , '' ),
array ( 'changeField' , 'image' , 'img_metadata' , 'bytea' , " decode(img_metadata,'escape') " ),
array ( 'changeField' , 'image' , 'img_size' , 'integer' , '' ),
array ( 'changeField' , 'image' , 'img_width' , 'integer' , '' ),
array ( 'changeField' , 'image' , 'img_height' , 'integer' , '' ),
array ( 'changeField' , 'interwiki' , 'iw_local' , 'smallint' , 'iw_local::smallint DEFAULT 0' ),
array ( 'changeField' , 'interwiki' , 'iw_trans' , 'smallint' , 'iw_trans::smallint DEFAULT 0' ),
array ( 'changeField' , 'ipblocks' , 'ipb_auto' , 'smallint' , 'ipb_auto::smallint DEFAULT 0' ),
array ( 'changeField' , 'ipblocks' , 'ipb_anon_only' , 'smallint' , " CASE WHEN ipb_anon_only=' ' THEN 0 ELSE ipb_anon_only::smallint END DEFAULT 0 " ),
array ( 'changeField' , 'ipblocks' , 'ipb_create_account' , 'smallint' , " CASE WHEN ipb_create_account=' ' THEN 0 ELSE ipb_create_account::smallint END DEFAULT 1 " ),
array ( 'changeField' , 'ipblocks' , 'ipb_enable_autoblock' , 'smallint' , " CASE WHEN ipb_enable_autoblock=' ' THEN 0 ELSE ipb_enable_autoblock::smallint END DEFAULT 1 " ),
array ( 'changeField' , 'ipblocks' , 'ipb_block_email' , 'smallint' , " CASE WHEN ipb_block_email=' ' THEN 0 ELSE ipb_block_email::smallint END DEFAULT 0 " ),
array ( 'changeField' , 'ipblocks' , 'ipb_address' , 'text' , 'ipb_address::text' ),
array ( 'changeField' , 'ipblocks' , 'ipb_deleted' , 'smallint' , 'ipb_deleted::smallint DEFAULT 0' ),
array ( 'changeField' , 'mwuser' , 'user_token' , 'text' , '' ),
array ( 'changeField' , 'mwuser' , 'user_email_token' , 'text' , '' ),
array ( 'changeField' , 'objectcache' , 'keyname' , 'text' , '' ),
array ( 'changeField' , 'oldimage' , 'oi_height' , 'integer' , '' ),
array ( 'changeField' , 'oldimage' , 'oi_metadata' , 'bytea' , " decode(img_metadata,'escape') " ),
array ( 'changeField' , 'oldimage' , 'oi_size' , 'integer' , '' ),
array ( 'changeField' , 'oldimage' , 'oi_width' , 'integer' , '' ),
array ( 'changeField' , 'page' , 'page_is_redirect' , 'smallint' , 'page_is_redirect::smallint DEFAULT 0' ),
array ( 'changeField' , 'page' , 'page_is_new' , 'smallint' , 'page_is_new::smallint DEFAULT 0' ),
array ( 'changeField' , 'querycache' , 'qc_value' , 'integer' , '' ),
array ( 'changeField' , 'querycachetwo' , 'qcc_value' , 'integer' , '' ),
array ( 'changeField' , 'recentchanges' , 'rc_bot' , 'smallint' , 'rc_bot::smallint DEFAULT 0' ),
array ( 'changeField' , 'recentchanges' , 'rc_deleted' , 'smallint' , '' ),
array ( 'changeField' , 'recentchanges' , 'rc_minor' , 'smallint' , 'rc_minor::smallint DEFAULT 0' ),
array ( 'changeField' , 'recentchanges' , 'rc_new' , 'smallint' , 'rc_new::smallint DEFAULT 0' ),
array ( 'changeField' , 'recentchanges' , 'rc_type' , 'smallint' , 'rc_type::smallint DEFAULT 0' ),
array ( 'changeField' , 'recentchanges' , 'rc_patrolled' , 'smallint' , 'rc_patrolled::smallint DEFAULT 0' ),
array ( 'changeField' , 'revision' , 'rev_deleted' , 'smallint' , 'rev_deleted::smallint DEFAULT 0' ),
array ( 'changeField' , 'revision' , 'rev_minor_edit' , 'smallint' , 'rev_minor_edit::smallint DEFAULT 0' ),
array ( 'changeField' , 'templatelinks' , 'tl_namespace' , 'smallint' , 'tl_namespace::smallint' ),
array ( 'changeField' , 'user_newtalk' , 'user_ip' , 'text' , 'host(user_ip)' ),
# null changes
array ( 'changeNullableField' , 'oldimage' , 'oi_bits' , 'NULL' ),
array ( 'changeNullableField' , 'oldimage' , 'oi_timestamp' , 'NULL' ),
array ( 'changeNullableField' , 'oldimage' , 'oi_major_mime' , 'NULL' ),
array ( 'changeNullableField' , 'oldimage' , 'oi_minor_mime' , 'NULL' ),
array ( 'checkOiDeleted' ),
# New indexes
array ( 'addPgIndex' , 'archive' , 'archive_user_text' , '(ar_user_text)' ),
array ( 'addPgIndex' , 'image' , 'img_sha1' , '(img_sha1)' ),
array ( 'addPgIndex' , 'oldimage' , 'oi_sha1' , '(oi_sha1)' ),
array ( 'addPgIndex' , 'page' , 'page_mediawiki_title' , '(page_title) WHERE page_namespace = 8' ),
2010-09-13 15:04:04 +00:00
array ( 'addPgIndex' , 'pagelinks' , 'pagelinks_title' , '(pl_title)' ),
2010-08-22 08:07:26 +00:00
array ( 'addPgIndex' , 'revision' , 'rev_text_id_idx' , '(rev_text_id)' ),
array ( 'addPgIndex' , 'recentchanges' , 'rc_timestamp_bot' , '(rc_timestamp) WHERE rc_bot = 0' ),
array ( 'addPgIndex' , 'templatelinks' , 'templatelinks_from' , '(tl_from)' ),
array ( 'addPgIndex' , 'watchlist' , 'wl_user' , '(wl_user)' ),
array ( 'addPgIndex' , 'logging' , 'logging_user_type_time' , '(log_user, log_type, log_timestamp)' ),
array ( 'addPgIndex' , 'logging' , 'logging_page_id_time' , '(log_page,log_timestamp)' ),
array ( 'addPgIndex' , 'iwlinks' , 'iwl_prefix_title_from' , '(iwl_prefix, iwl_title, iwl_from)' ),
2012-01-12 22:11:12 +00:00
array ( 'addPgIndex' , 'job' , 'job_timestamp_idx' , '(job_timestamp)' ),
2010-08-22 08:07:26 +00:00
array ( 'checkOiNameConstraint' ),
array ( 'checkPageDeletedTrigger' ),
array ( 'checkRcCurIdNullable' ),
array ( 'checkPagelinkUniqueIndex' ),
array ( 'checkRevUserFkey' ),
array ( 'checkIpbAdress' ),
array ( 'checkIwlPrefix' ),
# All FK columns should be deferred
array ( 'changeFkeyDeferrable' , 'archive' , 'ar_user' , 'mwuser(user_id) ON DELETE SET NULL' ),
array ( 'changeFkeyDeferrable' , 'categorylinks' , 'cl_from' , 'page(page_id) ON DELETE CASCADE' ),
array ( 'changeFkeyDeferrable' , 'externallinks' , 'el_from' , 'page(page_id) ON DELETE CASCADE' ),
array ( 'changeFkeyDeferrable' , 'filearchive' , 'fa_deleted_user' , 'mwuser(user_id) ON DELETE SET NULL' ),
array ( 'changeFkeyDeferrable' , 'filearchive' , 'fa_user' , 'mwuser(user_id) ON DELETE SET NULL' ),
array ( 'changeFkeyDeferrable' , 'image' , 'img_user' , 'mwuser(user_id) ON DELETE SET NULL' ),
array ( 'changeFkeyDeferrable' , 'imagelinks' , 'il_from' , 'page(page_id) ON DELETE CASCADE' ),
array ( 'changeFkeyDeferrable' , 'ipblocks' , 'ipb_by' , 'mwuser(user_id) ON DELETE CASCADE' ),
array ( 'changeFkeyDeferrable' , 'ipblocks' , 'ipb_user' , 'mwuser(user_id) ON DELETE SET NULL' ),
array ( 'changeFkeyDeferrable' , 'langlinks' , 'll_from' , 'page(page_id) ON DELETE CASCADE' ),
array ( 'changeFkeyDeferrable' , 'logging' , 'log_user' , 'mwuser(user_id) ON DELETE SET NULL' ),
array ( 'changeFkeyDeferrable' , 'oldimage' , 'oi_name' , 'image(img_name) ON DELETE CASCADE ON UPDATE CASCADE' ),
array ( 'changeFkeyDeferrable' , 'oldimage' , 'oi_user' , 'mwuser(user_id) ON DELETE SET NULL' ),
array ( 'changeFkeyDeferrable' , 'pagelinks' , 'pl_from' , 'page(page_id) ON DELETE CASCADE' ),
array ( 'changeFkeyDeferrable' , 'page_props' , 'pp_page' , 'page (page_id) ON DELETE CASCADE' ),
array ( 'changeFkeyDeferrable' , 'page_restrictions' , 'pr_page' , 'page(page_id) ON DELETE CASCADE' ),
array ( 'changeFkeyDeferrable' , 'protected_titles' , 'pt_user' , 'mwuser(user_id) ON DELETE SET NULL' ),
array ( 'changeFkeyDeferrable' , 'recentchanges' , 'rc_cur_id' , 'page(page_id) ON DELETE SET NULL' ),
array ( 'changeFkeyDeferrable' , 'recentchanges' , 'rc_user' , 'mwuser(user_id) ON DELETE SET NULL' ),
array ( 'changeFkeyDeferrable' , 'redirect' , 'rd_from' , 'page(page_id) ON DELETE CASCADE' ),
array ( 'changeFkeyDeferrable' , 'revision' , 'rev_page' , 'page (page_id) ON DELETE CASCADE' ),
array ( 'changeFkeyDeferrable' , 'revision' , 'rev_user' , 'mwuser(user_id) ON DELETE RESTRICT' ),
array ( 'changeFkeyDeferrable' , 'templatelinks' , 'tl_from' , 'page(page_id) ON DELETE CASCADE' ),
array ( 'changeFkeyDeferrable' , 'user_groups' , 'ug_user' , 'mwuser(user_id) ON DELETE CASCADE' ),
array ( 'changeFkeyDeferrable' , 'user_newtalk' , 'user_id' , 'mwuser(user_id) ON DELETE CASCADE' ),
array ( 'changeFkeyDeferrable' , 'user_properties' , 'up_user' , 'mwuser(user_id) ON DELETE CASCADE' ),
array ( 'changeFkeyDeferrable' , 'watchlist' , 'wl_user' , 'mwuser(user_id) ON DELETE CASCADE' ),
# end
array ( 'tsearchFixes' ),
);
}
protected function getOldGlobalUpdates () {
global $wgExtNewTables , $wgExtPGNewFields , $wgExtPGAlteredFields , $wgExtNewIndexes ;
$updates = array ();
# Add missing extension tables
foreach ( $wgExtNewTables as $tableRecord ) {
$updates [] = array (
'addTable' , $tableRecord [ 0 ], $tableRecord [ 1 ], true
);
}
# Add missing extension fields
2010-10-14 20:31:34 +00:00
foreach ( $wgExtPGNewFields as $fieldRecord ) {
2010-08-22 08:07:26 +00:00
$updates [] = array (
'addPgField' , $fieldRecord [ 0 ], $fieldRecord [ 1 ],
$fieldRecord [ 2 ]
);
}
# Change altered columns
foreach ( $wgExtPGAlteredFields as $fieldRecord ) {
$updates [] = array (
'changeField' , $fieldRecord [ 0 ], $fieldRecord [ 1 ],
$fieldRecord [ 2 ]
);
}
# Add missing extension indexes
2010-10-14 20:34:01 +00:00
foreach ( $wgExtNewIndexes as $fieldRecord ) {
2010-08-22 08:07:26 +00:00
$updates [] = array (
'addPgExtIndex' , $fieldRecord [ 0 ], $fieldRecord [ 1 ],
$fieldRecord [ 2 ]
);
}
2010-12-01 20:15:45 +00:00
2010-08-22 08:07:26 +00:00
return $updates ;
}
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 ),
2012-03-09 17:24:57 +00:00
$this -> db -> addQuotes ( $this -> db -> getCoreSchema () ) ) );
2010-08-22 08:07:26 +00:00
if ( ! $res ) {
return null ;
}
$cols = array ();
2010-10-13 23:11:40 +00:00
foreach ( $res as $r ) {
2010-08-22 08:07:26 +00:00
$cols [] = array (
" name " => $r [ 0 ],
" ord " => $r [ 1 ],
);
}
return $cols ;
}
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 ,
2012-03-09 17:24:57 +00:00
$this -> db -> addQuotes ( $this -> db -> getCoreSchea () ),
2010-08-22 08:07:26 +00:00
$this -> db -> addQuotes ( $idx )
)
);
if ( ! $res ) {
return null ;
}
if ( ! ( $r = $this -> db -> fetchRow ( $res ) ) ) {
return null ;
}
$indkey = $r [ 0 ];
$relid = intval ( $r [ 1 ] );
$indkeys = explode ( ' ' , $indkey );
$colnames = array ();
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 ) );
if ( ! $r2 ) {
return null ;
}
if ( ! ( $row2 = $this -> db -> fetchRow ( $r2 ) ) ) {
return null ;
}
$colnames [] = $row2 [ 0 ];
}
return $colnames ;
}
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 ,
2012-03-09 17:24:57 +00:00
$this -> db -> addQuotes ( $this -> db -> getCoreSchema () ),
2010-08-22 08:07:26 +00:00
$this -> db -> addQuotes ( $fkey )
)
);
if ( ! ( $row = $this -> db -> fetchRow ( $r ) ) ) {
return null ;
}
return $row [ 0 ];
}
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 ,
2012-03-09 17:24:57 +00:00
$this -> db -> addQuotes ( $this -> db -> getCoreSchema () ),
2010-08-22 08:07:26 +00:00
$this -> db -> addQuotes ( $table ),
$this -> db -> addQuotes ( $rule )
)
);
$row = $this -> db -> fetchRow ( $r );
if ( ! $row ) {
return null ;
}
$d = $row [ 0 ];
return $d ;
}
protected function addSequence ( $ns ) {
if ( ! $this -> db -> sequenceExists ( $ns ) ) {
2010-10-01 15:11:06 +00:00
$this -> output ( " Creating sequence $ns\n " );
2010-08-22 08:07:26 +00:00
$this -> db -> query ( " CREATE SEQUENCE $ns " );
}
}
protected function renameSequence ( $old , $new ) {
2011-11-20 17:54:47 +00:00
if ( $this -> db -> sequenceExists ( $new ) ) {
$this -> output ( " WARNING sequence $new already exists \n " );
return ;
}
2010-08-22 08:07:26 +00:00
if ( $this -> db -> sequenceExists ( $old ) ) {
2010-10-01 15:11:06 +00:00
$this -> output ( " Renaming sequence $old to $new\n " );
2010-08-22 08:07:26 +00:00
$this -> db -> query ( " ALTER SEQUENCE $old RENAME TO $new " );
}
}
2010-12-01 20:15:45 +00:00
protected function renameTable ( $old , $new ) {
if ( $this -> db -> tableExists ( $old ) ) {
$this -> output ( " Renaming table $old to $new\n " );
2011-02-23 16:42:42 +00:00
$old = $this -> db -> addQuotes ( $old );
$this -> db -> query ( " ALTER TABLE $old RENAME TO $new " );
2010-12-01 20:15:45 +00:00
}
}
2010-08-22 08:07:26 +00:00
protected function addPgField ( $table , $field , $type ) {
$fi = $this -> db -> fieldInfo ( $table , $field );
if ( ! is_null ( $fi ) ) {
2011-02-04 20:05:05 +00:00
$this -> output ( " ... column ' $table . $field ' already exists \n " );
2010-08-22 08:07:26 +00:00
return ;
} else {
2011-02-04 20:05:05 +00:00
$this -> output ( " Adding column ' $table . $field ' \n " );
2010-08-22 08:07:26 +00:00
$this -> db -> query ( " ALTER TABLE $table ADD $field $type " );
}
2010-08-15 18:55:08 +00:00
}
2010-08-22 08:07:26 +00:00
protected function changeField ( $table , $field , $newtype , $default ) {
$fi = $this -> db -> fieldInfo ( $table , $field );
if ( is_null ( $fi ) ) {
2010-10-01 15:11:06 +00:00
$this -> output ( " ... error: expected column $table . $field to exist \n " );
2010-08-22 08:07:26 +00:00
exit ( 1 );
}
if ( $fi -> type () === $newtype )
2011-02-04 20:05:05 +00:00
$this -> output ( " ... column ' $table . $field ' is already of type ' $newtype ' \n " );
2010-08-22 08:07:26 +00:00
else {
2011-02-04 20:05:05 +00:00
$this -> output ( " Changing column type of ' $table . $field ' from ' { $fi -> type () } ' to ' $newtype ' \n " );
2010-08-22 08:07:26 +00:00
$sql = " ALTER TABLE $table ALTER $field TYPE $newtype " ;
if ( strlen ( $default ) ) {
$res = array ();
if ( preg_match ( '/DEFAULT (.+)/' , $default , $res ) ) {
$sqldef = " ALTER TABLE $table ALTER $field SET DEFAULT $res[1] " ;
$this -> db -> query ( $sqldef );
$default = preg_replace ( '/\s*DEFAULT .+/' , '' , $default );
}
$sql .= " USING $default " ;
}
2011-10-11 21:54:10 +00:00
$this -> db -> begin ( __METHOD__ );
2010-08-22 08:07:26 +00:00
$this -> db -> query ( $sql );
2011-10-11 21:54:10 +00:00
$this -> db -> commit ( __METHOD__ );
2010-08-22 08:07:26 +00:00
}
}
protected function changeNullableField ( $table , $field , $null ) {
$fi = $this -> db -> fieldInfo ( $table , $field );
if ( is_null ( $fi ) ) {
2010-10-01 15:11:06 +00:00
$this -> output ( " ... error: expected column $table . $field to exist \n " );
2010-08-22 08:07:26 +00:00
exit ( 1 );
}
2010-11-21 19:56:51 +00:00
if ( $fi -> isNullable () ) {
2010-08-22 08:07:26 +00:00
# # It's NULL - does it need to be NOT NULL?
if ( 'NOT NULL' === $null ) {
2011-02-04 20:05:05 +00:00
$this -> output ( " Changing ' $table . $field ' to not allow NULLs \n " );
2010-08-22 08:07:26 +00:00
$this -> db -> query ( " ALTER TABLE $table ALTER $field SET NOT NULL " );
} else {
2011-02-04 20:05:05 +00:00
$this -> output ( " ... column ' $table . $field ' is already set as NULL \n " );
2010-08-22 08:07:26 +00:00
}
} else {
# # It's NOT NULL - does it need to be NULL?
if ( 'NULL' === $null ) {
2011-02-04 20:05:05 +00:00
$this -> output ( " Changing ' $table . $field ' to allow NULLs \n " );
2010-08-22 08:07:26 +00:00
$this -> db -> query ( " ALTER TABLE $table ALTER $field DROP NOT NULL " );
}
else {
2011-02-04 20:05:05 +00:00
$this -> output ( " ... column ' $table . $field ' is already set as NOT NULL \n " );
2010-08-22 08:07:26 +00:00
}
}
}
public function addPgIndex ( $table , $index , $type ) {
if ( $this -> db -> indexExists ( $table , $index ) ) {
2011-02-04 20:05:05 +00:00
$this -> output ( " ... index ' $index ' on table ' $table ' already exists \n " );
2010-08-22 08:07:26 +00:00
} else {
2011-02-04 20:05:05 +00:00
$this -> output ( " Creating index ' $index ' on table ' $table ' $type\n " );
2010-08-22 08:07:26 +00:00
$this -> db -> query ( " CREATE INDEX $index ON $table $type " );
}
}
public function addPgExtIndex ( $table , $index , $type ) {
2010-08-22 08:32:29 +00:00
if ( $this -> db -> indexExists ( $table , $index ) ) {
2011-02-04 20:05:05 +00:00
$this -> output ( " ... index ' $index ' on table ' $table ' already exists \n " );
2010-08-22 08:07:26 +00:00
} else {
2011-02-04 20:05:05 +00:00
$this -> output ( " Creating index ' $index ' on table ' $table ' \n " );
2010-08-22 08:07:26 +00:00
if ( preg_match ( '/^\(/' , $type ) ) {
$this -> db -> query ( " CREATE INDEX $index ON $table $type " );
} else {
$this -> applyPatch ( $type , true );
}
}
}
protected function changeFkeyDeferrable ( $table , $field , $clause ) {
$fi = $this -> db -> fieldInfo ( $table , $field );
if ( is_null ( $fi ) ) {
2011-02-04 20:05:05 +00:00
$this -> output ( " WARNING! Column ' $table . $field ' does not exist but it should! Please report this. \n " );
2010-08-22 08:07:26 +00:00
return ;
}
if ( $fi -> is_deferred () && $fi -> is_deferrable () ) {
return ;
}
2011-02-04 20:05:05 +00:00
$this -> output ( " Altering column ' $table . $field ' to be DEFERRABLE INITIALLY DEFERRED \n " );
2010-08-22 08:07:26 +00:00
$conname = $fi -> conname ();
$command = " ALTER TABLE $table DROP CONSTRAINT $conname " ;
$this -> db -> query ( $command );
$command = " ALTER TABLE $table ADD CONSTRAINT $conname FOREIGN KEY ( $field ) REFERENCES $clause DEFERRABLE INITIALLY DEFERRED " ;
$this -> db -> query ( $command );
}
protected function convertArchive2 () {
if ( $this -> db -> tableExists ( " archive2 " ) ) {
2011-02-04 20:05:05 +00:00
$this -> output ( " Converting 'archive2' back to normal archive table \n " );
2010-08-22 08:07:26 +00:00
if ( $this -> db -> ruleExists ( 'archive' , 'archive_insert' ) ) {
2011-02-04 20:05:05 +00:00
$this -> output ( " Dropping rule 'archive_insert' \n " );
2010-08-22 08:07:26 +00:00
$this -> db -> query ( 'DROP RULE archive_insert ON archive' );
}
if ( $this -> db -> ruleExists ( 'archive' , 'archive_delete' ) ) {
2011-02-04 20:05:05 +00:00
$this -> output ( " Dropping rule 'archive_delete' \n " );
2010-08-22 08:07:26 +00:00
$this -> db -> query ( 'DROP RULE archive_delete ON archive' );
}
2010-09-01 18:52:06 +00:00
$this -> applyPatch ( 'patch-remove-archive2.sql' );
2010-08-22 08:07:26 +00:00
} else {
2011-02-04 20:05:05 +00:00
$this -> output ( " ... obsolete table 'archive2' does not exist \n " );
2010-08-22 08:07:26 +00:00
}
}
protected function checkOiDeleted () {
if ( $this -> db -> fieldInfo ( 'oldimage' , 'oi_deleted' ) -> type () !== 'smallint' ) {
2011-02-04 20:05:05 +00:00
$this -> output ( " Changing 'oldimage.oi_deleted' to type 'smallint' \n " );
2010-08-22 08:07:26 +00:00
$this -> db -> query ( " ALTER TABLE oldimage ALTER oi_deleted DROP DEFAULT " );
$this -> db -> query ( " ALTER TABLE oldimage ALTER oi_deleted TYPE SMALLINT USING (oi_deleted::smallint) " );
$this -> db -> query ( " ALTER TABLE oldimage ALTER oi_deleted SET DEFAULT 0 " );
} else {
2011-02-04 20:05:05 +00:00
$this -> output ( " ... column 'oldimage.oi_deleted' is already of type 'smallint' \n " );
2010-08-22 08:07:26 +00:00
}
}
protected function checkOiNameConstraint () {
if ( $this -> db -> hasConstraint ( " oldimage_oi_name_fkey_cascaded " ) ) {
2011-02-04 20:05:05 +00:00
$this -> output ( " ... table 'oldimage' has correct cascading delete/update foreign key to image \n " );
2010-08-22 08:07:26 +00:00
} else {
if ( $this -> db -> hasConstraint ( " oldimage_oi_name_fkey " ) ) {
$this -> db -> query ( " ALTER TABLE oldimage DROP CONSTRAINT oldimage_oi_name_fkey " );
}
if ( $this -> db -> hasConstraint ( " oldimage_oi_name_fkey_cascade " ) ) {
$this -> db -> query ( " ALTER TABLE oldimage DROP CONSTRAINT oldimage_oi_name_fkey_cascade " );
}
2011-02-04 20:05:05 +00:00
$this -> output ( " Making foreign key on table 'oldimage' (to image) a cascade delete/update \n " );
2010-08-22 08:07:26 +00:00
$this -> db -> query ( " ALTER TABLE oldimage ADD CONSTRAINT oldimage_oi_name_fkey_cascaded " .
" FOREIGN KEY (oi_name) REFERENCES image(img_name) ON DELETE CASCADE ON UPDATE CASCADE " );
}
}
protected function checkPageDeletedTrigger () {
if ( ! $this -> db -> triggerExists ( 'page' , 'page_deleted' ) ) {
2011-02-04 20:05:05 +00:00
$this -> output ( " Adding function and trigger 'page_deleted' to table 'page' \n " );
2010-08-22 08:07:26 +00:00
$this -> applyPatch ( 'patch-page_deleted.sql' );
} else {
2011-02-04 20:05:05 +00:00
$this -> output ( " ... table 'page' has 'page_deleted' trigger \n " );
2010-08-22 08:07:26 +00:00
}
}
protected function checkRcCurIdNullable (){
$fi = $this -> db -> fieldInfo ( 'recentchanges' , 'rc_cur_id' );
2010-11-21 19:56:51 +00:00
if ( ! $fi -> isNullable () ) {
2011-02-04 20:05:05 +00:00
$this -> output ( " Removing NOT NULL constraint from 'recentchanges.rc_cur_id' \n " );
2010-08-22 08:07:26 +00:00
$this -> applyPatch ( 'patch-rc_cur_id-not-null.sql' );
} else {
2011-02-04 20:05:05 +00:00
$this -> output ( " ... column 'recentchanges.rc_cur_id' has a NOT NULL constraint \n " );
2010-08-22 08:07:26 +00:00
}
}
protected function checkPagelinkUniqueIndex () {
$pu = $this -> describeIndex ( 'pagelink_unique' );
if ( ! is_null ( $pu ) && ( $pu [ 0 ] != 'pl_from' || $pu [ 1 ] != 'pl_namespace' || $pu [ 2 ] != 'pl_title' ) ) {
2011-02-04 20:05:05 +00:00
$this -> output ( " Dropping obsolete version of index 'pagelink_unique index' \n " );
2010-08-22 08:07:26 +00:00
$this -> db -> query ( 'DROP INDEX pagelink_unique' );
$pu = null ;
} else {
2011-02-04 20:05:05 +00:00
$this -> output ( " ... obsolete version of index 'pagelink_unique index' does not exist \n " );
2010-08-22 08:07:26 +00:00
}
if ( is_null ( $pu ) ) {
2011-02-04 20:05:05 +00:00
$this -> output ( " Creating index 'pagelink_unique index' \n " );
2010-08-22 08:07:26 +00:00
$this -> db -> query ( 'CREATE UNIQUE INDEX pagelink_unique ON pagelinks (pl_from,pl_namespace,pl_title)' );
} else {
2011-02-04 20:05:05 +00:00
$this -> output ( " ... index 'pagelink_unique_index' already exists \n " );
2010-08-22 08:07:26 +00:00
}
}
protected function checkRevUserFkey () {
if ( $this -> fkeyDeltype ( 'revision_rev_user_fkey' ) == 'r' ) {
2011-02-04 20:05:05 +00:00
$this -> output ( " ... constraint 'revision_rev_user_fkey' is ON DELETE RESTRICT \n " );
2010-08-22 08:07:26 +00:00
} else {
2011-02-04 20:05:05 +00:00
$this -> output ( " Changing constraint 'revision_rev_user_fkey' to ON DELETE RESTRICT \n " );
2010-08-22 08:07:26 +00:00
$this -> applyPatch ( 'patch-revision_rev_user_fkey.sql' );
}
}
protected function checkIpbAdress () {
if ( $this -> db -> indexExists ( 'ipblocks' , 'ipb_address' ) ) {
2010-10-01 15:11:06 +00:00
$this -> output ( " Removing deprecated index 'ipb_address'... \n " );
2010-08-22 08:07:26 +00:00
$this -> db -> query ( 'DROP INDEX ipb_address' );
}
if ( $this -> db -> indexExists ( 'ipblocks' , 'ipb_address_unique' ) ) {
2010-10-01 15:11:06 +00:00
$this -> output ( " ... have ipb_address_unique \n " );
2010-08-22 08:07:26 +00:00
} else {
2010-10-01 15:11:06 +00:00
$this -> output ( " Adding ipb_address_unique index \n " );
2010-08-22 08:07:26 +00:00
$this -> applyPatch ( 'patch-ipb_address_unique.sql' );
}
}
protected function checkIwlPrefix () {
if ( $this -> db -> indexExists ( 'iwlinks' , 'iwl_prefix' ) ) {
2010-10-01 15:11:06 +00:00
$this -> output ( " Replacing index 'iwl_prefix' with 'iwl_prefix_from_title'... \n " );
2010-08-22 08:07:26 +00:00
$this -> applyPatch ( 'patch-rename-iwl_prefix.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
2011-12-27 11:26:24 +00:00
$this -> output ( " Refreshing ts2_page_title()... \n " );
2010-08-22 08:07:26 +00:00
$this -> applyPatch ( 'patch-ts2pagetitle.sql' );
2010-12-14 12:47:37 +00:00
# If the server is 8.3 or higher, rewrite the tsearch2 triggers
# in case they have the old 'default' versions
2010-08-22 08:07:26 +00:00
# Gather version numbers in case we need them
if ( $this -> db -> getServerVersion () >= 8.3 ) {
2011-12-27 11:26:24 +00:00
$this -> output ( " Rewriting tsearch2 triggers... \n " );
2010-08-22 08:07:26 +00:00
$this -> applyPatch ( 'patch-tsearch2funcs.sql' );
}
2010-08-15 18:55:08 +00:00
}
}