r52006 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r52005‎ | r52006 | r52007 >
Date:21:42, 16 June 2009
Author:demon
Status:deferred
Tags:
Comment:
* Move purgeRedundantText() to Maintenance, as it is useful for quite a few scripts
* Add $force param to maybeHelp() so things like error() might be able to use it one day
* Add arg count validation
* Remove unused $wgDBadminuser/$wgDBadminpassword
* Port updateSearchIndex, updateArticleCount, reassignEdits, purgeOldText, checkImages, clear_interwiki_cache,
** Delete now-unneeded .inc files
Modified paths:
  • /branches/maintenance-work/maintenance/Maintenance.php (modified) (history)
  • /branches/maintenance-work/maintenance/checkImages.php (modified) (history)
  • /branches/maintenance-work/maintenance/clear_interwiki_cache.php (modified) (history)
  • /branches/maintenance-work/maintenance/purgeOldText.php (modified) (history)
  • /branches/maintenance-work/maintenance/reassignEdits.inc (deleted) (history)
  • /branches/maintenance-work/maintenance/reassignEdits.php (modified) (history)
  • /branches/maintenance-work/maintenance/updateArticleCount.inc (deleted) (history)
  • /branches/maintenance-work/maintenance/updateArticleCount.php (modified) (history)
  • /branches/maintenance-work/maintenance/updateSearchIndex.inc (deleted) (history)
  • /branches/maintenance-work/maintenance/updateSearchIndex.php (modified) (history)
  • /branches/maintenance-work/t/Search.inc (modified) (history)

Diff [purge]

