MediaWiki  REL1_19
PostgresUpdater.php
Go to the documentation of this file.
00001 <?php
00016 class PostgresUpdater extends DatabaseUpdater {
00017 
00021         protected $db;
00022 
00027         protected function getCoreUpdateList() {
00028                 return array(
00029                         # rename tables 1.7.3
00030                         # r15791 Change reserved word table names "user" and "text"
00031                         array( 'renameTable', 'user', 'mwuser'),
00032                         array( 'renameTable', 'text', 'pagecontent'),
00033 
00034                         # new sequences
00035                         array( 'addSequence', 'logging_log_id_seq'          ),
00036                         array( 'addSequence', 'page_restrictions_pr_id_seq' ),
00037 
00038                         # renamed sequences
00039                         array( 'renameSequence', 'ipblocks_ipb_id_val', 'ipblocks_ipb_id_seq'         ),
00040                         array( 'renameSequence', 'rev_rev_id_val',      'revision_rev_id_seq'         ),
00041                         array( 'renameSequence', 'text_old_id_val',     'text_old_id_seq'             ),
00042                         array( 'renameSequence', 'category_id_seq',     'category_cat_id_seq'         ),
00043                         array( 'renameSequence', 'rc_rc_id_seq',        'recentchanges_rc_id_seq'     ),
00044                         array( 'renameSequence', 'log_log_id_seq',      'logging_log_id_seq'          ),
00045                         array( 'renameSequence', 'pr_id_val',           'page_restrictions_pr_id_seq' ),
00046                         array( 'renameSequence', 'us_id_seq',           'uploadstash_us_id_seq' ),
00047 
00048                         # new tables
00049                         array( 'addTable', 'category',          'patch-category.sql' ),
00050                         array( 'addTable', 'page',              'patch-page.sql' ),
00051                         array( 'addTable', 'querycachetwo',     'patch-querycachetwo.sql' ),
00052                         array( 'addTable', 'page_props',        'patch-page_props.sql' ),
00053                         array( 'addTable', 'page_restrictions', 'patch-page_restrictions.sql' ),
00054                         array( 'addTable', 'profiling',         'patch-profiling.sql' ),
00055                         array( 'addTable', 'protected_titles',  'patch-protected_titles.sql' ),
00056                         array( 'addTable', 'redirect',          'patch-redirect.sql' ),
00057                         array( 'addTable', 'updatelog',         'patch-updatelog.sql' ),
00058                         array( 'addTable', 'change_tag',        'patch-change_tag.sql' ),
00059                         array( 'addTable', 'tag_summary',       'patch-tag_summary.sql' ),
00060                         array( 'addTable', 'valid_tag',         'patch-valid_tag.sql' ),
00061                         array( 'addTable', 'user_properties',   'patch-user_properties.sql' ),
00062                         array( 'addTable', 'log_search',        'patch-log_search.sql' ),
00063                         array( 'addTable', 'l10n_cache',        'patch-l10n_cache.sql' ),
00064                         array( 'addTable', 'iwlinks',           'patch-iwlinks.sql' ),
00065                         array( 'addTable', 'msg_resource',      'patch-msg_resource.sql' ),
00066                         array( 'addTable', 'msg_resource_links','patch-msg_resource_links.sql' ),
00067                         array( 'addTable', 'module_deps',       'patch-module_deps.sql' ),
00068                         array( 'addTable', 'uploadstash',       'patch-uploadstash.sql' ),
00069                         array( 'addTable', 'user_former_groups','patch-user_former_groups.sql' ),
00070 
00071                         # Needed before new field
00072                         array( 'convertArchive2' ),
00073 
00074                         # new fields
00075                         array( 'addPgField', 'archive',       'ar_deleted',           'SMALLINT NOT NULL DEFAULT 0' ),
00076                         array( 'addPgField', 'archive',       'ar_len',               'INTEGER' ),
00077                         array( 'addPgField', 'archive',       'ar_page_id',           'INTEGER' ),
00078                         array( 'addPgField', 'archive',       'ar_parent_id',         'INTEGER' ),
00079                         array( 'addPgField', 'categorylinks', 'cl_sortkey_prefix',    "TEXT NOT NULL DEFAULT ''"),
00080                         array( 'addPgField', 'categorylinks', 'cl_collation',         "TEXT NOT NULL DEFAULT 0"),
00081                         array( 'addPgField', 'categorylinks', 'cl_type',              "TEXT NOT NULL DEFAULT 'page'"),
00082                         array( 'addPgField', 'image',         'img_sha1',             "TEXT NOT NULL DEFAULT ''" ),
00083                         array( 'addPgField', 'ipblocks',      'ipb_allow_usertalk',   'SMALLINT NOT NULL DEFAULT 0' ),
00084                         array( 'addPgField', 'ipblocks',      'ipb_anon_only',        'SMALLINT NOT NULL DEFAULT 0' ),
00085                         array( 'addPgField', 'ipblocks',      'ipb_by_text',          "TEXT NOT NULL DEFAULT ''" ),
00086                         array( 'addPgField', 'ipblocks',      'ipb_block_email',      'SMALLINT NOT NULL DEFAULT 0' ),
00087                         array( 'addPgField', 'ipblocks',      'ipb_create_account',   'SMALLINT NOT NULL DEFAULT 1' ),
00088                         array( 'addPgField', 'ipblocks',      'ipb_deleted',          'SMALLINT NOT NULL DEFAULT 0' ),
00089                         array( 'addPgField', 'ipblocks',      'ipb_enable_autoblock', 'SMALLINT NOT NULL DEFAULT 1' ),
00090                         array( 'addPgField', 'filearchive',   'fa_deleted',           'SMALLINT NOT NULL DEFAULT 0' ),
00091                         array( 'addPgField', 'logging',       'log_deleted',          'SMALLINT NOT NULL DEFAULT 0' ),
00092                         array( 'addPgField', 'logging',       'log_id',               "INTEGER NOT NULL PRIMARY KEY DEFAULT nextval('logging_log_id_seq')" ),
00093                         array( 'addPgField', 'logging',       'log_params',           'TEXT' ),
00094                         array( 'addPgField', 'mwuser',        'user_editcount',       'INTEGER' ),
00095                         array( 'addPgField', 'mwuser',        'user_newpass_time',    'TIMESTAMPTZ' ),
00096                         array( 'addPgField', 'oldimage',      'oi_deleted',           'SMALLINT NOT NULL DEFAULT 0' ),
00097                         array( 'addPgField', 'oldimage',      'oi_major_mime',        "TEXT NOT NULL DEFAULT 'unknown'" ),
00098                         array( 'addPgField', 'oldimage',      'oi_media_type',        'TEXT' ),
00099                         array( 'addPgField', 'oldimage',      'oi_metadata',          "BYTEA NOT NULL DEFAULT ''" ),
00100                         array( 'addPgField', 'oldimage',      'oi_minor_mime',        "TEXT NOT NULL DEFAULT 'unknown'" ),
00101                         array( 'addPgField', 'oldimage',      'oi_sha1',              "TEXT NOT NULL DEFAULT ''" ),
00102                         array( 'addPgField', 'page_restrictions', 'pr_id',            "INTEGER NOT NULL UNIQUE DEFAULT nextval('page_restrictions_pr_id_seq')" ),
00103                         array( 'addPgField', 'profiling',     'pf_memory',            'NUMERIC(18,10) NOT NULL DEFAULT 0' ),
00104                         array( 'addPgField', 'recentchanges', 'rc_deleted',           'SMALLINT NOT NULL DEFAULT 0' ),
00105                         array( 'addPgField', 'recentchanges', 'rc_log_action',        'TEXT' ),
00106                         array( 'addPgField', 'recentchanges', 'rc_log_type',          'TEXT' ),
00107                         array( 'addPgField', 'recentchanges', 'rc_logid',             'INTEGER NOT NULL DEFAULT 0' ),
00108                         array( 'addPgField', 'recentchanges', 'rc_new_len',           'INTEGER' ),
00109                         array( 'addPgField', 'recentchanges', 'rc_old_len',           'INTEGER' ),
00110                         array( 'addPgField', 'recentchanges', 'rc_params',            'TEXT' ),
00111                         array( 'addPgField', 'redirect',      'rd_interwiki',         'TEXT NULL' ),
00112                         array( 'addPgField', 'redirect',      'rd_fragment',          'TEXT NULL' ),
00113                         array( 'addPgField', 'revision',      'rev_deleted',          'SMALLINT NOT NULL DEFAULT 0' ),
00114                         array( 'addPgField', 'revision',      'rev_len',              'INTEGER' ),
00115                         array( 'addPgField', 'revision',      'rev_parent_id',        'INTEGER DEFAULT NULL' ),
00116                         array( 'addPgField', 'site_stats',    'ss_active_users',      "INTEGER DEFAULT '-1'" ),
00117                         array( 'addPgField', 'user_newtalk',  'user_last_timestamp',  'TIMESTAMPTZ' ),
00118                         array( 'addPgField', 'logging',       'log_user_text',        "TEXT NOT NULL DEFAULT ''" ),
00119                         array( 'addPgField', 'logging',       'log_page',             'INTEGER' ),
00120                         array( 'addPgField', 'interwiki',     'iw_api',               "TEXT NOT NULL DEFAULT ''"),
00121                         array( 'addPgField', 'interwiki',     'iw_wikiid',            "TEXT NOT NULL DEFAULT ''"),
00122                         array( 'addPgField', 'revision',      'rev_sha1',             "TEXT NOT NULL DEFAULT ''" ),
00123                         array( 'addPgField', 'archive',       'ar_sha1',              "TEXT NOT NULL DEFAULT ''" ),
00124                         array( 'addPgField', 'uploadstash',   'us_chunk_inx',         "INTEGER NULL" ),
00125                         array( 'addPgField', 'job',           'job_timestamp',        "TIMESTAMPTZ" ),
00126 
00127                         # type changes
00128                         array( 'changeField', 'archive',       'ar_deleted',      'smallint', '' ),
00129                         array( 'changeField', 'archive',       'ar_minor_edit',   'smallint', 'ar_minor_edit::smallint DEFAULT 0' ),
00130                         array( 'changeField', 'filearchive',   'fa_deleted',      'smallint', '' ),
00131                         array( 'changeField', 'filearchive',   'fa_height',       'integer',  '' ),
00132                         array( 'changeField', 'filearchive',   'fa_metadata',     'bytea',    "decode(fa_metadata,'escape')" ),
00133                         array( 'changeField', 'filearchive',   'fa_size',         'integer',  '' ),
00134                         array( 'changeField', 'filearchive',   'fa_width',        'integer',  '' ),
00135                         array( 'changeField', 'filearchive',   'fa_storage_group', 'text',     '' ),
00136                         array( 'changeField', 'filearchive',   'fa_storage_key',  'text',     '' ),
00137                         array( 'changeField', 'image',         'img_metadata',    'bytea',    "decode(img_metadata,'escape')" ),
00138                         array( 'changeField', 'image',         'img_size',        'integer',  '' ),
00139                         array( 'changeField', 'image',         'img_width',       'integer',  '' ),
00140                         array( 'changeField', 'image',         'img_height',      'integer',  '' ),
00141                         array( 'changeField', 'interwiki',     'iw_local',        'smallint', 'iw_local::smallint DEFAULT 0' ),
00142                         array( 'changeField', 'interwiki',     'iw_trans',        'smallint', 'iw_trans::smallint DEFAULT 0' ),
00143                         array( 'changeField', 'ipblocks',      'ipb_auto',        'smallint', 'ipb_auto::smallint DEFAULT 0' ),
00144                         array( 'changeField', 'ipblocks',      'ipb_anon_only',   'smallint', "CASE WHEN ipb_anon_only=' ' THEN 0 ELSE ipb_anon_only::smallint END DEFAULT 0" ),
00145                         array( 'changeField', 'ipblocks',      'ipb_create_account', 'smallint', "CASE WHEN ipb_create_account=' ' THEN 0 ELSE ipb_create_account::smallint END DEFAULT 1" ),
00146                         array( 'changeField', 'ipblocks',      'ipb_enable_autoblock', 'smallint', "CASE WHEN ipb_enable_autoblock=' ' THEN 0 ELSE ipb_enable_autoblock::smallint END DEFAULT 1" ),
00147                         array( 'changeField', 'ipblocks',      'ipb_block_email', 'smallint', "CASE WHEN ipb_block_email=' ' THEN 0 ELSE ipb_block_email::smallint END DEFAULT 0" ),
00148                         array( 'changeField', 'ipblocks',      'ipb_address',     'text',     'ipb_address::text' ),
00149                         array( 'changeField', 'ipblocks',      'ipb_deleted',     'smallint', 'ipb_deleted::smallint DEFAULT 0' ),
00150                         array( 'changeField', 'mwuser',        'user_token',      'text',     '' ),
00151                         array( 'changeField', 'mwuser',        'user_email_token', 'text',     '' ),
00152                         array( 'changeField', 'objectcache',   'keyname',         'text',     '' ),
00153                         array( 'changeField', 'oldimage',      'oi_height',       'integer',  '' ),
00154                         array( 'changeField', 'oldimage',      'oi_metadata',     'bytea',    "decode(img_metadata,'escape')" ),
00155                         array( 'changeField', 'oldimage',      'oi_size',         'integer',  '' ),
00156                         array( 'changeField', 'oldimage',      'oi_width',        'integer',  '' ),
00157                         array( 'changeField', 'page',          'page_is_redirect', 'smallint', 'page_is_redirect::smallint DEFAULT 0' ),
00158                         array( 'changeField', 'page',          'page_is_new',     'smallint', 'page_is_new::smallint DEFAULT 0' ),
00159                         array( 'changeField', 'querycache',    'qc_value',        'integer',  '' ),
00160                         array( 'changeField', 'querycachetwo', 'qcc_value',       'integer',  '' ),
00161                         array( 'changeField', 'recentchanges', 'rc_bot',          'smallint', 'rc_bot::smallint DEFAULT 0' ),
00162                         array( 'changeField', 'recentchanges', 'rc_deleted',      'smallint', '' ),
00163                         array( 'changeField', 'recentchanges', 'rc_minor',        'smallint', 'rc_minor::smallint DEFAULT 0' ),
00164                         array( 'changeField', 'recentchanges', 'rc_new',          'smallint', 'rc_new::smallint DEFAULT 0' ),
00165                         array( 'changeField', 'recentchanges', 'rc_type',         'smallint', 'rc_type::smallint DEFAULT 0' ),
00166                         array( 'changeField', 'recentchanges', 'rc_patrolled',    'smallint', 'rc_patrolled::smallint DEFAULT 0' ),
00167                         array( 'changeField', 'revision',      'rev_deleted',     'smallint', 'rev_deleted::smallint DEFAULT 0' ),
00168                         array( 'changeField', 'revision',      'rev_minor_edit',  'smallint', 'rev_minor_edit::smallint DEFAULT 0' ),
00169                         array( 'changeField', 'templatelinks', 'tl_namespace',    'smallint', 'tl_namespace::smallint' ),
00170                         array( 'changeField', 'user_newtalk',  'user_ip',         'text',     'host(user_ip)' ),
00171 
00172                         # null changes
00173                         array( 'changeNullableField', 'oldimage', 'oi_bits',       'NULL' ),
00174                         array( 'changeNullableField', 'oldimage', 'oi_timestamp',  'NULL' ),
00175                         array( 'changeNullableField', 'oldimage', 'oi_major_mime', 'NULL' ),
00176                         array( 'changeNullableField', 'oldimage', 'oi_minor_mime', 'NULL' ),
00177 
00178                         array( 'checkOiDeleted' ),
00179 
00180                         # New indexes
00181                         array( 'addPgIndex', 'archive',       'archive_user_text',      '(ar_user_text)' ),
00182                         array( 'addPgIndex', 'image',         'img_sha1',               '(img_sha1)' ),
00183                         array( 'addPgIndex', 'oldimage',      'oi_sha1',                '(oi_sha1)' ),
00184                         array( 'addPgIndex', 'page',          'page_mediawiki_title',   '(page_title) WHERE page_namespace = 8' ),
00185                         array( 'addPgIndex', 'pagelinks',     'pagelinks_title',        '(pl_title)' ),
00186                         array( 'addPgIndex', 'revision',      'rev_text_id_idx',        '(rev_text_id)' ),
00187                         array( 'addPgIndex', 'recentchanges', 'rc_timestamp_bot',       '(rc_timestamp) WHERE rc_bot = 0' ),
00188                         array( 'addPgIndex', 'templatelinks', 'templatelinks_from',     '(tl_from)' ),
00189                         array( 'addPgIndex', 'watchlist',     'wl_user',                '(wl_user)' ),
00190                         array( 'addPgIndex', 'logging',       'logging_user_type_time', '(log_user, log_type, log_timestamp)' ),
00191                         array( 'addPgIndex', 'logging',       'logging_page_id_time',   '(log_page,log_timestamp)' ),
00192                         array( 'addPgIndex', 'iwlinks',       'iwl_prefix_title_from',  '(iwl_prefix, iwl_title, iwl_from)' ),
00193                         array( 'addPgIndex', 'job',           'job_timestamp_idx',      '(job_timestamp)' ),
00194 
00195                         array( 'checkOiNameConstraint' ),
00196                         array( 'checkPageDeletedTrigger' ),
00197                         array( 'checkRcCurIdNullable' ),
00198                         array( 'checkPagelinkUniqueIndex' ),
00199                         array( 'checkRevUserFkey' ),
00200                         array( 'checkIpbAdress' ),
00201                         array( 'checkIwlPrefix' ),
00202 
00203                         # All FK columns should be deferred
00204                         array( 'changeFkeyDeferrable', 'archive',          'ar_user',         'mwuser(user_id) ON DELETE SET NULL' ),
00205                         array( 'changeFkeyDeferrable', 'categorylinks',    'cl_from',         'page(page_id) ON DELETE CASCADE' ),
00206                         array( 'changeFkeyDeferrable', 'externallinks',    'el_from',         'page(page_id) ON DELETE CASCADE' ),
00207                         array( 'changeFkeyDeferrable', 'filearchive',      'fa_deleted_user', 'mwuser(user_id) ON DELETE SET NULL' ),
00208                         array( 'changeFkeyDeferrable', 'filearchive',      'fa_user',         'mwuser(user_id) ON DELETE SET NULL' ),
00209                         array( 'changeFkeyDeferrable', 'image',            'img_user',        'mwuser(user_id) ON DELETE SET NULL' ),
00210                         array( 'changeFkeyDeferrable', 'imagelinks',       'il_from',         'page(page_id) ON DELETE CASCADE' ),
00211                         array( 'changeFkeyDeferrable', 'ipblocks',         'ipb_by',          'mwuser(user_id) ON DELETE CASCADE' ),
00212                         array( 'changeFkeyDeferrable', 'ipblocks',         'ipb_user',        'mwuser(user_id) ON DELETE SET NULL' ),
00213                         array( 'changeFkeyDeferrable', 'langlinks',        'll_from',         'page(page_id) ON DELETE CASCADE' ),
00214                         array( 'changeFkeyDeferrable', 'logging',          'log_user',        'mwuser(user_id) ON DELETE SET NULL' ),
00215                         array( 'changeFkeyDeferrable', 'oldimage',         'oi_name',         'image(img_name) ON DELETE CASCADE ON UPDATE CASCADE' ),
00216                         array( 'changeFkeyDeferrable', 'oldimage',         'oi_user',         'mwuser(user_id) ON DELETE SET NULL' ),
00217                         array( 'changeFkeyDeferrable', 'pagelinks',        'pl_from',         'page(page_id) ON DELETE CASCADE' ),
00218                         array( 'changeFkeyDeferrable', 'page_props',       'pp_page',         'page (page_id) ON DELETE CASCADE' ),
00219                         array( 'changeFkeyDeferrable', 'page_restrictions', 'pr_page',         'page(page_id) ON DELETE CASCADE' ),
00220                         array( 'changeFkeyDeferrable', 'protected_titles', 'pt_user',         'mwuser(user_id) ON DELETE SET NULL' ),
00221                         array( 'changeFkeyDeferrable', 'recentchanges',    'rc_cur_id',       'page(page_id) ON DELETE SET NULL' ),
00222                         array( 'changeFkeyDeferrable', 'recentchanges',    'rc_user',         'mwuser(user_id) ON DELETE SET NULL' ),
00223                         array( 'changeFkeyDeferrable', 'redirect',         'rd_from',         'page(page_id) ON DELETE CASCADE' ),
00224                         array( 'changeFkeyDeferrable', 'revision',         'rev_page',        'page (page_id) ON DELETE CASCADE' ),
00225                         array( 'changeFkeyDeferrable', 'revision',         'rev_user',        'mwuser(user_id) ON DELETE RESTRICT' ),
00226                         array( 'changeFkeyDeferrable', 'templatelinks',    'tl_from',         'page(page_id) ON DELETE CASCADE' ),
00227                         array( 'changeFkeyDeferrable', 'user_groups',      'ug_user',         'mwuser(user_id) ON DELETE CASCADE' ),
00228                         array( 'changeFkeyDeferrable', 'user_newtalk',     'user_id',         'mwuser(user_id) ON DELETE CASCADE' ),
00229                         array( 'changeFkeyDeferrable', 'user_properties',  'up_user',         'mwuser(user_id) ON DELETE CASCADE' ),
00230                         array( 'changeFkeyDeferrable', 'watchlist',        'wl_user',         'mwuser(user_id) ON DELETE CASCADE' ),
00231 
00232                         # end
00233                         array( 'tsearchFixes' ),
00234                 );
00235         }
00236 
00237         protected function getOldGlobalUpdates() {
00238                 global $wgExtNewTables, $wgExtPGNewFields, $wgExtPGAlteredFields, $wgExtNewIndexes;
00239 
00240                 $updates = array();
00241 
00242                 # Add missing extension tables
00243                 foreach ( $wgExtNewTables as $tableRecord ) {
00244                         $updates[] = array(
00245                                 'addTable', $tableRecord[0], $tableRecord[1], true
00246                         );
00247                 }
00248 
00249                 # Add missing extension fields
00250                 foreach ( $wgExtPGNewFields as $fieldRecord ) {
00251                         $updates[] = array(
00252                                         'addPgField', $fieldRecord[0], $fieldRecord[1],
00253                                         $fieldRecord[2]
00254                                 );
00255                 }
00256 
00257                 # Change altered columns
00258                 foreach ( $wgExtPGAlteredFields as $fieldRecord ) {
00259                         $updates[] = array(
00260                                         'changeField', $fieldRecord[0], $fieldRecord[1],
00261                                         $fieldRecord[2]
00262                                 );
00263                 }
00264 
00265                 # Add missing extension indexes
00266                 foreach ( $wgExtNewIndexes as $fieldRecord ) {
00267                         $updates[] = array(
00268                                         'addPgExtIndex', $fieldRecord[0], $fieldRecord[1],
00269                                         $fieldRecord[2]
00270                                 );
00271                 }
00272 
00273                 return $updates;
00274         }
00275 
00276         protected function describeTable( $table ) {
00277                 global $wgDBmwschema;
00278                 $q = <<<END
00279 SELECT attname, attnum FROM pg_namespace, pg_class, pg_attribute
00280         WHERE pg_class.relnamespace = pg_namespace.oid
00281           AND attrelid=pg_class.oid AND attnum > 0
00282           AND relname=%s AND nspname=%s
00283 END;
00284                 $res = $this->db->query( sprintf( $q,
00285                                 $this->db->addQuotes( $table ),
00286                                 $this->db->addQuotes( $wgDBmwschema ) ) );
00287                 if ( !$res ) {
00288                         return null;
00289                 }
00290 
00291                 $cols = array();
00292                 foreach ( $res as $r ) {
00293                         $cols[] = array(
00294                                         "name" => $r[0],
00295                                         "ord" => $r[1],
00296                                 );
00297                 }
00298                 return $cols;
00299         }
00300 
00301         function describeIndex( $idx ) {
00302                 global $wgDBmwschema;
00303 
00304                 // first fetch the key (which is a list of columns ords) and
00305                 // the table the index applies to (an oid)
00306                 $q = <<<END
00307 SELECT indkey, indrelid FROM pg_namespace, pg_class, pg_index
00308         WHERE nspname=%s
00309           AND pg_class.relnamespace = pg_namespace.oid
00310           AND relname=%s
00311           AND indexrelid=pg_class.oid
00312 END;
00313                 $res = $this->db->query(
00314                         sprintf(
00315                                 $q,
00316                                 $this->db->addQuotes( $wgDBmwschema ),
00317                                 $this->db->addQuotes( $idx )
00318                         )
00319                 );
00320                 if ( !$res ) {
00321                         return null;
00322                 }
00323                 if ( !( $r = $this->db->fetchRow( $res ) ) ) {
00324                         return null;
00325                 }
00326 
00327                 $indkey = $r[0];
00328                 $relid = intval( $r[1] );
00329                 $indkeys = explode( ' ', $indkey );
00330 
00331                 $colnames = array();
00332                 foreach ( $indkeys as $rid ) {
00333                         $query = <<<END
00334 SELECT attname FROM pg_class, pg_attribute
00335         WHERE attrelid=$relid
00336           AND attnum=%d
00337           AND attrelid=pg_class.oid
00338 END;
00339                         $r2 = $this->db->query( sprintf( $query, $rid ) );
00340                         if ( !$r2 ) {
00341                                 return null;
00342                         }
00343                         if ( !( $row2 = $this->db->fetchRow( $r2 ) ) ) {
00344                                 return null;
00345                         }
00346                         $colnames[] = $row2[0];
00347                 }
00348 
00349                 return $colnames;
00350         }
00351 
00352         function fkeyDeltype( $fkey ) {
00353                 global $wgDBmwschema;
00354                 $q = <<<END
00355 SELECT confdeltype FROM pg_constraint, pg_namespace
00356         WHERE connamespace=pg_namespace.oid
00357           AND nspname=%s
00358           AND conname=%s;
00359 END;
00360                 $r = $this->db->query(
00361                         sprintf(
00362                                 $q,
00363                                 $this->db->addQuotes( $wgDBmwschema ),
00364                                 $this->db->addQuotes( $fkey )
00365                         )
00366                 );
00367                 if ( !( $row = $this->db->fetchRow( $r ) ) ) {
00368                         return null;
00369                 }
00370                 return $row[0];
00371         }
00372 
00373         function ruleDef( $table, $rule ) {
00374                 global $wgDBmwschema;
00375         $q = <<<END
00376 SELECT definition FROM pg_rules
00377         WHERE schemaname = %s
00378           AND tablename = %s
00379           AND rulename = %s
00380 END;
00381                 $r = $this->db->query(
00382                         sprintf(
00383                                 $q,
00384                                 $this->db->addQuotes( $wgDBmwschema ),
00385                                 $this->db->addQuotes( $table ),
00386                                 $this->db->addQuotes( $rule )
00387                         )
00388                 );
00389                 $row = $this->db->fetchRow( $r );
00390                 if ( !$row ) {
00391                         return null;
00392                 }
00393                 $d = $row[0];
00394                 return $d;
00395         }
00396 
00397         protected function addSequence( $ns ) {
00398                 if ( !$this->db->sequenceExists( $ns ) ) {
00399                         $this->output( "Creating sequence $ns\n" );
00400                         $this->db->query( "CREATE SEQUENCE $ns" );
00401                 }
00402         }
00403 
00404         protected function renameSequence( $old, $new ) {
00405                 if ( $this->db->sequenceExists( $new ) ) {
00406                         $this->output( "...sequence $new already exists.\n" );
00407                         return;
00408                 }
00409                 if ( $this->db->sequenceExists( $old ) ) {
00410                         $this->output( "Renaming sequence $old to $new\n" );
00411                         $this->db->query( "ALTER SEQUENCE $old RENAME TO $new" );
00412                 }
00413         }
00414 
00415         protected function renameTable( $old, $new ) {
00416                 if ( $this->db->tableExists( $old ) ) {
00417                         $this->output( "Renaming table $old to $new\n" );
00418                         $old = $this->db->realTableName( $old, "quoted" );
00419                         $new = $this->db->realTableName( $new, "quoted" );
00420                         $this->db->query( "ALTER TABLE $old RENAME TO $new" );
00421                 }
00422         }
00423 
00424         protected function addPgField( $table, $field, $type ) {
00425                 $fi = $this->db->fieldInfo( $table, $field );
00426                 if ( !is_null( $fi ) ) {
00427                         $this->output( "...column '$table.$field' already exists\n" );
00428                         return;
00429                 } else {
00430                         $this->output( "Adding column '$table.$field'\n" );
00431                         $this->db->query( "ALTER TABLE $table ADD $field $type" );
00432                 }
00433         }
00434 
00435         protected function changeField( $table, $field, $newtype, $default ) {
00436                 $fi = $this->db->fieldInfo( $table, $field );
00437                 if ( is_null( $fi ) ) {
00438                         $this->output( "...ERROR: expected column $table.$field to exist\n" );
00439                         exit( 1 );
00440                 }
00441 
00442                 if ( $fi->type() === $newtype )
00443                         $this->output( "...column '$table.$field' is already of type '$newtype'\n" );
00444                 else {
00445                         $this->output( "Changing column type of '$table.$field' from '{$fi->type()}' to '$newtype'\n" );
00446                         $sql = "ALTER TABLE $table ALTER $field TYPE $newtype";
00447                         if ( strlen( $default ) ) {
00448                                 $res = array();
00449                                 if ( preg_match( '/DEFAULT (.+)/', $default, $res ) ) {
00450                                         $sqldef = "ALTER TABLE $table ALTER $field SET DEFAULT $res[1]";
00451                                         $this->db->query( $sqldef );
00452                                         $default = preg_replace( '/\s*DEFAULT .+/', '', $default );
00453                                 }
00454                                 $sql .= " USING $default";
00455                         }
00456                         $this->db->begin( __METHOD__ );
00457                         $this->db->query( $sql );
00458                         $this->db->commit( __METHOD__ );
00459                 }
00460         }
00461 
00462         protected function changeNullableField( $table, $field, $null ) {
00463                 $fi = $this->db->fieldInfo( $table, $field );
00464                 if ( is_null( $fi ) ) {
00465                         $this->output( "...ERROR: expected column $table.$field to exist\n" );
00466                         exit( 1 );
00467                 }
00468                 if ( $fi->isNullable() ) {
00469                         # # It's NULL - does it need to be NOT NULL?
00470                         if ( 'NOT NULL' === $null ) {
00471                                 $this->output( "Changing '$table.$field' to not allow NULLs\n" );
00472                                 $this->db->query( "ALTER TABLE $table ALTER $field SET NOT NULL" );
00473                         } else {
00474                                 $this->output( "...column '$table.$field' is already set as NULL\n" );
00475                         }
00476                 } else {
00477                         # # It's NOT NULL - does it need to be NULL?
00478                         if ( 'NULL' === $null ) {
00479                                 $this->output( "Changing '$table.$field' to allow NULLs\n" );
00480                                 $this->db->query( "ALTER TABLE $table ALTER $field DROP NOT NULL" );
00481                         }
00482                         else {
00483                                 $this->output( "...column '$table.$field' is already set as NOT NULL\n" );
00484                         }
00485                 }
00486         }
00487 
00488         public function addPgIndex( $table, $index, $type ) {
00489                 if ( $this->db->indexExists( $table, $index ) ) {
00490                         $this->output( "...index '$index' on table '$table' already exists\n" );
00491                 } else {
00492                         $this->output( "Creating index '$index' on table '$table' $type\n" );
00493                         $this->db->query( "CREATE INDEX $index ON $table $type" );
00494                 }
00495         }
00496 
00497         public function addPgExtIndex( $table, $index, $type ) {
00498                 if ( $this->db->indexExists( $table, $index ) ) {
00499                         $this->output( "...index '$index' on table '$table' already exists\n" );
00500                 } else {
00501                         $this->output( "Creating index '$index' on table '$table'\n" );
00502                         if ( preg_match( '/^\(/', $type ) ) {
00503                                 $this->db->query( "CREATE INDEX $index ON $table $type" );
00504                         } else {
00505                                 $this->applyPatch( $type, true );
00506                         }
00507                 }
00508         }
00509 
00510         protected function changeFkeyDeferrable( $table, $field, $clause ) {
00511                 $fi = $this->db->fieldInfo( $table, $field );
00512                 if ( is_null( $fi ) ) {
00513                         $this->output( "WARNING! Column '$table.$field' does not exist but it should! Please report this.\n" );
00514                         return;
00515                 }
00516                 if ( $fi->is_deferred() && $fi->is_deferrable() ) {
00517                         return;
00518                 }
00519                 $this->output( "Altering column '$table.$field' to be DEFERRABLE INITIALLY DEFERRED\n" );
00520                 $conname = $fi->conname();
00521                 $command = "ALTER TABLE $table DROP CONSTRAINT $conname";
00522                 $this->db->query( $command );
00523                 $command = "ALTER TABLE $table ADD CONSTRAINT $conname FOREIGN KEY ($field) REFERENCES $clause DEFERRABLE INITIALLY DEFERRED";
00524                 $this->db->query( $command );
00525         }
00526 
00527         protected function convertArchive2() {
00528                 if ( $this->db->tableExists( "archive2" ) ) {
00529                         $this->output( "Converting 'archive2' back to normal archive table\n" );
00530                         if ( $this->db->ruleExists( 'archive', 'archive_insert' ) ) {
00531                                 $this->output( "Dropping rule 'archive_insert'\n" );
00532                                 $this->db->query( 'DROP RULE archive_insert ON archive' );
00533                         }
00534                         if ( $this->db->ruleExists( 'archive', 'archive_delete' ) ) {
00535                                 $this->output( "Dropping rule 'archive_delete'\n" );
00536                                 $this->db->query( 'DROP RULE archive_delete ON archive' );
00537                         }
00538                         $this->applyPatch( 'patch-remove-archive2.sql' );
00539                 } else {
00540                         $this->output( "...obsolete table 'archive2' does not exist\n" );
00541                 }
00542         }
00543 
00544         protected function checkOiDeleted() {
00545                 if ( $this->db->fieldInfo( 'oldimage', 'oi_deleted' )->type() !== 'smallint' ) {
00546                         $this->output( "Changing 'oldimage.oi_deleted' to type 'smallint'\n" );
00547                         $this->db->query( "ALTER TABLE oldimage ALTER oi_deleted DROP DEFAULT" );
00548                         $this->db->query( "ALTER TABLE oldimage ALTER oi_deleted TYPE SMALLINT USING (oi_deleted::smallint)" );
00549                         $this->db->query( "ALTER TABLE oldimage ALTER oi_deleted SET DEFAULT 0" );
00550                 } else {
00551                         $this->output( "...column 'oldimage.oi_deleted' is already of type 'smallint'\n" );
00552                 }
00553         }
00554 
00555         protected function checkOiNameConstraint() {
00556                 if ( $this->db->hasConstraint( "oldimage_oi_name_fkey_cascaded" ) ) {
00557                         $this->output( "...table 'oldimage' has correct cascading delete/update foreign key to image\n" );
00558                 } else {
00559                         if ( $this->db->hasConstraint( "oldimage_oi_name_fkey" ) ) {
00560                                 $this->db->query( "ALTER TABLE oldimage DROP CONSTRAINT oldimage_oi_name_fkey" );
00561                         }
00562                         if ( $this->db->hasConstraint( "oldimage_oi_name_fkey_cascade" ) ) {
00563                                 $this->db->query( "ALTER TABLE oldimage DROP CONSTRAINT oldimage_oi_name_fkey_cascade" );
00564                         }
00565                         $this->output( "Making foreign key on table 'oldimage' (to image) a cascade delete/update\n" );
00566                         $this->db->query( "ALTER TABLE oldimage ADD CONSTRAINT oldimage_oi_name_fkey_cascaded " .
00567                                 "FOREIGN KEY (oi_name) REFERENCES image(img_name) ON DELETE CASCADE ON UPDATE CASCADE" );
00568                 }
00569         }
00570 
00571         protected function checkPageDeletedTrigger() {
00572                 if ( !$this->db->triggerExists( 'page', 'page_deleted' ) ) {
00573                         $this->output( "Adding function and trigger 'page_deleted' to table 'page'\n" );
00574                         $this->applyPatch( 'patch-page_deleted.sql' );
00575                 } else {
00576                         $this->output( "...table 'page' has 'page_deleted' trigger\n" );
00577                 }
00578         }
00579 
00580         protected function checkRcCurIdNullable(){
00581                 $fi = $this->db->fieldInfo( 'recentchanges', 'rc_cur_id' );
00582                 if ( !$fi->isNullable() ) {
00583                         $this->output( "Removing NOT NULL constraint from 'recentchanges.rc_cur_id'\n" );
00584                         $this->applyPatch( 'patch-rc_cur_id-not-null.sql' );
00585                 } else {
00586                         $this->output( "...column 'recentchanges.rc_cur_id' has a NOT NULL constraint\n" );
00587                 }
00588         }
00589 
00590         protected function checkPagelinkUniqueIndex() {
00591                 $pu = $this->describeIndex( 'pagelink_unique' );
00592                 if ( !is_null( $pu ) && ( $pu[0] != 'pl_from' || $pu[1] != 'pl_namespace' || $pu[2] != 'pl_title' ) ) {
00593                         $this->output( "Dropping obsolete version of index 'pagelink_unique index'\n" );
00594                         $this->db->query( 'DROP INDEX pagelink_unique' );
00595                         $pu = null;
00596                 } else {
00597                         $this->output( "...obsolete version of index 'pagelink_unique index' does not exist\n" );
00598                 }
00599 
00600                 if ( is_null( $pu ) ) {
00601                         $this->output( "Creating index 'pagelink_unique index'\n" );
00602                         $this->db->query( 'CREATE UNIQUE INDEX pagelink_unique ON pagelinks (pl_from,pl_namespace,pl_title)' );
00603                 } else {
00604                         $this->output( "...index 'pagelink_unique_index' already exists\n" );
00605                 }
00606         }
00607 
00608         protected function checkRevUserFkey() {
00609                 if ( $this->fkeyDeltype( 'revision_rev_user_fkey' ) == 'r' ) {
00610                         $this->output( "...constraint 'revision_rev_user_fkey' is ON DELETE RESTRICT\n" );
00611                 } else {
00612                         $this->output( "Changing constraint 'revision_rev_user_fkey' to ON DELETE RESTRICT\n" );
00613                         $this->applyPatch( 'patch-revision_rev_user_fkey.sql' );
00614                 }
00615         }
00616 
00617         protected function checkIpbAdress() {
00618                 if ( $this->db->indexExists( 'ipblocks', 'ipb_address' ) ) {
00619                         $this->output( "Removing deprecated index 'ipb_address'...\n" );
00620                         $this->db->query( 'DROP INDEX ipb_address' );
00621                 }
00622                 if ( $this->db->indexExists( 'ipblocks', 'ipb_address_unique' ) ) {
00623                         $this->output( "...have ipb_address_unique\n" );
00624                 } else {
00625                         $this->output( "Adding ipb_address_unique index\n" );
00626                         $this->applyPatch( 'patch-ipb_address_unique.sql' );
00627                 }
00628         }
00629 
00630         protected function checkIwlPrefix() {
00631                 if ( $this->db->indexExists( 'iwlinks', 'iwl_prefix' ) ) {
00632                         $this->output( "Replacing index 'iwl_prefix' with 'iwl_prefix_from_title'...\n" );
00633                         $this->applyPatch( 'patch-rename-iwl_prefix.sql' );
00634                 }
00635         }
00636 
00637         protected function tsearchFixes() {
00638                 # Tweak the page_title tsearch2 trigger to filter out slashes
00639                 # This is create or replace, so harmless to call if not needed
00640                 $this->output( "Refreshing ts2_page_title()...\n" );
00641                 $this->applyPatch( 'patch-ts2pagetitle.sql' );
00642 
00643                 # If the server is 8.3 or higher, rewrite the tsearch2 triggers
00644                 # in case they have the old 'default' versions
00645                 # Gather version numbers in case we need them
00646                 if ( $this->db->getServerVersion() >= 8.3 ) {
00647                         $this->output( "Rewriting tsearch2 triggers...\n" );
00648                         $this->applyPatch( 'patch-tsearch2funcs.sql' );
00649                 }
00650         }
00651 }