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