r71426 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r71425‎ | r71426 | r71427 >
Date:08:07, 22 August 2010
Author:ialex
Status:resolved (Comments)
Tags:
Comment:
Moved PostgreSQL schema update to PostgresUpdater:
* Still not doing sequential updates, but now this should be easier to correct
* Corrected addition of page_restrictions.pr_id field to use the correct sequence name
* Had to change DatabaseUpdater::getOldGlobalUpdates() from private to protected, since PostgreSQL has its own globals for extensions
* Moved do_all_updates() and archive() to the top of updaters.inc so that they are easier to find
Modified paths:
  • /trunk/phase3/includes/installer/DatabaseUpdater.php (modified) (history)
  • /trunk/phase3/includes/installer/PostgresUpdater.php (modified) (history)
  • /trunk/phase3/maintenance/updaters.inc (modified) (history)

Diff [purge]

Index: trunk/phase3/maintenance/updaters.inc
@@ -74,6 +74,43 @@
7575 }
7676 }
7777
 78+function do_all_updates( $shared = false, $purge = true ) {
 79+ global $wgDatabase, $wgDBtype;
 80+
 81+ $updater = DatabaseUpdater::newForDb( $wgDatabase, $shared );
 82+
 83+ wfRunHooks( 'LoadExtensionSchemaUpdates', array( &$updater ) );
 84+
 85+ $updater->doUpdates();
 86+
 87+ if ( !defined( 'MW_NO_SETUP' ) ) {
 88+ define( 'MW_NO_SETUP', true );
 89+ }
 90+
 91+ foreach( $updater->getPostDatabaseUpdateMaintenance() as $maint ) {
 92+ call_user_func_array( array( new $maint, 'execute' ), array() );
 93+ }
 94+
 95+ if ( $wgDBtype === 'postgres' ) {
 96+ return;
 97+ }
 98+
 99+ do_stats_init();
 100+
 101+ if ( $purge ) {
 102+ purge_cache();
 103+ }
 104+}
 105+
 106+function archive( $name ) {
 107+ global $wgDBtype, $IP;
 108+ if ( file_exists( "$IP/maintenance/$wgDBtype/archives/$name" ) ) {
 109+ return "$IP/maintenance/$wgDBtype/archives/$name";
 110+ } else {
 111+ return "$IP/maintenance/archives/$name";
 112+ }
 113+}
 114+