Index: branches/maintenance-work/maintenance/updateArticleCount.inc
@@ -1,61 +0,0 @@
2 -<?php
3 -/**
4 - * Support class for the updateArticleCount.php maintenance script
5 - *
6 - * @file
7 - * @ingroup Maintenance
8 - * @author Rob Church <robchur@gmail.com>
9 - */
10 -
11 -class ArticleCounter {
12 -
13 - var $dbr;
14 - var $namespaces;
15 -
16 - function ArticleCounter() {
17 - global $wgContentNamespaces;
18 - $this->namespaces = $wgContentNamespaces;
19 - $this->dbr = wfGetDB( DB_SLAVE );
20 - }
21 -
22 - /**
23 - * Produce a comma-delimited set of namespaces
24 - * Includes paranoia
25 - *
26 - * @return string
27 - */
28 - function makeNsSet() {
29 - foreach( $this->namespaces as $namespace )
30 - $namespaces[] = intval( $namespace );
31 - return implode( ', ', $namespaces );
32 - }
33 -
34 - /**
35 - * Produce SQL for the query
36 - *
37 - * @return string
38 - */
39 - function makeSql() {
40 - list( $page, $pagelinks ) = $this->dbr->tableNamesN( 'page', 'pagelinks' );
41 - $nsset = $this->makeNsSet();
42 - return "SELECT COUNT(DISTINCT page_namespace, page_title) AS pagecount " .
43 - "FROM $page, $pagelinks " .
44 - "WHERE pl_from=page_id and page_namespace IN ( $nsset ) " .
45 - "AND page_is_redirect = 0 AND page_len > 0";
46 - }
47 -
48 - /**
49 - * Count the number of valid content pages in the wiki
50 - *
51 - * @return mixed Integer, or false if there's a problem
52 - */
53 - function count() {
54 - $res = $this->dbr->query( $this->makeSql(), __METHOD__ );
55 - $row = $this->dbr->fetchObject( $res );
56 - $this->dbr->freeResult( $res );
57 - return $row->pagecount;
58 - }
59 -
60 -}
61 -
62 -
Index: branches/maintenance-work/maintenance/updateSearchIndex.inc
@@ -1,115 +0,0 @@
2 -<?php
3 -/**
4 - * @file
5 - * @ingroup Maintenance
6 - */
7 -
8 -/** */
9 -function updateSearchIndex( $start, $end, $maxLockTime, $quiet ) {
10 - global $wgQuiet;
11 - global $wgDisableSearchUpdate;
12 -
13 - $fname = "updateSearchIndex";
14 -
15 - $wgQuiet = $quiet;
16 - $wgDisableSearchUpdate = false;
17 -
18 - $dbw = wfGetDB( DB_MASTER );
19 - $recentchanges = $dbw->tableName( 'recentchanges' );
20 -
21 - output( "Updating searchindex between $start and $end\n" );
22 -
23 - # Select entries from recentchanges which are on top and between the specified times
24 - $start = $dbw->strencode( $start );
25 - $end = $dbw->strencode( $end );
26 -
27 - $page = $dbw->tableName( 'page' );
28 - $sql = "SELECT rc_cur_id,rc_type,rc_moved_to_ns,rc_moved_to_title FROM $recentchanges
29 - JOIN $page ON rc_cur_id=page_id AND rc_this_oldid=page_latest
30 - WHERE rc_timestamp BETWEEN '$start' AND '$end'
31 - ";
32 - $res = $dbw->query( $sql, $fname );
33 -
34 -
35 - # Lock searchindex
36 - if ( $maxLockTime ) {
37 - output( " --- Waiting for lock ---" );
38 - lockSearchindex( $dbw );
39 - $lockTime = time();
40 - output( "\n" );
41 - }
42 -
43 - # Loop through the results and do a search update
44 - while ( $row = $dbw->fetchObject( $res ) ) {
45 - # Allow reads to be processed
46 - if ( $maxLockTime && time() > $lockTime + $maxLockTime ) {
47 - output( " --- Relocking ---" );
48 - relockSearchindex( $dbw );
49 - $lockTime = time();
50 - output( "\n" );
51 - }
52 - if ( $row->rc_type == RC_LOG ) {
53 - continue;
54 - } elseif ( $row->rc_type == RC_MOVE || $row->rc_type == RC_MOVE_OVER_REDIRECT ) {
55 - # Rename searchindex entry
56 - $titleObj = Title::makeTitle( $row->rc_moved_to_ns, $row->rc_moved_to_title );
57 - $title = $titleObj->getPrefixedDBkey();
58 - output( "$title..." );
59 - $u = new SearchUpdate( $row->rc_cur_id, $title, false );
60 - output( "\n" );
61 - } else {
62 - // Get current revision
63 - $rev = Revision::loadFromPageId( $dbw, $row->rc_cur_id );
64 - if( $rev ) {
65 - $titleObj = $rev->getTitle();
66 - $title = $titleObj->getPrefixedDBkey();
67 - output( $title );
68 - # Update searchindex
69 - $u = new SearchUpdate( $row->rc_cur_id, $titleObj->getText(), $rev->getText() );
70 - $u->doUpdate();
71 - output( "\n" );
72 - }
73 - }
74 - }
75 -
76 - # Unlock searchindex
77 - if ( $maxLockTime ) {
78 - output( " --- Unlocking --" );
79 - unlockSearchindex( $dbw );
80 - output( "\n" );
81 - }
82 - output( "Done\n" );
83 -}
84 -
85 -function lockSearchindex( &$db ) {
86 - $write = array( 'searchindex' );
87 - $read = array( 'page', 'revision', 'text', 'interwiki' );
88 - $items = array();
89 -
90 - foreach( $write as $table ) {
91 - $items[] = $db->tableName( $table ) . ' LOW_PRIORITY WRITE';
92 - }
93 - foreach( $read as $table ) {
94 - $items[] = $db->tableName( $table ) . ' READ';
95 - }
96 - $sql = "LOCK TABLES " . implode( ',', $items );
97 - $db->query( $sql, 'updateSearchIndex.inc ' . __METHOD__ );
98 -}
99 -
100 -function unlockSearchindex( &$db ) {
101 - $db->query( "UNLOCK TABLES", 'updateSearchIndex.inc ' . __METHOD__ );
102 -}
103 -
104 -# Unlock and lock again
105 -# Since the lock is low-priority, queued reads will be able to complete
106 -function relockSearchindex( &$db ) {
107 - unlockSearchindex( $db );
108 - lockSearchindex( $db );
109 -}
110 -
111 -function output( $text ) {
112 - global $wgQuiet;
113 - if ( !$wgQuiet ) {
114 - print $text;
115 - }
116 -}
Index: branches/maintenance-work/maintenance/reassignEdits.inc
@@ -1,143 +0,0 @@
2 -<?php
3 -
4 -/**
5 - * Support functions for the reassignEdits script
6 - *
7 - * @file
8 - * @ingroup Maintenance
9 - * @author Rob Church <robchur@gmail.com>
10 - * @licence GNU General Public Licence 2.0 or later
11 - */
12 -
13 -/**
14 - * Reassign edits from one user to another
15 - *
16 - * @param $from User to take edits from
17 - * @param $to User to assign edits to
18 - * @param $rc Update the recent changes table
19 - * @param $report Don't change things; just echo numbers
20 - * @return integer Number of entries changed, or that would be changed
21 - */
22 -function reassignEdits( &$from, &$to, $rc = false, $report = false ) {
23 - $dbw = wfGetDB( DB_MASTER );
24 - $dbw->immediateBegin();
25 - $fname = 'reassignEdits';
26 -
27 - # Count things
28 - out( "Checking current edits..." );
29 - $res = $dbw->select( 'revision', 'COUNT(*) AS count', userConditions( $from, 'rev_user', 'rev_user_text' ), $fname );
30 - $row = $dbw->fetchObject( $res );
31 - $cur = $row->count;
32 - out( "found {$cur}.\n" );
33 -
34 - out( "Checking deleted edits..." );
35 - $res = $dbw->select( 'archive', 'COUNT(*) AS count', userConditions( $from, 'ar_user', 'ar_user_text' ), $fname );
36 - $row = $dbw->fetchObject( $res );
37 - $del = $row->count;
38 - out( "found {$del}.\n" );
39 -
40 - # Don't count recent changes if we're not supposed to
41 - if( $rc ) {
42 - out( "Checking recent changes..." );
43 - $res = $dbw->select( 'recentchanges', 'COUNT(*) AS count', userConditions( $from, 'rc_user', 'rc_user_text' ), $fname );
44 - $row = $dbw->fetchObject( $res );
45 - $rec = $row->count;
46 - out( "found {$rec}.\n" );
47 - } else {
48 - $rec = 0;
49 - }
50 -
51 - $total = $cur + $del + $rec;
52 - out( "\nTotal entries to change: {$total}\n" );
53 -
54 - if( !$report ) {
55 - if( $total ) {
56 - # Reassign edits
57 - out( "\nReassigning current edits..." );
58 - $res = $dbw->update( 'revision', userSpecification( $to, 'rev_user', 'rev_user_text' ), userConditions( $from, 'rev_user', 'rev_user_text' ), $fname );
59 - out( "done.\nReassigning deleted edits..." );
60 - $res = $dbw->update( 'archive', userSpecification( $to, 'ar_user', 'ar_user_text' ), userConditions( $from, 'ar_user', 'ar_user_text' ), $fname );
61 - out( "done.\n" );
62 - # Update recent changes if required
63 - if( $rc ) {
64 - out( "Updating recent changes..." );
65 - $res = $dbw->update( 'recentchanges', userSpecification( $to, 'rc_user', 'rc_user_text' ), userConditions( $from, 'rc_user', 'rc_user_text' ), $fname );
66 - out( "done.\n" );
67 - }
68 - }
69 - }
70 -
71 - $dbw->immediateCommit();
72 - return (int)$total;
73 -}
74 -
75 -/**
76 - * Return the most efficient set of user conditions
77 - * i.e. a user => id mapping, or a user_text => text mapping
78 - *
79 - * @param $user User for the condition
80 - * @param $idfield Field name containing the identifier
81 - * @param $utfield Field name containing the user text
82 - * @return array
83 - */
84 -function userConditions( &$user, $idfield, $utfield ) {
85 - return $user->getId() ? array( $idfield => $user->getId() ) : array( $utfield => $user->getName() );
86 -}
87 -
88 -/**
89 - * Return user specifications
90 - * i.e. user => id, user_text => text
91 - *
92 - * @param $user User for the spec
93 - * @param $idfield Field name containing the identifier
94 - * @param $utfield Field name containing the user text
95 - * @return array
96 - */
97 -function userSpecification( &$user, $idfield, $utfield ) {
98 - return array( $idfield => $user->getId(), $utfield => $user->getName() );
99 -}
100 -
101 -/**
102 - * Echo output if $wgSilent is off
103 - *
104 - * @param $output Output to echo
105 - * @return bool True if the output was echoed
106 - */
107 -function out( $output ) {
108 - global $wgSilent;
109 - if( !$wgSilent ) {
110 - echo( $output );
111 - return true;
112 - } else {
113 - return false;
114 - }
115 -}
116 -
117 -/**
118 - * Mutator for $wgSilent
119 - *
120 - * @param $silent Switch on $wgSilent
121 - */
122 -function silent( $silent = true ) {
123 - global $wgSilent;
124 - $wgSilent = $silent;
125 -}
126 -
127 -/**
128 - * Initialise the user object
129 - *
130 - * @param $username Username or IP address
131 - * @return User
132 - */
133 -function initialiseUser( $username ) {
134 - if( User::isIP( $username ) ) {
135 - $user = new User();
136 - $user->setId( 0 );
137 - $user->setName( $username );
138 - } else {
139 - $user = User::newFromName( $username );
140 - }
141 - $user->load();
142 - return $user;
143 -}
144 -
Index: branches/maintenance-work/maintenance/updateArticleCount.php
@@ -3,38 +3,84 @@
44 * Maintenance script to provide a better count of the number of articles
55 * and update the site statistics table, if desired
66 *
7 - * @file
87 * @ingroup Maintenance
98 * @author Rob Church <robchur@gmail.com>
109 */
1110
12 -$options = array( 'update', 'help' );
13 -require_once( 'commandLine.inc' );
14 -require_once( 'updateArticleCount.inc' );
15 -echo( "Update Article Count\n\n" );
 11+require_once( "Maintenance.php" );
