PageRenderTime 50ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/maintenance/archives/upgradeLogging.php

https://bitbucket.org/ghostfreeman/freeside-wiki
PHP | 214 lines | 134 code | 35 blank | 45 comment | 17 complexity | 45318a7332b6d2ac0f21db52aff3b3c2 MD5 | raw file
Possible License(s): GPL-2.0, Apache-2.0, LGPL-3.0
  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. require( __DIR__ . '/../commandLine.inc' );
  24. /**
  25. * Maintenance script that upgrade for log_id/log_deleted fields in a
  26. * replication-safe way.
  27. *
  28. * @ingroup Maintenance
  29. */
  30. class UpdateLogging {
  31. /**
  32. * @var DatabaseBase
  33. */
  34. var $dbw;
  35. var $batchSize = 1000;
  36. var $minTs = false;
  37. function execute() {
  38. $this->dbw = wfGetDB( DB_MASTER );
  39. $logging = $this->dbw->tableName( 'logging' );
  40. $logging_1_10 = $this->dbw->tableName( 'logging_1_10' );
  41. $logging_pre_1_10 = $this->dbw->tableName( 'logging_pre_1_10' );
  42. if ( $this->dbw->tableExists( 'logging_pre_1_10' ) && !$this->dbw->tableExists( 'logging' ) ) {
  43. # Fix previous aborted run
  44. echo "Cleaning up from previous aborted run\n";
  45. $this->dbw->query( "RENAME TABLE $logging_pre_1_10 TO $logging", __METHOD__ );
  46. }
  47. if ( $this->dbw->tableExists( 'logging_pre_1_10' ) ) {
  48. echo "This script has already been run to completion\n";
  49. return;
  50. }
  51. # Create the target table
  52. if ( !$this->dbw->tableExists( 'logging_1_10' ) ) {
  53. global $wgDBTableOptions;
  54. $sql = <<<EOT
  55. CREATE TABLE $logging_1_10 (
  56. -- Log ID, for referring to this specific log entry, probably for deletion and such.
  57. log_id int unsigned NOT NULL auto_increment,
  58. -- Symbolic keys for the general log type and the action type
  59. -- within the log. The output format will be controlled by the
  60. -- action field, but only the type controls categorization.
  61. log_type varbinary(10) NOT NULL default '',
  62. log_action varbinary(10) NOT NULL default '',
  63. -- Timestamp. Duh.
  64. log_timestamp binary(14) NOT NULL default '19700101000000',
  65. -- The user who performed this action; key to user_id
  66. log_user int unsigned NOT NULL default 0,
  67. -- Key to the page affected. Where a user is the target,
  68. -- this will point to the user page.
  69. log_namespace int NOT NULL default 0,
  70. log_title varchar(255) binary NOT NULL default '',
  71. -- Freeform text. Interpreted as edit history comments.
  72. log_comment varchar(255) NOT NULL default '',
  73. -- LF separated list of miscellaneous parameters
  74. log_params blob NOT NULL,
  75. -- rev_deleted for logs
  76. log_deleted tinyint unsigned NOT NULL default '0',
  77. PRIMARY KEY log_id (log_id),
  78. KEY type_time (log_type, log_timestamp),
  79. KEY user_time (log_user, log_timestamp),
  80. KEY page_time (log_namespace, log_title, log_timestamp),
  81. KEY times (log_timestamp)
  82. ) $wgDBTableOptions
  83. EOT;
  84. echo "Creating table logging_1_10\n";
  85. $this->dbw->query( $sql, __METHOD__ );
  86. }
  87. # Synchronise the tables
  88. echo "Doing initial sync...\n";
  89. $this->sync( 'logging', 'logging_1_10' );
  90. echo "Sync done\n\n";
  91. # Rename the old table away
  92. echo "Renaming the old table to $logging_pre_1_10\n";
  93. $this->dbw->query( "RENAME TABLE $logging TO $logging_pre_1_10", __METHOD__ );
  94. # Copy remaining old rows
  95. # Done before the new table is active so that $copyPos is accurate
  96. echo "Doing final sync...\n";
  97. $this->sync( 'logging_pre_1_10', 'logging_1_10' );
  98. # Move the new table in
  99. echo "Moving the new table in...\n";
  100. $this->dbw->query( "RENAME TABLE $logging_1_10 TO $logging", __METHOD__ );
  101. echo "Finished.\n";
  102. }
  103. /**
  104. * Copy all rows from $srcTable to $dstTable
  105. */
  106. function sync( $srcTable, $dstTable ) {
  107. $batchSize = 1000;
  108. $minTs = $this->dbw->selectField( $srcTable, 'MIN(log_timestamp)', false, __METHOD__ );
  109. $minTsUnix = wfTimestamp( TS_UNIX, $minTs );
  110. $numRowsCopied = 0;
  111. while ( true ) {
  112. $maxTs = $this->dbw->selectField( $srcTable, 'MAX(log_timestamp)', false, __METHOD__ );
  113. $copyPos = $this->dbw->selectField( $dstTable, 'MAX(log_timestamp)', false, __METHOD__ );
  114. $maxTsUnix = wfTimestamp( TS_UNIX, $maxTs );
  115. $copyPosUnix = wfTimestamp( TS_UNIX, $copyPos );
  116. if ( $copyPos === null ) {
  117. $percent = 0;
  118. } else {
  119. $percent = ( $copyPosUnix - $minTsUnix ) / ( $maxTsUnix - $minTsUnix ) * 100;
  120. }
  121. printf( "%s %.2f%%\n", $copyPos, $percent );
  122. # Handle all entries with timestamp equal to $copyPos
  123. if ( $copyPos !== null ) {
  124. $numRowsCopied += $this->copyExactMatch( $srcTable, $dstTable, $copyPos );
  125. }
  126. # Now copy a batch of rows
  127. if ( $copyPos === null ) {
  128. $conds = false;
  129. } else {
  130. $conds = array( 'log_timestamp > ' . $this->dbw->addQuotes( $copyPos ) );
  131. }
  132. $srcRes = $this->dbw->select( $srcTable, '*', $conds, __METHOD__,
  133. array( 'LIMIT' => $batchSize, 'ORDER BY' => 'log_timestamp' ) );
  134. if ( ! $srcRes->numRows() ) {
  135. # All done
  136. break;
  137. }
  138. $batch = array();
  139. foreach ( $srcRes as $srcRow ) {
  140. $batch[] = (array)$srcRow;
  141. }
  142. $this->dbw->insert( $dstTable, $batch, __METHOD__ );
  143. $numRowsCopied += count( $batch );
  144. wfWaitForSlaves();
  145. }
  146. echo "Copied $numRowsCopied rows\n";
  147. }
  148. function copyExactMatch( $srcTable, $dstTable, $copyPos ) {
  149. $numRowsCopied = 0;
  150. $srcRes = $this->dbw->select( $srcTable, '*', array( 'log_timestamp' => $copyPos ), __METHOD__ );
  151. $dstRes = $this->dbw->select( $dstTable, '*', array( 'log_timestamp' => $copyPos ), __METHOD__ );
  152. if ( $srcRes->numRows() ) {
  153. $srcRow = $srcRes->fetchObject();
  154. $srcFields = array_keys( (array)$srcRow );
  155. $srcRes->seek( 0 );
  156. $dstRowsSeen = array();
  157. # Make a hashtable of rows that already exist in the destination
  158. foreach ( $dstRes as $dstRow ) {
  159. $reducedDstRow = array();
  160. foreach ( $srcFields as $field ) {
  161. $reducedDstRow[$field] = $dstRow->$field;
  162. }
  163. $hash = md5( serialize( $reducedDstRow ) );
  164. $dstRowsSeen[$hash] = true;
  165. }
  166. # Copy all the source rows that aren't already in the destination
  167. foreach ( $srcRes as $srcRow ) {
  168. $hash = md5( serialize( (array)$srcRow ) );
  169. if ( !isset( $dstRowsSeen[$hash] ) ) {
  170. $this->dbw->insert( $dstTable, (array)$srcRow, __METHOD__ );
  171. $numRowsCopied++;
  172. }
  173. }
  174. }
  175. return $numRowsCopied;
  176. }
  177. }
  178. $ul = new UpdateLogging;
  179. $ul->execute();