[ Index ]

PHP Cross Reference of MediaWiki-1.24.0

title

Body

[close]

/maintenance/archives/ -> upgradeLogging.php (source)

   1  <?php
   2  /**
   3   * Replication-safe online upgrade for log_id/log_deleted fields.
   4   *
   5   * This program is free software; you can redistribute it and/or modify
   6   * it under the terms of the GNU General Public License as published by
   7   * the Free Software Foundation; either version 2 of the License, or
   8   * (at your option) any later version.
   9   *
  10   * This program is distributed in the hope that it will be useful,
  11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13   * GNU General Public License for more details.
  14   *
  15   * You should have received a copy of the GNU General Public License along
  16   * with this program; if not, write to the Free Software Foundation, Inc.,
  17   * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  18   * http://www.gnu.org/copyleft/gpl.html
  19   *
  20   * @file
  21   * @ingroup MaintenanceArchive
  22   */
  23  
  24  require  __DIR__ . '/../commandLine.inc';
  25  
  26  /**
  27   * Maintenance script that upgrade for log_id/log_deleted fields in a
  28   * replication-safe way.
  29   *
  30   * @ingroup Maintenance
  31   */
  32  class UpdateLogging {
  33  
  34      /**
  35       * @var DatabaseBase
  36       */
  37      public $dbw;
  38      public $batchSize = 1000;
  39      public $minTs = false;
  40  
  41  	function execute() {
  42          $this->dbw = wfGetDB( DB_MASTER );
  43          $logging = $this->dbw->tableName( 'logging' );
  44          $logging_1_10 = $this->dbw->tableName( 'logging_1_10' );
  45          $logging_pre_1_10 = $this->dbw->tableName( 'logging_pre_1_10' );
  46  
  47          if ( $this->dbw->tableExists( 'logging_pre_1_10' ) && !$this->dbw->tableExists( 'logging' ) ) {
  48              # Fix previous aborted run
  49              echo "Cleaning up from previous aborted run\n";
  50              $this->dbw->query( "RENAME TABLE $logging_pre_1_10 TO $logging", __METHOD__ );
  51          }
  52  
  53          if ( $this->dbw->tableExists( 'logging_pre_1_10' ) ) {
  54              echo "This script has already been run to completion\n";
  55  
  56              return;
  57          }
  58  
  59          # Create the target table
  60          if ( !$this->dbw->tableExists( 'logging_1_10' ) ) {
  61              global $wgDBTableOptions;
  62  
  63              $sql = <<<EOT
  64  CREATE TABLE $logging_1_10 (
  65    -- Log ID, for referring to this specific log entry, probably for deletion and such.
  66    log_id int unsigned NOT NULL auto_increment,
  67  
  68    -- Symbolic keys for the general log type and the action type
  69    -- within the log. The output format will be controlled by the
  70    -- action field, but only the type controls categorization.
  71    log_type varbinary(10) NOT NULL default '',
  72    log_action varbinary(10) NOT NULL default '',
  73  
  74    -- Timestamp. Duh.
  75    log_timestamp binary(14) NOT NULL default '19700101000000',
  76  
  77    -- The user who performed this action; key to user_id
  78    log_user int unsigned NOT NULL default 0,
  79  
  80    -- Key to the page affected. Where a user is the target,
  81    -- this will point to the user page.
  82    log_namespace int NOT NULL default 0,
  83    log_title varchar(255) binary NOT NULL default '',
  84  
  85    -- Freeform text. Interpreted as edit history comments.
  86    log_comment varchar(255) NOT NULL default '',
  87  
  88    -- LF separated list of miscellaneous parameters
  89    log_params blob NOT NULL,
  90  
  91    -- rev_deleted for logs
  92    log_deleted tinyint unsigned NOT NULL default '0',
  93  
  94    PRIMARY KEY log_id (log_id),
  95    KEY type_time (log_type, log_timestamp),
  96    KEY user_time (log_user, log_timestamp),
  97    KEY page_time (log_namespace, log_title, log_timestamp),
  98    KEY times (log_timestamp)
  99  
 100  ) $wgDBTableOptions
 101  EOT;
 102              echo "Creating table logging_1_10\n";
 103              $this->dbw->query( $sql, __METHOD__ );
 104          }
 105  
 106          # Synchronise the tables
 107          echo "Doing initial sync...\n";
 108          $this->sync( 'logging', 'logging_1_10' );
 109          echo "Sync done\n\n";
 110  
 111          # Rename the old table away
 112          echo "Renaming the old table to $logging_pre_1_10\n";
 113          $this->dbw->query( "RENAME TABLE $logging TO $logging_pre_1_10", __METHOD__ );
 114  
 115          # Copy remaining old rows
 116          # Done before the new table is active so that $copyPos is accurate
 117          echo "Doing final sync...\n";
 118          $this->sync( 'logging_pre_1_10', 'logging_1_10' );
 119  
 120          # Move the new table in
 121          echo "Moving the new table in...\n";
 122          $this->dbw->query( "RENAME TABLE $logging_1_10 TO $logging", __METHOD__ );
 123          echo "Finished.\n";
 124      }
 125  
 126      /**
 127       * Copy all rows from $srcTable to $dstTable
 128       * @param string $srcTable
 129       * @param string $dstTable
 130       */
 131  	function sync( $srcTable, $dstTable ) {
 132          $batchSize = 1000;
 133          $minTs = $this->dbw->selectField( $srcTable, 'MIN(log_timestamp)', false, __METHOD__ );
 134          $minTsUnix = wfTimestamp( TS_UNIX, $minTs );
 135          $numRowsCopied = 0;
 136  
 137          while ( true ) {
 138              $maxTs = $this->dbw->selectField( $srcTable, 'MAX(log_timestamp)', false, __METHOD__ );
 139              $copyPos = $this->dbw->selectField( $dstTable, 'MAX(log_timestamp)', false, __METHOD__ );
 140              $maxTsUnix = wfTimestamp( TS_UNIX, $maxTs );
 141              $copyPosUnix = wfTimestamp( TS_UNIX, $copyPos );
 142  
 143              if ( $copyPos === null ) {
 144                  $percent = 0;
 145              } else {
 146                  $percent = ( $copyPosUnix - $minTsUnix ) / ( $maxTsUnix - $minTsUnix ) * 100;
 147              }
 148              printf( "%s  %.2f%%\n", $copyPos, $percent );
 149  
 150              # Handle all entries with timestamp equal to $copyPos
 151              if ( $copyPos !== null ) {
 152                  $numRowsCopied += $this->copyExactMatch( $srcTable, $dstTable, $copyPos );
 153              }
 154  
 155              # Now copy a batch of rows
 156              if ( $copyPos === null ) {
 157                  $conds = false;
 158              } else {
 159                  $conds = array( 'log_timestamp > ' . $this->dbw->addQuotes( $copyPos ) );
 160              }
 161              $srcRes = $this->dbw->select( $srcTable, '*', $conds, __METHOD__,
 162                  array( 'LIMIT' => $batchSize, 'ORDER BY' => 'log_timestamp' ) );
 163  
 164              if ( !$srcRes->numRows() ) {
 165                  # All done
 166                  break;
 167              }
 168  
 169              $batch = array();
 170              foreach ( $srcRes as $srcRow ) {
 171                  $batch[] = (array)$srcRow;
 172              }
 173              $this->dbw->insert( $dstTable, $batch, __METHOD__ );
 174              $numRowsCopied += count( $batch );
 175  
 176              wfWaitForSlaves();
 177          }
 178          echo "Copied $numRowsCopied rows\n";
 179      }
 180  
 181  	function copyExactMatch( $srcTable, $dstTable, $copyPos ) {
 182          $numRowsCopied = 0;
 183          $srcRes = $this->dbw->select( $srcTable, '*', array( 'log_timestamp' => $copyPos ), __METHOD__ );
 184          $dstRes = $this->dbw->select( $dstTable, '*', array( 'log_timestamp' => $copyPos ), __METHOD__ );
 185  
 186          if ( $srcRes->numRows() ) {
 187              $srcRow = $srcRes->fetchObject();
 188              $srcFields = array_keys( (array)$srcRow );
 189              $srcRes->seek( 0 );
 190              $dstRowsSeen = array();
 191  
 192              # Make a hashtable of rows that already exist in the destination
 193              foreach ( $dstRes as $dstRow ) {
 194                  $reducedDstRow = array();
 195                  foreach ( $srcFields as $field ) {
 196                      $reducedDstRow[$field] = $dstRow->$field;
 197                  }
 198                  $hash = md5( serialize( $reducedDstRow ) );
 199                  $dstRowsSeen[$hash] = true;
 200              }
 201  
 202              # Copy all the source rows that aren't already in the destination
 203              foreach ( $srcRes as $srcRow ) {
 204                  $hash = md5( serialize( (array)$srcRow ) );
 205                  if ( !isset( $dstRowsSeen[$hash] ) ) {
 206                      $this->dbw->insert( $dstTable, (array)$srcRow, __METHOD__ );
 207                      $numRowsCopied++;
 208                  }
 209              }
 210          }
 211  
 212          return $numRowsCopied;
 213      }
 214  }
 215  
 216  $ul = new UpdateLogging;
 217  $ul->execute();


Generated: Fri Nov 28 14:03:12 2014 Cross-referenced by PHPXref 0.7.1