MediaWiki  master
upgradeLogging.php
Go to the documentation of this file.
1 <?php
24 require __DIR__ . '/../commandLine.inc';
25 
27 
35 
39  public $dbw;
40  public $batchSize = 1000;
41  public $minTs = false;
42 
43  public function execute() {
44  $this->dbw = wfGetDB( DB_MASTER );
45  $logging = $this->dbw->tableName( 'logging' );
46  $logging_1_10 = $this->dbw->tableName( 'logging_1_10' );
47  $logging_pre_1_10 = $this->dbw->tableName( 'logging_pre_1_10' );
48 
49  if ( $this->dbw->tableExists( 'logging_pre_1_10' ) && !$this->dbw->tableExists( 'logging' ) ) {
50  # Fix previous aborted run
51  echo "Cleaning up from previous aborted run\n";
52  $this->dbw->query( "RENAME TABLE $logging_pre_1_10 TO $logging", __METHOD__ );
53  }
54 
55  if ( $this->dbw->tableExists( 'logging_pre_1_10' ) ) {
56  echo "This script has already been run to completion\n";
57 
58  return;
59  }
60 
61  # Create the target table
62  if ( !$this->dbw->tableExists( 'logging_1_10' ) ) {
63  global $wgDBTableOptions;
64 
65  $sql = <<<EOT
66 CREATE TABLE $logging_1_10 (
67  -- Log ID, for referring to this specific log entry, probably for deletion and such.
68  log_id int unsigned NOT NULL auto_increment,
69 
70  -- Symbolic keys for the general log type and the action type
71  -- within the log. The output format will be controlled by the
72  -- action field, but only the type controls categorization.
73  log_type varbinary(10) NOT NULL default '',
74  log_action varbinary(10) NOT NULL default '',
75 
76  -- Timestamp. Duh.
77  log_timestamp binary(14) NOT NULL default '19700101000000',
78 
79  -- The user who performed this action; key to user_id
80  log_user int unsigned NOT NULL default 0,
81 
82  -- Key to the page affected. Where a user is the target,
83  -- this will point to the user page.
84  log_namespace int NOT NULL default 0,
85  log_title varchar(255) binary NOT NULL default '',
86 
87  -- Freeform text. Interpreted as edit history comments.
88  log_comment varchar(255) NOT NULL default '',
89 
90  -- LF separated list of miscellaneous parameters
91  log_params blob NOT NULL,
92 
93  -- rev_deleted for logs
94  log_deleted tinyint unsigned NOT NULL default '0',
95 
96  PRIMARY KEY log_id (log_id),
97  KEY type_time (log_type, log_timestamp),
98  KEY user_time (log_user, log_timestamp),
99  KEY page_time (log_namespace, log_title, log_timestamp),
100  KEY times (log_timestamp)
101 
103 EOT;
104  echo "Creating table logging_1_10\n";
105  $this->dbw->query( $sql, __METHOD__ );
106  }
107 
108  # Synchronise the tables
109  echo "Doing initial sync...\n";
110  $this->sync( 'logging', 'logging_1_10' );
111  echo "Sync done\n\n";
112 
113  # Rename the old table away
114  echo "Renaming the old table to $logging_pre_1_10\n";
115  $this->dbw->query( "RENAME TABLE $logging TO $logging_pre_1_10", __METHOD__ );
116 
117  # Copy remaining old rows
118  # Done before the new table is active so that $copyPos is accurate
119  echo "Doing final sync...\n";
120  $this->sync( 'logging_pre_1_10', 'logging_1_10' );
121 
122  # Move the new table in
123  echo "Moving the new table in...\n";
124  $this->dbw->query( "RENAME TABLE $logging_1_10 TO $logging", __METHOD__ );
125  echo "Finished.\n";
126  }
127 
133  private function sync( $srcTable, $dstTable ) {
134  $batchSize = 1000;
135  $minTs = $this->dbw->selectField( $srcTable, 'MIN(log_timestamp)', '', __METHOD__ );
136  $minTsUnix = wfTimestamp( TS_UNIX, $minTs );
137  $numRowsCopied = 0;
138 
139  while ( true ) {
140  $maxTs = $this->dbw->selectField( $srcTable, 'MAX(log_timestamp)', '', __METHOD__ );
141  $copyPos = $this->dbw->selectField( $dstTable, 'MAX(log_timestamp)', '', __METHOD__ );
142  $maxTsUnix = wfTimestamp( TS_UNIX, $maxTs );
143  $copyPosUnix = wfTimestamp( TS_UNIX, $copyPos );
144 
145  if ( $copyPos === null ) {
146  $percent = 0;
147  } else {
148  $percent = ( $copyPosUnix - $minTsUnix ) / ( $maxTsUnix - $minTsUnix ) * 100;
149  }
150  printf( "%s %.2f%%\n", $copyPos, $percent );
151 
152  # Handle all entries with timestamp equal to $copyPos
153  if ( $copyPos !== null ) {
154  $numRowsCopied += $this->copyExactMatch( $srcTable, $dstTable, $copyPos );
155  }
156 
157  # Now copy a batch of rows
158  if ( $copyPos === null ) {
159  $conds = false;
160  } else {
161  $conds = [ 'log_timestamp > ' . $this->dbw->addQuotes( $copyPos ) ];
162  }
163  $srcRes = $this->dbw->select( $srcTable, '*', $conds, __METHOD__,
164  [ 'LIMIT' => $batchSize, 'ORDER BY' => 'log_timestamp' ] );
165 
166  if ( !$srcRes->numRows() ) {
167  # All done
168  break;
169  }
170 
171  $batch = [];
172  foreach ( $srcRes as $srcRow ) {
173  $batch[] = (array)$srcRow;
174  }
175  $this->dbw->insert( $dstTable, $batch, __METHOD__ );
176  $numRowsCopied += count( $batch );
177 
178  wfWaitForSlaves();
179  }
180  echo "Copied $numRowsCopied rows\n";
181  }
182 
183  private function copyExactMatch( $srcTable, $dstTable, $copyPos ) {
184  $numRowsCopied = 0;
185  $srcRes = $this->dbw->select( $srcTable, '*', [ 'log_timestamp' => $copyPos ], __METHOD__ );
186  $dstRes = $this->dbw->select( $dstTable, '*', [ 'log_timestamp' => $copyPos ], __METHOD__ );
187 
188  if ( $srcRes->numRows() ) {
189  $srcRow = $srcRes->fetchObject();
190  $srcFields = array_keys( (array)$srcRow );
191  $srcRes->seek( 0 );
192  $dstRowsSeen = [];
193 
194  # Make a hashtable of rows that already exist in the destination
195  foreach ( $dstRes as $dstRow ) {
196  $reducedDstRow = [];
197  foreach ( $srcFields as $field ) {
198  $reducedDstRow[$field] = $dstRow->$field;
199  }
200  $hash = md5( serialize( $reducedDstRow ) );
201  $dstRowsSeen[$hash] = true;
202  }
203 
204  # Copy all the source rows that aren't already in the destination
205  foreach ( $srcRes as $srcRow ) {
206  $hash = md5( serialize( (array)$srcRow ) );
207  if ( !isset( $dstRowsSeen[$hash] ) ) {
208  $this->dbw->insert( $dstTable, (array)$srcRow, __METHOD__ );
209  $numRowsCopied++;
210  }
211  }
212  }
213 
214  return $numRowsCopied;
215  }
216 }
217 
218 $ul = new UpdateLogging;
219 $ul->execute();
Wikimedia\Rdbms\Database
Relational database abstraction object.
Definition: Database.php:49
wfTimestamp
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
Definition: GlobalFunctions.php:1871
UpdateLogging\$batchSize
$batchSize
Definition: upgradeLogging.php:40
serialize
serialize()
Definition: ApiMessageTrait.php:138
wfWaitForSlaves
wfWaitForSlaves( $ifWritesSince=null, $wiki=false, $cluster=false, $timeout=null)
Waits for the replica DBs to catch up to the master position.
Definition: GlobalFunctions.php:2722
UpdateLogging\$minTs
$minTs
Definition: upgradeLogging.php:41
UpdateLogging\execute
execute()
Definition: upgradeLogging.php:43
wfGetDB
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:2579
DB_MASTER
const DB_MASTER
Definition: defines.php:26
$ul
$ul
Definition: upgradeLogging.php:180
$wgDBTableOptions
$wgDBTableOptions
MySQL table options to use during installation or update.
Definition: DefaultSettings.php:1990
UpdateLogging\sync
sync( $srcTable, $dstTable)
Copy all rows from $srcTable to $dstTable.
Definition: upgradeLogging.php:133
UpdateLogging\copyExactMatch
copyExactMatch( $srcTable, $dstTable, $copyPos)
Definition: upgradeLogging.php:183
UpdateLogging\$dbw
Database $dbw
Definition: upgradeLogging.php:39
UpdateLogging
Maintenance script that upgrade for log_id/log_deleted fields in a replication-safe way.
Definition: upgradeLogging.php:34