MediaWiki
REL1_19
|
00001 <?php 00031 require_once( dirname( __FILE__ ) . '/Maintenance.php' ); 00032 00033 define( 'MW_UPGRADE_COPY', false ); 00034 define( 'MW_UPGRADE_ENCODE', true ); 00035 define( 'MW_UPGRADE_NULL', null ); 00036 define( 'MW_UPGRADE_CALLBACK', null ); // for self-documentation only 00037 00041 class FiveUpgrade extends Maintenance { 00042 00046 protected $db; 00047 00048 function __construct() { 00049 parent::__construct(); 00050 00051 $this->mDescription = 'Script for upgrades from 1.4 to 1.5 (NOT 1.15) in very special cases.'; 00052 00053 $this->addOption( 'upgrade', 'Really run the script' ); 00054 $this->addOption( 'noimage', '' ); 00055 $this->addOption( 'step', 'Only do a specific step', false, true ); 00056 } 00057 00058 public function getDbType() { 00059 return Maintenance::DB_ADMIN; 00060 } 00061 00062 public function execute() { 00063 $this->output( "ATTENTION: This script is for upgrades from 1.4 to 1.5 (NOT 1.15) in very special cases.\n" ); 00064 $this->output( "Use update.php for usual updates.\n" ); 00065 00066 if ( !$this->hasOption( 'upgrade' ) ) { 00067 $this->output( "Please run this script with --upgrade key to actually run the updater.\n" ); 00068 return; 00069 } 00070 00071 $this->setMembers(); 00072 00073 $tables = array( 00074 'page', 00075 'links', 00076 'user', 00077 'image', 00078 'oldimage', 00079 'watchlist', 00080 'logging', 00081 'archive', 00082 'imagelinks', 00083 'categorylinks', 00084 'ipblocks', 00085 'recentchanges', 00086 'querycache' 00087 ); 00088 00089 foreach ( $tables as $table ) { 00090 if ( $this->doing( $table ) ) { 00091 $method = 'upgrade' . ucfirst( $table ); 00092 $this->$method(); 00093 } 00094 } 00095 00096 if ( $this->doing( 'cleanup' ) ) { 00097 $this->upgradeCleanup(); 00098 } 00099 } 00100 00101 protected function setMembers() { 00102 $this->conversionTables = $this->prepareWindows1252(); 00103 00104 $this->loadBalancers = array(); 00105 $this->dbw = wfGetDB( DB_MASTER ); 00106 $this->dbr = $this->streamConnection(); 00107 00108 $this->cleanupSwaps = array(); 00109 $this->emailAuth = false; # don't preauthenticate emails 00110 $this->step = $this->getOption( 'step', null ); 00111 } 00112 00113 function doing( $step ) { 00114 return is_null( $this->step ) || $step == $this->step; 00115 } 00116 00122 function newConnection() { 00123 $lb = wfGetLBFactory()->newMainLB(); 00124 $db = $lb->getConnection( DB_MASTER ); 00125 00126 $this->loadBalancers[] = $lb; 00127 return $db; 00128 } 00129 00133 function close() { 00134 foreach ( $this->loadBalancers as $lb ) { 00135 $lb->commitMasterChanges(); 00136 $lb->closeAll(); 00137 } 00138 } 00139 00147 function streamConnection() { 00148 $timeout = 3600 * 24; 00149 $db = $this->newConnection(); 00150 $db->bufferResults( false ); 00151 if ( $db->getType() == 'mysql' ) { 00152 $db->query( "SET net_read_timeout=$timeout" ); 00153 $db->query( "SET net_write_timeout=$timeout" ); 00154 } 00155 return $db; 00156 } 00157 00167 function prepareWindows1252() { 00168 # Mappings from: 00169 # http://www.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1252.TXT 00170 static $cp1252 = array( 00171 0x80 => 0x20AC, # EURO SIGN 00172 0x81 => 0xFFFD, # REPLACEMENT CHARACTER (no mapping) 00173 0x82 => 0x201A, # SINGLE LOW-9 QUOTATION MARK 00174 0x83 => 0x0192, # LATIN SMALL LETTER F WITH HOOK 00175 0x84 => 0x201E, # DOUBLE LOW-9 QUOTATION MARK 00176 0x85 => 0x2026, # HORIZONTAL ELLIPSIS 00177 0x86 => 0x2020, # DAGGER 00178 0x87 => 0x2021, # DOUBLE DAGGER 00179 0x88 => 0x02C6, # MODIFIER LETTER CIRCUMFLEX ACCENT 00180 0x89 => 0x2030, # PER MILLE SIGN 00181 0x8A => 0x0160, # LATIN CAPITAL LETTER S WITH CARON 00182 0x8B => 0x2039, # SINGLE LEFT-POINTING ANGLE QUOTATION MARK 00183 0x8C => 0x0152, # LATIN CAPITAL LIGATURE OE 00184 0x8D => 0xFFFD, # REPLACEMENT CHARACTER (no mapping) 00185 0x8E => 0x017D, # LATIN CAPITAL LETTER Z WITH CARON 00186 0x8F => 0xFFFD, # REPLACEMENT CHARACTER (no mapping) 00187 0x90 => 0xFFFD, # REPLACEMENT CHARACTER (no mapping) 00188 0x91 => 0x2018, # LEFT SINGLE QUOTATION MARK 00189 0x92 => 0x2019, # RIGHT SINGLE QUOTATION MARK 00190 0x93 => 0x201C, # LEFT DOUBLE QUOTATION MARK 00191 0x94 => 0x201D, # RIGHT DOUBLE QUOTATION MARK 00192 0x95 => 0x2022, # BULLET 00193 0x96 => 0x2013, # EN DASH 00194 0x97 => 0x2014, # EM DASH 00195 0x98 => 0x02DC, # SMALL TILDE 00196 0x99 => 0x2122, # TRADE MARK SIGN 00197 0x9A => 0x0161, # LATIN SMALL LETTER S WITH CARON 00198 0x9B => 0x203A, # SINGLE RIGHT-POINTING ANGLE QUOTATION MARK 00199 0x9C => 0x0153, # LATIN SMALL LIGATURE OE 00200 0x9D => 0xFFFD, # REPLACEMENT CHARACTER (no mapping) 00201 0x9E => 0x017E, # LATIN SMALL LETTER Z WITH CARON 00202 0x9F => 0x0178, # LATIN CAPITAL LETTER Y WITH DIAERESIS 00203 ); 00204 $pairs = array(); 00205 for ( $i = 0; $i < 0x100; $i++ ) { 00206 $unicode = isset( $cp1252[$i] ) ? $cp1252[$i] : $i; 00207 $pairs[chr( $i )] = codepointToUtf8( $unicode ); 00208 } 00209 return $pairs; 00210 } 00211 00218 function conv( $text ) { 00219 global $wgUseLatin1; 00220 return is_null( $text ) 00221 ? null 00222 : ( $wgUseLatin1 00223 ? strtr( $text, $this->conversionTables ) 00224 : $text ); 00225 } 00226 00232 function log( $message ) { 00233 $this->output( wfWikiID() . ' ' . wfTimestamp( TS_DB ) . ': ' . $message . "\n" ); 00234 } 00235 00251 function setChunkScale( $chunksize, $final, $table, $fname ) { 00252 $this->chunkSize = $chunksize; 00253 $this->chunkFinal = $final; 00254 $this->chunkCount = 0; 00255 $this->chunkStartTime = wfTime(); 00256 $this->chunkOptions = array( 'IGNORE' ); 00257 $this->chunkTable = $table; 00258 $this->chunkFunction = $fname; 00259 } 00260 00271 function addChunk( &$chunk, $key = null ) { 00272 if ( count( $chunk ) >= $this->chunkSize ) { 00273 $this->insertChunk( $chunk ); 00274 00275 $this->chunkCount += count( $chunk ); 00276 $now = wfTime(); 00277 $delta = $now - $this->chunkStartTime; 00278 $rate = $this->chunkCount / $delta; 00279 00280 if ( is_null( $key ) ) { 00281 $completed = $this->chunkCount; 00282 } else { 00283 $completed = $key; 00284 } 00285 $portion = $completed / $this->chunkFinal; 00286 00287 $estimatedTotalTime = $delta / $portion; 00288 $eta = $this->chunkStartTime + $estimatedTotalTime; 00289 00290 printf( "%s: %6.2f%% done on %s; ETA %s [%d/%d] %.2f/sec\n", 00291 wfTimestamp( TS_DB, intval( $now ) ), 00292 $portion * 100.0, 00293 $this->chunkTable, 00294 wfTimestamp( TS_DB, intval( $eta ) ), 00295 $completed, 00296 $this->chunkFinal, 00297 $rate ); 00298 flush(); 00299 00300 $chunk = array(); 00301 } 00302 } 00303 00309 function lastChunk( &$chunk ) { 00310 $n = count( $chunk ); 00311 if ( $n > 0 ) { 00312 $this->insertChunk( $chunk ); 00313 } 00314 $this->log( "100.00% done on $this->chunkTable (last chunk $n rows)." ); 00315 } 00316 00322 function insertChunk( &$chunk ) { 00323 // Give slaves a chance to catch up 00324 wfWaitForSlaves(); 00325 $this->dbw->insert( $this->chunkTable, $chunk, $this->chunkFunction, $this->chunkOptions ); 00326 } 00327 00333 static private function notUpgradeNull( $x ) { 00334 return $x !== MW_UPGRADE_NULL; 00335 } 00336 00350 function copyTable( $name, $tabledef, $fields, $callback = null ) { 00351 $name_temp = $name . '_temp'; 00352 $this->log( "Migrating $name table to $name_temp..." ); 00353 00354 $table_temp = $this->dbw->tableName( $name_temp ); 00355 00356 // Create temporary table; we're going to copy everything in there, 00357 // then at the end rename the final tables into place. 00358 $def = str_replace( '$1', $table_temp, $tabledef ); 00359 $this->dbw->query( $def, __METHOD__ ); 00360 00361 $numRecords = $this->dbw->selectField( $name, 'COUNT(*)', '', __METHOD__ ); 00362 $this->setChunkScale( 100, $numRecords, $name_temp, __METHOD__ ); 00363 00364 // Pull all records from the second, streaming database connection. 00365 $sourceFields = array_keys( array_filter( $fields, 'FiveUpgrade::notUpgradeNull' ) ); 00366 $result = $this->dbr->select( $name, 00367 $sourceFields, 00368 '', 00369 __METHOD__ ); 00370 00371 $add = array(); 00372 foreach ( $result as $row ) { 00373 $copy = array(); 00374 foreach ( $fields as $field => $source ) { 00375 if ( $source === MW_UPGRADE_COPY ) { 00376 $copy[$field] = $row->$field; 00377 } elseif ( $source === MW_UPGRADE_ENCODE ) { 00378 $copy[$field] = $this->conv( $row->$field ); 00379 } elseif ( $source === MW_UPGRADE_NULL ) { 00380 $copy[$field] = null; 00381 } else { 00382 $this->log( "Unknown field copy type: $field => $source" ); 00383 } 00384 } 00385 if ( is_callable( $callback ) ) { 00386 $copy = call_user_func( $callback, $row, $copy ); 00387 } 00388 $add[] = $copy; 00389 $this->addChunk( $add ); 00390 } 00391 $this->lastChunk( $add ); 00392 00393 $this->log( "Done converting $name." ); 00394 $this->cleanupSwaps[] = $name; 00395 } 00396 00397 function upgradePage() { 00398 $chunksize = 100; 00399 00400 if ( $this->dbw->tableExists( 'page' ) ) { 00401 $this->error( 'Page table already exists.', true ); 00402 } 00403 00404 $this->log( "Checking cur table for unique title index and applying if necessary" ); 00405 $this->checkDupes(); 00406 00407 $this->log( "...converting from cur/old to page/revision/text DB structure." ); 00408 00409 list ( $cur, $old, $page, $revision, $text ) = $this->dbw->tableNamesN( 'cur', 'old', 'page', 'revision', 'text' ); 00410 00411 $this->log( "Creating page and revision tables..." ); 00412 $this->dbw->query( "CREATE TABLE $page ( 00413 page_id int(8) unsigned NOT NULL auto_increment, 00414 page_namespace int NOT NULL, 00415 page_title varchar(255) binary NOT NULL, 00416 page_restrictions tinyblob NOT NULL default '', 00417 page_counter bigint(20) unsigned NOT NULL default '0', 00418 page_is_redirect tinyint(1) unsigned NOT NULL default '0', 00419 page_is_new tinyint(1) unsigned NOT NULL default '0', 00420 page_random real unsigned NOT NULL, 00421 page_touched char(14) binary NOT NULL default '', 00422 page_latest int(8) unsigned NOT NULL, 00423 page_len int(8) unsigned NOT NULL, 00424 00425 PRIMARY KEY page_id (page_id), 00426 UNIQUE INDEX name_title (page_namespace,page_title), 00427 INDEX (page_random), 00428 INDEX (page_len) 00429 ) TYPE=InnoDB", __METHOD__ ); 00430 $this->dbw->query( "CREATE TABLE $revision ( 00431 rev_id int(8) unsigned NOT NULL auto_increment, 00432 rev_page int(8) unsigned NOT NULL, 00433 rev_text_id int(8) unsigned NOT NULL, 00434 rev_comment tinyblob NOT NULL default '', 00435 rev_user int(5) unsigned NOT NULL default '0', 00436 rev_user_text varchar(255) binary NOT NULL default '', 00437 rev_timestamp char(14) binary NOT NULL default '', 00438 rev_minor_edit tinyint(1) unsigned NOT NULL default '0', 00439 rev_deleted tinyint(1) unsigned NOT NULL default '0', 00440 00441 PRIMARY KEY rev_page_id (rev_page, rev_id), 00442 UNIQUE INDEX rev_id (rev_id), 00443 INDEX rev_timestamp (rev_timestamp), 00444 INDEX page_timestamp (rev_page,rev_timestamp), 00445 INDEX user_timestamp (rev_user,rev_timestamp), 00446 INDEX usertext_timestamp (rev_user_text,rev_timestamp) 00447 ) TYPE=InnoDB", __METHOD__ ); 00448 00449 $maxold = intval( $this->dbw->selectField( 'old', 'max(old_id)', '', __METHOD__ ) ); 00450 $this->log( "Last old record is {$maxold}" ); 00451 00452 global $wgLegacySchemaConversion; 00453 if ( $wgLegacySchemaConversion ) { 00454 // Create HistoryBlobCurStub entries. 00455 // Text will be pulled from the leftover 'cur' table at runtime. 00456 echo "......Moving metadata from cur; using blob references to text in cur table.\n"; 00457 $cur_text = "concat('O:18:\"historyblobcurstub\":1:{s:6:\"mCurId\";i:',cur_id,';}')"; 00458 $cur_flags = "'object'"; 00459 } else { 00460 // Copy all cur text in immediately: this may take longer but avoids 00461 // having to keep an extra table around. 00462 echo "......Moving text from cur.\n"; 00463 $cur_text = 'cur_text'; 00464 $cur_flags = "''"; 00465 } 00466 00467 $maxcur = $this->dbw->selectField( 'cur', 'max(cur_id)', '', __METHOD__ ); 00468 $this->log( "Last cur entry is $maxcur" ); 00469 00476 $this->setChunkScale( $chunksize, $maxcur, 'old', __METHOD__ ); 00477 $result = $this->dbr->query( 00478 "SELECT cur_id, cur_namespace, cur_title, $cur_text AS text, cur_comment, 00479 cur_user, cur_user_text, cur_timestamp, cur_minor_edit, $cur_flags AS flags 00480 FROM $cur 00481 ORDER BY cur_id", __METHOD__ ); 00482 $add = array(); 00483 foreach ( $result as $row ) { 00484 $add[] = array( 00485 'old_namespace' => $row->cur_namespace, 00486 'old_title' => $row->cur_title, 00487 'old_text' => $row->text, 00488 'old_comment' => $row->cur_comment, 00489 'old_user' => $row->cur_user, 00490 'old_user_text' => $row->cur_user_text, 00491 'old_timestamp' => $row->cur_timestamp, 00492 'old_minor_edit' => $row->cur_minor_edit, 00493 'old_flags' => $row->flags ); 00494 $this->addChunk( $add, $row->cur_id ); 00495 } 00496 $this->lastChunk( $add ); 00497 00502 # $newmaxold = $this->dbw->selectField( 'old', 'max(old_id)', '', __METHOD__ ); 00503 # $this->setChunkScale( $chunksize, $newmaxold, 'revision', __METHOD__ ); 00504 # $countold = $this->dbw->selectField( 'old', 'count(old_id)', '', __METHOD__ ); 00505 $countold = $this->dbw->selectField( 'old', 'max(old_id)', '', __METHOD__ ); 00506 $this->setChunkScale( $chunksize, $countold, 'revision', __METHOD__ ); 00507 00508 $this->log( "......Setting up revision table." ); 00509 $result = $this->dbr->query( 00510 "SELECT old_id, cur_id, old_comment, old_user, old_user_text, 00511 old_timestamp, old_minor_edit 00512 FROM $old,$cur WHERE old_namespace=cur_namespace AND old_title=cur_title", 00513 __METHOD__ ); 00514 00515 $add = array(); 00516 foreach ( $result as $row ) { 00517 $add[] = array( 00518 'rev_id' => $row->old_id, 00519 'rev_page' => $row->cur_id, 00520 'rev_text_id' => $row->old_id, 00521 'rev_comment' => $this->conv( $row->old_comment ), 00522 'rev_user' => $row->old_user, 00523 'rev_user_text' => $this->conv( $row->old_user_text ), 00524 'rev_timestamp' => $row->old_timestamp, 00525 'rev_minor_edit' => $row->old_minor_edit ); 00526 $this->addChunk( $add ); 00527 } 00528 $this->lastChunk( $add ); 00529 00530 00535 $this->log( "......Setting up page table." ); 00536 $this->setChunkScale( $chunksize, $maxcur, 'page', __METHOD__ ); 00537 $result = $this->dbr->query( " 00538 SELECT cur_id, cur_namespace, cur_title, cur_restrictions, cur_counter, cur_is_redirect, cur_is_new, 00539 cur_random, cur_touched, rev_id, LENGTH(cur_text) AS len 00540 FROM $cur,$revision 00541 WHERE cur_id=rev_page AND rev_timestamp=cur_timestamp AND rev_id > {$maxold} 00542 ORDER BY cur_id", __METHOD__ ); 00543 $add = array(); 00544 foreach ( $result as $row ) { 00545 $add[] = array( 00546 'page_id' => $row->cur_id, 00547 'page_namespace' => $row->cur_namespace, 00548 'page_title' => $this->conv( $row->cur_title ), 00549 'page_restrictions' => $row->cur_restrictions, 00550 'page_counter' => $row->cur_counter, 00551 'page_is_redirect' => $row->cur_is_redirect, 00552 'page_is_new' => $row->cur_is_new, 00553 'page_random' => $row->cur_random, 00554 'page_touched' => $this->dbw->timestamp(), 00555 'page_latest' => $row->rev_id, 00556 'page_len' => $row->len ); 00557 # $this->addChunk( $add, $row->cur_id ); 00558 $this->addChunk( $add ); 00559 } 00560 $this->lastChunk( $add ); 00561 00562 $this->log( "...done with cur/old -> page/revision." ); 00563 } 00564 00565 function upgradeLinks() { 00566 $chunksize = 200; 00567 list ( $links, $brokenlinks, $pagelinks, $cur ) = $this->dbw->tableNamesN( 'links', 'brokenlinks', 'pagelinks', 'cur' ); 00568 00569 $this->log( 'Checking for interwiki table change in case of bogus items...' ); 00570 if ( $this->dbw->fieldExists( 'interwiki', 'iw_trans' ) ) { 00571 $this->log( 'interwiki has iw_trans.' ); 00572 } else { 00573 global $IP; 00574 $this->log( 'adding iw_trans...' ); 00575 $this->dbw->sourceFile( $IP . '/maintenance/archives/patch-interwiki-trans.sql' ); 00576 $this->log( 'added iw_trans.' ); 00577 } 00578 00579 $this->log( 'Creating pagelinks table...' ); 00580 $this->dbw->query( " 00581 CREATE TABLE $pagelinks ( 00582 -- Key to the page_id of the page containing the link. 00583 pl_from int(8) unsigned NOT NULL default '0', 00584 00585 -- Key to page_namespace/page_title of the target page. 00586 -- The target page may or may not exist, and due to renames 00587 -- and deletions may refer to different page records as time 00588 -- goes by. 00589 pl_namespace int NOT NULL default '0', 00590 pl_title varchar(255) binary NOT NULL default '', 00591 00592 UNIQUE KEY pl_from(pl_from,pl_namespace,pl_title), 00593 KEY (pl_namespace,pl_title) 00594 00595 ) TYPE=InnoDB" ); 00596 00597 $this->log( 'Importing live links -> pagelinks' ); 00598 $nlinks = $this->dbw->selectField( 'links', 'count(*)', '', __METHOD__ ); 00599 if ( $nlinks ) { 00600 $this->setChunkScale( $chunksize, $nlinks, 'pagelinks', __METHOD__ ); 00601 $result = $this->dbr->query( " 00602 SELECT l_from,cur_namespace,cur_title 00603 FROM $links, $cur 00604 WHERE l_to=cur_id", __METHOD__ ); 00605 $add = array(); 00606 foreach ( $result as $row ) { 00607 $add[] = array( 00608 'pl_from' => $row->l_from, 00609 'pl_namespace' => $row->cur_namespace, 00610 'pl_title' => $this->conv( $row->cur_title ) ); 00611 $this->addChunk( $add ); 00612 } 00613 $this->lastChunk( $add ); 00614 } else { 00615 $this->log( 'no links!' ); 00616 } 00617 00618 $this->log( 'Importing brokenlinks -> pagelinks' ); 00619 $nbrokenlinks = $this->dbw->selectField( 'brokenlinks', 'count(*)', '', __METHOD__ ); 00620 if ( $nbrokenlinks ) { 00621 $this->setChunkScale( $chunksize, $nbrokenlinks, 'pagelinks', __METHOD__ ); 00622 $result = $this->dbr->query( 00623 "SELECT bl_from, bl_to FROM $brokenlinks", 00624 __METHOD__ ); 00625 $add = array(); 00626 foreach ( $result as $row ) { 00627 $pagename = $this->conv( $row->bl_to ); 00628 $title = Title::newFromText( $pagename ); 00629 if ( is_null( $title ) ) { 00630 $this->log( "** invalid brokenlink: $row->bl_from -> '$pagename' (converted from '$row->bl_to')" ); 00631 } else { 00632 $add[] = array( 00633 'pl_from' => $row->bl_from, 00634 'pl_namespace' => $title->getNamespace(), 00635 'pl_title' => $title->getDBkey() ); 00636 $this->addChunk( $add ); 00637 } 00638 } 00639 $this->lastChunk( $add ); 00640 } else { 00641 $this->log( 'no brokenlinks!' ); 00642 } 00643 00644 $this->log( 'Done with links.' ); 00645 } 00646 00647 function userDupeCallback( $str ) { 00648 echo $str; 00649 } 00650 00651 function upgradeUser() { 00652 // Apply unique index, if necessary: 00653 $duper = new UserDupes( $this->dbw, array( $this, 'userDupeCallback' ) ); 00654 if ( $duper->hasUniqueIndex() ) { 00655 $this->log( "Already have unique user_name index." ); 00656 } else { 00657 $this->log( "Clearing user duplicates..." ); 00658 if ( !$duper->clearDupes() ) { 00659 $this->log( "WARNING: Duplicate user accounts, may explode!" ); 00660 } 00661 } 00662 00663 $tabledef = <<<END 00664 CREATE TABLE $1 ( 00665 user_id int(5) unsigned NOT NULL auto_increment, 00666 user_name varchar(255) binary NOT NULL default '', 00667 user_real_name varchar(255) binary NOT NULL default '', 00668 user_password tinyblob NOT NULL default '', 00669 user_newpassword tinyblob NOT NULL default '', 00670 user_email tinytext NOT NULL default '', 00671 user_options blob NOT NULL default '', 00672 user_touched char(14) binary NOT NULL default '', 00673 user_token char(32) binary NOT NULL default '', 00674 user_email_authenticated CHAR(14) BINARY, 00675 user_email_token CHAR(32) BINARY, 00676 user_email_token_expires CHAR(14) BINARY, 00677 00678 PRIMARY KEY user_id (user_id), 00679 UNIQUE INDEX user_name (user_name), 00680 INDEX (user_email_token) 00681 00682 ) TYPE=InnoDB 00683 END; 00684 $fields = array( 00685 'user_id' => MW_UPGRADE_COPY, 00686 'user_name' => MW_UPGRADE_ENCODE, 00687 'user_real_name' => MW_UPGRADE_ENCODE, 00688 'user_password' => MW_UPGRADE_COPY, 00689 'user_newpassword' => MW_UPGRADE_COPY, 00690 'user_email' => MW_UPGRADE_ENCODE, 00691 'user_options' => MW_UPGRADE_ENCODE, 00692 'user_touched' => MW_UPGRADE_CALLBACK, 00693 'user_token' => MW_UPGRADE_COPY, 00694 'user_email_authenticated' => MW_UPGRADE_CALLBACK, 00695 'user_email_token' => MW_UPGRADE_NULL, 00696 'user_email_token_expires' => MW_UPGRADE_NULL ); 00697 $this->copyTable( 'user', $tabledef, $fields, 00698 array( &$this, 'userCallback' ) ); 00699 } 00700 00701 function userCallback( $row, $copy ) { 00702 $now = $this->dbw->timestamp(); 00703 $copy['user_touched'] = $now; 00704 $copy['user_email_authenticated'] = $this->emailAuth ? $now : null; 00705 return $copy; 00706 } 00707 00708 function upgradeImage() { 00709 $tabledef = <<<END 00710 CREATE TABLE $1 ( 00711 img_name varchar(255) binary NOT NULL default '', 00712 img_size int(8) unsigned NOT NULL default '0', 00713 img_width int(5) NOT NULL default '0', 00714 img_height int(5) NOT NULL default '0', 00715 img_metadata mediumblob NOT NULL, 00716 img_bits int(3) NOT NULL default '0', 00717 img_media_type ENUM("UNKNOWN", "BITMAP", "DRAWING", "AUDIO", "VIDEO", "MULTIMEDIA", "OFFICE", "TEXT", "EXECUTABLE", "ARCHIVE") default NULL, 00718 img_major_mime ENUM("unknown", "application", "audio", "image", "text", "video", "message", "model", "multipart") NOT NULL default "unknown", 00719 img_minor_mime varchar(32) NOT NULL default "unknown", 00720 img_description tinyblob NOT NULL default '', 00721 img_user int(5) unsigned NOT NULL default '0', 00722 img_user_text varchar(255) binary NOT NULL default '', 00723 img_timestamp char(14) binary NOT NULL default '', 00724 00725 PRIMARY KEY img_name (img_name), 00726 INDEX img_size (img_size), 00727 INDEX img_timestamp (img_timestamp) 00728 ) TYPE=InnoDB 00729 END; 00730 $fields = array( 00731 'img_name' => MW_UPGRADE_ENCODE, 00732 'img_size' => MW_UPGRADE_COPY, 00733 'img_width' => MW_UPGRADE_CALLBACK, 00734 'img_height' => MW_UPGRADE_CALLBACK, 00735 'img_metadata' => MW_UPGRADE_CALLBACK, 00736 'img_bits' => MW_UPGRADE_CALLBACK, 00737 'img_media_type' => MW_UPGRADE_CALLBACK, 00738 'img_major_mime' => MW_UPGRADE_CALLBACK, 00739 'img_minor_mime' => MW_UPGRADE_CALLBACK, 00740 'img_description' => MW_UPGRADE_ENCODE, 00741 'img_user' => MW_UPGRADE_COPY, 00742 'img_user_text' => MW_UPGRADE_ENCODE, 00743 'img_timestamp' => MW_UPGRADE_COPY ); 00744 $this->copyTable( 'image', $tabledef, $fields, 00745 array( &$this, 'imageCallback' ) ); 00746 } 00747 00748 function imageCallback( $row, $copy ) { 00749 if ( !$this->hasOption( 'noimage' ) ) { 00750 // Fill in the new image info fields 00751 $info = $this->imageInfo( $row->img_name ); 00752 00753 $copy['img_width' ] = $info['width']; 00754 $copy['img_height' ] = $info['height']; 00755 $copy['img_metadata' ] = ""; // loaded on-demand 00756 $copy['img_bits' ] = $info['bits']; 00757 $copy['img_media_type'] = $info['media']; 00758 $copy['img_major_mime'] = $info['major']; 00759 $copy['img_minor_mime'] = $info['minor']; 00760 } 00761 00762 // If doing UTF8 conversion the file must be renamed 00763 $this->renameFile( $row->img_name, 'wfImageDir' ); 00764 00765 return $copy; 00766 } 00767 00768 function imageInfo( $filename ) { 00769 $info = array( 00770 'width' => 0, 00771 'height' => 0, 00772 'bits' => 0, 00773 'media' => '', 00774 'major' => '', 00775 'minor' => '' ); 00776 00777 $magic = MimeMagic::singleton(); 00778 $mime = $magic->guessMimeType( $filename, true ); 00779 list( $info['major'], $info['minor'] ) = explode( '/', $mime ); 00780 00781 $info['media'] = $magic->getMediaType( $filename, $mime ); 00782 00783 $image = UnregisteredLocalFile::newFromPath( $filename, $mime ); 00784 00785 $info['width'] = $image->getWidth(); 00786 $info['height'] = $image->getHeight(); 00787 00788 $gis = $image->getImageSize( $filename ); 00789 if ( isset( $gis['bits'] ) ) { 00790 $info['bits'] = $gis['bits']; 00791 } 00792 00793 return $info; 00794 } 00795 00796 00801 function clearTable( $table ) { 00802 print "Clearing $table...\n"; 00803 $tableName = $this->db->tableName( $table ); 00804 $this->db->query( "TRUNCATE $tableName" ); 00805 } 00806 00817 function renameFile( $oldname, $subdirCallback = 'wfImageDir', $basename = null ) { 00818 $newname = $this->conv( $oldname ); 00819 if ( $newname == $oldname ) { 00820 // No need to rename; another field triggered this row. 00821 return false; 00822 } 00823 00824 if ( is_null( $basename ) ) $basename = $oldname; 00825 $ubasename = $this->conv( $basename ); 00826 $oldpath = call_user_func( $subdirCallback, $basename ) . '/' . $oldname; 00827 $newpath = call_user_func( $subdirCallback, $ubasename ) . '/' . $newname; 00828 00829 $this->log( "$oldpath -> $newpath" ); 00830 if ( rename( $oldpath, $newpath ) ) { 00831 $relpath = wfRelativePath( $newpath, dirname( $oldpath ) ); 00832 if ( !symlink( $relpath, $oldpath ) ) { 00833 $this->log( "... symlink failed!" ); 00834 } 00835 return $newname; 00836 } else { 00837 $this->log( "... rename failed!" ); 00838 return false; 00839 } 00840 } 00841 00842 function upgradeOldImage() { 00843 $tabledef = <<<END 00844 CREATE TABLE $1 ( 00845 -- Base filename: key to image.img_name 00846 oi_name varchar(255) binary NOT NULL default '', 00847 00848 -- Filename of the archived file. 00849 -- This is generally a timestamp and '!' prepended to the base name. 00850 oi_archive_name varchar(255) binary NOT NULL default '', 00851 00852 -- Other fields as in image... 00853 oi_size int(8) unsigned NOT NULL default 0, 00854 oi_width int(5) NOT NULL default 0, 00855 oi_height int(5) NOT NULL default 0, 00856 oi_bits int(3) NOT NULL default 0, 00857 oi_description tinyblob NOT NULL default '', 00858 oi_user int(5) unsigned NOT NULL default '0', 00859 oi_user_text varchar(255) binary NOT NULL default '', 00860 oi_timestamp char(14) binary NOT NULL default '', 00861 00862 INDEX oi_name (oi_name(10)) 00863 00864 ) TYPE=InnoDB; 00865 END; 00866 $fields = array( 00867 'oi_name' => MW_UPGRADE_ENCODE, 00868 'oi_archive_name' => MW_UPGRADE_ENCODE, 00869 'oi_size' => MW_UPGRADE_COPY, 00870 'oi_width' => MW_UPGRADE_CALLBACK, 00871 'oi_height' => MW_UPGRADE_CALLBACK, 00872 'oi_bits' => MW_UPGRADE_CALLBACK, 00873 'oi_description' => MW_UPGRADE_ENCODE, 00874 'oi_user' => MW_UPGRADE_COPY, 00875 'oi_user_text' => MW_UPGRADE_ENCODE, 00876 'oi_timestamp' => MW_UPGRADE_COPY ); 00877 $this->copyTable( 'oldimage', $tabledef, $fields, 00878 array( &$this, 'oldimageCallback' ) ); 00879 } 00880 00881 function oldimageCallback( $row, $copy ) { 00882 global $options; 00883 if ( !isset( $options['noimage'] ) ) { 00884 // Fill in the new image info fields 00885 $info = $this->imageInfo( $row->oi_archive_name, 'wfImageArchiveDir', $row->oi_name ); 00886 $copy['oi_width' ] = $info['width' ]; 00887 $copy['oi_height'] = $info['height']; 00888 $copy['oi_bits' ] = $info['bits' ]; 00889 } 00890 00891 // If doing UTF8 conversion the file must be renamed 00892 $this->renameFile( $row->oi_archive_name, 'wfImageArchiveDir', $row->oi_name ); 00893 00894 return $copy; 00895 } 00896 00897 00898 function upgradeWatchlist() { 00899 $chunksize = 100; 00900 00901 list ( $watchlist, $watchlist_temp ) = $this->dbw->tableNamesN( 'watchlist', 'watchlist_temp' ); 00902 00903 $this->log( 'Migrating watchlist table to watchlist_temp...' ); 00904 $this->dbw->query( 00905 "CREATE TABLE $watchlist_temp ( 00906 -- Key to user_id 00907 wl_user int(5) unsigned NOT NULL, 00908 00909 -- Key to page_namespace/page_title 00910 -- Note that users may watch patches which do not exist yet, 00911 -- or existed in the past but have been deleted. 00912 wl_namespace int NOT NULL default '0', 00913 wl_title varchar(255) binary NOT NULL default '', 00914 00915 -- Timestamp when user was last sent a notification e-mail; 00916 -- cleared when the user visits the page. 00917 -- FIXME: add proper null support etc 00918 wl_notificationtimestamp varchar(14) binary NOT NULL default '0', 00919 00920 UNIQUE KEY (wl_user, wl_namespace, wl_title), 00921 KEY namespace_title (wl_namespace,wl_title) 00922 00923 ) TYPE=InnoDB;", __METHOD__ ); 00924 00925 // Fix encoding for Latin-1 upgrades, add some fields, 00926 // and double article to article+talk pairs 00927 $numwatched = $this->dbw->selectField( 'watchlist', 'count(*)', '', __METHOD__ ); 00928 00929 $this->setChunkScale( $chunksize, $numwatched * 2, 'watchlist_temp', __METHOD__ ); 00930 $result = $this->dbr->select( 'watchlist', 00931 array( 00932 'wl_user', 00933 'wl_namespace', 00934 'wl_title' ), 00935 '', 00936 __METHOD__ ); 00937 00938 $add = array(); 00939 foreach ( $result as $row ) { 00940 $add[] = array( 00941 'wl_user' => $row->wl_user, 00942 'wl_namespace' => MWNamespace::getSubject( $row->wl_namespace ), 00943 'wl_title' => $this->conv( $row->wl_title ), 00944 'wl_notificationtimestamp' => '0' ); 00945 $this->addChunk( $add ); 00946 00947 $add[] = array( 00948 'wl_user' => $row->wl_user, 00949 'wl_namespace' => MWNamespace::getTalk( $row->wl_namespace ), 00950 'wl_title' => $this->conv( $row->wl_title ), 00951 'wl_notificationtimestamp' => '0' ); 00952 $this->addChunk( $add ); 00953 } 00954 $this->lastChunk( $add ); 00955 00956 $this->log( 'Done converting watchlist.' ); 00957 $this->cleanupSwaps[] = 'watchlist'; 00958 } 00959 00960 function upgradeLogging() { 00961 $tabledef = <<<ENDS 00962 CREATE TABLE $1 ( 00963 -- Symbolic keys for the general log type and the action type 00964 -- within the log. The output format will be controlled by the 00965 -- action field, but only the type controls categorization. 00966 log_type char(10) NOT NULL default '', 00967 log_action char(10) NOT NULL default '', 00968 00969 -- Timestamp. Duh. 00970 log_timestamp char(14) NOT NULL default '19700101000000', 00971 00972 -- The user who performed this action; key to user_id 00973 log_user int unsigned NOT NULL default 0, 00974 00975 -- Key to the page affected. Where a user is the target, 00976 -- this will point to the user page. 00977 log_namespace int NOT NULL default 0, 00978 log_title varchar(255) binary NOT NULL default '', 00979 00980 -- Freeform text. Interpreted as edit history comments. 00981 log_comment varchar(255) NOT NULL default '', 00982 00983 -- LF separated list of miscellaneous parameters 00984 log_params blob NOT NULL default '', 00985 00986 KEY type_time (log_type, log_timestamp), 00987 KEY user_time (log_user, log_timestamp), 00988 KEY page_time (log_namespace, log_title, log_timestamp) 00989 00990 ) TYPE=InnoDB 00991 ENDS; 00992 $fields = array( 00993 'log_type' => MW_UPGRADE_COPY, 00994 'log_action' => MW_UPGRADE_COPY, 00995 'log_timestamp' => MW_UPGRADE_COPY, 00996 'log_user' => MW_UPGRADE_COPY, 00997 'log_namespace' => MW_UPGRADE_COPY, 00998 'log_title' => MW_UPGRADE_ENCODE, 00999 'log_comment' => MW_UPGRADE_ENCODE, 01000 'log_params' => MW_UPGRADE_ENCODE ); 01001 $this->copyTable( 'logging', $tabledef, $fields ); 01002 } 01003 01004 function upgradeArchive() { 01005 $tabledef = <<<ENDS 01006 CREATE TABLE $1 ( 01007 ar_namespace int NOT NULL default '0', 01008 ar_title varchar(255) binary NOT NULL default '', 01009 ar_text mediumblob NOT NULL default '', 01010 01011 ar_comment tinyblob NOT NULL default '', 01012 ar_user int(5) unsigned NOT NULL default '0', 01013 ar_user_text varchar(255) binary NOT NULL, 01014 ar_timestamp char(14) binary NOT NULL default '', 01015 ar_minor_edit tinyint(1) NOT NULL default '0', 01016 01017 ar_flags tinyblob NOT NULL default '', 01018 01019 ar_rev_id int(8) unsigned, 01020 ar_text_id int(8) unsigned, 01021 01022 KEY name_title_timestamp (ar_namespace,ar_title,ar_timestamp) 01023 01024 ) TYPE=InnoDB 01025 ENDS; 01026 $fields = array( 01027 'ar_namespace' => MW_UPGRADE_COPY, 01028 'ar_title' => MW_UPGRADE_ENCODE, 01029 'ar_text' => MW_UPGRADE_COPY, 01030 'ar_comment' => MW_UPGRADE_ENCODE, 01031 'ar_user' => MW_UPGRADE_COPY, 01032 'ar_user_text' => MW_UPGRADE_ENCODE, 01033 'ar_timestamp' => MW_UPGRADE_COPY, 01034 'ar_minor_edit' => MW_UPGRADE_COPY, 01035 'ar_flags' => MW_UPGRADE_COPY, 01036 'ar_rev_id' => MW_UPGRADE_NULL, 01037 'ar_text_id' => MW_UPGRADE_NULL ); 01038 $this->copyTable( 'archive', $tabledef, $fields ); 01039 } 01040 01041 function upgradeImagelinks() { 01042 global $wgUseLatin1; 01043 if ( $wgUseLatin1 ) { 01044 $tabledef = <<<ENDS 01045 CREATE TABLE $1 ( 01046 -- Key to page_id of the page containing the image / media link. 01047 il_from int(8) unsigned NOT NULL default '0', 01048 01049 -- Filename of target image. 01050 -- This is also the page_title of the file's description page; 01051 -- all such pages are in namespace 6 (NS_FILE). 01052 il_to varchar(255) binary NOT NULL default '', 01053 01054 UNIQUE KEY il_from(il_from,il_to), 01055 KEY (il_to) 01056 01057 ) TYPE=InnoDB 01058 ENDS; 01059 $fields = array( 01060 'il_from' => MW_UPGRADE_COPY, 01061 'il_to' => MW_UPGRADE_ENCODE ); 01062 $this->copyTable( 'imagelinks', $tabledef, $fields ); 01063 } 01064 } 01065 01066 function upgradeCategorylinks() { 01067 global $wgUseLatin1; 01068 if ( $wgUseLatin1 ) { 01069 $tabledef = <<<ENDS 01070 CREATE TABLE $1 ( 01071 cl_from int(8) unsigned NOT NULL default '0', 01072 cl_to varchar(255) binary NOT NULL default '', 01073 cl_sortkey varchar(86) binary NOT NULL default '', 01074 cl_timestamp timestamp NOT NULL, 01075 01076 UNIQUE KEY cl_from(cl_from,cl_to), 01077 KEY cl_sortkey(cl_to,cl_sortkey), 01078 KEY cl_timestamp(cl_to,cl_timestamp) 01079 ) TYPE=InnoDB 01080 ENDS; 01081 $fields = array( 01082 'cl_from' => MW_UPGRADE_COPY, 01083 'cl_to' => MW_UPGRADE_ENCODE, 01084 'cl_sortkey' => MW_UPGRADE_ENCODE, 01085 'cl_timestamp' => MW_UPGRADE_COPY ); 01086 $this->copyTable( 'categorylinks', $tabledef, $fields ); 01087 } 01088 } 01089 01090 function upgradeIpblocks() { 01091 global $wgUseLatin1; 01092 if ( $wgUseLatin1 ) { 01093 $tabledef = <<<ENDS 01094 CREATE TABLE $1 ( 01095 ipb_id int(8) NOT NULL auto_increment, 01096 ipb_address varchar(40) binary NOT NULL default '', 01097 ipb_user int(8) unsigned NOT NULL default '0', 01098 ipb_by int(8) unsigned NOT NULL default '0', 01099 ipb_reason tinyblob NOT NULL default '', 01100 ipb_timestamp char(14) binary NOT NULL default '', 01101 ipb_auto tinyint(1) NOT NULL default '0', 01102 ipb_expiry char(14) binary NOT NULL default '', 01103 01104 PRIMARY KEY ipb_id (ipb_id), 01105 INDEX ipb_address (ipb_address), 01106 INDEX ipb_user (ipb_user) 01107 01108 ) TYPE=InnoDB 01109 ENDS; 01110 $fields = array( 01111 'ipb_id' => MW_UPGRADE_COPY, 01112 'ipb_address' => MW_UPGRADE_COPY, 01113 'ipb_user' => MW_UPGRADE_COPY, 01114 'ipb_by' => MW_UPGRADE_COPY, 01115 'ipb_reason' => MW_UPGRADE_ENCODE, 01116 'ipb_timestamp' => MW_UPGRADE_COPY, 01117 'ipb_auto' => MW_UPGRADE_COPY, 01118 'ipb_expiry' => MW_UPGRADE_COPY ); 01119 $this->copyTable( 'ipblocks', $tabledef, $fields ); 01120 } 01121 } 01122 01123 function upgradeRecentchanges() { 01124 // There's a format change in the namespace field 01125 $tabledef = <<<ENDS 01126 CREATE TABLE $1 ( 01127 rc_id int(8) NOT NULL auto_increment, 01128 rc_timestamp varchar(14) binary NOT NULL default '', 01129 rc_cur_time varchar(14) binary NOT NULL default '', 01130 01131 rc_user int(10) unsigned NOT NULL default '0', 01132 rc_user_text varchar(255) binary NOT NULL default '', 01133 01134 rc_namespace int NOT NULL default '0', 01135 rc_title varchar(255) binary NOT NULL default '', 01136 01137 rc_comment varchar(255) binary NOT NULL default '', 01138 rc_minor tinyint(3) unsigned NOT NULL default '0', 01139 01140 rc_bot tinyint(3) unsigned NOT NULL default '0', 01141 rc_new tinyint(3) unsigned NOT NULL default '0', 01142 01143 rc_cur_id int(10) unsigned NOT NULL default '0', 01144 rc_this_oldid int(10) unsigned NOT NULL default '0', 01145 rc_last_oldid int(10) unsigned NOT NULL default '0', 01146 01147 rc_type tinyint(3) unsigned NOT NULL default '0', 01148 rc_moved_to_ns tinyint(3) unsigned NOT NULL default '0', 01149 rc_moved_to_title varchar(255) binary NOT NULL default '', 01150 01151 rc_patrolled tinyint(3) unsigned NOT NULL default '0', 01152 01153 rc_ip char(15) NOT NULL default '', 01154 01155 PRIMARY KEY rc_id (rc_id), 01156 INDEX rc_timestamp (rc_timestamp), 01157 INDEX rc_namespace_title (rc_namespace, rc_title), 01158 INDEX rc_cur_id (rc_cur_id), 01159 INDEX new_name_timestamp(rc_new,rc_namespace,rc_timestamp), 01160 INDEX rc_ip (rc_ip) 01161 01162 ) TYPE=InnoDB 01163 ENDS; 01164 $fields = array( 01165 'rc_id' => MW_UPGRADE_COPY, 01166 'rc_timestamp' => MW_UPGRADE_COPY, 01167 'rc_cur_time' => MW_UPGRADE_COPY, 01168 'rc_user' => MW_UPGRADE_COPY, 01169 'rc_user_text' => MW_UPGRADE_ENCODE, 01170 'rc_namespace' => MW_UPGRADE_COPY, 01171 'rc_title' => MW_UPGRADE_ENCODE, 01172 'rc_comment' => MW_UPGRADE_ENCODE, 01173 'rc_minor' => MW_UPGRADE_COPY, 01174 'rc_bot' => MW_UPGRADE_COPY, 01175 'rc_new' => MW_UPGRADE_COPY, 01176 'rc_cur_id' => MW_UPGRADE_COPY, 01177 'rc_this_oldid' => MW_UPGRADE_COPY, 01178 'rc_last_oldid' => MW_UPGRADE_COPY, 01179 'rc_type' => MW_UPGRADE_COPY, 01180 'rc_moved_to_ns' => MW_UPGRADE_COPY, 01181 'rc_moved_to_title' => MW_UPGRADE_ENCODE, 01182 'rc_patrolled' => MW_UPGRADE_COPY, 01183 'rc_ip' => MW_UPGRADE_COPY ); 01184 $this->copyTable( 'recentchanges', $tabledef, $fields ); 01185 } 01186 01187 function upgradeQuerycache() { 01188 // There's a format change in the namespace field 01189 $tabledef = <<<ENDS 01190 CREATE TABLE $1 ( 01191 -- A key name, generally the base name of of the special page. 01192 qc_type char(32) NOT NULL, 01193 01194 -- Some sort of stored value. Sizes, counts... 01195 qc_value int(5) unsigned NOT NULL default '0', 01196 01197 -- Target namespace+title 01198 qc_namespace int NOT NULL default '0', 01199 qc_title char(255) binary NOT NULL default '', 01200 01201 KEY (qc_type,qc_value) 01202 01203 ) TYPE=InnoDB 01204 ENDS; 01205 $fields = array( 01206 'qc_type' => MW_UPGRADE_COPY, 01207 'qc_value' => MW_UPGRADE_COPY, 01208 'qc_namespace' => MW_UPGRADE_COPY, 01209 'qc_title' => MW_UPGRADE_ENCODE ); 01210 $this->copyTable( 'querycache', $tabledef, $fields ); 01211 } 01212 01219 function checkDupes() { 01220 $dbw = wfGetDB( DB_MASTER ); 01221 if ( $dbw->indexExists( 'cur', 'name_title' ) && 01222 $dbw->indexUnique( 'cur', 'name_title' ) ) { 01223 echo wfWikiID() . ": cur table has the current unique index; no duplicate entries.\n"; 01224 return; 01225 } elseif ( $dbw->indexExists( 'cur', 'name_title_dup_prevention' ) ) { 01226 echo wfWikiID() . ": cur table has a temporary name_title_dup_prevention unique index; no duplicate entries.\n"; 01227 return; 01228 } 01229 01230 echo wfWikiID() . ": cur table has the old non-unique index and may have duplicate entries.\n"; 01231 01232 $dbw = wfGetDB( DB_MASTER ); 01233 $cur = $dbw->tableName( 'cur' ); 01234 $old = $dbw->tableName( 'old' ); 01235 $dbw->query( "LOCK TABLES $cur WRITE, $old WRITE" ); 01236 echo "Checking for duplicate cur table entries... (this may take a while on a large wiki)\n"; 01237 $res = $dbw->query( <<<END 01238 SELECT cur_namespace,cur_title,count(*) as c,min(cur_id) as id 01239 FROM $cur 01240 GROUP BY cur_namespace,cur_title 01241 HAVING c > 1 01242 END 01243 ); 01244 $n = $dbw->numRows( $res ); 01245 echo "Found $n titles with duplicate entries.\n"; 01246 if ( $n > 0 ) { 01247 echo "Correcting...\n"; 01248 foreach ( $res as $row ) { 01249 $ns = intval( $row->cur_namespace ); 01250 $title = $dbw->addQuotes( $row->cur_title ); 01251 01252 # Get the first responding ID; that'll be the one we keep. 01253 $id = $dbw->selectField( 'cur', 'cur_id', array( 01254 'cur_namespace' => $row->cur_namespace, 01255 'cur_title' => $row->cur_title ) ); 01256 01257 echo "$ns:$row->cur_title (canonical ID $id)\n"; 01258 if ( $id != $row->id ) { 01259 echo " ** minimum ID $row->id; "; 01260 $timeMin = $dbw->selectField( 'cur', 'cur_timestamp', array( 01261 'cur_id' => $row->id ) ); 01262 $timeFirst = $dbw->selectField( 'cur', 'cur_timestamp', array( 01263 'cur_id' => $id ) ); 01264 if ( $timeMin == $timeFirst ) { 01265 echo "timestamps match at $timeFirst; ok\n"; 01266 } else { 01267 echo "timestamps don't match! min: $timeMin, first: $timeFirst; "; 01268 if ( $timeMin > $timeFirst ) { 01269 $id = $row->id; 01270 echo "keeping minimum: $id\n"; 01271 } else { 01272 echo "keeping first: $id\n"; 01273 } 01274 } 01275 } 01276 01277 $dbw->query( <<<END 01278 INSERT 01279 INTO $old 01280 (old_namespace, old_title, old_text, 01281 old_comment, old_user, old_user_text, 01282 old_timestamp, old_minor_edit, old_flags, 01283 inverse_timestamp) 01284 SELECT cur_namespace, cur_title, cur_text, 01285 cur_comment, cur_user, cur_user_text, 01286 cur_timestamp, cur_minor_edit, '', 01287 inverse_timestamp 01288 FROM $cur 01289 WHERE cur_namespace=$ns 01290 AND cur_title=$title 01291 AND cur_id != $id 01292 END 01293 ); 01294 $dbw->query( <<<END 01295 DELETE 01296 FROM $cur 01297 WHERE cur_namespace=$ns 01298 AND cur_title=$title 01299 AND cur_id != $id 01300 END 01301 ); 01302 } 01303 } 01304 $dbw->query( 'UNLOCK TABLES' ); 01305 echo "Done.\n"; 01306 } 01307 01313 function upgradeCleanup() { 01314 $this->renameTable( 'old', 'text' ); 01315 01316 foreach ( $this->cleanupSwaps as $table ) { 01317 $this->swap( $table ); 01318 } 01319 } 01320 01321 function renameTable( $from, $to ) { 01322 $this->log( "Renaming $from to $to..." ); 01323 01324 $fromtable = $this->dbw->tableName( $from ); 01325 $totable = $this->dbw->tableName( $to ); 01326 $this->dbw->query( "ALTER TABLE $fromtable RENAME TO $totable" ); 01327 } 01328 01329 function swap( $base ) { 01330 $this->renameTable( $base, "{$base}_old" ); 01331 $this->renameTable( "{$base}_temp", $base ); 01332 } 01333 01334 } 01335 01336 $maintClass = 'FiveUpgrade'; 01337 require_once( RUN_MAINTENANCE_IF_MAIN );