78115 function do_interwiki_update() {
79116 # Check that interwiki table exists; if it doesn't source it
80117 global $wgDatabase, $IP;
@@ -829,43 +866,6 @@
830867 wfOut( "done.\n" );
831868 }
832869
833 -function do_all_updates( $shared = false, $purge = true ) {
834 - global $wgDatabase, $wgDBtype;
835 -
836 - $updater = DatabaseUpdater::newForDb( $wgDatabase, $shared );
837 -
838 - wfRunHooks( 'LoadExtensionSchemaUpdates', array( &$updater ) );
839 -
840 - $updater->doUpdates();
841 -
842 - if ( !defined( 'MW_NO_SETUP' ) ) {
843 - define( 'MW_NO_SETUP', true );
844 - }
845 -
846 - foreach( $updater->getPostDatabaseUpdateMaintenance() as $maint ) {
847 - call_user_func_array( array( new $maint, 'execute' ), array() );
848 - }
849 -
850 - if ( $wgDBtype === 'postgres' ) {
851 - return;
852 - }
853 -
854 - do_stats_init();
855 -
856 - if ( $purge ) {
857 - purge_cache();
858 - }
859 -}
860 -
861 -function archive( $name ) {
862 - global $wgDBtype, $IP;
863 - if ( file_exists( "$IP/maintenance/$wgDBtype/archives/$name" ) ) {
864 - return "$IP/maintenance/$wgDBtype/archives/$name";
865 - } else {
866 - return "$IP/maintenance/archives/$name";
867 - }
868 -}
869 -
870870 function do_restrictions_update() {
871871 # Adding page_restrictions table, obsoleting page.page_restrictions.
872872 # Migrating old restrictions to new table
@@ -1083,661 +1083,3 @@
10841084 wfOut( "ok\n" );
10851085 }
10861086 }
1087 -
1088 -/***********************************************************************
1089 - * Start PG stuff
1090 - * TODO: merge with above
1091 - ***********************************************************************/
1092 -
1093 -function pg_describe_table( $table ) {
1094 - global $wgDatabase, $wgDBmwschema;
1095 - $q = <<<END
1096 -SELECT attname, attnum FROM pg_namespace, pg_class, pg_attribute
1097 - WHERE pg_class.relnamespace = pg_namespace.oid
1098 - AND attrelid=pg_class.oid AND attnum > 0
1099 - AND relname=%s AND nspname=%s
1100 -END;
1101 - $res = $wgDatabase->query( sprintf( $q,
1102 - $wgDatabase->addQuotes( $table ),
1103 - $wgDatabase->addQuotes( $wgDBmwschema ) ) );
1104 - if ( !$res ) {
1105 - return null;
1106 - }
1107 -
1108 - $cols = array();
1109 - while ( $r = $wgDatabase->fetchRow( $res ) ) {
1110 - $cols[] = array(
1111 - "name" => $r[0],
1112 - "ord" => $r[1],
1113 - );
1114 - }
1115 - return $cols;
1116 -}
1117 -
1118 -function pg_describe_index( $idx ) {
1119 - global $wgDatabase, $wgDBmwschema;
1120 -
1121 - // first fetch the key (which is a list of columns ords) and
1122 - // the table the index applies to (an oid)
1123 - $q = <<<END
1124 -SELECT indkey, indrelid FROM pg_namespace, pg_class, pg_index
1125 - WHERE nspname=%s
1126 - AND pg_class.relnamespace = pg_namespace.oid
1127 - AND relname=%s
1128 - AND indexrelid=pg_class.oid
1129 -END;
1130 - $res = $wgDatabase->query( sprintf( $q,
1131 - $wgDatabase->addQuotes( $wgDBmwschema ),
1132 - $wgDatabase->addQuotes( $idx ) ) );
1133 - if ( !$res ) {
1134 - return null;
1135 - }
1136 - if ( !( $r = $wgDatabase->fetchRow( $res ) ) ) {
1137 - return null;
1138 - }
1139 -
1140 - $indkey = $r[0];
1141 - $relid = intval( $r[1] );
1142 - $indkeys = explode( " ", $indkey );
1143 -
1144 - $colnames = array();
1145 - foreach ( $indkeys as $rid ) {
1146 - $query = <<<END
1147 -SELECT attname FROM pg_class, pg_attribute
1148 - WHERE attrelid=$relid
1149 - AND attnum=%d
1150 - AND attrelid=pg_class.oid
1151 -END;
1152 - $r2 = $wgDatabase->query( sprintf( $query, $rid ) );
1153 - if ( !$r2 ) {
1154 - return null;
1155 - }
1156 - if ( !( $row2 = $wgDatabase->fetchRow( $r2 ) ) ) {
1157 - return null;
1158 - }
1159 - $colnames[] = $row2[0];
1160 - }
1161 -
1162 - return $colnames;
1163 -}
1164 -
1165 -function pg_index_exists( $table, $index ) {
1166 - global $wgDatabase, $wgDBmwschema;
1167 - $exists = $wgDatabase->selectField( "pg_indexes", "indexname",
1168 - array( "indexname" => $index,
1169 - "tablename" => $table,
1170 - "schemaname" => $wgDBmwschema ) );
1171 - return $exists === $index;
1172 -}
1173 -
1174 -function pg_fkey_deltype( $fkey ) {
1175 - global $wgDatabase, $wgDBmwschema;
1176 - $q = <<<END
1177 -SELECT confdeltype FROM pg_constraint, pg_namespace
1178 - WHERE connamespace=pg_namespace.oid
1179 - AND nspname=%s
1180 - AND conname=%s;
1181 -END;
1182 - $r = $wgDatabase->query( sprintf( $q,
1183 - $wgDatabase->addQuotes( $wgDBmwschema ),
1184 - $wgDatabase->addQuotes( $fkey ) ) );
1185 - if ( !( $row = $wgDatabase->fetchRow( $r ) ) ) {
1186 - return null;
1187 - }
1188 - return $row[0];
1189 -}
1190 -
1191 -function pg_rule_def( $table, $rule ) {
1192 - global $wgDatabase, $wgDBmwschema;
1193 - $q = <<<END
1194 -SELECT definition FROM pg_rules
1195 - WHERE schemaname = %s
1196 - AND tablename = %s
1197 - AND rulename = %s
1198 -END;
1199 - $r = $wgDatabase->query( sprintf( $q,
1200 - $wgDatabase->addQuotes( $wgDBmwschema ),
1201 - $wgDatabase->addQuotes( $table ),
1202 - $wgDatabase->addQuotes( $rule ) ) );
1203 - $row = $wgDatabase->fetchRow( $r );
1204 - if ( !$row ) {
1205 - return null;
1206 - }
1207 - $d = $row[0];
1208 - return $d;
1209 -}
1210 -
1211 -function do_postgres_updates() {
1212 - global $wgDatabase, $wgDBmwschema, $wgDBts2schema, $wgDBuser;
1213 -
1214 - # Gather version numbers in case we need them
1215 - $numver = $wgDatabase->getServerVersion();
1216 -
1217 - # Just in case their LocalSettings.php does not have this:
1218 - if ( !isset( $wgDBmwschema ) ) {
1219 - $wgDBmwschema = 'mediawiki';
1220 - }
1221 -
1222 - # Verify that this user is configured correctly
1223 - $safeuser = $wgDatabase->addQuotes( $wgDBuser );
1224 - $SQL = "SELECT array_to_string(useconfig,'*') FROM pg_catalog.pg_user WHERE usename = $safeuser";
1225 - $config = pg_fetch_result( $wgDatabase->doQuery( $SQL ), 0, 0 );
1226 - $conf = array();
1227 - foreach ( explode( '*', $config ) as $c ) {
1228 - list( $x, $y ) = explode( '=', $c );
1229 - $conf[$x] = $y;
1230 - }
1231 - if ( !array_key_exists( 'search_path', $conf ) ) {
1232 - $search_path = '';
1233 - } else {
1234 - $search_path = $conf['search_path'];
1235 - }
1236 - if ( strpos( $search_path, $wgDBmwschema ) === false ) {
1237 - wfOut( "Adding in schema \"$wgDBmwschema\" to search_path for user \"$wgDBuser\"\n" );
1238 - $search_path = "$wgDBmwschema, $search_path";
1239 - }
1240 - if ( strpos( $search_path, $wgDBts2schema ) === false ) {
1241 - wfOut( "Adding in schema \"$wgDBts2schema\" to search_path for user \"$wgDBuser\"\n" );
1242 - $search_path = "$search_path, $wgDBts2schema";
1243 - }
1244 - $search_path = str_replace( ', ,', ',', $search_path );
1245 - if ( array_key_exists( 'search_path', $conf ) === false || $search_path != $conf['search_path'] ) {
1246 - $wgDatabase->doQuery( "ALTER USER $wgDBuser SET search_path = $search_path" );
1247 - $wgDatabase->doQuery( "SET search_path = $search_path" );
1248 - } else {
1249 - $path = $conf['search_path'];
1250 - wfOut( "... search_path for user \"$wgDBuser\" looks correct ($path)\n" );
1251 - }
1252 - $goodconf = array(
1253 - 'client_min_messages' => 'error',
1254 - 'DateStyle' => 'ISO, YMD',
1255 - 'TimeZone' => 'GMT'
1256 - );
1257 - foreach ( array_keys( $goodconf ) AS $key ) {
1258 - $value = $goodconf[$key];
1259 - if ( !array_key_exists( $key, $conf ) or $conf[$key] !== $value ) {
1260 - wfOut( "Setting $key to '$value' for user \"$wgDBuser\"\n" );
1261 - $wgDatabase->doQuery( "ALTER USER $wgDBuser SET $key = '$value'" );
1262 - $wgDatabase->doQuery( "SET $key = '$value'" );
1263 - } else {
1264 - wfOut( "... default value of \"$key\" is correctly set to \"$value\" for user \"$wgDBuser\"\n" );
1265 - }
1266 - }
1267 -
1268 - $newsequences = array(
1269 - "logging_log_id_seq",
1270 - "page_restrictions_pr_id_seq",
1271 - );
1272 -
1273 - $renamed_sequences = array(
1274 - array('ipblocks_ipb_id_val', 'ipblocks_ipb_id_seq'),
1275 - array('rev_rev_id_val', 'revision_rev_id_seq'),
1276 - array('text_old_id_val', 'text_old_id_seq'),
1277 - array('category_id_seq', 'category_cat_id_seq'),
1278 - array('rc_rc_id_seq', 'recentchanges_rc_id_seq'),
1279 - array('log_log_id_seq', 'logging_log_id_seq'),
1280 - array('pr_id_val', 'page_restrictions_pr_id_seq'),
1281 - );
1282 -
1283 - $newtables = array(
1284 - array( "category", "patch-category.sql" ),
1285 - array( "mwuser", "patch-mwuser.sql" ),
1286 - array( "pagecontent", "patch-pagecontent.sql" ),
1287 - array( "querycachetwo", "patch-querycachetwo.sql" ),
1288 - array( "page_props", "patch-page_props.sql" ),
1289 - array( "page_restrictions", "patch-page_restrictions.sql" ),
1290 - array( "profiling", "patch-profiling.sql" ),
1291 - array( "protected_titles", "patch-protected_titles.sql" ),
1292 - array( "redirect", "patch-redirect.sql" ),
1293 - array( "updatelog", "patch-updatelog.sql" ),
1294 - array( 'change_tag', 'patch-change_tag.sql' ),
1295 - array( 'tag_summary', 'patch-change_tag.sql' ),
1296 - array( 'valid_tag', 'patch-change_tag.sql' ),
1297 - array( 'user_properties', 'patch-user_properties.sql' ),
1298 - array( 'log_search', 'patch-log_search.sql' ),
1299 - array( 'l10n_cache', 'patch-l10n_cache.sql' ),
1300 - array( 'iwlinks', 'patch-iwlinks.sql' ),
1301 - );
1302 -
1303 - $newcols = array(
1304 - array( "archive", "ar_deleted", "SMALLINT NOT NULL DEFAULT 0" ),
1305 - array( "archive", "ar_len", "INTEGER" ),
1306 - array( "archive", "ar_page_id", "INTEGER" ),
1307 - array( "archive", "ar_parent_id", "INTEGER" ),
1308 - array( "image", "img_sha1", "TEXT NOT NULL DEFAULT ''" ),
1309 - array( "ipblocks", "ipb_allow_usertalk", "SMALLINT NOT NULL DEFAULT 0" ),
1310 - array( "ipblocks", "ipb_anon_only", "SMALLINT NOT NULL DEFAULT 0" ),
1311 - array( "ipblocks", "ipb_by_text", "TEXT NOT NULL DEFAULT ''" ),
1312 - array( "ipblocks", "ipb_block_email", "SMALLINT NOT NULL DEFAULT 0" ),
1313 - array( "ipblocks", "ipb_create_account", "SMALLINT NOT NULL DEFAULT 1" ),
1314 - array( "ipblocks", "ipb_deleted", "SMALLINT NOT NULL DEFAULT 0" ),
1315 - array( "ipblocks", "ipb_enable_autoblock", "SMALLINT NOT NULL DEFAULT 1" ),
1316 - array( "filearchive", "fa_deleted", "SMALLINT NOT NULL DEFAULT 0" ),
1317 - array( "logging", "log_deleted", "SMALLINT NOT NULL DEFAULT 0" ),
1318 - array( "logging", "log_id", "INTEGER NOT NULL PRIMARY KEY DEFAULT nextval('logging_log_id_seq')" ),
1319 - array( "logging", "log_params", "TEXT" ),
1320 - array( "mwuser", "user_editcount", "INTEGER" ),
1321 - array( "mwuser", "user_hidden", "SMALLINT NOT NULL DEFAULT 0" ),
1322 - array( "mwuser", "user_newpass_time", "TIMESTAMPTZ" ),
1323 - array( "oldimage", "oi_deleted", "SMALLINT NOT NULL DEFAULT 0" ),
1324 - array( "oldimage", "oi_major_mime", "TEXT NOT NULL DEFAULT 'unknown'" ),
1325 - array( "oldimage", "oi_media_type", "TEXT" ),
1326 - array( "oldimage", "oi_metadata", "BYTEA NOT NULL DEFAULT ''" ),
1327 - array( "oldimage", "oi_minor_mime", "TEXT NOT NULL DEFAULT 'unknown'" ),
1328 - array( "oldimage", "oi_sha1", "TEXT NOT NULL DEFAULT ''" ),
1329 - array( "page_restrictions", "pr_id", "INTEGER NOT NULL UNIQUE DEFAULT nextval('page_restrictions_pr_id_val')" ),
1330 - array( "profiling", "pf_memory", "NUMERIC(18,10) NOT NULL DEFAULT 0" ),
1331 - array( "recentchanges", "rc_deleted", "SMALLINT NOT NULL DEFAULT 0" ),
1332 - array( "recentchanges", "rc_log_action", "TEXT" ),
1333 - array( "recentchanges", "rc_log_type", "TEXT" ),
1334 - array( "recentchanges", "rc_logid", "INTEGER NOT NULL DEFAULT 0" ),
1335 - array( "recentchanges", "rc_new_len", "INTEGER" ),
1336 - array( "recentchanges", "rc_old_len", "INTEGER" ),
1337 - array( "recentchanges", "rc_params", "TEXT" ),
1338 - array( "redirect", "rd_interwiki", "TEXT NULL" ),
1339 - array( "redirect", "rd_fragment", "TEXT NULL" ),
1340 - array( "revision", "rev_deleted", "SMALLINT NOT NULL DEFAULT 0" ),
1341 - array( "revision", "rev_len", "INTEGER" ),
1342 - array( "revision", "rev_parent_id", "INTEGER DEFAULT NULL" ),
1343 - array( "site_stats", "ss_active_users", "INTEGER DEFAULT '-1'" ),
1344 - array( "user_newtalk", "user_last_timestamp", "TIMESTAMPTZ" ),
1345 - array( "logging", "log_user_text", "TEXT NOT NULL DEFAULT ''" ),
1346 - array( "logging", "log_page", "INTEGER" ),
1347 - array( "interwiki", "iw_api", "TEXT NOT NULL DEFAULT ''"),
1348 - array( "interwiki", "iw_wikiid", "TEXT NOT NULL DEFAULT ''"),
1349 - );
1350 -
1351 -
1352 - # table, column, desired type, USING clause if needed (with new default if needed)
1353 - $typechanges = array(
1354 - array( "archive", "ar_deleted", "smallint", "" ),
1355 - array( "archive", "ar_minor_edit", "smallint", "ar_minor_edit::smallint DEFAULT 0" ),
1356 - array( "filearchive", "fa_deleted", "smallint", "" ),
1357 - array( "filearchive", "fa_height", "integer", "" ),
1358 - array( "filearchive", "fa_metadata", "bytea", "decode(fa_metadata,'escape')" ),
1359 - array( "filearchive", "fa_size", "integer", "" ),
1360 - array( "filearchive", "fa_width", "integer", "" ),
1361 - array( "filearchive", "fa_storage_group", "text", "" ),
1362 - array( "filearchive", "fa_storage_key", "text", "" ),
1363 - array( "image", "img_metadata", "bytea", "decode(img_metadata,'escape')" ),
1364 - array( "image", "img_size", "integer", "" ),
1365 - array( "image", "img_width", "integer", "" ),
1366 - array( "image", "img_height", "integer", "" ),
1367 - array( "interwiki", "iw_local", "smallint", "iw_local::smallint DEFAULT 0" ),
1368 - array( "interwiki", "iw_trans", "smallint", "iw_trans::smallint DEFAULT 0" ),
1369 - array( "ipblocks", "ipb_auto", "smallint", "ipb_auto::smallint DEFAULT 0" ),
1370 - array( "ipblocks", "ipb_anon_only", "smallint", "CASE WHEN ipb_anon_only=' ' THEN 0 ELSE ipb_anon_only::smallint END DEFAULT 0" ),
1371 - array( "ipblocks", "ipb_create_account", "smallint", "CASE WHEN ipb_create_account=' ' THEN 0 ELSE ipb_create_account::smallint END DEFAULT 1" ),
1372 - array( "ipblocks", "ipb_enable_autoblock", "smallint", "CASE WHEN ipb_enable_autoblock=' ' THEN 0 ELSE ipb_enable_autoblock::smallint END DEFAULT 1" ),
1373 - array( "ipblocks", "ipb_block_email", "smallint", "CASE WHEN ipb_block_email=' ' THEN 0 ELSE ipb_block_email::smallint END DEFAULT 0" ),
1374 - array( "ipblocks", "ipb_address", "text", "ipb_address::text" ),
1375 - array( "ipblocks", "ipb_deleted", "smallint", "ipb_deleted::smallint DEFAULT 0" ),
1376 - array( "math", "math_inputhash", "bytea", "decode(math_inputhash,'escape')" ),
1377 - array( "math", "math_outputhash", "bytea", "decode(math_outputhash,'escape')" ),
1378 - array( "mwuser", "user_token", "text", "" ),
1379 - array( "mwuser", "user_email_token", "text", "" ),
1380 - array( "objectcache", "keyname", "text", "" ),
1381 - array( "oldimage", "oi_height", "integer", "" ),
1382 - array( "oldimage", "oi_metadata", "bytea", "decode(img_metadata,'escape')" ),
1383 - array( "oldimage", "oi_size", "integer", "" ),
1384 - array( "oldimage", "oi_width", "integer", "" ),
1385 - array( "page", "page_is_redirect", "smallint", "page_is_redirect::smallint DEFAULT 0" ),
1386 - array( "page", "page_is_new", "smallint", "page_is_new::smallint DEFAULT 0" ),
1387 - array( "querycache", "qc_value", "integer", "" ),
1388 - array( "querycachetwo", "qcc_value", "integer", "" ),
1389 - array( "recentchanges", "rc_bot", "smallint", "rc_bot::smallint DEFAULT 0" ),
1390 - array( "recentchanges", "rc_deleted", "smallint", "" ),
1391 - array( "recentchanges", "rc_minor", "smallint", "rc_minor::smallint DEFAULT 0" ),
1392 - array( "recentchanges", "rc_new", "smallint", "rc_new::smallint DEFAULT 0" ),
1393 - array( "recentchanges", "rc_type", "smallint", "rc_type::smallint DEFAULT 0" ),
1394 - array( "recentchanges", "rc_patrolled", "smallint", "rc_patrolled::smallint DEFAULT 0" ),
1395 - array( "revision", "rev_deleted", "smallint", "rev_deleted::smallint DEFAULT 0" ),
1396 - array( "revision", "rev_minor_edit", "smallint", "rev_minor_edit::smallint DEFAULT 0" ),
1397 - array( "templatelinks", "tl_namespace", "smallint", "tl_namespace::smallint" ),
1398 - array( "user_newtalk", "user_ip", "text", "host(user_ip)" ),
1399 - );
1400 -
1401 - # table, column, nullability
1402 - $nullchanges = array(
1403 - array( "oldimage", "oi_bits", "NULL" ),
1404 - array( "oldimage", "oi_timestamp", "NULL" ),
1405 - array( "oldimage", "oi_major_mime", "NULL" ),
1406 - array( "oldimage", "oi_minor_mime", "NULL" ),
1407 - );
1408 -
1409 - $newindexes = array(
1410 - array( "archive", "archive_user_text", "(ar_user_text)" ),
1411 - array( "image", "img_sha1", "(img_sha1)" ),
1412 - array( "oldimage", "oi_sha1", "(oi_sha1)" ),
1413 - array( "page", "page_mediawiki_title", "(page_title) WHERE page_namespace = 8" ),
1414 - array( "revision", "rev_text_id_idx", "(rev_text_id)" ),
1415 - array( "recentchanges", "rc_timestamp_bot", "(rc_timestamp) WHERE rc_bot = 0" ),
1416 - array( "templatelinks", "templatelinks_from", "(tl_from)" ),
1417 - array( "watchlist", "wl_user", "(wl_user)" ),
1418 - array( "logging", "logging_user_type_time", "(log_user, log_type, log_timestamp)" ),
1419 - array( "logging", "logging_page_id_time", "(log_page,log_timestamp)" ),
1420 - array( "iwlinks", "iwl_prefix_title_from", "(iwl_prefix, iwl_title, iwl_from)" ),
1421 - );
1422 -
1423 - $newrules = array(
1424 - );
1425 -
1426 - # # All FK columns should be deferred
1427 - $deferredcols = array(
1428 - array( "archive", "ar_user", "mwuser(user_id) ON DELETE SET NULL" ),
1429 - array( "categorylinks", "cl_from", "page(page_id) ON DELETE CASCADE" ),
1430 - array( "externallinks", "el_from", "page(page_id) ON DELETE CASCADE" ),
1431 - array( "filearchive", "fa_deleted_user", "mwuser(user_id) ON DELETE SET NULL" ),
1432 - array( "filearchive", "fa_user", "mwuser(user_id) ON DELETE SET NULL" ),
1433 - array( "image", "img_user", "mwuser(user_id) ON DELETE SET NULL" ),
1434 - array( "imagelinks", "il_from", "page(page_id) ON DELETE CASCADE" ),
1435 - array( "ipblocks", "ipb_by", "mwuser(user_id) ON DELETE CASCADE" ),
1436 - array( "ipblocks", "ipb_user", "mwuser(user_id) ON DELETE SET NULL" ),
1437 - array( "langlinks", "ll_from", "page(page_id) ON DELETE CASCADE" ),
1438 - array( "logging", "log_user", "mwuser(user_id) ON DELETE SET NULL" ),
1439 - array( "oldimage", "oi_name", "image(img_name) ON DELETE CASCADE ON UPDATE CASCADE" ),
1440 - array( "oldimage", "oi_user", "mwuser(user_id) ON DELETE SET NULL" ),
1441 - array( "pagelinks", "pl_from", "page(page_id) ON DELETE CASCADE" ),
1442 - array( "page_props", "pp_page", "page (page_id) ON DELETE CASCADE" ),
1443 - array( "page_restrictions", "pr_page", "page(page_id) ON DELETE CASCADE" ),
1444 - array( "protected_titles", "pt_user", "mwuser(user_id) ON DELETE SET NULL" ),
1445 - array( "recentchanges", "rc_cur_id", "page(page_id) ON DELETE SET NULL" ),
1446 - array( "recentchanges", "rc_user", "mwuser(user_id) ON DELETE SET NULL" ),
1447 - array( "redirect", "rd_from", "page(page_id) ON DELETE CASCADE" ),
1448 - array( "revision", "rev_page", "page (page_id) ON DELETE CASCADE" ),
1449 - array( "revision", "rev_user", "mwuser(user_id) ON DELETE RESTRICT" ),
1450 - array( "templatelinks", "tl_from", "page(page_id) ON DELETE CASCADE" ),
1451 - array( "trackbacks", "tb_page", "page(page_id) ON DELETE CASCADE" ),
1452 - array( "user_groups", "ug_user", "mwuser(user_id) ON DELETE CASCADE" ),
1453 - array( "user_newtalk", "user_id", "mwuser(user_id) ON DELETE CASCADE" ),
1454 - array( "user_properties", "up_user", "mwuser(user_id) ON DELETE CASCADE" ),
1455 - array( "watchlist", "wl_user", "mwuser(user_id) ON DELETE CASCADE" ),
1456 - );
1457 -
1458 - # Create new sequences
1459 - foreach ( $newsequences as $ns ) {
1460 - if ( ! $wgDatabase->sequenceExists( $ns ) ) {
1461 - wfOut( "Creating sequence $ns\n" );
1462 - $wgDatabase->query( "CREATE SEQUENCE $ns" );
1463 - }
1464 - }
1465 -
1466 - # Rename sequences
1467 - foreach ( $renamed_sequences as $ren ) {
1468 - if ( $wgDatabase->sequenceExists( $ren[0] ) ) {
1469 - wfOut( "Renaming sequence $ren[0] to $ren[1]\n" );
1470 - $wgDatabase->query( "ALTER SEQUENCE $ren[0] RENAME TO $ren[1]" );
1471 - }
1472 - }
1473 -
1474 - # Create new tables
1475 - foreach ( $newtables as $nt ) {
1476 - if ( $wgDatabase->tableExists( $nt[0] ) ) {
1477 - wfOut( "... table \"$nt[0]\" already exists\n" );
1478 - continue;
1479 - }
1480 -
1481 - wfOut( "Creating table \"$nt[0]\"\n" );
1482 - $wgDatabase->sourceFile( archive( $nt[1] ) );
1483 - }
1484 -
1485 - # Needed before newcols
1486 - if ( $wgDatabase->tableExists( "archive2" ) ) {
1487 - wfOut( "Converting \"archive2\" back to normal archive table\n" );
1488 - if ( $wgDatabase->ruleExists( "archive", "archive_insert" ) ) {
1489 - wfOut( "Dropping rule \"archive_insert\"\n" );
1490 - $wgDatabase->query( "DROP RULE archive_insert ON archive" );
1491 - }
1492 - if ( $wgDatabase->ruleExists( "archive", "archive_delete" ) ) {
1493 - wfOut( "Dropping rule \"archive_delete\"\n" );
1494 - $wgDatabase->query( "DROP RULE archive_delete ON archive" );
1495 - }
1496 - $wgDatabase->sourceFile( archive( "patch-remove-archive2.sql" ) );
1497 - }
1498 - else
1499 - wfOut( "... obsolete table \"archive2\" does not exist\n" );
1500 -
1501 - foreach ( $newcols as $nc ) {
1502 - $fi = $wgDatabase->fieldInfo( $nc[0], $nc[1] );
1503 - if ( !is_null( $fi ) ) {
1504 - wfOut( "... column \"$nc[0].$nc[1]\" already exists\n" );
1505 - continue;
1506 - }
1507 -
1508 - wfOut( "Adding column \"$nc[0].$nc[1]\"\n" );
1509 - $wgDatabase->query( "ALTER TABLE $nc[0] ADD $nc[1] $nc[2]" );
1510 - }
1511 -
1512 - foreach ( $typechanges as $tc ) {
1513 - $fi = $wgDatabase->fieldInfo( $tc[0], $tc[1] );
1514 - if ( is_null( $fi ) ) {
1515 - wfOut( "... error: expected column $tc[0].$tc[1] to exist\n" );
1516 - exit( 1 );
1517 - }
1518 -
1519 - if ( $fi->type() === $tc[2] )
1520 - wfOut( "... column \"$tc[0].$tc[1]\" is already of type \"$tc[2]\"\n" );
1521 - else {
1522 - wfOut( "Changing column type of \"$tc[0].$tc[1]\" from \"{$fi->type()}\" to \"$tc[2]\"\n" );
1523 - $sql = "ALTER TABLE $tc[0] ALTER $tc[1] TYPE $tc[2]";
1524 - if ( strlen( $tc[3] ) ) {
1525 - $default = array();
1526 - if ( preg_match( '/DEFAULT (.+)/', $tc[3], $default ) ) {
1527 - $sqldef = "ALTER TABLE $tc[0] ALTER $tc[1] SET DEFAULT $default[1]";
1528 - $wgDatabase->query( $sqldef );
1529 - $tc[3] = preg_replace( '/\s*DEFAULT .+/', '', $tc[3] );
1530 - }
1531 - $sql .= " USING $tc[3]";
1532 - }
1533 - $sql .= ";\nCOMMIT;\n";
1534 - $wgDatabase->query( $sql );
1535 - }
1536 - }
1537 -
1538 - foreach ( $nullchanges as $nc ) {
1539 - $fi = $wgDatabase->fieldInfo( $nc[0], $nc[1] );
1540 - if ( is_null( $fi ) ) {
1541 - wfOut( "... error: expected column $nc[0].$nc[1] to exist\n" );
1542 - exit( 1 );
1543 - }
1544 - if ( $fi->nullable() ) {
1545 - # # It's NULL - does it need to be NOT NULL?
1546 - if ( 'NOT NULL' === $nc[2] ) {
1547 - wfOut( "Changing \"$nc[0].$nc[1]\" to not allow NULLs\n" );
1548 - $wgDatabase->query( "ALTER TABLE $nc[0] ALTER $nc[1] SET NOT NULL" );
1549 - } else {
1550 - wfOut( "... column \"$nc[0].$nc[1]\" is already set as NULL\n" );
1551 - }
1552 - } else {
1553 - # # It's NOT NULL - does it need to be NULL?
1554 - if ( 'NULL' === $nc[2] ) {
1555 - wfOut( "Changing \"$nc[0].$nc[1]\" to allow NULLs\n" );
1556 - $wgDatabase->query( "ALTER TABLE $nc[0] ALTER $nc[1] DROP NOT NULL" );
1557 - }
1558 - else {
1559 - wfOut( "... column \"$nc[0].$nc[1]\" is already set as NOT NULL\n" );
1560 - }
1561 - }
1562 - }
1563 -
1564 - if ( $wgDatabase->fieldInfo( 'oldimage', 'oi_deleted' )->type() !== 'smallint' ) {
1565 - wfOut( "Changing \"oldimage.oi_deleted\" to type \"smallint\"\n" );
1566 - $wgDatabase->query( "ALTER TABLE oldimage ALTER oi_deleted DROP DEFAULT" );
1567 - $wgDatabase->query( "ALTER TABLE oldimage ALTER oi_deleted TYPE SMALLINT USING (oi_deleted::smallint)" );
1568 - $wgDatabase->query( "ALTER TABLE oldimage ALTER oi_deleted SET DEFAULT 0" );
1569 - } else {
1570 - wfOut( "... column \"oldimage.oi_deleted\" is already of type \"smallint\"\n" );
1571 - }
1572 -
1573 - foreach ( $newindexes as $ni ) {
1574 - if ( pg_index_exists( $ni[0], $ni[1] ) ) {
1575 - wfOut( "... index \"$ni[1]\" on table \"$ni[0]\" already exists\n" );
1576 - continue;
1577 - }
1578 - wfOut( "Creating index \"$ni[1]\" on table \"$ni[0]\" $ni[2]\n" );
1579 - $wgDatabase->query( "CREATE INDEX $ni[1] ON $ni[0] $ni[2]" );
1580 - }
1581 -
1582 - foreach ( $newrules as $nr ) {
1583 - if ( $wgDatabase->ruleExists( $nr[0], $nr[1] ) ) {
1584 - wfOut( "... rule \"$nr[1]\" on table \"$nr[0]\" already exists\n" );
1585 - continue;
1586 - }
1587 - wfOut( "Adding rule \"$nr[1]\" to table \"$nr[0]\"\n" );
1588 - $wgDatabase->sourceFile( archive( $nr[2] ) );
1589 - }
1590 -
1591 - if ( $wgDatabase->hasConstraint( "oldimage_oi_name_fkey_cascaded" ) ) {
1592 - wfOut( "... table \"oldimage\" has correct cascading delete/update foreign key to image\n" );
1593 - } else {
1594 - if ( $wgDatabase->hasConstraint( "oldimage_oi_name_fkey" ) ) {
1595 - $wgDatabase->query( "ALTER TABLE oldimage DROP CONSTRAINT oldimage_oi_name_fkey" );
1596 - }
1597 - if ( $wgDatabase->hasConstraint( "oldimage_oi_name_fkey_cascade" ) ) {
1598 - $wgDatabase->query( "ALTER TABLE oldimage DROP CONSTRAINT oldimage_oi_name_fkey_cascade" );
1599 - }
1600 - wfOut( "Making foreign key on table \"oldimage\" (to image) a cascade delete/update\n" );
1601 - $wgDatabase->query( "ALTER TABLE oldimage ADD CONSTRAINT oldimage_oi_name_fkey_cascaded " .
1602 - "FOREIGN KEY (oi_name) REFERENCES image(img_name) ON DELETE CASCADE ON UPDATE CASCADE" );
1603 - }
1604 -
1605 - if ( !$wgDatabase->triggerExists( "page", "page_deleted" ) ) {
1606 - wfOut( "Adding function and trigger \"page_deleted\" to table \"page\"\n" );
1607 - $wgDatabase->sourceFile( archive( 'patch-page_deleted.sql' ) );
1608 - } else {
1609 - wfOut( "... table \"page\" has \"page_deleted\" trigger\n" );
1610 - }
1611 -
1612 - $fi = $wgDatabase->fieldInfo( "recentchanges", "rc_cur_id" );
1613 - if ( !$fi->nullable() ) {
1614 - wfOut( "Removing NOT NULL constraint from \"recentchanges.rc_cur_id\"\n" );
1615 - $wgDatabase->sourceFile( archive( 'patch-rc_cur_id-not-null.sql' ) );
1616 - } else {
1617 - wfOut( "... column \"recentchanges.rc_cur_id\" has a NOT NULL constraint\n" );
1618 - }
1619 -
1620 - $pu = pg_describe_index( "pagelink_unique" );
1621 - if ( !is_null( $pu ) && ( $pu[0] != "pl_from" || $pu[1] != "pl_namespace" || $pu[2] != "pl_title" ) ) {
1622 - wfOut( "Dropping obsolete version of index \"pagelink_unique index\"\n" );
1623 - $wgDatabase->query( "DROP INDEX pagelink_unique" );
1624 - $pu = null;
1625 - } else {
1626 - wfOut( "... obsolete version of index \"pagelink_unique index\" does not exist\n" );
1627 - }
1628 -
1629 - if ( is_null( $pu ) ) {
1630 - wfOut( "Creating index \"pagelink_unique index\"\n" );
1631 - $wgDatabase->query( "CREATE UNIQUE INDEX pagelink_unique ON pagelinks (pl_from,pl_namespace,pl_title)" );
1632 - } else {
1633 - wfOut( "... index \"pagelink_unique_index\" already exists\n" );
1634 - }
1635 -
1636 - if ( pg_fkey_deltype( "revision_rev_user_fkey" ) == 'r' ) {
1637 - wfOut( "... constraint \"revision_rev_user_fkey\" is ON DELETE RESTRICT\n" );
1638 - } else {
1639 - wfOut( "Changing constraint \"revision_rev_user_fkey\" to ON DELETE RESTRICT\n" );
1640 - $wgDatabase->sourceFile( archive( 'patch-revision_rev_user_fkey.sql' ) );
1641 - }
1642 -
1643 - # Fix ipb_address index
1644 - if ( pg_index_exists( 'ipblocks', 'ipb_address' ) ) {
1645 - wfOut( "Removing deprecated index 'ipb_address'...\n" );
1646 - $wgDatabase->query( 'DROP INDEX ipb_address' );
1647 - }
1648 - if ( pg_index_exists( 'ipblocks', 'ipb_address_unique' ) ) {
1649 - wfOut( "... have ipb_address_unique\n" );
1650 - } else {
1651 - wfOut( "Adding ipb_address_unique index\n" );
1652 - $wgDatabase->sourceFile( archive( 'patch-ipb_address_unique.sql' ) );
1653 - }
1654 -
1655 - # Fix iwlinks index
1656 - if ( pg_index_exists( 'iwlinks', 'iwl_prefix' ) ) {
1657 - wfOut( "Replacing index 'iwl_prefix' with 'iwl_prefix_from_title'...\n" );
1658 - $wgDatabase->sourceFile( archive( 'patch-rename-iwl_prefix.sql' ) );
1659 - }
1660 -
1661 - global $wgExtNewTables, $wgExtPGNewFields, $wgExtPGAlteredFields, $wgExtNewIndexes;
1662 - # Add missing extension tables
1663 - foreach ( $wgExtNewTables as $nt ) {
1664 - if ( $wgDatabase->tableExists( $nt[0] ) ) {
1665 - wfOut( "... table \"$nt[0]\" already exists\n" );
1666 - continue;
1667 - }
1668 - wfOut( "Creating table \"$nt[0]\"\n" );
1669 - $wgDatabase->sourceFile( $nt[1] );
1670 - }
1671 - # Add missing extension fields
1672 - foreach ( $wgExtPGNewFields as $nc ) {
1673 - $fi = $wgDatabase->fieldInfo( $nc[0], $nc[1] );
1674 - if ( !is_null( $fi ) ) {
1675 - wfOut( "... column \"$nc[0].$nc[1]\" already exists\n" );
1676 - continue;
1677 - }
1678 - wfOut( "Adding column \"$nc[0].$nc[1]\"\n" );
1679 - $wgDatabase->query( "ALTER TABLE $nc[0] ADD $nc[1] $nc[2]" );
1680 - }
1681 - # Change altered columns
1682 - foreach ( $wgExtPGAlteredFields as $nc ) {
1683 - $fi = $wgDatabase->fieldInfo( $nc[0], $nc[1] );
1684 - if ( is_null( $fi ) ) {
1685 - wfOut( "WARNING! Column \"$nc[0].$nc[1]\" does not exist but had an alter request! Please report this.\n" );
1686 - continue;
1687 - }
1688 - $oldtype = $fi->type();
1689 - $newtype = strtolower( $nc[2] );
1690 - if ( $oldtype === $newtype ) {
1691 - wfOut( "... column \"$nc[0].$nc[1]\" has correct type of \"$newtype\"\n" );
1692 - continue;
1693 - }
1694 - $command = "ALTER TABLE $nc[0] ALTER $nc[1] TYPE $nc[2]";
1695 - if ( isset( $nc[3] ) ) {
1696 - $command .= " USING $nc[3]";
1697 - }
1698 - wfOut( "Altering column \"$nc[0].$nc[1]\" from type \"$oldtype\" to \"$newtype\"\n" );
1699 - $wgDatabase->query( $command );
1700 - }
1701 - # Add missing extension indexes
1702 - foreach ( $wgExtNewIndexes as $ni ) {
1703 - if ( pg_index_exists( $ni[0], $ni[1] ) ) {
1704 - wfOut( "... index \"$ni[1]\" on table \"$ni[0]\" already exists\n" );
1705 - continue;
1706 - }
1707 - wfOut( "Creating index \"$ni[1]\" on table \"$ni[0]\"\n" );
1708 - if ( preg_match( '/^\(/', $ni[2] ) ) {
1709 - $wgDatabase->query( "CREATE INDEX $ni[1] ON $ni[0] $ni[2]" );
1710 - }
1711 - else {
1712 - $wgDatabase->sourceFile( $ni[2] );
1713 - }
1714 - }
1715 -
1716 - foreach ( $deferredcols AS $dc ) {
1717 - $fi = $wgDatabase->fieldInfo( $dc[0], $dc[1] );
1718 - if ( is_null( $fi ) ) {
1719 - wfOut( "WARNING! Column \"$dc[0].$dc[1]\" does not exist but it should! Please report this.\n" );
1720 - continue;
1721 - }
1722 - if ( $fi->is_deferred() && $fi->is_deferrable() ) {
1723 - continue;
1724 - }
1725 - wfOut( "Altering column \"$dc[0].$dc[1]\" to be DEFERRABLE INITIALLY DEFERRED\n" );
1726 - $conname = $fi->conname();
1727 - $clause = $dc[2];
1728 - $command = "ALTER TABLE $dc[0] DROP CONSTRAINT $conname";
1729 - $wgDatabase->query( $command );
1730 - $command = "ALTER TABLE $dc[0] ADD CONSTRAINT $conname FOREIGN KEY ($dc[1]) REFERENCES $clause DEFERRABLE INITIALLY DEFERRED";
1731 - $wgDatabase->query( $command );
1732 - }
1733 -
1734 - # Tweak the page_title tsearch2 trigger to filter out slashes
1735 - # This is create or replace, so harmless to call if not needed
1736 - $wgDatabase->sourceFile( archive( 'patch-ts2pagetitle.sql' ) );
1737 -
1738 - # # If the server is 8.3 or higher, rewrite the tsearch2 triggers
1739 - # # in case they have the old 'default' versions
1740 - if ( $numver >= 8.3 ) {
1741 - $wgDatabase->sourceFile( archive( 'patch-tsearch2funcs.sql' ) );
1742 - }
1743 - return;
1744 -}
Index: trunk/phase3/includes/installer/DatabaseUpdater.php
@@ -98,7 +98,7 @@
9999 * version these like we do with our core updates, so they have to go
100100 * in 'always'
101101 */
102 - private function getOldGlobalUpdates() {
 102+ protected function getOldGlobalUpdates() {
103103 global $wgUpdates, $wgExtNewFields, $wgExtNewTables,
104104 $wgExtModifiedFields, $wgExtNewIndexes, $wgSharedDB, $wgSharedTables;
105105
Index: trunk/phase3/includes/installer/PostgresUpdater.php
@@ -9,21 +9,670 @@
1010 /**
1111 * Class for handling updates to Postgres databases.
1212 *
13 - * @todo FIXME: Postgres should use sequential updates like Mysql, Sqlite
14 - * and everybody else. It never got refactored like it should've. For now,
15 - * just wrap the old do_postgres_updates() in this class so we can clean up
16 - * the higher-level stuff.
17 - *
1813 * @ingroup Deployment
1914 * @since 1.17
2015 */
2116
2217 class PostgresUpdater extends DatabaseUpdater {
 18+
 19+ /**
 20+ * @todo FIXME: Postgres should use sequential updates like Mysql, Sqlite
 21+ * and everybody else. It never got refactored like it should've.
 22+ */
2323 protected function getCoreUpdateList() {
24 - return array();
 24+ return array(
 25+ # beginning
 26+ array( 'checkPgUser' ),
 27+
 28+ # new sequences
 29+ array( 'addSequence', 'logging_log_id_seq' ),
 30+ array( 'addSequence', 'page_restrictions_pr_id_seq' ),
 31+
 32+ # renamed sequences
 33+ array( 'renameSequence', 'ipblocks_ipb_id_val', 'ipblocks_ipb_id_seq' ),
 34+ array( 'renameSequence', 'rev_rev_id_val', 'revision_rev_id_seq' ),
 35+ array( 'renameSequence', 'text_old_id_val', 'text_old_id_seq' ),
 36+ array( 'renameSequence', 'category_id_seq', 'category_cat_id_seq' ),
 37+ array( 'renameSequence', 'rc_rc_id_seq', 'recentchanges_rc_id_seq' ),
 38+ array( 'renameSequence', 'log_log_id_seq', 'logging_log_id_seq' ),
 39+ array( 'renameSequence', 'pr_id_val', 'page_restrictions_pr_id_seq' ),
 40+
 41+ # new tables
 42+ array( 'addTable', 'category', 'patch-category.sql' ),
 43+ array( 'addTable', 'mwuser', 'patch-mwuser.sql' ),
 44+ array( 'addTable', 'pagecontent', 'patch-pagecontent.sql' ),
 45+ array( 'addTable', 'querycachetwo', 'patch-querycachetwo.sql' ),
 46+ array( 'addTable', 'page_props', 'patch-page_props.sql' ),
 47+ array( 'addTable', 'page_restrictions', 'patch-page_restrictions.sql' ),
 48+ array( 'addTable', 'profiling', 'patch-profiling.sql' ),
 49+ array( 'addTable', 'protected_titles', 'patch-protected_titles.sql' ),
 50+ array( 'addTable', 'redirect', 'patch-redirect.sql' ),
 51+ array( 'addTable', 'updatelog', 'patch-updatelog.sql' ),
 52+ array( 'addTable', 'change_tag', 'patch-change_tag.sql' ),
 53+ array( 'addTable', 'tag_summary', 'patch-change_tag.sql' ),
 54+ array( 'addTable', 'valid_tag', 'patch-change_tag.sql' ),
 55+ array( 'addTable', 'user_properties', 'patch-user_properties.sql' ),
 56+ array( 'addTable', 'log_search', 'patch-log_search.sql' ),
 57+ array( 'addTable', 'l10n_cache', 'patch-l10n_cache.sql' ),
 58+ array( 'addTable', 'iwlinks', 'patch-iwlinks.sql' ),
 59+
 60+ # Needed before new field
 61+ array( 'convertArchive2' ),
 62+
 63+ # new fields
 64+ array( 'addPgField', 'archive', 'ar_deleted', 'SMALLINT NOT NULL DEFAULT 0' ),
 65+ array( 'addPgField', 'archive', 'ar_len', 'INTEGER' ),
 66+ array( 'addPgField', 'archive', 'ar_page_id', 'INTEGER' ),
 67+ array( 'addPgField', 'archive', 'ar_parent_id', 'INTEGER' ),
 68+ array( 'addPgField', 'image', 'img_sha1', "TEXT NOT NULL DEFAULT ''" ),
 69+ array( 'addPgField', 'ipblocks', 'ipb_allow_usertalk', 'SMALLINT NOT NULL DEFAULT 0' ),
 70+ array( 'addPgField', 'ipblocks', 'ipb_anon_only', 'SMALLINT NOT NULL DEFAULT 0' ),
 71+ array( 'addPgField', 'ipblocks', 'ipb_by_text', "TEXT NOT NULL DEFAULT ''" ),
 72+ array( 'addPgField', 'ipblocks', 'ipb_block_email', 'SMALLINT NOT NULL DEFAULT 0' ),
 73+ array( 'addPgField', 'ipblocks', 'ipb_create_account', 'SMALLINT NOT NULL DEFAULT 1' ),
 74+ array( 'addPgField', 'ipblocks', 'ipb_deleted', 'SMALLINT NOT NULL DEFAULT 0' ),
 75+ array( 'addPgField', 'ipblocks', 'ipb_enable_autoblock', 'SMALLINT NOT NULL DEFAULT 1' ),
 76+ array( 'addPgField', 'filearchive', 'fa_deleted', 'SMALLINT NOT NULL DEFAULT 0' ),
 77+ array( 'addPgField', 'logging', 'log_deleted', 'SMALLINT NOT NULL DEFAULT 0' ),
 78+ array( 'addPgField', 'logging', 'log_id', "INTEGER NOT NULL PRIMARY KEY DEFAULT nextval('logging_log_id_seq')" ),
 79+ array( 'addPgField', 'logging', 'log_params', 'TEXT' ),
 80+ array( 'addPgField', 'mwuser', 'user_editcount', 'INTEGER' ),
 81+ array( 'addPgField', 'mwuser', 'user_hidden', 'SMALLINT NOT NULL DEFAULT 0' ),
 82+ array( 'addPgField', 'mwuser', 'user_newpass_time', 'TIMESTAMPTZ' ),
 83+ array( 'addPgField', 'oldimage', 'oi_deleted', 'SMALLINT NOT NULL DEFAULT 0' ),
 84+ array( 'addPgField', 'oldimage', 'oi_major_mime', "TEXT NOT NULL DEFAULT 'unknown'" ),
 85+ array( 'addPgField', 'oldimage', 'oi_media_type', 'TEXT' ),
 86+ array( 'addPgField', 'oldimage', 'oi_metadata', "BYTEA NOT NULL DEFAULT ''" ),
 87+ array( 'addPgField', 'oldimage', 'oi_minor_mime', "TEXT NOT NULL DEFAULT 'unknown'" ),
 88+ array( 'addPgField', 'oldimage', 'oi_sha1', "TEXT NOT NULL DEFAULT ''" ),
 89+ array( 'addPgField', 'page_restrictions', 'pr_id', "INTEGER NOT NULL UNIQUE DEFAULT nextval('page_restrictions_pr_id_seq')" ),
 90+ array( 'addPgField', 'profiling', 'pf_memory', 'NUMERIC(18,10) NOT NULL DEFAULT 0' ),
 91+ array( 'addPgField', 'recentchanges', 'rc_deleted', 'SMALLINT NOT NULL DEFAULT 0' ),
 92+ array( 'addPgField', 'recentchanges', 'rc_log_action', 'TEXT' ),
 93+ array( 'addPgField', 'recentchanges', 'rc_log_type', 'TEXT' ),
 94+ array( 'addPgField', 'recentchanges', 'rc_logid', 'INTEGER NOT NULL DEFAULT 0' ),
 95+ array( 'addPgField', 'recentchanges', 'rc_new_len', 'INTEGER' ),
 96+ array( 'addPgField', 'recentchanges', 'rc_old_len', 'INTEGER' ),
 97+ array( 'addPgField', 'recentchanges', 'rc_params', 'TEXT' ),
 98+ array( 'addPgField', 'redirect', 'rd_interwiki', 'TEXT NULL' ),
 99+ array( 'addPgField', 'redirect', 'rd_fragment', 'TEXT NULL' ),
 100+ array( 'addPgField', 'revision', 'rev_deleted', 'SMALLINT NOT NULL DEFAULT 0' ),
 101+ array( 'addPgField', 'revision', 'rev_len', 'INTEGER' ),
 102+ array( 'addPgField', 'revision', 'rev_parent_id', 'INTEGER DEFAULT NULL' ),
 103+ array( 'addPgField', 'site_stats', 'ss_active_users', "INTEGER DEFAULT '-1'" ),
 104+ array( 'addPgField', 'user_newtalk', 'user_last_timestamp', 'TIMESTAMPTZ' ),
 105+ array( 'addPgField', 'logging', 'log_user_text', "TEXT NOT NULL DEFAULT ''" ),
 106+ array( 'addPgField', 'logging', 'log_page', 'INTEGER' ),
 107+ array( 'addPgField', 'interwiki', 'iw_api', "TEXT NOT NULL DEFAULT ''"),
 108+ array( 'addPgField', 'interwiki', 'iw_wikiid', "TEXT NOT NULL DEFAULT ''"),
 109+
 110+ # type changes
 111+ array( 'changeField', 'archive', 'ar_deleted', 'smallint', '' ),
 112+ array( 'changeField', 'archive', 'ar_minor_edit', 'smallint', 'ar_minor_edit::smallint DEFAULT 0' ),
 113+ array( 'changeField', 'filearchive', 'fa_deleted', 'smallint', '' ),
 114+ array( 'changeField', 'filearchive', 'fa_height', 'integer', '' ),
 115+ array( 'changeField', 'filearchive', 'fa_metadata', 'bytea', "decode(fa_metadata,'escape')" ),
 116+ array( 'changeField', 'filearchive', 'fa_size', 'integer', '' ),
 117+ array( 'changeField', 'filearchive', 'fa_width', 'integer', '' ),
 118+ array( 'changeField', 'filearchive', 'fa_storage_group', 'text', '' ),
 119+ array( 'changeField', 'filearchive', 'fa_storage_key', 'text', '' ),
 120+ array( 'changeField', 'image', 'img_metadata', 'bytea', "decode(img_metadata,'escape')" ),
 121+ array( 'changeField', 'image', 'img_size', 'integer', '' ),
 122+ array( 'changeField', 'image', 'img_width', 'integer', '' ),
 123+ array( 'changeField', 'image', 'img_height', 'integer', '' ),
 124+ array( 'changeField', 'interwiki', 'iw_local', 'smallint', 'iw_local::smallint DEFAULT 0' ),
 125+ array( 'changeField', 'interwiki', 'iw_trans', 'smallint', 'iw_trans::smallint DEFAULT 0' ),
 126+ array( 'changeField', 'ipblocks', 'ipb_auto', 'smallint', 'ipb_auto::smallint DEFAULT 0' ),
 127+ array( 'changeField', 'ipblocks', 'ipb_anon_only', 'smallint', "CASE WHEN ipb_anon_only=' ' THEN 0 ELSE ipb_anon_only::smallint END DEFAULT 0" ),
 128+ array( 'changeField', 'ipblocks', 'ipb_create_account', 'smallint', "CASE WHEN ipb_create_account=' ' THEN 0 ELSE ipb_create_account::smallint END DEFAULT 1" ),
 129+ array( 'changeField', 'ipblocks', 'ipb_enable_autoblock', 'smallint', "CASE WHEN ipb_enable_autoblock=' ' THEN 0 ELSE ipb_enable_autoblock::smallint END DEFAULT 1" ),
 130+ array( 'changeField', 'ipblocks', 'ipb_block_email', 'smallint', "CASE WHEN ipb_block_email=' ' THEN 0 ELSE ipb_block_email::smallint END DEFAULT 0" ),
 131+ array( 'changeField', 'ipblocks', 'ipb_address', 'text', 'ipb_address::text' ),
 132+ array( 'changeField', 'ipblocks', 'ipb_deleted', 'smallint', 'ipb_deleted::smallint DEFAULT 0' ),
 133+ array( 'changeField', 'math', 'math_inputhash', 'bytea', "decode(math_inputhash,'escape')" ),
 134+ array( 'changeField', 'math', 'math_outputhash', 'bytea', "decode(math_outputhash,'escape')" ),
 135+ array( 'changeField', 'mwuser', 'user_token', 'text', '' ),
 136+ array( 'changeField', 'mwuser', 'user_email_token', 'text', '' ),
 137+ array( 'changeField', 'objectcache', 'keyname', 'text', '' ),
 138+ array( 'changeField', 'oldimage', 'oi_height', 'integer', '' ),
 139+ array( 'changeField', 'oldimage', 'oi_metadata', 'bytea', "decode(img_metadata,'escape')" ),
 140+ array( 'changeField', 'oldimage', 'oi_size', 'integer', '' ),
 141+ array( 'changeField', 'oldimage', 'oi_width', 'integer', '' ),
 142+ array( 'changeField', 'page', 'page_is_redirect', 'smallint', 'page_is_redirect::smallint DEFAULT 0' ),
 143+ array( 'changeField', 'page', 'page_is_new', 'smallint', 'page_is_new::smallint DEFAULT 0' ),
 144+ array( 'changeField', 'querycache', 'qc_value', 'integer', '' ),
 145+ array( 'changeField', 'querycachetwo', 'qcc_value', 'integer', '' ),
 146+ array( 'changeField', 'recentchanges', 'rc_bot', 'smallint', 'rc_bot::smallint DEFAULT 0' ),
 147+ array( 'changeField', 'recentchanges', 'rc_deleted', 'smallint', '' ),
 148+ array( 'changeField', 'recentchanges', 'rc_minor', 'smallint', 'rc_minor::smallint DEFAULT 0' ),
 149+ array( 'changeField', 'recentchanges', 'rc_new', 'smallint', 'rc_new::smallint DEFAULT 0' ),
 150+ array( 'changeField', 'recentchanges', 'rc_type', 'smallint', 'rc_type::smallint DEFAULT 0' ),
 151+ array( 'changeField', 'recentchanges', 'rc_patrolled', 'smallint', 'rc_patrolled::smallint DEFAULT 0' ),
 152+ array( 'changeField', 'revision', 'rev_deleted', 'smallint', 'rev_deleted::smallint DEFAULT 0' ),
 153+ array( 'changeField', 'revision', 'rev_minor_edit', 'smallint', 'rev_minor_edit::smallint DEFAULT 0' ),
 154+ array( 'changeField', 'templatelinks', 'tl_namespace', 'smallint', 'tl_namespace::smallint' ),
 155+ array( 'changeField', 'user_newtalk', 'user_ip', 'text', 'host(user_ip)' ),
 156+
 157+ # null changes
 158+ array( 'changeNullableField', 'oldimage', 'oi_bits', 'NULL' ),
 159+ array( 'changeNullableField', 'oldimage', 'oi_timestamp', 'NULL' ),
 160+ array( 'changeNullableField', 'oldimage', 'oi_major_mime', 'NULL' ),
 161+ array( 'changeNullableField', 'oldimage', 'oi_minor_mime', 'NULL' ),
 162+
 163+ array( 'checkOiDeleted' ),
 164+
 165+ # New indexes
 166+ array( 'addPgIndex', 'archive', 'archive_user_text', '(ar_user_text)' ),
 167+ array( 'addPgIndex', 'image', 'img_sha1', '(img_sha1)' ),
 168+ array( 'addPgIndex', 'oldimage', 'oi_sha1', '(oi_sha1)' ),
 169+ array( 'addPgIndex', 'page', 'page_mediawiki_title', '(page_title) WHERE page_namespace = 8' ),
 170+ array( 'addPgIndex', 'revision', 'rev_text_id_idx', '(rev_text_id)' ),
 171+ array( 'addPgIndex', 'recentchanges', 'rc_timestamp_bot', '(rc_timestamp) WHERE rc_bot = 0' ),
 172+ array( 'addPgIndex', 'templatelinks', 'templatelinks_from', '(tl_from)' ),
 173+ array( 'addPgIndex', 'watchlist', 'wl_user', '(wl_user)' ),
 174+ array( 'addPgIndex', 'logging', 'logging_user_type_time', '(log_user, log_type, log_timestamp)' ),
 175+ array( 'addPgIndex', 'logging', 'logging_page_id_time', '(log_page,log_timestamp)' ),
 176+ array( 'addPgIndex', 'iwlinks', 'iwl_prefix_title_from', '(iwl_prefix, iwl_title, iwl_from)' ),
 177+
 178+ array( 'checkOiNameConstraint' ),
 179+ array( 'checkPageDeletedTrigger' ),
 180+ array( 'checkRcCurIdNullable' ),
 181+ array( 'checkPagelinkUniqueIndex' ),
 182+ array( 'checkRevUserFkey' ),
 183+ array( 'checkIpbAdress' ),
 184+ array( 'checkIwlPrefix' ),
 185+
 186+ # All FK columns should be deferred
 187+ array( 'changeFkeyDeferrable', 'archive', 'ar_user', 'mwuser(user_id) ON DELETE SET NULL' ),
 188+ array( 'changeFkeyDeferrable', 'categorylinks', 'cl_from', 'page(page_id) ON DELETE CASCADE' ),
 189+ array( 'changeFkeyDeferrable', 'externallinks', 'el_from', 'page(page_id) ON DELETE CASCADE' ),
 190+ array( 'changeFkeyDeferrable', 'filearchive', 'fa_deleted_user', 'mwuser(user_id) ON DELETE SET NULL' ),
 191+ array( 'changeFkeyDeferrable', 'filearchive', 'fa_user', 'mwuser(user_id) ON DELETE SET NULL' ),
 192+ array( 'changeFkeyDeferrable', 'image', 'img_user', 'mwuser(user_id) ON DELETE SET NULL' ),
 193+ array( 'changeFkeyDeferrable', 'imagelinks', 'il_from', 'page(page_id) ON DELETE CASCADE' ),
 194+ array( 'changeFkeyDeferrable', 'ipblocks', 'ipb_by', 'mwuser(user_id) ON DELETE CASCADE' ),
 195+ array( 'changeFkeyDeferrable', 'ipblocks', 'ipb_user', 'mwuser(user_id) ON DELETE SET NULL' ),
 196+ array( 'changeFkeyDeferrable', 'langlinks', 'll_from', 'page(page_id) ON DELETE CASCADE' ),
 197+ array( 'changeFkeyDeferrable', 'logging', 'log_user', 'mwuser(user_id) ON DELETE SET NULL' ),
 198+ array( 'changeFkeyDeferrable', 'oldimage', 'oi_name', 'image(img_name) ON DELETE CASCADE ON UPDATE CASCADE' ),
 199+ array( 'changeFkeyDeferrable', 'oldimage', 'oi_user', 'mwuser(user_id) ON DELETE SET NULL' ),
 200+ array( 'changeFkeyDeferrable', 'pagelinks', 'pl_from', 'page(page_id) ON DELETE CASCADE' ),
 201+ array( 'changeFkeyDeferrable', 'page_props', 'pp_page', 'page (page_id) ON DELETE CASCADE' ),
 202+ array( 'changeFkeyDeferrable', 'page_restrictions', 'pr_page', 'page(page_id) ON DELETE CASCADE' ),
 203+ array( 'changeFkeyDeferrable', 'protected_titles', 'pt_user', 'mwuser(user_id) ON DELETE SET NULL' ),
 204+ array( 'changeFkeyDeferrable', 'recentchanges', 'rc_cur_id', 'page(page_id) ON DELETE SET NULL' ),
 205+ array( 'changeFkeyDeferrable', 'recentchanges', 'rc_user', 'mwuser(user_id) ON DELETE SET NULL' ),
 206+ array( 'changeFkeyDeferrable', 'redirect', 'rd_from', 'page(page_id) ON DELETE CASCADE' ),
 207+ array( 'changeFkeyDeferrable', 'revision', 'rev_page', 'page (page_id) ON DELETE CASCADE' ),
 208+ array( 'changeFkeyDeferrable', 'revision', 'rev_user', 'mwuser(user_id) ON DELETE RESTRICT' ),
 209+ array( 'changeFkeyDeferrable', 'templatelinks', 'tl_from', 'page(page_id) ON DELETE CASCADE' ),
 210+ array( 'changeFkeyDeferrable', 'trackbacks', 'tb_page', 'page(page_id) ON DELETE CASCADE' ),
 211+ array( 'changeFkeyDeferrable', 'user_groups', 'ug_user', 'mwuser(user_id) ON DELETE CASCADE' ),
 212+ array( 'changeFkeyDeferrable', 'user_newtalk', 'user_id', 'mwuser(user_id) ON DELETE CASCADE' ),
 213+ array( 'changeFkeyDeferrable', 'user_properties', 'up_user', 'mwuser(user_id) ON DELETE CASCADE' ),
 214+ array( 'changeFkeyDeferrable', 'watchlist', 'wl_user', 'mwuser(user_id) ON DELETE CASCADE' ),
 215+
 216+ # end
 217+ array( 'tsearchFixes' ),
 218+ );
25219 }
26220
27 - public function doUpdates() {
28 - do_postgres_updates();
 221+ protected function getOldGlobalUpdates() {
 222+ global $wgExtNewTables, $wgExtPGNewFields, $wgExtPGAlteredFields, $wgExtNewIndexes;
 223+
 224+ $updates = array();
 225+
 226+ # Add missing extension tables
 227+ foreach ( $wgExtNewTables as $tableRecord ) {
 228+ $updates[] = array(
 229+ 'addTable', $tableRecord[0], $tableRecord[1], true
 230+ );
 231+ }
 232+
 233+ # Add missing extension fields
 234+ foreach ( $wgExtPGNewFields as $nc ) {
 235+ $updates[] = array(
 236+ 'addPgField', $fieldRecord[0], $fieldRecord[1],
 237+ $fieldRecord[2]
 238+ );
 239+ }
 240+
 241+ # Change altered columns
 242+ foreach ( $wgExtPGAlteredFields as $fieldRecord ) {
 243+ $updates[] = array(
 244+ 'changeField', $fieldRecord[0], $fieldRecord[1],
 245+ $fieldRecord[2]
 246+ );
 247+ }
 248+
 249+ # Add missing extension indexes
 250+ foreach ( $wgExtNewIndexes as $ni ) {
 251+ $updates[] = array(
 252+ 'addPgExtIndex', $fieldRecord[0], $fieldRecord[1],
 253+ $fieldRecord[2]
 254+ );
 255+ }
 256+
 257+ return $updates;
29258 }
 259+
 260+ protected function describeTable( $table ) {
 261+ global $wgDBmwschema;
 262+ $q = <<<END
 263+SELECT attname, attnum FROM pg_namespace, pg_class, pg_attribute
 264+ WHERE pg_class.relnamespace = pg_namespace.oid
 265+ AND attrelid=pg_class.oid AND attnum > 0
 266+ AND relname=%s AND nspname=%s
 267+END;
 268+ $res = $this->db->query( sprintf( $q,
 269+ $this->db->addQuotes( $table ),
 270+ $this->db->addQuotes( $wgDBmwschema ) ) );
 271+ if ( !$res ) {
 272+ return null;
 273+ }
 274+
 275+ $cols = array();
 276+ while ( $r = $this->db->fetchRow( $res ) ) {
 277+ $cols[] = array(
 278+ "name" => $r[0],
 279+ "ord" => $r[1],
 280+ );
 281+ }
 282+ return $cols;
 283+ }
 284+
 285+ function describeIndex( $idx ) {
 286+ global $wgDBmwschema;
 287+
 288+ // first fetch the key (which is a list of columns ords) and
 289+ // the table the index applies to (an oid)
 290+ $q = <<<END
 291+SELECT indkey, indrelid FROM pg_namespace, pg_class, pg_index
 292+ WHERE nspname=%s
 293+ AND pg_class.relnamespace = pg_namespace.oid
 294+ AND relname=%s
 295+ AND indexrelid=pg_class.oid
 296+END;
 297+ $res = $this->db->query(
 298+ sprintf(
 299+ $q,
 300+ $this->db->addQuotes( $wgDBmwschema ),
 301+ $this->db->addQuotes( $idx )
 302+ )
 303+ );
 304+ if ( !$res ) {
 305+ return null;
 306+ }
 307+ if ( !( $r = $this->db->fetchRow( $res ) ) ) {
 308+ return null;
 309+ }
 310+
 311+ $indkey = $r[0];
 312+ $relid = intval( $r[1] );
 313+ $indkeys = explode( ' ', $indkey );
 314+
 315+ $colnames = array();
 316+ foreach ( $indkeys as $rid ) {
 317+ $query = <<<END
 318+SELECT attname FROM pg_class, pg_attribute
 319+ WHERE attrelid=$relid
 320+ AND attnum=%d
 321+ AND attrelid=pg_class.oid
 322+END;
 323+ $r2 = $this->db->query( sprintf( $query, $rid ) );
 324+ if ( !$r2 ) {
 325+ return null;
 326+ }
 327+ if ( !( $row2 = $this->db->fetchRow( $r2 ) ) ) {
 328+ return null;
 329+ }
 330+ $colnames[] = $row2[0];
 331+ }
 332+
 333+ return $colnames;
 334+ }
 335+
 336+ function fkeyDeltype( $fkey ) {
 337+ global $wgDBmwschema;
 338+ $q = <<<END
 339+SELECT confdeltype FROM pg_constraint, pg_namespace
 340+ WHERE connamespace=pg_namespace.oid
 341+ AND nspname=%s
 342+ AND conname=%s;
 343+END;
 344+ $r = $this->db->query(
 345+ sprintf(
 346+ $q,
 347+ $this->db->addQuotes( $wgDBmwschema ),
 348+ $this->db->addQuotes( $fkey )
 349+ )
 350+ );
 351+ if ( !( $row = $this->db->fetchRow( $r ) ) ) {
 352+ return null;
 353+ }
 354+ return $row[0];
 355+ }
 356+
 357+ function ruleDef( $table, $rule ) {
 358+ global $wgDBmwschema;
 359+ $q = <<<END
 360+SELECT definition FROM pg_rules
 361+ WHERE schemaname = %s
 362+ AND tablename = %s
 363+ AND rulename = %s
 364+END;
 365+ $r = $this->db->query(
 366+ sprintf(
 367+ $q,
 368+ $this->db->addQuotes( $wgDBmwschema ),
 369+ $this->db->addQuotes( $table ),
 370+ $this->db->addQuotes( $rule )
 371+ )
 372+ );
 373+ $row = $this->db->fetchRow( $r );
 374+ if ( !$row ) {
 375+ return null;
 376+ }
 377+ $d = $row[0];
 378+ return $d;
 379+ }
 380+
 381+ protected function addSequence( $ns ) {
 382+ if ( !$this->db->sequenceExists( $ns ) ) {
 383+ wfOut( "Creating sequence $ns\n" );
 384+ $this->db->query( "CREATE SEQUENCE $ns" );
 385+ }
 386+ }
 387+
 388+ protected function renameSequence( $old, $new ) {
 389+ if ( $this->db->sequenceExists( $old ) ) {
 390+ wfOut( "Renaming sequence $old to $new\n" );
 391+ $this->db->query( "ALTER SEQUENCE $old RENAME TO $new" );
 392+ }
 393+ }
 394+
 395+ protected function addPgField( $table, $field, $type ) {
 396+ $fi = $this->db->fieldInfo( $table, $field );
 397+ if ( !is_null( $fi ) ) {
 398+ wfOut( "... column \"$table.$field\" already exists\n" );
 399+ return;
 400+ } else {
 401+ wfOut( "Adding column \"$table.$field\"\n" );
 402+ $this->db->query( "ALTER TABLE $table ADD $field $type" );
 403+ }
 404+ }
 405+
 406+ protected function changeField( $table, $field, $newtype, $default ) {
 407+ $fi = $this->db->fieldInfo( $table, $field );
 408+ if ( is_null( $fi ) ) {
 409+ wfOut( "... error: expected column $table.$field to exist\n" );
 410+ exit( 1 );
 411+ }
 412+
 413+ if ( $fi->type() === $newtype )
 414+ wfOut( "... column \"$table.$field\" is already of type \"$newtype\"\n" );
 415+ else {
 416+ wfOut( "Changing column type of \"$table.$field\" from \"{$fi->type()}\" to \"$newtype\"\n" );
 417+ $sql = "ALTER TABLE $table ALTER $field TYPE $newtype";
 418+ if ( strlen( $default ) ) {
 419+ $res = array();
 420+ if ( preg_match( '/DEFAULT (.+)/', $default, $res ) ) {
 421+ $sqldef = "ALTER TABLE $table ALTER $field SET DEFAULT $res[1]";
 422+ $this->db->query( $sqldef );
 423+ $default = preg_replace( '/\s*DEFAULT .+/', '', $default );
 424+ }
 425+ $sql .= " USING $default";
 426+ }
 427+ $sql .= ";\nCOMMIT;\n";
 428+ $this->db->query( $sql );
 429+ }
 430+ }
 431+
 432+ protected function changeNullableField( $table, $field, $null ) {
 433+ $fi = $this->db->fieldInfo( $table, $field );
 434+ if ( is_null( $fi ) ) {
 435+ wfOut( "... error: expected column $table.$field to exist\n" );
 436+ exit( 1 );
 437+ }
 438+ if ( $fi->nullable() ) {
 439+ # # It's NULL - does it need to be NOT NULL?
 440+ if ( 'NOT NULL' === $null ) {
 441+ wfOut( "Changing \"$table.$field\" to not allow NULLs\n" );
 442+ $this->db->query( "ALTER TABLE $table ALTER $field SET NOT NULL" );
 443+ } else {
 444+ wfOut( "... column \"$table.$field\" is already set as NULL\n" );
 445+ }
 446+ } else {
 447+ # # It's NOT NULL - does it need to be NULL?
 448+ if ( 'NULL' === $null ) {
 449+ wfOut( "Changing \"$table.$field\" to allow NULLs\n" );
 450+ $this->db->query( "ALTER TABLE $table ALTER $field DROP NOT NULL" );
 451+ }
 452+ else {
 453+ wfOut( "... column \"$table.$field\" is already set as NOT NULL\n" );
 454+ }
 455+ }
 456+ }
 457+
 458+ public function addPgIndex( $table, $index, $type ) {
 459+ if ( $this->db->indexExists( $table, $index ) ) {
 460+ wfOut( "... index \"$index\" on table \"$table\" already exists\n" );
 461+ } else {
 462+ wfOut( "Creating index \"$index\" on table \"$table\" $type\n" );
 463+ $this->db->query( "CREATE INDEX $index ON $table $type" );
 464+ }
 465+ }
 466+
 467+ public function addPgExtIndex( $table, $index, $type ) {
 468+ if ( $this->db->indexExists( $table, $ni[1] ) ) {
 469+ wfOut( "... index \"$index\" on table \"$table\" already exists\n" );
 470+ } else {
 471+ wfOut( "Creating index \"$index\" on table \"$table\"\n" );
 472+ if ( preg_match( '/^\(/', $type ) ) {
 473+ $this->db->query( "CREATE INDEX $index ON $table $type" );
 474+ } else {
 475+ $this->applyPatch( $type, true );
 476+ }
 477+ }
 478+ }
 479+
 480+ protected function changeFkeyDeferrable( $table, $field, $clause ) {
 481+ $fi = $this->db->fieldInfo( $table, $field );
 482+ if ( is_null( $fi ) ) {
 483+ wfOut( "WARNING! Column \"$table.$field\" does not exist but it should! Please report this.\n" );
 484+ return;
 485+ }
 486+ if ( $fi->is_deferred() && $fi->is_deferrable() ) {
 487+ return;
 488+ }
 489+ wfOut( "Altering column \"$table.$field\" to be DEFERRABLE INITIALLY DEFERRED\n" );
 490+ $conname = $fi->conname();
 491+ $command = "ALTER TABLE $table DROP CONSTRAINT $conname";
 492+ $this->db->query( $command );
 493+ $command = "ALTER TABLE $table ADD CONSTRAINT $conname FOREIGN KEY ($field) REFERENCES $clause DEFERRABLE INITIALLY DEFERRED";
 494+ $this->db->query( $command );
 495+ }
 496+
 497+ /**
 498+ * Verify that this user is configured correctly
 499+ */
 500+ protected function checkPgUser() {
 501+ global $wgDBmwschema, $wgDBts2schema, $wgDBuser;
 502+
 503+ # Just in case their LocalSettings.php does not have this:
 504+ if ( !isset( $wgDBmwschema ) ) {
 505+ $wgDBmwschema = 'mediawiki';
 506+ }
 507+
 508+ $safeuser = $this->db->addQuotes( $wgDBuser );
 509+ $SQL = "SELECT array_to_string(useconfig,'*') FROM pg_catalog.pg_user WHERE usename = $safeuser";
 510+ $config = pg_fetch_result( $this->db->doQuery( $SQL ), 0, 0 );
 511+ $conf = array();
 512+ foreach ( explode( '*', $config ) as $c ) {
 513+ list( $x, $y ) = explode( '=', $c );
 514+ $conf[$x] = $y;
 515+ }
 516+
 517+ if ( !array_key_exists( 'search_path', $conf ) ) {
 518+ $search_path = '';
 519+ } else {
 520+ $search_path = $conf['search_path'];
 521+ }
 522+
 523+ if ( strpos( $search_path, $wgDBmwschema ) === false ) {
 524+ wfOut( "Adding in schema \"$wgDBmwschema\" to search_path for user \"$wgDBuser\"\n" );
 525+ $search_path = "$wgDBmwschema, $search_path";
 526+ }
 527+ if ( strpos( $search_path, $wgDBts2schema ) === false ) {
 528+ wfOut( "Adding in schema \"$wgDBts2schema\" to search_path for user \"$wgDBuser\"\n" );
 529+ $search_path = "$search_path, $wgDBts2schema";
 530+ }
 531+ $search_path = str_replace( ', ,', ',', $search_path );
 532+ if ( array_key_exists( 'search_path', $conf ) === false || $search_path != $conf['search_path'] ) {
 533+ $this->db->doQuery( "ALTER USER $wgDBuser SET search_path = $search_path" );
 534+ $this->db->doQuery( "SET search_path = $search_path" );
 535+ } else {
 536+ $path = $conf['search_path'];
 537+ wfOut( "... search_path for user \"$wgDBuser\" looks correct ($path)\n" );
 538+ }
 539+
 540+ $goodconf = array(
 541+ 'client_min_messages' => 'error',
 542+ 'DateStyle' => 'ISO, YMD',
 543+ 'TimeZone' => 'GMT'
 544+ );
 545+
 546+ foreach ( $goodconf as $key => $value ) {
 547+ if ( !array_key_exists( $key, $conf ) or $conf[$key] !== $value ) {
 548+ wfOut( "Setting $key to '$value' for user \"$wgDBuser\"\n" );
 549+ $this->db->doQuery( "ALTER USER $wgDBuser SET $key = '$value'" );
 550+ $this->db->doQuery( "SET $key = '$value'" );
 551+ } else {
 552+ wfOut( "... default value of \"$key\" is correctly set to \"$value\" for user \"$wgDBuser\"\n" );
 553+ }
 554+ }
 555+ }
 556+
 557+ protected function convertArchive2() {
 558+ if ( $this->db->tableExists( "archive2" ) ) {
 559+ wfOut( "Converting \"archive2\" back to normal archive table\n" );
 560+ if ( $this->db->ruleExists( 'archive', 'archive_insert' ) ) {
 561+ wfOut( "Dropping rule \"archive_insert\"\n" );
 562+ $this->db->query( 'DROP RULE archive_insert ON archive' );
 563+ }
 564+ if ( $this->db->ruleExists( 'archive', 'archive_delete' ) ) {
 565+ wfOut( "Dropping rule \"archive_delete\"\n" );
 566+ $this->db->query( 'DROP RULE archive_delete ON archive' );
 567+ }
 568+ $this->db->sourceFile( archive( 'patch-remove-archive2.sql' ) );
 569+ } else {
 570+ wfOut( "... obsolete table \"archive2\" does not exist\n" );
 571+ }
 572+ }
 573+
 574+ protected function checkOiDeleted() {
 575+ if ( $this->db->fieldInfo( 'oldimage', 'oi_deleted' )->type() !== 'smallint' ) {
 576+ wfOut( "Changing \"oldimage.oi_deleted\" to type \"smallint\"\n" );
 577+ $this->db->query( "ALTER TABLE oldimage ALTER oi_deleted DROP DEFAULT" );
 578+ $this->db->query( "ALTER TABLE oldimage ALTER oi_deleted TYPE SMALLINT USING (oi_deleted::smallint)" );
 579+ $this->db->query( "ALTER TABLE oldimage ALTER oi_deleted SET DEFAULT 0" );
 580+ } else {
 581+ wfOut( "... column \"oldimage.oi_deleted\" is already of type \"smallint\"\n" );
 582+ }
 583+ }
 584+
 585+ protected function checkOiNameConstraint() {
 586+ if ( $this->db->hasConstraint( "oldimage_oi_name_fkey_cascaded" ) ) {
 587+ wfOut( "... table \"oldimage\" has correct cascading delete/update foreign key to image\n" );
 588+ } else {
 589+ if ( $this->db->hasConstraint( "oldimage_oi_name_fkey" ) ) {
 590+ $this->db->query( "ALTER TABLE oldimage DROP CONSTRAINT oldimage_oi_name_fkey" );
 591+ }
 592+ if ( $this->db->hasConstraint( "oldimage_oi_name_fkey_cascade" ) ) {
 593+ $this->db->query( "ALTER TABLE oldimage DROP CONSTRAINT oldimage_oi_name_fkey_cascade" );
 594+ }
 595+ wfOut( "Making foreign key on table \"oldimage\" (to image) a cascade delete/update\n" );
 596+ $this->db->query( "ALTER TABLE oldimage ADD CONSTRAINT oldimage_oi_name_fkey_cascaded " .
 597+ "FOREIGN KEY (oi_name) REFERENCES image(img_name) ON DELETE CASCADE ON UPDATE CASCADE" );
 598+ }
 599+ }
 600+
 601+ protected function checkPageDeletedTrigger() {
 602+ if ( !$this->db->triggerExists( 'page', 'page_deleted' ) ) {
 603+ wfOut( "Adding function and trigger \"page_deleted\" to table \"page\"\n" );
 604+ $this->applyPatch( 'patch-page_deleted.sql' );
 605+ } else {
 606+ wfOut( "... table \"page\" has \"page_deleted\" trigger\n" );
 607+ }
 608+ }
 609+
 610+ protected function checkRcCurIdNullable(){
 611+ $fi = $this->db->fieldInfo( 'recentchanges', 'rc_cur_id' );
 612+ if ( !$fi->nullable() ) {
 613+ wfOut( "Removing NOT NULL constraint from \"recentchanges.rc_cur_id\"\n" );
 614+ $this->applyPatch( 'patch-rc_cur_id-not-null.sql' );
 615+ } else {
 616+ wfOut( "... column \"recentchanges.rc_cur_id\" has a NOT NULL constraint\n" );
 617+ }
 618+ }
 619+
 620+ protected function checkPagelinkUniqueIndex() {
 621+ $pu = $this->describeIndex( 'pagelink_unique' );
 622+ if ( !is_null( $pu ) && ( $pu[0] != 'pl_from' || $pu[1] != 'pl_namespace' || $pu[2] != 'pl_title' ) ) {
 623+ wfOut( "Dropping obsolete version of index \"pagelink_unique index\"\n" );
 624+ $this->db->query( 'DROP INDEX pagelink_unique' );
 625+ $pu = null;
 626+ } else {
 627+ wfOut( "... obsolete version of index \"pagelink_unique index\" does not exist\n" );
 628+ }
 629+
 630+ if ( is_null( $pu ) ) {
 631+ wfOut( "Creating index \"pagelink_unique index\"\n" );
 632+ $this->db->query( 'CREATE UNIQUE INDEX pagelink_unique ON pagelinks (pl_from,pl_namespace,pl_title)' );
 633+ } else {
 634+ wfOut( "... index \"pagelink_unique_index\" already exists\n" );
 635+ }
 636+ }
 637+
 638+ protected function checkRevUserFkey() {
 639+ if ( $this->fkeyDeltype( 'revision_rev_user_fkey' ) == 'r' ) {
 640+ wfOut( "... constraint \"revision_rev_user_fkey\" is ON DELETE RESTRICT\n" );
 641+ } else {
 642+ wfOut( "Changing constraint \"revision_rev_user_fkey\" to ON DELETE RESTRICT\n" );
 643+ $this->applyPatch( 'patch-revision_rev_user_fkey.sql' );
 644+ }
 645+ }
 646+
 647+ protected function checkIpbAdress() {
 648+ if ( $this->db->indexExists( 'ipblocks', 'ipb_address' ) ) {
 649+ wfOut( "Removing deprecated index 'ipb_address'...\n" );
 650+ $this->db->query( 'DROP INDEX ipb_address' );
 651+ }
 652+ if ( $this->db->indexExists( 'ipblocks', 'ipb_address_unique' ) ) {
 653+ wfOut( "... have ipb_address_unique\n" );
 654+ } else {
 655+ wfOut( "Adding ipb_address_unique index\n" );
 656+ $this->applyPatch( 'patch-ipb_address_unique.sql' );
 657+ }
 658+ }
 659+
 660+ protected function checkIwlPrefix() {
 661+ if ( $this->db->indexExists( 'iwlinks', 'iwl_prefix' ) ) {
 662+ wfOut( "Replacing index 'iwl_prefix' with 'iwl_prefix_from_title'...\n" );
 663+ $this->applyPatch( 'patch-rename-iwl_prefix.sql' );
 664+ }
 665+ }
 666+
 667+ protected function tsearchFixes() {
 668+ # Tweak the page_title tsearch2 trigger to filter out slashes
 669+ # This is create or replace, so harmless to call if not needed
 670+ $this->applyPatch( 'patch-ts2pagetitle.sql' );
 671+
 672+ # # If the server is 8.3 or higher, rewrite the tsearch2 triggers
 673+ # # in case they have the old 'default' versions
 674+ # Gather version numbers in case we need them
 675+ if ( $this->db->getServerVersion() >= 8.3 ) {
 676+ $this->applyPatch( 'patch-tsearch2funcs.sql' );
 677+ }
 678+ }
30679 }

Follow-up revisions

RevisionCommit summaryAuthorDate
r71427Oops, forgot to change this in r71426ialex08:32, 22 August 2010
r74795Followup r71426, not $nc it's $fieldRecordreedy20:31, 14 October 2010
r74796Another followup to r71426, $ni to $fieldRecordreedy20:34, 14 October 2010

Comments

#Comment by MarkAHershberger (talk | contribs)   07:13, 25 November 2010

+ array( 'addTable', 'mwuser', 'patch-mwuser.sql' ),

+			array( 'addTable', 'pagecontent',       'patch-pagecontent.sql' ),

Both of these are renaming the table, not adding it, and cause errors when run.

#Comment by MarkAHershberger (talk | contribs)   07:16, 25 November 2010

FWIW, something like the follwing WFM:

+			# rename tables
+			array( 'renameTable', 'text',       'pagecontent' ),
+			array( 'renameTable', 'user',       'mwuser' ),
[...]
+	protected function renameTable( $old, $new ) {
+		if ( $this->db->tableExists( $old ) ) {
+			$this->output( "Renaming table $old to $new\n" );
+			$this->db->query( "ALTER TABLE \"$old\" RENAME TO $new" );
+		}
#Comment by IAlex (talk | contribs)   10:30, 1 December 2010

Both patches only renames tables:

  • patch-pagecontent.sql contains ALTER TABLE "text" RENAME TO pagecontent;
  • patch-mwuser.sql contains ALTER TABLE "user" RENAME TO mwuser;
#Comment by MarkAHershberger (talk | contribs)   21:59, 1 December 2010

But in my testing they failed. Hence r77533.

Status & tagging log