1612
17 -if( isset( $options['help'] ) && $options['help'] ) {
18 - echo( "Usage: php updateArticleCount.php [--update]\n\n" );
19 - echo( "--update : Update site statistics table\n" );
20 - exit( 0 );
21 -}
 13+class UpdateArticleCount extends Maintenance {
2214
23 -echo( "Counting articles..." );
24 -$counter = new ArticleCounter();
25 -$result = $counter->count();
 15+ // Content namespaces
 16+ private $namespaces;
2617
27 -if( $result !== false ) {
28 - echo( "found {$result}.\n" );
29 - if( isset( $options['update'] ) && $options['update'] ) {
30 - echo( "Updating site statistics table... " );
31 - $dbw = wfGetDB( DB_MASTER );
32 - $dbw->update( 'site_stats', array( 'ss_good_articles' => $result ), array( 'ss_row_id' => 1 ), __METHOD__ );
33 - echo( "done.\n" );
34 - } else {
35 - echo( "To update the site statistics table, run the script with the --update option.\n" );
 18+ public function __construct() {
 19+ global $wgContentNamespaces;
 20+ parent::__construct();
 21+ $this->mDescription = "Count of the number of articles and update the site statistics table";
 22+ $this->addParam( 'update', 'Update the site_stats table with the new count' );
 23+ $this->namespaces = $wgContentNamespaces;
3624 }
37 -} else {
38 - echo( "failed.\n" );
 25+
 26+ public function execute() {
 27+ $this->output( "Counting articles..." );
 28+ $result = $this->count();
 29+
 30+ if( $result !== false ) {
 31+ $this->output( "found {$result}.\n" );
 32+ if( isset( $options['update'] ) && $options['update'] ) {
 33+ $this->output( "Updating site statistics table... " );
 34+ $dbw = wfGetDB( DB_MASTER );
 35+ $dbw->update( 'site_stats', array( 'ss_good_articles' => $result ), array( 'ss_row_id' => 1 ), __METHOD__ );
 36+ $this->output( "done.\n" );
 37+ } else {
 38+ $this->output( "To update the site statistics table, run the script with the --update option.\n" );
 39+ }
 40+ } else {
 41+ $this->output( "failed.\n" );
 42+ }
 43+ }
 44+
 45+ /**
 46+ * Produce a comma-delimited set of namespaces
 47+ * Includes paranoia
 48+ *
 49+ * @return string
 50+ */
 51+ private function makeNsSet() {
 52+ foreach( $this->namespaces as $namespace )
 53+ $namespaces[] = intval( $namespace );
 54+ return implode( ', ', $namespaces );
 55+ }
 56+
 57+ /**
 58+ * Produce SQL for the query
 59+ *
 60+ * @param $dbr Database handle
 61+ * @return string
 62+ */
 63+ private function makeSql( $dbr ) {
 64+ list( $page, $pagelinks ) = $dbr->tableNamesN( 'page', 'pagelinks' );
 65+ $nsset = $this->makeNsSet();
 66+ return "SELECT COUNT(DISTINCT page_namespace, page_title) AS pagecount " .
 67+ "FROM $page, $pagelinks " .
 68+ "WHERE pl_from=page_id and page_namespace IN ( $nsset ) " .
 69+ "AND page_is_redirect = 0 AND page_len > 0";
 70+ }
 71+
 72+ /**
 73+ * Count the number of valid content pages in the wiki
 74+ *
 75+ * @return mixed Integer, or false if there's a problem
 76+ */
 77+ private function count() {
 78+ $dbr = wfGetDB( DB_SLAVE );
 79+ $res = $dbr->query( $this->makeSql( $dbr ), __METHOD__ );
 80+ $row = $dbr->fetchObject( $res );
 81+ $dbr->freeResult( $res );
 82+ return $row->pagecount;
 83+ }
3984 }
40 -echo( "\n" );
4185
 86+$maintClass = "UpdateArticleCount";
 87+require_once( DO_MAINTENANCE );
Index: branches/maintenance-work/maintenance/updateSearchIndex.php
@@ -5,58 +5,159 @@
66 * Usage: php updateSearchIndex.php [-s START] [-e END] [-p POSFILE] [-l LOCKTIME] [-q]
77 * Where START is the starting timestamp
88 * END is the ending timestamp
9 - * POSFILE is a file to load timestamps from and save them to, searchUpdate.pos by default
10 - * LOCKTIME is how long the searchindex and cur tables will be locked for
 9+ * POSFILE is a file to load timestamps from and save them to, searchUpdate.WIKI_ID.pos by default
 10+ * LOCKTIME is how long the searchindex and revision tables will be locked for
1111 * -q means quiet
1212 *
13 - * @file
1413 * @ingroup Maintenance
1514 */
 15+
 16+require_once( "Maintenance.php" );
1617
17 -/** */
18 -$optionsWithArgs = array( 's', 'e', 'p' );
 18+class UpdateSearchIndex extends Maintenance {
1919
20 -require_once( 'commandLine.inc' );
21 -require_once( 'updateSearchIndex.inc' );
 20+ public function __construct() {
 21+ parent::__construct();
 22+ $this->mDescription = "Script for periodic off-peak updating of the search index";
 23+ $this->addParam( 's', 'starting timestamp', false, true );
 24+ $this->addParam( 'e', 'Ending timestamp', false, true );
 25+ $this->addParam( 'p', 'File for saving/loading timestamps, searchUpdate.WIKI_ID.pos by default', false, true );
 26+ $this->addParam( 'l', 'How long the searchindex and revision tables will be locked for', false, true );
 27+ }
2228
23 -if ( isset( $options['p'] ) ) {
24 - $posFile = $options['p'];
25 -} else {
26 - $posFile = 'searchUpdate.' . wfWikiId() . '.pos';
27 -}
 29+ public function execute() {
 30+ $posFile = $this->getOption( 'p', 'searchUpdate.' . wfWikiId() . '.pos' );
 31+ $end = $this->getOption( 'e', wfTimestampNow() );
 32+ if ( $this->hasOption( 's' ) ) {
 33+ $start = $this->getOption('s');
 34+ } elseif( is_readable( 'searchUpdate.pos' ) ) {
 35+ # B/c to the old position file name which was hardcoded
 36+ # We can safely delete the file when we're done though.
 37+ $start = file_get_contents( 'searchUpdate.pos' );
 38+ unlink( 'searchUpdate.pos' );
 39+ } else {
 40+ $start = @file_get_contents( $posFile );
 41+ if ( !$start ) {
 42+ $start = wfTimestamp( TS_MW, time() - 86400 );
 43+ }
 44+ }
 45+ $lockTime = $this->getOption( 'l', 20 );
 46+
 47+ $this->updateSearchIndex( $start, $end, $lockTime );
 48+ $file = fopen( $posFile, 'w' );
 49+ fwrite( $file, $end );
 50+ fclose( $file );
 51+ }
 52+
 53+ private function updateSearchIndex( $start, $end, $maxLockTime ) {
 54+ global $wgDisableSearchUpdate;
2855
29 -if ( isset( $options['e'] ) ) {
30 - $end = $options['e'];
31 -} else {
32 - $end = wfTimestampNow();
33 -}
 56+ $wgDisableSearchUpdate = false;
3457
35 -if ( isset( $options['s'] ) ) {
36 - $start = $options['s'];
37 -} elseif( is_readable( 'searchUpdate.pos' ) ) {
38 - # B/c to the old position file name which was hardcoded
39 - # We can safely delete the file when we're done though.
40 - $start = file_get_contents( 'searchUpdate.pos' );
41 - unlink( 'searchUpdate.pos' );
42 -} else {
43 - $start = @file_get_contents( $posFile );
44 - if ( !$start ) {
45 - $start = wfTimestamp( TS_MW, time() - 86400 );
46 - }
47 -}
 58+ $dbw = wfGetDB( DB_MASTER );
 59+ $recentchanges = $dbw->tableName( 'recentchanges' );
4860
49 -if ( isset( $options['l'] ) ) {
50 - $lockTime = $options['l'];
51 -} else {
52 - $lockTime = 20;
53 -}
 61+ $this->output( "Updating searchindex between $start and $end\n" );
5462
55 -$quiet = (bool)(@$options['q']);
 63+ # Select entries from recentchanges which are on top and between the specified times
 64+ $start = $dbw->strencode( $start );
 65+ $end = $dbw->strencode( $end );
5666
57 -updateSearchIndex( $start, $end, $lockTime, $quiet );
 67+ $page = $dbw->tableName( 'page' );
 68+ $sql = "SELECT rc_cur_id,rc_type,rc_moved_to_ns,rc_moved_to_title FROM $recentchanges
 69+ JOIN $page ON rc_cur_id=page_id AND rc_this_oldid=page_latest
 70+ WHERE rc_timestamp BETWEEN '$start' AND '$end'
 71+ ";
 72+ $res = $dbw->query( $sql, __METHOD__ );
5873
59 -$file = fopen( $posFile, 'w' );
60 -fwrite( $file, $end );
61 -fclose( $file );
6274
 75+ # Lock searchindex
 76+ if ( $maxLockTime ) {
 77+ $this->output( " --- Waiting for lock ---" );
 78+ $this->lockSearchindex( $dbw );
 79+ $lockTime = time();
 80+ $this->output( "\n" );
 81+ }
6382
 83+ # Loop through the results and do a search update
 84+ while ( $row = $dbw->fetchObject( $res ) ) {
 85+ # Allow reads to be processed
 86+ if ( $maxLockTime && time() > $lockTime + $maxLockTime ) {
 87+ $this->output( " --- Relocking ---" );
 88+ $this->relockSearchindex( $dbw );
 89+ $lockTime = time();
 90+ $this->output( "\n" );
 91+ }
 92+ if ( $row->rc_type == RC_LOG ) {
 93+ continue;
 94+ } elseif ( $row->rc_type == RC_MOVE || $row->rc_type == RC_MOVE_OVER_REDIRECT ) {
 95+ # Rename searchindex entry
 96+ $titleObj = Title::makeTitle( $row->rc_moved_to_ns, $row->rc_moved_to_title );
 97+ $title = $titleObj->getPrefixedDBkey();
 98+ $this->output( "$title..." );
 99+ $u = new SearchUpdate( $row->rc_cur_id, $title, false );
 100+ $this->output( "\n" );
 101+ } else {
 102+ // Get current revision
 103+ $rev = Revision::loadFromPageId( $dbw, $row->rc_cur_id );
 104+ if( $rev ) {
 105+ $titleObj = $rev->getTitle();
 106+ $title = $titleObj->getPrefixedDBkey();
 107+ $this->output( $title );
 108+ # Update searchindex
 109+ $u = new SearchUpdate( $row->rc_cur_id, $titleObj->getText(), $rev->getText() );
 110+ $u->doUpdate();
 111+ $this->output( "\n" );
 112+ }
 113+ }
 114+ }
 115+
 116+ # Unlock searchindex
 117+ if ( $maxLockTime ) {
 118+ $this->output( " --- Unlocking --" );
 119+ $this->unlockSearchindex( $dbw );
 120+ $this->output( "\n" );
 121+ }
 122+ $this->output( "Done\n" );
 123+ }
 124+
 125+ /**
 126+ * Lock the search index
 127+ * @param &$db Database object
 128+ */
 129+ private function lockSearchindex( &$db ) {
 130+ $write = array( 'searchindex' );
 131+ $read = array( 'page', 'revision', 'text', 'interwiki' );
 132+ $items = array();
 133+
 134+ foreach( $write as $table ) {
 135+ $items[] = $db->tableName( $table ) . ' LOW_PRIORITY WRITE';
 136+ }
 137+ foreach( $read as $table ) {
 138+ $items[] = $db->tableName( $table ) . ' READ';
 139+ }
 140+ $sql = "LOCK TABLES " . implode( ',', $items );
 141+ $db->query( $sql, 'updateSearchIndex.php ' . __METHOD__ );
 142+ }
 143+
 144+ /**
 145+ * Unlock the tables
 146+ * @param &$db Database object
 147+ */
 148+ private function unlockSearchindex( &$db ) {
 149+ $db->query( "UNLOCK TABLES", 'updateSearchIndex.php ' . __METHOD__ );
 150+ }
 151+
 152+ /**
 153+ * Unlock and lock again
 154+ * Since the lock is low-priority, queued reads will be able to complete
 155+ * @param &$db Database object
 156+ */
 157+ private function relockSearchindex( &$db ) {
 158+ $this->unlockSearchindex( $db );
 159+ $this->lockSearchindex( $db );
 160+ }
 161+}
 162+
 163+$maintClass = "UpdateSearchIndex";
 164+require_once( DO_MAINTENANCE );
Index: branches/maintenance-work/maintenance/clear_interwiki_cache.php
@@ -3,25 +3,36 @@
44 * This script is used to clear the interwiki links for ALL languages in
55 * memcached.
66 *
7 - * @file
87 * @ingroup Maintenance
98 */
109
11 -/** */
12 -require_once('commandLine.inc');
 10+require_once( "Maintenance.php" );
1311
14 -$dbr = wfGetDB( DB_SLAVE );
15 -$res = $dbr->select( 'interwiki', array( 'iw_prefix' ), false );
16 -$prefixes = array();
17 -while ( $row = $dbr->fetchObject( $res ) ) {
18 - $prefixes[] = $row->iw_prefix;
19 -}
 12+class ClearInterwikiCache extends Maintenance {
2013
21 -foreach ( $wgLocalDatabases as $db ) {
22 - print "$db ";
23 - foreach ( $prefixes as $prefix ) {
24 - $wgMemc->delete("$db:interwiki:$prefix");
 14+ public function __construct() {
 15+ parent::__construct();
 16+ $this->mDescription = "Clear all interwiki links for all languages from the cache";
2517 }
 18+
 19+ public function execute() {
 20+ global $wgLocalDatabases;
 21+ $dbr = wfGetDB( DB_SLAVE );
 22+ $res = $dbr->select( 'interwiki', array( 'iw_prefix' ), false );
 23+ $prefixes = array();
 24+ while ( $row = $dbr->fetchObject( $res ) ) {
 25+ $prefixes[] = $row->iw_prefix;
 26+ }
 27+
 28+ foreach ( $wgLocalDatabases as $db ) {
 29+ $this->output( "$db..." );
 30+ foreach ( $prefixes as $prefix ) {
 31+ $wgMemc->delete("$db:interwiki:$prefix");
 32+ }
 33+ $this->output( "done\n" );
 34+ }
 35+ }
2636 }
27 -print "\n";
2837
 38+$maintClass = "ClearInterwikiCache";
 39+require_once( DO_MAINTENANCE );
Index: branches/maintenance-work/maintenance/purgeOldText.php
@@ -1,29 +1,24 @@
22 <?php
3 -
43 /**
54 * Purge old text records from the database
65 *
7 - * @file
86 * @ingroup Maintenance
97 * @author Rob Church <robchur@gmail.com>
108 */
119
12 -$options = array( 'purge', 'help' );
13 -require_once( 'commandLine.inc' );
14 -require_once( 'purgeOldText.inc' );
 10+require_once( "Maintenance.php" );
1511
16 -echo( "Purge Old Text\n\n" );
17 -
18 -if( @$options['help'] ) {
19 - ShowUsage();
20 -} else {
21 - PurgeRedundantText( @$options['purge'] );
 12+class PurgeOldText extends Maintenance {
 13+ public function __construct() {
 14+ parent::__construct();
 15+ $this->mDescription = "Purge old text records from the database";
 16+ $this->addOption( 'purge', 'Performs the deletion' );
 17+ }
 18+
 19+ public function execute() {
 20+ $this->purgeRedundantText( $this->hasOption('purge') );
 21+ }
2222 }
2323
24 -function ShowUsage() {
25 - echo( "Prunes unused text records from the database.\n\n" );
26 - echo( "Usage: php purgeOldText.php [--purge]\n\n" );
27 - echo( "purge : Performs the deletion\n" );
28 - echo( " help : Show this usage information\n" );
29 -}
30 -
 24+$maintClass = "PurgeOldText";
 25+require_once( DO_MAINTENANCE );
Index: branches/maintenance-work/maintenance/checkImages.php
@@ -1,51 +1,61 @@
22 <?php
33
4 -require( 'commandLine.inc' );
 4+require_once( "Maintenance.php" );
55
6 -$batchSize = 1000;
7 -$start = '';
8 -$dbr = wfGetDB( DB_SLAVE );
9 -$localRepo = RepoGroup::singleton()->getLocalRepo();
 6+class CheckImages extends Maintenance {
107
11 -$numImages = 0;
12 -$numGood = 0;
 8+ public function __construct() {
 9+ parent::__construct();
 10+ $this->mDescription = "Check images to see if they exist, are readable, etc";
 11+ }
 12+
 13+ public function execute() {
 14+ $batchSize = 1000;
 15+ $start = '';
 16+ $dbr = wfGetDB( DB_SLAVE );
1317
14 -do {
15 - $res = $dbr->select( 'image', '*', array( 'img_name > ' . $dbr->addQuotes( $start ) ),
16 - 'checkImages.php', array( 'LIMIT' => $batchSize ) );
17 - foreach ( $res as $row ) {
18 - $numImages++;
19 - $start = $row->img_name;
20 - $file = $localRepo->newFileFromRow( $row );
21 - $path = $file->getPath();
22 - if ( !$path ) {
23 - echo "{$row->img_name}: not locally accessible\n";
24 - continue;
25 - }
26 - $stat = @stat( $file->getPath() );
27 - if ( !$stat ) {
28 - echo "{$row->img_name}: missing\n";
29 - continue;
30 - }
31 -
32 - if ( $stat['mode'] & 040000 ) {
33 - echo "{$row->img_name}: is a directory\n";
34 - continue;
35 - }
36 -
37 - if ( $stat['size'] == 0 && $row->img_size != 0 ) {
38 - echo "{$row->img_name}: truncated, was {$row->img_size}\n";
39 - continue;
40 - }
41 -
42 - if ( $stat['size'] != $row->img_size ) {
43 - echo "{$row->img_name}: size mismatch DB={$row->img_size}, actual={$stat['size']}\n";
44 - continue;
45 - }
46 -
47 - $numGood++;
 18+ $numImages = 0;
 19+ $numGood = 0;
 20+
 21+ do {
 22+ $res = $dbr->select( 'image', '*', array( 'img_name > ' . $dbr->addQuotes( $start ) ),
 23+ __METHOD__, array( 'LIMIT' => $batchSize ) );
 24+ foreach ( $res as $row ) {
 25+ $numImages++;
 26+ $start = $row->img_name;
 27+ $file = RepoGroup::singleton()->getLocalRepo()->newFileFromRow( $row );
 28+ $path = $file->getPath();
 29+ if ( !$path ) {
 30+ $this->output( "{$row->img_name}: not locally accessible\n";
 31+ continue;
 32+ }
 33+ $stat = @stat( $file->getPath() );
 34+ if ( !$stat ) {
 35+ $this->output( "{$row->img_name}: missing\n" );
 36+ continue;
 37+ }
 38+
 39+ if ( $stat['mode'] & 040000 ) {
 40+ $this->output( "{$row->img_name}: is a directory\n" );
 41+ continue;
 42+ }
 43+
 44+ if ( $stat['size'] == 0 && $row->img_size != 0 ) {
 45+ $this->output( "{$row->img_name}: truncated, was {$row->img_size}\n" );
 46+ continue;
 47+ }
 48+
 49+ if ( $stat['size'] != $row->img_size ) {
 50+ $this->output( "{$row->img_name}: size mismatch DB={$row->img_size}, actual={$stat['size']}\n" );
 51+ continue;
 52+ }
 53+
 54+ $numGood++;
 55+ }
 56+
 57+ } while ( $res->numRows() );
 58+
 59+ $this->output( "Good images: $numGood/$numImages\n" );
4860 }
 61+}
4962
50 -} while ( $res->numRows() );
51 -
52 -echo "Good images: $numGood/$numImages\n";
Index: branches/maintenance-work/maintenance/Maintenance.php
@@ -155,8 +155,8 @@
156156 */
157157 protected function error( $err, $die = false ) {
158158 $f = fopen( 'php://stderr', 'w' );
159 - fwrite( $f, $err );
160 - fclose( $f );
 159+ fwrite( $f, $err );
 160+ fclose( $f );
161161 if( $die ) die();
162162 }
163163
@@ -326,6 +326,11 @@
327327 $this->error( "Param $opt required.\n", true );
328328 }
329329 }
 330+
 331+ # Also make sure we've got enough arguments
 332+ if ( count( $args ) < count( $this->mArgList ) ) {
 333+ $this->error( "Not enough arguments passed", true );
 334+ }
330335
331336 $this->mOptions = $options;
332337 $this->mArgs = $args;
@@ -333,9 +338,10 @@
334339
335340 /**
336341 * Maybe show the help.
 342+ * @param $force boolean Whether to force the help to show, default false
337343 */
338 - private function maybeHelp() {
339 - if( $this->hasOption('help') || in_array( 'help', $this->mArgs ) ) {
 344+ private function maybeHelp( $force = false ) {
 345+ if( $this->hasOption('help') || in_array( 'help', $this->mArgs ) || $force ) {
340346 $this->mQuiet = false;
341347 if( $this->mDescription ) {
342348 $this->output( $this->mDescription . "\n" );
@@ -484,4 +490,62 @@
485491 $this->finalSetup();
486492 return $settingsFile;
487493 }
488 -}
\ No newline at end of file
 494+
 495+ /**
 496+ * Support function for cleaning up redundant text records
 497+ * @param $delete boolean Whether or not to actually delete the records
 498+ * @author Rob Church <robchur@gmail.com>
 499+ */
 500+ protected function purgeRedundantText( $delete = true ) {
 501+ # Data should come off the master, wrapped in a transaction
 502+ $dbw = wfGetDB( DB_MASTER );
 503+ $dbw->begin();
 504+
 505+ $tbl_arc = $dbw->tableName( 'archive' );
 506+ $tbl_rev = $dbw->tableName( 'revision' );
 507+ $tbl_txt = $dbw->tableName( 'text' );
 508+
 509+ # Get "active" text records from the revisions table
 510+ $this->output( "Searching for active text records in revisions table..." );
 511+ $res = $dbw->query( "SELECT DISTINCT rev_text_id FROM $tbl_rev" );
 512+ while( $row = $dbw->fetchObject( $res ) ) {
 513+ $cur[] = $row->rev_text_id;
 514+ }
 515+ $this->output( "done.\n" );
 516+
 517+ # Get "active" text records from the archive table
 518+ $this->output( "Searching for active text records in archive table..." );
 519+ $res = $dbw->query( "SELECT DISTINCT ar_text_id FROM $tbl_arc" );
 520+ while( $row = $dbw->fetchObject( $res ) ) {
 521+ $cur[] = $row->ar_text_id;
 522+ }
 523+ $this->output( "done.\n" );
 524+
 525+ # Get the IDs of all text records not in these sets
 526+ $this->output( "Searching for inactive text records..." );
 527+ $set = implode( ', ', $cur );
 528+ $res = $dbw->query( "SELECT old_id FROM $tbl_txt WHERE old_id NOT IN ( $set )" );
 529+ $old = array();
 530+ while( $row = $dbw->fetchObject( $res ) ) {
 531+ $old[] = $row->old_id;
 532+ }
 533+ $this->output( "done.\n" );
 534+
 535+ # Inform the user of what we're going to do
 536+ $count = count( $old );
 537+ $this->output( "$count inactive items found.\n" );
 538+
 539+ # Delete as appropriate
 540+ if( $delete && $count ) {
 541+ $this->output( "Deleting..." );
 542+ $set = implode( ', ', $old );
 543+ $dbw->query( "DELETE FROM $tbl_txt WHERE old_id IN ( $set )" );
 544+ $this->output( "done.\n" );
 545+ }
 546+
 547+ # Done
 548+ $dbw->commit();
 549+
 550+ }
 551+}
 552+
Index: branches/maintenance-work/maintenance/reassignEdits.php
@@ -1,56 +1,153 @@
22 <?php
3 -
43 /**
54 * Reassign edits from a user or IP address to another user
65 *
7 - * @file
86 * @ingroup Maintenance
97 * @author Rob Church <robchur@gmail.com>
108 * @licence GNU General Public Licence 2.0 or later
119 */
1210
13 -$options = array( 'force', 'norc', 'quiet', 'report' );
14 -require_once( 'commandLine.inc' );
15 -require_once( 'reassignEdits.inc' );
 11+require_once( "Maintenance.php" );
1612
17 -# Set silent mode; --report overrides --quiet
18 -if( !@$options['report'] && @$options['quiet'] )
19 - setSilent();
 13+class ReassignEdits extends Maintenance {
 14+ public function __construct() {
 15+ parent::__construct();
 16+ $this->mDescription = "Reassign edits from one user to another";
 17+ $this->addParam( "force", "Reassign even if the target user doesn't exist" );
 18+ $this->addParam( "norc", "Don't update the recent changes table" );
 19+ $this->addParam( "report", "Print out details of what would be changed, but don't update it" );
 20+ $this->addArgs( array( 'from', 'to' ) );
 21+ }
2022
21 -out( "Reassign Edits\n\n" );
 23+ public function execute() {
 24+ if( $this->hasArg(0) && $this->hasArg(1) ) {
 25+ # Set up the users involved
 26+ $from =& $this->initialiseUser( $this->getArg(0) );
 27+ $to =& $this->initialiseUser( $this->getArg(1) );
 28+
 29+ # If the target doesn't exist, and --force is not set, stop here
 30+ if( $to->getId() || $this->hasOption('force') ) {
 31+ # Reassign the edits
 32+ $report = $this->hasOption('report');
 33+ $count = reassignEdits( $from, $to, !$this->hasOption('norc'), $report );
 34+ # If reporting, and there were items, advise the user to run without --report
 35+ if( $report )
 36+ $this->output( "Run the script again without --report to update.\n" );
 37+ } else {
 38+ $ton = $to->getName();
 39+ $this->error( "User '{$ton}' not found.\n" );
 40+ }
 41+ }
 42+ }
2243
23 -if( @$args[0] && @$args[1] ) {
 44+ /**
 45+ * Reassign edits from one user to another
 46+ *
 47+ * @param $from User to take edits from
 48+ * @param $to User to assign edits to
 49+ * @param $rc Update the recent changes table
 50+ * @param $report Don't change things; just echo numbers
 51+ * @return integer Number of entries changed, or that would be changed
 52+ */
 53+ private function reassignEdits( &$from, &$to, $rc = false, $report = false ) {
 54+ $dbw = wfGetDB( DB_MASTER );
 55+ $dbw->immediateBegin();
2456
25 - # Set up the users involved
26 - $from =& initialiseUser( $args[0] );
27 - $to =& initialiseUser( $args[1] );
 57+ # Count things
 58+ $this->output( "Checking current edits..." );
 59+ $res = $dbw->select( 'revision', 'COUNT(*) AS count', $this->userConditions( $from, 'rev_user', 'rev_user_text' ), __METHOD__ );
 60+ $row = $dbw->fetchObject( $res );
 61+ $cur = $row->count;
 62+ $this->output( "found {$cur}.\n" );
 63+
 64+ $this->output( "Checking deleted edits..." );
 65+ $res = $dbw->select( 'archive', 'COUNT(*) AS count', $this->userConditions( $from, 'ar_user', 'ar_user_text' ), __METHOD__ );
 66+ $row = $dbw->fetchObject( $res );
 67+ $del = $row->count;
 68+ $this->output( "found {$del}.\n" );
 69+
 70+ # Don't count recent changes if we're not supposed to
 71+ if( $rc ) {
 72+ $this->output( "Checking recent changes..." );
 73+ $res = $dbw->select( 'recentchanges', 'COUNT(*) AS count', $this->userConditions( $from, 'rc_user', 'rc_user_text' ), __METHOD__ );
 74+ $row = $dbw->fetchObject( $res );
 75+ $rec = $row->count;
 76+ $this->output( "found {$rec}.\n" );
 77+ } else {
 78+ $rec = 0;
 79+ }
2880
29 - # If the target doesn't exist, and --force is not set, stop here
30 - if( $to->getId() || @$options['force'] ) {
31 - # Reassign the edits
32 - $report = @$options['report'];
33 - $count = reassignEdits( $from, $to, !@$options['norc'], $report );
34 - # If reporting, and there were items, advise the user to run without --report
35 - if( $report )
36 - out( "Run the script again without --report to update.\n" );
37 - } else {
38 - $ton = $to->getName();
39 - echo( "User '{$ton}' not found.\n" );
 81+ $total = $cur + $del + $rec;
 82+ $this->output( "\nTotal entries to change: {$total}\n" );
 83+
 84+ if( !$report ) {
 85+ if( $total ) {
 86+ # Reassign edits
 87+ $this->output( "\nReassigning current edits..." );
 88+ $res = $dbw->update( 'revision', userSpecification( $to, 'rev_user', 'rev_user_text' ), $this->userConditions( $from, 'rev_user', 'rev_user_text' ), __METHOD__ );
 89+ $this->output( "done.\nReassigning deleted edits..." );
 90+ $res = $dbw->update( 'archive', userSpecification( $to, 'ar_user', 'ar_user_text' ), $this->userConditions( $from, 'ar_user', 'ar_user_text' ), __METHOD__ );
 91+ $this->output( "done.\n" );
 92+ # Update recent changes if required
 93+ if( $rc ) {
 94+ $this->output( "Updating recent changes..." );
 95+ $res = $dbw->update( 'recentchanges', $this->userSpecification( $to, 'rc_user', 'rc_user_text' ), $this->userConditions( $from, 'rc_user', 'rc_user_text' ), __METHOD__ );
 96+ $this->output( "done.\n" );
 97+ }
 98+ }
 99+ }
 100+
 101+ $dbw->immediateCommit();
 102+ return (int)$total;
40103 }
41104
42 -} else {
43 - ShowUsage();
44 -}
 105+ /**
 106+ * Return the most efficient set of user conditions
 107+ * i.e. a user => id mapping, or a user_text => text mapping
 108+ *
 109+ * @param $user User for the condition
 110+ * @param $idfield Field name containing the identifier
 111+ * @param $utfield Field name containing the user text
 112+ * @return array
 113+ */
 114+ private function userConditions( &$user, $idfield, $utfield ) {
 115+ return $user->getId() ? array( $idfield => $user->getId() ) : array( $utfield => $user->getName() );
 116+ }
 117+
 118+ /**
 119+ * Return user specifications
 120+ * i.e. user => id, user_text => text
 121+ *
 122+ * @param $user User for the spec
 123+ * @param $idfield Field name containing the identifier
 124+ * @param $utfield Field name containing the user text
 125+ * @return array
 126+ */
 127+ private function userSpecification( &$user, $idfield, $utfield ) {
 128+ return array( $idfield => $user->getId(), $utfield => $user->getName() );
 129+ }
 130+
 131+ /**
 132+ * Initialise the user object
 133+ *
 134+ * @param $username Username or IP address
 135+ * @return User
 136+ */
 137+ private function initialiseUser( $username ) {
 138+ if( User::isIP( $username ) ) {
 139+ $user = new User();
 140+ $user->setId( 0 );
 141+ $user->setName( $username );
 142+ } else {
 143+ $user = User::newFromName( $username );
 144+ }
 145+ $user->load();
 146+ return $user;
 147+ }
45148
46 -/** Show script usage information */
47 -function ShowUsage() {
48 - echo( "Reassign edits from one user to another.\n\n" );
49 - echo( "Usage: php reassignEdits.php [--force|--quiet|--norc|--report] <from> <to>\n\n" );
50 - echo( " <from> : Name of the user to assign edits from\n" );
51 - echo( " <to> : Name of the user to assign edits to\n" );
52 - echo( " --force : Reassign even if the target user doesn't exist\n" );
53 - echo( " --quiet : Don't print status information (except for errors)\n" );
54 - echo( " --norc : Don't update the recent changes table\n" );
55 - echo( " --report : Print out details of what would be changed, but don't update it\n\n" );
 149+
56150 }
57151
 152+$maintClass = "ReassignEdits";
 153+require_once( DO_MAINTENANCE );
 154+
Index: branches/maintenance-work/t/Search.inc
@@ -10,7 +10,7 @@
1111 require 'includes/Setup.php';
1212
1313 function buildTestDatabase( $tables ) {
14 - global $wgDBprefix, $wgDBserver, $wgDBadminuser, $wgDBadminpassword, $wgDBname, $wgDBtype;
 14+ global $wgDBprefix, $wgDBserver, $wgDBname, $wgDBtype;
1515 $oldPrefix = $wgDBprefix;
1616 $wgDBprefix = 'parsertest';
1717

Status & tagging log