Index: branches/maintenance-work/maintenance/convertLinks.inc |
— | — | @@ -1,216 +0,0 @@ |
2 | | -<?php |
3 | | -/** |
4 | | - * @file |
5 | | - * @todo document |
6 | | - * @ingroup Maintenance |
7 | | - */ |
8 | | - |
9 | | -/** */ |
10 | | -function convertLinks() { |
11 | | - global $wgDBtype; |
12 | | - if( $wgDBtype == 'postgres' ) { |
13 | | - wfOut( "Links table already ok on Postgres.\n" ); |
14 | | - return; |
15 | | - } |
16 | | - |
17 | | - wfOut( "Converting links table to ID-ID...\n" ); |
18 | | - |
19 | | - global $wgLang, $wgDBserver, $wgDBadminuser, $wgDBadminpassword, $wgDBname; |
20 | | - global $noKeys, $logPerformance, $fh; |
21 | | - |
22 | | - $tuplesAdded = $numBadLinks = $curRowsRead = 0; #counters etc |
23 | | - $totalTuplesInserted = 0; # total tuples INSERTed into links_temp |
24 | | - |
25 | | - $reportCurReadProgress = true; #whether or not to give progress reports while reading IDs from cur table |
26 | | - $curReadReportInterval = 1000; #number of rows between progress reports |
27 | | - |
28 | | - $reportLinksConvProgress = true; #whether or not to give progress reports during conversion |
29 | | - $linksConvInsertInterval = 1000; #number of rows per INSERT |
30 | | - |
31 | | - $initialRowOffset = 0; |
32 | | - #$finalRowOffset = 0; # not used yet; highest row number from links table to process |
33 | | - |
34 | | - # Overwrite the old links table with the new one. If this is set to false, |
35 | | - # the new table will be left at links_temp. |
36 | | - $overwriteLinksTable = true; |
37 | | - |
38 | | - # Don't create keys, and so allow duplicates in the new links table. |
39 | | - # This gives a huge speed improvement for very large links tables which are MyISAM. (What about InnoDB?) |
40 | | - $noKeys = false; |
41 | | - |
42 | | - |
43 | | - $logPerformance = false; # output performance data to a file |
44 | | - $perfLogFilename = "convLinksPerf.txt"; |
45 | | - #-------------------------------------------------------------------- |
46 | | - |
47 | | - $dbw = wfGetDB( DB_MASTER ); |
48 | | - list ($cur, $links, $links_temp, $links_backup) = $dbw->tableNamesN( 'cur', 'links', 'links_temp', 'links_backup' ); |
49 | | - |
50 | | - $res = $dbw->query( "SELECT l_from FROM $links LIMIT 1" ); |
51 | | - if ( $dbw->fieldType( $res, 0 ) == "int" ) { |
52 | | - wfOut( "Schema already converted\n" ); |
53 | | - return; |
54 | | - } |
55 | | - |
56 | | - $res = $dbw->query( "SELECT COUNT(*) AS count FROM $links" ); |
57 | | - $row = $dbw->fetchObject($res); |
58 | | - $numRows = $row->count; |
59 | | - $dbw->freeResult( $res ); |
60 | | - |
61 | | - if ( $numRows == 0 ) { |
62 | | - wfOut( "Updating schema (no rows to convert)...\n" ); |
63 | | - createTempTable(); |
64 | | - } else { |
65 | | - if ( $logPerformance ) { $fh = fopen ( $perfLogFilename, "w" ); } |
66 | | - $baseTime = $startTime = getMicroTime(); |
67 | | - # Create a title -> cur_id map |
68 | | - wfOut( "Loading IDs from $cur table...\n" ); |
69 | | - performanceLog ( "Reading $numRows rows from cur table...\n" ); |
70 | | - performanceLog ( "rows read vs seconds elapsed:\n" ); |
71 | | - |
72 | | - $dbw->bufferResults( false ); |
73 | | - $res = $dbw->query( "SELECT cur_namespace,cur_title,cur_id FROM $cur" ); |
74 | | - $ids = array(); |
75 | | - |
76 | | - while ( $row = $dbw->fetchObject( $res ) ) { |
77 | | - $title = $row->cur_title; |
78 | | - if ( $row->cur_namespace ) { |
79 | | - $title = $wgLang->getNsText( $row->cur_namespace ) . ":$title"; |
80 | | - } |
81 | | - $ids[$title] = $row->cur_id; |
82 | | - $curRowsRead++; |
83 | | - if ($reportCurReadProgress) { |
84 | | - if (($curRowsRead % $curReadReportInterval) == 0) { |
85 | | - performanceLog( $curRowsRead . " " . (getMicroTime() - $baseTime) . "\n" ); |
86 | | - wfOut( "\t$curRowsRead rows of $cur table read.\n" ); |
87 | | - } |
88 | | - } |
89 | | - } |
90 | | - $dbw->freeResult( $res ); |
91 | | - $dbw->bufferResults( true ); |
92 | | - wfOut( "Finished loading IDs.\n\n" ); |
93 | | - performanceLog( "Took " . (getMicroTime() - $baseTime) . " seconds to load IDs.\n\n" ); |
94 | | - #-------------------------------------------------------------------- |
95 | | - |
96 | | - # Now, step through the links table (in chunks of $linksConvInsertInterval rows), |
97 | | - # convert, and write to the new table. |
98 | | - createTempTable(); |
99 | | - performanceLog( "Resetting timer.\n\n" ); |
100 | | - $baseTime = getMicroTime(); |
101 | | - wfOut( "Processing $numRows rows from $links table...\n" ); |
102 | | - performanceLog( "Processing $numRows rows from $links table...\n" ); |
103 | | - performanceLog( "rows inserted vs seconds elapsed:\n" ); |
104 | | - |
105 | | - for ($rowOffset = $initialRowOffset; $rowOffset < $numRows; $rowOffset += $linksConvInsertInterval) { |
106 | | - $sqlRead = "SELECT * FROM $links "; |
107 | | - $sqlRead = $dbw->limitResult($sqlRead, $linksConvInsertInterval,$rowOffset); |
108 | | - $res = $dbw->query($sqlRead); |
109 | | - if ( $noKeys ) { |
110 | | - $sqlWrite = array("INSERT INTO $links_temp (l_from,l_to) VALUES "); |
111 | | - } else { |
112 | | - $sqlWrite = array("INSERT IGNORE INTO $links_temp (l_from,l_to) VALUES "); |
113 | | - } |
114 | | - |
115 | | - $tuplesAdded = 0; # no tuples added to INSERT yet |
116 | | - while ( $row = $dbw->fetchObject($res) ) { |
117 | | - $fromTitle = $row->l_from; |
118 | | - if ( array_key_exists( $fromTitle, $ids ) ) { # valid title |
119 | | - $from = $ids[$fromTitle]; |
120 | | - $to = $row->l_to; |
121 | | - if ( $tuplesAdded != 0 ) { |
122 | | - $sqlWrite[] = ","; |
123 | | - } |
124 | | - $sqlWrite[] = "($from,$to)"; |
125 | | - $tuplesAdded++; |
126 | | - } else { # invalid title |
127 | | - $numBadLinks++; |
128 | | - } |
129 | | - } |
130 | | - $dbw->freeResult($res); |
131 | | - #wfOut( "rowOffset: $rowOffset\ttuplesAdded: $tuplesAdded\tnumBadLinks: $numBadLinks\n" ); |
132 | | - if ( $tuplesAdded != 0 ) { |
133 | | - if ($reportLinksConvProgress) { |
134 | | - wfOut( "Inserting $tuplesAdded tuples into $links_temp..." ); |
135 | | - } |
136 | | - $dbw->query( implode("",$sqlWrite) ); |
137 | | - $totalTuplesInserted += $tuplesAdded; |
138 | | - if ($reportLinksConvProgress) |
139 | | - wfOut( " done. Total $totalTuplesInserted tuples inserted.\n" ); |
140 | | - performanceLog( $totalTuplesInserted . " " . (getMicroTime() - $baseTime) . "\n" ); |
141 | | - } |
142 | | - } |
143 | | - wfOut( "$totalTuplesInserted valid titles and $numBadLinks invalid titles were processed.\n\n" ); |
144 | | - performanceLog( "$totalTuplesInserted valid titles and $numBadLinks invalid titles were processed.\n" ); |
145 | | - performanceLog( "Total execution time: " . (getMicroTime() - $startTime) . " seconds.\n" ); |
146 | | - if ( $logPerformance ) { fclose ( $fh ); } |
147 | | - } |
148 | | - #-------------------------------------------------------------------- |
149 | | - |
150 | | - if ( $overwriteLinksTable ) { |
151 | | - $dbConn = Database::newFromParams( $wgDBserver, $wgDBadminuser, $wgDBadminpassword, $wgDBname ); |
152 | | - if (!($dbConn->isOpen())) { |
153 | | - wfOut( "Opening connection to database failed.\n" ); |
154 | | - return; |
155 | | - } |
156 | | - # Check for existing links_backup, and delete it if it exists. |
157 | | - wfOut( "Dropping backup links table if it exists..." ); |
158 | | - $dbConn->query( "DROP TABLE IF EXISTS $links_backup", DB_MASTER); |
159 | | - wfOut( " done.\n" ); |
160 | | - |
161 | | - # Swap in the new table, and move old links table to links_backup |
162 | | - wfOut( "Swapping tables '$links' to '$links_backup'; '$links_temp' to '$links'..." ); |
163 | | - $dbConn->query( "RENAME TABLE links TO $links_backup, $links_temp TO $links", DB_MASTER ); |
164 | | - wfOut( " done.\n\n" ); |
165 | | - |
166 | | - $dbConn->close(); |
167 | | - wfOut( "Conversion complete. The old table remains at $links_backup;\n" ); |
168 | | - wfOut( "delete at your leisure.\n" ); |
169 | | - } else { |
170 | | - wfOut( "Conversion complete. The converted table is at $links_temp;\n" ); |
171 | | - wfOut( "the original links table is unchanged.\n" ); |
172 | | - } |
173 | | -} |
174 | | - |
175 | | -#-------------------------------------------------------------------- |
176 | | - |
177 | | -function createTempTable() { |
178 | | - global $wgDBserver, $wgDBadminuser, $wgDBadminpassword, $wgDBname; |
179 | | - global $noKeys; |
180 | | - $dbConn = Database::newFromParams( $wgDBserver, $wgDBadminuser, $wgDBadminpassword, $wgDBname ); |
181 | | - |
182 | | - if (!($dbConn->isOpen())) { |
183 | | - wfOut( "Opening connection to database failed.\n" ); |
184 | | - return; |
185 | | - } |
186 | | - $links_temp = $dbConn->tableName( 'links_temp' ); |
187 | | - |
188 | | - wfOut( "Dropping temporary links table if it exists..." ); |
189 | | - $dbConn->query( "DROP TABLE IF EXISTS $links_temp"); |
190 | | - wfOut( " done.\n" ); |
191 | | - |
192 | | - wfOut( "Creating temporary links table..." ); |
193 | | - if ( $noKeys ) { |
194 | | - $dbConn->query( "CREATE TABLE $links_temp ( " . |
195 | | - "l_from int(8) unsigned NOT NULL default '0', " . |
196 | | - "l_to int(8) unsigned NOT NULL default '0')"); |
197 | | - } else { |
198 | | - $dbConn->query( "CREATE TABLE $links_temp ( " . |
199 | | - "l_from int(8) unsigned NOT NULL default '0', " . |
200 | | - "l_to int(8) unsigned NOT NULL default '0', " . |
201 | | - "UNIQUE KEY l_from(l_from,l_to), " . |
202 | | - "KEY (l_to))"); |
203 | | - } |
204 | | - wfOut( " done.\n\n" ); |
205 | | -} |
206 | | - |
207 | | -function performanceLog( $text ) { |
208 | | - global $logPerformance, $fh; |
209 | | - if ( $logPerformance ) { |
210 | | - fwrite( $fh, $text ); |
211 | | - } |
212 | | -} |
213 | | - |
214 | | -function getMicroTime() { # return time in seconds, with microsecond accuracy |
215 | | - list($usec, $sec) = explode(" ", microtime()); |
216 | | - return ((float)$usec + (float)$sec); |
217 | | -} |
Index: branches/maintenance-work/maintenance/dumpUploads.php |
— | — | @@ -4,34 +4,36 @@ |
5 | 5 | * @ingroup Maintenance |
6 | 6 | */ |
7 | 7 | |
8 | | -require_once 'commandLine.inc'; |
| 8 | +require_once( "Maintenance.php" ); |
9 | 9 | |
10 | | -class UploadDumper { |
11 | | - function __construct( $args ) { |
| 10 | +class UploadDumper extends Maintenance { |
| 11 | + public function __construct() { |
| 12 | + parent::__construct(); |
| 13 | + $this->mDescription = "Generates list of uploaded files which can be fed to tar or similar. |
| 14 | +By default, outputs relative paths against the parent directory of \$wgUploadDirectory."; |
| 15 | + $this->addOption( 'base', 'Set base relative path instead of wiki include root', false, true ); |
| 16 | + $this->addOption( 'local', 'List all local files, used or not. No shared files included' ); |
| 17 | + $this->addOption( 'used', 'Skip local images that are not used' ); |
| 18 | + $this->addOption( 'shared', 'Include images used from shared repository' ); |
| 19 | + } |
| 20 | + |
| 21 | + public function execute() { |
12 | 22 | global $IP, $wgUseSharedUploads; |
13 | 23 | $this->mAction = 'fetchLocal'; |
14 | | - $this->mBasePath = $IP; |
| 24 | + $this->mBasePath = $this->getOption( 'base', $IP ); |
15 | 25 | $this->mShared = false; |
16 | 26 | $this->mSharedSupplement = false; |
17 | | - |
18 | | - if( isset( $args['help'] ) ) { |
19 | | - $this->mAction = 'help'; |
20 | | - } |
21 | | - |
22 | | - if( isset( $args['base'] ) ) { |
23 | | - $this->mBasePath = $args['base']; |
24 | | - } |
25 | | - |
26 | | - if( isset( $args['local'] ) ) { |
| 27 | + |
| 28 | + if( $this->hasOption('local') ) { |
27 | 29 | $this->mAction = 'fetchLocal'; |
28 | 30 | } |
29 | 31 | |
30 | | - if( isset( $args['used'] ) ) { |
| 32 | + if( $this->hasOption('used') ) { |
31 | 33 | $this->mAction = 'fetchUsed'; |
32 | 34 | } |
33 | 35 | |
34 | | - if( isset( $args['shared'] ) ) { |
35 | | - if( isset( $args['used'] ) ) { |
| 36 | + if( $this->hasOption('shared') ) { |
| 37 | + if( $this->hasOption('used') ) { |
36 | 38 | // Include shared-repo files in the used check |
37 | 39 | $this->mShared = true; |
38 | 40 | } else { |
— | — | @@ -39,34 +41,12 @@ |
40 | 42 | $this->mSharedSupplement = true; |
41 | 43 | } |
42 | 44 | } |
43 | | - } |
44 | | - |
45 | | - function run() { |
46 | 45 | $this->{$this->mAction}( $this->mShared ); |
47 | 46 | if( $this->mSharedSupplement ) { |
48 | 47 | $this->fetchUsed( true ); |
49 | 48 | } |
50 | 49 | } |
51 | | - |
52 | | - function help() { |
53 | | - echo <<<END |
54 | | -Generates list of uploaded files which can be fed to tar or similar. |
55 | | -By default, outputs relative paths against the parent directory of |
56 | | -\$wgUploadDirectory. |
57 | 50 | |
58 | | -Usage: |
59 | | -php dumpUploads.php [options] > list-o-files.txt |
60 | | - |
61 | | -Options: |
62 | | - |
63 | | - |
64 | | -END; |
65 | | - } |
66 | | - |
67 | 51 | /** |
68 | 52 | * Fetch a list of all or used images from a particular image source. |
69 | 53 | * @param string $table |
— | — | @@ -89,7 +69,7 @@ |
90 | 70 | } |
91 | 71 | $dbr->freeResult( $result ); |
92 | 72 | } |
93 | | - |
| 73 | + |
94 | 74 | function fetchLocal( $shared ) { |
95 | 75 | $dbr = wfGetDB( DB_SLAVE ); |
96 | 76 | $result = $dbr->select( 'image', |
— | — | @@ -108,17 +88,16 @@ |
109 | 89 | if( $file && $this->filterItem( $file, $shared ) ) { |
110 | 90 | $filename = $file->getFullPath(); |
111 | 91 | $rel = wfRelativePath( $filename, $this->mBasePath ); |
112 | | - echo "$rel\n"; |
| 92 | + $this->output( "$rel\n" ); |
113 | 93 | } else { |
114 | 94 | wfDebug( __METHOD__ . ": base file? $name\n" ); |
115 | 95 | } |
116 | 96 | } |
117 | | - |
| 97 | + |
118 | 98 | function filterItem( $file, $shared ) { |
119 | 99 | return $shared || $file->isLocal(); |
120 | 100 | } |
121 | 101 | } |
122 | 102 | |
123 | | -$dumper = new UploadDumper( $options ); |
124 | | -$dumper->run(); |
125 | | - |
| 103 | +$maintClass = "UploadDumper"; |
| 104 | +require_once( DO_MAINTENANCE ); |
Index: branches/maintenance-work/maintenance/rebuildFileCache.php |
— | — | @@ -98,4 +98,6 @@ |
99 | 99 | unset($wgArticle); |
100 | 100 | } |
101 | 101 | } |
102 | | -require_once( "commandLine.inc" ); |
| 102 | + |
| 103 | +$maintClass = "RebuildFileCache"; |
| 104 | +require_once( DO_MAINTENANCE ); |
Index: branches/maintenance-work/maintenance/fixUserRegistration.php |
— | — | @@ -7,28 +7,35 @@ |
8 | 8 | * @ingroup Maintenance |
9 | 9 | */ |
10 | 10 | |
11 | | -require_once( 'commandLine.inc' ); |
| 11 | +require_once( "Maintenance.php" ); |
12 | 12 | |
13 | | -$fname = 'fixUserRegistration.php'; |
| 13 | +class FixUserRegistration extends Maintenance { |
| 14 | + public function __construct() { |
| 15 | + parent::__construct(); |
| 16 | + $this->mDescription = "Fix the user_registration field"; |
| 17 | + } |
14 | 18 | |
15 | | -$dbr = wfGetDB( DB_SLAVE ); |
16 | | -$dbw = wfGetDB( DB_MASTER ); |
| 19 | + public function execute() { |
| 20 | + $dbr = wfGetDB( DB_SLAVE ); |
| 21 | + $dbw = wfGetDB( DB_MASTER ); |
17 | 22 | |
18 | | -// Get user IDs which need fixing |
19 | | -$res = $dbr->select( 'user', 'user_id', 'user_registration IS NULL', $fname ); |
20 | | - |
21 | | -while ( $row = $dbr->fetchObject( $res ) ) { |
22 | | - $id = $row->user_id; |
23 | | - // Get first edit time |
24 | | - $timestamp = $dbr->selectField( 'revision', 'MIN(rev_timestamp)', array( 'rev_user' => $id ), $fname ); |
25 | | - // Update |
26 | | - if ( !empty( $timestamp ) ) { |
27 | | - $dbw->update( 'user', array( 'user_registration' => $timestamp ), array( 'user_id' => $id ), $fname ); |
28 | | - print "$id $timestamp\n"; |
29 | | - } else { |
30 | | - print "$id NULL\n"; |
| 23 | + // Get user IDs which need fixing |
| 24 | + $res = $dbr->select( 'user', 'user_id', 'user_registration IS NULL', __METHOD__ ); |
| 25 | + while ( $row = $dbr->fetchObject( $res ) ) { |
| 26 | + $id = $row->user_id; |
| 27 | + // Get first edit time |
| 28 | + $timestamp = $dbr->selectField( 'revision', 'MIN(rev_timestamp)', array( 'rev_user' => $id ), __METHOD__ ); |
| 29 | + // Update |
| 30 | + if ( !empty( $timestamp ) ) { |
| 31 | + $dbw->update( 'user', array( 'user_registration' => $timestamp ), array( 'user_id' => $id ), __METHOD__ ); |
| 32 | + $this->output( "$id $timestamp\n" ); |
| 33 | + } else { |
| 34 | + $this->output( "$id NULL\n" ); |
| 35 | + } |
| 36 | + } |
| 37 | + $this->output( "\n" ); |
31 | 38 | } |
32 | 39 | } |
33 | | -print "\n"; |
34 | 40 | |
35 | | - |
| 41 | +$maintClass = "FixUserRegistration"; |
| 42 | +require_once( DO_MAINTENANCE ); |
Index: branches/maintenance-work/maintenance/edit.php |
— | — | @@ -4,74 +4,70 @@ |
5 | 5 | * @ingroup Maintenance |
6 | 6 | */ |
7 | 7 | |
8 | | -$optionsWithArgs = array( 'u', 's' ); |
| 8 | +require_once( "Maintenance.php" ); |
9 | 9 | |
10 | | -require_once( 'commandLine.inc' ); |
| 10 | +class EditCLI extends Maintenance { |
| 11 | + public function __construct() { |
| 12 | + parent::__construct(); |
| 13 | + $this->mDescription = "Edit an article from the command line, text is from stdin"; |
| 14 | + $this->addOption( 'u', 'Username', false, true ); |
| 15 | + $this->addOption( 's', 'Edit summary', false, true ); |
| 16 | + $this->addOption( 'm', 'Minor edit' ); |
| 17 | + $this->addOption( 'b', 'Bot edit' ); |
| 18 | + $this->addOption( 'a', 'Enable autosummary' ); |
| 19 | + $this->addOption( 'no-rc', 'Do not show the change in recent changes' ); |
| 20 | + $this->addArgs( array( 'title' ) ); |
| 21 | + } |
11 | 22 | |
12 | | -if ( count( $args ) == 0 || isset( $options['help'] ) ) { |
13 | | - print <<<EOT |
14 | | -Edit an article from the command line |
| 23 | + public function execute() { |
| 24 | + global $wgUser, $wgTitle, $wgArticle; |
15 | 25 | |
16 | | -Usage: php edit.php [options...] <title> |
17 | | - |
18 | | -Options: |
19 | | - -u <user> Username |
20 | | - -s <summary> Edit summary |
21 | | - -m Minor edit |
22 | | - -b Bot (hidden) edit |
23 | | - -a Enable autosummary |
24 | | - --no-rc Do not show the change in recent changes |
25 | | - |
26 | | -If the specified user does not exist, it will be created. |
27 | | -The text for the edit will be read from stdin. |
28 | | - |
29 | | -EOT; |
30 | | - exit( 1 ); |
| 26 | + $userName = $this->getOption( 'u', 'Maintenance script' ); |
| 27 | + $summary = $this->getOption( 's', '' ); |
| 28 | + $minor = $this->hasOption( 'm' ); |
| 29 | + $bot = $this->hasOption( 'b' ); |
| 30 | + $autoSummary = $this->hasOption( 'a' ); |
| 31 | + $noRC = $this->hasOption( 'no-rc' ); |
| 32 | + |
| 33 | + $wgUser = User::newFromName( $userName ); |
| 34 | + if ( !$wgUser ) { |
| 35 | + $this->error( "Invalid username\n", true ); |
| 36 | + } |
| 37 | + if ( $wgUser->isAnon() ) { |
| 38 | + $wgUser->addToDatabase(); |
| 39 | + } |
| 40 | + |
| 41 | + $wgTitle = Title::newFromText( $this->getArg() ); |
| 42 | + if ( !$wgTitle ) { |
| 43 | + $this->error( "Invalid title\n", true ); |
| 44 | + } |
| 45 | + |
| 46 | + $wgArticle = new Article( $wgTitle ); |
| 47 | + |
| 48 | + # Read the text |
| 49 | + $text = $this->getStdin(); |
| 50 | + |
| 51 | + # Do the edit |
| 52 | + $this->output( "Saving... " ); |
| 53 | + $status = $wgArticle->doEdit( $text, $summary, |
| 54 | + ( $minor ? EDIT_MINOR : 0 ) | |
| 55 | + ( $bot ? EDIT_FORCE_BOT : 0 ) | |
| 56 | + ( $autoSummary ? EDIT_AUTOSUMMARY : 0 ) | |
| 57 | + ( $noRC ? EDIT_SUPPRESS_RC : 0 ) ); |
| 58 | + if ( $status->isOK() ) { |
| 59 | + $this->output( "done\n" ); |
| 60 | + $exit = 0; |
| 61 | + } else { |
| 62 | + $this->output( "failed\n" ); |
| 63 | + $exit = 1; |
| 64 | + } |
| 65 | + if ( !$status->isGood() ) { |
| 66 | + $this->output( $status->getWikiText() . "\n" ); |
| 67 | + } |
| 68 | + exit( $exit ); |
| 69 | + } |
31 | 70 | } |
32 | 71 | |
33 | | -$userName = isset( $options['u'] ) ? $options['u'] : 'Maintenance script'; |
34 | | -$summary = isset( $options['s'] ) ? $options['s'] : ''; |
35 | | -$minor = isset( $options['m'] ); |
36 | | -$bot = isset( $options['b'] ); |
37 | | -$autoSummary = isset( $options['a'] ); |
38 | | -$noRC = isset( $options['no-rc'] ); |
| 72 | +$maintClass = "EditCLI"; |
| 73 | +require_once( DO_MAINTENANCE ); |
39 | 74 | |
40 | | -$wgUser = User::newFromName( $userName ); |
41 | | -if ( !$wgUser ) { |
42 | | - print "Invalid username\n"; |
43 | | - exit( 1 ); |
44 | | -} |
45 | | -if ( $wgUser->isAnon() ) { |
46 | | - $wgUser->addToDatabase(); |
47 | | -} |
48 | | - |
49 | | -$wgTitle = Title::newFromText( $args[0] ); |
50 | | -if ( !$wgTitle ) { |
51 | | - print "Invalid title\n"; |
52 | | - exit( 1 ); |
53 | | -} |
54 | | - |
55 | | -$wgArticle = new Article( $wgTitle ); |
56 | | - |
57 | | -# Read the text |
58 | | -$text = file_get_contents( 'php://stdin' ); |
59 | | - |
60 | | -# Do the edit |
61 | | -print "Saving... "; |
62 | | -$status = $wgArticle->doEdit( $text, $summary, |
63 | | - ( $minor ? EDIT_MINOR : 0 ) | |
64 | | - ( $bot ? EDIT_FORCE_BOT : 0 ) | |
65 | | - ( $autoSummary ? EDIT_AUTOSUMMARY : 0 ) | |
66 | | - ( $noRC ? EDIT_SUPPRESS_RC : 0 ) ); |
67 | | -if ( $status->isOK() ) { |
68 | | - print "done\n"; |
69 | | - $exit = 0; |
70 | | -} else { |
71 | | - print "failed\n"; |
72 | | - $exit = 1; |
73 | | -} |
74 | | -if ( !$status->isGood() ) { |
75 | | - print $status->getWikiText() . "\n"; |
76 | | -} |
77 | | -exit( $exit ); |
78 | | - |
Index: branches/maintenance-work/maintenance/Maintenance.php |
— | — | @@ -396,7 +396,7 @@ |
397 | 397 | # Short options |
398 | 398 | for ( $p=1; $p<strlen( $arg ); $p++ ) { |
399 | 399 | $option = $arg{$p}; |
400 | | - if ( isset( $this->mParams[$option]['withArg'] ) ) { |
| 400 | + if ( $this->mParams[$option]['withArg'] ) { |
401 | 401 | $param = next( $argv ); |
402 | 402 | if ( $param === false ) { |
403 | 403 | $this->error( "$arg needs a value after it\n", true ); |
— | — | @@ -444,6 +444,8 @@ |
445 | 445 | $this->mDbPass = $this->getOption( 'dbpass' ); |
446 | 446 | if( $this->hasOption( 'quiet' ) ) |
447 | 447 | $this->mQuiet = true; |
| 448 | + if( $this->hasOption( 'batch-size' ) ) |
| 449 | + $this->mBatchSize = $this->getOption( 'batch-size' ); |
448 | 450 | } |
449 | 451 | |
450 | 452 | /** |
— | — | @@ -454,7 +456,7 @@ |
455 | 457 | if( $this->hasOption('help') || in_array( 'help', $this->mArgs ) || $force ) { |
456 | 458 | $this->mQuiet = false; |
457 | 459 | if( $this->mDescription ) { |
458 | | - $this->output( $this->mDescription . "\n" ); |
| 460 | + $this->output( "\n" . $this->mDescription . "\n" ); |
459 | 461 | } |
460 | 462 | $this->output( "\nUsage: php " . $this->mSelf ); |
461 | 463 | if( $this->mParams ) { |
Index: branches/maintenance-work/maintenance/cleanupSpam.php |
— | — | @@ -1,114 +1,113 @@ |
2 | 2 | <?php |
3 | 3 | /** |
4 | | - * @file |
| 4 | + * Cleanup all spam from a given hostname |
5 | 5 | * @ingroup Maintenance |
6 | 6 | */ |
7 | 7 | |
8 | | -require_once( 'commandLine.inc' ); |
9 | | -require_once( "$IP/includes/LinkFilter.php" ); |
| 8 | +require_once( "Maintenance.php" ); |
10 | 9 | |
11 | | -function cleanupArticle( $id, $domain ) { |
12 | | - $title = Title::newFromID( $id ); |
13 | | - if ( !$title ) { |
14 | | - print "Internal error: no page for ID $id\n"; |
15 | | - return; |
| 10 | +class CleanupSpam extends Maintenance { |
| 11 | + public function __construct() { |
| 12 | + parent::__construct(); |
| 13 | + $this->mDescription = "Cleanup all spam from a given hostname"; |
| 14 | + $this->addOption( 'all', 'Check all wikis in $wgLocalDatabases' ); |
| 15 | + $this->addArgs( array( 'hostname' ) ); |
16 | 16 | } |
17 | 17 | |
18 | | - print $title->getPrefixedDBkey() . " ..."; |
19 | | - $rev = Revision::newFromTitle( $title ); |
20 | | - $revId = $rev->getId(); |
21 | | - $currentRevId = $revId; |
| 18 | + public function execute() { |
| 19 | + global $wgLocalDatabases; |
| 20 | + |
| 21 | + $username = wfMsg( 'spambot_username' ); |
| 22 | + $wgUser = User::newFromName( $username ); |
| 23 | + // Create the user if necessary |
| 24 | + if ( !$wgUser->getId() ) { |
| 25 | + $wgUser->addToDatabase(); |
| 26 | + } |
| 27 | + $spec = $this->getArg(); |
| 28 | + $like = LinkFilter::makeLike( $spec ); |
| 29 | + if ( !$like ) { |
| 30 | + $this->error( "Not a valid hostname specification: $spec\n", true ); |
| 31 | + } |
| 32 | + |
| 33 | + $dbr = wfGetDB( DB_SLAVE ); |
22 | 34 | |
23 | | - while ( $rev && LinkFilter::matchEntry( $rev->getText() , $domain ) ) { |
24 | | - # Revision::getPrevious can't be used in this way before MW 1.6 (Revision.php 1.26) |
25 | | - #$rev = $rev->getPrevious(); |
26 | | - $revId = $title->getPreviousRevisionID( $revId ); |
27 | | - if ( $revId ) { |
28 | | - $rev = Revision::newFromTitle( $title, $revId ); |
| 35 | + if ( $this->hasOption('all') ) { |
| 36 | + // Clean up spam on all wikis |
| 37 | + $dbr = wfGetDB( DB_SLAVE ); |
| 38 | + $this->output( "Finding spam on " . count($wgLocalDatabases) . " wikis\n" ); |
| 39 | + $found = false; |
| 40 | + foreach ( $wgLocalDatabases as $db ) { |
| 41 | + $count = $dbr->selectField( "`$db`.externallinks", 'COUNT(*)', |
| 42 | + array( 'el_index LIKE ' . $dbr->addQuotes( $like ) ), __METHOD__ ); |
| 43 | + if ( $count ) { |
| 44 | + $found = true; |
| 45 | + passthru( "php cleanupSpam.php $db $spec | sed s/^/$db: /" ); |
| 46 | + } |
| 47 | + } |
| 48 | + if ( $found ) { |
| 49 | + $this->output( "All done\n" ); |
| 50 | + } else { |
| 51 | + $this->output( "None found\n" ); |
| 52 | + } |
29 | 53 | } else { |
30 | | - $rev = false; |
| 54 | + // Clean up spam on this wiki |
| 55 | + $res = $dbr->select( 'externallinks', array( 'DISTINCT el_from' ), |
| 56 | + array( 'el_index LIKE ' . $dbr->addQuotes( $like ) ), __METHOD__ ); |
| 57 | + $count = $dbr->numRows( $res ); |
| 58 | + $this->output( "Found $count articles containing $spec\n" ); |
| 59 | + while ( $row = $dbr->fetchObject( $res ) ) { |
| 60 | + $this->cleanupArticle( $row->el_from, $spec ); |
| 61 | + } |
| 62 | + if ( $count ) { |
| 63 | + $this->output( "Done\n" ); |
| 64 | + } |
31 | 65 | } |
32 | 66 | } |
33 | | - if ( $revId == $currentRevId ) { |
34 | | - // The regex didn't match the current article text |
35 | | - // This happens e.g. when a link comes from a template rather than the page itself |
36 | | - print "False match\n"; |
37 | | - } else { |
38 | | - $dbw = wfGetDB( DB_MASTER ); |
39 | | - $dbw->immediateBegin(); |
40 | | - if ( !$rev ) { |
41 | | - // Didn't find a non-spammy revision, blank the page |
42 | | - print "blanking\n"; |
43 | | - $article = new Article( $title ); |
44 | | - $article->updateArticle( '', wfMsg( 'spam_blanking', $domain ), |
45 | | - false, false ); |
46 | 67 | |
| 68 | + private function cleanupArticle( $id, $domain ) { |
| 69 | + $title = Title::newFromID( $id ); |
| 70 | + if ( !$title ) { |
| 71 | + $this->error( "Internal error: no page for ID $id\n" ); |
| 72 | + return; |
| 73 | + } |
| 74 | + |
| 75 | + $this->output( $title->getPrefixedDBkey() . " ..." ); |
| 76 | + $rev = Revision::newFromTitle( $title ); |
| 77 | + $revId = $rev->getId(); |
| 78 | + $currentRevId = $revId; |
| 79 | + |
| 80 | + while ( $rev && LinkFilter::matchEntry( $rev->getText() , $domain ) ) { |
| 81 | + # Revision::getPrevious can't be used in this way before MW 1.6 (Revision.php 1.26) |
| 82 | + #$rev = $rev->getPrevious(); |
| 83 | + $revId = $title->getPreviousRevisionID( $revId ); |
| 84 | + if ( $revId ) { |
| 85 | + $rev = Revision::newFromTitle( $title, $revId ); |
| 86 | + } else { |
| 87 | + $rev = false; |
| 88 | + } |
| 89 | + } |
| 90 | + if ( $revId == $currentRevId ) { |
| 91 | + // The regex didn't match the current article text |
| 92 | + // This happens e.g. when a link comes from a template rather than the page itself |
| 93 | + $this->output( "False match\n" ); |
47 | 94 | } else { |
48 | | - // Revert to this revision |
49 | | - print "reverting\n"; |
50 | | - $article = new Article( $title ); |
51 | | - $article->updateArticle( $rev->getText(), wfMsg( 'spam_reverting', $domain ), false, false ); |
| 95 | + $dbw = wfGetDB( DB_MASTER ); |
| 96 | + $dbw->immediateBegin(); |
| 97 | + if ( !$rev ) { |
| 98 | + // Didn't find a non-spammy revision, blank the page |
| 99 | + $this->output( "blanking\n" ); |
| 100 | + $article = new Article( $title ); |
| 101 | + $article->updateArticle( '', wfMsg( 'spam_blanking', $domain ), |
| 102 | + false, false ); |
| 103 | + |
| 104 | + } else { |
| 105 | + // Revert to this revision |
| 106 | + $this->output( "reverting\n" ); |
| 107 | + $article = new Article( $title ); |
| 108 | + $article->updateArticle( $rev->getText(), wfMsg( 'spam_reverting', $domain ), false, false ); |
| 109 | + } |
| 110 | + $dbw->immediateCommit(); |
| 111 | + wfDoUpdates(); |
52 | 112 | } |
53 | | - $dbw->immediateCommit(); |
54 | | - wfDoUpdates(); |
55 | 113 | } |
56 | 114 | } |
57 | | -//------------------------------------------------------------------------------ |
58 | | - |
59 | | - |
60 | | - |
61 | | - |
62 | | -$username = wfMsg( 'spambot_username' ); |
63 | | -$fname = $username; |
64 | | -$wgUser = User::newFromName( $username ); |
65 | | -// Create the user if necessary |
66 | | -if ( !$wgUser->getId() ) { |
67 | | - $wgUser->addToDatabase(); |
68 | | -} |
69 | | - |
70 | | -if ( !isset( $args[0] ) ) { |
71 | | - print "Usage: php cleanupSpam.php <hostname>\n"; |
72 | | - exit(1); |
73 | | -} |
74 | | -$spec = $args[0]; |
75 | | -$like = LinkFilter::makeLike( $spec ); |
76 | | -if ( !$like ) { |
77 | | - print "Not a valid hostname specification: $spec\n"; |
78 | | - exit(1); |
79 | | -} |
80 | | - |
81 | | -$dbr = wfGetDB( DB_SLAVE ); |
82 | | - |
83 | | -if ( isset($options['all']) ) { |
84 | | - // Clean up spam on all wikis |
85 | | - $dbr = wfGetDB( DB_SLAVE ); |
86 | | - print "Finding spam on " . count($wgLocalDatabases) . " wikis\n"; |
87 | | - $found = false; |
88 | | - foreach ( $wgLocalDatabases as $db ) { |
89 | | - $count = $dbr->selectField( "`$db`.externallinks", 'COUNT(*)', |
90 | | - array( 'el_index LIKE ' . $dbr->addQuotes( $like ) ), $fname ); |
91 | | - if ( $count ) { |
92 | | - $found = true; |
93 | | - passthru( "php cleanupSpam.php $db $spec | sed s/^/$db: /" ); |
94 | | - } |
95 | | - } |
96 | | - if ( $found ) { |
97 | | - print "All done\n"; |
98 | | - } else { |
99 | | - print "None found\n"; |
100 | | - } |
101 | | -} else { |
102 | | - // Clean up spam on this wiki |
103 | | - $res = $dbr->select( 'externallinks', array( 'DISTINCT el_from' ), |
104 | | - array( 'el_index LIKE ' . $dbr->addQuotes( $like ) ), $fname ); |
105 | | - $count = $dbr->numRows( $res ); |
106 | | - print "Found $count articles containing $spec\n"; |
107 | | - while ( $row = $dbr->fetchObject( $res ) ) { |
108 | | - cleanupArticle( $row->el_from, $spec ); |
109 | | - } |
110 | | - if ( $count ) { |
111 | | - print "Done\n"; |
112 | | - } |
113 | | -} |
114 | | - |
115 | | - |
Index: branches/maintenance-work/maintenance/updateRestrictions.php |
— | — | @@ -14,7 +14,7 @@ |
15 | 15 | class UpdateRestrictions extends Maintenance { |
16 | 16 | public function __construct() { |
17 | 17 | parent::__construct(); |
18 | | - $this->mDescription = ""; |
| 18 | + $this->mDescription = "Updates page_restrictions table from old page_restriction column"; |
19 | 19 | $this->setBatchSize( 100 ); |
20 | 20 | } |
21 | 21 | |
Index: branches/maintenance-work/maintenance/purgeList.php |
— | — | @@ -6,34 +6,42 @@ |
7 | 7 | * @ingroup Maintenance |
8 | 8 | */ |
9 | 9 | |
10 | | -require_once( "commandLine.inc" ); |
| 10 | +require_once( "Maintenance.php" ); |
11 | 11 | |
12 | | -$stdin = fopen( "php://stdin", "rt" ); |
13 | | -$urls = array(); |
| 12 | +class PurgeList extends Maintenance { |
| 13 | + public function __construct() { |
| 14 | + parent::__construct(); |
| 15 | + $this->mDescription = "Send purge requests for listed pages to squid"; |
| 16 | + } |
14 | 17 | |
15 | | -while( !feof( $stdin ) ) { |
16 | | - $page = trim( fgets( $stdin ) ); |
17 | | - if ( substr( $page, 0, 7 ) == 'http://' ) { |
18 | | - $urls[] = $page; |
19 | | - } elseif( $page !== '' ) { |
20 | | - $title = Title::newFromText( $page ); |
21 | | - if( $title ) { |
22 | | - $url = $title->getFullUrl(); |
23 | | - echo "$url\n"; |
24 | | - $urls[] = $url; |
25 | | - if( isset( $options['purge'] ) ) { |
26 | | - $title->invalidateCache(); |
| 18 | + public function execute() { |
| 19 | + $stdin = $this->getStdin(); |
| 20 | + $urls = array(); |
| 21 | + |
| 22 | + while( !feof( $stdin ) ) { |
| 23 | + $page = trim( fgets( $stdin ) ); |
| 24 | + if ( substr( $page, 0, 7 ) == 'http://' ) { |
| 25 | + $urls[] = $page; |
| 26 | + } elseif( $page !== '' ) { |
| 27 | + $title = Title::newFromText( $page ); |
| 28 | + if( $title ) { |
| 29 | + $url = $title->getFullUrl(); |
| 30 | + $this->output( "$url\n" ); |
| 31 | + $urls[] = $url; |
| 32 | + if( isset( $options['purge'] ) ) { |
| 33 | + $title->invalidateCache(); |
| 34 | + } |
| 35 | + } else { |
| 36 | + $this->output( "(Invalid title '$page')\n" ); |
| 37 | + } |
27 | 38 | } |
28 | | - } else { |
29 | | - echo "(Invalid title '$page')\n"; |
30 | 39 | } |
| 40 | + |
| 41 | + $this->output( "Purging " . count( $urls ) . " urls...\n" ); |
| 42 | + $u = new SquidUpdate( $urls ); |
| 43 | + $u->doUpdate(); |
| 44 | + |
| 45 | + $this->output( "Done!\n" ); |
31 | 46 | } |
32 | 47 | } |
33 | 48 | |
34 | | -echo "Purging " . count( $urls ) . " urls...\n"; |
35 | | -$u = new SquidUpdate( $urls ); |
36 | | -$u->doUpdate(); |
37 | | - |
38 | | -echo "Done!\n"; |
39 | | - |
40 | | - |
Index: branches/maintenance-work/maintenance/initEditCount.php |
— | — | @@ -4,86 +4,90 @@ |
5 | 5 | * @ingroup Maintenance |
6 | 6 | */ |
7 | 7 | |
8 | | -require_once "commandLine.inc"; |
| 8 | +require_once( "Maintenance.php" ); |
9 | 9 | |
10 | | -if( isset( $options['help'] ) ) { |
11 | | - die( "Batch-recalculate user_editcount fields from the revision table. |
12 | | -Options: |
13 | | - --quick Force the update to be done in a single query. |
14 | | - --background Force replication-friendly mode; may be inefficient but |
15 | | - avoids locking tables or lagging slaves with large updates; |
16 | | - calculates counts on a slave if possible. |
| 10 | +class InitEditCount extends Maintenance { |
| 11 | + public function __construct() { |
| 12 | + parent::__construct(); |
| 13 | + $this->addOption( 'quick', 'Force the update to be done in a single query' ); |
| 14 | + $this->addOption( 'background', 'Force replication-friendly mode; may be inefficient but |
| 15 | + avoids locking tables or lagging slaves with large updates; |
| 16 | + calculates counts on a slave if possible. |
17 | 17 | |
18 | 18 | Background mode will be automatically used if the server is MySQL 4.0 |
19 | 19 | (which does not support subqueries) or if multiple servers are listed |
20 | | -in \$wgDBservers, usually indicating a replication environment. |
| 20 | +in $wgDBservers, usually indicating a replication environment.' ); |
| 21 | + $this->mDescription = "Batch-recalculate user_editcount fields from the revision table"; |
| 22 | + } |
21 | 23 | |
22 | | -"); |
23 | | -} |
24 | | -$dbw = wfGetDB( DB_MASTER ); |
25 | | -$user = $dbw->tableName( 'user' ); |
26 | | -$revision = $dbw->tableName( 'revision' ); |
| 24 | + public function execute() { |
| 25 | + $dbw = wfGetDB( DB_MASTER ); |
| 26 | + $user = $dbw->tableName( 'user' ); |
| 27 | + $revision = $dbw->tableName( 'revision' ); |
27 | 28 | |
28 | | -$dbver = $dbw->getServerVersion(); |
| 29 | + $dbver = $dbw->getServerVersion(); |
29 | 30 | |
30 | | -// Autodetect mode... |
31 | | -$backgroundMode = count( $wgDBservers ) > 1 || |
32 | | - ($dbw instanceof DatabaseMySql && version_compare( $dbver, '4.1' ) < 0); |
| 31 | + // Autodetect mode... |
| 32 | + $backgroundMode = count( $wgDBservers ) > 1 || |
| 33 | + ($dbw instanceof DatabaseMySql && version_compare( $dbver, '4.1' ) < 0); |
| 34 | + |
| 35 | + if( $this->hasOption('background') ) { |
| 36 | + $backgroundMode = true; |
| 37 | + } elseif( $this->hasOption('quick') ) { |
| 38 | + $backgroundMode = false; |
| 39 | + } |
33 | 40 | |
34 | | -if( isset( $options['background'] ) ) { |
35 | | - $backgroundMode = true; |
36 | | -} elseif( isset( $options['quick'] ) ) { |
37 | | - $backgroundMode = false; |
38 | | -} |
| 41 | + if( $backgroundMode ) { |
| 42 | + $this->output( "Using replication-friendly background mode...\n" ); |
39 | 43 | |
40 | | -if( $backgroundMode ) { |
41 | | - echo "Using replication-friendly background mode...\n"; |
42 | | - |
43 | | - $dbr = wfGetDB( DB_SLAVE ); |
44 | | - $chunkSize = 100; |
45 | | - $lastUser = $dbr->selectField( 'user', 'MAX(user_id)', '', __FUNCTION__ ); |
46 | | - |
47 | | - $start = microtime( true ); |
48 | | - $migrated = 0; |
49 | | - for( $min = 0; $min <= $lastUser; $min += $chunkSize ) { |
50 | | - $max = $min + $chunkSize; |
51 | | - $result = $dbr->query( |
52 | | - "SELECT |
53 | | - user_id, |
54 | | - COUNT(rev_user) AS user_editcount |
55 | | - FROM $user |
56 | | - LEFT OUTER JOIN $revision ON user_id=rev_user |
57 | | - WHERE user_id > $min AND user_id <= $max |
58 | | - GROUP BY user_id", |
59 | | - __FUNCTION__ ); |
60 | | - |
61 | | - while( $row = $dbr->fetchObject( $result ) ) { |
62 | | - $dbw->update( 'user', |
63 | | - array( 'user_editcount' => $row->user_editcount ), |
64 | | - array( 'user_id' => $row->user_id ), |
65 | | - __FUNCTION__ ); |
66 | | - ++$migrated; |
| 44 | + $dbr = wfGetDB( DB_SLAVE ); |
| 45 | + $chunkSize = 100; |
| 46 | + $lastUser = $dbr->selectField( 'user', 'MAX(user_id)', '', __METHOD__ ); |
| 47 | + |
| 48 | + $start = microtime( true ); |
| 49 | + $migrated = 0; |
| 50 | + for( $min = 0; $min <= $lastUser; $min += $chunkSize ) { |
| 51 | + $max = $min + $chunkSize; |
| 52 | + $result = $dbr->query( |
| 53 | + "SELECT |
| 54 | + user_id, |
| 55 | + COUNT(rev_user) AS user_editcount |
| 56 | + FROM $user |
| 57 | + LEFT OUTER JOIN $revision ON user_id=rev_user |
| 58 | + WHERE user_id > $min AND user_id <= $max |
| 59 | + GROUP BY user_id", |
| 60 | + __METHOD__ ); |
| 61 | + |
| 62 | + while( $row = $dbr->fetchObject( $result ) ) { |
| 63 | + $dbw->update( 'user', |
| 64 | + array( 'user_editcount' => $row->user_editcount ), |
| 65 | + array( 'user_id' => $row->user_id ), |
| 66 | + __METHOD__ ); |
| 67 | + ++$migrated; |
| 68 | + } |
| 69 | + $dbr->freeResult( $result ); |
| 70 | + |
| 71 | + $delta = microtime( true ) - $start; |
| 72 | + $rate = ($delta == 0.0) ? 0.0 : $migrated / $delta; |
| 73 | + $this->output( sprintf( "%s %d (%0.1f%%) done in %0.1f secs (%0.3f accounts/sec).\n", |
| 74 | + $wgDBname, |
| 75 | + $migrated, |
| 76 | + min( $max, $lastUser ) / $lastUser * 100.0, |
| 77 | + $delta, |
| 78 | + $rate ) ); |
| 79 | + |
| 80 | + wfWaitForSlaves( 10 ); |
| 81 | + } |
| 82 | + } else { |
| 83 | + // Subselect should work on modern MySQLs etc |
| 84 | + $this->output( "Using single-query mode...\n" ); |
| 85 | + $sql = "UPDATE $user SET user_editcount=(SELECT COUNT(*) FROM $revision WHERE rev_user=user_id)"; |
| 86 | + $dbw->query( $sql ); |
67 | 87 | } |
68 | | - $dbr->freeResult( $result ); |
69 | | - |
70 | | - $delta = microtime( true ) - $start; |
71 | | - $rate = ($delta == 0.0) ? 0.0 : $migrated / $delta; |
72 | | - printf( "%s %d (%0.1f%%) done in %0.1f secs (%0.3f accounts/sec).\n", |
73 | | - $wgDBname, |
74 | | - $migrated, |
75 | | - min( $max, $lastUser ) / $lastUser * 100.0, |
76 | | - $delta, |
77 | | - $rate ); |
78 | | - |
79 | | - wfWaitForSlaves( 10 ); |
| 88 | + |
| 89 | + $this->output( "Done!\n" ); |
80 | 90 | } |
81 | | -} else { |
82 | | - // Subselect should work on modern MySQLs etc |
83 | | - echo "Using single-query mode...\n"; |
84 | | - $sql = "UPDATE $user SET user_editcount=(SELECT COUNT(*) FROM $revision WHERE rev_user=user_id)"; |
85 | | - $dbw->query( $sql ); |
86 | 91 | } |
87 | 92 | |
88 | | -echo "Done!\n"; |
89 | | - |
90 | | - |
| 93 | +$maintClass = "InitEditCount"; |
| 94 | +require_once( DO_MAINTENANCE ); |
Index: branches/maintenance-work/maintenance/convertLinks.php |
— | — | @@ -7,8 +7,223 @@ |
8 | 8 | * @ingroup Maintenance |
9 | 9 | */ |
10 | 10 | |
11 | | -/** */ |
12 | | -require_once( "commandLine.inc" ); |
13 | | -require_once( "convertLinks.inc" ); |
| 11 | +require_once( "Maintenance.php" ); |
14 | 12 | |
15 | | -convertLinks(); |
| 13 | +class ConvertLinks extends Maintenance { |
| 14 | + |
| 15 | + public function __construct() { |
| 16 | + parent::__construct(); |
| 17 | + $this->mDescription = "Convert from the old links schema (string->ID) to the new schema (ID->ID) |
| 18 | +The wiki should be put into read-only mode while this script executes"; |
| 19 | + } |
| 20 | + |
| 21 | + public function execute() { |
| 22 | + global $wgDBtype; |
| 23 | + if( $wgDBtype == 'postgres' ) { |
| 24 | + $this->output( "Links table already ok on Postgres.\n" ); |
| 25 | + return; |
| 26 | + } |
| 27 | + |
| 28 | + $this->output( "Converting links table to ID-ID...\n" ); |
| 29 | + |
| 30 | + global $wgLang, $wgDBserver, $wgDBadminuser, $wgDBadminpassword, $wgDBname; |
| 31 | + global $noKeys, $logPerformance, $fh; |
| 32 | + |
| 33 | + $tuplesAdded = $numBadLinks = $curRowsRead = 0; #counters etc |
| 34 | + $totalTuplesInserted = 0; # total tuples INSERTed into links_temp |
| 35 | + |
| 36 | + $reportCurReadProgress = true; #whether or not to give progress reports while reading IDs from cur table |
| 37 | + $curReadReportInterval = 1000; #number of rows between progress reports |
| 38 | + |
| 39 | + $reportLinksConvProgress = true; #whether or not to give progress reports during conversion |
| 40 | + $linksConvInsertInterval = 1000; #number of rows per INSERT |
| 41 | + |
| 42 | + $initialRowOffset = 0; |
| 43 | + #$finalRowOffset = 0; # not used yet; highest row number from links table to process |
| 44 | + |
| 45 | + # Overwrite the old links table with the new one. If this is set to false, |
| 46 | + # the new table will be left at links_temp. |
| 47 | + $overwriteLinksTable = true; |
| 48 | + |
| 49 | + # Don't create keys, and so allow duplicates in the new links table. |
| 50 | + # This gives a huge speed improvement for very large links tables which are MyISAM. (What about InnoDB?) |
| 51 | + $noKeys = false; |
| 52 | + |
| 53 | + |
| 54 | + $logPerformance = false; # output performance data to a file |
| 55 | + $perfLogFilename = "convLinksPerf.txt"; |
| 56 | + #-------------------------------------------------------------------- |
| 57 | + |
| 58 | + $dbw = wfGetDB( DB_MASTER ); |
| 59 | + list ($cur, $links, $links_temp, $links_backup) = $dbw->tableNamesN( 'cur', 'links', 'links_temp', 'links_backup' ); |
| 60 | + |
| 61 | + $res = $dbw->query( "SELECT l_from FROM $links LIMIT 1" ); |
| 62 | + if ( $dbw->fieldType( $res, 0 ) == "int" ) { |
| 63 | + $this->output( "Schema already converted\n" ); |
| 64 | + return; |
| 65 | + } |
| 66 | + |
| 67 | + $res = $dbw->query( "SELECT COUNT(*) AS count FROM $links" ); |
| 68 | + $row = $dbw->fetchObject($res); |
| 69 | + $numRows = $row->count; |
| 70 | + $dbw->freeResult( $res ); |
| 71 | + |
| 72 | + if ( $numRows == 0 ) { |
| 73 | + $this->output( "Updating schema (no rows to convert)...\n" ); |
| 74 | + $this->createTempTable(); |
| 75 | + } else { |
| 76 | + if ( $logPerformance ) { $fh = fopen ( $perfLogFilename, "w" ); } |
| 77 | + $baseTime = $startTime = $this->getMicroTime(); |
| 78 | + # Create a title -> cur_id map |
| 79 | + $this->output( "Loading IDs from $cur table...\n" ); |
| 80 | + $this->performanceLog ( "Reading $numRows rows from cur table...\n" ); |
| 81 | + $this->performanceLog ( "rows read vs seconds elapsed:\n" ); |
| 82 | + |
| 83 | + $dbw->bufferResults( false ); |
| 84 | + $res = $dbw->query( "SELECT cur_namespace,cur_title,cur_id FROM $cur" ); |
| 85 | + $ids = array(); |
| 86 | + |
| 87 | + while ( $row = $dbw->fetchObject( $res ) ) { |
| 88 | + $title = $row->cur_title; |
| 89 | + if ( $row->cur_namespace ) { |
| 90 | + $title = $wgLang->getNsText( $row->cur_namespace ) . ":$title"; |
| 91 | + } |
| 92 | + $ids[$title] = $row->cur_id; |
| 93 | + $curRowsRead++; |
| 94 | + if ($reportCurReadProgress) { |
| 95 | + if (($curRowsRead % $curReadReportInterval) == 0) { |
| 96 | + $this->performanceLog( $curRowsRead . " " . ($this->getMicroTime() - $baseTime) . "\n" ); |
| 97 | + $this->output( "\t$curRowsRead rows of $cur table read.\n" ); |
| 98 | + } |
| 99 | + } |
| 100 | + } |
| 101 | + $dbw->freeResult( $res ); |
| 102 | + $dbw->bufferResults( true ); |
| 103 | + $this->output( "Finished loading IDs.\n\n" ); |
| 104 | + $this->performanceLog( "Took " . ($this->getMicroTime() - $baseTime) . " seconds to load IDs.\n\n" ); |
| 105 | + #-------------------------------------------------------------------- |
| 106 | + |
| 107 | + # Now, step through the links table (in chunks of $linksConvInsertInterval rows), |
| 108 | + # convert, and write to the new table. |
| 109 | + $this->createTempTable(); |
| 110 | + $this->performanceLog( "Resetting timer.\n\n" ); |
| 111 | + $baseTime = $this->getMicroTime(); |
| 112 | + $this->output( "Processing $numRows rows from $links table...\n" ); |
| 113 | + $this->performanceLog( "Processing $numRows rows from $links table...\n" ); |
| 114 | + $this->performanceLog( "rows inserted vs seconds elapsed:\n" ); |
| 115 | + |
| 116 | + for ($rowOffset = $initialRowOffset; $rowOffset < $numRows; $rowOffset += $linksConvInsertInterval) { |
| 117 | + $sqlRead = "SELECT * FROM $links "; |
| 118 | + $sqlRead = $dbw->limitResult($sqlRead, $linksConvInsertInterval,$rowOffset); |
| 119 | + $res = $dbw->query($sqlRead); |
| 120 | + if ( $noKeys ) { |
| 121 | + $sqlWrite = array("INSERT INTO $links_temp (l_from,l_to) VALUES "); |
| 122 | + } else { |
| 123 | + $sqlWrite = array("INSERT IGNORE INTO $links_temp (l_from,l_to) VALUES "); |
| 124 | + } |
| 125 | + |
| 126 | + $tuplesAdded = 0; # no tuples added to INSERT yet |
| 127 | + while ( $row = $dbw->fetchObject($res) ) { |
| 128 | + $fromTitle = $row->l_from; |
| 129 | + if ( array_key_exists( $fromTitle, $ids ) ) { # valid title |
| 130 | + $from = $ids[$fromTitle]; |
| 131 | + $to = $row->l_to; |
| 132 | + if ( $tuplesAdded != 0 ) { |
| 133 | + $sqlWrite[] = ","; |
| 134 | + } |
| 135 | + $sqlWrite[] = "($from,$to)"; |
| 136 | + $tuplesAdded++; |
| 137 | + } else { # invalid title |
| 138 | + $numBadLinks++; |
| 139 | + } |
| 140 | + } |
| 141 | + $dbw->freeResult($res); |
| 142 | + #$this->output( "rowOffset: $rowOffset\ttuplesAdded: $tuplesAdded\tnumBadLinks: $numBadLinks\n" ); |
| 143 | + if ( $tuplesAdded != 0 ) { |
| 144 | + if ($reportLinksConvProgress) { |
| 145 | + $this->output( "Inserting $tuplesAdded tuples into $links_temp..." ); |
| 146 | + } |
| 147 | + $dbw->query( implode("",$sqlWrite) ); |
| 148 | + $totalTuplesInserted += $tuplesAdded; |
| 149 | + if ($reportLinksConvProgress) |
| 150 | + $this->output( " done. Total $totalTuplesInserted tuples inserted.\n" ); |
| 151 | + $this->performanceLog( $totalTuplesInserted . " " . ($this->getMicroTime() - $baseTime) . "\n" ); |
| 152 | + } |
| 153 | + } |
| 154 | + $this->output( "$totalTuplesInserted valid titles and $numBadLinks invalid titles were processed.\n\n" ); |
| 155 | + $this->performanceLog( "$totalTuplesInserted valid titles and $numBadLinks invalid titles were processed.\n" ); |
| 156 | + $this->performanceLog( "Total execution time: " . ($this->getMicroTime() - $startTime) . " seconds.\n" ); |
| 157 | + if ( $logPerformance ) { fclose ( $fh ); } |
| 158 | + } |
| 159 | + #-------------------------------------------------------------------- |
| 160 | + |
| 161 | + if ( $overwriteLinksTable ) { |
| 162 | + $dbConn = Database::newFromParams( $wgDBserver, $wgDBadminuser, $wgDBadminpassword, $wgDBname ); |
| 163 | + if (!($dbConn->isOpen())) { |
| 164 | + $this->output( "Opening connection to database failed.\n" ); |
| 165 | + return; |
| 166 | + } |
| 167 | + # Check for existing links_backup, and delete it if it exists. |
| 168 | + $this->output( "Dropping backup links table if it exists..." ); |
| 169 | + $dbConn->query( "DROP TABLE IF EXISTS $links_backup", DB_MASTER); |
| 170 | + $this->output( " done.\n" ); |
| 171 | + |
| 172 | + # Swap in the new table, and move old links table to links_backup |
| 173 | + $this->output( "Swapping tables '$links' to '$links_backup'; '$links_temp' to '$links'..." ); |
| 174 | + $dbConn->query( "RENAME TABLE links TO $links_backup, $links_temp TO $links", DB_MASTER ); |
| 175 | + $this->output( " done.\n\n" ); |
| 176 | + |
| 177 | + $dbConn->close(); |
| 178 | + $this->output( "Conversion complete. The old table remains at $links_backup;\n" ); |
| 179 | + $this->output( "delete at your leisure.\n" ); |
| 180 | + } else { |
| 181 | + $this->output( "Conversion complete. The converted table is at $links_temp;\n" ); |
| 182 | + $this->output( "the original links table is unchanged.\n" ); |
| 183 | + } |
| 184 | + } |
| 185 | + |
| 186 | + private function createTempTable() { |
| 187 | + global $wgDBserver, $wgDBadminuser, $wgDBadminpassword, $wgDBname; |
| 188 | + global $noKeys; |
| 189 | + $dbConn = Database::newFromParams( $wgDBserver, $wgDBadminuser, $wgDBadminpassword, $wgDBname ); |
| 190 | + |
| 191 | + if (!($dbConn->isOpen())) { |
| 192 | + $this->output( "Opening connection to database failed.\n" ); |
| 193 | + return; |
| 194 | + } |
| 195 | + $links_temp = $dbConn->tableName( 'links_temp' ); |
| 196 | + |
| 197 | + $this->output( "Dropping temporary links table if it exists..." ); |
| 198 | + $dbConn->query( "DROP TABLE IF EXISTS $links_temp"); |
| 199 | + $this->output( " done.\n" ); |
| 200 | + |
| 201 | + $this->output( "Creating temporary links table..." ); |
| 202 | + if ( $noKeys ) { |
| 203 | + $dbConn->query( "CREATE TABLE $links_temp ( " . |
| 204 | + "l_from int(8) unsigned NOT NULL default '0', " . |
| 205 | + "l_to int(8) unsigned NOT NULL default '0')"); |
| 206 | + } else { |
| 207 | + $dbConn->query( "CREATE TABLE $links_temp ( " . |
| 208 | + "l_from int(8) unsigned NOT NULL default '0', " . |
| 209 | + "l_to int(8) unsigned NOT NULL default '0', " . |
| 210 | + "UNIQUE KEY l_from(l_from,l_to), " . |
| 211 | + "KEY (l_to))"); |
| 212 | + } |
| 213 | + $this->output( " done.\n\n" ); |
| 214 | + } |
| 215 | + |
| 216 | + private function performanceLog( $text ) { |
| 217 | + global $logPerformance, $fh; |
| 218 | + if ( $logPerformance ) { |
| 219 | + fwrite( $fh, $text ); |
| 220 | + } |
| 221 | + } |
| 222 | + |
| 223 | + private function getMicroTime() { # return time in seconds, with microsecond accuracy |
| 224 | + list($usec, $sec) = explode(" ", microtime()); |
| 225 | + return ((float)$usec + (float)$sec); |
| 226 | + } |
| 227 | +} |
| 228 | + |
| 229 | +$maintClass = "ConvertLinks"; |
| 230 | +require_once( DO_MAINTENANCE ); |