MediaWiki  REL1_24
upgradeLogging.php
Go to the documentation of this file.
00001 <?php
00024 require __DIR__ . '/../commandLine.inc';
00025 
00032 class UpdateLogging {
00033 
00037     public $dbw;
00038     public $batchSize = 1000;
00039     public $minTs = false;
00040 
00041     function execute() {
00042         $this->dbw = wfGetDB( DB_MASTER );
00043         $logging = $this->dbw->tableName( 'logging' );
00044         $logging_1_10 = $this->dbw->tableName( 'logging_1_10' );
00045         $logging_pre_1_10 = $this->dbw->tableName( 'logging_pre_1_10' );
00046 
00047         if ( $this->dbw->tableExists( 'logging_pre_1_10' ) && !$this->dbw->tableExists( 'logging' ) ) {
00048             # Fix previous aborted run
00049             echo "Cleaning up from previous aborted run\n";
00050             $this->dbw->query( "RENAME TABLE $logging_pre_1_10 TO $logging", __METHOD__ );
00051         }
00052 
00053         if ( $this->dbw->tableExists( 'logging_pre_1_10' ) ) {
00054             echo "This script has already been run to completion\n";
00055 
00056             return;
00057         }
00058 
00059         # Create the target table
00060         if ( !$this->dbw->tableExists( 'logging_1_10' ) ) {
00061             global $wgDBTableOptions;
00062 
00063             $sql = <<<EOT
00064 CREATE TABLE $logging_1_10 (
00065   -- Log ID, for referring to this specific log entry, probably for deletion and such.
00066   log_id int unsigned NOT NULL auto_increment,
00067 
00068   -- Symbolic keys for the general log type and the action type
00069   -- within the log. The output format will be controlled by the
00070   -- action field, but only the type controls categorization.
00071   log_type varbinary(10) NOT NULL default '',
00072   log_action varbinary(10) NOT NULL default '',
00073 
00074   -- Timestamp. Duh.
00075   log_timestamp binary(14) NOT NULL default '19700101000000',
00076 
00077   -- The user who performed this action; key to user_id
00078   log_user int unsigned NOT NULL default 0,
00079 
00080   -- Key to the page affected. Where a user is the target,
00081   -- this will point to the user page.
00082   log_namespace int NOT NULL default 0,
00083   log_title varchar(255) binary NOT NULL default '',
00084 
00085   -- Freeform text. Interpreted as edit history comments.
00086   log_comment varchar(255) NOT NULL default '',
00087 
00088   -- LF separated list of miscellaneous parameters
00089   log_params blob NOT NULL,
00090 
00091   -- rev_deleted for logs
00092   log_deleted tinyint unsigned NOT NULL default '0',
00093 
00094   PRIMARY KEY log_id (log_id),
00095   KEY type_time (log_type, log_timestamp),
00096   KEY user_time (log_user, log_timestamp),
00097   KEY page_time (log_namespace, log_title, log_timestamp),
00098   KEY times (log_timestamp)
00099 
00100 ) $wgDBTableOptions
00101 EOT;
00102             echo "Creating table logging_1_10\n";
00103             $this->dbw->query( $sql, __METHOD__ );
00104         }
00105 
00106         # Synchronise the tables
00107         echo "Doing initial sync...\n";
00108         $this->sync( 'logging', 'logging_1_10' );
00109         echo "Sync done\n\n";
00110 
00111         # Rename the old table away
00112         echo "Renaming the old table to $logging_pre_1_10\n";
00113         $this->dbw->query( "RENAME TABLE $logging TO $logging_pre_1_10", __METHOD__ );
00114 
00115         # Copy remaining old rows
00116         # Done before the new table is active so that $copyPos is accurate
00117         echo "Doing final sync...\n";
00118         $this->sync( 'logging_pre_1_10', 'logging_1_10' );
00119 
00120         # Move the new table in
00121         echo "Moving the new table in...\n";
00122         $this->dbw->query( "RENAME TABLE $logging_1_10 TO $logging", __METHOD__ );
00123         echo "Finished.\n";
00124     }
00125 
00131     function sync( $srcTable, $dstTable ) {
00132         $batchSize = 1000;
00133         $minTs = $this->dbw->selectField( $srcTable, 'MIN(log_timestamp)', false, __METHOD__ );
00134         $minTsUnix = wfTimestamp( TS_UNIX, $minTs );
00135         $numRowsCopied = 0;
00136 
00137         while ( true ) {
00138             $maxTs = $this->dbw->selectField( $srcTable, 'MAX(log_timestamp)', false, __METHOD__ );
00139             $copyPos = $this->dbw->selectField( $dstTable, 'MAX(log_timestamp)', false, __METHOD__ );
00140             $maxTsUnix = wfTimestamp( TS_UNIX, $maxTs );
00141             $copyPosUnix = wfTimestamp( TS_UNIX, $copyPos );
00142 
00143             if ( $copyPos === null ) {
00144                 $percent = 0;
00145             } else {
00146                 $percent = ( $copyPosUnix - $minTsUnix ) / ( $maxTsUnix - $minTsUnix ) * 100;
00147             }
00148             printf( "%s  %.2f%%\n", $copyPos, $percent );
00149 
00150             # Handle all entries with timestamp equal to $copyPos
00151             if ( $copyPos !== null ) {
00152                 $numRowsCopied += $this->copyExactMatch( $srcTable, $dstTable, $copyPos );
00153             }
00154 
00155             # Now copy a batch of rows
00156             if ( $copyPos === null ) {
00157                 $conds = false;
00158             } else {
00159                 $conds = array( 'log_timestamp > ' . $this->dbw->addQuotes( $copyPos ) );
00160             }
00161             $srcRes = $this->dbw->select( $srcTable, '*', $conds, __METHOD__,
00162                 array( 'LIMIT' => $batchSize, 'ORDER BY' => 'log_timestamp' ) );
00163 
00164             if ( !$srcRes->numRows() ) {
00165                 # All done
00166                 break;
00167             }
00168 
00169             $batch = array();
00170             foreach ( $srcRes as $srcRow ) {
00171                 $batch[] = (array)$srcRow;
00172             }
00173             $this->dbw->insert( $dstTable, $batch, __METHOD__ );
00174             $numRowsCopied += count( $batch );
00175 
00176             wfWaitForSlaves();
00177         }
00178         echo "Copied $numRowsCopied rows\n";
00179     }
00180 
00181     function copyExactMatch( $srcTable, $dstTable, $copyPos ) {
00182         $numRowsCopied = 0;
00183         $srcRes = $this->dbw->select( $srcTable, '*', array( 'log_timestamp' => $copyPos ), __METHOD__ );
00184         $dstRes = $this->dbw->select( $dstTable, '*', array( 'log_timestamp' => $copyPos ), __METHOD__ );
00185 
00186         if ( $srcRes->numRows() ) {
00187             $srcRow = $srcRes->fetchObject();
00188             $srcFields = array_keys( (array)$srcRow );
00189             $srcRes->seek( 0 );
00190             $dstRowsSeen = array();
00191 
00192             # Make a hashtable of rows that already exist in the destination
00193             foreach ( $dstRes as $dstRow ) {
00194                 $reducedDstRow = array();
00195                 foreach ( $srcFields as $field ) {
00196                     $reducedDstRow[$field] = $dstRow->$field;
00197                 }
00198                 $hash = md5( serialize( $reducedDstRow ) );
00199                 $dstRowsSeen[$hash] = true;
00200             }
00201 
00202             # Copy all the source rows that aren't already in the destination
00203             foreach ( $srcRes as $srcRow ) {
00204                 $hash = md5( serialize( (array)$srcRow ) );
00205                 if ( !isset( $dstRowsSeen[$hash] ) ) {
00206                     $this->dbw->insert( $dstTable, (array)$srcRow, __METHOD__ );
00207                     $numRowsCopied++;
00208                 }
00209             }
00210         }
00211 
00212         return $numRowsCopied;
00213     }
00214 }
00215 
00216 $ul = new UpdateLogging;
00217 $ul->execute();