From b817c0c15fc8e7cf483c21089834f65d716d9786 Mon Sep 17 00:00:00 2001 From: River Tarnell Date: Tue, 2 Aug 2005 13:35:19 +0000 Subject: [PATCH] merge ORACLE_WORK. sorry, this may break some parts of MySQL, i did not test extensively. --- config/index.php | 192 +++-- includes/Article.php | 11 +- includes/BagOStuff.php | 149 ++-- includes/Block.php | 72 +- includes/CategoryPage.php | 34 +- includes/Database.php | 345 +++++---- includes/DatabaseFunctions.php | 86 +-- includes/DatabaseOracle.php | 706 ++++++++++++++++++ includes/EditPage.php | 80 +- includes/GlobalFunctions.php | 85 ++- includes/ImagePage.php | 6 +- includes/LinkCache.php | 55 +- includes/LoadBalancer.php | 53 +- includes/LogPage.php | 34 +- includes/Namespace.php | 12 +- includes/ObjectCache.php | 8 +- includes/PageHistory.php | 38 +- includes/QueryPage.php | 22 +- includes/RecentChange.php | 8 +- includes/Revision.php | 119 +-- includes/SearchEngine.php | 40 +- includes/SiteStatsUpdate.php | 10 +- includes/Skin.php | 4 +- includes/SkinTemplate.php | 6 +- includes/SpecialAllpages.php | 9 +- includes/SpecialContributions.php | 19 +- includes/SpecialLog.php | 71 +- includes/SpecialPreferences.php | 4 +- includes/SpecialRandompage.php | 16 +- includes/SpecialRecentchanges.php | 57 +- includes/SpecialUndelete.php | 68 +- includes/Title.php | 8 +- includes/User.php | 4 +- includes/UserMailer.php | 76 +- includes/WatchedItem.php | 24 +- install-utils.inc | 15 +- maintenance/InitialiseMessages.inc | 54 +- maintenance/importDump.php | 39 +- .../oracle/archives/patch-transcache.sql | 5 + maintenance/oracle/interwiki.sql | 178 +++++ maintenance/oracle/tables.sql | 344 +++++++++ maintenance/update.php | 9 +- maintenance/updaters.inc | 99 +-- 43 files changed, 2352 insertions(+), 922 deletions(-) create mode 100644 includes/DatabaseOracle.php create mode 100644 maintenance/oracle/archives/patch-transcache.sql create mode 100644 maintenance/oracle/interwiki.sql create mode 100644 maintenance/oracle/tables.sql diff --git a/config/index.php b/config/index.php index bfabc7ac67..768ab3a4ac 100644 --- a/config/index.php +++ b/config/index.php @@ -367,6 +367,7 @@ print "
  • Script URI path: " . htmlspecialchars( $conf->ScriptPath ) . "EmergencyContact = importPost( "EmergencyContact", $defaultEmail ); + $conf->DBtype = importPost( "DBtype", "mysql" ); $conf->DBserver = importPost( "DBserver", "localhost" ); $conf->DBname = importPost( "DBname", "wikidb" ); $conf->DBuser = importPost( "DBuser", "wikiuser" ); @@ -453,6 +454,14 @@ if( $conf->posted && ( 0 == count( $errs ) ) ) { $wgCommandLineMode = false; chdir( ".." ); eval($local); + if (!in_array($conf->DBtype, array("mysql", "oracle"))) { + $errs["DBtype"] = "Unknown database type."; + continue; + } + print "
  • Database type: {$conf->DBtype}
  • \n"; + $dbclass = 'Database'.ucfirst($conf->DBtype); + require_once("$dbclass.php"); + $wgDBtype = $conf->DBtype; $wgDBadminuser = "root"; $wgDBadminpassword = $conf->RootPW; $wgDBprefix = $conf->DBprefix; @@ -471,58 +480,70 @@ if( $conf->posted && ( 0 == count( $errs ) ) ) { see http://dev.mysql.com/doc/mysql/en/old-client.html for help.\n"; } - print "
  • Trying to connect to MySQL on $wgDBserver as root...\n"; - $wgDatabase = Database::newFromParams( $wgDBserver, "root", $conf->RootPW, "", 1 ); - - if( $wgDatabase->isOpen() ) { - $myver = mysql_get_server_info( $wgDatabase->mConn ); - $wgDatabase->ignoreErrors(true); - $conf->Root = true; - print "
    • Connected as root (automatic)
  • \n"; - } else { - print ""; - $ok = false; - switch( $err ) { - case 1045: - case 2000: - if( $conf->Root ) { - $errs["RootPW"] = "Check password"; - } else { - print "
  • Trying regular user...\n"; - /* Try the regular user... */ - $wgDBadminuser = $wgDBuser; - $wgDBadminpassword = $wgDBpassword; - /* Wait one second for connection rate limiting, present on some systems */ - sleep(1); - $wgDatabase = Database::newFromParams( $wgDBserver, $wgDBuser, $wgDBpassword, "", 1 ); - if( !$wgDatabase->isOpen() ) { - print "
    • MySQL error " . ($err = mysql_errno() ) . - ": " . htmlspecialchars( mysql_error() ) . "
  • "; - $errs["DBuser"] = "Check name/pass"; - $errs["DBpassword"] = "or enter root"; - $errs["DBpassword2"] = "password below"; - $errs["RootPW"] = "Got root?"; + $dbc = new $dbclass; + if ($conf->DBtype == 'mysql') { + print "
  • Trying to connect to database server on $wgDBserver as root...\n"; + $wgDatabase = $dbc->newFromParams( $wgDBserver, "root", $conf->RootPW, "", 1 ); + + if( $wgDatabase->isOpen() ) { + $myver = get_db_version(); + $wgDatabase->ignoreErrors(true); + $conf->Root = true; + print "
    • Connected as root (automatic)
  • \n"; + } else { + print ""; + $ok = false; + switch( $err ) { + case 1045: + case 2000: + if( $conf->Root ) { + $errs["RootPW"] = "Check password"; } else { - $myver = mysql_get_server_info( $wgDatabase->mConn ); - $wgDatabase->ignoreErrors(true); - $conf->Root = false; - $conf->RootPW = ""; - print " ok.\n"; - # And keep going... - $ok = true; + print "
  • Trying regular user...\n"; + /* Try the regular user... */ + $wgDBadminuser = $wgDBuser; + $wgDBadminpassword = $wgDBpassword; + /* Wait one second for connection rate limiting, present on some systems */ + sleep(1); + $wgDatabase = Database::newFromParams( $wgDBserver, $wgDBuser, $wgDBpassword, "", 1 ); + if( !$wgDatabase->isOpen() ) { + print "
    • MySQL error " . ($err = mysql_errno() ) . + ": " . htmlspecialchars( mysql_error() ) . "
  • "; + $errs["DBuser"] = "Check name/pass"; + $errs["DBpassword"] = "or enter root"; + $errs["DBpassword2"] = "password below"; + $errs["RootPW"] = "Got root?"; + } else { + $myver = mysql_get_server_info( $wgDatabase->mConn ); + $wgDatabase->ignoreErrors(true); + $conf->Root = false; + $conf->RootPW = ""; + print " ok.\n"; + # And keep going... + $ok = true; + } + break; } + case 2002: + case 2003: + $errs["DBserver"] = "Connection failed"; + break; + default: + $errs["DBserver"] = "Couldn't connect to database"; break; } - case 2002: - case 2003: - $errs["DBserver"] = "Connection failed"; - break; - default: - $errs["DBserver"] = "Couldn't connect to database"; - break; + if( !$ok ) continue; + } + } else /* not mysql */ { + print "
  • Connecting to SQL server..."; + $wgDatabase = $dbc->newFromParams($wgDBserver, $wgDBuser, $wgDBpassword, $wgDBname, 1); + if (!$wgDatabase->isOpen()) { + print " error: " . $wgDatabase->lastError() . "
  • \n"; + } else { + $wgDatabase->ignoreErrors(true); + $myver = get_db_version(); } - if( !$ok ) continue; } if ( !$wgDatabase->isOpen() ) { @@ -545,28 +566,30 @@ if( $conf->posted && ( 0 == count( $errs ) ) ) { } print "\n"; - @$sel = mysql_select_db( $wgDBname, $wgDatabase->mConn ); - if( $sel ) { - print "
  • Database " . htmlspecialchars( $wgDBname ) . " exists
  • \n"; - } else { - $err = mysql_errno(); - if ( $err != 1049 ) { - print ""; - continue; - } - $res = $wgDatabase->query( "CREATE DATABASE `$wgDBname`" ); - if( !$res ) { - print "
  • Couldn't create database " . - htmlspecialchars( $wgDBname ) . - "; try with root access or check your username/pass.
  • \n"; - $errs["RootPW"] = "<- Enter"; - continue; + if ($conf->DBtype == 'mysql') { + @$sel = mysql_select_db( $wgDBname, $wgDatabase->mConn ); + if( $sel ) { + print "
  • Database " . htmlspecialchars( $wgDBname ) . " exists
  • \n"; + } else { + $err = mysql_errno(); + if ( $err != 1049 ) { + print ""; + continue; + } + $res = $wgDatabase->query( "CREATE DATABASE `$wgDBname`" ); + if( !$res ) { + print "
  • Couldn't create database " . + htmlspecialchars( $wgDBname ) . + "; try with root access or check your username/pass.
  • \n"; + $errs["RootPW"] = "<- Enter"; + continue; + } + print "
  • Created database " . htmlspecialchars( $wgDBname ) . "
  • \n"; } - print "
  • Created database " . htmlspecialchars( $wgDBname ) . "
  • \n"; + $wgDatabase->selectDB( $wgDBname ); } - $wgDatabase->selectDB( $wgDBname ); if( $wgDatabase->tableExists( "cur" ) || $wgDatabase->tableExists( "revision" ) ) { print "
  • There are already MediaWiki tables in this database. Checking if updates are needed...
  • \n"; @@ -597,8 +620,14 @@ if( $conf->posted && ( 0 == count( $errs ) ) ) { } else { # FIXME: Check for errors print "
  • Creating tables..."; - dbsource( "../maintenance/tables.sql", $wgDatabase ); - dbsource( "../maintenance/interwiki.sql", $wgDatabase ); + if ($conf->DBtype == 'mysql') { + dbsource( "../maintenance/tables.sql", $wgDatabase ); + dbsource( "../maintenance/interwiki.sql", $wgDatabase ); + } else { + dbsource( "../maintenance/oracle/tables.sql", $wgDatabase ); + dbsource( "../maintenance/oracle/interwiki.sql", $wgDatabase ); + } + print " done.
  • \n"; print "
  • Initializing data..."; @@ -902,17 +931,31 @@ if( count( $errs ) ) {

    Database config

    +
    + +
    Select the database server software:
    +
      + +
    +
    +
    If your database server isn't on your web server, enter the name - or IP address here. + or IP address here. MySQL only.
    +
    + If using Oracle, set this to your connection identifier. +
    @@ -1131,6 +1174,7 @@ if ( \$wgCommandLineMode ) { \$wgDBuser = \"{$slconf['DBuser']}\"; \$wgDBpassword = \"{$slconf['DBpassword']}\"; \$wgDBprefix = \"{$slconf['DBprefix']}\"; +\$wgDBtype = \"{$slconf['DBtype']}\"; # If you're on MySQL 3.x, this next line must be FALSE: \$wgDBmysql4 = {$conf->DBmysql4}; @@ -1294,6 +1338,14 @@ function locate_executable($loc, $names, $versioninfo = false) { return false; } +function get_db_version() { + global $wgDatabase, $conf; + if ($conf->DBtype == 'mysql') + return mysql_get_server_info( $wgDatabase->mConn ); + else if ($conf->DBtype == 'oracle') + return oci_server_version($wgDatabase->mConn); +} + # Test a memcached server function testMemcachedServer( $server ) { $hostport = explode(":", $server); diff --git a/includes/Article.php b/includes/Article.php index e0bd2016f2..7242d741ba 100644 --- a/includes/Article.php +++ b/includes/Article.php @@ -486,6 +486,8 @@ class Article { * Get the database which should be used for reads */ function &getDB() { + $ret = wfGetDB( DB_MASTER ); + return $ret; #if ( $this->mForUpdate ) { $ret =& wfGetDB( DB_MASTER ); #} else { @@ -608,7 +610,7 @@ class Article { function getTimestamp() { $this->loadLastEdit(); - return $this->mTimestamp; + return wfTimestamp(TS_MW, $this->mTimestamp); } function getUser() { @@ -864,7 +866,7 @@ class Article { $tbtext = ""; while ($o = $dbr->fetchObject($tbs)) { - $rmvtext = ""; + $rmvtxt = ""; if ($wgUser->isSysop()) { $delurl = $this->mTitle->getFullURL("action=deletetrackback&tbid=" . $o->tb_id . "&token=" . $wgUser->editToken()); @@ -970,13 +972,14 @@ class Article { # An extra check against threads stepping on each other $conditions['page_latest'] = $lastRevision; } + $text = $revision->getText(); $dbw->update( 'page', array( /* SET */ 'page_latest' => $revision->getId(), 'page_touched' => $dbw->timestamp(), 'page_is_new' => ($lastRevision === 0) ? 1 : 0, - 'page_is_redirect' => Article::isRedirect( $text ), + 'page_is_redirect' => Article::isRedirect( $text ) ? 1 : 0, 'page_len' => strlen( $text ), ), $conditions, @@ -1005,7 +1008,7 @@ class Article { 'page_latest=rev_id' ), $fname ); if( $row ) { - if( $row->rev_timestamp >= $revision->getTimestamp() ) { + if( wfTimestamp(TS_MW, $row->rev_timestamp) >= $revision->getTimestamp() ) { wfProfileOut( $fname ); return false; } diff --git a/includes/BagOStuff.php b/includes/BagOStuff.php index 662e32c9b9..7e48fb04a7 100644 --- a/includes/BagOStuff.php +++ b/includes/BagOStuff.php @@ -2,17 +2,17 @@ # # Copyright (C) 2003-2004 Brion Vibber # http://www.mediawiki.org/ -# +# # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or +# the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. @@ -21,7 +21,7 @@ * * @package MediaWiki */ - + /** * Simple generic object store * @@ -37,18 +37,18 @@ */ class BagOStuff { var $debugmode; - + function BagOStuff() { $this->set_debug( false ); } - + function set_debug($bool) { $this->debugmode = $bool; } - + /* *** THE GUTS OF THE OPERATION *** */ /* Override these with functional things in subclasses */ - + function get($key) { /* stub */ return false; @@ -58,12 +58,12 @@ class BagOStuff { /* stub */ return false; } - + function delete($key, $time=0) { /* stub */ return false; } - + function lock($key, $timeout = 0) { /* stub */ return true; @@ -73,7 +73,7 @@ class BagOStuff { /* stub */ return true; } - + /* *** Emulated functions *** */ /* Better performance can likely be got with custom written versions */ function get_multi($keys) { @@ -82,19 +82,19 @@ class BagOStuff { $out[$key] = $this->get($key); return $out; } - + function set_multi($hash, $exptime=0) { foreach($hash as $key => $value) $this->set($key, $value, $exptime); } - + function add($key, $value, $exptime=0) { if( $this->get($key) == false ) { $this->set($key, $value, $exptime); return true; } } - + function add_multi($hash, $exptime=0) { foreach($hash as $key => $value) $this->add($key, $value, $exptime); @@ -104,12 +104,12 @@ class BagOStuff { foreach($keys as $key) $this->delete($key, $time); } - + function replace($key, $value, $exptime=0) { if( $this->get($key) !== false ) $this->set($key, $value, $exptime); } - + function incr($key, $value=1) { if ( !$this->lock($key) ) { return false; @@ -125,7 +125,7 @@ class BagOStuff { $this->unlock($key); return $n; } - + function decr($key, $value=1) { if ( !$this->lock($key) ) { return false; @@ -142,7 +142,7 @@ class BagOStuff { $this->unlock($key); return $m; } - + function _debug($text) { if($this->debugmode) wfDebug("BagOStuff debug: $text\n"); @@ -162,11 +162,11 @@ class HashBagOStuff extends BagOStuff { persist between program runs. */ var $bag; - + function HashBagOStuff() { $this->bag = array(); } - + function _expire($key) { $et = $this->bag[$key][1]; if(($et == 0) || ($et > time())) @@ -174,7 +174,7 @@ class HashBagOStuff extends BagOStuff { $this->delete($key); return true; } - + function get($key) { if(!$this->bag[$key]) return false; @@ -182,13 +182,13 @@ class HashBagOStuff extends BagOStuff { return false; return $this->bag[$key][0]; } - + function set($key,$value,$exptime=0) { if(($exptime != 0) && ($exptime < 3600*24*30)) $exptime = time() + $exptime; $this->bag[$key] = array( $value, $exptime ); } - + function delete($key,$time=0) { if(!$this->bag[$key]) return false; @@ -219,11 +219,11 @@ class SqlBagOStuff extends BagOStuff { function SqlBagOStuff($tablename = 'objectcache') { $this->table = $tablename; } - + function get($key) { /* expire old entries if any */ $this->garbageCollect(); - + $res = $this->_query( "SELECT value,exptime FROM $0 WHERE keyname='$1'", $key); if(!$res) { @@ -238,7 +238,7 @@ class SqlBagOStuff extends BagOStuff { } return false; } - + function set($key,$value,$exptime=0) { $exptime = intval($exptime); if($exptime < 0) $exptime = 0; @@ -250,22 +250,24 @@ class SqlBagOStuff extends BagOStuff { $exp = $this->_fromunixtime($exptime); } $this->delete( $key ); - $this->_query( - "INSERT INTO $0 (keyname,value,exptime) VALUES('$1','$2','$exp')", - $key, $this->_serialize($value)); + $this->_doinsert($this->getTableName(), array( + 'keyname' => $key, + 'value' => $this->_blobencode($this->_serialize($value)), + 'exptime' => $exp + )); return true; /* ? */ } - + function delete($key,$time=0) { $this->_query( "DELETE FROM $0 WHERE keyname='$1'", $key ); return true; /* ? */ } - + function getTableName() { return $this->table; } - + function _query($sql) { $reps = func_get_args(); $reps[0] = $this->getTableName(); @@ -273,7 +275,7 @@ class SqlBagOStuff extends BagOStuff { for($i=0;$i_strencode($reps[$i]), + $i > 0 ? $this->_strencode($reps[$i]) : $reps[$i], $sql); } $res = $this->_doquery($sql); @@ -282,38 +284,43 @@ class SqlBagOStuff extends BagOStuff { } return $res; } - + function _strencode($str) { /* Protect strings in SQL */ return str_replace( "'", "''", $str ); } - + function _blobencode($str) { + return $str; + } + function _doinsert($table, $vals) { + die( 'abstract function SqlBagOStuff::_doinsert() must be defined' ); + } function _doquery($sql) { die( 'abstract function SqlBagOStuff::_doquery() must be defined' ); } - + function _fetchrow($res) { die( 'abstract function SqlBagOStuff::_fetchrow() must be defined' ); } - + function _freeresult($result) { /* stub */ return false; } - + function _dberror($result) { /* stub */ return 'unknown error'; } - + function _maxdatetime() { die( 'abstract function SqlBagOStuff::_maxdatetime() must be defined' ); } - + function _fromunixtime() { die( 'abstract function SqlBagOStuff::_fromunixtime() must be defined' ); } - + function garbageCollect() { /* Ignore 99% of requests */ if ( !mt_rand( 0, 100 ) ) { @@ -325,18 +332,18 @@ class SqlBagOStuff extends BagOStuff { } } } - + function expireall() { /* Remove any items that have expired */ $now = $this->_fromunixtime( time() ); - $this->_query( "DELETE FROM $0 WHERE exptime<'$now'" ); + $this->_query( "DELETE FROM $0 WHERE exptime < '$now'" ); } - + function deleteall() { /* Clear *all* items from cache table */ $this->_query( "DELETE FROM $0" ); } - + /** * Serialize an object and, if possible, compress the representation. * On typical message and page data, this can provide a 3X decrease @@ -353,7 +360,7 @@ class SqlBagOStuff extends BagOStuff { return $serial; } } - + /** * Unserialize and, if necessary, decompress an object. * @param string $serial @@ -366,7 +373,9 @@ class SqlBagOStuff extends BagOStuff { $serial = $decomp; } } - return unserialize( $serial ); + wfdebug("serial: [$serial]\n"); + $ret = unserialize( $serial ); + return $ret; } } @@ -379,7 +388,11 @@ class MediaWikiBagOStuff extends SqlBagOStuff { function _doquery($sql) { $dbw =& wfGetDB( DB_MASTER ); - return $dbw->query($sql, 'MediaWikiBagOStuff:_doquery'); + return $dbw->query($sql, 'MediaWikiBagOStuff::_doquery'); + } + function _doinsert($t, $v) { + $dbw =& wfGetDB( DB_MASTER ); + return $dbw->insert($t, $v, 'MediaWikiBagOStuff::_doinsert'); } function _fetchobject($result) { $dbw =& wfGetDB( DB_MASTER ); @@ -394,15 +407,21 @@ class MediaWikiBagOStuff extends SqlBagOStuff { return $dbw->lastError(); } function _maxdatetime() { - return '9999-12-31 12:59:59'; + $dbw =& wfGetDB(DB_MASTER); + return $dbw->timestamp('9999-12-31 12:59:59'); } function _fromunixtime($ts) { - return gmdate( 'Y-m-d H:i:s', $ts ); + $dbw =& wfGetDB(DB_MASTER); + return $dbw->timestamp($ts); } function _strencode($s) { $dbw =& wfGetDB( DB_MASTER ); return $dbw->strencode($s); } + function _blobencode($s) { + $dbw =& wfGetDB( DB_MASTER ); + return $dbw->encodeBlob($s); + } function getTableName() { if ( !$this->tableInitialised ) { $dbw =& wfGetDB( DB_MASTER ); @@ -418,16 +437,16 @@ class MediaWikiBagOStuff extends SqlBagOStuff { } /** - * This is a wrapper for Turck MMCache's shared memory functions. - * - * You can store objects with mmcache_put() and mmcache_get(), but Turck seems - * to use a weird custom serializer that randomly segfaults. So we wrap calls + * This is a wrapper for Turck MMCache's shared memory functions. + * + * You can store objects with mmcache_put() and mmcache_get(), but Turck seems + * to use a weird custom serializer that randomly segfaults. So we wrap calls * with serialize()/unserialize(). - * + * * The thing I noticed about the Turck serialized data was that unlike ordinary - * serialize(), it contained the names of methods, and judging by the amount of - * binary data, perhaps even the bytecode of the methods themselves. It may be - * that Turck's serializer is faster, so a possible future extension would be + * serialize(), it contained the names of methods, and judging by the amount of + * binary data, perhaps even the bytecode of the methods themselves. It may be + * that Turck's serializer is faster, so a possible future extension would be * to use it for arrays but not for objects. * * @package MediaWiki @@ -445,7 +464,7 @@ class TurckBagOStuff extends BagOStuff { mmcache_put( $key, serialize( $value ), $exptime ); return true; } - + function delete($key, $time=0) { mmcache_rm( $key ); return true; @@ -460,11 +479,11 @@ class TurckBagOStuff extends BagOStuff { mmcache_unlock( $key ); return true; } -} +} /** - * This is a wrapper for eAccelerator's shared memory functions. - * + * This is a wrapper for eAccelerator's shared memory functions. + * * This is basically identical to the Turck MMCache version, * mostly because eAccelerator is based on Turck MMCache. * @@ -483,7 +502,7 @@ class eAccelBagOStuff extends BagOStuff { eaccelerator_put( $key, serialize( $value ), $exptime ); return true; } - + function delete($key, $time=0) { eaccelerator_rm( $key ); return true; @@ -498,5 +517,5 @@ class eAccelBagOStuff extends BagOStuff { eaccelerator_unlock( $key ); return true; } -} +} ?> diff --git a/includes/Block.php b/includes/Block.php index 0763133249..c4110d0e40 100644 --- a/includes/Block.php +++ b/includes/Block.php @@ -12,9 +12,9 @@ define ( 'EB_FOR_UPDATE', 2 ); /** * The block class - * All the functions in this class assume the object is either explicitly + * All the functions in this class assume the object is either explicitly * loaded or filled. It is not load-on-demand. There are no accessors. - * + * * To use delete(), you only need to fill $mAddress * Globals used: $wgBlockCache, $wgAutoblockExpiry * @@ -25,9 +25,9 @@ class Block { /* public*/ var $mAddress, $mUser, $mBy, $mReason, $mTimestamp, $mAuto, $mId, $mExpiry; /* private */ var $mNetworkBits, $mIntegerAddr, $mForUpdate; - - function Block( $address = '', $user = '', $by = 0, $reason = '', - $timestamp = '' , $auto = 0, $expiry = '' ) + + function Block( $address = '', $user = '', $by = 0, $reason = '', + $timestamp = '' , $auto = 0, $expiry = '' ) { $this->mAddress = $address; $this->mUser = $user; @@ -40,19 +40,19 @@ class Block } else { $this->mExpiry = wfTimestamp( TS_MW, $expiry ); } - + $this->mForUpdate = false; $this->initialiseRange(); } - - /*static*/ function newFromDB( $address, $user = 0, $killExpired = true ) + + /*static*/ function newFromDB( $address, $user = 0, $killExpired = true ) { $ban = new Block(); $ban->load( $address, $user, $killExpired ); return $ban; } - - function clear() + + function clear() { $mAddress = $mReason = $mTimestamp = ''; $mUser = $mBy = 0; @@ -61,7 +61,7 @@ class Block /** * Get a ban from the DB, with either the given address or the given username */ - function load( $address = '', $user = 0, $killExpired = true ) + function load( $address = '', $user = 0, $killExpired = true ) { global $wgDBmysql4, $wgAntiLockFlags; $fname = 'Block::load'; @@ -96,7 +96,7 @@ class Block } else { # If there are options, a UNION can not be used, use one # SELECT instead. Will do a full table scan. - $sql = "SELECT * FROM $ipblocks WHERE (ipb_address='" . $db->strencode( $address ) . + $sql = "SELECT * FROM $ipblocks WHERE (ipb_address='" . $db->strencode( $address ) . "' OR ipb_user={$user}) $options"; } @@ -120,7 +120,7 @@ class Block } } } while ( $killed && $row ); - + # If there were any left after the killing finished, return true if ( !$row ) { $ret = false; @@ -135,8 +135,8 @@ class Block $db->freeResult( $res ); return $ret; } - - function initFromRow( $row ) + + function initFromRow( $row ) { $this->mAddress = $row->ipb_address; $this->mReason = $row->ipb_reason; @@ -150,7 +150,7 @@ class Block $row->ipb_expiry; $this->initialiseRange(); - } + } function initialiseRange() { @@ -167,11 +167,11 @@ class Block $this->mIntegerAddr = false; } } - + /** * Callback with a Block object for every block */ - /*static*/ function enumBlocks( $callback, $tag, $flags = 0 ) + /*static*/ function enumBlocks( $callback, $tag, $flags = 0 ) { global $wgAntiLockFlags; @@ -187,9 +187,9 @@ class Block } else { $db =& wfGetDB( DB_SLAVE ); $options = ''; - } + } $ipblocks = $db->tableName( 'ipblocks' ); - + $sql = "SELECT * FROM $ipblocks ORDER BY ipb_timestamp DESC $options"; $res = $db->query( $sql, 'Block::enumBans' ); @@ -206,7 +206,7 @@ class Block wfFreeResult( $res ); } - function delete() + function delete() { $fname = 'Block::delete'; if (wfReadOnly()) { @@ -223,12 +223,14 @@ class Block $this->clearCache(); } - function insert() + function insert() { wfDebug( "Block::insert; timestamp {$this->mTimestamp}\n" ); $dbw =& wfGetDB( DB_MASTER ); + $ipb_id = $dbw->nextSequenceValue('ipblocks_ipb_id_val'); $dbw->insert( 'ipblocks', array( + 'ipb_id' => $ipb_id, 'ipb_address' => $this->mAddress, 'ipb_user' => $this->mUser, 'ipb_by' => $this->mBy, @@ -238,13 +240,13 @@ class Block 'ipb_expiry' => $this->mExpiry ? $dbw->timestamp($this->mExpiry) : $this->mExpiry, - ), 'Block::insert' + ), 'Block::insert' ); $this->clearCache(); } - function deleteIfExpired() + function deleteIfExpired() { if ( $this->isExpired() ) { wfDebug( "Block::deleteIfExpired() -- deleting\n" ); @@ -256,8 +258,8 @@ class Block } } - function isExpired() - { + function isExpired() + { wfDebug( "Block::isExpired() checking current " . wfTimestampNow() . " vs $this->mExpiry\n" ); if ( !$this->mExpiry ) { return false; @@ -266,27 +268,27 @@ class Block } } - function isValid() + function isValid() { return $this->mAddress != ''; } - - function updateTimestamp() + + function updateTimestamp() { if ( $this->mAuto ) { $this->mTimestamp = wfTimestamp(); $this->mExpiry = Block::getAutoblockExpiry( $this->mTimestamp ); $dbw =& wfGetDB( DB_MASTER ); - $dbw->update( 'ipblocks', - array( /* SET */ + $dbw->update( 'ipblocks', + array( /* SET */ 'ipb_timestamp' => $dbw->timestamp($this->mTimestamp), 'ipb_expiry' => $dbw->timestamp($this->mExpiry), ), array( /* WHERE */ 'ipb_address' => $this->mAddress - ), 'Block::updateTimestamp' + ), 'Block::updateTimestamp' ); - + $this->clearCache(); } } @@ -298,12 +300,12 @@ class Block $wgBlockCache->loadFromDB(); } } - + function getIntegerAddr() { return $this->mIntegerAddr; } - + function getNetworkBits() { return $this->mNetworkBits; diff --git a/includes/CategoryPage.php b/includes/CategoryPage.php index bb6314f376..e5c11e822c 100644 --- a/includes/CategoryPage.php +++ b/includes/CategoryPage.php @@ -5,7 +5,7 @@ * * @package MediaWiki */ - + if( !defined( 'MEDIAWIKI' ) ) die(); @@ -15,7 +15,7 @@ if( $wgCategoryMagicGallery ) require_once('ImageGallery.php'); /** - * @package MediaWiki + * @package MediaWiki */ class CategoryPage extends Article { @@ -27,7 +27,7 @@ class CategoryPage extends Article { } Article::view(); - + # If the article we've just shown is in the "Image" namespace, # follow it with the history list and link list for the image # it describes. @@ -40,7 +40,7 @@ class CategoryPage extends Article { function openShowCategory() { # For overloading } - + # generate a list of subcategories and pages for a category # depending on wfMsg("usenewcategorypage") it either calls the new # or the old code. The new code will not work properly for some @@ -85,7 +85,7 @@ class CategoryPage extends Article { $pageCondition = 'cl_sortkey < ' . $dbr->addQuotes( $until ); $flip = true; } else { - $pageCondition = '1'; + $pageCondition = '1 = 1'; $flip = false; } $limit = 200; @@ -100,7 +100,7 @@ class CategoryPage extends Article { $fname, array( 'ORDER BY' => $flip ? 'cl_sortkey DESC' : 'cl_sortkey', 'LIMIT' => $limit + 1 ) ); - + $sk =& $wgUser->getSkin(); $r = "
    \n"; $count = 0; @@ -112,13 +112,13 @@ class CategoryPage extends Article { $nextPage = $x->cl_sortkey; break; } - + $title = Title::makeTitle( $x->page_namespace, $x->page_title ); - + if( $title->getNamespace() == NS_CATEGORY ) { // Subcategory; strip the 'Category' namespace from the link text. array_push( $children, $sk->makeKnownLinkObj( $title, $wgContLang->convertHtml( $title->getText() ) ) ); - + // If there's a link from Category:A to Category:B, the sortkey of the resulting // entry in the categorylinks table is Category:A, not A, which it SHOULD be. // Workaround: If sortkey == "Category:".$title, than use $title for sorting, @@ -151,13 +151,13 @@ class CategoryPage extends Article { $articles = array_reverse( $articles ); $articles_start_char = array_reverse( $articles_start_char ); } - + if( $until != '' ) { $r .= $this->pagingLinks( $this->mTitle, $nextPage, $until, $limit ); } elseif( $nextPage != '' || $from != '' ) { $r .= $this->pagingLinks( $this->mTitle, $from, $nextPage, $limit ); } - + # Don't show subcategories section if there are none. if( count( $children ) > 0 ) { # Showing subcategories @@ -214,7 +214,7 @@ class CategoryPage extends Article { } return ''; } - + /** * Format a list of articles chunked by letter in a three-column * list, ordered vertically. @@ -231,7 +231,7 @@ class CategoryPage extends Article { // get and display header $r = ''; - $prev_start_char = 'none'; + $prev_start_char = 'none'; // loop through the chunks for($startChunk = 0, $endChunk = $chunk, $chunkIndex = 0; @@ -275,7 +275,7 @@ class CategoryPage extends Article { $r .= '
    '; return $r; } - + /** * Format a list of articles chunked by letter in a bullet list. * @param array $articles @@ -298,7 +298,7 @@ class CategoryPage extends Article { $r .= ''; return $r; } - + /** * @param Title $title * @param string $first @@ -312,7 +312,7 @@ class CategoryPage extends Article { global $wgUser, $wgLang; $sk =& $wgUser->getSkin(); $limitText = $wgLang->formatNum( $limit ); - + $prevLink = htmlspecialchars( wfMsg( 'prevn', $limitText ) ); if( $first != '' ) { $prevLink = $sk->makeLinkObj( $title, $prevLink, @@ -323,7 +323,7 @@ class CategoryPage extends Article { $nextLink = $sk->makeLinkObj( $title, $nextLink, wfArrayToCGI( $query + array( 'from' => $last ) ) ); } - + return "($prevLink) ($nextLink)"; } } diff --git a/includes/Database.php b/includes/Database.php index b66e7178dc..6e8a992c02 100644 --- a/includes/Database.php +++ b/includes/Database.php @@ -1,6 +1,6 @@ mData = $data; + } + + function isLOB() { + return false; + } + + function data() { + return $this->mData; + } +}; + /** * Database abstraction object * @package MediaWiki @@ -36,11 +52,11 @@ class Database { * @access private */ var $mLastQuery = ''; - + var $mServer, $mUser, $mPassword, $mConn, $mDBname; var $mOut, $mOpened = false; - - var $mFailFunction; + + var $mFailFunction; var $mTablePrefix; var $mFlags; var $mTrxLevel = 0; @@ -51,30 +67,30 @@ class Database { # Accessors #------------------------------------------------------------------------------ # These optionally set a variable and return the previous state - + /** * Fail function, takes a Database as a parameter * Set to false for default, 1 for ignore errors */ - function failFunction( $function = NULL ) { - return wfSetVar( $this->mFailFunction, $function ); + function failFunction( $function = NULL ) { + return wfSetVar( $this->mFailFunction, $function ); } - + /** * Output page, used for reporting errors * FALSE means discard output */ - function &setOutputPage( &$out ) { - $this->mOut =& $out; + function &setOutputPage( &$out ) { + $this->mOut =& $out; } - + /** * Boolean, controls output of large amounts of debug information */ - function debug( $debug = NULL ) { - return wfSetBit( $this->mFlags, DBO_DEBUG, $debug ); + function debug( $debug = NULL ) { + return wfSetBit( $this->mFlags, DBO_DEBUG, $debug ); } - + /** * Turns buffering of SQL result sets on (true) or off (false). * Default is "on" and it should not be changed without good reasons. @@ -83,7 +99,7 @@ class Database { if ( is_null( $buffer ) ) { return !(bool)( $this->mFlags & DBO_NOBUFFER ); } else { - return !wfSetBit( $this->mFlags, DBO_NOBUFFER, !$buffer ); + return !wfSetBit( $this->mFlags, DBO_NOBUFFER, !$buffer ); } } @@ -94,10 +110,10 @@ class Database { * code should use wfLastErrno() and wfLastError() to handle the * situation as appropriate. */ - function ignoreErrors( $ignoreErrors = NULL ) { - return wfSetBit( $this->mFlags, DBO_IGNORE, $ignoreErrors ); + function ignoreErrors( $ignoreErrors = NULL ) { + return wfSetBit( $this->mFlags, DBO_IGNORE, $ignoreErrors ); } - + /** * The current depth of nested transactions * @param integer $level @@ -106,7 +122,7 @@ class Database { return wfSetVar( $this->mTrxLevel, $level ); } - /** + /** * Number of errors logged, only useful when errors are ignored */ function errorCount( $count = NULL ) { @@ -130,15 +146,15 @@ class Database { * @param string $password database user password * @param string $dbname database name */ - + /** * @param failFunction * @param $flags * @param string $tablePrefix Database table prefixes. By default use the prefix gave in LocalSettings.php */ - function Database( $server = false, $user = false, $password = false, $dbName = false, + function Database( $server = false, $user = false, $password = false, $dbName = false, $failFunction = false, $flags = 0, $tablePrefix = 'get from global' ) { - + global $wgOut, $wgDBprefix, $wgCommandLineMode; # Can't get a reference if it hasn't been set yet if ( !isset( $wgOut ) ) { @@ -148,7 +164,7 @@ class Database { $this->mFailFunction = $failFunction; $this->mFlags = $flags; - + if ( $this->mFlags & DBO_DEFAULT ) { if ( $wgCommandLineMode ) { $this->mFlags &= ~DBO_TRX; @@ -163,7 +179,7 @@ class Database { $this->mFlags |= DBO_PERSISTENT; $this->mFlags &= ~DBO_TRX; }*/ - + /** Get the default table prefix*/ if ( $tablePrefix == 'get from global' ) { $this->mTablePrefix = $wgDBprefix; @@ -175,18 +191,18 @@ class Database { $this->open( $server, $user, $password, $dbName ); } } - + /** * @static * @param failFunction * @param $flags */ - function newFromParams( $server, $user, $password, $dbName, + function newFromParams( $server, $user, $password, $dbName, $failFunction = false, $flags = 0 ) { return new Database( $server, $user, $password, $dbName, $failFunction, $flags ); } - + /** * Usually aborts on failure * If the failFunction is set to a non-zero integer, returns success @@ -202,15 +218,15 @@ class Database { if ( !function_exists( 'mysql_connect' ) ) { die( "MySQL functions missing, have you compiled PHP with the --with-mysql option?\n" ); } - + $this->close(); $this->mServer = $server; $this->mUser = $user; $this->mPassword = $password; $this->mDBname = $dbName; - + $success = false; - + if ( $this->mFlags & DBO_PERSISTENT ) { @/**/$this->mConn = mysql_pconnect( $server, $user, $password ); } else { @@ -226,7 +242,7 @@ class Database { } } else { wfDebug( "DB connection error\n" ); - wfDebug( "Server: $server, User: $user, Password: " . + wfDebug( "Server: $server, User: $user, Password: " . substr( $password, 0, 3 ) . "..., error: " . mysql_error() . "\n" ); $success = false; } @@ -234,7 +250,7 @@ class Database { # Delay USE query $success = (bool)$this->mConn; } - + if ( !$success ) { $this->reportConnectionError(); $this->close(); @@ -243,7 +259,7 @@ class Database { return $success; } /**#@-*/ - + /** * Closes a database connection. * if it is open : commits any open transactions @@ -262,7 +278,7 @@ class Database { return true; } } - + /** * @access private * @param string $msg error message ? @@ -272,24 +288,24 @@ class Database { if ( $this->mFailFunction ) { if ( !is_int( $this->mFailFunction ) ) { $ff = $this->mFailFunction; - $ff( $this, mysql_error() ); + $ff( $this, $this->lastError() ); } } else { wfEmergencyAbort( $this, mysql_error() ); } } - + /** * Usually aborts on failure * If errors are explicitly ignored, returns success */ function query( $sql, $fname = '', $tempIgnore = false ) { global $wgProfiling, $wgCommandLineMode; - + if ( wfReadOnly() ) { - # This is a quick check for the most common kinds of write query used - # in MediaWiki, to provide extra safety in addition to UI-level checks. - # It is not intended to prevent every conceivable write query, or even + # This is a quick check for the most common kinds of write query used + # in MediaWiki, to provide extra safety in addition to UI-level checks. + # It is not intended to prevent every conceivable write query, or even # to handle such queries gracefully. if ( preg_match( '/^(update|insert|replace|delete)/i', $sql ) ) { wfDebug( "Write query from $fname blocked\n" ); @@ -304,9 +320,9 @@ class Database { wfProfileIn( 'Database::query' ); wfProfileIn( $profName ); } - + $this->mLastQuery = $sql; - + # Add a comment for easy SHOW PROCESSLIST interpretation if ( $fname ) { $commentedSql = "/* $fname */ $sql"; @@ -324,7 +340,7 @@ class Database { $sqlx = strtr( $sqlx, "\t\n", ' ' ); wfDebug( "SQL: $sqlx\n" ); } - + # Do the query and handle errors $ret = $this->doQuery( $commentedSql ); @@ -344,14 +360,14 @@ class Database { if ( false === $ret ) { $this->reportQueryError( $this->lastError(), $this->lastErrno(), $sql, $fname, $tempIgnore ); } - + if ( $wgProfiling ) { wfProfileOut( $profName ); wfProfileOut( 'Database::query' ); } return $ret; } - + /** * The DBMS-dependent part of query() * @param string $sql SQL query. @@ -361,7 +377,7 @@ class Database { $ret = mysql_query( $sql, $this->mConn ); } else { $ret = mysql_unbuffered_query( $sql, $this->mConn ); - } + } return $ret; } @@ -389,13 +405,13 @@ class Database { "Query: $sql\n" . "Function: $fname\n" . "Error: $errno $error\n"; - if ( !$wgCommandLineMode ) { + if ( !$wgCommandLineMode ) { $message = nl2br( $message ); } wfDebugDieBacktrace( $message ); } else { // this calls wfAbruptExit() - $this->mOut->databaseError( $fname, $sql, $error, $errno ); + $this->mOut->databaseError( $fname, $sql, $error, $errno ); } } $this->ignoreErrors( $ignore ); @@ -417,11 +433,11 @@ class Database { the bits later. */ return array( 'query' => $sql, 'func' => $func ); } - + function freePrepared( $prepared ) { /* No-op for MySQL */ } - + /** * Execute a prepared query with the various arguments * @param string $prepared the prepared sql @@ -436,7 +452,7 @@ class Database { $sql = $this->fillPrepared( $prepared['query'], $args ); return $this->query( $sql, $prepared['func'] ); } - + /** * Prepare & execute an SQL statement, quoting and inserting arguments * in the appropriate places. @@ -454,7 +470,7 @@ class Database { $this->freePrepared( $prepared ); return $retval; } - + /** * For faking prepared SQL statements on DBs that don't support * it directly. @@ -469,12 +485,12 @@ class Database { return preg_replace_callback( '/(\\\\[?!&]|[?!&])/', array( &$this, 'fillPreparedArg' ), $preparedQuery ); } - + /** * preg_callback func for fillPrepared() * The arguments should be in $this->preparedArgs and must not be touched * while we're doing this. - * + * * @param array $matches * @return string * @access private @@ -496,7 +512,7 @@ class Database { wfDebugDieBacktrace( 'Received invalid match. This should never happen!' ); } } - + /**#@+ * @param mixed $res A SQL result */ @@ -508,7 +524,7 @@ class Database { wfDebugDieBacktrace( "Unable to free MySQL result\n" ); } } - + /** * Fetch the next row from the given result object, in object form */ @@ -530,19 +546,19 @@ class Database { wfDebugDieBacktrace( 'Error in fetchRow(): ' . htmlspecialchars( mysql_error() ) ); } return $row; - } + } /** * Get the number of rows in a result object */ function numRows( $res ) { - @/**/$n = mysql_num_rows( $res ); + @/**/$n = mysql_num_rows( $res ); if( mysql_errno() ) { wfDebugDieBacktrace( 'Error in numRows(): ' . htmlspecialchars( mysql_error() ) ); } return $n; } - + /** * Get the number of fields in a result object * See documentation for mysql_num_fields() @@ -566,32 +582,32 @@ class Database { * $id = $dbw->insertId(); */ function insertId() { return mysql_insert_id( $this->mConn ); } - + /** * Change the position of the cursor in a result object * See mysql_data_seek() */ function dataSeek( $res, $row ) { return mysql_data_seek( $res, $row ); } - + /** * Get the last error number * See mysql_errno() */ - function lastErrno() { + function lastErrno() { if ( $this->mConn ) { - return mysql_errno( $this->mConn ); + return mysql_errno( $this->mConn ); } else { return mysql_errno(); } } - + /** * Get a description of the last error * See mysql_error() for more details */ - function lastError() { + function lastError() { if ( $this->mConn ) { - $error = mysql_error( $this->mConn ); + $error = mysql_error( $this->mConn ); } else { $error = mysql_error(); } @@ -599,7 +615,7 @@ class Database { $error .= ' (' . $this->mServer . ')'; } return $error; - } + } /** * Get the number of rows affected by the last write query * See mysql_affected_rows() for more details @@ -612,7 +628,7 @@ class Database { * Usually aborts on failure * If errors are explicitly ignored, returns success * - * This function exists for historical reasons, Database::update() has a more standard + * This function exists for historical reasons, Database::update() has a more standard * calling convention and feature set */ function set( $table, $var, $value, $cond, $fname = 'Database::set' ) @@ -622,7 +638,7 @@ class Database { $this->strencode( $value ) . "' WHERE ($cond)"; return (bool)$this->query( $sql, DB_MASTER, $fname ); } - + /** * Simple SELECT wrapper, returns a single field, input must be encoded * Usually aborts on failure @@ -646,7 +662,7 @@ class Database { return false; } } - + /** * Returns an optional USE INDEX clause to go after the table, and a * string to go at the end of the query @@ -665,15 +681,12 @@ class Database { } if ( isset( $options['ORDER BY'] ) ) { $tailOpts .= " ORDER BY {$options['ORDER BY']}"; - } - if ( isset( $options['LIMIT'] ) ) { - $tailOpts .= " LIMIT {$options['LIMIT']}"; } if ( is_numeric( array_search( 'FOR UPDATE', $options ) ) ) { $tailOpts .= ' FOR UPDATE'; } - + if ( is_numeric( array_search( 'LOCK IN SHARE MODE', $options ) ) ) { $tailOpts .= ' LOCK IN SHARE MODE'; } @@ -694,6 +707,9 @@ class Database { if( is_array( $vars ) ) { $vars = implode( ',', $vars ); } + if( !is_array( $options ) ) { + $options = array( $options ); + } if( is_array( $table ) ) { $from = ' FROM ' . implode( ',', array_map( array( &$this, 'tableName' ), $table ) ); } elseif ($table!='') { @@ -702,8 +718,8 @@ class Database { $from = ''; } - list( $useIndex, $tailOpts ) = $this->makeSelectOptions( (array)$options ); - + list( $useIndex, $tailOpts ) = $this->makeSelectOptions( array($options) ); + if( !empty( $conds ) ) { if ( is_array( $conds ) ) { $conds = $this->makeList( $conds, LIST_AND ); @@ -712,15 +728,18 @@ class Database { } else { $sql = "SELECT $vars $from $useIndex $tailOpts"; } + if (isset($options['LIMIT'])) { + $sql = $this->limitResult($sql, $options['LIMIT'], false); + } return $this->query( $sql, $fname ); } /** * Single row SELECT wrapper * Aborts or returns FALSE on error - * + * * $vars: the selected variables - * $conds: a condition map, terms are ANDed together. + * $conds: a condition map, terms are ANDed together. * Items with numeric keys are taken to be literal conditions * Takes an array of selected variables, and a condition map, which is ANDed * e.g: selectRow( "page", array( "page_id" ), array( "page_namespace" => @@ -732,15 +751,18 @@ class Database { function selectRow( $table, $vars, $conds, $fname = 'Database::selectRow', $options = array() ) { $options['LIMIT'] = 1; $res = $this->select( $table, $vars, $conds, $fname, $options ); - if ( $res === false || !$this->numRows( $res ) ) { + if ( $res === false ) + return false; + if ( !$this->numRows($res) ) { + $this->freeResult($res); return false; } $obj = $this->fetchObject( $res ); $this->freeResult( $res ); return $obj; - + } - + /** * Removes most variables from an SQL query and replaces them with X or N for numbers. * It's only slightly flawed. Don't use for anything important. @@ -748,26 +770,26 @@ class Database { * @param string $sql A SQL Query * @static */ - function generalizeSQL( $sql ) { + function generalizeSQL( $sql ) { # This does the same as the regexp below would do, but in such a way # as to avoid crashing php on some large strings. # $sql = preg_replace ( "/'([^\\\\']|\\\\.)*'|\"([^\\\\\"]|\\\\.)*\"/", "'X'", $sql); - + $sql = str_replace ( "\\\\", '', $sql); $sql = str_replace ( "\\'", '', $sql); $sql = str_replace ( "\\\"", '', $sql); $sql = preg_replace ("/'.*'/s", "'X'", $sql); $sql = preg_replace ('/".*"/s', "'X'", $sql); - + # All newlines, tabs, etc replaced by single space $sql = preg_replace ( "/\s+/", ' ', $sql); - - # All numbers => N + + # All numbers => N $sql = preg_replace ('/-?[0-9]+/s', 'N', $sql); - + return $sql; } - + /** * Determines whether a field exists in a table * Usually aborts on failure @@ -779,9 +801,9 @@ class Database { if ( !$res ) { return NULL; } - + $found = false; - + while ( $row = $this->fetchObject( $res ) ) { if ( $row->Field == $field ) { $found = true; @@ -790,7 +812,7 @@ class Database { } return $found; } - + /** * Determines whether an index exists * Usually aborts on failure @@ -804,8 +826,8 @@ class Database { return $info !== false; } } - - + + /** * Get information about an index into an object * Returns false if the index does not exist @@ -820,7 +842,7 @@ class Database { if ( !$res ) { return NULL; } - + while ( $row = $this->fetchObject( $res ) ) { if ( $row->Key_name == $index ) { return $row; @@ -828,7 +850,7 @@ class Database { } return false; } - + /** * Query whether a given table exists */ @@ -864,7 +886,7 @@ class Database { } return false; } - + /** * mysql_field_type() wrapper */ @@ -886,7 +908,7 @@ class Database { /** * INSERT wrapper, inserts an array into a table * - * $a may be a single associative array, or an array of these with numeric keys, for + * $a may be a single associative array, or an array of these with numeric keys, for * multi-row insert. * * Usually aborts on failure @@ -910,7 +932,7 @@ class Database { $keys = array_keys( $a ); } - $sql = 'INSERT ' . implode( ' ', $options ) . + $sql = 'INSERT ' . implode( ' ', $options ) . " INTO $table (" . implode( ',', $keys ) . ') VALUES '; if ( $multi ) { @@ -968,7 +990,7 @@ class Database { } $this->query( $sql, $fname ); } - + /** * Makes a wfStrencoded list from an array * $mode: LIST_COMMA - comma separated, no field names @@ -1006,7 +1028,7 @@ class Database { } return $list; } - + /** * Change the current database */ @@ -1036,13 +1058,13 @@ class Database { /** * Format a table name ready for use in constructing an SQL query - * - * This does two important things: it quotes table names which as necessary, + * + * This does two important things: it quotes table names which as necessary, * and it adds a table prefix if there is one. - * - * All functions of this object which require a table name call this function + * + * All functions of this object which require a table name call this function * themselves. Pass the canonical name to such functions. This is only needed - * when calling query() directly. + * when calling query() directly. * * @param string $name database table name */ @@ -1059,7 +1081,7 @@ class Database { # Standard quoting $name = "`$name`"; } - } + } return $name; } @@ -1069,7 +1091,7 @@ class Database { * * Example: * extract($dbr->tableNames('user','watchlist')); - * $sql = "SELECT wl_namespace,wl_title FROM $watchlist,$user + * $sql = "SELECT wl_namespace,wl_title FROM $watchlist,$user * WHERE wl_user=user_id AND wl_user=$nameWithQuotes"; */ function tableNames() { @@ -1080,7 +1102,7 @@ class Database { } return $retVal; } - + /** * Wrapper for addslashes() * @param string $s String to be slashed. @@ -1103,9 +1125,9 @@ class Database { # _are_ strings such as article titles and string->number->string # conversion is not 1:1. return "'" . $this->strencode( $s ) . "'"; - } + } } - + /** * Returns an appropriately quoted sequence value for inserting a new row. * MySQL has autoincrement fields, so this is just NULL. But the PostgreSQL @@ -1127,11 +1149,11 @@ class Database { * REPLACE query wrapper * PostgreSQL simulates this with a DELETE followed by INSERT * $row is the row to insert, an associative array - * $uniqueIndexes is an array of indexes. Each element may be either a + * $uniqueIndexes is an array of indexes. Each element may be either a * field name or an array of field names - * - * It may be more efficient to leave off unique indexes which are unlikely to collide. - * However if you do this, you run the risk of encountering errors which wouldn't have + * + * It may be more efficient to leave off unique indexes which are unlikely to collide. + * However if you do this, you run the risk of encountering errors which wouldn't have * occurred in MySQL * * @todo migrate comment to phodocumentor format @@ -1161,7 +1183,7 @@ class Database { * DELETE where the condition is a join * MySQL does this with a multi-table DELETE syntax, PostgreSQL does it with sub-selects * - * For safety, an empty $conds will not delete everything. If you want to delete all rows where the + * For safety, an empty $conds will not delete everything. If you want to delete all rows where the * join condition matches, set $conds='*' * * DO NOT put the join condition in $conds @@ -1183,7 +1205,7 @@ class Database { if ( $conds != '*' ) { $sql .= ' AND ' . $this->makeList( $conds, LIST_AND ); } - + return $this->query( $sql, $fname ); } @@ -1240,11 +1262,11 @@ class Database { $destTable = $this->tableName( $destTable ); if( is_array( $srcTable ) ) { $srcTable = implode( ',', array_map( array( &$this, 'tableName' ), $srcTable ) ); - } else { + } else { $srcTable = $this->tableName( $srcTable ); } $sql = "INSERT INTO $destTable (" . implode( ',', array_keys( $varMap ) ) . ')' . - ' SELECT ' . implode( ',', $varMap ) . + ' SELECT ' . implode( ',', $varMap ) . " FROM $srcTable"; if ( $conds != '*' ) { $sql .= ' WHERE ' . $this->makeList( $conds, LIST_AND ); @@ -1256,8 +1278,11 @@ class Database { * Construct a LIMIT query with optional offset * This is used for query pages */ - function limitResult($limit,$offset) { - return ' LIMIT '.(is_numeric($offset)?"{$offset},":"")."{$limit} "; + function limitResult($sql, $limit, $offset) { + return "$sql LIMIT ".((is_numeric($offset) && $offset != 0)?"{$offset},":"")."{$limit} "; + } + function limitResultForUpdate($sql, $num) { + return $this->limitResult($sql, $num, 0); } /** @@ -1283,23 +1308,23 @@ class Database { /** * Perform a deadlock-prone transaction. * - * This function invokes a callback function to perform a set of write - * queries. If a deadlock occurs during the processing, the transaction + * This function invokes a callback function to perform a set of write + * queries. If a deadlock occurs during the processing, the transaction * will be rolled back and the callback function will be called again. * - * Usage: + * Usage: * $dbw->deadlockLoop( callback, ... ); * - * Extra arguments are passed through to the specified callback function. - * - * Returns whatever the callback function returned on its successful, - * iteration, or false on error, for example if the retry limit was + * Extra arguments are passed through to the specified callback function. + * + * Returns whatever the callback function returned on its successful, + * iteration, or false on error, for example if the retry limit was * reached. */ function deadlockLoop() { $myFname = 'Database::deadlockLoop'; - - $this->query( 'BEGIN', $myFname ); + + $this->begin(); $args = func_get_args(); $function = array_shift( $args ); $oldIgnore = $this->ignoreErrors( true ); @@ -1314,7 +1339,7 @@ class Database { $error = $this->lastError(); $errno = $this->lastErrno(); $sql = $this->lastQuery(); - + if ( $errno ) { if ( $this->wasDeadlock() ) { # Retry @@ -1345,11 +1370,11 @@ class Database { function masterPosWait( $file, $pos, $timeout ) { $fname = 'Database::masterPosWait'; wfProfileIn( $fname ); - - + + # Commit any open transactions $this->immediateCommit(); - + # Call doQuery() directly, to avoid opening a transaction if DBO_TRX is set $encFile = $this->strencode( $file ); $sql = "SELECT MASTER_POS_WAIT('$encFile', $pos, $timeout)"; @@ -1376,7 +1401,7 @@ class Database { return array( false, false ); } } - + /** * Get the position of the master from SHOW MASTER STATUS */ @@ -1428,7 +1453,7 @@ class Database { $this->query( 'BEGIN', $fname ); $this->mTrxLevel = 1; } - + /** * Commit transaction, if one is open */ @@ -1443,7 +1468,7 @@ class Database { function timestamp( $ts=0 ) { return wfTimestamp(TS_MW,$ts); } - + /** * Local database timestamp format or null */ @@ -1454,7 +1479,7 @@ class Database { return $this->timestamp( $ts ); } } - + /** * @todo document */ @@ -1472,14 +1497,14 @@ class Database { function aggregateValue ($valuedata,$valuename='value') { return $valuename; } - + /** * @return string wikitext of a link to the server software's web site */ function getSoftwareLink() { return "[http://www.mysql.com/ MySQL]"; } - + /** * @return string Version information from the database */ @@ -1498,14 +1523,14 @@ class Database { return true; } } - + /** * Get slave lag. * At the moment, this will only work if the DB user has the PROCESS privilege */ function getLag() { $res = $this->query( 'SHOW PROCESSLIST' ); - # Find slave SQL thread. Assumed to be the second one running, which is a bit + # Find slave SQL thread. Assumed to be the second one running, which is a bit # dubious, but unfortunately there's no easy rigorous way $slaveThreads = 0; while ( $row = $this->fetchObject( $res ) ) { @@ -1530,7 +1555,25 @@ class Database { } return $status; } -} + + /** + * Return the maximum number of items allowed in a list, or 0 for unlimited. + */ + function maxListLen() { + return 0; + } + + function encodeBlob($b) { + return $b; + } + + function notNullTimestamp() { + return "!= 0"; + } + function isNullTimestamp() { + return "= '0'"; + } +} /** * Database abstraction object for mySQL @@ -1551,7 +1594,7 @@ class DatabaseMysql extends Database { */ class ResultWrapper { var $db, $result; - + /** * @todo document */ @@ -1566,21 +1609,21 @@ class ResultWrapper { function numRows() { return $this->db->numRows( $this->result ); } - + /** * @todo document */ function fetchObject() { return $this->db->fetchObject( $this->result ); } - + /** * @todo document */ function &fetchRow() { return $this->db->fetchRow( $this->result ); } - + /** * @todo document */ @@ -1607,11 +1650,11 @@ class ResultWrapper { function wfEmergencyAbort( &$conn, $error ) { global $wgTitle, $wgUseFileCache, $title, $wgInputEncoding, $wgOutputEncoding; global $wgSitename, $wgServer; - + # I give up, Brion is right. Getting the message cache to work when there is no DB is tricky. # Hard coding strings instead. - $noconnect = 'Sorry! The wiki is experiencing some technical difficulties, and cannot contact the database server.
    + $noconnect = 'Sorry! The wiki is experiencing some technical difficulties, and cannot contact the database server: $1.
    $1'; $mainpage = 'Main Page'; $searchdisabled = << } elseif (@/**/$_REQUEST['search']) { $search = $_REQUEST['search']; echo $searchdisabled; - echo str_replace( array( '$1', '$2' ), array( htmlspecialchars( $search ), + echo str_replace( array( '$1', '$2' ), array( htmlspecialchars( $search ), $wgInputEncoding ), $googlesearch ); wfErrorExit(); } else { @@ -1675,7 +1718,7 @@ border=\"0\" ALT=\"Google\"> if( $cache->isFileCached() ) { $msg = '

    '.$msg."
    \n" . $cachederror . "

    \n"; - + $tag = '
    '; $text = str_replace( $tag, @@ -1683,7 +1726,7 @@ border=\"0\" ALT=\"Google\"> $cache->fetchPageText() ); } } - + echo $text; wfErrorExit(); } diff --git a/includes/DatabaseFunctions.php b/includes/DatabaseFunctions.php index 37059696e4..0618950389 100644 --- a/includes/DatabaseFunctions.php +++ b/includes/DatabaseFunctions.php @@ -1,7 +1,7 @@ query( $sql, $fname ); - } else { + } else { return false; } } @@ -52,7 +52,7 @@ function &wfGetDB( $db = DB_LAST, $groups = array() ) { $ret =& $wgLoadBalancer->getConnection( $db, true, $groups ); return $ret; } - + /** * Turns on (false) or off (true) the automatic generation and sending * of a "we're sorry, but there has been a database error" page on @@ -82,13 +82,13 @@ function wfIgnoreSQLErrors( $newstate, $dbi = DB_LAST ) { * Free a database result * @return bool whether result is sucessful or not */ -function wfFreeResult( $res, $dbi = DB_LAST ) -{ +function wfFreeResult( $res, $dbi = DB_LAST ) +{ $db =& wfGetDB( $dbi ); if ( $db !== false ) { - $db->freeResult( $res ); + $db->freeResult( $res ); return true; - } else { + } else { return false; } } @@ -97,11 +97,11 @@ function wfFreeResult( $res, $dbi = DB_LAST ) * Get an object from a database result * @return object|false object we requested */ -function wfFetchObject( $res, $dbi = DB_LAST ) { +function wfFetchObject( $res, $dbi = DB_LAST ) { $db =& wfGetDB( $dbi ); if ( $db !== false ) { - return $db->fetchObject( $res, $dbi = DB_LAST ); - } else { + return $db->fetchObject( $res, $dbi = DB_LAST ); + } else { return false; } } @@ -114,7 +114,7 @@ function wfFetchRow( $res, $dbi = DB_LAST ) { $db =& wfGetDB( $dbi ); if ( $db !== false ) { return $db->fetchRow ( $res, $dbi = DB_LAST ); - } else { + } else { return false; } } @@ -123,11 +123,11 @@ function wfFetchRow( $res, $dbi = DB_LAST ) { * Get a number of rows from a database result * @return integer|false number of rows */ -function wfNumRows( $res, $dbi = DB_LAST ) { +function wfNumRows( $res, $dbi = DB_LAST ) { $db =& wfGetDB( $dbi ); if ( $db !== false ) { - return $db->numRows( $res, $dbi = DB_LAST ); - } else { + return $db->numRows( $res, $dbi = DB_LAST ); + } else { return false; } } @@ -136,11 +136,11 @@ function wfNumRows( $res, $dbi = DB_LAST ) { * Get the number of fields from a database result * @return integer|false number of fields */ -function wfNumFields( $res, $dbi = DB_LAST ) { +function wfNumFields( $res, $dbi = DB_LAST ) { $db =& wfGetDB( $dbi ); if ( $db !== false ) { - return $db->numFields( $res ); - } else { + return $db->numFields( $res ); + } else { return false; } } @@ -150,12 +150,12 @@ function wfNumFields( $res, $dbi = DB_LAST ) { * @param integer $n id of the field * @return string|false name of field */ -function wfFieldName( $res, $n, $dbi = DB_LAST ) -{ +function wfFieldName( $res, $n, $dbi = DB_LAST ) +{ $db =& wfGetDB( $dbi ); if ( $db !== false ) { - return $db->fieldName( $res, $n, $dbi = DB_LAST ); - } else { + return $db->fieldName( $res, $n, $dbi = DB_LAST ); + } else { return false; } } @@ -164,11 +164,11 @@ function wfFieldName( $res, $n, $dbi = DB_LAST ) /** * @todo document function */ -function wfInsertId( $dbi = DB_LAST ) { +function wfInsertId( $dbi = DB_LAST ) { $db =& wfGetDB( $dbi ); if ( $db !== false ) { - return $db->insertId(); - } else { + return $db->insertId(); + } else { return false; } } @@ -176,11 +176,11 @@ function wfInsertId( $dbi = DB_LAST ) { /** * @todo document function */ -function wfDataSeek( $res, $row, $dbi = DB_LAST ) { +function wfDataSeek( $res, $row, $dbi = DB_LAST ) { $db =& wfGetDB( $dbi ); if ( $db !== false ) { - return $db->dataSeek( $res, $row ); - } else { + return $db->dataSeek( $res, $row ); + } else { return false; } } @@ -188,11 +188,11 @@ function wfDataSeek( $res, $row, $dbi = DB_LAST ) { /** * @todo document function */ -function wfLastErrno( $dbi = DB_LAST ) { +function wfLastErrno( $dbi = DB_LAST ) { $db =& wfGetDB( $dbi ); if ( $db !== false ) { - return $db->lastErrno(); - } else { + return $db->lastErrno(); + } else { return false; } } @@ -200,11 +200,11 @@ function wfLastErrno( $dbi = DB_LAST ) { /** * @todo document function */ -function wfLastError( $dbi = DB_LAST ) { +function wfLastError( $dbi = DB_LAST ) { $db =& wfGetDB( $dbi ); if ( $db !== false ) { - return $db->lastError(); - } else { + return $db->lastError(); + } else { return false; } } @@ -212,11 +212,11 @@ function wfLastError( $dbi = DB_LAST ) { /** * @todo document function */ -function wfAffectedRows( $dbi = DB_LAST ) { +function wfAffectedRows( $dbi = DB_LAST ) { $db =& wfGetDB( $dbi ); if ( $db !== false ) { - return $db->affectedRows(); - } else { + return $db->affectedRows(); + } else { return false; } } @@ -228,7 +228,7 @@ function wfLastDBquery( $dbi = DB_LAST ) { $db =& wfGetDB( $dbi ); if ( $db !== false ) { return $db->lastQuery(); - } else { + } else { return false; } } @@ -241,7 +241,7 @@ function wfSetSQL( $table, $var, $value, $cond, $dbi = DB_MASTER ) $db =& wfGetDB( $dbi ); if ( $db !== false ) { return $db->set( $table, $var, $value, $cond ); - } else { + } else { return false; } } @@ -255,7 +255,7 @@ function wfGetSQL( $table, $var, $cond='', $dbi = DB_LAST ) $db =& wfGetDB( $dbi ); if ( $db !== false ) { return $db->selectField( $table, $var, $cond ); - } else { + } else { return false; } } @@ -267,7 +267,7 @@ function wfFieldExists( $table, $field, $dbi = DB_LAST ) { $db =& wfGetDB( $dbi ); if ( $db !== false ) { return $db->fieldExists( $table, $field ); - } else { + } else { return false; } } @@ -279,7 +279,7 @@ function wfIndexExists( $table, $index, $dbi = DB_LAST ) { $db =& wfGetDB( $dbi ); if ( $db !== false ) { return $db->indexExists( $table, $index ); - } else { + } else { return false; } } @@ -291,7 +291,7 @@ function wfInsertArray( $table, $array, $fname = 'wfInsertArray', $dbi = DB_MAST $db =& wfGetDB( $dbi ); if ( $db !== false ) { return $db->insert( $table, $array, $fname ); - } else { + } else { return false; } } @@ -303,7 +303,7 @@ function wfGetArray( $table, $vars, $conds, $fname = 'wfGetArray', $dbi = DB_LAS $db =& wfGetDB( $dbi ); if ( $db !== false ) { return $db->getArray( $table, $vars, $conds, $fname ); - } else { + } else { return false; } } diff --git a/includes/DatabaseOracle.php b/includes/DatabaseOracle.php new file mode 100644 index 0000000000..5aa0aa5f97 --- /dev/null +++ b/includes/DatabaseOracle.php @@ -0,0 +1,706 @@ +mData; + } +}; + +/** + * + * @package MediaWiki + */ +class DatabaseOracle extends Database { + var $mInsertId = NULL; + var $mLastResult = NULL; + var $mFetchCache = array(); + var $mFetchID = array(); + var $mNcols = array(); + var $mFieldNames = array(), $mFieldTypes = array(); + var $mAffectedRows = array(); + var $mErr; + + function DatabaseOracle($server = false, $user = false, $password = false, $dbName = false, + $failFunction = false, $flags = 0, $tablePrefix = 'get from global' ) + { + Database::Database( $server, $user, $password, $dbName, $failFunction, $flags, $tablePrefix ); + } + + /* static */ function newFromParams( $server = false, $user = false, $password = false, $dbName = false, + $failFunction = false, $flags = 0, $tablePrefix = 'get from global' ) + { + return new DatabaseOracle( $server, $user, $password, $dbName, $failFunction, $flags, $tablePrefix ); + } + + /** + * Usually aborts on failure + * If the failFunction is set to a non-zero integer, returns success + */ + function open( $server, $user, $password, $dbName ) { + if ( !function_exists( 'oci_connect' ) ) { + die( "Oracle functions missing, have you compiled PHP with the --with-oci8 option?\n" ); + } + $this->close(); + $this->mServer = $server; + $this->mUser = $user; + $this->mPassword = $password; + $this->mDBname = $dbName; + + $success = false; + + $hstring=""; + $this->mConn = oci_new_connect($user, $password, $dbName, "AL32UTF8"); + if ( $this->mConn === false ) { + wfDebug( "DB connection error\n" ); + wfDebug( "Server: $server, Database: $dbName, User: $user, Password: " + . substr( $password, 0, 3 ) . "...\n" ); + wfDebug( $this->lastError()."\n" ); + } else { + $this->mOpened = true; + } + return $this->mConn; + } + + /** + * Closes a database connection, if it is open + * Returns success, true if already closed + */ + function close() { + $this->mOpened = false; + if ($this->mConn) { + return oci_close($this->mConn); + } else { + return true; + } + } + + function parseStatement($sql) { + $this->mErr = $this->mLastResult = false; + if (($stmt = oci_parse($this->mConn, $sql)) === false) { + $this->lastError(); + return $this->mLastResult = false; + } + $this->mAffectedRows[$stmt] = 0; + return $this->mLastResult = $stmt; + } + + function doQuery($sql) { + if (($stmt = $this->parseStatement($sql)) === false) + return false; + return $this->executeStatement($stmt); + } + + function executeStatement($stmt) { + if (!oci_execute($stmt, OCI_DEFAULT)) { + $this->lastError(); + oci_free_statement($stmt); + return false; + } + $this->mAffectedRows[$stmt] = oci_num_rows($stmt); + $this->mFetchCache[$stmt] = array(); + $this->mFetchID[$stmt] = 0; + $this->mNcols[$stmt] = oci_num_fields($stmt); + if ($this->mNcols[$stmt] == 0) + return $this->mLastResult; + for ($i = 1; $i <= $this->mNcols[$stmt]; $i++) { + $this->mFieldNames[$stmt][$i] = oci_field_name($stmt, $i); + $this->mFieldTypes[$stmt][$i] = oci_field_type($stmt, $i); + } + while (($o = oci_fetch_array($stmt)) !== false) { + foreach ($o as $key => $value) { + if (is_object($value)) { + $o[$key] = $value->load(); + } + } + $this->mFetchCache[$stmt][] = $o; + } + return $this->mLastResult; + } + + function queryIgnore( $sql, $fname = '' ) { + return $this->query( $sql, $fname, true ); + } + + function freeResult( $res ) { + if (!oci_free_statement($res)) { + wfDebugDieBacktrace( "Unable to free Oracle result\n" ); + } + unset($this->mFetchID[$res]); + unset($this->mFetchCache[$res]); + unset($this->mNcols[$res]); + unset($this->mFieldNames[$res]); + unset($this->mFieldTypes[$res]); + } + + function fetchAssoc($res) { + if ($this->mFetchID[$res] >= count($this->mFetchCache[$res])) + return false; + + for ($i = 1; $i <= $this->mNcols[$res]; $i++) { + $name = $this->mFieldNames[$res][$i]; + $type = $this->mFieldTypes[$res][$i]; + if (isset($this->mFetchCache[$res][$this->mFetchID[$res]][$name])) + $value = $this->mFetchCache[$res][$this->mFetchID[$res]][$name]; + else $value = NULL; + $key = strtolower($name); + wfdebug("'$key' => '$value'\n"); + $ret[$key] = $value; + } + $this->mFetchID[$res]++; + return $ret; + } + + function fetchRow($res) { + $r = $this->fetchAssoc($res); + if (!$r) + return false; + $i = 0; + $ret = array(); + foreach ($r as $key => $value) { + wfdebug("ret[$i]=[$value]\n"); + $ret[$i++] = $value; + } + return $ret; + } + + function fetchObject($res) { + $row = $this->fetchAssoc($res); + if (!$row) + return false; + $ret = new stdClass; + foreach ($row as $key => $value) + $ret->$key = $value; + return $ret; + } + + function numRows($res) { + return count($this->mFetchCache[$res]); + } + function numFields( $res ) { return pg_num_fields( $res ); } + function fieldName( $res, $n ) { return pg_field_name( $res, $n ); } + + /** + * This must be called after nextSequenceVal + */ + function insertId() { + return $this->mInsertId; + } + + function dataSeek($res, $row) { + $this->mFetchID[$res] = $row; + } + + function lastError() { + if ($this->mErr === false) { + if ($this->mLastResult !== false) $what = $this->mLastResult; + else if ($this->mConn !== false) $what = $this->mConn; + else $what = false; + $err = ($what !== false) ? oci_error($what) : oci_error(); + if ($err === false) + $this->mErr = 'no error'; + else + $this->mErr = $err['message']; + } + return str_replace("\n", '
    ', $this->mErr); + } + function lastErrno() { + return 0; + } + + function affectedRows() { + return $this->mAffectedRows[$this->mLastResult]; + } + + /** + * Returns information about an index + * If errors are explicitly ignored, returns NULL on failure + */ + function indexInfo ($table, $index, $fname = 'Database::indexInfo' ) { + $table = $this->tableName($table, true); + if ($index == 'PRIMARY') + $index = "${table}_pk"; + $sql = "SELECT uniqueness FROM all_indexes WHERE table_name='" . + $table . "' AND index_name='" . + $this->strencode(strtoupper($index)) . "'"; + $res = $this->query($sql, $fname); + if (!$res) + return NULL; + if (($row = $this->fetchObject($res)) == NULL) + return false; + $this->freeResult($res); + $row->Non_unique = !$row->uniqueness; + return $row; + } + + function indexUnique ($table, $index, $fname = 'indexUnique') { + if (!($i = $this->indexInfo($table, $index, $fname))) + return $i; + return $i->uniqueness == 'UNIQUE'; + } + + function fieldInfo( $table, $field ) { + $o = new stdClass; + $o->multiple_key = true; /* XXX */ + return $o; + } + + function getColumnInformation($table, $field) { + $table = $this->tableName($table, true); + $field = strtoupper($field); + + $res = $this->doQuery("SELECT * FROM all_tab_columns " . + "WHERE table_name='".$table."' " . + "AND column_name='".$field."'"); + if (!$res) + return false; + $o = $this->fetchObject($res); + $this->freeResult($res); + return $o; + } + + function fieldExists( $table, $field, $fname = 'Database::fieldExists' ) { + $column = $this->getColumnInformation($table, $field); + if (!$column) + return false; + return true; + } + + function startTimer( $timeout ) + { + global $IP; + wfDebugDieBacktrace( 'Database::startTimer() error : mysql_thread_id() not implemented for postgre' ); + /*$tid = mysql_thread_id( $this->mConn ); + exec( "php $IP/killthread.php $timeout $tid &>/dev/null &" );*/ + } + + function tableName($name, $forddl = false) { + # First run any transformations from the parent object + $name = parent::tableName( $name ); + + # Replace backticks into empty + # Note: "foo" and foo are not the same in Oracle! + $name = str_replace('`', '', $name); + + # Now quote Oracle reserved keywords + switch( $name ) { + case 'user': + case 'group': + case 'validate': + if ($forddl) + return $name; + else + return '"' . $name . '"'; + + default: + return strtoupper($name); + } + } + + function strencode( $s ) { + return str_replace("'", "''", $s); + } + + /** + * Return the next in a sequence, save the value for retrieval via insertId() + */ + function nextSequenceValue( $seqName ) { + $r = $this->doQuery("SELECT $seqName.nextval AS val FROM dual"); + $o = $this->fetchObject($r); + $this->freeResult($r); + return $this->mInsertId = (int)$o->val; + } + + /** + * USE INDEX clause + * PostgreSQL doesn't have them and returns "" + */ + function useIndexClause( $index ) { + return ''; + } + + # REPLACE query wrapper + # PostgreSQL simulates this with a DELETE followed by INSERT + # $row is the row to insert, an associative array + # $uniqueIndexes is an array of indexes. Each element may be either a + # field name or an array of field names + # + # It may be more efficient to leave off unique indexes which are unlikely to collide. + # However if you do this, you run the risk of encountering errors which wouldn't have + # occurred in MySQL + function replace( $table, $uniqueIndexes, $rows, $fname = 'Database::replace' ) { + $table = $this->tableName( $table ); + + if (count($rows)==0) { + return; + } + + # Single row case + if ( !is_array( reset( $rows ) ) ) { + $rows = array( $rows ); + } + + foreach( $rows as $row ) { + # Delete rows which collide + if ( $uniqueIndexes ) { + $sql = "DELETE FROM $table WHERE "; + $first = true; + foreach ( $uniqueIndexes as $index ) { + if ( $first ) { + $first = false; + $sql .= "("; + } else { + $sql .= ') OR ('; + } + if ( is_array( $index ) ) { + $first2 = true; + foreach ( $index as $col ) { + if ( $first2 ) { + $first2 = false; + } else { + $sql .= ' AND '; + } + $sql .= $col.'=' . $this->addQuotes( $row[$col] ); + } + } else { + $sql .= $index.'=' . $this->addQuotes( $row[$index] ); + } + } + $sql .= ')'; + $this->query( $sql, $fname ); + } + + # Now insert the row + $sql = "INSERT INTO $table (" . $this->makeList( array_keys( $row ), LIST_NAMES ) .') VALUES (' . + $this->makeList( $row, LIST_COMMA ) . ')'; + $this->query( $sql, $fname ); + } + } + + # DELETE where the condition is a join + function deleteJoin( $delTable, $joinTable, $delVar, $joinVar, $conds, $fname = "Database::deleteJoin" ) { + if ( !$conds ) { + wfDebugDieBacktrace( 'Database::deleteJoin() called with empty $conds' ); + } + + $delTable = $this->tableName( $delTable ); + $joinTable = $this->tableName( $joinTable ); + $sql = "DELETE FROM $delTable WHERE $delVar IN (SELECT $joinVar FROM $joinTable "; + if ( $conds != '*' ) { + $sql .= 'WHERE ' . $this->makeList( $conds, LIST_AND ); + } + $sql .= ')'; + + $this->query( $sql, $fname ); + } + + # Returns the size of a text field, or -1 for "unlimited" + function textFieldSize( $table, $field ) { + $table = $this->tableName( $table ); + $sql = "SELECT t.typname as ftype,a.atttypmod as size + FROM pg_class c, pg_attribute a, pg_type t + WHERE relname='$table' AND a.attrelid=c.oid AND + a.atttypid=t.oid and a.attname='$field'"; + $res =$this->query($sql); + $row=$this->fetchObject($res); + if ($row->ftype=="varchar") { + $size=$row->size-4; + } else { + $size=$row->size; + } + $this->freeResult( $res ); + return $size; + } + + function lowPriorityOption() { + return ''; + } + + function limitResult($sql, $limit, $offset) { + $ret = "SELECT * FROM ($sql) WHERE ROWNUM < " . ((int)$limit + (int)($offset+1)); + if (is_numeric($offset)) + $ret .= " AND ROWNUM >= " . (int)$offset; + return $ret; + } + function limitResultForUpdate($sql, $limit) { + return $sql; + } + /** + * Returns an SQL expression for a simple conditional. + * Uses CASE on PostgreSQL. + * + * @param string $cond SQL expression which will result in a boolean value + * @param string $trueVal SQL expression to return if true + * @param string $falseVal SQL expression to return if false + * @return string SQL fragment + */ + function conditional( $cond, $trueVal, $falseVal ) { + return " (CASE WHEN $cond THEN $trueVal ELSE $falseVal END) "; + } + + # FIXME: actually detecting deadlocks might be nice + function wasDeadlock() { + return false; + } + + # Return DB-style timestamp used for MySQL schema + function timestamp($ts = 0) { + return $this->strencode(wfTimestamp(TS_ORACLE, $ts)); +# return "TO_TIMESTAMP('" . $this->strencode(wfTimestamp(TS_DB, $ts)) . "', 'RRRR-MM-DD HH24:MI:SS')"; + } + + function notNullTimestamp() { + return "IS NOT NULL"; + } + function isNullTimestamp() { + return "IS NULL"; + } + /** + * Return aggregated value function call + */ + function aggregateValue ($valuedata,$valuename='value') { + return $valuedata; + } + + + function reportQueryError( $error, $errno, $sql, $fname, $tempIgnore = false ) { + $message = "A database error has occurred\n" . + "Query: $sql\n" . + "Function: $fname\n" . + "Error: $errno $error\n"; + wfDebugDieBacktrace($message); + } + + /** + * @return string wikitext of a link to the server software's web site + */ + function getSoftwareLink() { + return "[http://www.oracle.com/ Oracle]"; + } + + /** + * @return string Version information from the database + */ + function getServerVersion() { + return oci_server_version($this->mConn); + } + + function setSchema($schema=false) { + $schemas=$this->mSchemas; + if ($schema) { array_unshift($schemas,$schema); } + $searchpath=$this->makeList($schemas,LIST_NAMES); + $this->query("SET search_path = $searchpath"); + } + + function begin() { + } + + function immediateCommit( $fname = 'Database::immediateCommit' ) { + oci_commit($this->mConn); + $this->mTrxLevel = 0; + } + function rollback( $fname = 'Database::rollback' ) { + oci_rollback($this->mConn); + $this->mTrxLevel = 0; + } + function getLag() { + return false; + } + function getStatus() { + $result = array('Threads_running' => 0, 'Threads_connected' => 0); + return $result; + } + + /** + * Returns an optional USE INDEX clause to go after the table, and a + * string to go at the end of the query + * + * @access private + * + * @param array $options an associative array of options to be turned into + * an SQL query, valid keys are listed in the function. + * @return array + */ + function makeSelectOptions($options) { + $tailOpts = ''; + + if (isset( $options['ORDER BY'])) { + $tailOpts .= " ORDER BY {$options['ORDER BY']}"; + } + + return array('', $tailOpts); + } + + function maxListLen() { + return 1000; + } + + /** + * Query whether a given table exists + */ + function tableExists( $table ) { + $table = $this->tableName($table, true); + $res = $this->query( "SELECT COUNT(*) as NUM FROM user_tables WHERE table_name='" + . $table . "'" ); + if (!$res) + return false; + $row = $this->fetchObject($res); + $this->freeResult($res); + return $row->num >= 1; + } + + /** + * UPDATE wrapper, takes a condition array and a SET array + */ + function update( $table, $values, $conds, $fname = 'Database::update' ) { + $table = $this->tableName( $table ); + + $sql = "UPDATE $table SET "; + $first = true; + foreach ($values as $field => $v) { + if ($first) + $first = false; + else + $sql .= ", "; + $sql .= "$field = :n$field "; + } + if ( $conds != '*' ) { + $sql .= " WHERE " . $this->makeList( $conds, LIST_AND ); + } + $stmt = $this->parseStatement($sql); + if ($stmt === false) { + $this->reportQueryError( $this->lastError(), $this->lastErrno(), $stmt ); + return false; + } + if ($this->debug()) + wfDebug("SQL: $sql\n"); + $s = ''; + foreach ($values as $field => $v) { + oci_bind_by_name($stmt, ":n$field", $values[$field]); + if ($this->debug()) + $s .= " [$field] = [$v]\n"; + } + if ($this->debug()) + wfdebug(" PH: $s\n"); + $ret = $this->executeStatement($stmt); + return $ret; + } + + /** + * INSERT wrapper, inserts an array into a table + * + * $a may be a single associative array, or an array of these with numeric keys, for + * multi-row insert. + * + * Usually aborts on failure + * If errors are explicitly ignored, returns success + */ + function insert( $table, $a, $fname = 'Database::insert', $options = array() ) { + # No rows to insert, easy just return now + if ( !count( $a ) ) { + return true; + } + + $table = $this->tableName( $table ); + if (!is_array($options)) + $options = array($options); + + $oldIgnore = false; + if (in_array('IGNORE', $options)) + $oldIgnore = $this->ignoreErrors( true ); + + if ( isset( $a[0] ) && is_array( $a[0] ) ) { + $multi = true; + $keys = array_keys( $a[0] ); + } else { + $multi = false; + $keys = array_keys( $a ); + } + + $sql = "INSERT INTO $table (" . implode( ',', $keys ) . ') VALUES ('; + $return = ''; + $first = true; + foreach ($a as $key => $value) { + if ($first) + $first = false; + else + $sql .= ", "; + if (is_object($value) && $value->isLOB()) { + $sql .= "EMPTY_BLOB()"; + $return = "RETURNING $key INTO :bobj"; + } else + $sql .= ":$key"; + } + $sql .= ") $return"; + + if ($this->debug()) { + wfDebug("SQL: $sql\n"); + } + + if (($stmt = $this->parseStatement($sql)) === false) { + $this->reportQueryError($this->lastError(), $this->lastErrno(), $sql, $fname); + $this->ignoreErrors($oldIgnore); + return false; + } + + /* + * If we're inserting multiple rows, parse the statement once and + * execute it for each set of values. Otherwise, convert it into an + * array and pretend. + */ + if (!$multi) + $a = array($a); + + foreach ($a as $key => $row) { + $blob = false; + $bdata = false; + $s = ''; + foreach ($row as $k => $value) { + if (is_object($value) && $value->isLOB()) { + $blob = oci_new_descriptor($this->mConn, OCI_D_LOB); + $bdata = $value->data(); + oci_bind_by_name($stmt, ":bobj", &$blob, -1, OCI_B_BLOB); + } else + oci_bind_by_name($stmt, ":$k", $a[$key][$k], -1); + if ($this->debug()) + $s .= " [$k] = {$row[$k]}"; + } + if ($this->debug()) + wfDebug(" PH: $s\n"); + if (($s = $this->executeStatement($stmt)) === false) { + $this->reportQueryError($this->lastError(), $this->lastErrno(), $sql, $fname); + $this->ignoreErrors($oldIgnore); + return false; + } + + if ($blob) { + $blob->save($bdata); + } + } + $this->ignoreErrors($oldIgnore); + return $this->mLastResult = $s; + } + + function ping() { + return true; + } + + function encodeBlob($b) { + return new OracleBlob($b); + } +} + +?> diff --git a/includes/EditPage.php b/includes/EditPage.php index 1bf5650be9..b259aad44b 100644 --- a/includes/EditPage.php +++ b/includes/EditPage.php @@ -17,14 +17,14 @@ class EditPage { var $mArticle; var $mTitle; var $mMetaData = ''; - + # Form values var $save = false, $preview = false, $diff = false; var $minoredit = false, $watchthis = false; var $textbox1 = '', $textbox2 = '', $summary = ''; var $edittime = '', $section = ''; var $oldid = 0; - + /** * @todo document * @param $article @@ -97,7 +97,7 @@ class EditPage { { $sat[] = strtolower ( $x ) ; } - + } # Templates, but only some @@ -139,7 +139,7 @@ class EditPage { $wgOut->setArticleFlag(false); $this->importFormData( $wgRequest ); - + if( $this->live ) { $this->livePreview(); return; @@ -189,7 +189,7 @@ class EditPage { } } } - + /** * Return true if this page should be previewed when the edit form * is initially opened. @@ -215,7 +215,7 @@ class EditPage { $this->textbox2 = $this->safeUnicodeInput( $request, 'wpTextbox2' ); $this->mMetaData = rtrim( $request->getText( 'metadata' ) ); $this->summary = $request->getText( 'wpSummary' ); - + $this->edittime = $request->getVal( 'wpEdittime' ); if( is_null( $this->edittime ) ) { # If the form is incomplete, force to preview. @@ -238,7 +238,7 @@ class EditPage { if( !preg_match( '/^\d{14}$/', $this->edittime )) { $this->edittime = null; } - + $this->minoredit = $request->getCheck( 'wpMinoredit' ); $this->watchthis = $request->getCheck( 'wpWatchthis' ); } else { @@ -259,7 +259,7 @@ class EditPage { # Section edit can come from either the form or a link $this->section = $request->getVal( 'wpSection', $request->getVal( 'section' ) ); - + $this->live = $request->getCheck( 'live' ); } @@ -280,7 +280,7 @@ class EditPage { return $wgUser->matchEditToken( $request->getVal( 'wpEditToken' ) ); } } - + function submit() { $this->edit(); } @@ -314,7 +314,7 @@ class EditPage { if(isset($introtitle) && $introtitle->userCanRead()) { $rev=Revision::newFromTitle($introtitle); if($rev) { - $wgOut->addWikiText($rev->getText()); + $wgOut->addWikiText($rev->getText()); $addstandardintro=false; } } @@ -401,6 +401,7 @@ class EditPage { $this->mArticle->clear(); # Force reload of dates, etc. $this->mArticle->forUpdate( true ); # Lock the article +wfdebug("CONFLICT: edittime=".$this->edittime." article timestamp=".$this->mArticle->getTimestamp()."\n"); if( ( $this->section != 'new' ) && ($this->mArticle->getTimestamp() != $this->edittime ) ) { $isConflict = true; @@ -450,7 +451,7 @@ class EditPage { # XXX: might be better to integrate this into Article::getTextOfLastEditWithSectionReplacedOrAdded # for duplicate heading checking and maybe parsing $hasmatch = preg_match( "/^ *([=]{1,6})(.*?)(\\1) *\\n/i", $this->textbox1, $matches ); - # we can't deal with anchors, includes, html etc in the header for now, + # we can't deal with anchors, includes, html etc in the header for now, # headline would need to be parsed to improve this #if($hasmatch and strlen($matches[2]) > 0 and !preg_match( "/[\\['{<>]/", $matches[2])) { if($hasmatch and strlen($matches[2]) > 0) { @@ -466,16 +467,17 @@ class EditPage { $this->section = ''; if (wfRunHooks('ArticleSave', array(&$this->mArticle, &$wgUser, &$text, - &$this->summary, &$this->minoredit, - &$this->watchthis, &$sectionanchor))) + &$this->summary, &$this->minoredit, + &$this->watchthis, &$sectionanchor))) { # update the article here if($this->mArticle->updateArticle( $text, $this->summary, $this->minoredit, - $this->watchthis, '', $sectionanchor )) + $this->watchthis, '', $sectionanchor )) { - wfRunHooks('ArticleSaveComplete', array(&$this->mArticle, &$wgUser, $text, - $this->summary, $this->minoredit, - $this->watchthis, $sectionanchor)); + wfRunHooks('ArticleSaveComplete', + array(&$this->mArticle, &$wgUser, $text, + $this->summary, $this->minoredit, + $this->watchthis, $sectionanchor)); return; } else { $isConflict = true; @@ -613,9 +615,11 @@ class EditPage { $watchhtml = ''; if ( $wgUser->isLoggedIn() ) { - $watchhtml = "watchthis?" checked='checked'":""). - " accesskey='".wfMsg('accesskey-watch')."' id='wpWatchthis' />". - ""; + $watchhtml = "watchthis?" checked='checked'":""). + " accesskey=\"".htmlspecialchars(wfMsg('accesskey-watch'))."\" id='wpWatchthis' />". + ""; } $checkboxhtml = $minoredithtml . $watchhtml . '
    '; @@ -627,7 +631,7 @@ class EditPage { $wgOut->addHTML( $previewOutput ); if($this->mTitle->getNamespace() == NS_CATEGORY) { $this->mArticle->closeShowCategory(); - } + } $wgOut->addHTML( "
    \n" ); } } @@ -666,7 +670,7 @@ class EditPage { } $templates .= ''; } - + global $wgLivePreview, $wgStylePath; /** * Live Preview lets us fetch rendered preview page content and @@ -687,7 +691,7 @@ class EditPage { } else { $liveOnclick = ''; } - + global $wgUseMetadataEdit ; if ( $wgUseMetadataEdit ) { @@ -743,8 +747,8 @@ END $wgOut->addHTML( " \n" ); } - - + + if ( $isConflict ) { require_once( "DifferenceEngine.php" ); $wgOut->addWikiText( '==' . wfMsg( "yourdiff" ) . '==' ); @@ -809,18 +813,18 @@ END } if ( $this->mMetaData != "" ) $toparse .= "\n" . $this->mMetaData ; - + $parserOutput = $wgParser->parse( $this->mArticle->preSaveTransform( $toparse ) ."\n\n", - $wgTitle, $parserOptions ); - + $wgTitle, $parserOptions ); + $previewHTML = $parserOutput->mText; - + $wgOut->addCategoryLinks($parserOutput->getCategoryLinks()); $wgOut->addLanguageLinks($parserOutput->getLanguageLinks()); return $previewhead . $previewHTML; } } - + /** * @todo document */ @@ -834,7 +838,7 @@ END $id = $wgUser->blockedBy(); $reason = $wgUser->blockedFor(); $ip = $wgIP; - + if ( is_numeric( $id ) ) { $name = User::whoIs( $id ); } else { @@ -885,11 +889,11 @@ END function proxyCheck() { global $wgBlockOpenProxies, $wgProxyPorts, $wgProxyScriptPath; global $wgIP, $wgUseMemCached, $wgMemc, $wgDBname, $wgProxyMemcExpiry; - + if ( !$wgBlockOpenProxies ) { return; } - + # Get MemCached key $skip = false; if ( $wgUseMemCached ) { @@ -931,7 +935,7 @@ END wfProfileIn( $fname ); $db =& wfGetDB( DB_MASTER ); - + // This is the revision the editor started from $baseRevision = Revision::loadFromTimestamp( $db, $this->mArticle->mTitle, $this->edittime ); @@ -949,7 +953,7 @@ END return false; } $currentText = $currentRevision->getText(); - + if( wfMerge( $baseText, $editText, $currentText, $result ) ){ $editText = $result; wfProfileOut( $fname ); @@ -980,7 +984,7 @@ END */ function sectionAnchor( $text ) { $headline = Sanitizer::decodeCharReferences( $text ); - # strip out HTML + # strip out HTML $headline = preg_replace( '/<.*?' . '>/', '', $headline ); $headline = trim( $headline ); $sectionanchor = '#' . urlencode( str_replace( ' ', '_', $headline ) ); @@ -1120,7 +1124,7 @@ END $toolbar.="/*]]>*/\n"; return $toolbar; } - + /** * Output preview text only. This can be sucked into the edit page * via JavaScript, and saves the server time rendering the skin as @@ -1162,7 +1166,7 @@ END if ( $oldtext != wfMsg( 'noarticletext' ) || $newtext != '' ) { $difftext = DifferenceEngine::getDiff( $oldtext, $newtext, $oldtitle, $newtitle ); } - + return '
    ' . $difftext . '
    '; } diff --git a/includes/GlobalFunctions.php b/includes/GlobalFunctions.php index 0b85b00dd3..a0cee454e9 100644 --- a/includes/GlobalFunctions.php +++ b/includes/GlobalFunctions.php @@ -8,7 +8,7 @@ /** * Some globals and requires needed */ - + /** * Total number of articles * @global integer $wgNumberOfArticles @@ -67,14 +67,14 @@ if( !function_exists('is_a') ) { # UTF-8 substr function based on a PHP manual comment if ( !function_exists( 'mb_substr' ) ) { - function mb_substr( $str, $start ) { + function mb_substr( $str, $start ) { preg_match_all( '/./us', $str, $ar ); if( func_num_args() >= 3 ) { - $end = func_get_arg( 2 ); - return join( '', array_slice( $ar[0], $start, $end ) ); - } else { - return join( '', array_slice( $ar[0], $start ) ); + $end = func_get_arg( 2 ); + return join( '', array_slice( $ar[0], $start, $end ) ); + } else { + return join( '', array_slice( $ar[0], $start ) ); } } } @@ -235,7 +235,7 @@ function wfReadOnly() { if ( '' == $wgReadOnlyFile ) { return false; } - + // Set $wgReadOnly and unset $wgReadOnlyFile, for faster access next time if ( is_file( $wgReadOnlyFile ) ) { $wgReadOnly = true; @@ -250,7 +250,7 @@ function wfReadOnly() { /** * Get a message from anywhere, for the current user language * - * @param string + * @param string */ function wfMsg( $key ) { $args = func_get_args(); @@ -302,7 +302,7 @@ function wfMsgNoDBForContent( $key ) { function wfMsgReal( $key, $args, $useDB, $forContent=false ) { $fname = 'wfMsgReal'; wfProfileIn( $fname ); - + $message = wfMsgGetKey( $key, $useDB, $forContent ); $message = wfMsgReplaceArgs( $message, $args ); wfProfileOut( $fname ); @@ -321,7 +321,7 @@ function wfMsgGetKey( $key, $useDB, $forContent = false ) { global $wgParser, $wgMsgParserOptions; global $wgContLang, $wgLanguageCode; global $wgMessageCache, $wgLang; - + if( is_object( $wgMessageCache ) ) { $message = $wgMessageCache->get( $key, $useDB, $forContent ); } else { @@ -332,7 +332,7 @@ function wfMsgGetKey( $key, $useDB, $forContent = false ) { } wfSuppressWarnings(); - + if( is_object( $lang ) ) { $message = $lang->getMessage( $key ); } else { @@ -358,7 +358,7 @@ function wfMsgGetKey( $key, $useDB, $forContent = false ) { */ function wfMsgReplaceArgs( $message, $args ) { static $replacementKeys = array( '$1', '$2', '$3', '$4', '$5', '$6', '$7', '$8', '$9' ); - + # Fix windows line-endings # Some messages are split with explode("\n", $msg) $message = str_replace( "\r", '', $message ); @@ -449,7 +449,7 @@ function wfBacktrace() { if ( !function_exists( 'debug_backtrace' ) ) { return false; } - + if ( $wgCommandLineMode ) { $msg = ''; } else { @@ -519,7 +519,7 @@ function wfViewPrevNext( $offset, $limit, $link, $query = '', $atend = false ) { $fmtLimit = $wgLang->formatNum( $limit ); $prev = wfMsg( 'prevn', $fmtLimit ); $next = wfMsg( 'nextn', $fmtLimit ); - + if( is_object( $link ) ) { $title =& $link; } else { @@ -528,7 +528,7 @@ function wfViewPrevNext( $offset, $limit, $link, $query = '', $atend = false ) { return false; } } - + $sk = $wgUser->getSkin(); if ( 0 != $offset ) { $po = $offset - $limit; @@ -611,7 +611,7 @@ function wfCheckLimits( $deflimit = 50, $optionname = 'rclimit' ) { * @param string $text Text to be escaped */ function wfEscapeWikiText( $text ) { - $text = str_replace( + $text = str_replace( array( '[', '|', '\'', 'ISBN ' , '://' , "\n=", '{{' ), array( '[', '|', ''', 'ISBN ', '://' , "\n=", '{{' ), htmlspecialchars($text) ); @@ -655,7 +655,7 @@ function wfEscapeJsString( $string ) { '\'' => '\\\'', "\n" => "\\n", "\r" => "\\r", - + # To avoid closing the element or CDATA section "<" => "\\x3c", ">" => "\\x3e", @@ -741,7 +741,7 @@ function wfPurgeSquidServers ($urlArr) { /** * Windows-compatible version of escapeshellarg() - * Windows doesn't recognise single-quotes in the shell, but the escapeshellarg() + * Windows doesn't recognise single-quotes in the shell, but the escapeshellarg() * function puts single quotes in regardless of OS */ function wfEscapeShellArg( ) { @@ -754,7 +754,7 @@ function wfEscapeShellArg( ) { } else { $first = false; } - + if ( wfIsWindows() ) { $retVal .= '"' . str_replace( '"','\"', $arg ) . '"'; } else { @@ -843,8 +843,8 @@ function wfHttpError( $code, $label, $desc ) { header( 'Content-type: text/html' ); print "" . - htmlspecialchars( $label ) . - "

    " . + htmlspecialchars( $label ) . + "

    " . htmlspecialchars( $label ) . "

    " . htmlspecialchars( $desc ) . @@ -958,7 +958,7 @@ function wfNegotiateType( $cprefs, $sprefs ) { * Array lookup * Returns an array where the values in the first array are replaced by the * values in the second array with the corresponding keys - * + * * @return array */ function wfArrayLookup( $a, $b ) { @@ -1005,7 +1005,7 @@ function wfRestoreWarnings() { # Autodetect, convert and provide timestamps of various types -/** +/** * Unix time - the number of seconds since 1970-01-01 00:00:00 UTC */ define('TS_UNIX', 0); @@ -1034,6 +1034,10 @@ define('TS_RFC2822', 3); */ define('TS_EXIF', 4); +/** + * Oracle format time. + */ +define('TS_ORACLE', 5); /** * @param mixed $outputtype A timestamp in one of the supported formats, the @@ -1042,8 +1046,9 @@ define('TS_EXIF', 4); * @return string Time in the format specified in $outputtype */ function wfTimestamp($outputtype=TS_UNIX,$ts=0) { - if ($ts==0) { - $uts=time(); +wfdebug("ts: $ts\n"); + if ($ts==0) { + $uts=time(); } elseif (preg_match("/^(\d{4})\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d)$/",$ts,$da)) { # TS_DB $uts=gmmktime((int)$da[4],(int)$da[5],(int)$da[6], @@ -1059,13 +1064,17 @@ function wfTimestamp($outputtype=TS_UNIX,$ts=0) { } elseif (preg_match("/^(\d{1,13})$/",$ts,$datearray)) { # TS_UNIX $uts=$ts; + } elseif (preg_match('/^(\d{1,2})-(...)-(\d\d(\d\d)?) (\d\d)\.(\d\d)\.(\d\d)/', $ts, $da)) { + # TS_ORACLE + $uts = strtotime(preg_replace('/(\d\d)\.(\d\d)\.(\d\d)(\.(\d+))?/', "$1:$2:$3", + str_replace("+00:00", "UTC", $ts))); } else { # Bogus value; fall back to the epoch... wfDebug("wfTimestamp() fed bogus time value: $outputtype; $ts\n"); $uts = 0; } - + switch($outputtype) { case TS_UNIX: return $uts; @@ -1078,6 +1087,8 @@ function wfTimestamp($outputtype=TS_UNIX,$ts=0) { return gmdate( 'Y:m:d H:i:s', $uts ); case TS_RFC2822: return gmdate( 'D, d M Y H:i:s', $uts ) . ' GMT'; + case TS_ORACLE: + return gmdate( 'd-M-y h.i.s A', $uts) . ' +00:00'; default: wfDebugDieBacktrace( 'wfTimestamp() called with illegal output type.'); } @@ -1103,13 +1114,13 @@ function wfTimestampOrNull( $outputtype = TS_UNIX, $ts = null ) { * * @return bool True if it's windows, False otherwise. */ -function wfIsWindows() { - if (substr(php_uname(), 0, 7) == 'Windows') { - return true; - } else { - return false; - } -} +function wfIsWindows() { + if (substr(php_uname(), 0, 7) == 'Windows') { + return true; + } else { + return false; + } +} /** * Swap two variables @@ -1228,7 +1239,7 @@ function &HTMLnamespaceselector($selected = '', $allnamespaces = null) { /** Global singleton instance of MimeMagic. This is initialized on demand, * please always use the wfGetMimeMagic() function to get the instance. -* +* * @private */ $wgMimeMagic= NULL; @@ -1241,7 +1252,7 @@ $wgMimeMagic= NULL; */ function &wfGetMimeMagic() { global $wgMimeMagic; - + if (!is_null($wgMimeMagic)) { return $wgMimeMagic; } @@ -1250,9 +1261,9 @@ function &wfGetMimeMagic() { #include on demand require_once("MimeMagic.php"); } - + $wgMimeMagic= new MimeMagic(); - + return $wgMimeMagic; } diff --git a/includes/ImagePage.php b/includes/ImagePage.php index 5808580952..f3ead557c1 100644 --- a/includes/ImagePage.php +++ b/includes/ImagePage.php @@ -312,7 +312,7 @@ class ImagePage extends Article { if ( $line ) { $list =& new ImageHistoryList( $sk ); $s = $list->beginImageHistoryList() . - $list->imageHistoryLine( true, $line->img_timestamp, + $list->imageHistoryLine( true, wfTimestamp(TS_MW, $line->img_timestamp), $this->mTitle->getDBkey(), $line->img_user, $line->img_user_text, $line->img_size, $line->img_description ); @@ -344,8 +344,8 @@ class ImagePage extends Article { $imagelinks = $dbr->tableName( 'imagelinks' ); $sql = "SELECT page_namespace,page_title FROM $imagelinks,$page WHERE il_to=" . - $dbr->addQuotes( $this->mTitle->getDBkey() ) . " AND il_from=page_id" - . " LIMIT 500"; # quickie emergency brake + $dbr->addQuotes( $this->mTitle->getDBkey() ) . " AND il_from=page_id"; + $sql = $dbr->limitResult($sql, 500, 0); $res = $dbr->query( $sql, "ImagePage::imageLinks" ); if ( 0 == $dbr->numRows( $res ) ) { diff --git a/includes/LinkCache.php b/includes/LinkCache.php index dae7533ccf..4eda8d20ca 100644 --- a/includes/LinkCache.php +++ b/includes/LinkCache.php @@ -18,7 +18,7 @@ define ('LINKCACHE_PAGE', 3); * @package MediaWiki * @subpackage Cache */ -class LinkCache { +class LinkCache { // Increment $mClassVer whenever old serialized versions of this class // becomes incompatible with the new version. /* private */ var $mClassVer = 3; @@ -33,7 +33,7 @@ class LinkCache { global $wgDBname; return $wgDBname.':lc:title:'.$title; } - + function LinkCache() { $this->mActive = true; $this->mPreFilled = false; @@ -51,10 +51,10 @@ class LinkCache { /** * General accessor to get/set whether SELECT FOR UPDATE should be used */ - function forUpdate( $update = NULL ) { + function forUpdate( $update = NULL ) { return wfSetVar( $this->mForUpdate, $update ); } - + function getGoodLinkID( $title ) { if ( array_key_exists( $title, $this->mGoodLinks ) ) { return $this->mGoodLinks[$title]; @@ -64,7 +64,7 @@ class LinkCache { } function isBadLink( $title ) { - return array_key_exists( $title, $this->mBadLinks ); + return array_key_exists( $title, $this->mBadLinks ); } function addGoodLinkObj( $id, $title ) { @@ -90,11 +90,11 @@ class LinkCache { function addImageLinkObj( $nt ) { if ( $this->mActive ) { $this->mImageLinks[$nt->getDBkey()] = 1; } } - + function addCategoryLink( $title, $sortkey ) { if ( $this->mActive ) { $this->mCategoryLinks[$title] = $sortkey; } } - + function addCategoryLinkObj( &$nt, $sortkey ) { $this->addCategoryLink( $nt->getDBkey(), $sortkey ); } @@ -103,7 +103,7 @@ class LinkCache { unset( $this->mBadLinks[$title] ); $this->clearLink( $title ); } - + function clearLink( $title ) { global $wgMemc, $wgLinkCacheMemcached; if( $wgLinkCacheMemcached ) @@ -126,11 +126,11 @@ class LinkCache { return 0; } } - + function addLinkObj( &$nt ) { global $wgMemc, $wgLinkCacheMemcached, $wgAntiLockFlags; $title = $nt->getPrefixedDBkey(); - if ( $this->isBadLink( $title ) ) { return 0; } + if ( $this->isBadLink( $title ) ) { return 0; } $id = $this->getGoodLinkID( $title ); if ( 0 != $id ) { return $id; } @@ -140,11 +140,11 @@ class LinkCache { $ns = $nt->getNamespace(); $t = $nt->getDBkey(); - if ( '' == $title ) { + if ( '' == $title ) { wfProfileOut( $fname ); - return 0; + return 0; } - + $id = NULL; if( $wgLinkCacheMemcached ) $id = $wgMemc->get( $key = $this->getKey( $title ) ); @@ -159,14 +159,17 @@ class LinkCache { $options = array(); } - $id = $db->selectField( 'page', 'page_id', array( 'page_namespace' => $ns, 'page_title' => $t ), $fname, $options ); + $id = $db->selectField( 'page', 'page_id', + array( 'page_namespace' => $ns, 'page_title' => $t ), + $fname, $options ); +wfdebug("link cache: id=$id\n"); if ( !$id ) { $id = 0; } if( $wgLinkCacheMemcached ) $wgMemc->add( $key, $id, 3600*24 ); } - + if( 0 == $id ) { $this->addBadLinkObj( $nt ); } else { @@ -188,13 +191,13 @@ class LinkCache { $this->suspend(); $id = $fromtitle->getArticleID(); $this->resume(); - + if( $id == 0 ) { wfDebug( "$fname - got id 0 for title '" . $fromtitle->getPrefixedDBkey() . "'\n" ); wfProfileOut( $fname ); return; } - + if ( $this->mForUpdate ) { $db =& wfGetDB( DB_MASTER ); if ( !( $wgAntiLockFlags & ALF_NO_LINK_LOCK ) ) { @@ -254,7 +257,7 @@ class LinkCache { function getImageDeletions() { return array_diff_assoc( $this->mOldImageLinks, $this->mImageLinks ); } - + function getPageAdditions() { $set = array_diff( array_keys( $this->mPageLinks ), array_keys( $this->mOldPageLinks ) ); $out = array(); @@ -263,7 +266,7 @@ class LinkCache { } return $out; } - + function getPageDeletions() { $set = array_diff( array_keys( $this->mOldPageLinks ), array_keys( $this->mPageLinks ) ); $out = array(); @@ -308,14 +311,14 @@ class LinkCache { $add = $this->getPageAdditions(); break; default: # LINKCACHE_IMAGE - return false; + return false; } - + return true; } /** - * Clears cache + * Clears cache */ function clear() { $this->mPageLinks = array(); @@ -347,7 +350,7 @@ class LinkCache { * @subpackage Cache */ class LinkBatch { - /** + /** * 2-d array, first index namespace, second index dbkey, value arbitrary */ var $data = array(); @@ -357,7 +360,7 @@ class LinkBatch { $this->addObj( $item ); } } - + function addObj( $title ) { $this->add( $title->getNamespace(), $title->getDBkey() ); } @@ -389,7 +392,7 @@ class LinkBatch { $page = $dbr->tableName( 'page' ); $sql = "SELECT page_id, page_namespace, page_title FROM $page WHERE " . $this->constructSet( 'page', $dbr ); - + // Do query $res = $dbr->query( $sql, $fname ); @@ -414,7 +417,7 @@ class LinkBatch { wfProfileOut( $fname ); } - + /** * Construct a WHERE clause which will match all the given titles. * Give the appropriate table's field name prefix ('page', 'pl', etc). diff --git a/includes/LoadBalancer.php b/includes/LoadBalancer.php index ee402d68e5..54fc617d9b 100644 --- a/includes/LoadBalancer.php +++ b/includes/LoadBalancer.php @@ -74,9 +74,9 @@ class LoadBalancer { $this->mGroupLoads[$group][$i] = $ratio; } } - } + } } - + /** * Given an array of non-normalised probabilities, this function will select * an element and return the appropriate key @@ -101,7 +101,7 @@ class LoadBalancer { } $max = mt_getrandmax(); $rand = mt_rand(0, $max) / $max * $sum; - + $sum = 0; foreach ( $weights as $i => $w ) { $sum += $w; @@ -130,7 +130,7 @@ class LoadBalancer { if ( $sum == 0 ) { # No appropriate DB servers except maybe the master and some slaves with zero load # Do NOT use the master - # Instead, this function will return false, triggering read-only mode, + # Instead, this function will return false, triggering read-only mode, # and a lagged slave will be used instead. unset ( $loads[0] ); } @@ -147,7 +147,7 @@ class LoadBalancer { /** * Get the index of the reader connection, which may be a slave - * This takes into account load ratios and lag times. It should + * This takes into account load ratios and lag times. It should * always return a consistent index during a given invocation * * Side effect: opens connections to databases @@ -185,15 +185,15 @@ class LoadBalancer { if ( $i !== false ) { wfDebug( "Using reader #$i: {$this->mServers[$i]['host']}...\n" ); $this->openConnection( $i ); - + if ( !$this->isOpen( $i ) ) { wfDebug( "Failed\n" ); unset( $loads[$i] ); $sleepTime = 0; } else { $status = $this->mConnections[$i]->getStatus(); - if ( isset( $this->mServers[$i]['max threads'] ) && - $status['Threads_running'] > $this->mServers[$i]['max threads'] ) + if ( isset( $this->mServers[$i]['max threads'] ) && + $status['Threads_running'] > $this->mServers[$i]['max threads'] ) { # Slave is lagged, wait for a while $sleepTime = 5000 * $status['Threads_connected']; @@ -232,7 +232,7 @@ class LoadBalancer { wfProfileOut( $fname ); return $i; } - + /** * Get a random server to use in a query group */ @@ -245,7 +245,7 @@ class LoadBalancer { wfDebug( "Query group $group => $i\n" ); return $i; } - + /** * Set the master wait position * If a DB_SLAVE connection has been opened already, waits @@ -269,7 +269,7 @@ class LoadBalancer { $this->mServers[$i]['slave pos'] = $this->mConnections[$i]->getSlavePos(); $this->mLaggedSlaveMode = true; } - } + } } wfProfileOut( $fname ); } @@ -279,7 +279,7 @@ class LoadBalancer { */ function doWait( $index ) { global $wgMemc; - + $retVal = false; # Debugging hacks @@ -314,7 +314,7 @@ class LoadBalancer { } } return $retVal; - } + } /** * Get a connection by index @@ -323,7 +323,7 @@ class LoadBalancer { { $fname = 'LoadBalancer::getConnection'; wfProfileIn( $fname ); - + # Query groups $groupIndex = false; foreach ( $groups as $group ) { @@ -333,9 +333,9 @@ class LoadBalancer { break; } } - + # Operation-based index - if ( $i == DB_SLAVE ) { + if ( $i == DB_SLAVE ) { $i = $this->getReaderIndex(); } elseif ( $i == DB_MASTER ) { $i = $this->getWriterIndex(); @@ -350,7 +350,7 @@ class LoadBalancer { } # Now we have an explicit index into the servers array $this->openConnection( $i, $fail ); - + wfProfileOut( $fname ); return $this->mConnections[$i]; } @@ -369,6 +369,7 @@ class LoadBalancer { if ( !$this->isOpen( $i ) ) { $this->mConnections[$i] = $this->reallyOpenConnection( $this->mServers[$i] ); } + if ( !$this->isOpen( $i ) ) { wfDebug( "Failed to connect to database $i at {$this->mServers[$i]['host']}\n" ); if ( $fail ) { @@ -390,15 +391,15 @@ class LoadBalancer { if( !is_integer( $index ) ) { return false; } - if ( array_key_exists( $index, $this->mConnections ) && is_object( $this->mConnections[$index] ) && - $this->mConnections[$index]->isOpen() ) + if ( array_key_exists( $index, $this->mConnections ) && is_object( $this->mConnections[$index] ) && + $this->mConnections[$index]->isOpen() ) { return true; } else { return false; } } - + /** * Really opens a connection * @private @@ -407,7 +408,7 @@ class LoadBalancer { if( !is_array( $server ) ) { wfDebugDieBacktrace( 'You must update your load-balancing configuration. See DefaultSettings.php entry for $wgDBservers.' ); } - + extract( $server ); # Get class for this database type $class = 'Database' . ucfirst( $type ); @@ -418,13 +419,13 @@ class LoadBalancer { # Create object return new $class( $host, $user, $password, $dbname, 1, $flags ); } - + function reportConnectionError( &$conn ) { $fname = 'LoadBalancer::reportConnectionError'; wfProfileIn( $fname ); # Prevent infinite recursion - + static $reporting = false; if ( !$reporting ) { $reporting = true; @@ -441,7 +442,7 @@ class LoadBalancer { } wfProfileOut( $fname ); } - + function getWriterIndex() { return 0; @@ -470,7 +471,7 @@ class LoadBalancer { function saveMasterPos() { global $wgSessionStarted; if ( $wgSessionStarted && count( $this->mServers ) > 1 ) { - # If this entire request was served from a slave without opening a connection to the + # If this entire request was served from a slave without opening a connection to the # master (however unlikely that may be), then we can fetch the position from the slave. if ( empty( $this->mConnections[0] ) ) { $conn =& $this->getConnection( DB_SLAVE ); @@ -556,7 +557,7 @@ class LoadBalancer { } return array( $host, $maxLag ); } - + /** * Get lag time for each DB * Results are cached for a short time in memcached diff --git a/includes/LogPage.php b/includes/LogPage.php index 4447dd76a0..1fc51168f0 100644 --- a/includes/LogPage.php +++ b/includes/LogPage.php @@ -2,17 +2,17 @@ # # Copyright (C) 2002, 2004 Brion Vibber # http://www.mediawiki.org/ -# +# # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or +# the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. @@ -67,7 +67,7 @@ class LogPage { 'log_params' => $this->params ), $fname ); - + # And update recentchanges if ( $this->updateRecentChanges ) { $titleObj = Title::makeTitle( NS_SPECIAL, 'Log/' . $this->type ); @@ -78,7 +78,7 @@ class LogPage { else $rcComment .= ': ' . $this->comment; } - + RecentChange::notifyLog( $now, $titleObj, $wgUser, $rcComment ); } return true; @@ -92,7 +92,7 @@ class LogPage { wfRunHooks( 'LogPageValidTypes', array( &$types) ); return $types; } - + /** * @static */ @@ -108,14 +108,14 @@ class LogPage { ); return $actions[$type]; } - + /** * @static */ function isLogType( $type ) { return in_array( $type, LogPage::validTypes() ); } - + /** * @static */ @@ -133,7 +133,7 @@ class LogPage { return str_replace( '_', ' ', wfMsg( $typeText[$type] ) ); } - + /** * @static */ @@ -151,7 +151,7 @@ class LogPage { return wfMsg( $headerText[$type] ); } - + /** * @static */ @@ -175,7 +175,7 @@ class LogPage { $key = "$type/$action"; if( isset( $actions[$key] ) ) { if( is_null( $title ) ) { - $rv=wfMsgForContent( $actions[$key] ); + $rv=wfMsg( $actions[$key] ); } else { if( $skin ) { if ( $type == 'move' ) { @@ -190,10 +190,10 @@ class LogPage { $titleLink = $title->getPrefixedText(); } if( count( $params ) == 0 ) { - $rv = wfMsgForContent( $actions[$key], $titleLink ); + $rv = wfMsg( $actions[$key], $titleLink ); } else { array_unshift( $params, $titleLink ); - $rv = wfMsgReal( $actions[$key], $params, true, true ); + $rv = wfMsgReal( $actions[$key], $params, true, false ); } } } else { @@ -218,18 +218,18 @@ class LogPage { if ( !is_array( $params ) ) { $params = array( $params ); } - + $this->action = $action; $this->target =& $target; $this->comment = $comment; $this->params = LogPage::makeParamBlob( $params ); - + $this->actionText = LogPage::actionText( $this->type, $action, $target, NULL, $params ); return $this->saveContent(); } - /** + /** * Create a blob from a parameter array * @static */ diff --git a/includes/Namespace.php b/includes/Namespace.php index d1d06aca06..8466137427 100644 --- a/includes/Namespace.php +++ b/includes/Namespace.php @@ -3,13 +3,13 @@ * Provide things related to namespaces * @package MediaWiki */ - + /** * This is not a valid entry point, perform no further processing unless MEDIAWIKI is defined */ if( defined( 'MEDIAWIKI' ) ) { - + /** * Definitions of the NS_ constants are in Defines.php * @private @@ -56,8 +56,8 @@ class Namespace { * @return bool */ function isMovable( $index ) { - if ( $index < NS_MAIN || $index == NS_IMAGE || $index == NS_CATEGORY ) { - return false; + if ( $index < NS_MAIN || $index == NS_IMAGE || $index == NS_CATEGORY ) { + return false; } return true; } @@ -71,10 +71,10 @@ class Namespace { return ( $index == NS_TALK || $index == NS_USER_TALK || $index == NS_PROJECT_TALK || $index == NS_IMAGE_TALK || $index == NS_MEDIAWIKI_TALK || $index == NS_TEMPLATE_TALK || - $index == NS_HELP_TALK || $index == NS_CATEGORY_TALK + $index == NS_HELP_TALK || $index == NS_CATEGORY_TALK || ( (isset($wgExtraNamespaces) && $index % 2) ) ); - + } /** diff --git a/includes/ObjectCache.php b/includes/ObjectCache.php index dbf6221c96..f01171e0a4 100644 --- a/includes/ObjectCache.php +++ b/includes/ObjectCache.php @@ -53,9 +53,9 @@ function &wfGetCache( $inputType ) { } if ( $type == CACHE_MEMCACHED ) { - if ( !array_key_exists( CACHE_MEMCACHED, $wgCaches ) ){ + if ( !array_key_exists( CACHE_MEMCACHED, $wgCaches ) ){ require_once( 'memcached-client.php' ); - + if (!class_exists("MemcachedClientforWiki")) { class MemCachedClientforWiki extends memcached { function _debugprint( $text ) { @@ -64,7 +64,7 @@ function &wfGetCache( $inputType ) { } } - $wgCaches[CACHE_DB] = new MemCachedClientforWiki( + $wgCaches[CACHE_DB] = new MemCachedClientforWiki( array('persistant' => false, 'compress_threshold' => 1500 ) ); $cache =& $wgCaches[CACHE_DB]; $cache->set_servers( $wgMemCachedServers ); @@ -94,7 +94,7 @@ function &wfGetCache( $inputType ) { } $cache =& $wgCaches[CACHE_DB]; } - + if ( $cache === false ) { if ( !array_key_exists( CACHE_NONE, $wgCaches ) ) { $wgCaches[CACHE_NONE] = new FakeMemCachedClient; diff --git a/includes/PageHistory.php b/includes/PageHistory.php index 1bf67a5d46..5079a3b9d9 100644 --- a/includes/PageHistory.php +++ b/includes/PageHistory.php @@ -64,6 +64,8 @@ class PageHistory { $fname = 'PageHistory::history'; wfProfileIn( $fname ); + $dbr = wfGetDB(DB_SLAVE); + /* * Setup page variables. */ @@ -93,7 +95,9 @@ class PageHistory { /* Offset must be an integral. */ if (!strlen($offset) || !preg_match("/^[0-9]+$/", $offset)) $offset = 0; - +# $offset = $dbr->timestamp($offset); + $dboffset = $dbr->timestamp($offset); +wfdebug("offset=[$offset] dboffset=[$dboffset]\n"); /* * "go=last" means to jump to the last history page. */ @@ -102,7 +106,8 @@ class PageHistory { case "first": if (($lastid = $this->getLastOffsetForPaging($id, $limit)) === NULL) break; - $gourl = $wgTitle->getLocalURL("action=history&limit={$limit}&offset={$lastid}"); + $gourl = $wgTitle->getLocalURL("action=history&limit={$limit}&offset=". + wfTimestamp(TS_MW, $lastid)); break; default: $gourl = NULL; @@ -122,7 +127,7 @@ class PageHistory { * previous revisions when generating the URL. */ $direction = $this->getDirection(); - $revisions = $this->fetchRevisions($limit, $offset, $direction); + $revisions = $this->fetchRevisions($limit, $dboffset, $direction); $navbar = $this->makeNavbar($revisions, $offset, $limit, $direction); /* @@ -261,7 +266,7 @@ class PageHistory { /** @todo document */ function revLink( $row ) { global $wgUser, $wgLang; - $date = $wgLang->timeanddate( $row->rev_timestamp, true ); + $date = $wgLang->timeanddate( wfTimestamp(TS_MW, $row->rev_timestamp), true ); if( $row->rev_deleted && !$wgUser->isAllowed( 'undelete' ) ) { return $date; } else { @@ -382,7 +387,8 @@ class PageHistory { $db =& wfGetDB(DB_SLAVE); $revision = $db->tableName( 'revision' ); $sql = "SELECT rev_timestamp FROM $revision WHERE rev_page = $id " . - "ORDER BY rev_timestamp ASC LIMIT $step"; + "ORDER BY rev_timestamp ASC"; + $sql = $db->limitResult($sql, $step, 0); $res = $db->query( $sql, "PageHistory::getLastOffsetForPaging" ); $n = $db->numRows( $res ); @@ -429,8 +435,6 @@ class PageHistory { if ($offset) $offsets .= " AND rev_timestamp $oper '$offset' "; - if ($limit) - $limits .= " LIMIT $limitplus "; $page_id = $this->mTitle->getArticleID(); $sql = "SELECT rev_id,rev_user," . @@ -438,8 +442,9 @@ class PageHistory { "FROM $revision $use_index " . "WHERE rev_page=$page_id " . $offsets . - "ORDER BY rev_timestamp $dirs " . - $limits; + "ORDER BY rev_timestamp $dirs "; + if ($limit) + $sql = $db->limitResult($sql, $limitplus, 0); $res = $db->query($sql, "PageHistory::fetchRevisions"); $result = array(); @@ -480,8 +485,8 @@ class PageHistory { $revisions = array_slice($revisions, 0, $limit); $pageid = $this->mTitle->getArticleID(); - $latestTimestamp = $this->getLatestOffset( $pageid ); - $earliestTimestamp = $this->getEarliestOffset( $pageid ); + $latestTimestamp = wfTimestamp(TS_MW, $this->getLatestOffset( $pageid )); + $earliestTimestamp = wfTimestamp(TS_MW, $this->getEarliestOffset( $pageid )); /* * When we're displaying previous revisions, we need to reverse @@ -498,8 +503,8 @@ class PageHistory { $lowts = $hights = 0; if( count( $revisions ) ) { - $latestShown = $revisions[0]->rev_timestamp; - $earliestShown = $revisions[count($revisions) - 1]->rev_timestamp; + $latestShown = wfTimestamp(TS_MW, $revisions[0]->rev_timestamp); + $earliestShown = wfTimestamp(TS_MW, $revisions[count($revisions) - 1]->rev_timestamp); } $firsturl = $wgTitle->escapeLocalURL("action=history&limit={$limit}&go=first"); @@ -507,13 +512,14 @@ class PageHistory { $firsttext = wfMsgHtml('histfirst'); $lasttext = wfMsgHtml('histlast'); - $prevurl = $wgTitle->escapeLocalURL("action=history&dir=prev&offset={$latestShown}&limit={$limit}"); - $nexturl = $wgTitle->escapeLocalURL("action=history&offset={$earliestShown}&limit={$limit}"); + $prevurl = $wgTitle->escapeLocalURL("action=history&dir=prev&offset=$latestShown&limit={$limit}"); + $nexturl = $wgTitle->escapeLocalURL("action=history&offset=$earliestShown&limit={$limit}"); $urls = array(); foreach (array(20, 50, 100, 250, 500) as $num) { $urls[] = "escapeLocalURL( - "action=history&offset={$offset}&limit={$num}")."\">".$wgLang->formatNum($num).""; + "action=history&offset=" . wfTimestamp(TS_MW, $offset) . + "&limit={$num}")."\">".$wgLang->formatNum($num).""; } $bits = implode($urls, ' | '); diff --git a/includes/QueryPage.php b/includes/QueryPage.php index 8c6e110364..3d94b6c577 100644 --- a/includes/QueryPage.php +++ b/includes/QueryPage.php @@ -20,7 +20,7 @@ $wgQueryPages = array( array( 'DeadendPagesPage', 'Deadendpages' ), array( 'DisambiguationsPage', 'Disambiguations' ), array( 'DoubleRedirectsPage', 'DoubleRedirects' ), - array( 'ListUsersPage', 'Listusers' ), + array( 'ListUsersPage', 'Listusers' ), array( 'LonelyPagesPage', 'Lonelypages' ), array( 'LongPagesPage', 'Longpages' ), array( 'NewPagesPage', 'Newpages' ), @@ -118,7 +118,7 @@ class QueryPage { function getPageHeader( ) { return ''; } - + /** * If using extra form wheely-dealies, return a set of parameters here * as an associative array. They will be encoded and added to the paging @@ -128,7 +128,7 @@ class QueryPage { function linkParameters() { return array(); } - + /** * Some special pages (for example SpecialListusers) might not return the * current object formatted, but return the previous one instead. @@ -151,7 +151,7 @@ class QueryPage { } $querycache = $dbr->tableName( 'querycache' ); - + if ( $ignoreErrors ) { $ignoreW = $dbw->ignoreErrors( true ); $ignoreR = $dbr->ignoreErrors( true ); @@ -236,21 +236,21 @@ class QueryPage { $wgOut->addWikiText( wfMsg( 'perfcached' ) ); } } - - $res = $dbr->query( $sql . $this->getOrder() . - $dbr->limitResult( $limit,$offset ), $fname ); + + $sql = $dbr->limitResult($sql . $this->getOrder(), $limit, $offset); + $res = $dbr->query( $sql ); $num = $dbr->numRows($res); - + $sk = $wgUser->getSkin( ); if($shownavigation) { $wgOut->addHTML( $this->getPageHeader() ); $top = wfShowingResults( $offset, $num); $wgOut->addHTML( "

    {$top}\n" ); - + # often disable 'next' link when we reach the end if($num < $limit) { $atend = true; } else { $atend = false; } - + $sl = wfViewPrevNext( $offset, $limit , $wgContLang->specialPage( $sname ), wfArrayToCGI( $this->linkParameters() ), $atend ); @@ -279,7 +279,7 @@ class QueryPage { $s .= "{$format}

  • \n"; } } - + $dbr->freeResult( $res ); $s .= ''; $wgOut->addHTML( $s ); diff --git a/includes/RecentChange.php b/includes/RecentChange.php index 69cbb17c9f..48dcf12dae 100644 --- a/includes/RecentChange.php +++ b/includes/RecentChange.php @@ -118,8 +118,9 @@ class RecentChange } # Fixup database timestamps - $this->mAttribs['rc_timestamp']=$dbw->timestamp($this->mAttribs['rc_timestamp']); - $this->mAttribs['rc_cur_time']=$dbw->timestamp($this->mAttribs['rc_cur_time']); + $this->mAttribs['rc_timestamp'] = $dbw->timestamp($this->mAttribs['rc_timestamp']); + $this->mAttribs['rc_cur_time'] = $dbw->timestamp($this->mAttribs['rc_cur_time']); + $this->mAttribs['rc_id'] = $dbw->nextSequenceValue( 'rc_rc_id_seq' ); # Insert new row $dbw->insert( 'recentchanges', $this->mAttribs, $fname ); @@ -357,6 +358,7 @@ class RecentChange function loadFromRow( $row ) { $this->mAttribs = get_object_vars( $row ); + $this->mAttribs["rc_timestamp"] = wfTimestamp(TS_MW, $this->mAttribs["rc_timestamp"]); $this->mExtra = array(); } @@ -364,7 +366,7 @@ class RecentChange function loadFromCurRow( $row ) { $this->mAttribs = array( - 'rc_timestamp' => $row->rev_timestamp, + 'rc_timestamp' => wfTimestamp(TS_MW, $row->rev_timestamp), 'rc_cur_time' => $row->rev_timestamp, 'rc_user' => $row->rev_user, 'rc_user_text' => $row->rev_user_text, diff --git a/includes/Revision.php b/includes/Revision.php index e7e9b3ba44..d9cdf551f7 100644 --- a/includes/Revision.php +++ b/includes/Revision.php @@ -25,8 +25,9 @@ class Revision { return Revision::newFromConds( array( 'page_id=rev_page', 'rev_id' => IntVal( $id ) ) ); + return $ret; } - + /** * Load either the current, or a specified, revision * that's attached to a given title. If not attached @@ -50,7 +51,7 @@ class Revision { 'page_namespace' => $title->getNamespace(), 'page_title' => $title->getDbkey() ) ); } - + /** * Load either the current, or a specified, revision * that's attached to a given page. If not attached @@ -68,13 +69,14 @@ class Revision { } else { $matchId = 'page_latest'; } - return Revision::loadFromConds( + $ret = Revision::loadFromConds( $db, array( "rev_id=$matchId", 'rev_page' => IntVal( $pageid ), 'page_id=rev_page' ) ); + return $ret; } - + /** * Load either the current, or a specified, revision * that's attached to a given page. If not attached @@ -99,7 +101,7 @@ class Revision { 'page_namespace' => $title->getNamespace(), 'page_title' => $title->getDbkey() ) ); } - + /** * Load the revision for the given title with the given timestamp. * WARNING: Timestamps may in some circumstances not be unique, @@ -120,7 +122,7 @@ class Revision { 'page_namespace' => $title->getNamespace(), 'page_title' => $title->getDbkey() ) ); } - + /** * Given a set of conditions, fetch a revision. * @@ -138,7 +140,7 @@ class Revision { } return $row; } - + /** * Given a set of conditions, fetch a revision from * the given database connection. @@ -155,12 +157,14 @@ class Revision { $row = $res->fetchObject(); $res->free(); if( $row ) { - return new Revision( $row ); + $ret = new Revision( $row ); + return $ret; } } - return null; + $ret = null; + return $ret; } - + /** * Return a wrapper for a series of database rows to * fetch all of a given page's revisions in turn. @@ -176,9 +180,9 @@ class Revision { wfGetDB( DB_SLAVE ), array( 'page_namespace' => $title->getNamespace(), 'page_title' => $title->getDbkey(), - 'page_id=rev_page' ) ); + 'page_id=rev_page' ) ); } - + /** * Return a wrapper for a series of database rows to * fetch all of a given page's revisions in turn. @@ -195,9 +199,9 @@ class Revision { array( 'rev_id=page_latest', 'page_namespace' => $title->getNamespace(), 'page_title' => $title->getDbkey(), - 'page_id=rev_page' ) ); + 'page_id=rev_page' ) ); } - + /** * Given a set of conditions, return a ResultWrapper * which will return matching database rows with the @@ -227,9 +231,10 @@ class Revision { $conditions, 'Revision::fetchRow', array( 'LIMIT' => 1 ) ); - return $db->resultObject( $res ); + $ret = $db->resultObject( $res ); + return $ret; } - + /** * @param object $row * @access private @@ -245,11 +250,11 @@ class Revision { $this->mMinorEdit = IntVal( $row->rev_minor_edit ); $this->mTimestamp = $row->rev_timestamp; $this->mDeleted = IntVal( $row->rev_deleted ); - + $this->mCurrent = ( $row->rev_id == $row->page_latest ); $this->mTitle = Title::makeTitle( $row->page_namespace, $row->page_title ); - + if( isset( $row->old_text ) ) { $this->mText = $this->getRevisionText( $row ); } else { @@ -258,7 +263,7 @@ class Revision { } elseif( is_array( $row ) ) { // Build a new revision to be saved... global $wgUser; - + $this->mId = isset( $row['id'] ) ? IntVal( $row['id'] ) : null; $this->mPage = isset( $row['page'] ) ? IntVal( $row['page'] ) : null; $this->mTextId = isset( $row['text_id'] ) ? IntVal( $row['text_id'] ) : null; @@ -267,36 +272,36 @@ class Revision { $this->mMinorEdit = isset( $row['minor_edit'] ) ? IntVal( $row['minor_edit'] ) : 0; $this->mTimestamp = isset( $row['timestamp'] ) ? StrVal( $row['timestamp'] ) : wfTimestamp( TS_MW ); $this->mDeleted = isset( $row['deleted'] ) ? IntVal( $row['deleted'] ) : 0; - + // Enforce spacing trimming on supplied text $this->mComment = isset( $row['comment'] ) ? trim( StrVal( $row['comment'] ) ) : null; $this->mText = isset( $row['text'] ) ? rtrim( StrVal( $row['text'] ) ) : null; - + $this->mTitle = null; # Load on demand if needed $this->mCurrent = false; } else { wfDebugDieBacktrace( 'Revision constructor passed invalid row format.' ); } } - + /**#@+ * @access public */ - + /** * @return int */ function getId() { return $this->mId; } - + /** * @return int */ function getTextId() { return $this->mTextId; } - + /** * Returns the title of the page associated with this entry. * @return Title @@ -318,49 +323,49 @@ class Revision { } return $this->mTitle; } - + /** * @return int */ function getPage() { return $this->mPage; } - + /** * @return int */ function getUser() { return $this->mUser; } - + /** * @return string */ function getUserText() { return $this->mUserText; } - + /** * @return string */ function getComment() { return $this->mComment; } - + /** * @return bool */ function isMinor() { return (bool)$this->mMinorEdit; } - + /** * @return bool */ function isDeleted() { return (bool)$this->mDeleted; } - + /** * @return string */ @@ -371,21 +376,21 @@ class Revision { } return $this->mText; } - + /** * @return string */ function getTimestamp() { - return $this->mTimestamp; + return wfTimestamp(TS_MW, $this->mTimestamp); } - + /** * @return bool */ function isCurrent() { return $this->mCurrent; } - + /** * @return Revision */ @@ -415,7 +420,7 @@ class Revision { function getRevisionText( $row, $prefix = 'old_' ) { $fname = 'Revision::getRevisionText'; wfProfileIn( $fname ); - + # Get data $textField = $prefix . 'text'; $flagsField = $prefix . 'flags'; @@ -451,7 +456,7 @@ class Revision { # as pages are saved if $wgCompressRevisions is set. $text = gzinflate( $text ); } - + if( in_array( 'object', $flags ) ) { # Generic compressed storage $obj = unserialize( $text ); @@ -463,7 +468,7 @@ class Revision { $text = $obj->getText(); } - + global $wgLegacyEncoding; if( $wgLegacyEncoding && !in_array( 'utf-8', $flags ) ) { # Old revisions kept around in a legacy encoding? @@ -489,11 +494,11 @@ class Revision { function compressRevisionText( &$text ) { global $wgCompressRevisions; $flags = array(); - + # Revisions not marked this way will be converted # on load if $wgLegacyCharset is set in the future. $flags[] = 'utf-8'; - + if( $wgCompressRevisions ) { if( function_exists( 'gzdeflate' ) ) { $text = gzdeflate( $text ); @@ -504,7 +509,7 @@ class Revision { } return implode( ',', $flags ); } - + /** * Insert a new revision into the database, returning the new revision ID * number on success and dies horribly on failure. @@ -515,10 +520,10 @@ class Revision { function insertOn( &$dbw ) { $fname = 'Revision::insertOn'; wfProfileIn( $fname ); - + $mungedText = $this->mText; $flags = Revision::compressRevisionText( $mungedText ); - + # Record the text to the text table if( !isset( $this->mTextId ) ) { $old_id = $dbw->nextSequenceValue( 'text_old_id_val' ); @@ -531,7 +536,7 @@ class Revision { ); $this->mTextId = $dbw->insertId(); } - + # Record the edit in revisions $rev_id = isset( $this->mId ) ? $this->mId @@ -549,13 +554,13 @@ class Revision { 'rev_deleted' => $this->mDeleted, ), $fname ); - - $this->mId = $dbw->insertId(); - + + $this->mId = !is_null($rev_id) ? $rev_id : $dbw->insertId(); + wfProfileOut( $fname ); return $this->mId; } - + /** * Lazy-load the revision's text. * Currently hardcoded to the 'text' table storage engine. @@ -566,13 +571,13 @@ class Revision { function loadText() { $fname = 'Revision::loadText'; wfProfileIn( $fname ); - + $dbr =& wfGetDB( DB_SLAVE ); $row = $dbr->selectRow( 'text', array( 'old_text', 'old_flags' ), array( 'old_id' => $this->getTextId() ), $fname); - + if( !$row ) { $dbw =& wfGetDB( DB_MASTER ); $row = $dbw->selectRow( 'text', @@ -580,10 +585,10 @@ class Revision { array( 'old_id' => $this->getTextId() ), $fname); } - + $text = Revision::getRevisionText( $row ); wfProfileOut( $fname ); - + return $text; } @@ -604,7 +609,7 @@ class Revision { function newNullRevision( &$dbw, $pageId, $summary, $minor ) { $fname = 'Revision::newNullRevision'; wfProfileIn( $fname ); - + $current = $dbw->selectRow( array( 'page', 'revision' ), array( 'page_latest', 'rev_text_id' ), @@ -613,7 +618,7 @@ class Revision { 'page_latest=rev_id', ), $fname ); - + if( $current ) { $revision = new Revision( array( 'page' => $pageId, @@ -624,10 +629,10 @@ class Revision { } else { $revision = null; } - + wfProfileOut( $fname ); return $revision; } - + } ?> diff --git a/includes/SearchEngine.php b/includes/SearchEngine.php index 45642ee9b0..b335a38aaf 100644 --- a/includes/SearchEngine.php +++ b/includes/SearchEngine.php @@ -14,7 +14,7 @@ class SearchEngine { var $searchTerms = array(); var $namespaces = array( 0 ); var $showRedirects = false; - + /** * Perform a full text search query and return a result set. * If title searches are not supported or disabled, return null. @@ -40,7 +40,7 @@ class SearchEngine { function searchTitle( $term ) { return null; } - + /** * If an exact title match can be find, or a very slightly close match, * return the title. If no match, returns NULL. @@ -107,15 +107,15 @@ class SearchEngine { if ( $title->getNamespace() == NS_USER ) { return $title; } - + # Quoted term? Try without the quotes... if( preg_match( '/^"([^"]+)"$/', $term, $matches ) ) { return SearchEngine::getNearMatch( $matches[1] ); } - + return NULL; } - + function legalSearchChars() { return "A-Za-z_'0-9\\x80-\\xFF\\-"; } @@ -132,7 +132,7 @@ class SearchEngine { $this->limit = IntVal( $limit ); $this->offset = IntVal( $offset ); } - + /** * Set which namespaces the search should include. * Give an array of namespace index numbers. @@ -143,7 +143,7 @@ class SearchEngine { function setNamespaces( $namespaces ) { $this->namespaces = $namespaces; } - + /** * Make a list of searchable namespaces and their canonical names. * @return array @@ -159,7 +159,7 @@ class SearchEngine { } return $arr; } - + /** * Return a 'cleaned up' search string * @@ -199,7 +199,7 @@ class SearchEngine { $search->setLimitOffset(0,0); return $search; } - + /** * Create or update the search index record for the given page. * Title and text should be pre-processed. @@ -239,11 +239,11 @@ class SearchResultSet { function termMatches() { return array(); } - + function numRows() { return 0; } - + /** * Return true if results are included in this result set. * @return bool @@ -252,7 +252,7 @@ class SearchResultSet { function hasResults() { return false; } - + /** * Some search modes return a total hit count for the query * in the entire article database. This may include pages @@ -267,7 +267,7 @@ class SearchResultSet { function getTotalHits() { return null; } - + /** * Some search modes return a suggested alternate term if there are * no exact hits. Returns true if there is one on this set. @@ -278,7 +278,7 @@ class SearchResultSet { function hasSuggestion() { return false; } - + /** * Some search modes return a suggested alternate term if there are * no exact hits. Check hasSuggestion() first. @@ -289,7 +289,7 @@ class SearchResultSet { function getSuggestion() { return ''; } - + /** * Fetches next search result, or false. * @return SearchResult @@ -306,7 +306,7 @@ class SearchResult { function SearchResult( $row ) { $this->mTitle = Title::makeTitle( $row->page_namespace, $row->page_title ); } - + /** * @return Title * @access public @@ -314,7 +314,7 @@ class SearchResult { function getTitle() { return $this->mTitle; } - + /** * @return double or null if not supported */ @@ -330,5 +330,11 @@ class SearchEngineDummy { function search( $term ) { return null; } + function setLimitOffset($l, $o) {} + function legalSearchChars() {} + function update() {} + function setnamespaces() {} + function searchtitle() {} + function searchtext() {} } diff --git a/includes/SiteStatsUpdate.php b/includes/SiteStatsUpdate.php index 00e54943e2..1c82d08668 100644 --- a/includes/SiteStatsUpdate.php +++ b/includes/SiteStatsUpdate.php @@ -33,7 +33,7 @@ class SiteStatsUpdate { } } } - + function doUpdate() { global $wgDBname; $fname = 'SiteStatsUpdate::doUpdate'; @@ -43,7 +43,7 @@ class SiteStatsUpdate { $row = $dbw->selectRow( 'site_stats', '*', false, $fname ); $updates = ''; - + $this->appendUpdate( $updates, 'ss_total_views', $this->mViews ); $this->appendUpdate( $updates, 'ss_total_edits', $this->mEdits ); $this->appendUpdate( $updates, 'ss_good_articles', $this->mGood ); @@ -63,19 +63,19 @@ class SiteStatsUpdate { $res = $dbr->query( $sql, $fname ); $userRow = $dbr->fetchObject( $res ); $users = $userRow->total + $this->mUsers; - + if ( $updates ) { $updates .= ','; } $updates .= "ss_total_pages=$pages, ss_users=$users"; - } else { + } else { $this->appendUpdate( $updates, 'ss_total_pages', $this->mPages ); $this->appendUpdate( $updates, 'ss_users', $this->mUsers ); } } if ( $updates ) { $site_stats = $dbw->tableName( 'site_stats' ); - $sql = "UPDATE $site_stats SET $updates LIMIT 1"; + $sql = $dbw->limitResultForUpdate("UPDATE $site_stats SET $updates", 1); $dbw->query( $sql, $fname ); } } diff --git a/includes/Skin.php b/includes/Skin.php index dc1fa4a6bc..84dc210281 100644 --- a/includes/Skin.php +++ b/includes/Skin.php @@ -644,7 +644,7 @@ END; $n = $wgIP; $tl = $this->makeKnownLinkObj( $wgUser->getTalkPage(), - $wgContLang->getNsText( NS_TALK ) ); + $wgLang->getNsText( NS_TALK ) ); $s .= $n . ' ('.$tl.')'; } else { @@ -663,7 +663,7 @@ END; $n = $wgUser->getName(); $rt = $wgTitle->getPrefixedURL(); $tl = $this->makeKnownLinkObj( $wgUser->getTalkPage(), - $wgContLang->getNsText( NS_TALK ) ); + $wgLang->getNsText( NS_TALK ) ); $tl = " ({$tl})"; diff --git a/includes/SkinTemplate.php b/includes/SkinTemplate.php index 0cd2963658..3138224416 100644 --- a/includes/SkinTemplate.php +++ b/includes/SkinTemplate.php @@ -540,16 +540,18 @@ class SkinTemplate extends Skin { $content_actions = array(); if( $this->iscontent ) { + $subjpage = $this->mTitle->getSubjectPage(); + $talkpage = $this->mTitle->getTalkPage(); $nskey = $this->getNameSpaceKey(); $content_actions[$nskey] = $this->tabAction( - $this->mTitle->getSubjectPage(), + $subjpage, $nskey, !$this->mTitle->isTalkPage(), '', true); $content_actions['talk'] = $this->tabAction( - $this->mTitle->getTalkPage(), + $talkpage, 'talk', $this->mTitle->isTalkPage(), '', diff --git a/includes/SpecialAllpages.php b/includes/SpecialAllpages.php index 8ab20b1ef2..359e6fce10 100644 --- a/includes/SpecialAllpages.php +++ b/includes/SpecialAllpages.php @@ -155,7 +155,6 @@ function showToplevel ( $namespace = NS_MAIN, $including = false ) { $out .= $this->showline ( $inpoint, $outpoint, $namespace, false ); } $out .= ''; - $nsForm = $this->namespaceForm ( $namespace, '', false ); # Is there more? @@ -178,7 +177,7 @@ function showToplevel ( $namespace = NS_MAIN, $including = false ) { /** * @todo Document - * @param string $from + * @param string $from * @param integer $namespace (Default NS_MAIN) */ function showline( $inpoint, $outpoint, $namespace = NS_MAIN ) { @@ -191,7 +190,7 @@ function showline( $inpoint, $outpoint, $namespace = NS_MAIN ) { $queryparams = ($namespace ? "namespace=$namespace" : ''); $special = Title::makeTitle( NS_SPECIAL, 'Allpages/' . $inpoint ); $link = $special->escapeLocalUrl( $queryparams ); - + $out = wfMsg( 'alphaindexline', "$inpointf", @@ -239,7 +238,7 @@ function showChunk( $namespace = NS_MAIN, $from, $including = false ) { $n = 0; $out = ''; - + $namespaces = $wgContLang->getFormattedNamespaces(); while( ($n < $this->maxPerPage) && ($s = $dbr->fetchObject( $res )) ) { $t = Title::makeTitle( $s->page_namespace, $s->page_title ); @@ -263,7 +262,7 @@ function showChunk( $namespace = NS_MAIN, $from, $including = false ) { $out .= ''; } $out .= '
    '; - + if ( $including ) { $out2 = ''; } else { diff --git a/includes/SpecialContributions.php b/includes/SpecialContributions.php index 0e55285048..5a04b59b7a 100644 --- a/includes/SpecialContributions.php +++ b/includes/SpecialContributions.php @@ -83,9 +83,10 @@ class contribs_finder { $sql = "SELECT rev_timestamp FROM $page, $revision $use_index " . "WHERE page_id = rev_page AND rev_timestamp > '" . $this->offset . "' AND " . - "rev_user_text = " . $this->dbr->addQuotes($this->username) . - $nscond; - $sql .= " ORDER BY rev_timestamp ASC LIMIT " . ($this->limit+1); + "rev_user_text = " . $this->dbr->addQuotes($this->username) + . $nscond + $sql .= " ORDER BY rev_timestamp ASC"; + $sql = $this->dbr->limitResult($sql, $this->limit, 0); $res = $this->dbr->query($sql); $rows = array(); while ($obj = $this->dbr->fetchObject($res)) @@ -101,9 +102,10 @@ class contribs_finder { $nscond = $this->get_namespace_cond(); $sql = "SELECT rev_timestamp FROM $page, $revision $use_index " . "WHERE page_id = rev_page AND " . - "rev_user_text = " . $this->dbr->addQuotes($this->username) . - $nscond; - $sql .= " ORDER BY rev_timestamp ASC LIMIT " . $this->limit; + "rev_user_text = " . $this->dbr->addQuotes($this->username) + . $nscond + $sql .= " ORDER BY rev_timestamp ASC"; + $sql = $this->dbr->limitResult($sql, $this->limit + 1, 0); $res = $this->dbr->query($sql); $rows = array(); while ($obj = $this->dbr->fetchObject($res)) @@ -130,7 +132,8 @@ class contribs_finder { rev_deleted FROM $page,$revision $use_index WHERE page_id=rev_page AND $userCond $nscond $offsetQuery - ORDER BY rev_timestamp DESC $limitQuery"; + ORDER BY rev_timestamp DESC"; + $sql = $this->dbr->limitResult($sql, $this->limit, 0); return $sql; } @@ -369,7 +372,7 @@ function ucListEdit( $sk, $row ) { $histlink='('.$sk->makeKnownLinkObj( $page, $messages['hist'], 'action=history' ) . ')'; $comment = $sk->commentBlock( $row->rev_comment, $page ); - $d = $wgLang->timeanddate( $row->rev_timestamp, true ); + $d = $wgLang->timeanddate( wfTimestamp(TS_MW, $row->rev_timestamp), true ); if( $row->rev_minor_edit ) { $mflag = '' . $messages['minoreditletter'] . ' '; diff --git a/includes/SpecialLog.php b/includes/SpecialLog.php index 7a7b0462a8..8e59e933aa 100644 --- a/includes/SpecialLog.php +++ b/includes/SpecialLog.php @@ -1,17 +1,17 @@ # http://www.mediawiki.org/ -# +# # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or +# the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. @@ -44,7 +44,7 @@ function wfSpecialLog( $par = '' ) { class LogReader { var $db, $joinClauses, $whereClauses; var $type = '', $user = '', $title = null; - + /** * @param WebRequest $request For internal use use a FauxRequest object to pass arbitrary parameters. */ @@ -52,7 +52,7 @@ class LogReader { $this->db =& wfGetDB( DB_SLAVE ); $this->setupQuery( $request ); } - + /** * Basic setup and applies the limiting factors from the WebRequest object. * @param WebRequest $request @@ -63,16 +63,16 @@ class LogReader { $user = $this->db->tableName( 'user' ); $this->joinClauses = array( "LEFT OUTER JOIN $page ON log_namespace=page_namespace AND log_title=page_title" ); $this->whereClauses = array( 'user_id=log_user' ); - + $this->limitType( $request->getVal( 'type' ) ); $this->limitUser( $request->getText( 'user' ) ); $this->limitTitle( $request->getText( 'page' ) ); $this->limitTime( $request->getVal( 'from' ), '>=' ); $this->limitTime( $request->getVal( 'until' ), '<=' ); - + list( $this->limit, $this->offset ) = $request->getLimitOffset(); } - + /** * Set the log reader to return only entries of the given type. * @param string $type A log type ('upload', 'delete', etc) @@ -86,7 +86,7 @@ class LogReader { $safetype = $this->db->strencode( $type ); $this->whereClauses[] = "log_type='$safetype'"; } - + /** * Set the log reader to return only entries by the given user. * @param string $name Valid user name @@ -102,7 +102,7 @@ class LogReader { $user = $this->db->tableName( 'user' ); $this->whereClauses[] = "user_name='$safename'"; } - + /** * Set the log reader to return only entries affecting the given page. * (For the block and rights logs, this is a user page.) @@ -119,7 +119,7 @@ class LogReader { $ns = $title->getNamespace(); $this->whereClauses[] = "log_namespace=$ns AND log_title='$safetitle'"; } - + /** * Set the log reader to return only entries in a given time range. * @param string $time Timestamp of one endpoint @@ -134,7 +134,7 @@ class LogReader { $safetime = $this->db->strencode( wfTimestamp( TS_MW, $time ) ); $this->whereClauses[] = "log_timestamp $direction '$safetime'"; } - + /** * Build an SQL query from all the set parameters. * @return string the SQL query @@ -154,32 +154,33 @@ class LogReader { $sql .= " WHERE " . implode( ' AND ', $this->whereClauses ); } $sql .= " ORDER BY log_timestamp DESC "; - $sql .= $this->db->limitResult( $this->limit, $this->offset ); + $sql = $this->db->limitResult( $sql, $this->limit, $this->offset ); return $sql; } - + /** * Execute the query and start returning results. * @return ResultWrapper result object to return the relevant rows */ function getRows() { - return $this->db->resultObject( $this->db->query( $this->getQuery() ) ); + $res = $this->db->query( $this->getQuery() ); + return $this->db->resultObject( $res ); } - + /** * @return string The query type that this LogReader has been limited to. */ function queryType() { return $this->type; } - + /** * @return string The username type that this LogReader has been limited to, if any. */ function queryUser() { return $this->user; } - + /** * @return string The text of the title that this LogReader has been limited to. */ @@ -203,7 +204,7 @@ class LogViewer { */ var $reader; var $numResults = 0; - + /** * @param LogReader &$reader where to get our data from */ @@ -212,7 +213,7 @@ class LogViewer { $this->skin =& $wgUser->getSkin(); $this->reader =& $reader; } - + /** * Take over the whole output page in $wgOut with the log display. */ @@ -230,7 +231,7 @@ class LogViewer { * Load the data from the linked LogReader * Preload the link cache * Initialise numResults - * + * * Must be called before calling showPrevNext * * @return object database result set @@ -260,7 +261,7 @@ class LogViewer { return $result; } - + /** * Output just the list of entries given by the linked LogReader, * with extraneous UI elements. Use for displaying log fragments in @@ -270,7 +271,7 @@ class LogViewer { function showList( &$out ) { $this->doShowList( $out, $this->getLogRows() ); } - + function doShowList( &$out, $result ) { // Rewind result pointer and go through it again, making the HTML $html=''; @@ -285,7 +286,7 @@ class LogViewer { $result->free(); $out->addHTML( $html ); } - + /** * @param Object $s a single row from the result set * @return string Formatted HTML list item @@ -295,7 +296,7 @@ class LogViewer { global $wgLang, $wgLinkCache; $title = Title::makeTitle( $s->log_namespace, $s->log_title ); $user = Title::makeTitleSafe( NS_USER, $s->user_name ); - $time = $wgLang->timeanddate( $s->log_timestamp, true ); + $time = $wgLang->timeanddate( wfTimestamp(TS_MW, $s->log_timestamp), true ); // Enter the existence or non-existence of this page into the link cache, // for faster makeLinkObj() in LogPage::actionText() @@ -304,7 +305,7 @@ class LogViewer { } else { $wgLinkCache->addBadLinkObj( $title ); } - + $userLink = $this->skin->makeLinkObj( $user, htmlspecialchars( $s->user_name ) ); $comment = $this->skin->commentBlock( $s->log_comment ); $paramArray = LogPage::extractParams( $s->log_params ); @@ -314,7 +315,7 @@ class LogViewer { $destTitle = Title::newFromText( $paramArray[0] ); if ( $destTitle ) { $revert = '(' . $this->skin->makeKnownLinkObj( $specialTitle, wfMsg( 'revertmove' ), - 'wpOldTitle=' . urlencode( $destTitle->getPrefixedDBkey() ) . + 'wpOldTitle=' . urlencode( $destTitle->getPrefixedDBkey() ) . '&wpNewTitle=' . urlencode( $title->getPrefixedDBkey() ) . '&wpReason=' . urlencode( wfMsgForContent( 'revertmove' ) ) . '&wpMovetalk=0' ) . ')'; @@ -325,7 +326,7 @@ class LogViewer { $out = "
  • $time $userLink $action $comment $revert
  • \n"; return $out; } - + /** * @param OutputPage &$out where to send output * @private @@ -337,7 +338,7 @@ class LogViewer { $out->addWikiText( LogPage::logHeader( $type ) ); } } - + /** * @param OutputPage &$out where to send output * @private @@ -355,7 +356,7 @@ class LogViewer { "" . "" ); } - + /** * @return string Formatted HTML * @private @@ -370,7 +371,7 @@ class LogViewer { $out .= "\n"; return $out; } - + /** * @return string Formatted HTML * @private @@ -379,7 +380,7 @@ class LogViewer { $user = htmlspecialchars( $this->reader->queryUser() ); return wfMsg('specialloguserlabel') . "\n"; } - + /** * @return string Formatted HTML * @private @@ -388,7 +389,7 @@ class LogViewer { $title = htmlspecialchars( $this->reader->queryTitle() ); return wfMsg('speciallogtitlelabel') . "\n"; } - + /** * @param OutputPage &$out where to send output * @private @@ -401,7 +402,7 @@ class LogViewer { $pieces[] = 'page=' . urlencode( $this->reader->queryTitle() ); $bits = implode( '&', $pieces ); list( $limit, $offset ) = $wgRequest->getLimitOffset(); - + # TODO: use timestamps instead of offsets to make it more natural # to go huge distances in time $html = wfViewPrevNext( $offset, $limit, diff --git a/includes/SpecialPreferences.php b/includes/SpecialPreferences.php index 6e99b3f03f..91e59ff3e7 100644 --- a/includes/SpecialPreferences.php +++ b/includes/SpecialPreferences.php @@ -378,7 +378,7 @@ class PreferencesForm { $checked = $wgUser->getOption( $tname ) == 1 ? ' checked="checked"' : ''; $trailer = $trailer ? $trailer : ''; return "
    " . - " $trailer
    "; + " $trailer\n"; } function getToggles( $items ) { @@ -769,7 +769,7 @@ class PreferencesForm { - + "); foreach ( $togs as $tname ) { if( !array_key_exists( $tname, $this->mUsedToggles ) ) { diff --git a/includes/SpecialRandompage.php b/includes/SpecialRandompage.php index 82d580cb6a..53138af2c8 100644 --- a/includes/SpecialRandompage.php +++ b/includes/SpecialRandompage.php @@ -7,7 +7,7 @@ /** * Constructor * - * @param string $par the namespace to get a random page from (default NS_MAIN), + * @param string $par the namespace to get a random page from (default NS_MAIN), * used as e.g. Special:Randompage/Category */ function wfSpecialRandompage( $par = NS_MAIN ) { @@ -19,7 +19,7 @@ function wfSpecialRandompage( $par = NS_MAIN ) { if ($namespace === false || $namespace < NS_MAIN) { $namespace = NS_MAIN; } - + # NOTE! We use a literal constant in the SQL instead of the RAND() # function because RAND() will return a different value for every row # in the table. That's both very slow and returns results heavily @@ -28,10 +28,10 @@ function wfSpecialRandompage( $par = NS_MAIN ) { # # Using a literal constant means the whole thing gets optimized on # the index, and the comparison is both fast and fair. - + # interpolation and sprintf() can muck up with locale-specific decimal separator $randstr = wfRandom(); - + $db =& wfGetDB( DB_SLAVE ); $use_index = $db->useIndexClause( 'page_random' ); $page = $db->tableName( 'page' ); @@ -41,14 +41,14 @@ function wfSpecialRandompage( $par = NS_MAIN ) { FROM $page $use_index WHERE page_namespace=$namespace AND page_is_redirect=0 $extra AND page_random>$randstr - ORDER BY page_random - LIMIT 1"; + ORDER BY page_random"; + $sql = $db->limitResult($sql, 1, 0); $res = $db->query( $sql, $fname ); - + $title = null; if( $s = $db->fetchObject( $res ) ) { $title =& Title::makeTitle( $namespace, $s->page_title ); - } + } if( is_null( $title ) ) { # That's not supposed to happen :) $title = Title::newFromText( wfMsg( 'mainpage' ) ); diff --git a/includes/SpecialRecentchanges.php b/includes/SpecialRecentchanges.php index 653cf84264..e31758f2b1 100644 --- a/includes/SpecialRecentchanges.php +++ b/includes/SpecialRecentchanges.php @@ -39,7 +39,7 @@ function wfSpecialRecentchanges( $par, $specialPage ) { ); extract($defaults); - + $days = $wgUser->getOption( 'rcdays' ); if ( !$days ) { $days = $defaults['days']; } @@ -52,9 +52,9 @@ function wfSpecialRecentchanges( $par, $specialPage ) { $limit = $wgRequest->getInt( 'limit', $limit ); /* order of selection: url > preferences > default */ - $hideminor = $wgRequest->getBool( 'hideminor', $wgUser->getOption( 'hideminor') ? true : $defaults['hideminor'] ); + $hideminor = $wgRequest->getBool( 'hideminor', $wgUser->getOption( 'hideminor') ? true : $defaults['hideminor'] ); + - # As a feed, use limited settings only if( $feedFormat ) { global $wgFeedLimit; @@ -69,7 +69,7 @@ function wfSpecialRecentchanges( $par, $specialPage ) { $hidebots = $wgRequest->getBool( 'hidebots', $defaults['hidebots'] ); $hideliu = $wgRequest->getBool( 'hideliu', $defaults['hideliu'] ); $hidepatrolled = $wgRequest->getBool( 'hidepatrolled', $defaults['hidepatrolled'] ); - $from = $wgRequest->getVal( 'from', $defaults['from'] ); + $from = $wgRequest->getVal( 'from', $defaults['from'] ); # Get query parameters from path if( $par ) { @@ -104,7 +104,7 @@ function wfSpecialRecentchanges( $par, $specialPage ) { $dbr =& wfGetDB( DB_SLAVE ); extract( $dbr->tableNames( 'recentchanges', 'watchlist' ) ); - + $cutoff_unixtime = time() - ( $days * 86400 ); $cutoff_unixtime = $cutoff_unixtime - ($cutoff_unixtime % 86400); $cutoff = $dbr->timestamp( $cutoff_unixtime ); @@ -113,7 +113,7 @@ function wfSpecialRecentchanges( $par, $specialPage ) { } else { $from = $defaults['from']; } - + # 10 seconds server-side caching max $wgOut->setSquidMaxage( 10 ); @@ -138,10 +138,11 @@ function wfSpecialRecentchanges( $par, $specialPage ) { $uid = $wgUser->getID(); // Perform query - $sql2 = "SELECT *" . ($uid ? ",wl_user,wl_notificationtimestamp" : "") . " FROM $recentchanges " . + $sql2 = "SELECT * FROM $recentchanges " . ($uid ? "LEFT OUTER JOIN $watchlist ON wl_user={$uid} AND wl_title=rc_title AND wl_namespace=rc_namespace " : "") . "WHERE rc_timestamp > '{$cutoff}' {$hidem} " . - "ORDER BY rc_timestamp DESC LIMIT {$limit}"; + "ORDER BY rc_timestamp DESC"; + $sql2 = $dbr->limitResult($sql2, $limit, 0); $res = $dbr->query( $sql2, $fname ); // Fetch results, prepare a batch link existence check query @@ -172,7 +173,7 @@ function wfSpecialRecentchanges( $par, $specialPage ) { // Output header if ( !$specialPage->including() ) { $wgOut->addWikiText( wfMsgForContent( "recentchangestext" ) ); - + // Dump everything here $nondefaults = array(); @@ -235,15 +236,15 @@ function wfSpecialRecentchanges( $par, $specialPage ) { function rcOutputFeed( $rows, $feedFormat, $limit, $hideminor, $lastmod ) { global $messageMemc, $wgDBname, $wgFeedCacheTimeout; global $wgFeedClasses, $wgTitle, $wgSitename, $wgContLanguageCode; - + if( !isset( $wgFeedClasses[$feedFormat] ) ) { wfHttpError( 500, "Internal Server Error", "Unsupported feed type." ); return false; } - + $timekey = "$wgDBname:rcfeed:$feedFormat:timestamp"; $key = "$wgDBname:rcfeed:$feedFormat:limit:$limit:minor:$hideminor"; - + $feedTitle = $wgSitename . ' - ' . wfMsgForContent( 'recentchanges' ) . ' [' . $wgContLanguageCode . ']'; $feed = new $wgFeedClasses[$feedFormat]( @@ -284,7 +285,7 @@ function rcOutputFeed( $rows, $feedFormat, $limit, $hideminor, $lastmod ) { rcDoOutputFeed( $rows, $feed ); $cachedFeed = ob_get_contents(); ob_end_flush(); - + $expire = 3600 * 24; # One day $messageMemc->set( $key, $cachedFeed ); $messageMemc->set( $timekey, wfTimestamp( TS_MW ), $expire ); @@ -294,9 +295,9 @@ function rcOutputFeed( $rows, $feedFormat, $limit, $hideminor, $lastmod ) { function rcDoOutputFeed( $rows, &$feed ) { global $wgSitename, $wgFeedClasses, $wgContLanguageCode; - + $feed->outHeader(); - + # Merge adjacent edits by one user $sorted = array(); $n = 0; @@ -312,7 +313,7 @@ function rcDoOutputFeed( $rows, &$feed ) { } $first = false; } - + foreach( $sorted as $obj ) { $title = Title::makeTitle( $obj->rc_namespace, $obj->rc_title ); $talkpage = $title->getTalkPage(); @@ -431,11 +432,11 @@ function rcOptionsPanel( $defaults, $nondefaults ) { array( 'hidepatrolled' => 1-$options['hidepatrolled'] ), $nondefaults); $hl = wfMsg( 'showhideminor', $minorLink, $botLink, $liuLink, $patrLink ); - + // show from this onward link $now = $wgLang->timeanddate( wfTimestampNow(), true ); $tl = makeOptionsLink( $now, array( 'from' => wfTimestampNow()), $nondefaults ); - + $rclinks = wfMsg( 'rclinks', $cl, $dl, $hl ); $rclistfrom = wfMsg( 'rclistfrom', $tl ); return "$note
    $rclinks
    $rclistfrom"; @@ -486,16 +487,16 @@ function rcNamespaceForm ( $namespace, $invert, $nondefaults ) { function rcFormatDiff( $row ) { $fname = 'rcFormatDiff'; wfProfileIn( $fname ); - + require_once( 'DifferenceEngine.php' ); $comment = "

    " . htmlspecialchars( $row->rc_comment ) . "

    \n"; - + if( $row->rc_namespace >= 0 ) { global $wgContLang; - + #$diff =& new DifferenceEngine( $row->rc_this_oldid, $row->rc_last_oldid, $row->rc_id ); #$diff->showDiffPage(); - + $titleObj = Title::makeTitle( $row->rc_namespace, $row->rc_title ); $dbr =& wfGetDB( DB_SLAVE ); $newrev =& Revision::newFromTitle( $titleObj, $row->rc_this_oldid ); @@ -516,11 +517,11 @@ function rcFormatDiff( $row ) { return $comment . $diffText; } $oldtext = $oldrev->getText(); - + # Old entries may contain illegal characters # which will damage output $oldtext = UtfNormal::cleanUp( $oldtext ); - + global $wgFeedDiffCutoff; if( strlen( $newtext ) > $wgFeedDiffCutoff || strlen( $oldtext ) > $wgFeedDiffCutoff ) { @@ -539,16 +540,16 @@ function rcFormatDiff( $row ) { } wfProfileOut( "$fname-dodiff" ); } else { - $diffText = '

    ' . wfMsg( 'newpage' ) . '

    ' . + $diffText = '

    ' . wfMsg( 'newpage' ) . '

    ' . '
    ' . nl2br( htmlspecialchars( $newtext ) ) . '
    '; } - + wfProfileOut( $fname ); return $comment . $diffText; } - + wfProfileOut( $fname ); - return $comment; + return $comment; } ?> diff --git a/includes/SpecialUndelete.php b/includes/SpecialUndelete.php index 09ef8678e5..eb73ff39b9 100644 --- a/includes/SpecialUndelete.php +++ b/includes/SpecialUndelete.php @@ -25,14 +25,14 @@ function wfSpecialUndelete( $par ) { */ class PageArchive { var $title; - + function PageArchive( &$title ) { if( is_null( $title ) ) { wfDebugDieBacktrace( 'Archiver() given a null title.'); } $this->title =& $title; } - + /** * List all deleted pages recorded in the archive table. Returns result * wrapper with (ar_namespace, ar_title, count) fields, ordered by page @@ -44,12 +44,12 @@ class PageArchive { $dbr =& wfGetDB( DB_SLAVE ); $archive = $dbr->tableName( 'archive' ); - $sql = "SELECT ar_namespace,ar_title, COUNT(*) AS count FROM $archive " . + $sql = "SELECT ar_namespace,ar_title, COUNT(*) AS count FROM $archive " . "GROUP BY ar_namespace,ar_title ORDER BY ar_namespace,ar_title"; return $dbr->resultObject( $dbr->query( $sql, 'PageArchive::listAllPages' ) ); } - + /** * List the revisions of the given page. Returns result wrapper with * (ar_minor_edit, ar_timestamp, ar_user, ar_user_text, ar_comment) fields. @@ -58,14 +58,16 @@ class PageArchive { */ function listRevisions() { $dbr =& wfGetDB( DB_SLAVE ); - return $dbr->resultObject( $dbr->select( 'archive', + $res = $dbr->select( 'archive', array( 'ar_minor_edit', 'ar_timestamp', 'ar_user', 'ar_user_text', 'ar_comment' ), array( 'ar_namespace' => $this->title->getNamespace(), 'ar_title' => $this->title->getDBkey() ), 'PageArchive::listRevisions', - array( 'ORDER BY' => 'ar_timestamp DESC' ) ) ); + array( 'ORDER BY' => 'ar_timestamp DESC' ) ); + $ret = $dbr->resultObject( $res ); + return $ret; } - + /** * Fetch (and decompress if necessary) the stored text for the deleted * revision of the page with the given timestamp. @@ -94,7 +96,7 @@ class PageArchive { return Revision::getRevisionText( $text ); } } - + /** * Fetch (and decompress if necessary) the stored text of the most * recently edited deleted revision of the page. @@ -117,7 +119,7 @@ class PageArchive { return NULL; } } - + /** * Quick check if any archived revisions are present for the page. * @return bool @@ -129,7 +131,7 @@ class PageArchive { 'ar_title' => $this->title->getDBkey() ) ); return ($n > 0); } - + /** * This is the meaty bit -- restores archived revisions of the given page * to the cur/old tables. If the page currently exists, all revisions will @@ -178,18 +180,18 @@ class PageArchive { $previousRevId = 0; $previousTimestamp = 0; } - + if( $restoreAll ) { - $oldones = '1'; # All revisions... + $oldones = '1 = 1'; # All revisions... } else { $oldts = implode( ',', array_map( array( &$dbw, 'addQuotes' ), array_map( array( &$dbw, 'timestamp' ), $timestamps ) ) ); - + $oldones = "ar_timestamp IN ( {$oldts} )"; } - + /** * Restore each revision... */ @@ -227,28 +229,28 @@ class PageArchive { ) ); $revision->insertOn( $dbw ); } - + if( $revision ) { # FIXME: Update latest if newer as well... if( $newid ) { # FIXME: update article count if changed... $article->updateRevisionOn( $dbw, $revision, $previousRevId ); - + # Finally, clean up the link tables $wgLinkCache = new LinkCache(); # Select for update $wgLinkCache->forUpdate( true ); - + # Create a dummy OutputPage to update the outgoing links $dummyOut = new OutputPage(); $dummyOut->addWikiText( $revision->getText() ); $u = new LinksUpdate( $newid, $this->title->getPrefixedDBkey() ); array_push( $wgDeferredUpdateList, $u ); - + #TODO: SearchUpdate, etc. } - + if( $newid ) { Article::onArticleCreate( $this->title ); } else { @@ -265,7 +267,7 @@ class PageArchive { 'ar_title' => $this->title->getDBkey(), $oldones ), $fname ); - + # Touch the log! $log = new LogPage( 'delete' ); if( $restoreAll ) { @@ -332,10 +334,10 @@ class UndeleteForm { /* private */ function showList() { global $wgLang, $wgContLang, $wgUser, $wgOut; $fname = "UndeleteForm::showList"; - - # List undeletable articles + + # List undeletable articles $result = PageArchive::listAllPages(); - + $wgOut->setPagetitle( wfMsg( "undeletepage" ) ); $wgOut->addWikiText( wfMsg( "undeletepagetext" ) ); @@ -343,7 +345,7 @@ class UndeleteForm { $undelete =& Title::makeTitle( NS_SPECIAL, 'Undelete' ); $wgOut->addHTML( "
      \n" ); while( $row = $result->fetchObject() ) { - $n = ($row->ar_namespace ? + $n = ($row->ar_namespace ? ($wgContLang->getNsText( $row->ar_namespace ) . ":") : ""). $row->ar_title; $link = $sk->makeKnownLinkObj( $undelete, @@ -354,10 +356,10 @@ class UndeleteForm { } $result->free(); $wgOut->addHTML( "
    \n" ); - + return true; } - + /* private */ function showRevision( $timestamp ) { global $wgLang, $wgUser, $wgOut; $fname = "UndeleteForm::showRevision"; @@ -366,7 +368,7 @@ class UndeleteForm { $archive =& new PageArchive( $this->mTargetObj ); $text = $archive->getRevisionText( $timestamp ); - + $wgOut->setPagetitle( wfMsg( "undeletepage" ) ); $wgOut->addWikiText( "(" . wfMsg( "undeleterevision", $wgLang->date( $timestamp ) ) . ")\n
    \n" . $text ); @@ -374,7 +376,7 @@ class UndeleteForm { /* private */ function showHistory() { global $wgLang, $wgUser, $wgOut; - + $sk = $wgUser->getSkin(); $wgOut->setPagetitle( wfMsg( "undeletepage" ) ); @@ -388,13 +390,13 @@ class UndeleteForm { # List all stored revisions $revisions = $archive->listRevisions(); - + $titleObj = Title::makeTitle( NS_SPECIAL, "Undelete" ); $action = $titleObj->escapeLocalURL( "action=submit" ); $encTarget = htmlspecialchars( $this->mTarget ); $button = htmlspecialchars( wfMsg("undeletebtn") ); $token = htmlspecialchars( $wgUser->editToken() ); - + $wgOut->addHTML("
    @@ -411,7 +413,7 @@ class UndeleteForm { array( 'page' => $this->mTargetObj->getPrefixedText(), 'type' => 'delete' ) ) ) ); $logViewer->showList( $wgOut ); - + # The page's stored (deleted) history: $wgOut->addHTML( "

    " . htmlspecialchars( wfMsg( "history" ) ) . "

    \n" ); $wgOut->addHTML("
      "); @@ -420,7 +422,7 @@ class UndeleteForm { $ts = wfTimestamp( TS_MW, $row->ar_timestamp ); $checkBox = ""; $pageLink = $sk->makeKnownLinkObj( $titleObj, - $wgLang->timeanddate( $row->ar_timestamp, true ), + $wgLang->timeanddate( $ts, true ), "target=$target×tamp=$ts" ); $userLink = htmlspecialchars( $row->ar_user_text ); if( $row->ar_user ) { @@ -438,7 +440,7 @@ class UndeleteForm { } $revisions->free(); $wgOut->addHTML("
    \n
    "); - + return true; } diff --git a/includes/Title.php b/includes/Title.php index 4d7994b42b..e005c637ad 100644 --- a/includes/Title.php +++ b/includes/Title.php @@ -145,7 +145,8 @@ class Title { return $t; } else { wfProfileOut( $fname ); - return NULL; + $ret = NULL; + return $ret; } } @@ -1139,7 +1140,6 @@ class Title { */ function getArticleID( $flags = 0 ) { global $wgLinkCache; - if ( $flags & GAID_FOR_UPDATE ) { $oldUpdate = $wgLinkCache->forUpdate( true ); $this->mArticleID = $wgLinkCache->addLinkObj( $this ); @@ -1149,6 +1149,7 @@ class Title { $this->mArticleID = $wgLinkCache->addLinkObj( $this ); } } +wfdebug("title: articleid = ".$this->mArticleID."\n"); return $this->mArticleID; } @@ -2088,7 +2089,8 @@ class Title { while( $row = $dbw->fetchObject( $res ) ) { $toucharr[] = $row->pl_from; } - + if (!count($toucharr)) + return; $dbw->update( 'page', /* SET */ array( 'page_touched' => $dbw->timestamp() ), /* WHERE */ array( 'page_id' => $toucharr ),$fname); } diff --git a/includes/User.php b/includes/User.php index 9022831824..6a97109c06 100644 --- a/includes/User.php +++ b/includes/User.php @@ -756,7 +756,7 @@ class User { array( 'wl_title' => $this->getTitleKey(), 'wl_namespace' => NS_USER_TALK, 'wl_user' => $this->mId, - 'wl_notificationtimestamp != 0' ), + 'wl_notificationtimestamp ' . $dbr->notNullTimestamp() ), 'User::getNewtalk' ); if( $dbr->numRows($res) > 0 ) { $this->mNewtalk = 1; @@ -1151,7 +1151,7 @@ class User { $dbw =& wfGetDB( DB_MASTER ); $success = $dbw->update( 'watchlist', array( /* SET */ - 'wl_notificationtimestamp' => 0 + 'wl_notificationtimestamp' => NULL ), array( /* WHERE */ 'wl_title' => $title->getDBkey(), 'wl_namespace' => $title->getNamespace(), diff --git a/includes/UserMailer.php b/includes/UserMailer.php index 274c066c7a..d032cc2a51 100644 --- a/includes/UserMailer.php +++ b/includes/UserMailer.php @@ -18,10 +18,10 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * http://www.gnu.org/copyleft/gpl.html - * + * * @author * @author - * + * * @package MediaWiki */ @@ -49,12 +49,12 @@ function wfRFC822Phrase( $phrase ) { */ function userMailer( $to, $from, $subject, $body, $replyto=false ) { global $wgUser, $wgSMTP, $wgOutputEncoding, $wgErrorString, $wgEmergencyContact; - + if (is_array( $wgSMTP )) { require_once( 'Mail.php' ); - + $timestamp = time(); - + $headers['From'] = $from; if ( $replyto ) { $headers['Reply-To'] = $replyto; @@ -65,13 +65,13 @@ function userMailer( $to, $from, $subject, $body, $replyto=false ) { $headers['Content-transfer-encoding'] = '8bit'; $headers['Message-ID'] = "<{$timestamp}" . $wgUser->getName() . '@' . $wgSMTP['IDHost'] . '>'; $headers['X-Mailer'] = 'MediaWiki mailer'; - + // Create the mail object using the Mail::factory method $mail_object =& Mail::factory('smtp', $wgSMTP); wfDebug( "Sending mail via PEAR::Mail\n" ); $mailResult =& $mail_object->send($to, $headers, $body); - - # Based on the result return an error string, + + # Based on the result return an error string, if ($mailResult === true) return ''; else if (is_object($mailResult)) @@ -130,9 +130,9 @@ function mailErrorHandler( $code, $string ) { * same timeoffset in their preferences. * * Visit the documentation pages under http://meta.wikipedia.com/Enotif - * + * * @package MediaWiki - * + * */ class EmailNotification { /**#@+ @@ -140,9 +140,9 @@ class EmailNotification { */ var $to, $subject, $body, $replyto, $from; var $user, $title, $timestamp, $summary, $minorEdit, $oldid; - + /**#@-*/ - + /** * @todo document * @param $currentPage @@ -153,7 +153,7 @@ class EmailNotification { * @param $oldid (default: false) */ function notifyOnPageChange(&$title, $timestamp, $summary, $minorEdit, $oldid=false) { - + # we use $wgEmergencyContact as sender's address global $wgUser, $wgLang, $wgEmergencyContact; global $wgEnotifWatchlist, $wgEnotifMinorEdits; @@ -165,30 +165,30 @@ class EmailNotification { $fname = 'UserMailer::notifyOnPageChange'; wfProfileIn( $fname ); - + # The following code is only run, if several conditions are met: # 1. EmailNotification for pages (other than user_talk pages) must be enabled # 2. minor edits (changes) are only regarded if the global flag indicates so - + $isUserTalkPage = ($title->getNamespace() == NS_USER_TALK); $enotifusertalkpage = ($isUserTalkPage && $wgEnotifUserTalk); - $enotifwatchlistpage = (!$isUserTalkPage && $wgEnotifWatchlist); + $enotifwatchlistpage = (!$isUserTalkPage && $wgEnotifWatchlist); if ( ($enotifusertalkpage || $enotifwatchlistpage) && (!$minorEdit || $wgEnotifMinorEdits) ) { $dbr =& wfGetDB( DB_MASTER ); extract( $dbr->tableNames( 'watchlist' ) ); - $res = $dbr->select( 'watchlist', array( 'wl_user' ), + $res = $dbr->select( 'watchlist', array( 'wl_user' ), array( 'wl_title' => $title->getDBkey(), 'wl_namespace' => $title->getNamespace(), 'wl_user <> ' . $wgUser->getID(), - 'wl_notificationtimestamp <= 1', + 'wl_notificationtimestamp ' . $dbr->isNullTimestamp(), ), $fname ); # if anyone is watching ... set up the email message text which is # common for all receipients ... - if ( $dbr->numRows( $res ) > 0 ) { + if ( $dbr->numRows( $res ) > 0 ) { $this->user &= $wgUser; $this->title =& $title; $this->timestamp = $timestamp; @@ -198,9 +198,9 @@ class EmailNotification { $this->composeCommonMailtext(); $watchingUser = new User(); - + # ... now do for all watching users ... if the options fit - for ($i = 1; $i <= $dbr->numRows( $res ); $i++) { + for ($i = 1; $i <= $dbr->numRows( $res ); $i++) { $wuser = $dbr->fetchObject( $res ); $watchingUser->setID($wuser->wl_user); @@ -218,12 +218,12 @@ class EmailNotification { } # if $wgEnotifWatchlist = true if ( $wgShowUpdatedMarker || $wgEnotifWatchlist ) { - # mark the changed watch-listed page with a timestamp, so that the page is + # mark the changed watch-listed page with a timestamp, so that the page is # listed with an "updated since your last visit" icon in the watch list, ... $dbw =& wfGetDB( DB_MASTER ); $success = $dbw->update( 'watchlist', array( /* SET */ - 'wl_notificationtimestamp' => $timestamp + 'wl_notificationtimestamp' => $dbw->timestamp($timestamp) ), array( /* WHERE */ 'wl_title' => $title->getDBkey(), 'wl_namespace' => $title->getNamespace(), @@ -232,7 +232,7 @@ class EmailNotification { } } # function NotifyOnChange - + /** * @access private */ @@ -241,20 +241,20 @@ class EmailNotification { global $wgEnotifRevealEditorAddress; global $wgEnotifFromEditor; global $wgNoReplyAddress; - + $summary = ($this->summary == '') ? ' - ' : $this->summary; $medit = ($this->minorEdit) ? wfMsg( 'minoredit' ) : ''; - + # You as the WikiAdmin and Sysops can make use of plenty of # named variables when composing your notification emails while # simply editing the Meta pages - + $subject = wfMsgForContent( 'enotif_subject' ); $body = wfMsgForContent( 'enotif_body' ); $from = ''; /* fail safe */ $replyto = ''; /* fail safe */ $keys = array(); - + # regarding the use of oldid as an indicator for the last visited version, see also # http://bugzilla.wikipeda.org/show_bug.cgi?id=603 "Delete + undelete cycle doesn't preserve old_id" # However, in the case of a new page which is already watched, we have no previous version to compare @@ -269,16 +269,15 @@ class EmailNotification { $keys['$OLDID'] = ''; $keys['$CHANGEDORCREATED'] = wfMsgForContent( 'created' ); } - - $body = strtr( $body, $keys ); + + $body = strtr( $body, $keys ); $pagetitle = $this->title->getPrefixedText(); - $keys['$PAGETITLE'] = $pagetitle; $keys['$PAGETITLE_URL'] = $this->title->getFullUrl(); $keys['$PAGEMINOREDIT'] = $medit; $keys['$PAGESUMMARY'] = $summary; - + $subject = strtr( $subject, $keys ); # Reveal the page editor's address as REPLY-TO address only if @@ -300,13 +299,13 @@ class EmailNotification { $from = $adminAddress; $replyto = $wgNoReplyAddress; } - + if( $wgUser->isIP( $name ) ) { #real anon (user:xxx.xxx.xxx.xxx) $anon = $name . ' (anonymous user)'; $anonUrl = wfUrlencode( $name ) . ' (anonymous user)'; $subject = str_replace('$PAGEEDITOR', 'anonymous user '. $name, $subject); - + $keys['$PAGEEDITOR'] = 'anonymous user ' . $name; $keys['$PAGEEDITOR_EMAIL'] = wfMsgForContent( 'noemailtitle' ); } else { @@ -318,7 +317,6 @@ class EmailNotification { $userPage = $wgUser->getUserPage(); $keys['$PAGEEDITOR_WIKI'] = $userPage->getFullUrl(); $body = strtr( $body, $keys ); - $body = wordwrap( $body, 72 ); # now save this as the constant user-independent part of the message @@ -329,7 +327,7 @@ class EmailNotification { } - + /** * Does the per-user customizations to a notification e-mail (name, * timestamp in proper timezone, etc) and sends it out. @@ -343,11 +341,11 @@ class EmailNotification { function composeAndSendPersonalisedMail( $watchingUser ) { global $wgLang; // From the PHP manual: - // Note: The to parameter cannot be an address in the form of "Something ". + // Note: The to parameter cannot be an address in the form of "Something ". // The mail command will not parse this properly while talking with the MTA. $to = $watchingUser->getEmail(); $body = str_replace( '$WATCHINGUSERNAME', $watchingUser->getName() , $this->body ); - + $timecorrection = $watchingUser->getOption( 'timecorrection' ); if( !$timecorrection ) { # fail safe - I prefer it. TomGries @@ -359,7 +357,7 @@ class EmailNotification { $body = str_replace('$PAGEEDITDATE', $wgLang->timeanddate( $this->timestamp, true, false, $timecorrection ), $body); - + $error = userMailer( $to, $this->from, $this->subject, $body, $this->replyto ); return ($error == ''); } diff --git a/includes/WatchedItem.php b/includes/WatchedItem.php index b27c53faf9..fbbc366b10 100644 --- a/includes/WatchedItem.php +++ b/includes/WatchedItem.php @@ -38,7 +38,7 @@ class WatchedItem { global $wgDBname; return "$wgDBname:watchlist:user:$this->id:page:$this->ns:$this->ti"; } - + /** * Is mTitle being watched by mUser? */ @@ -51,9 +51,9 @@ class WatchedItem { $key = $this->watchKey(); $iswatched = $wgMemc->get( $key ); if( is_integer( $iswatched ) ) return $iswatched; - + $dbr =& wfGetDB( DB_SLAVE ); - $res = $dbr->select( 'watchlist', 1, array( 'wl_user' => $this->id, 'wl_namespace' => $this->ns, + $res = $dbr->select( 'watchlist', 1, array( 'wl_user' => $this->id, 'wl_namespace' => $this->ns, 'wl_title' => $this->ti ), $fname ); $iswatched = ($dbr->numRows( $res ) > 0) ? 1 : 0; $wgMemc->set( $key, $iswatched ); @@ -70,11 +70,11 @@ class WatchedItem { # accidentally reloads a watch-add operation. $dbw =& wfGetDB( DB_MASTER ); $dbw->replace( 'watchlist', array(array('wl_user', 'wl_namespace', 'wl_title', 'wl_notificationtimestamp')), - array( + array( 'wl_user' => $this->id, 'wl_namespace' => ($this->ns & ~1), 'wl_title' => $this->ti, - 'wl_notificationtimestamp' => '0' + 'wl_notificationtimestamp' => NULL ), $fname ); # the following code compensates the new behaviour, introduced by the enotif patch, @@ -85,7 +85,7 @@ class WatchedItem { 'wl_user' => $this->id, 'wl_namespace' => ($this->ns | 1 ), 'wl_title' => $this->ti, - 'wl_notificationtimestamp' => '0' + 'wl_notificationtimestamp' => NULL ), $fname ); global $wgMemc; @@ -100,9 +100,9 @@ class WatchedItem { $success = false; $dbw =& wfGetDB( DB_MASTER ); - $dbw->delete( 'watchlist', - array( - 'wl_user' => $this->id, + $dbw->delete( 'watchlist', + array( + 'wl_user' => $this->id, 'wl_namespace' => ($this->ns & ~1), 'wl_title' => $this->ti ), $fname @@ -122,7 +122,7 @@ class WatchedItem { 'wl_title' => $this->ti ), $fname ); - + if ( $dbw->affectedRows() ) { $success = true; } @@ -145,8 +145,8 @@ class WatchedItem { $dbw =& wfGetDB( DB_MASTER ); $watchlist = $dbw->tableName( 'watchlist' ); - - $res = $dbw->select( 'watchlist', 'wl_user', + + $res = $dbw->select( 'watchlist', 'wl_user', array( 'wl_namespace' => $oldnamespace, 'wl_title' => $oldtitle ), $fname, 'FOR UPDATE' ); diff --git a/install-utils.inc b/install-utils.inc index 62d35c6243..faabab61ba 100644 --- a/install-utils.inc +++ b/install-utils.inc @@ -4,7 +4,7 @@ function install_version_checks() { # We dare not turn output buffer _off_ since this will break completely # if PHP is globally configured to run through a gzip filter. @ob_implicit_flush( true ); - + if( !function_exists( 'version_compare' ) ) { # version_compare was introduced in 4.1.0 die( "Your PHP version is much too old; 4.0.x will _not_ work. 4.3.2 or higher is recommended. ABORTING.\n" ); @@ -12,7 +12,7 @@ function install_version_checks() { if( version_compare( phpversion(), '4.3.2' ) < 0 ) { echo "WARNING: PHP 4.3.2 or higher is recommended. Older versions from 4.1.x up may work but are not actively supported.\n\n"; } - + if (!extension_loaded('mysql')) { if (!dl('mysql.so')) { print 'Could not load MySQL driver! Please compile '. @@ -20,7 +20,7 @@ function install_version_checks() { exit; } } - + global $wgCommandLineMode; $wgCommandLineMode = true; umask( 000 ); @@ -108,18 +108,19 @@ function dbsource( $fname, $database = false ) { if ( $sl < 0 ) { continue; } if ( '-' == $line{0} && '-' == $line{1} ) { continue; } - if ( ';' == $line{$sl} ) { + if ( ';' == $line{$sl} && ($sl < 2 || ';' != $line{$sl - 1})) { $done = true; $line = substr( $line, 0, $sl ); } if ( '' != $cmd ) { $cmd .= ' '; } - $cmd .= $line; + $cmd .= "$line\n"; if ( $done ) { + $cmd = str_replace(';;', ";", $cmd); $cmd = replacevars( $cmd ); if( $database ) - $res = $database->query( $cmd ); + $res = $database->query( $cmd, 'dbsource', true ); else $res = mysql_query( $cmd ); @@ -142,7 +143,7 @@ function field_exists( $table, $field ) { $db =& wfGetDB( DB_SLAVE ); $res = $db->query( "DESCRIBE $table", $fname ); $found = false; - + while ( $row = $db->fetchObject( $res ) ) { if ( $row->Field == $field ) { $found = true; diff --git a/maintenance/InitialiseMessages.inc b/maintenance/InitialiseMessages.inc index 3976d1864d..f85808375a 100755 --- a/maintenance/InitialiseMessages.inc +++ b/maintenance/InitialiseMessages.inc @@ -108,9 +108,6 @@ function initialiseMessagesReal( $overwrite = false, $messageArray = false ) { $timestamp = wfTimestampNow(); #$sql = "SELECT cur_title,cur_is_new,cur_user_text FROM $cur WHERE cur_namespace=$ns AND cur_title IN("; - $sql = "SELECT page_title,page_is_new,rev_user_text FROM $page, $revision WHERE - page_namespace=$ns AND rev_page=page_id AND page_title IN("; - # Get keys from $wgAllMessagesEn, which is more complete than the local language $first = true; if ( $messageArray ) { @@ -123,34 +120,49 @@ function initialiseMessagesReal( $overwrite = false, $messageArray = false ) { # SELECT all existing messages # Can't afford to be locking all rows for update, this script can take quite a long time to complete - foreach ( $sortedArray as $key => $enMsg ) { - if ( $key == '' ) { - continue; // Skip odd members - } - if ( $first ) { - $first = false; - } else { - $sql .= ','; + $rows = array(); + $nitems = count($sortedArray); + $maxitems = $dbr->maxListLen(); + $pos = 0; + if ($maxitems) + $chunks = array_chunk($sortedArray, $maxitems); + else + $chucks = array($sortedArray); + + foreach ($chunks as $chunk) { + $first = true; + $sql = "SELECT page_title,page_is_new,rev_user_text FROM $page, $revision WHERE + page_namespace=$ns AND rev_page=page_id AND page_title IN("; + + foreach ( $chunk as $key => $enMsg ) { + if ( $key == '' ) { + continue; // Skip odd members + } + if ( $first ) { + $first = false; + } else { + $sql .= ','; + } + $titleObj = Title::newFromText( $wgContLang->ucfirst( $key ) ); + $enctitle = $dbr->strencode($titleObj->getDBkey()); + $sql .= "'$enctitle'"; } - $titleObj = Title::newFromText( $wgContLang->ucfirst( $key ) ); - $enctitle = $dbr->strencode($titleObj->getDBkey()); - $sql .= "'$enctitle'"; - } - $sql .= ')'; - $res = $dbr->query( $sql ); - $row = $dbr->fetchObject( $res ); + $sql .= ')'; + $res = $dbr->query( $sql ); + while ($row = $dbr->fetchObject($res)) + $rows[] = $row; + } + # Read the results into an array # Decide whether or not each one needs to be overwritten $existingTitles = array(); - while ( $row ) { + foreach ($rows as $row) { if ( $row->rev_user_text != $username && $row->rev_user_text != 'Template namespace initialisation script' ) { $existingTitles[$row->page_title] = 'keep'; } else { $existingTitles[$row->page_title] = 'chuck'; } - - $row = $dbr->fetchObject( $res ); } # Insert queries are done in one multi-row insert diff --git a/maintenance/importDump.php b/maintenance/importDump.php index 95c0f31bc4..314c44c148 100644 --- a/maintenance/importDump.php +++ b/maintenance/importDump.php @@ -2,17 +2,17 @@ /** * Copyright (C) 2005 Brion Vibber * http://www.mediawiki.org/ - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or + * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. @@ -33,35 +33,38 @@ class BackupReader { var $pageCount = 0; var $revCount = 0; var $dryRun = false; - + function BackupReader() { $this->stderr = fopen( "php://stderr", "wt" ); } - + function reportPage( $page ) { $this->pageCount++; } - + function handleRevision( $rev ) { $title = $rev->getTitle(); + if (!$title) { + return; + } $display = $title->getPrefixedText(); $timestamp = $rev->getTimestamp(); #echo "$display $timestamp\n"; - + $this->revCount++; $this->report(); - + if( !$this->dryRun ) { call_user_func( $this->importCallback, $rev ); } } - + function report( $final = false ) { if( $final xor ( $this->pageCount % $this->reportingInterval == 0 ) ) { $this->showReport(); } } - + function showReport() { if( $this->reporting ) { $delta = wfTime() - $this->startTime; @@ -75,11 +78,11 @@ class BackupReader { $this->progress( "$this->pageCount ($rate pages/sec $revrate revs/sec)" ); } } - + function progress( $string ) { fwrite( $this->stderr, $string . "\n" ); } - + function importFromFile( $filename ) { if( preg_match( '/\.gz$/', $filename ) ) { $filename = 'compress.zlib://' . $filename; @@ -87,22 +90,22 @@ class BackupReader { $file = fopen( $filename, 'rt' ); $this->importFromHandle( $file ); } - + function importFromStdin() { $file = fopen( 'php://stdin', 'rt' ); $this->importFromHandle( $file ); } - + function importFromHandle( $handle ) { $this->startTime = wfTime(); - + $source = new ImportStreamSource( $handle ); $importer = new WikiImporter( $source ); - + $importer->setPageCallback( array( &$this, 'reportPage' ) ); $this->importCallback = $importer->setRevisionCallback( array( &$this, 'handleRevision' ) ); - + $importer->doImport(); } } diff --git a/maintenance/oracle/archives/patch-transcache.sql b/maintenance/oracle/archives/patch-transcache.sql new file mode 100644 index 0000000000..62ad2c7dfa --- /dev/null +++ b/maintenance/oracle/archives/patch-transcache.sql @@ -0,0 +1,5 @@ +CREATE TABLE transcache ( + tc_url VARCHAR2(255) NOT NULL UNIQUE, + tc_contents CLOB, + tc_time TIMESTAMP NOT NULL +); diff --git a/maintenance/oracle/interwiki.sql b/maintenance/oracle/interwiki.sql new file mode 100644 index 0000000000..09d01c6462 --- /dev/null +++ b/maintenance/oracle/interwiki.sql @@ -0,0 +1,178 @@ +-- Based more or less on the public interwiki map from MeatballWiki +-- Default interwiki prefixes... + +CALL add_interwiki('abbenormal','http://www.ourpla.net/cgi-bin/pikie.cgi?$1',0); +CALL add_interwiki('acadwiki','http://xarch.tu-graz.ac.at/autocad/wiki/$1',0); +CALL add_interwiki('acronym','http://www.acronymfinder.com/af-query.asp?String=exact&Acronym=$1',0); +CALL add_interwiki('advogato','http://www.advogato.org/$1',0); +CALL add_interwiki('aiwiki','http://www.ifi.unizh.ch/ailab/aiwiki/aiw.cgi?$1',0); +CALL add_interwiki('alife','http://news.alife.org/wiki/index.php?$1',0); +CALL add_interwiki('annotation','http://bayle.stanford.edu/crit/nph-med.cgi/$1',0); +CALL add_interwiki('annotationwiki','http://www.seedwiki.com/page.cfm?wikiid=368&doc=$1',0); +CALL add_interwiki('arxiv','http://www.arxiv.org/abs/$1',0); +CALL add_interwiki('aspienetwiki','http://aspie.mela.de/Wiki/index.php?title=$1',0); +CALL add_interwiki('bemi','http://bemi.free.fr/vikio/index.php?$1',0); +CALL add_interwiki('benefitswiki','http://www.benefitslink.com/cgi-bin/wiki.cgi?$1',0); +CALL add_interwiki('brasilwiki','http://rio.ifi.unizh.ch/brasilienwiki/index.php/$1',0); +CALL add_interwiki('bridgeswiki','http://c2.com/w2/bridges/$1',0); +CALL add_interwiki('c2find','http://c2.com/cgi/wiki?FindPage&value=$1',0); +CALL add_interwiki('cache','http://www.google.com/search?q=cache:$1',0); +CALL add_interwiki('ciscavate','http://ciscavate.org/index.php/$1',0); +CALL add_interwiki('cliki','http://ww.telent.net/cliki/$1',0); +CALL add_interwiki('cmwiki','http://www.ourpla.net/cgi-bin/wiki.pl?$1',0); +CALL add_interwiki('codersbase','http://www.codersbase.com/$1',0); +CALL add_interwiki('commons','http://commons.wikimedia.org/wiki/$1',0); +CALL add_interwiki('consciousness','http://teadvus.inspiral.org/',0); +CALL add_interwiki('corpknowpedia','http://corpknowpedia.org/wiki/index.php/$1',0); +CALL add_interwiki('creationmatters','http://www.ourpla.net/cgi-bin/wiki.pl?$1',0); +CALL add_interwiki('dejanews','http://www.deja.com/=dnc/getdoc.xp?AN=$1',0); +CALL add_interwiki('demokraatia','http://wiki.demokraatia.ee/',0); +CALL add_interwiki('dictionary','http://www.dict.org/bin/Dict?Database=*&Form=Dict1&Strategy=*&Query=$1',0); +CALL add_interwiki('disinfopedia','http://www.disinfopedia.org/wiki.phtml?title=$1',0); +CALL add_interwiki('diveintoosx','http://diveintoosx.org/$1',0); +CALL add_interwiki('docbook','http://docbook.org/wiki/moin.cgi/$1',0); +CALL add_interwiki('dolphinwiki','http://www.object-arts.com/wiki/html/Dolphin/$1',0); +CALL add_interwiki('drumcorpswiki','http://www.drumcorpswiki.com/index.php/$1',0); +CALL add_interwiki('dwjwiki','http://www.suberic.net/cgi-bin/dwj/wiki.cgi?$1',0); +CALL add_interwiki('eĉei','http://www.ikso.net/cgi-bin/wiki.pl?$1',0); +CALL add_interwiki('echei','http://www.ikso.net/cgi-bin/wiki.pl?$1',0); +CALL add_interwiki('ecxei','http://www.ikso.net/cgi-bin/wiki.pl?$1',0); +CALL add_interwiki('efnetceewiki','http://purl.net/wiki/c/$1',0); +CALL add_interwiki('efnetcppwiki','http://purl.net/wiki/cpp/$1',0); +CALL add_interwiki('efnetpythonwiki','http://purl.net/wiki/python/$1',0); +CALL add_interwiki('efnetxmlwiki','http://purl.net/wiki/xml/$1',0); +CALL add_interwiki('eljwiki','http://elj.sourceforge.net/phpwiki/index.php/$1',0); +CALL add_interwiki('emacswiki','http://www.emacswiki.org/cgi-bin/wiki.pl?$1',0); +CALL add_interwiki('elibre','http://enciclopedia.us.es/index.php/$1',0); +CALL add_interwiki('eokulturcentro','http://esperanto.toulouse.free.fr/wakka.php?wiki=$1',0); +CALL add_interwiki('evowiki','http://www.evowiki.org/index.php/$1',0); +CALL add_interwiki('finalempire','http://final-empire.sourceforge.net/cgi-bin/wiki.pl?$1',0); +CALL add_interwiki('firstwiki','http://firstwiki.org/index.php/$1',0); +CALL add_interwiki('foldoc','http://www.foldoc.org/foldoc/foldoc.cgi?$1',0); +CALL add_interwiki('foxwiki','http://fox.wikis.com/wc.dll?Wiki~$1',0); +CALL add_interwiki('fr.be','http://fr.wikinations.be/$1',0); +CALL add_interwiki('fr.ca','http://fr.ca.wikinations.org/$1',0); +CALL add_interwiki('fr.fr','http://fr.fr.wikinations.org/$1',0); +CALL add_interwiki('fr.org','http://fr.wikinations.org/$1',0); +CALL add_interwiki('freebsdman','http://www.FreeBSD.org/cgi/man.cgi?apropos=1&query=$1',0); +CALL add_interwiki('gamewiki','http://gamewiki.org/wiki/index.php/$1',0); +CALL add_interwiki('gej','http://www.esperanto.de/cgi-bin/aktivikio/wiki.pl?$1',0); +CALL add_interwiki('gentoo-wiki','http://gentoo-wiki.com/$1',0); +CALL add_interwiki('globalvoices','http://cyber.law.harvard.edu/dyn/globalvoices/wiki/$1',0); +CALL add_interwiki('gmailwiki','http://www.gmailwiki.com/index.php/$1',0); +CALL add_interwiki('google','http://www.google.com/search?q=$1',0); +CALL add_interwiki('googlegroups','http://groups.google.com/groups?q=$1',0); +CALL add_interwiki('gotamac','http://www.got-a-mac.org/$1',0); +CALL add_interwiki('greencheese','http://www.greencheese.org/$1',0); +CALL add_interwiki('hammondwiki','http://www.dairiki.org/HammondWiki/index.php3?$1',0); +CALL add_interwiki('haribeau','http://wiki.haribeau.de/cgi-bin/wiki.pl?$1',0); +CALL add_interwiki('hewikisource','http://he.wikisource.org/wiki/$1',1); +CALL add_interwiki('herzkinderwiki','http://www.herzkinderinfo.de/Mediawiki/index.php/$1',0); +CALL add_interwiki('hrwiki','http://www.hrwiki.org/index.php/$1',0); +CALL add_interwiki('iawiki','http://www.IAwiki.net/$1',0); +CALL add_interwiki('imdb','http://us.imdb.com/Title?$1',0); +CALL add_interwiki('infosecpedia','http://www.infosecpedia.org/pedia/index.php/$1',0); +CALL add_interwiki('jargonfile','http://sunir.org/apps/meta.pl?wiki=JargonFile&redirect=$1',0); +CALL add_interwiki('jefo','http://www.esperanto-jeunes.org/vikio/index.php?$1',0); +CALL add_interwiki('jiniwiki','http://www.cdegroot.com/cgi-bin/jini?$1',0); +CALL add_interwiki('jspwiki','http://www.ecyrd.com/JSPWiki/Wiki.jsp?page=$1',0); +CALL add_interwiki('kerimwiki','http://wiki.oxus.net/$1',0); +CALL add_interwiki('kmwiki','http://www.voght.com/cgi-bin/pywiki?$1',0); +CALL add_interwiki('knowhow','http://www2.iro.umontreal.ca/~paquetse/cgi-bin/wiki.cgi?$1',0); +CALL add_interwiki('lanifexwiki','http://opt.lanifex.com/cgi-bin/wiki.pl?$1',0); +CALL add_interwiki('lasvegaswiki','http://wiki.gmnow.com/index.php/$1',0); +CALL add_interwiki('linuxwiki','http://www.linuxwiki.de/$1',0); +CALL add_interwiki('lojban','http://www.lojban.org/tiki/tiki-index.php?page=$1',0); +CALL add_interwiki('lqwiki','http://wiki.linuxquestions.org/wiki/$1',0); +CALL add_interwiki('lugkr','http://lug-kr.sourceforge.net/cgi-bin/lugwiki.pl?$1',0); +CALL add_interwiki('lutherwiki','http://www.lutheranarchives.com/mw/index.php/$1',0); +CALL add_interwiki('mathsongswiki','http://SeedWiki.com/page.cfm?wikiid=237&doc=$1',0); +CALL add_interwiki('mbtest','http://www.usemod.com/cgi-bin/mbtest.pl?$1',0); +CALL add_interwiki('meatball','http://www.usemod.com/cgi-bin/mb.pl?$1',0); +CALL add_interwiki('mediazilla','http://bugzilla.wikipedia.org/$1',1); +CALL add_interwiki('memoryalpha','http://www.memory-alpha.org/en/index.php/$1',0); +CALL add_interwiki('metaweb','http://www.metaweb.com/wiki/wiki.phtml?title=$1',0); +CALL add_interwiki('metawiki','http://sunir.org/apps/meta.pl?$1',0); +CALL add_interwiki('metawikipedia','http://meta.wikimedia.org/wiki/$1',0); +CALL add_interwiki('moinmoin','http://purl.net/wiki/moin/$1',0); +CALL add_interwiki('mozillawiki','http://wiki.mozilla.org/index.php/$1',0); +CALL add_interwiki('muweb','http://www.dunstable.com/scripts/MuWebWeb?$1',0); +CALL add_interwiki('netvillage','http://www.netbros.com/?$1',0); +CALL add_interwiki('oeis','http://www.research.att.com/cgi-bin/access.cgi/as/njas/sequences/eisA.cgi?Anum=$1',0); +CALL add_interwiki('openfacts','http://openfacts.berlios.de/index.phtml?title=$1',0); +CALL add_interwiki('openwiki','http://openwiki.com/?$1',0); +CALL add_interwiki('opera7wiki','http://nontroppo.org/wiki/$1',0); +CALL add_interwiki('orgpatterns','http://www.bell-labs.com/cgi-user/OrgPatterns/OrgPatterns?$1',0); +CALL add_interwiki('osi reference model','http://wiki.tigma.ee/',0); +CALL add_interwiki('pangalacticorg','http://www.pangalactic.org/Wiki/$1',0); +CALL add_interwiki('personaltelco','http://www.personaltelco.net/index.cgi/$1',0); +CALL add_interwiki('patwiki','http://gauss.ffii.org/$1',0); +CALL add_interwiki('phpwiki','http://phpwiki.sourceforge.net/phpwiki/index.php?$1',0); +CALL add_interwiki('pikie','http://pikie.darktech.org/cgi/pikie?$1',0); +CALL add_interwiki('pmeg','http://www.bertilow.com/pmeg/$1.php',0); +CALL add_interwiki('ppr','http://c2.com/cgi/wiki?$1',0); +CALL add_interwiki('purlnet','http://purl.oclc.org/NET/$1',0); +CALL add_interwiki('pythoninfo','http://www.python.org/cgi-bin/moinmoin/$1',0); +CALL add_interwiki('pythonwiki','http://www.pythonwiki.de/$1',0); +CALL add_interwiki('pywiki','http://www.voght.com/cgi-bin/pywiki?$1',0); +CALL add_interwiki('raec','http://www.raec.clacso.edu.ar:8080/raec/Members/raecpedia/$1',0); +CALL add_interwiki('revo','http://purl.org/NET/voko/revo/art/$1.html',0); +CALL add_interwiki('rfc','http://www.rfc-editor.org/rfc/rfc$1.txt',0); +CALL add_interwiki('s23wiki','http://is-root.de/wiki/index.php/$1',0); +CALL add_interwiki('scoutpedia','http://www.scoutpedia.info/index.php/$1',0); +CALL add_interwiki('seapig','http://www.seapig.org/$1',0); +CALL add_interwiki('seattlewiki','http://seattlewiki.org/wiki/$1',0); +CALL add_interwiki('seattlewireless','http://seattlewireless.net/?$1',0); +CALL add_interwiki('seeds','http://www.IslandSeeds.org/wiki/$1',0); +CALL add_interwiki('senseislibrary','http://senseis.xmp.net/?$1',0); +CALL add_interwiki('shakti','http://cgi.algonet.se/htbin/cgiwrap/pgd/ShaktiWiki/$1',0); +CALL add_interwiki('slashdot','http://slashdot.org/article.pl?sid=$1',0); +CALL add_interwiki('smikipedia','http://www.smikipedia.org/$1',0); +CALL add_interwiki('sockwiki','http://wiki.socklabs.com/$1',0); +CALL add_interwiki('sourceforge','http://sourceforge.net/$1',0); +CALL add_interwiki('squeak','http://minnow.cc.gatech.edu/squeak/$1',0); +CALL add_interwiki('strikiwiki','http://ch.twi.tudelft.nl/~mostert/striki/teststriki.pl?$1',0); +CALL add_interwiki('susning','http://www.susning.nu/$1',0); +CALL add_interwiki('svgwiki','http://www.protocol7.com/svg-wiki/default.asp?$1',0); +CALL add_interwiki('tavi','http://tavi.sourceforge.net/$1',0); +CALL add_interwiki('tejo','http://www.tejo.org/vikio/$1',0); +CALL add_interwiki('terrorwiki','http://www.liberalsagainstterrorism.com/wiki/index.php/$1',0); +CALL add_interwiki('tmbw','http://www.tmbw.net/wiki/index.php/$1',0); +CALL add_interwiki('tmnet','http://www.technomanifestos.net/?$1',0); +CALL add_interwiki('tmwiki','http://www.EasyTopicMaps.com/?page=$1',0); +CALL add_interwiki('turismo','http://www.tejo.org/turismo/$1',0); +CALL add_interwiki('theopedia','http://www.theopedia.com/$1',0); +CALL add_interwiki('twiki','http://twiki.org/cgi-bin/view/$1',0); +CALL add_interwiki('twistedwiki','http://purl.net/wiki/twisted/$1',0); +CALL add_interwiki('uea','http://www.tejo.org/uea/$1',0); +CALL add_interwiki('unreal','http://wiki.beyondunreal.com/wiki/$1',0); +CALL add_interwiki('ursine','http://ursine.ca/$1',0); +CALL add_interwiki('usej','http://www.tejo.org/usej/$1',0); +CALL add_interwiki('usemod','http://www.usemod.com/cgi-bin/wiki.pl?$1',0); +CALL add_interwiki('visualworks','http://wiki.cs.uiuc.edu/VisualWorks/$1',0); +CALL add_interwiki('warpedview','http://www.warpedview.com/index.php/$1',0); +CALL add_interwiki('webdevwikinl','http://www.promo-it.nl/WebDevWiki/index.php?page=$1',0); +CALL add_interwiki('webisodes','http://www.webisodes.org/$1',0); +CALL add_interwiki('webseitzwiki','http://webseitz.fluxent.com/wiki/$1',0); +CALL add_interwiki('why','http://clublet.com/c/c/why?$1',0); +CALL add_interwiki('wiki','http://c2.com/cgi/wiki?$1',0); +CALL add_interwiki('wikia','http://www.wikia.com/wiki/index.php/$1',0); +CALL add_interwiki('wikibooks','http://en.wikibooks.org/wiki/$1',1); +CALL add_interwiki('wikicities','http://www.wikicities.com/index.php/$1',0); +CALL add_interwiki('wikif1','http://www.wikif1.org/$1',0); +CALL add_interwiki('wikinfo','http://www.wikinfo.org/wiki.php?title=$1',0); +CALL add_interwiki('wikimedia','http://wikimediafoundation.org/wiki/$1',0); +CALL add_interwiki('wikiquote','http://en.wikiquote.org/wiki/$1',1); +CALL add_interwiki('wikinews','http://en.wikinews.org/wiki/$1',0); +CALL add_interwiki('wikisource','http://sources.wikipedia.org/wiki/$1',1); +CALL add_interwiki('wikispecies','http://species.wikipedia.org/wiki/$1',1); +CALL add_interwiki('wikitravel','http://wikitravel.org/en/$1',0); +CALL add_interwiki('wikiworld','http://WikiWorld.com/wiki/index.php/$1',0); +CALL add_interwiki('wiktionary','http://en.wiktionary.org/wiki/$1',1); +CALL add_interwiki('wlug','http://www.wlug.org.nz/$1',0); +CALL add_interwiki('wlwiki','http://winslowslair.supremepixels.net/wiki/index.php/$1',0); +CALL add_interwiki('ypsieyeball','http://sknkwrks.dyndns.org:1957/writewiki/wiki.pl?$1',0); +CALL add_interwiki('zwiki','http://www.zwiki.org/$1',0); +CALL add_interwiki('zzz wiki','http://wiki.zzz.ee/',0); +CALL add_interwiki('wikt','http://en.wiktionary.org/wiki/$1',1); + diff --git a/maintenance/oracle/tables.sql b/maintenance/oracle/tables.sql new file mode 100644 index 0000000000..7ccdb60f8c --- /dev/null +++ b/maintenance/oracle/tables.sql @@ -0,0 +1,344 @@ +-- SQL to create the initial tables for the MediaWiki database. +-- This is read and executed by the install script; you should +-- not have to run it by itself unless doing a manual install. + +CREATE SEQUENCE user_user_id_seq; + +CREATE TABLE "user" ( + user_id NUMBER(5) NOT NULL PRIMARY KEY, + user_name VARCHAR2(255) DEFAULT '' NOT NULL, + user_real_name VARCHAR2(255) DEFAULT '', + user_password VARCHAR2(128) DEFAULT '', + user_newpassword VARCHAR2(128) default '', + user_email VARCHAR2(255) default '', + user_options CLOB default '', + user_touched TIMESTAMP WITH TIME ZONE, + user_token CHAR(32) default '', + user_email_authenticated TIMESTAMP WITH TIME ZONE DEFAULT NULL, + user_email_token CHAR(32), + user_email_token_expires TIMESTAMP WITH TIME ZONE DEFAULT NULL +); +CREATE UNIQUE INDEX user_name_idx ON "user" (user_name); +CREATE INDEX user_email_token_idx ON "user" (user_email_token); + +CREATE TABLE user_groups ( + ug_user NUMBER(5) DEFAULT '0' NOT NULL + REFERENCES "user" (user_id) + ON DELETE CASCADE, + ug_group VARCHAR2(16) NOT NULL, + CONSTRAINT user_groups_pk PRIMARY KEY (ug_user, ug_group) +); +CREATE INDEX user_groups_group_idx ON user_groups(ug_group); + +CREATE TABLE user_newtalk ( + user_id NUMBER(5) DEFAULT 0 NOT NULL, + user_ip VARCHAR2(40) DEFAULT '' NOT NULL +); +CREATE INDEX user_newtalk_id_idx ON user_newtalk(user_id); +CREATE INDEX user_newtalk_ip_idx ON user_newtalk(user_ip); + +CREATE SEQUENCE page_page_id_seq; +CREATE TABLE page ( + page_id NUMBER(8) NOT NULL PRIMARY KEY, + page_namespace NUMBER(5) NOT NULL, + page_title VARCHAR(255) NOT NULL, + page_restrictions CLOB DEFAULT '', + page_counter NUMBER(20) DEFAULT 0 NOT NULL, + page_is_redirect NUMBER(1) DEFAULT 0 NOT NULL, + page_is_new NUMBER(1) DEFAULT 0 NOT NULL, + page_random NUMBER(25, 24) NOT NULL, + page_touched TIMESTAMP WITH TIME ZONE, + page_latest NUMBER(8) NOT NULL, + page_len NUMBER(8) DEFAULT 0 +); +CREATE UNIQUE INDEX page_id_namespace_title_idx ON page(page_namespace, page_title); +CREATE INDEX page_random_idx ON page(page_random); +CREATE INDEX page_len_idx ON page(page_len); + +CREATE SEQUENCE rev_rev_id_val; +CREATE TABLE revision ( + rev_id NUMBER(8) NOT NULL, + rev_page NUMBER(8) NOT NULL + REFERENCES page (page_id) + ON DELETE CASCADE, + rev_text_id NUMBER(8) NOT NULL, + rev_comment CLOB, + rev_user NUMBER(8) DEFAULT 0 NOT NULL, + rev_user_text VARCHAR2(255) DEFAULT '' NOT NULL, + rev_timestamp TIMESTAMP WITH TIME ZONE NOT NULL, + rev_minor_edit NUMBER(1) DEFAULT 0 NOT NULL, + rev_deleted NUMBER(1) DEFAULT 0 NOT NULL, + CONSTRAINT revision_pk PRIMARY KEY (rev_page, rev_id) +); + +CREATE UNIQUE INDEX rev_id_idx ON revision(rev_id); +CREATE INDEX rev_timestamp_idx ON revision(rev_timestamp); +CREATE INDEX rev_page_timestamp_idx ON revision(rev_page, rev_timestamp); +CREATE INDEX rev_user_timestamp_idx ON revision(rev_user, rev_timestamp); +CREATE INDEX rev_usertext_timestamp_idx ON revision(rev_user_text, rev_timestamp); + +CREATE SEQUENCE text_old_id_val; + +CREATE TABLE text ( + old_id NUMBER(8) NOT NULL, + old_text CLOB, + old_flags CLOB, + CONSTRAINT text_pk PRIMARY KEY (old_id) +); + +CREATE TABLE archive ( + ar_namespace NUMBER(5) NOT NULL, + ar_title VARCHAR2(255) NOT NULL, + ar_text CLOB, + ar_comment CLOB, + ar_user NUMBER(8), + ar_user_text VARCHAR2(255) NOT NULL, + ar_timestamp TIMESTAMP WITH TIME ZONE NOT NULL, + ar_minor_edit NUMBER(1) DEFAULT 0 NOT NULL, + ar_flags CLOB, + ar_rev_id NUMBER(8), + ar_text_id NUMBER(8) +); +CREATE INDEX archive_name_title_timestamp ON archive(ar_namespace,ar_title,ar_timestamp); + +CREATE TABLE pagelinks ( + pl_from NUMBER(8) NOT NULL + REFERENCES page(page_id) + ON DELETE CASCADE, + pl_namespace NUMBER(4) DEFAULT 0 NOT NULL, + pl_title VARCHAR2(255) NOT NULL +); +CREATE UNIQUE INDEX pl_from ON pagelinks(pl_from, pl_namespace, pl_title); +CREATE INDEX pl_namespace ON pagelinks(pl_namespace, pl_title); + +CREATE TABLE imagelinks ( + il_from NUMBER(8) NOT NULL REFERENCES page(page_id) ON DELETE CASCADE, + il_to VARCHAR2(255) NOT NULL +); +CREATE UNIQUE INDEX il_from ON imagelinks(il_from, il_to); +CREATE INDEX il_to ON imagelinks(il_to); + +CREATE TABLE categorylinks ( + cl_from NUMBER(8) NOT NULL REFERENCES page(page_id) ON DELETE CASCADE, + cl_to VARCHAR2(255) NOT NULL, + cl_sortkey VARCHAR2(86) default '', + cl_timestamp TIMESTAMP WITH TIME ZONE NOT NULL +); +CREATE UNIQUE INDEX cl_from ON categorylinks(cl_from, cl_to); +CREATE INDEX cl_sortkey ON categorylinks(cl_to, cl_sortkey); +CREATE INDEX cl_timestamp ON categorylinks(cl_to, cl_timestamp); + +-- +-- Contains a single row with some aggregate info +-- on the state of the site. +-- +CREATE TABLE site_stats ( + ss_row_id NUMBER(8) NOT NULL, + ss_total_views NUMBER(20) default 0, + ss_total_edits NUMBER(20) default 0, + ss_good_articles NUMBER(20) default 0, + ss_total_pages NUMBER(20) default -1, + ss_users NUMBER(20) default -1, + ss_admins NUMBER(10) default -1 +); +CREATE UNIQUE INDEX ss_row_id ON site_stats(ss_row_id); + +-- +-- Stores an ID for every time any article is visited; +-- depending on $wgHitcounterUpdateFreq, it is +-- periodically cleared and the page_counter column +-- in the page table updated for the all articles +-- that have been visited.) +-- +CREATE TABLE hitcounter ( + hc_id NUMBER NOT NULL +); + +-- +-- The internet is full of jerks, alas. Sometimes it's handy +-- to block a vandal or troll account. +-- +CREATE SEQUENCE ipblocks_ipb_id_val; +CREATE TABLE ipblocks ( + ipb_id NUMBER(8) NOT NULL, + ipb_address VARCHAR2(40), + ipb_user NUMBER(8), + ipb_by NUMBER(8) NOT NULL + REFERENCES "user" (user_id) + ON DELETE CASCADE, + ipb_reason CLOB, + ipb_timestamp TIMESTAMP WITH TIME ZONE NOT NULL, + ipb_auto NUMBER(1) DEFAULT 0 NOT NULL, + ipb_expiry TIMESTAMP WITH TIME ZONE, + CONSTRAINT ipblocks_pk PRIMARY KEY (ipb_id) +); +CREATE INDEX ipb_address ON ipblocks(ipb_address); +CREATE INDEX ipb_user ON ipblocks(ipb_user); + +CREATE TABLE image ( + img_name VARCHAR2(255) NOT NULL, + img_size NUMBER(8) NOT NULL, + img_width NUMBER(5) NOT NULL, + img_height NUMBER(5) NOT NULL, + img_metadata CLOB, + img_bits NUMBER(3), + img_media_type VARCHAR2(10), + img_major_mime VARCHAR2(12) DEFAULT 'unknown', + img_minor_mime VARCHAR2(32) DEFAULT 'unknown', + img_description CLOB NOT NULL, + img_user NUMBER(8) NOT NULL REFERENCES "user"(user_id) ON DELETE CASCADE, + img_user_text VARCHAR2(255) NOT NULL, + img_timestamp TIMESTAMP WITH TIME ZONE, + CONSTRAINT image_pk PRIMARY KEY (img_name) +); +CREATE INDEX img_size_idx ON image(img_size); +CREATE INDEX img_timestamp_idx ON image(img_timestamp); + +CREATE TABLE oldimage ( + oi_name VARCHAR2(255) NOT NULL, + oi_archive_name VARCHAR2(255) NOT NULL, + oi_size NUMBER(8) NOT NULL, + oi_width NUMBER(5) NOT NULL, + oi_height NUMBER(5) NOT NULL, + oi_bits NUMBER(3) NOT NULL, + oi_description CLOB, + oi_user NUMBER(8) NOT NULL REFERENCES "user"(user_id), + oi_user_text VARCHAR2(255) NOT NULL, + oi_timestamp TIMESTAMP WITH TIME ZONE NOT NULL +); +CREATE INDEX oi_name ON oldimage (oi_name); + +CREATE SEQUENCE rc_rc_id_seq; +CREATE TABLE recentchanges ( + rc_id NUMBER(8) NOT NULL, + rc_timestamp TIMESTAMP WITH TIME ZONE, + rc_cur_time TIMESTAMP WITH TIME ZONE, + rc_user NUMBER(8) DEFAULT 0 NOT NULL, + rc_user_text VARCHAR2(255), + rc_namespace NUMBER(4) DEFAULT 0 NOT NULL, + rc_title VARCHAR2(255) NOT NULL, + rc_comment VARCHAR2(255), + rc_minor NUMBER(3) DEFAULT 0 NOT NULL, + rc_bot NUMBER(3) DEFAULT 0 NOT NULL, + rc_new NUMBER(3) DEFAULT 0 NOT NULL, + rc_cur_id NUMBER(8), + rc_this_oldid NUMBER(8) NOT NULL, + rc_last_oldid NUMBER(8) NOT NULL, + rc_type NUMBER(3) DEFAULT 0 NOT NULL, + rc_moved_to_ns NUMBER(3), + rc_moved_to_title VARCHAR2(255), + rc_patrolled NUMBER(3) DEFAULT 0 NOT NULL, + rc_ip VARCHAR2(40), + CONSTRAINT rc_pk PRIMARY KEY (rc_id) +); +CREATE INDEX rc_timestamp ON recentchanges (rc_timestamp); +CREATE INDEX rc_namespace_title ON recentchanges(rc_namespace, rc_title); +CREATE INDEX rc_cur_id ON recentchanges(rc_cur_id); +CREATE INDEX new_name_timestamp ON recentchanges(rc_new, rc_namespace, rc_timestamp); +CREATE INDEX rc_ip ON recentchanges(rc_ip); + +CREATE TABLE watchlist ( + wl_user NUMBER(8) NOT NULL + REFERENCES "user"(user_id) + ON DELETE CASCADE, + wl_namespace NUMBER(8) DEFAULT 0 NOT NULL, + wl_title VARCHAR2(255) NOT NULL, + wl_notificationtimestamp TIMESTAMP WITH TIME ZONE DEFAULT NULL +); +CREATE UNIQUE INDEX wl_user_namespace_title ON watchlist + (wl_user, wl_namespace, wl_title); +CREATE INDEX wl_namespace_title ON watchlist(wl_namespace, wl_title); + +-- +-- Used by texvc math-rendering extension to keep track +-- of previously-rendered items. +-- +CREATE TABLE math ( + math_inputhash VARCHAR2(16) NOT NULL UNIQUE, + math_outputhash VARCHAR2(16) NOT NULL, + math_html_conservativeness NUMBER(1) NOT NULL, + math_html CLOB, + math_mathml CLOB +); + +-- +-- Recognized interwiki link prefixes +-- +CREATE TABLE interwiki ( + iw_prefix VARCHAR2(32) NOT NULL UNIQUE, + iw_url VARCHAR2(127) NOT NULL, + iw_local NUMBER(1) NOT NULL, + iw_trans NUMBER(1) DEFAULT 0 NOT NULL +); + +CREATE TABLE querycache ( + qc_type VARCHAR2(32) NOT NULL, + qc_value NUMBER(5) DEFAULT 0 NOT NULL, + qc_namespace NUMBER(4) DEFAULT 0 NOT NULL, + qc_title VARCHAR2(255) +); +CREATE INDEX querycache_type_value ON querycache(qc_type, qc_value); + +-- +-- For a few generic cache operations if not using Memcached +-- +CREATE TABLE objectcache ( + keyname CHAR(255) DEFAULT '', + value CLOB, + exptime TIMESTAMP WITH TIME ZONE +); +CREATE UNIQUE INDEX oc_keyname_idx ON objectcache(keyname); +CREATE INDEX oc_exptime_idx ON objectcache(exptime); + +CREATE TABLE "validate" ( + val_user NUMBER(11) DEFAULT 0 NOT NULL, + val_page NUMBER(11) DEFAULT 0 NOT NULL, + val_revision NUMBER(11) DEFAULT 0 NOT NULL, + val_type NUMBER(11) DEFAULT 0 NOT NULL, + val_value NUMBER(11) DEFAULT 0, + val_comment VARCHAR2(255), + val_ip VARCHAR2(20) +); +CREATE INDEX val_user ON "validate" (val_user,val_revision); + +CREATE TABLE logging ( + log_type VARCHAR2(10) NOT NULL, + log_action VARCHAR2(10) NOT NULL, + log_timestamp TIMESTAMP WITH TIME ZONE NOT NULL, + log_user NUMBER(8) REFERENCES "user"(user_id), + log_namespace NUMBER(4), + log_title VARCHAR2(255) NOT NULL, + log_comment VARCHAR2(255), + log_params CLOB +); +CREATE INDEX logging_type_name ON logging(log_type, log_timestamp); +CREATE INDEX logging_user_time ON logging(log_user, log_timestamp); +CREATE INDEX logging_page_time ON logging(log_namespace, log_title, log_timestamp); + +-- Hold group name and description +--CREATE TABLE /*$wgDBprefix*/groups ( +-- gr_id int(5) unsigned NOT NULL auto_increment, +-- gr_name varchar(50) NOT NULL default '', +-- gr_description varchar(255) NOT NULL default '', +-- gr_rights tinyblob, +-- PRIMARY KEY (gr_id) +-- +--) TYPE=InnoDB; + +CREATE OR REPLACE PROCEDURE add_user_right (name VARCHAR2, new_right VARCHAR2) AS + user_id "user".user_id%TYPE;; + user_is_missing EXCEPTION;; +BEGIN + SELECT user_id INTO user_id FROM "user" WHERE user_name = name;; + INSERT INTO user_groups (ug_user, ug_group) VALUES(user_id, new_right);; +EXCEPTION + WHEN NO_DATA_FOUND THEN + DBMS_OUTPUT.PUT_LINE('The specified user does not exist.');; +END add_user_right;; +; + +CREATE OR REPLACE PROCEDURE add_interwiki (prefix VARCHAR2, url VARCHAR2, is_local NUMBER) AS +BEGIN + INSERT INTO interwiki (iw_prefix, iw_url, iw_local) VALUES(prefix, url, is_local);; +END add_interwiki;; +; \ No newline at end of file diff --git a/maintenance/update.php b/maintenance/update.php index 41d9e9ca1a..ccb21bcc47 100644 --- a/maintenance/update.php +++ b/maintenance/update.php @@ -5,21 +5,24 @@ * @todo document * @package MediaWiki * @subpackage Maintenance - */ + */ /** */ $options = array( 'quick' ); require_once( "commandLine.inc" ); require_once( "updaters.inc" ); $wgTitle = Title::newFromText( "MediaWiki database updater" ); -$wgDatabase = Database::newFromParams( $wgDBserver, $wgDBadminuser, $wgDBadminpassword, $wgDBname ); +$dbclass = 'Database'.ucfirst($wgDBtype); +require_once("$dbclass.php"); +$dbc = new $dbclass; +$wgDatabase = $dbc->newFromParams( $wgDBserver, $wgDBadminuser, $wgDBadminpassword, $wgDBname ); print "Going to run database updates for $wgDBname\n"; print "Depending on the size of your database this may take a while!\n"; if( !isset( $options['quick'] ) ) { print "Abort with control-c in the next five seconds... "; - + for ($i = 5; $i >= 0; --$i) { echo $i; sleep(1); diff --git a/maintenance/updaters.inc b/maintenance/updaters.inc index c2034ce9a9..2676b354ad 100644 --- a/maintenance/updaters.inc +++ b/maintenance/updaters.inc @@ -3,7 +3,7 @@ * @package MediaWiki * @subpackage Maintenance */ - + /** */ require_once 'convertLinks.inc'; @@ -59,7 +59,7 @@ function rename_table( $from, $to, $patch ) { echo "...can't move table $from to $to, $to already exists.\n"; } else { echo "Moving table $from to $to..."; - dbsource( "maintenance/archives/$patch", $wgDatabase ); + dbsource( archive($patch), $wgDatabase ); echo "ok\n"; } } else { @@ -75,7 +75,7 @@ function add_table( $name, $patch ) { echo "...$name table already exists.\n"; } else { echo "Creating $name table..."; - dbsource( "maintenance/archives/$patch", $wgDatabase ); + dbsource( archive($patch), $wgDatabase ); echo "ok\n"; } } @@ -88,7 +88,7 @@ function add_field( $table, $field, $patch ) { echo "...have $field field in $table table.\n"; } else { echo "Adding $field field to table $table..."; - dbsource( "maintenance/archives/$patch" , $wgDatabase ); + dbsource( archive($patch) , $wgDatabase ); echo "ok\n"; } } @@ -102,7 +102,7 @@ function do_revision_updates() { function update_passwords() { wfDebugDieBacktrace( "This function needs to be updated or removed.\n" ); - + global $wgDatabase; $fname = "Update script: update_passwords()"; print "\nIt appears that you need to update the user passwords in your\n" . @@ -136,7 +136,7 @@ function do_interwiki_update() { return true; } echo "Creating interwiki table: "; - dbsource( "maintenance/archives/patch-interwiki.sql" ); + dbsource( archive("patch-interwiki.sql") ); echo "ok\n"; echo "Adding default interwiki definitions: "; dbsource( "maintenance/interwiki.sql" ); @@ -149,7 +149,7 @@ function do_index_update() { $meta = $wgDatabase->fieldInfo( "recentchanges", "rc_timestamp" ); if( $meta->multiple_key == 0 ) { echo "Updating indexes to 20031107: "; - dbsource( "maintenance/archives/patch-indexes.sql" ); + dbsource( archive("patch-indexes.sql") ); echo "ok\n"; return true; } @@ -163,7 +163,7 @@ function do_image_name_unique_update() { echo "...image primary key already set.\n"; } else { echo "Making img_name the primary key... "; - dbsource( "maintenance/archives/patch-image_name_primary.sql", $wgDatabase ); + dbsource( archive("patch-image_name_primary.sql"), $wgDatabase ); echo "ok\n"; } } @@ -235,12 +235,14 @@ function do_user_update() { * which causes a collation mismatch error on joins in MySQL 4.1. */ function do_logging_encoding() { - global $wgDatabase; + global $wgDatabase, $wgDBtype; + if ($wgDBtype != 'mysql') + return; $logging = $wgDatabase->tableName( 'logging' ); $res = $wgDatabase->query( "SELECT log_title FROM $logging LIMIT 0" ); $flags = explode( ' ', mysql_field_flags( $res, 0 ) ); $wgDatabase->freeResult( $res ); - + if( in_array( 'binary', $flags ) ) { echo "Logging table has correct title encoding.\n"; } else { @@ -259,7 +261,7 @@ function do_schema_restructuring() { echo "...converting from cur/old to page/revision/text DB structure.\n"; flush(); echo wfTimestamp(); echo "......checking for duplicate entries.\n"; flush(); - + extract( $wgDatabase->tableNames( 'cur', 'old', 'page', 'revision', 'text' ) ); $rows = $wgDatabase->query( "SELECT cur_title, cur_namespace, COUNT(cur_namespace) AS c @@ -277,7 +279,7 @@ function do_schema_restructuring() { echo ( sprintf( " %-60s %3s %5s\n", $row->cur_title, $row->cur_namespace, $row->c ) ); } $sql = "SELECT cur_title, cur_namespace, cur_id, cur_timestamp FROM $cur WHERE "; - $firstCond = true; + $firstCond = true; foreach ( $duplicate as $ns => $titles ) { if ( $firstCond ) { $firstCond = false; @@ -317,7 +319,7 @@ function do_schema_restructuring() { echo wfTimestamp(); echo "......Deleted ".$wgDatabase->affectedRows()." records.\n"; } - + echo wfTimestamp(); echo "......Creating tables.\n"; @@ -348,7 +350,7 @@ function do_schema_restructuring() { rev_timestamp char(14) binary NOT NULL default '', rev_minor_edit tinyint(1) unsigned NOT NULL default '0', rev_deleted tinyint(1) unsigned NOT NULL default '0', - + PRIMARY KEY rev_page_id (rev_page, rev_id), UNIQUE INDEX rev_id (rev_id), INDEX rev_timestamp (rev_timestamp), @@ -409,7 +411,7 @@ function do_schema_restructuring() { echo wfTimestamp(); echo "......Renaming old.\n"; $wgDatabase->query( "ALTER TABLE $old RENAME TO $text", $fname ); - + echo wfTimestamp(); echo "...done.\n"; } @@ -454,22 +456,24 @@ function do_namespace_size() { } function do_namespace_size_on( $table, $prefix ) { - global $wgDatabase; + global $wgDatabase, $wgDBtype; + if ($wgDBtype != 'mysql') + return; $field = $prefix . '_namespace'; - + $tablename = $wgDatabase->tableName( $table ); $result = $wgDatabase->query( "SHOW COLUMNS FROM $tablename LIKE '$field'" ); $info = $wgDatabase->fetchObject( $result ); $wgDatabase->freeResult( $result ); - + if( substr( $info->Type, 0, 3 ) == 'int' ) { echo "...$field is already a full int ($info->Type).\n"; } else { echo "Promoting $field from $info->Type to int... "; - + $sql = "ALTER TABLE $tablename MODIFY $field int NOT NULL"; $wgDatabase->query( $sql ); - + echo "ok\n"; } } @@ -483,7 +487,7 @@ function do_pagelinks_update() { dbsource( "maintenance/archives/patch-pagelinks.sql", $wgDatabase ); echo "ok\n"; flush(); - + global $wgCanonicalNamespaceNames; foreach( $wgCanonicalNamespaceNames as $ns => $name ) { if( $ns != 0 ) { @@ -495,28 +499,28 @@ function do_pagelinks_update() { function do_pagelinks_namespace( $namespace ) { global $wgDatabase, $wgContLang; - + $ns = IntVal( $namespace ); echo "Cleaning up broken links for namespace $ns... "; - + $pagelinks = $wgDatabase->tableName( 'pagelinks' ); $name = $wgContLang->getNsText( $ns ); $prefix = $wgDatabase->strencode( $name ); $likeprefix = str_replace( '_', '\\_', $prefix); - + $sql = "UPDATE $pagelinks SET pl_namespace=$ns, pl_title=TRIM(LEADING '$prefix:' FROM pl_title) WHERE pl_namespace=0 AND pl_title LIKE '$likeprefix:%'"; - + $wgDatabase->query( $sql, 'do_pagelinks_namespace' ); echo "ok\n"; } function do_drop_img_type() { global $wgDatabase; - + if( $wgDatabase->fieldExists( 'image', 'img_type' ) ) { echo "Dropping unused img_type field in image table... "; dbsource( "maintenance/archives/patch-drop_img_type.sql", $wgDatabase ); @@ -553,16 +557,16 @@ function do_user_unique_update() { function do_user_groups_update() { $fname = 'do_user_groups_update'; global $wgDatabase; - + if( $wgDatabase->tableExists( 'user_groups' ) ) { echo "...user_groups table already exists.\n"; return do_user_groups_reformat(); } - + echo "Adding user_groups table... "; dbsource( 'maintenance/archives/patch-user_groups.sql', $wgDatabase ); echo "ok\n"; - + if( !$wgDatabase->tableExists( 'user_rights' ) ) { if( $wgDatabase->fieldExists( 'user', 'user_rights' ) ) { echo "Upgrading from a 1.3 or older database? Breaking out user_rights for conversion..."; @@ -575,18 +579,18 @@ function do_user_groups_update() { return; } } - + echo "Converting user_rights table to user_groups... "; $result = $wgDatabase->select( 'user_rights', array( 'ur_user', 'ur_rights' ), array( "ur_rights != ''" ), $fname ); - + while( $row = $wgDatabase->fetchObject( $result ) ) { $groups = array_unique( array_map( 'trim', explode( ',', $row->ur_rights ) ) ); - + foreach( $groups as $group ) { $wgDatabase->insert( 'user_groups', array( @@ -603,18 +607,18 @@ function do_user_groups_reformat() { # Check for bogus formats from previous 1.5 alpha code. global $wgDatabase; $info = $wgDatabase->fieldInfo( 'user_groups', 'ug_group' ); - + if( $info->type == 'int' ) { $oldug = $wgDatabase->tableName( 'user_groups' ); $newug = $wgDatabase->tableName( 'user_groups_bogus' ); echo "user_groups is in bogus intermediate format. Renaming to $newug... "; $wgDatabase->query( "ALTER TABLE $oldug RENAME TO $newug" ); echo "ok\n"; - + echo "Re-adding fresh user_groups table... "; dbsource( 'maintenance/archives/patch-user_groups.sql', $wgDatabase ); echo "ok\n"; - + echo "***\n"; echo "*** WARNING: You will need to manually fix up user permissions in the user_groups\n"; echo "*** table. Old 1.5 alpha versions did some pretty funky stuff...\n"; @@ -622,12 +626,12 @@ function do_user_groups_reformat() { } else { echo "...user_groups is in current format.\n"; } - + } function do_all_updates() { global $wgNewTables, $wgNewFields, $wgRenamedTables; - + # Rename tables foreach ( $wgRenamedTables as $tableRecord ) { rename_table( $tableRecord[0], $tableRecord[1], $tableRecord[2] ); @@ -644,7 +648,7 @@ function do_all_updates() { add_field( $fieldRecord[0], $fieldRecord[1], $fieldRecord[2] ); flush(); } - + # Do schema updates which require special handling do_interwiki_update(); flush(); do_index_update(); flush(); @@ -654,20 +658,29 @@ function do_all_updates() { do_user_update(); flush(); ###### do_copy_newtalk_to_watchlist(); flush(); do_logging_encoding(); flush(); - + do_schema_restructuring(); flush(); do_inverse_timestamp(); flush(); do_text_id(); flush(); do_namespace_size(); flush(); - + do_pagelinks_update(); flush(); - + do_drop_img_type(); flush(); - + do_user_unique_update(); flush(); do_user_groups_update(); flush(); - + initialiseMessages(); flush(); } +function archive($name) { + global $wgDBtype; + switch ($wgDBtype) { + case "oracle": + return "maintenance/oracle/archives/$name"; + default: + return "maintenance/archives/$name"; + } +} ?> -- 2.20.1