From 86eb53c8caae8ffa90ac4069737a2c21997f1914 Mon Sep 17 00:00:00 2001 From: Alexandre Emsenhuber Date: Sun, 12 Sep 2010 16:24:03 +0000 Subject: [PATCH] GOOD BYE, dear old updaters.inc! * Moved update functions to MysqlUpdater and DatabaseUpdater (and archive() to install-utils.inc just in case someone is still using it) * Moved update_row_exists() to DatabaseUpdater * Added cl_collation patch for SQLite, was missing and also run collationUpdate.php while updation SQLite --- includes/installer/DatabaseUpdater.php | 76 +- includes/installer/MysqlUpdater.php | 674 +++++++++++++++- includes/installer/SqliteUpdater.php | 12 +- maintenance/install-utils.inc | 16 +- .../patch-categorylinks-better-collation.sql | 7 + maintenance/updaters.inc | 727 ------------------ 6 files changed, 733 insertions(+), 779 deletions(-) create mode 100644 maintenance/sqlite/archives/patch-categorylinks-better-collation.sql delete mode 100644 maintenance/updaters.inc diff --git a/includes/installer/DatabaseUpdater.php b/includes/installer/DatabaseUpdater.php index fcf489b21d..9b16b24057 100644 --- a/includes/installer/DatabaseUpdater.php +++ b/includes/installer/DatabaseUpdater.php @@ -123,8 +123,7 @@ abstract class DatabaseUpdater { * @param $purge Boolean: whether to clear the objectcache table after updates */ public function doUpdates( $purge = true ) { - global $IP, $wgVersion; - require_once( "$IP/maintenance/updaters.inc" ); + global $wgVersion; $this->runUpdates( $this->getCoreUpdateList(), false ); $this->runUpdates( $this->getOldGlobalUpdates(), false ); @@ -169,6 +168,21 @@ abstract class DatabaseUpdater { __METHOD__ ); } + /** + * Helper function: check if the given key is present in the updatelog table. + * Obviously, only use this for updates that occur after the updatelog table was + * created! + */ + public function updateRowExists( $key ) { + $row = $this->db->selectRow( + 'updatelog', + '1', + array( 'ul_key' => $key ), + __METHOD__ + ); + return (bool)$row; + } + /** * Updatelog was changed in 1.17 to have a ul_value column so we can record * more information about what kind of updates we've done (that's what this @@ -395,4 +409,62 @@ abstract class DatabaseUpdater { SiteStatsInit::doAllAndCommit( false ); } + # Common updater functions + + protected function doActiveUsersInit() { + $activeUsers = $this->db->selectField( 'site_stats', 'ss_active_users', false, __METHOD__ ); + if ( $activeUsers == -1 ) { + $activeUsers = $this->db->selectField( 'recentchanges', + 'COUNT( DISTINCT rc_user_text )', + array( 'rc_user != 0', 'rc_bot' => 0, "rc_log_type != 'newusers'" ), __METHOD__ + ); + $this->db->update( 'site_stats', + array( 'ss_active_users' => intval( $activeUsers ) ), + array( 'ss_row_id' => 1 ), __METHOD__, array( 'LIMIT' => 1 ) + ); + } + wfOut( "...ss_active_users user count set...\n" ); + } + + protected function doLogSearchPopulation() { + if ( $this->updateRowExists( 'populate log_search' ) ) { + wfOut( "...log_search table already populated.\n" ); + return; + } + + wfOut( + "Populating log_search table, printing progress markers. For large\n" . + "databases, you may want to hit Ctrl-C and do this manually with\n" . + "maintenance/populateLogSearch.php.\n" ); + $task = new PopulateLogSearch(); + $task->execute(); + wfOut( "Done populating log_search table.\n" ); + } + + function doUpdateTranscacheField() { + if ( $this->updateRowExists( 'convert transcache field' ) ) { + wfOut( "...transcache tc_time already converted.\n" ); + return; + } + + wfOut( "Converting tc_time from UNIX epoch to MediaWiki timestamp... " ); + $this->applyPatch( 'patch-tc-timestamp.sql' ); + wfOut( "ok\n" ); + } + + protected function doCollationUpdate() { + global $wgCategoryCollation; + if ( $this->db->selectField( + 'categorylinks', + 'COUNT(*)', + 'cl_collation != ' . $this->db->addQuotes( $wgCategoryCollation ), + __METHOD__ + ) == 0 ) { + wfOut( "...collations up-to-date.\n" ); + return; + } + + $task = new UpdateCollation(); + $task->execute(); + } } diff --git a/includes/installer/MysqlUpdater.php b/includes/installer/MysqlUpdater.php index 28501e46c3..a4a41533c9 100644 --- a/includes/installer/MysqlUpdater.php +++ b/includes/installer/MysqlUpdater.php @@ -19,8 +19,8 @@ class MysqlUpdater extends DatabaseUpdater { // 1.2 array( 'addField', 'ipblocks', 'ipb_id', 'patch-ipblocks.sql' ), array( 'addField', 'ipblocks', 'ipb_expiry', 'patch-ipb_expiry.sql' ), - array( 'do_interwiki_update' ), - array( 'do_index_update' ), + array( 'doInterwikiUpdate' ), + array( 'doIndexUpdate' ), array( 'addTable', 'hitcounter', 'patch-hitcounter.sql' ), array( 'addField', 'recentchanges', 'rc_type', 'patch-rc_type.sql' ), @@ -29,24 +29,24 @@ class MysqlUpdater extends DatabaseUpdater { array( 'addTable', 'querycache', 'patch-querycache.sql' ), array( 'addTable', 'objectcache', 'patch-objectcache.sql' ), array( 'addTable', 'categorylinks', 'patch-categorylinks.sql' ), - array( 'do_old_links_update' ), - array( 'fix_ancient_imagelinks' ), + array( 'doOldLinksUpdate' ), + array( 'doFixAncientImagelinks' ), array( 'addField', 'recentchanges', 'rc_ip', 'patch-rc_ip.sql' ), // 1.4 - array( 'do_image_name_unique_update' ), + array( 'addIndex', 'image', 'PRIMARY', 'patch-image_name_primary.sql' ), array( 'addField', 'recentchanges', 'rc_id', 'patch-rc_id.sql' ), array( 'addField', 'recentchanges', 'rc_patrolled', 'patch-rc-patrol.sql' ), array( 'addTable', 'logging', 'patch-logging.sql' ), array( 'addField', 'user', 'user_token', 'patch-user_token.sql' ), array( 'addField', 'watchlist', 'wl_notificationtimestamp', 'patch-email-notification.sql' ), - array( 'do_watchlist_update' ), + array( 'doWatchlistUpdate' ), array( 'dropField', 'user', 'user_emailauthenticationtimestamp', 'patch-email-authentication.sql' ), // 1.5 - array( 'do_schema_restructuring' ), + array( 'doSchemaRestructuring' ), array( 'addField', 'logging', 'log_params', 'patch-log_params.sql' ), - array( 'check_bin', 'logging', 'log_title', 'patch-logging-title.sql', ), + array( 'checkBin', 'logging', 'log_title', 'patch-logging-title.sql', ), array( 'addField', 'archive', 'ar_rev_id', 'patch-archive-rev_id.sql' ), array( 'addField', 'page', 'page_len', 'patch-page_len.sql' ), array( 'dropField', 'revision', 'inverse_timestamp', 'patch-inverse_timestamp.sql' ), @@ -58,10 +58,10 @@ class MysqlUpdater extends DatabaseUpdater { array( 'addField', 'archive', 'ar_text_id', 'patch-archive-text_id.sql' ), array( 'doNamespaceSize' ), array( 'addField', 'image', 'img_media_type', 'patch-img_media_type.sql' ), - array( 'do_pagelinks_update' ), + array( 'doPagelinksUpdate' ), array( 'dropField', 'image', 'img_type', 'patch-drop_img_type.sql' ), - array( 'do_user_unique_update' ), - array( 'do_user_groups_update' ), + array( 'doUserUniqueUpdate' ), + array( 'doUserGroupsUpdate' ), array( 'addField', 'site_stats', 'ss_total_pages', 'patch-ss_total_articles.sql' ), array( 'addTable', 'user_newtalk', 'patch-usernewtalk2.sql' ), array( 'addTable', 'transcache', 'patch-transcache.sql' ), @@ -69,12 +69,12 @@ class MysqlUpdater extends DatabaseUpdater { array( 'addTable', 'trackbacks', 'patch-trackbacks.sql' ), // 1.6 - array( 'do_watchlist_null' ), + array( 'doWatchlistNull' ), array( 'addIndex', 'logging', 'times', 'patch-logging-times-index.sql' ), array( 'addField', 'ipblocks', 'ipb_range_start', 'patch-ipb_range_start.sql' ), - array( 'do_page_random_update' ), + array( 'doPageRandomUpdate' ), array( 'addField', 'user', 'user_registration', 'patch-user_registration.sql' ), - array( 'do_templatelinks_update' ), + array( 'doTemplatelinksUpdate' ), array( 'addTable', 'externallinks', 'patch-externallinks.sql' ), array( 'addTable', 'job', 'patch-job.sql' ), array( 'addField', 'site_stats', 'ss_images', 'patch-ss_images.sql' ), @@ -82,19 +82,20 @@ class MysqlUpdater extends DatabaseUpdater { array( 'addTable', 'querycache_info', 'patch-querycacheinfo.sql' ), array( 'addTable', 'filearchive', 'patch-filearchive.sql' ), array( 'addField', 'ipblocks', 'ipb_anon_only', 'patch-ipb_anon_only.sql' ), - array( 'do_rc_indices_update' ), + array( 'addIndex', 'recentchanges', 'rc_ns_usertext', 'patch-recentchanges-utindex.sql' ), + array( 'addIndex', 'recentchanges', 'rc_user_text', 'patch-rc_user_text-index.sql' ), // 1.9 array( 'addField', 'user', 'user_newpass_time', 'patch-user_newpass_time.sql' ), array( 'addTable', 'redirect', 'patch-redirect.sql' ), array( 'addTable', 'querycachetwo', 'patch-querycachetwo.sql' ), array( 'addField', 'ipblocks', 'ipb_enable_autoblock', 'patch-ipb_optional_autoblock.sql' ), - array( 'do_backlinking_indices_update' ), + array( 'doBacklinkingIndicesUpdate' ), array( 'addField', 'recentchanges', 'rc_old_len', 'patch-rc_len.sql' ), array( 'addField', 'user', 'user_editcount', 'patch-user_editcount.sql' ), // 1.10 - array( 'do_restrictions_update' ), + array( 'doRestrictionsUpdate' ), array( 'addField', 'logging', 'log_id', 'patch-log_id.sql' ), array( 'addField', 'revision', 'rev_parent_id', 'patch-rev_parent_id.sql' ), array( 'addField', 'page_restrictions', 'pr_id', 'patch-page_restrictions_sortkey.sql' ), @@ -108,7 +109,7 @@ class MysqlUpdater extends DatabaseUpdater { // 1.11 array( 'addField', 'ipblocks', 'ipb_block_email', 'patch-ipb_emailban.sql' ), - array( 'do_categorylinks_indices_update' ), + array( 'doCategorylinksIndicesUpdate' ), array( 'addField', 'oldimage', 'oi_metadata', 'patch-oi_metadata.sql' ), array( 'addIndex', 'archive', 'usertext_timestamp', 'patch-archive-user-index.sql' ), array( 'addIndex', 'image', 'img_usertext_timestamp', 'patch-image-user-index.sql' ), @@ -124,21 +125,21 @@ class MysqlUpdater extends DatabaseUpdater { array( 'addTable', 'page_props', 'patch-page_props.sql' ), array( 'addTable', 'updatelog', 'patch-updatelog.sql' ), array( 'addTable', 'category', 'patch-category.sql' ), - array( 'do_category_population' ), + array( 'doCategoryPopulation' ), array( 'addField', 'archive', 'ar_parent_id', 'patch-ar_parent_id.sql' ), array( 'addField', 'user_newtalk', 'user_last_timestamp', 'patch-user_last_timestamp.sql' ), - array( 'do_populate_parent_id' ), - array( 'check_bin', 'protected_titles', 'pt_title', 'patch-pt_title-encoding.sql', ), - array( 'maybe_do_profiling_memory_update' ), - array( 'do_filearchive_indices_update' ), + array( 'doPopulateParentId' ), + array( 'checkBin', 'protected_titles', 'pt_title', 'patch-pt_title-encoding.sql', ), + array( 'doMaybeProfilingMemoryUpdate' ), + array( 'doFilearchiveIndicesUpdate' ), // 1.14 array( 'addField', 'site_stats', 'ss_active_users', 'patch-ss_active_users.sql' ), - array( 'do_active_users_init' ), + array( 'doActiveUsersInit' ), array( 'addField', 'ipblocks', 'ipb_allow_usertalk', 'patch-ipb_allow_usertalk.sql' ), // 1.15 - array( 'do_unique_pl_tl_il' ), + array( 'doUniquePlTlIl' ), array( 'addTable', 'change_tag', 'patch-change_tag.sql' ), array( 'addTable', 'tag_summary', 'patch-change_tag.sql' ), array( 'addTable', 'valid_tag', 'patch-change_tag.sql' ), @@ -146,17 +147,17 @@ class MysqlUpdater extends DatabaseUpdater { // 1.16 array( 'addTable', 'user_properties', 'patch-user_properties.sql' ), array( 'addTable', 'log_search', 'patch-log_search.sql' ), - array( 'do_log_search_population' ), + array( 'doLogSearchPopulation' ), array( 'addField', 'logging', 'log_user_text', 'patch-log_user_text.sql' ), array( 'addTable', 'l10n_cache', 'patch-l10n_cache.sql' ), array( 'addTable', 'external_user', 'patch-external_user.sql' ), array( 'addIndex', 'log_search', 'ls_field_val', 'patch-log_search-rename-index.sql' ), array( 'addIndex', 'change_tag', 'change_tag_rc_tag', 'patch-change_tag-indexes.sql' ), array( 'addField', 'redirect', 'rd_interwiki', 'patch-rd_interwiki.sql' ), - array( 'do_update_transcache_field' ), - array( 'rename_eu_wiki_id' ), - array( 'do_update_mime_minor_field' ), - array( 'do_populate_rev_len' ), + array( 'doUpdateTranscacheField' ), + array( 'renameEuWikiId' ), + array( 'doUpdateMimeMinorField' ), + array( 'doPopulateRevLen' ), // 1.17 array( 'addTable', 'iwlinks', 'patch-iwlinks.sql' ), @@ -166,13 +167,296 @@ class MysqlUpdater extends DatabaseUpdater { array( 'dropIndex', 'iwlinks', 'iwl_prefix', 'patch-kill-iwl_prefix.sql' ), array( 'dropIndex', 'iwlinks', 'iwl_prefix_from_title', 'patch-kill-iwl_pft.sql' ), array( 'addField', 'categorylinks', 'cl_collation', 'patch-categorylinks-better-collation.sql' ), - array( 'do_cl_fields_update' ), - array( 'do_collation_update' ), + array( 'doClFieldsUpdate' ), + array( 'doCollationUpdate' ), array( 'addTable', 'msg_resource', 'patch-msg_resource.sql' ), array( 'addTable', 'module_deps', 'patch-module_deps.sql' ), ); } + /** + * 1.4 betas were missing the 'binary' marker from logging.log_title, + * which causes a collation mismatch error on joins in MySQL 4.1. + * + * @param $table String: table name + * @param $field String: field name to check + * @param $patchFile String: path to the patch to correct the field + */ + protected function checkBin( $table, $field, $patchFile ) { + $tableName = $this->db->tableName( $table ); + $res = $this->db->query( "SELECT $field FROM $tableName LIMIT 0", __METHOD__ ); + $flags = explode( ' ', mysql_field_flags( $res->result, 0 ) ); + + if ( in_array( 'binary', $flags ) ) { + wfOut( "...$table table has correct $field encoding.\n" ); + } else { + wfOut( "Fixing $field encoding on $table table... " ); + $this->db->applyPatch( $patchFile ); + wfOut( "ok\n" ); + } + } + + /** + * Check whether an index contain a field + * + * @param $table String: table name + * @param $index String: index name to check + * @param $field String: field that should be in the index + * @return Boolean + */ + protected function indexHasField( $table, $index, $field ) { + $info = $this->db->indexInfo( $table, $index, __METHOD__ ); + if ( $info ) { + foreach ( $info as $row ) { + if ( $row->Column_name == $field ) { + wfOut( "...index $index on table $table includes field $field\n" ); + return true; + } + } + } + wfOut( "...index $index on table $table has no field $field; adding\n" ); + return false; + } + + /** + * Check that interwiki table exists; if it doesn't source it + */ + protected function doInterwikiUpdate() { + global $IP; + + if ( $this->db->tableExists( "interwiki" ) ) { + wfOut( "...already have interwiki table\n" ); + return; + } + + wfOut( 'Creating interwiki table...' ); + $this->applyPatch( 'patch-interwiki.sql' ); + wfOut( "ok\n" ); + wfOut( 'Adding default interwiki definitions...' ); + $this->applyPatch( "$IP/maintenance/interwiki.sql", true ); + wfOut( "ok\n" ); + } + + /** + * Check that proper indexes are in place + */ + protected function doIndexUpdate() { + $meta = $this->db->fieldInfo( 'recentchanges', 'rc_timestamp' ); + if ( $meta->isMultipleKey() ) { + wfOut( "...indexes seem up to 20031107 standards\n" ); + return; + } + + wfOut( "Updating indexes to 20031107..." ); + $this->applyPatch( 'patch-indexes.sql', true ); + wfOut( "ok\n" ); + } + + protected function doOldLinksUpdate() { + $cl = new ConvertLinks(); + $cl->execute(); + } + + protected function doFixAncientImagelinks() { + $info = $this->db->fieldInfo( 'imagelinks', 'il_from' ); + if ( !$info || $info->type() !== 'string' ) { + wfOut( "...il_from OK\n" ); + return; + } + + wfOut( "Fixing ancient broken imagelinks table.\n" ); + wfOut( "NOTE: you will have to run maintenance/refreshLinks.php after this.\n" ); + $this->applyPatch( 'patch-fix-il_from.sql' ); + wfOut( "ok\n" ); + } + + /** + * Check if we need to add talk page rows to the watchlist + */ + function doWatchlistUpdate() { + $talk = $this->db->selectField( 'watchlist', 'count(*)', 'wl_namespace & 1', __METHOD__ ); + $nontalk = $this->db->selectField( 'watchlist', 'count(*)', 'NOT (wl_namespace & 1)', __METHOD__ ); + if ( $talk == $nontalk ) { + wfOut( "...watchlist talk page rows already present\n" ); + return; + } + + wfOut( "Adding missing watchlist talk page rows... " ); + $this->db->insertSelect( 'watchlist', 'watchlist', + array( + 'wl_user' => 'wl_user', + 'wl_namespace' => 'wl_namespace | 1', + 'wl_title' => 'wl_title', + 'wl_notificationtimestamp' => 'wl_notificationtimestamp' + ), array( 'NOT (wl_namespace & 1)' ), __METHOD__, 'IGNORE' ); + wfOut( "ok\n" ); + } + + function doSchemaRestructuring() { + if ( $this->db->tableExists( 'page' ) ) { + wfOut( "...page table already exists.\n" ); + return; + } + + wfOut( "...converting from cur/old to page/revision/text DB structure.\n" ); + wfOut( wfTimestamp( TS_DB ) ); + wfOut( "......checking for duplicate entries.\n" ); + + list ( $cur, $old, $page, $revision, $text ) = $this->db->tableNamesN( 'cur', 'old', 'page', 'revision', 'text' ); + + $rows = $this->db->query( "SELECT cur_title, cur_namespace, COUNT(cur_namespace) AS c + FROM $cur GROUP BY cur_title, cur_namespace HAVING c>1", __METHOD__ ); + + if ( $rows->numRows() > 0 ) { + wfOut( wfTimestamp( TS_DB ) ); + wfOut( "......Found duplicate entries\n" ); + wfOut( sprintf( " %-60s %3s %5s\n", 'Title', 'NS', 'Count' ) ); + foreach ( $rows as $row ) { + if ( ! isset( $duplicate[$row->cur_namespace] ) ) { + $duplicate[$row->cur_namespace] = array(); + } + $duplicate[$row->cur_namespace][] = $row->cur_title; + wfOut( sprintf( " %-60s %3s %5s\n", $row->cur_title, $row->cur_namespace, $row->c ) ); + } + $sql = "SELECT cur_title, cur_namespace, cur_id, cur_timestamp FROM $cur WHERE "; + $firstCond = true; + foreach ( $duplicate as $ns => $titles ) { + if ( $firstCond ) { + $firstCond = false; + } else { + $sql .= ' OR '; + } + $sql .= "( cur_namespace = {$ns} AND cur_title in ("; + $first = true; + foreach ( $titles as $t ) { + if ( $first ) { + $sql .= $this->db->addQuotes( $t ); + $first = false; + } else { + $sql .= ', ' . $this->db->addQuotes( $t ); + } + } + $sql .= ") ) \n"; + } + # By sorting descending, the most recent entry will be the first in the list. + # All following entries will be deleted by the next while-loop. + $sql .= 'ORDER BY cur_namespace, cur_title, cur_timestamp DESC'; + + $rows = $this->db->query( $sql, __METHOD__ ); + + $prev_title = $prev_namespace = false; + $deleteId = array(); + + foreach ( $rows as $row ) { + if ( $prev_title == $row->cur_title && $prev_namespace == $row->cur_namespace ) { + $deleteId[] = $row->cur_id; + } + $prev_title = $row->cur_title; + $prev_namespace = $row->cur_namespace; + } + $sql = "DELETE FROM $cur WHERE cur_id IN ( " . join( ',', $deleteId ) . ')'; + $rows = $this->db->query( $sql, __METHOD__ ); + wfOut( wfTimestamp( TS_DB ) ); + wfOut( "......Deleted " . $this->db->affectedRows() . " records.\n" ); + } + + + wfOut( wfTimestamp( TS_DB ) ); + wfOut( "......Creating tables.\n" ); + $this->db->query( "CREATE TABLE $page ( + page_id int(8) unsigned NOT NULL auto_increment, + page_namespace int NOT NULL, + page_title varchar(255) binary NOT NULL, + page_restrictions tinyblob NOT NULL, + page_counter bigint(20) unsigned NOT NULL default '0', + page_is_redirect tinyint(1) unsigned NOT NULL default '0', + page_is_new tinyint(1) unsigned NOT NULL default '0', + page_random real unsigned NOT NULL, + page_touched char(14) binary NOT NULL default '', + page_latest int(8) unsigned NOT NULL, + page_len int(8) unsigned NOT NULL, + + PRIMARY KEY page_id (page_id), + UNIQUE INDEX name_title (page_namespace,page_title), + INDEX (page_random), + INDEX (page_len) + ) ENGINE=InnoDB", __METHOD__ ); + $this->db->query( "CREATE TABLE $revision ( + rev_id int(8) unsigned NOT NULL auto_increment, + rev_page int(8) unsigned NOT NULL, + rev_comment tinyblob NOT NULL, + rev_user int(5) unsigned NOT NULL default '0', + rev_user_text varchar(255) binary NOT NULL default '', + rev_timestamp char(14) binary NOT NULL default '', + rev_minor_edit tinyint(1) unsigned NOT NULL default '0', + rev_deleted tinyint(1) unsigned NOT NULL default '0', + rev_len int(8) unsigned, + rev_parent_id int(8) unsigned default NULL, + PRIMARY KEY rev_page_id (rev_page, rev_id), + UNIQUE INDEX rev_id (rev_id), + INDEX rev_timestamp (rev_timestamp), + INDEX page_timestamp (rev_page,rev_timestamp), + INDEX user_timestamp (rev_user,rev_timestamp), + INDEX usertext_timestamp (rev_user_text,rev_timestamp) + ) ENGINE=InnoDB", __METHOD__ ); + + wfOut( wfTimestamp( TS_DB ) ); + wfOut( "......Locking tables.\n" ); + $this->db->query( "LOCK TABLES $page WRITE, $revision WRITE, $old WRITE, $cur WRITE", __METHOD__ ); + + $maxold = intval( $this->db->selectField( 'old', 'max(old_id)', '', __METHOD__ ) ); + wfOut( wfTimestamp( TS_DB ) ); + wfOut( "......maxold is {$maxold}\n" ); + + wfOut( wfTimestamp( TS_DB ) ); + global $wgLegacySchemaConversion; + if ( $wgLegacySchemaConversion ) { + // Create HistoryBlobCurStub entries. + // Text will be pulled from the leftover 'cur' table at runtime. + wfOut( "......Moving metadata from cur; using blob references to text in cur table.\n" ); + $cur_text = "concat('O:18:\"historyblobcurstub\":1:{s:6:\"mCurId\";i:',cur_id,';}')"; + $cur_flags = "'object'"; + } else { + // Copy all cur text in immediately: this may take longer but avoids + // having to keep an extra table around. + wfOut( "......Moving text from cur.\n" ); + $cur_text = 'cur_text'; + $cur_flags = "''"; + } + $this->db->query( "INSERT INTO $old (old_namespace, old_title, old_text, old_comment, old_user, old_user_text, + old_timestamp, old_minor_edit, old_flags) + SELECT cur_namespace, cur_title, $cur_text, cur_comment, cur_user, cur_user_text, cur_timestamp, cur_minor_edit, $cur_flags + FROM $cur", __METHOD__ ); + + wfOut( wfTimestamp( TS_DB ) ); + wfOut( "......Setting up revision table.\n" ); + $this->db->query( "INSERT INTO $revision (rev_id, rev_page, rev_comment, rev_user, rev_user_text, rev_timestamp, + rev_minor_edit) + SELECT old_id, cur_id, old_comment, old_user, old_user_text, + old_timestamp, old_minor_edit + FROM $old,$cur WHERE old_namespace=cur_namespace AND old_title=cur_title", __METHOD__ ); + + wfOut( wfTimestamp( TS_DB ) ); + wfOut( "......Setting up page table.\n" ); + $this->db->query( "INSERT INTO $page (page_id, page_namespace, page_title, page_restrictions, page_counter, + page_is_redirect, page_is_new, page_random, page_touched, page_latest, page_len) + SELECT cur_id, cur_namespace, cur_title, cur_restrictions, cur_counter, cur_is_redirect, cur_is_new, + cur_random, cur_touched, rev_id, LENGTH(cur_text) + FROM $cur,$revision + WHERE cur_id=rev_page AND rev_timestamp=cur_timestamp AND rev_id > {$maxold}", __METHOD__ ); + + wfOut( wfTimestamp( TS_DB ) ); + wfOut( "......Unlocking tables.\n" ); + $this->db->query( "UNLOCK TABLES", __METHOD__ ); + + wfOut( wfTimestamp( TS_DB ) ); + wfOut( "......Renaming old.\n" ); + $this->db->query( "ALTER TABLE $old RENAME TO $text", __METHOD__ ); + + wfOut( wfTimestamp( TS_DB ) ); + wfOut( "...done.\n" ); + } + protected function doNamespaceSize() { $tables = array( 'page' => 'page', @@ -198,4 +482,330 @@ class MysqlUpdater extends DatabaseUpdater { } } } + + protected function doPagelinksUpdate() { + if ( $this->db->tableExists( 'pagelinks' ) ) { + wfOut( "...already have pagelinks table.\n" ); + return; + } + + wfOut( "Converting links and brokenlinks tables to pagelinks... " ); + $this->applyPatch( 'patch-pagelinks.sql' ); + wfOut( "ok\n" ); + + global $wgContLang; + foreach ( MWNamespace::getCanonicalNamespaces() as $ns => $name ) { + if ( $ns == 0 ) { + continue; + } + + wfOut( "Cleaning up broken links for namespace $ns... " ); + + $pagelinks = $this->db->tableName( 'pagelinks' ); + $name = $wgContLang->getNsText( $ns ); + $prefix = $this->db->strencode( $name ); + $likeprefix = str_replace( '_', '\\_', $prefix ); + + $sql = "UPDATE $pagelinks + SET pl_namespace=$ns, + pl_title=TRIM(LEADING '$prefix:' FROM pl_title) + WHERE pl_namespace=0 + AND pl_title LIKE '$likeprefix:%'"; + + $this->db->query( $sql, __METHOD__ ); + wfOut( "ok\n" ); + } + } + + protected function doUserUniqueUpdate() { + $duper = new UserDupes( $this->db ); + if ( $duper->hasUniqueIndex() ) { + wfOut( "...already have unique user_name index.\n" ); + return; + } + + if ( !$duper->clearDupes() ) { + wfOut( "WARNING: This next step will probably fail due to unfixed duplicates...\n" ); + } + wfOut( "Adding unique index on user_name... " ); + $this->applyPatch( 'patch-user_nameindex.sql' ); + wfOut( "ok\n" ); + } + + protected function doUserGroupsUpdate() { + if ( $this->db->tableExists( 'user_groups' ) ) { + $info = $this->db->fieldInfo( 'user_groups', 'ug_group' ); + if ( $info->type() == 'int' ) { + $oldug = $this->db->tableName( 'user_groups' ); + $newug = $this->db->tableName( 'user_groups_bogus' ); + wfOut( "user_groups table exists but is in bogus intermediate format. Renaming to $newug... " ); + $this->db->query( "ALTER TABLE $oldug RENAME TO $newug", __METHOD__ ); + wfOut( "ok\n" ); + + wfOut( "Re-adding fresh user_groups table... " ); + $this->applyPatch( 'patch-user_groups.sql' ); + wfOut( "ok\n" ); + + wfOut( "***\n" ); + wfOut( "*** WARNING: You will need to manually fix up user permissions in the user_groups\n" ); + wfOut( "*** table. Old 1.5 alpha versions did some pretty funky stuff...\n" ); + wfOut( "***\n" ); + } else { + wfOut( "...user_groups table exists and is in current format.\n" ); + } + return; + } + + wfOut( "Adding user_groups table... " ); + $this->applyPatch( 'patch-user_groups.sql' ); + wfOut( "ok\n" ); + + if ( !$this->db->tableExists( 'user_rights' ) ) { + if ( $this->db->fieldExists( 'user', 'user_rights' ) ) { + wfOut( "Upgrading from a 1.3 or older database? Breaking out user_rights for conversion..." ); + $this->db->applyPatch( 'patch-user_rights.sql' ); + wfOut( "ok\n" ); + } else { + wfOut( "*** WARNING: couldn't locate user_rights table or field for upgrade.\n" ); + wfOut( "*** You may need to manually configure some sysops by manipulating\n" ); + wfOut( "*** the user_groups table.\n" ); + return; + } + } + + wfOut( "Converting user_rights table to user_groups... " ); + $result = $this->db->select( 'user_rights', + array( 'ur_user', 'ur_rights' ), + array( "ur_rights != ''" ), + __METHOD__ ); + + foreach ( $result as $row ) { + $groups = array_unique( + array_map( 'trim', + explode( ',', $row->ur_rights ) ) ); + + foreach ( $groups as $group ) { + $this->db->insert( 'user_groups', + array( + 'ug_user' => $row->ur_user, + 'ug_group' => $group ), + __METHOD__ ); + } + } + wfOut( "ok\n" ); + } + + /** + * Make sure wl_notificationtimestamp can be NULL, + * and update old broken items. + */ + protected function doWatchlistNull() { + $info = $this->db->fieldInfo( 'watchlist', 'wl_notificationtimestamp' ); + if ( $info->nullable() ) { + wfOut( "...wl_notificationtimestamp is already nullable.\n" ); + return; + } + + wfOut( "Making wl_notificationtimestamp nullable... " ); + $this->applyPatch( 'patch-watchlist-null.sql' ); + wfOut( "ok\n" ); + } + + /** + * Set page_random field to a random value where it is equals to 0. + * + * @see bug 3946 + */ + protected function doPageRandomUpdate() { + wfOut( "Setting page_random to a random value on rows where it equals 0..." ); + + $page = $this->db->tableName( 'page' ); + $this->db->query( "UPDATE $page SET page_random = RAND() WHERE page_random = 0", __METHOD__ ); + $rows = $this->db->affectedRows(); + + wfOut( "changed $rows rows\n" ); + } + + protected function doTemplatelinksUpdate() { + if ( $this->db->tableExists( 'templatelinks' ) ) { + wfOut( "...templatelinks table already exists\n" ); + return; + } + + wfOut( "Creating templatelinks table...\n" ); + $this->applyPatch( 'patch-templatelinks.sql' ); + wfOut( "Populating...\n" ); + if ( wfGetLB()->getServerCount() > 1 ) { + // Slow, replication-friendly update + $res = $this->db->select( 'pagelinks', array( 'pl_from', 'pl_namespace', 'pl_title' ), + array( 'pl_namespace' => NS_TEMPLATE ), __METHOD__ ); + $count = 0; + foreach ( $res as $row ) { + $count = ( $count + 1 ) % 100; + if ( $count == 0 ) { + wfWaitForSlaves( 10 ); + } + $this->db->insert( 'templatelinks', + array( + 'tl_from' => $row->pl_from, + 'tl_namespace' => $row->pl_namespace, + 'tl_title' => $row->pl_title, + ), __METHOD__ + ); + + } + } else { + // Fast update + $this->db->insertSelect( 'templatelinks', 'pagelinks', + array( + 'tl_from' => 'pl_from', + 'tl_namespace' => 'pl_namespace', + 'tl_title' => 'pl_title' + ), array( + 'pl_namespace' => 10 + ), __METHOD__ + ); + } + wfOut( "Done. Please run maintenance/refreshLinks.php for a more thorough templatelinks update.\n" ); + } + + protected function doBacklinkingIndicesUpdate() { + if ( !$this->indexHasField( 'pagelinks', 'pl_namespace', 'pl_from' ) || + !$this->indexHasField( 'templatelinks', 'tl_namespace', 'tl_from' ) || + !$this->indexHasField( 'imagelinks', 'il_to', 'il_from' ) ) + { + $this->applyPatch( 'patch-backlinkindexes.sql' ); + wfOut( "...backlinking indices updated\n" ); + } + } + + /** + * Adding page_restrictions table, obsoleting page.page_restrictions. + * Migrating old restrictions to new table + * -- Andrew Garrett, January 2007. + */ + protected function doRestrictionsUpdate() { + if ( $this->db->tableExists( 'page_restrictions' ) ) { + wfOut( "...page_restrictions table already exists.\n" ); + return; + } + + wfOut( "Creating page_restrictions table..." ); + $this->applyPatch( 'patch-page_restrictions.sql' ); + $this->applyPatch( 'patch-page_restrictions_sortkey.sql' ); + wfOut( "ok\n" ); + + wfOut( "Migrating old restrictions to new table...\n" ); + $task = new UpdateRestrictions(); + $task->execute(); + } + + protected function doCategorylinksIndicesUpdate() { + if ( !$this->indexHasField( 'categorylinks', 'cl_sortkey', 'cl_from' ) ) { + $this->applyPatch( 'patch-categorylinksindex.sql' ); + wfOut( "...categorylinks indices updated\n" ); + } + } + + protected function doCategoryPopulation() { + if ( $this->updateRowExists( 'populate category' ) ) { + wfOut( "...category table already populated.\n" ); + return; + } + + wfOut( + "Populating category table, printing progress markers. " . + "For large databases, you\n" . + "may want to hit Ctrl-C and do this manually with maintenance/\n" . + "populateCategory.php.\n" + ); + $task = new PopulateCategory(); + $task->execute(); + wfOut( "Done populating category table.\n" ); + } + + protected function doPopulateParentId() { + if ( $this->updateRowExists( 'populate rev_parent_id' ) ) { + wfOut( "...rev_parent_id column already populated.\n" ); + return; + } + + $task = new PopulateParentId(); + $task->execute(); + } + + protected function doMaybeProfilingMemoryUpdate() { + if ( !$this->db->tableExists( 'profiling' ) ) { + // Simply ignore + } elseif ( $this->db->fieldExists( 'profiling', 'pf_memory' ) ) { + wfOut( "...profiling table has pf_memory field.\n" ); + } else { + wfOut( "Adding pf_memory field to table profiling..." ); + $this->applyPatch( 'patch-profiling-memory.sql' ); + wfOut( "ok\n" ); + } + } + + protected function doFilearchiveIndicesUpdate() { + $info = $this->db->indexInfo( 'filearchive', 'fa_user_timestamp', __METHOD__ ); + if ( !$info ) { + $this->applyPatch( 'patch-filearchive-user-index.sql' ); + wfOut( "...filearchive indices updated\n" ); + } + } + + protected function doUniquePlTlIl() { + $info = $this->db->indexInfo( 'pagelinks', 'pl_namespace' ); + if ( is_array( $info ) && !$info[0]->Non_unique ) { + wfOut( "...pl_namespace, tl_namespace, il_to indices are already UNIQUE.\n" ); + return; + } + + wfOut( "Making pl_namespace, tl_namespace and il_to indices UNIQUE... " ); + $this->applyPatch( 'patch-pl-tl-il-unique.sql' ); + wfOut( "ok\n" ); + } + + protected function renameEuWikiId() { + if ( $this->db->fieldExists( 'external_user', 'eu_local_id' ) ) { + wfOut( "...eu_wiki_id already renamed to eu_local_id.\n" ); + return; + } + + wfOut( "Renaming eu_wiki_id -> eu_local_id... " ); + $this->applyPatch( 'patch-eu_local_id.sql' ); + wfOut( "ok\n" ); + } + + protected function doUpdateMimeMinorField() { + if ( $this->updateRowExists( 'mime_minor_length' ) ) { + wfOut( "...*_mime_minor fields are already long enough.\n" ); + return; + } + + wfOut( "Altering all *_mime_minor fields to 100 bytes in size ... " ); + $this->applyPatch( 'patch-mime_minor_length.sql' ); + wfOut( "ok\n" ); + } + + protected function doPopulateRevLen() { + if ( $this->updateRowExists( 'populate rev_len' ) ) { + wfOut( "...rev_len column already populated.\n" ); + return; + } + + $task = new PopulateRevisionLength(); + $task->execute(); + } + + protected function doClFieldsUpdate() { + if ( $this->updateRowExists( 'cl_fields_update' ) ) { + wfOut( "...categorylinks up-to-date.\n" ); + return; + } + + wfOut( 'Updating categorylinks (again)...' ); + $this->applyPatch( 'patch-categorylinks-better-collation2.sql' ); + wfOut( "done.\n" ); + } } diff --git a/includes/installer/SqliteUpdater.php b/includes/installer/SqliteUpdater.php index b2f0de3c14..b615c8eaec 100644 --- a/includes/installer/SqliteUpdater.php +++ b/includes/installer/SqliteUpdater.php @@ -18,7 +18,7 @@ class SqliteUpdater extends DatabaseUpdater { return array( // 1.14 array( 'addField', 'site_stats', 'ss_active_users', 'patch-ss_active_users.sql' ), - array( 'do_active_users_init' ), + array( 'doActiveUsersInit' ), array( 'addField', 'ipblocks', 'ipb_allow_usertalk', 'patch-ipb_allow_usertalk.sql' ), array( 'sqliteInitialIndexes' ), @@ -30,14 +30,14 @@ class SqliteUpdater extends DatabaseUpdater { // 1.16 array( 'addTable', 'user_properties', 'patch-user_properties.sql' ), array( 'addTable', 'log_search', 'patch-log_search.sql' ), - array( 'do_log_search_population' ), + array( 'doLogSearchPopulation' ), array( 'addField', 'logging', 'log_user_text', 'patch-log_user_text.sql' ), array( 'addTable', 'l10n_cache', 'patch-l10n_cache.sql' ), array( 'addTable', 'external_user', 'patch-external_user.sql' ), array( 'addIndex', 'log_search', 'ls_field_val', 'patch-log_search-rename-index.sql' ), array( 'addIndex', 'change_tag', 'change_tag_rc_tag', 'patch-change_tag-indexes.sql' ), array( 'addField', 'redirect', 'rd_interwiki', 'patch-rd_interwiki.sql' ), - array( 'do_update_transcache_field' ), + array( 'doUpdateTranscacheField' ), array( 'sqliteSetupSearchindex' ), // 1.17 @@ -47,6 +47,8 @@ class SqliteUpdater extends DatabaseUpdater { array( 'addField', 'interwiki', 'iw_api', 'patch-iw_api_and_wikiid.sql' ), array( 'dropIndex', 'iwlinks', 'iwl_prefix', 'patch-kill-iwl_prefix.sql' ), array( 'dropIndex', 'iwlinks', 'iwl_prefix_from_title', 'patch-kill-iwl_pft.sql' ), + array( 'addField', 'categorylinks', 'cl_collation', 'patch-categorylinks-better-collation.sql' ), + array( 'doCollationUpdate' ), array( 'addTable', 'msg_resource', 'patch-msg_resource.sql' ), array( 'addTable', 'module_deps', 'patch-module_deps.sql' ), ); @@ -54,7 +56,7 @@ class SqliteUpdater extends DatabaseUpdater { protected function sqliteInitialIndexes() { // initial-indexes.sql fails if the indexes are already present, so we perform a quick check if our database is newer. - if ( update_row_exists( 'initial_indexes' ) || $this->db->indexExists( 'user', 'user_name' ) ) { + if ( $this->updateRowExists( 'initial_indexes' ) || $this->db->indexExists( 'user', 'user_name' ) ) { wfOut( "...have initial indexes\n" ); return; } @@ -65,7 +67,7 @@ class SqliteUpdater extends DatabaseUpdater { protected function sqliteSetupSearchindex() { $module = $this->db->getFulltextSearchModule(); - $fts3tTable = update_row_exists( 'fts3' ); + $fts3tTable = $this->updateRowExists( 'fts3' ); if ( $fts3tTable && !$module ) { wfOut( '...PHP is missing FTS3 support, downgrading tables...' ); $this->applyPatch( 'searchindex-no-fts.sql' ); diff --git a/maintenance/install-utils.inc b/maintenance/install-utils.inc index 546dad42c9..e37aeb9f7c 100644 --- a/maintenance/install-utils.inc +++ b/maintenance/install-utils.inc @@ -189,19 +189,9 @@ function dbsource( $fname, $db = false ) { } } -/** - * Helper function: check if the given key is present in the updatelog table. - * Obviously, only use this for updates that occur after the updatelog table was - * created! - */ -function update_row_exists( $key ) { - $row = wfGetDB( DB_MASTER )->selectRow( - 'updatelog', - '1', - array( 'ul_key' => $key ), - __FUNCTION__ - ); - return (bool)$row; +function archive( $name ) { + wfDeprecated( __METHOD__ ); + return DatabaseBase::patchPath( $name ); } /** diff --git a/maintenance/sqlite/archives/patch-categorylinks-better-collation.sql b/maintenance/sqlite/archives/patch-categorylinks-better-collation.sql new file mode 100644 index 0000000000..0c517507c4 --- /dev/null +++ b/maintenance/sqlite/archives/patch-categorylinks-better-collation.sql @@ -0,0 +1,7 @@ +ALTER TABLE /*_*/categorylinks ADD COLUMN cl_sortkey_prefix TEXT binary NOT NULL default ''; +ALTER TABLE /*_*/categorylinks ADD COLUMN cl_collation BLOB NOT NULL default ''; +ALTER TABLE /*_*/categorylinks ADD COLUMN cl_type ENUM('page', 'subcat', 'file') NOT NULL default 'page'; +CREATE INDEX cl_collation ON /*_*/categorylinks (cl_collation); +DROP INDEX cl_sortkey; +CREATE INDEX cl_sortkey ON /*_*/categorylinks (cl_to, cl_type, cl_sortkey, cl_from); +INSERT IGNORE INTO /*_*/updatelog (ul_key) VALUES ('cl_fields_update'); diff --git a/maintenance/updaters.inc b/maintenance/updaters.inc deleted file mode 100644 index 4824765c74..0000000000 --- a/maintenance/updaters.inc +++ /dev/null @@ -1,727 +0,0 @@ -tableExists( "interwiki" ) ) { - wfOut( "...already have interwiki table\n" ); - return true; - } - wfOut( "Creating interwiki table: " ); - $dbw->sourceFile( archive( "patch-interwiki.sql" ) ); - wfOut( "ok\n" ); - wfOut( "Adding default interwiki definitions: " ); - $dbw->sourceFile( "$IP/maintenance/interwiki.sql" ); - wfOut( "ok\n" ); -} - -function do_index_update() { - # Check that proper indexes are in place - $dbw = wfGetDB( DB_MASTER ); - $meta = $dbw->fieldInfo( "recentchanges", "rc_timestamp" ); - if ( !$meta->isMultipleKey() ) { - wfOut( "Updating indexes to 20031107: " ); - $dbw->sourceFile( archive( "patch-indexes.sql" ) ); - wfOut( "ok\n" ); - return true; - } - wfOut( "...indexes seem up to 20031107 standards\n" ); - return false; -} - -function do_image_name_unique_update() { - $dbw = wfGetDB( DB_MASTER ); - if ( $dbw->indexExists( 'image', 'PRIMARY' ) ) { - wfOut( "...image primary key already set.\n" ); - } else { - wfOut( "Making img_name the primary key... " ); - $dbw->sourceFile( archive( "patch-image_name_primary.sql" ) ); - wfOut( "ok\n" ); - } -} - -function do_watchlist_update() { - $dbw = wfGetDB( DB_MASTER ); - # Check if we need to add talk page rows to the watchlist - $talk = $dbw->selectField( 'watchlist', 'count(*)', 'wl_namespace & 1', __METHOD__ ); - $nontalk = $dbw->selectField( 'watchlist', 'count(*)', 'NOT (wl_namespace & 1)', __METHOD__ ); - if ( $talk != $nontalk ) { - wfOut( "Adding missing watchlist talk page rows... " ); - - $dbw->insertSelect( 'watchlist', 'watchlist', - array( - 'wl_user' => 'wl_user', - 'wl_namespace' => 'wl_namespace | 1', - 'wl_title' => 'wl_title', - 'wl_notificationtimestamp' => 'wl_notificationtimestamp' - ), array( 'NOT (wl_namespace & 1)' ), __METHOD__, 'IGNORE' ); - wfOut( "ok\n" ); - } else { - wfOut( "...watchlist talk page rows already present\n" ); - } -} - -/** - * 1.4 betas were missing the 'binary' marker from logging.log_title, - * which causes a collation mismatch error on joins in MySQL 4.1. - */ -function check_bin( $table, $field, $patchFile ) { - $dbw = wfGetDB( DB_MASTER ); - if ( $dbw->getType() != 'mysql' ) - return; - $tableName = $dbw->tableName( $table ); - $res = $dbw->query( "SELECT $field FROM $tableName LIMIT 0", __METHOD__ ); - $flags = explode( ' ', mysql_field_flags( $res->result, 0 ) ); - - if ( in_array( 'binary', $flags ) ) { - wfOut( "...$table table has correct $field encoding.\n" ); - } else { - wfOut( "Fixing $field encoding on $table table... " ); - $dbw->sourceFile( archive( $patchFile ) ); - wfOut( "ok\n" ); - } -} - -function do_schema_restructuring() { - $dbw = wfGetDB( DB_MASTER ); - if ( $dbw->tableExists( 'page' ) ) { - wfOut( "...page table already exists.\n" ); - } else { - wfOut( "...converting from cur/old to page/revision/text DB structure.\n" ); - wfOut( wfTimestamp( TS_DB ) ); - wfOut( "......checking for duplicate entries.\n" ); - - list ( $cur, $old, $page, $revision, $text ) = $dbw->tableNamesN( 'cur', 'old', 'page', 'revision', 'text' ); - - $rows = $dbw->query( "SELECT cur_title, cur_namespace, COUNT(cur_namespace) AS c - FROM $cur GROUP BY cur_title, cur_namespace HAVING c>1", __METHOD__ ); - - if ( $dbw->numRows( $rows ) > 0 ) { - wfOut( wfTimestamp( TS_DB ) ); - wfOut( "......Found duplicate entries\n" ); - wfOut( sprintf( " %-60s %3s %5s\n", 'Title', 'NS', 'Count' ) ); - while ( $row = $dbw->fetchObject( $rows ) ) { - if ( ! isset( $duplicate[$row->cur_namespace] ) ) { - $duplicate[$row->cur_namespace] = array(); - } - $duplicate[$row->cur_namespace][] = $row->cur_title; - wfOut( sprintf( " %-60s %3s %5s\n", $row->cur_title, $row->cur_namespace, $row->c ) ); - } - $sql = "SELECT cur_title, cur_namespace, cur_id, cur_timestamp FROM $cur WHERE "; - $firstCond = true; - foreach ( $duplicate as $ns => $titles ) { - if ( $firstCond ) { - $firstCond = false; - } else { - $sql .= ' OR '; - } - $sql .= "( cur_namespace = {$ns} AND cur_title in ("; - $first = true; - foreach ( $titles as $t ) { - if ( $first ) { - $sql .= $dbw->addQuotes( $t ); - $first = false; - } else { - $sql .= ', ' . $dbw->addQuotes( $t ); - } - } - $sql .= ") ) \n"; - } - # By sorting descending, the most recent entry will be the first in the list. - # All following entries will be deleted by the next while-loop. - $sql .= 'ORDER BY cur_namespace, cur_title, cur_timestamp DESC'; - - $rows = $dbw->query( $sql, __METHOD__ ); - - $prev_title = $prev_namespace = false; - $deleteId = array(); - - while ( $row = $dbw->fetchObject( $rows ) ) { - if ( $prev_title == $row->cur_title && $prev_namespace == $row->cur_namespace ) { - $deleteId[] = $row->cur_id; - } - $prev_title = $row->cur_title; - $prev_namespace = $row->cur_namespace; - } - $sql = "DELETE FROM $cur WHERE cur_id IN ( " . join( ',', $deleteId ) . ')'; - $rows = $dbw->query( $sql, __METHOD__ ); - wfOut( wfTimestamp( TS_DB ) ); - wfOut( "......Deleted " . $dbw->affectedRows() . " records.\n" ); - } - - - wfOut( wfTimestamp( TS_DB ) ); - wfOut( "......Creating tables.\n" ); - $dbw->query( "CREATE TABLE $page ( - page_id int(8) unsigned NOT NULL auto_increment, - page_namespace int NOT NULL, - page_title varchar(255) binary NOT NULL, - page_restrictions tinyblob NOT NULL, - page_counter bigint(20) unsigned NOT NULL default '0', - page_is_redirect tinyint(1) unsigned NOT NULL default '0', - page_is_new tinyint(1) unsigned NOT NULL default '0', - page_random real unsigned NOT NULL, - page_touched char(14) binary NOT NULL default '', - page_latest int(8) unsigned NOT NULL, - page_len int(8) unsigned NOT NULL, - - PRIMARY KEY page_id (page_id), - UNIQUE INDEX name_title (page_namespace,page_title), - INDEX (page_random), - INDEX (page_len) - ) ENGINE=InnoDB", __METHOD__ ); - $dbw->query( "CREATE TABLE $revision ( - rev_id int(8) unsigned NOT NULL auto_increment, - rev_page int(8) unsigned NOT NULL, - rev_comment tinyblob NOT NULL, - rev_user int(5) unsigned NOT NULL default '0', - rev_user_text varchar(255) binary NOT NULL default '', - rev_timestamp char(14) binary NOT NULL default '', - rev_minor_edit tinyint(1) unsigned NOT NULL default '0', - rev_deleted tinyint(1) unsigned NOT NULL default '0', - rev_len int(8) unsigned, - rev_parent_id int(8) unsigned default NULL, - PRIMARY KEY rev_page_id (rev_page, rev_id), - UNIQUE INDEX rev_id (rev_id), - INDEX rev_timestamp (rev_timestamp), - INDEX page_timestamp (rev_page,rev_timestamp), - INDEX user_timestamp (rev_user,rev_timestamp), - INDEX usertext_timestamp (rev_user_text,rev_timestamp) - ) ENGINE=InnoDB", __METHOD__ ); - - wfOut( wfTimestamp( TS_DB ) ); - wfOut( "......Locking tables.\n" ); - $dbw->query( "LOCK TABLES $page WRITE, $revision WRITE, $old WRITE, $cur WRITE", __METHOD__ ); - - $maxold = intval( $dbw->selectField( 'old', 'max(old_id)', '', __METHOD__ ) ); - wfOut( wfTimestamp( TS_DB ) ); - wfOut( "......maxold is {$maxold}\n" ); - - wfOut( wfTimestamp( TS_DB ) ); - global $wgLegacySchemaConversion; - if ( $wgLegacySchemaConversion ) { - // Create HistoryBlobCurStub entries. - // Text will be pulled from the leftover 'cur' table at runtime. - wfOut( "......Moving metadata from cur; using blob references to text in cur table.\n" ); - $cur_text = "concat('O:18:\"historyblobcurstub\":1:{s:6:\"mCurId\";i:',cur_id,';}')"; - $cur_flags = "'object'"; - } else { - // Copy all cur text in immediately: this may take longer but avoids - // having to keep an extra table around. - wfOut( "......Moving text from cur.\n" ); - $cur_text = 'cur_text'; - $cur_flags = "''"; - } - $dbw->query( "INSERT INTO $old (old_namespace, old_title, old_text, old_comment, old_user, old_user_text, - old_timestamp, old_minor_edit, old_flags) - SELECT cur_namespace, cur_title, $cur_text, cur_comment, cur_user, cur_user_text, cur_timestamp, cur_minor_edit, $cur_flags - FROM $cur", __METHOD__ ); - - wfOut( wfTimestamp( TS_DB ) ); - wfOut( "......Setting up revision table.\n" ); - $dbw->query( "INSERT INTO $revision (rev_id, rev_page, rev_comment, rev_user, rev_user_text, rev_timestamp, - rev_minor_edit) - SELECT old_id, cur_id, old_comment, old_user, old_user_text, - old_timestamp, old_minor_edit - FROM $old,$cur WHERE old_namespace=cur_namespace AND old_title=cur_title", __METHOD__ ); - - wfOut( wfTimestamp( TS_DB ) ); - wfOut( "......Setting up page table.\n" ); - $dbw->query( "INSERT INTO $page (page_id, page_namespace, page_title, page_restrictions, page_counter, - page_is_redirect, page_is_new, page_random, page_touched, page_latest, page_len) - SELECT cur_id, cur_namespace, cur_title, cur_restrictions, cur_counter, cur_is_redirect, cur_is_new, - cur_random, cur_touched, rev_id, LENGTH(cur_text) - FROM $cur,$revision - WHERE cur_id=rev_page AND rev_timestamp=cur_timestamp AND rev_id > {$maxold}", __METHOD__ ); - - wfOut( wfTimestamp( TS_DB ) ); - wfOut( "......Unlocking tables.\n" ); - $dbw->query( "UNLOCK TABLES", __METHOD__ ); - - wfOut( wfTimestamp( TS_DB ) ); - wfOut( "......Renaming old.\n" ); - $dbw->query( "ALTER TABLE $old RENAME TO $text", __METHOD__ ); - - wfOut( wfTimestamp( TS_DB ) ); - wfOut( "...done.\n" ); - } -} - -function do_pagelinks_update() { - $dbw = wfGetDB( DB_MASTER ); - if ( $dbw->tableExists( 'pagelinks' ) ) { - wfOut( "...already have pagelinks table.\n" ); - } else { - wfOut( "Converting links and brokenlinks tables to pagelinks... " ); - $dbw->sourceFile( archive( 'patch-pagelinks.sql' ) ); - wfOut( "ok\n" ); - - global $wgCanonicalNamespaceNames; - foreach ( $wgCanonicalNamespaceNames as $ns => $name ) { - if ( $ns != 0 ) { - do_pagelinks_namespace( $ns ); - } - } - } -} - -function do_pagelinks_namespace( $namespace ) { - global $wgContLang; - - $dbw = wfGetDB( DB_MASTER ); - $ns = intval( $namespace ); - wfOut( "Cleaning up broken links for namespace $ns... " ); - - $pagelinks = $dbw->tableName( 'pagelinks' ); - $name = $wgContLang->getNsText( $ns ); - $prefix = $dbw->strencode( $name ); - $likeprefix = str_replace( '_', '\\_', $prefix ); - - $sql = "UPDATE $pagelinks - SET pl_namespace=$ns, - pl_title=TRIM(LEADING '$prefix:' FROM pl_title) - WHERE pl_namespace=0 - AND pl_title LIKE '$likeprefix:%'"; - - $dbw->query( $sql, 'do_pagelinks_namespace' ); - wfOut( "ok\n" ); -} - -function do_old_links_update() { - $cl = new ConvertLinks(); - $cl->execute(); -} - -function fix_ancient_imagelinks() { - $dbw = wfGetDB( DB_MASTER ); - $info = $dbw->fieldInfo( 'imagelinks', 'il_from' ); - if ( $info && $info->type() === 'string' ) { - wfOut( "Fixing ancient broken imagelinks table.\n" ); - wfOut( "NOTE: you will have to run maintenance/refreshLinks.php after this.\n" ); - $dbw->sourceFile( archive( 'patch-fix-il_from.sql' ) ); - wfOut( "ok\n" ); - } else { - wfOut( "...il_from OK\n" ); - } -} - -function do_user_unique_update() { - $dbw = wfGetDB( DB_MASTER ); - $duper = new UserDupes( $dbw ); - if ( $duper->hasUniqueIndex() ) { - wfOut( "...already have unique user_name index.\n" ); - } else { - if ( !$duper->clearDupes() ) { - wfOut( "WARNING: This next step will probably fail due to unfixed duplicates...\n" ); - } - wfOut( "Adding unique index on user_name... " ); - $dbw->sourceFile( archive( 'patch-user_nameindex.sql' ) ); - wfOut( "ok\n" ); - } -} - -function do_user_groups_update() { - $dbw = wfGetDB( DB_MASTER ); - - if ( $dbw->tableExists( 'user_groups' ) ) { - wfOut( "...user_groups table already exists.\n" ); - return do_user_groups_reformat(); - } - - wfOut( "Adding user_groups table... " ); - $dbw->sourceFile( archive( 'patch-user_groups.sql' ) ); - wfOut( "ok\n" ); - - if ( !$dbw->tableExists( 'user_rights' ) ) { - if ( $dbw->fieldExists( 'user', 'user_rights' ) ) { - wfOut( "Upgrading from a 1.3 or older database? Breaking out user_rights for conversion..." ); - $dbw->sourceFile( archive( 'patch-user_rights.sql' ) ); - wfOut( "ok\n" ); - } else { - wfOut( "*** WARNING: couldn't locate user_rights table or field for upgrade.\n" ); - wfOut( "*** You may need to manually configure some sysops by manipulating\n" ); - wfOut( "*** the user_groups table.\n" ); - return; - } - } - - wfOut( "Converting user_rights table to user_groups... " ); - $result = $dbw->select( 'user_rights', - array( 'ur_user', 'ur_rights' ), - array( "ur_rights != ''" ), - __METHOD__ ); - - while ( $row = $dbw->fetchObject( $result ) ) { - $groups = array_unique( - array_map( 'trim', - explode( ',', $row->ur_rights ) ) ); - - foreach ( $groups as $group ) { - $dbw->insert( 'user_groups', - array( - 'ug_user' => $row->ur_user, - 'ug_group' => $group ), - __METHOD__ ); - } - } - wfOut( "ok\n" ); -} - -function do_user_groups_reformat() { - # Check for bogus formats from previous 1.5 alpha code. - $dbw = wfGetDB( DB_MASTER ); - $info = $dbw->fieldInfo( 'user_groups', 'ug_group' ); - - if ( $info->type() == 'int' ) { - $oldug = $dbw->tableName( 'user_groups' ); - $newug = $dbw->tableName( 'user_groups_bogus' ); - wfOut( "user_groups is in bogus intermediate format. Renaming to $newug... " ); - $dbw->query( "ALTER TABLE $oldug RENAME TO $newug" ); - wfOut( "ok\n" ); - - wfOut( "Re-adding fresh user_groups table... " ); - $dbw->sourceFile( archive( 'patch-user_groups.sql' ) ); - wfOut( "ok\n" ); - - wfOut( "***\n" ); - wfOut( "*** WARNING: You will need to manually fix up user permissions in the user_groups\n" ); - wfOut( "*** table. Old 1.5 alpha versions did some pretty funky stuff...\n" ); - wfOut( "***\n" ); - } else { - wfOut( "...user_groups is in current format.\n" ); - } - -} - -function do_watchlist_null() { - # Make sure wl_notificationtimestamp can be NULL, - # and update old broken items. - $dbw = wfGetDB( DB_MASTER ); - $info = $dbw->fieldInfo( 'watchlist', 'wl_notificationtimestamp' ); - - if ( !$info->nullable() ) { - wfOut( "Making wl_notificationtimestamp nullable... " ); - $dbw->sourceFile( archive( 'patch-watchlist-null.sql' ) ); - wfOut( "ok\n" ); - } else { - wfOut( "...wl_notificationtimestamp is already nullable.\n" ); - } - -} - -/** - * @bug 3946 - */ -function do_page_random_update() { - wfOut( "Setting page_random to a random value on rows where it equals 0..." ); - - $dbw = wfGetDB( DB_MASTER ); - $page = $dbw->tableName( 'page' ); - $dbw->query( "UPDATE $page SET page_random = RAND() WHERE page_random = 0", 'do_page_random_update' ); - $rows = $dbw->affectedRows(); - - wfOut( "changed $rows rows\n" ); -} - -function do_templatelinks_update() { - $dbw = wfGetDB( DB_MASTER ); - - if ( $dbw->tableExists( 'templatelinks' ) ) { - wfOut( "...templatelinks table already exists\n" ); - return; - } - wfOut( "Creating templatelinks table...\n" ); - $dbw->sourceFile( archive( 'patch-templatelinks.sql' ) ); - wfOut( "Populating...\n" ); - if ( wfGetLB()->getServerCount() > 1 ) { - // Slow, replication-friendly update - $res = $dbw->select( 'pagelinks', array( 'pl_from', 'pl_namespace', 'pl_title' ), - array( 'pl_namespace' => NS_TEMPLATE ), __METHOD__ ); - $count = 0; - while ( $row = $dbw->fetchObject( $res ) ) { - $count = ( $count + 1 ) % 100; - if ( $count == 0 ) { - if ( function_exists( 'wfWaitForSlaves' ) ) { - wfWaitForSlaves( 10 ); - } else { - sleep( 1 ); - } - } - $dbw->insert( 'templatelinks', - array( - 'tl_from' => $row->pl_from, - 'tl_namespace' => $row->pl_namespace, - 'tl_title' => $row->pl_title, - ), __METHOD__ - ); - - } - } else { - // Fast update - $dbw->insertSelect( 'templatelinks', 'pagelinks', - array( - 'tl_from' => 'pl_from', - 'tl_namespace' => 'pl_namespace', - 'tl_title' => 'pl_title' - ), array( - 'pl_namespace' => 10 - ), __METHOD__ - ); - } - wfOut( "Done. Please run maintenance/refreshLinks.php for a more thorough templatelinks update.\n" ); -} - -// Add index on ( rc_namespace, rc_user_text ) [Jul. 2006] -// Add index on ( rc_user_text, rc_timestamp ) [Nov. 2006] -function do_rc_indices_update() { - $dbw = wfGetDB( DB_MASTER ); - wfOut( "Checking for additional recent changes indices...\n" ); - - $indexes = array( - 'rc_ns_usertext' => 'patch-recentchanges-utindex.sql', - 'rc_user_text' => 'patch-rc_user_text-index.sql', - ); - - foreach ( $indexes as $index => $patch ) { - $info = $dbw->indexInfo( 'recentchanges', $index, __METHOD__ ); - if ( !$info ) { - wfOut( "...index `{$index}` not found; adding..." ); - $dbw->sourceFile( archive( $patch ) ); - wfOut( "done.\n" ); - } else { - wfOut( "...index `{$index}` seems ok.\n" ); - } - } -} - -function index_has_field( $table, $index, $field ) { - wfOut( "Checking if $table index $index includes field $field...\n" ); - $info = wfGetDB( DB_MASTER )->indexInfo( $table, $index, __METHOD__ ); - if ( $info ) { - foreach ( $info as $row ) { - if ( $row->Column_name == $field ) { - wfOut( "...index $index on table $table seems to be ok\n" ); - return true; - } - } - } - wfOut( "...index $index on table $table has no field $field; adding\n" ); - return false; -} - -function do_backlinking_indices_update() { - wfOut( "Checking for backlinking indices...\n" ); - if ( !index_has_field( 'pagelinks', 'pl_namespace', 'pl_from' ) || - !index_has_field( 'templatelinks', 'tl_namespace', 'tl_from' ) || - !index_has_field( 'imagelinks', 'il_to', 'il_from' ) ) - { - wfGetDB( DB_MASTER )->sourceFile( archive( 'patch-backlinkindexes.sql' ) ); - wfOut( "...backlinking indices updated\n" ); - } -} - -function do_categorylinks_indices_update() { - wfOut( "Checking for categorylinks indices...\n" ); - if ( !index_has_field( 'categorylinks', 'cl_sortkey', 'cl_from' ) ) - { - wfGetDB( DB_MASTER )->sourceFile( archive( 'patch-categorylinksindex.sql' ) ); - wfOut( "...categorylinks indices updated\n" ); - } -} - -function do_filearchive_indices_update() { - $dbw = wfGetDB( DB_MASTER ); - wfOut( "Checking filearchive indices...\n" ); - $info = $dbw->indexInfo( 'filearchive', 'fa_user_timestamp', __METHOD__ ); - if ( !$info ) - { - $dbw->sourceFile( archive( 'patch-filearchive-user-index.sql' ) ); - wfOut( "...filearchive indices updated\n" ); - } -} - -function maybe_do_profiling_memory_update() { - $dbw = wfGetDB( DB_MASTER ); - if ( !$dbw->tableExists( 'profiling' ) ) { - // Simply ignore - } elseif ( $dbw->fieldExists( 'profiling', 'pf_memory' ) ) { - wfOut( "...profiling table has pf_memory field.\n" ); - } else { - wfOut( "Adding pf_memory field to table profiling..." ); - $dbw->sourceFile( archive( 'patch-profiling-memory.sql' ) ); - wfOut( "ok\n" ); - } -} - -function do_active_users_init() { - $dbw = wfGetDB( DB_MASTER ); - $activeUsers = $dbw->selectField( 'site_stats', 'ss_active_users', false, __METHOD__ ); - if ( $activeUsers == -1 ) { - $activeUsers = $dbw->selectField( 'recentchanges', - 'COUNT( DISTINCT rc_user_text )', - array( 'rc_user != 0', 'rc_bot' => 0, "rc_log_type != 'newusers'" ), __METHOD__ - ); - $dbw->update( 'site_stats', - array( 'ss_active_users' => intval( $activeUsers ) ), - array( 'ss_row_id' => 1 ), __METHOD__, array( 'LIMIT' => 1 ) - ); - } - wfOut( "...ss_active_users user count set...\n" ); -} - -/** - * Adding page_restrictions table, obsoleting page.page_restrictions. - * Migrating old restrictions to new table - * -- Andrew Garrett, January 2007. - */ -function do_restrictions_update() { - $dbw = wfGetDB( DB_MASTER ); - if ( $dbw->tableExists( 'page_restrictions' ) ) { - wfOut( "...page_restrictions table already exists.\n" ); - } else { - wfOut( "Creating page_restrictions table..." ); - $dbw->sourceFile( archive( 'patch-page_restrictions.sql' ) ); - $dbw->sourceFile( archive( 'patch-page_restrictions_sortkey.sql' ) ); - wfOut( "ok\n" ); - - wfOut( "Migrating old restrictions to new table...\n" ); - $task = new UpdateRestrictions(); - $task->execute(); - } -} - -function do_category_population() { - if ( update_row_exists( 'populate category' ) ) { - wfOut( "...category table already populated.\n" ); - return; - } - - wfOut( - "Populating category table, printing progress markers. " . - "For large databases, you\n" . - "may want to hit Ctrl-C and do this manually with maintenance/\n" . - "populateCategory.php.\n" - ); - $task = new PopulateCategory(); - $task->execute(); - wfOut( "Done populating category table.\n" ); -} - -function do_populate_parent_id() { - if ( update_row_exists( 'populate rev_parent_id' ) ) { - wfOut( "...rev_parent_id column already populated.\n" ); - return; - } - - $task = new PopulateParentId(); - $task->execute(); -} - -function do_populate_rev_len() { - if ( update_row_exists( 'populate rev_len' ) ) { - wfOut( "...rev_len column already populated.\n" ); - return; - } - - $task = new PopulateRevisionLength(); - $task->execute(); -} - -function do_cl_fields_update() { - if ( update_row_exists( 'cl_fields_update' ) ) { - wfOut( "...categorylinks up-to-date.\n" ); - return; - } - wfOut( 'Updating categorylinks (again)...' ); - wfGetDB( DB_MASTER )->sourceFile( archive( 'patch-categorylinks-better-collation2.sql' ) ); - wfOut( "done.\n" ); -} - -function do_collation_update() { - global $wgCategoryCollation; - $dbw = wfGetDB( DB_MASTER ); - if ( $dbw->selectField( - 'categorylinks', - 'COUNT(*)', - 'cl_collation != ' . $dbw->addQuotes( $wgCategoryCollation ), - __FUNCTION__ - ) == 0 ) { - wfOut( "...collations up-to-date.\n" ); - return; - } - - $task = new UpdateCollation(); - $task->execute(); -} - -function do_unique_pl_tl_il() { - $dbw = wfGetDB( DB_MASTER ); - $info = $dbw->indexInfo( 'pagelinks', 'pl_namespace' ); - if ( is_array( $info ) && !$info[0]->Non_unique ) { - wfOut( "...pl_namespace, tl_namespace, il_to indices are already UNIQUE.\n" ); - } else { - wfOut( "Making pl_namespace, tl_namespace and il_to indices UNIQUE... " ); - $dbw->sourceFile( archive( 'patch-pl-tl-il-unique.sql' ) ); - wfOut( "ok\n" ); - } -} - -function do_log_search_population() { - if ( update_row_exists( 'populate log_search' ) ) { - wfOut( "...log_search table already populated.\n" ); - return; - } - wfOut( -"Populating log_search table, printing progress markers. For large\n" . -"databases, you may want to hit Ctrl-C and do this manually with\n" . -"maintenance/populateLogSearch.php.\n" ); - $task = new PopulateLogSearch(); - $task->execute(); - wfOut( "Done populating log_search table.\n" ); -} - -function rename_eu_wiki_id() { - $dbw = wfGetDB( DB_MASTER ); - if ( $dbw->fieldExists( 'external_user', 'eu_local_id' ) ) { - wfOut( "...eu_wiki_id already renamed to eu_local_id.\n" ); - return; - } - wfOut( "Renaming eu_wiki_id -> eu_local_id... " ); - $dbw->sourceFile( archive( 'patch-eu_local_id.sql' ) ); - wfOut( "ok\n" ); -} - -function do_update_transcache_field() { - if ( update_row_exists( 'convert transcache field' ) ) { - wfOut( "...transcache tc_time already converted.\n" ); - return; - } else { - wfOut( "Converting tc_time from UNIX epoch to MediaWiki timestamp... " ); - wfGetDB( DB_MASTER )->sourceFile( archive( 'patch-tc-timestamp.sql' ) ); - wfOut( "ok\n" ); - } -} - -function do_update_mime_minor_field() { - if ( update_row_exists( 'mime_minor_length' ) ) { - wfOut( "...*_mime_minor fields are already long enough.\n" ); - } else { - wfOut( "Altering all *_mime_minor fields to 100 bytes in size ... " ); - wfGetDB( DB_MASTER )->sourceFile( archive( 'patch-mime_minor_length.sql' ) ); - wfOut( "ok\n" ); - } -} -- 2.20.1