Index: trunk/phase3/maintenance/updaters.inc |
— | — | @@ -74,6 +74,43 @@ |
75 | 75 | } |
76 | 76 | } |
77 | 77 | |
| 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 | + |
78 | 115 | function do_interwiki_update() { |
79 | 116 | # Check that interwiki table exists; if it doesn't source it |
80 | 117 | global $wgDatabase, $IP; |
— | — | @@ -829,43 +866,6 @@ |
830 | 867 | wfOut( "done.\n" ); |
831 | 868 | } |
832 | 869 | |
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 | | - |
870 | 870 | function do_restrictions_update() { |
871 | 871 | # Adding page_restrictions table, obsoleting page.page_restrictions. |
872 | 872 | # Migrating old restrictions to new table |
— | — | @@ -1083,661 +1083,3 @@ |
1084 | 1084 | wfOut( "ok\n" ); |
1085 | 1085 | } |
1086 | 1086 | } |
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 @@ |
99 | 99 | * version these like we do with our core updates, so they have to go |
100 | 100 | * in 'always' |
101 | 101 | */ |
102 | | - private function getOldGlobalUpdates() { |
| 102 | + protected function getOldGlobalUpdates() { |
103 | 103 | global $wgUpdates, $wgExtNewFields, $wgExtNewTables, |
104 | 104 | $wgExtModifiedFields, $wgExtNewIndexes, $wgSharedDB, $wgSharedTables; |
105 | 105 | |
Index: trunk/phase3/includes/installer/PostgresUpdater.php |
— | — | @@ -9,21 +9,670 @@ |
10 | 10 | /** |
11 | 11 | * Class for handling updates to Postgres databases. |
12 | 12 | * |
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 | | - * |
18 | 13 | * @ingroup Deployment |
19 | 14 | * @since 1.17 |
20 | 15 | */ |
21 | 16 | |
22 | 17 | 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 | + */ |
23 | 23 | 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 | + ); |
25 | 219 | } |
26 | 220 | |
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; |
29 | 258 | } |
| 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 | + } |
30 | 679 | } |