* Introducing bit field for database parameters
authorTim Starling <tstarling@users.mediawiki.org>
Sat, 24 Jul 2004 07:24:04 +0000 (07:24 +0000)
committerTim Starling <tstarling@users.mediawiki.org>
Sat, 24 Jul 2004 07:24:04 +0000 (07:24 +0000)
** Database constructor calling sequence, and Database::newFromParams()
** Including flags in the server initialisation structs
** Support for setting appropriate flags from legacy globals in Setup.php
** Moved some defines to Define.php so that they can be used in LocalSettings.php, most importantly the bit field constants

* Changes related to post-parse link colouring
** Turn the link cache back on when using it for updating the links table
** No longer need to call preFill() on page view

* Better synchronisation of slave servers
** Rearranged getConnection()
** System for beginning and committing transactions when multiple connections are open
** wfAbruptExit() commits transactions, wfErrorExit() does not. Various functions changed to use wfErrorAbort()

* Allowed reporting of database errors during deferred updates by moving them above output()

14 files changed:
includes/Article.php
includes/Database.php
includes/DatabasePostgreSQL.php
includes/DefaultSettings.php
includes/GlobalFunctions.php
includes/LinksUpdate.php
includes/LoadBalancer.php
includes/Namespace.php
includes/OutputPage.php
includes/Setup.php
includes/SpecialPage.php
index.php
maintenance/refreshLinks.inc
update.php

index bfe325a..c42f349 100644 (file)
@@ -153,7 +153,7 @@ class Article {
                                                        # Try to open, return false on failure
                                                        $params = $wgKnownDBServers[$machineId];
                                                        $db = Database::newFromParams( $params['server'], $params['user'], $params['password'],
-                                                               $dbName, 1, false, true, true );
+                                                               $dbName, 1, DBO_IGNORE );
                                                }
                                        }
                                        if ( $db->isOpen() ) {
@@ -711,8 +711,6 @@ class Article {
                                $pcache = false;
                        }
 
-                       $wgLinkCache->preFill( $this->mTitle );
-
                        # wrap user css and user js in pre and don't parse
                        # XXX: use $this->mTitle->usCssJsSubpage() when php is fixed/ a workaround is found
                        if (
@@ -743,7 +741,7 @@ class Article {
 
        /* private */ function insertNewArticle( $text, $summary, $isminor, $watchthis )
        {
-               global $wgOut, $wgUser, $wgLinkCache, $wgMwRedir;
+               global $wgOut, $wgUser, $wgMwRedir;
                global $wgUseSquid, $wgDeferredUpdateList, $wgInternalServer;
 
                $fname = 'Article::insertNewArticle';
@@ -888,7 +886,7 @@ class Article {
 
        function updateArticle( $text, $summary, $minor, $watchthis, $forceBot = false, $sectionanchor = '' )
        {
-               global $wgOut, $wgUser, $wgLinkCache;
+               global $wgOut, $wgUser;
                global $wgDBtransactions, $wgMwRedir;
                global $wgUseSquid, $wgInternalServer;
                
@@ -1036,7 +1034,11 @@ class Article {
                # Get old version of link table to allow incremental link updates
                $wgLinkCache->preFill( $this->mTitle );
                $wgLinkCache->clear();
-
+               
+               # Switch on use of link cache in the skin
+               $sk =& $wgUser->getSkin();
+               $sk->postParseLinkColour( false );
+               
                # Now update the link cache by parsing the text
                $wgOut = new OutputPage();
                $wgOut->addWikiText( $text );
@@ -1509,7 +1511,7 @@ class Article {
                        $linkID = $titleObj->getArticleID();
                        $brokenLinks[] = array( 'bl_from' => $linkID, 'bl_to' => $t );
                }
-               $dbw->insertArray( 'brokenlinks', $brokenLinks, $fname );
+               $dbw->insert( 'brokenlinks', $brokenLinks, $fname, 'IGNORE' );
                
                # Delete live links
                $dbw->delete( 'links', array( 'l_to' => $id ) );
index 2771995..b76b2f2 100644 (file)
@@ -22,43 +22,61 @@ class Database {
 # Variables
 #------------------------------------------------------------------------------        
        /* private */ var $mLastQuery = "";
-       /* private */ var $mBufferResults = true;
-       /* private */ var $mIgnoreErrors = false;
        
        /* private */ var $mServer, $mUser, $mPassword, $mConn, $mDBname;
-       /* private */ var $mOut, $mDebug, $mOpened = false;
+       /* private */ var $mOut, $mOpened = false;
        
        /* private */ var $mFailFunction; 
        /* private */ var $mTablePrefix;
+       /* private */ var $mFlags;
+       /* private */ var $mTrxLevel = 0;
 
 #------------------------------------------------------------------------------
 # Accessors
 #------------------------------------------------------------------------------
-       # Set functions
-       # These set a variable and return the previous state
+       # 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 setFailFunction( $function ) { 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 setDebug( $debug ) { return wfSetVar( $this->mDebug, $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. 
-       function setBufferResults( $buffer ) { return wfSetVar( $this->mBufferResults, $buffer ); }
+       function bufferResults( $buffer = NULL ) {
+               if ( is_null( $buffer ) ) {
+                       return !(bool)( $this->mFlags & DBO_NOBUFFER );
+               } else {
+                       return !wfSetBit( $this->mFlags, DBO_NOBUFFER, !$buffer ); 
+               }
+       }
 
        # 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
        # database errors. Default is on (false). When turned off, the
        # code should use wfLastErrno() and wfLastError() to handle the
        # situation as appropriate.
-       function setIgnoreErrors( $ignoreErrors ) { return wfSetVar( $this->mIgnoreErrors, $ignoreErrors ); }
+       function ignoreErrors( $ignoreErrors = NULL ) { 
+               return wfSetBit( $this->mFlags, DBO_IGNORE, $ignoreErrors ); 
+       }
        
+       # The current depth of nested transactions
+       function trxLevel( $level = NULL ) {
+               return wfSetVar( $this->mTrxLevel, $level );
+       }
+
        # Get functions
        
        function lastQuery() { return $this->mLastQuery; }
@@ -69,10 +87,9 @@ class Database {
 #------------------------------------------------------------------------------
 
        function Database( $server = false, $user = false, $password = false, $dbName = false, 
-               $failFunction = false, $debug = false, $bufferResults = true, $ignoreErrors = false,
-               $tablePrefix = 'get from global' )
+               $failFunction = false, $flags = 0, $tablePrefix = 'get from global' )
        {
-               global $wgOut, $wgDBprefix;
+               global $wgOut, $wgDBprefix, $wgCommandLineMode;
                # Can't get a reference if it hasn't been set yet
                if ( !isset( $wgOut ) ) {
                        $wgOut = NULL;
@@ -80,9 +97,16 @@ class Database {
                $this->mOut =& $wgOut;
 
                $this->mFailFunction = $failFunction;
-               $this->mIgnoreErrors = $ignoreErrors;
-               $this->mDebug = $debug;
-               $this->mBufferResults = $bufferResults;
+               $this->mFlags = $flags;
+               
+               if ( $this->mFlags & DBO_DEFAULT ) {
+                       if ( $wgCommandLineMode ) {
+                               $this->mFlags &= ~DBO_TRX;
+                       } else {
+                               $this->mFlags |= DBO_TRX;
+                       }
+               }
+
                if ( $tablePrefix == 'get from global' ) {
                        $this->mTablePrefix = $wgDBprefix;
                } else {
@@ -95,10 +119,9 @@ class Database {
        }
        
        /* static */ function newFromParams( $server, $user, $password, $dbName, 
-               $failFunction = false, $debug = false, $bufferResults = true, $ignoreErrors = false )
+               $failFunction = false, $flags = 0 )
        {
-               return new Database( $server, $user, $password, $dbName, $failFunction, $debug, 
-                 $bufferResults, $ignoreErrors );
+               return new Database( $server, $user, $password, $dbName, $failFunction, $flags );
        }
        
        # Usually aborts on failure
@@ -146,11 +169,15 @@ class Database {
        }
        
        # Closes a database connection, if it is open
+       # Commits any open transactions
        # Returns success, true if already closed
        function close()
        {
                $this->mOpened = false;
                if ( $this->mConn ) {
+                       if ( $this->trxLevel() ) {
+                               $this->immediateCommit();
+                       }
                        return mysql_close( $this->mConn );
                } else {
                        return true;
@@ -184,7 +211,7 @@ class Database {
                
                $this->mLastQuery = $sql;
                
-               if ( $this->mDebug ) {
+               if ( $this->debug() ) {
                        $sqlx = substr( $sql, 0, 500 );
                        $sqlx = wordwrap(strtr($sqlx,"\t\n","  "));
                        wfDebug( "SQL: $sqlx\n" );
@@ -196,26 +223,37 @@ class Database {
                        $commentedSql = $sql;
                }
                
-               if( $this->mBufferResults ) {
-                       $ret = mysql_query( $commentedSql, $this->mConn );
-               } else {
-                       $ret = mysql_unbuffered_query( $commentedSql, $this->mConn );
+               # If DBO_TRX is set, start a transaction
+               if ( ( $this->mFlags & DBO_TRX ) && !$this->trxLevel() && $sql != 'BEGIN' ) {
+                       $this->begin();
                }
-       
+               
+               # Do the query and handle errors
+               $ret = $this->doQuery( $commentedSql );
                if ( false === $ret ) {
-                       $this->reportQueryError( mysql_error(), mysql_errno(), $sql, $fname, $tempIgnore );
+                       $this->reportQueryError( $this->lastError(), $this->lastErrno(), $sql, $fname, $tempIgnore );
                }
-               
+                               
                if ( $wgProfiling ) {
                        wfProfileOut( $profName );
                }
                return $ret;
        }
        
+       # The DBMS-dependent part of query()
+       function doQuery( $sql ) {
+               if( $this->bufferResults() ) {
+                       $ret = mysql_query( $sql, $this->mConn );
+               } else {
+                       $ret = mysql_unbuffered_query( $sql, $this->mConn );
+               }       
+               return $ret;
+       }
+
        function reportQueryError( $error, $errno, $sql, $fname, $tempIgnore = false ) {
                global $wgCommandLineMode, $wgFullyInitialised;
                # Ignore errors during error handling to avoid infinite recursion
-               $ignore = $this->setIgnoreErrors( true );
+               $ignore = $this->ignoreErrors( true );
 
                if( $ignore || $tempIgnore ) {
                        wfDebug("SQL ERROR (ignored): " . $error . "\n");
@@ -237,7 +275,7 @@ class Database {
                                $this->mOut->databaseError( $fname, $sql, $error, $errno );                             
                        }
                }
-               $this->setIgnoreErrors( $ignore );
+               $this->ignoreErrors( $ignore );
        }
 
        function freeResult( $res ) {
@@ -470,10 +508,9 @@ class Database {
        function tableExists( $table )
        {
                $table = $this->tableName( $table );
-               $old = $this->mIgnoreErrors;
-               $this->mIgnoreErrors = true;
+               $old = $this->ignoreErrors( true );
                $res = $this->query( "SELECT 1 FROM $table LIMIT 1" );
-               $this->mIgnoreErrors = $old;
+               $this->ignoreErrors( $old );
                if( $res ) {
                        $this->freeResult( $res );
                        return true;
@@ -521,6 +558,11 @@ class Database {
        # 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 );
@@ -532,7 +574,7 @@ class Database {
                        $multi = false;
                        $keys = array_keys( $a );
                }
-               
+
                $sql = 'INSERT ' . implode( ' ', $options ) . 
                        " INTO $table (" . implode( ',', $keys ) . ') VALUES ';
 
@@ -784,7 +826,7 @@ class Database {
                $this->query( "BEGIN", $myFname );
                $args = func_get_args();
                $function = array_shift( $args );
-               $oldIgnore = $dbw->setIgnoreErrors( true );
+               $oldIgnore = $dbw->ignoreErrors( true );
                $tries = DEADLOCK_TRIES;
                if ( is_array( $function ) ) {
                        $fname = $function[0];
@@ -806,7 +848,7 @@ class Database {
                                }
                        }
                } while( $dbw->wasDeadlock && --$tries > 0 );
-               $this->setIgnoreErrors( $oldIgnore );
+               $this->ignoreErrors( $oldIgnore );
                if ( $tries <= 0 ) {
                        $this->query( "ROLLBACK", $myFname );
                        $this->reportQueryError( $error, $errno, $sql, $fname );
@@ -851,6 +893,43 @@ class Database {
                        return array( false, false );
                }
        }
+
+       # Begin a transaction, or if a transaction has already started, continue it
+       function begin( $fname = 'Database::begin' ) {
+               if ( !$this->mTrxLevel ) {
+                       $this->immediateBegin( $fname );
+               } else {
+                       $this->mTrxLevel++;
+               }
+       }
+
+       # End a transaction, or decrement the nest level if transactions are nested
+       function commit( $fname = 'Database::commit' ) {
+               if ( $this->mTrxLevel ) {
+                       $this->mTrxLevel--;
+               }
+               if ( !$this->mTrxLevel ) {
+                       $this->immediateCommit( $fname );
+               }
+       }
+
+       # Rollback a transaction
+       function rollback( $fname = 'Database::rollback' ) {
+               $this->query( 'ROLLBACK', $fname );
+               $this->mTrxLevel = 0;
+       }
+
+       # Begin a transaction, committing any previously open transaction
+       function immediateBegin( $fname = 'Database::immediateBegin' ) {
+               $this->query( 'BEGIN', $fname );
+               $this->mTrxLevel = 1;
+       }
+       
+       # Commit transaction, if one is open
+       function immediateCommit( $fname = 'Database::immediateCommit' ) {
+               $this->query( 'COMMIT', $fname );
+               $this->mTrxLevel = 0;
+       }
 } 
 
 class DatabaseMysql extends Database {
@@ -887,7 +966,7 @@ function wfEmergencyAbort( &$conn, $error ) {
                                $search = $_REQUEST['search'];
                                echo wfMsgNoDB( "searchdisabled" );
                                echo wfMsgNoDB( "googlesearch", htmlspecialchars( $search ), $wgInputEncoding );
-                               wfAbruptExit();
+                               wfErrorExit();
                        } else {
                                $t = Title::newFromText( wfMsgNoDB( "mainpage" ) );
                        }
@@ -907,7 +986,7 @@ function wfEmergencyAbort( &$conn, $error ) {
        }
        
        echo $text;
-       wfAbruptExit();
+       wfErrorExit();
 }
 
 function wfLimitResult( $limit, $offset ) {
index 06a79aa..36b622c 100644 (file)
@@ -18,17 +18,15 @@ class DatabasePgsql extends Database {
        var $mInsertId = NULL;
 
        function DatabasePgsql($server = false, $user = false, $password = false, $dbName = false, 
-               $failFunction = false, $debug = false, $bufferResults = true, $ignoreErrors = false)
+               $failFunction = false, $flags = 0, $tablePrefix = 'get from global' )
        {
-               Database::Database( $server, $user, $password, $dbName, $failFunction, $debug, 
-                 $bufferResults, $ignoreErrors );
+               Database::Database( $server, $user, $password, $dbName, $failFunction, $flags, $tablePrefix );
        }
 
-       /* static */ function newFromParams( $server, $user, $password, $dbName, 
-               $failFunction = false, $debug = false, $bufferResults = true, $ignoreErrors = false )
+       /* static */ function newFromParams( $server = false, $user = false, $password = false, $dbName = false, 
+               $failFunction = false, $flags = 0, $tablePrefix = 'get from global' )
        {
-               return new DatabasePgsql( $server, $user, $password, $dbName, $failFunction, $debug, 
-                 $bufferResults, $ignoreErrors );
+               return new DatabasePgsql( $server, $user, $password, $dbName, $failFunction, $flags, $tablePrefix );
        }
 
        # Usually aborts on failure
@@ -74,53 +72,10 @@ class DatabasePgsql extends Database {
                }
        }
        
-       # Usually aborts on failure
-       # If errors are explicitly ignored, returns success
-       function query( $sql, $fname = "", $tempIgnore = false )
-       {
-               global $wgProfiling;
-               
-               if ( $wgProfiling ) {
-                       # generalizeSQL will probably cut down the query to reasonable
-                       # logging size most of the time. The substr is really just a sanity check.
-                       $profName = "query: " . substr( Database::generalizeSQL( $sql ), 0, 255 ); 
-                       wfProfileIn( $profName );
-               }
-               
-               $this->mLastQuery = $sql;
-               
-               if ( $this->mDebug ) {
-                       $sqlx = substr( $sql, 0, 500 );
-                       $sqlx = wordwrap(strtr($sqlx,"\t\n","  "));
-                       wfDebug( "SQL: $sqlx\n" );
-               }
-
-               $ret = pg_query( $this->mConn , $sql);
-               $this->mLastResult = $ret;
-               if ( false == $ret ) {
-                       // Ignore errors during error handling to prevent infinite recursion
-                       $ignore = $this->setIgnoreErrors( true );
-                       $error = pg_last_error( $this->mConn );
-                       // TODO FIXME : no error number function in postgre
-                       // $errno = mysql_errno( $this->mConn );
-                       if( $ignore || $tempIgnore ) {
-                               wfDebug("SQL ERROR (ignored): " . $error . "\n");
-                       } else {
-                               wfDebug("SQL ERROR: " . $error . "\n");
-                               if ( $this->mOut ) {
-                                       // this calls wfAbruptExit()
-                                       $this->mOut->databaseError( $fname, $sql, $error, 0 );                          
-                               }
-                       }
-                       $this->setIgnoreErrors( $ignore );
-               }
-               
-               if ( $wgProfiling ) {
-                       wfProfileOut( $profName );
-               }
-               return $ret;
+       function doQuery( $sql ) {
+               return pg_query( $this->mConn , $sql);
        }
-       
+               
        function queryIgnore( $sql, $fname = "" ) {
                return $this->query( $sql, $fname, true );
        }
@@ -168,6 +123,8 @@ class DatabasePgsql extends Database {
 
        function dataSeek( $res, $row ) { return pg_result_seek( $res, $row ); }
        function lastError() { return pg_last_error(); }
+       function lastErrno() { return 1; }
+
        function affectedRows() { 
                return pg_affected_rows( $this->mLastResult ); 
        }
@@ -215,14 +172,14 @@ class DatabasePgsql extends Database {
                # IGNORE is performed using single-row inserts, ignoring errors in each
                if ( in_array( 'IGNORE', $options ) ) {
                        # FIXME: need some way to distiguish between key collision and other types of error
-                       $oldIgnore = $this->setIgnoreErrors( true );
+                       $oldIgnore = $this->ignoreErrors( true );
                        if ( !is_array( reset( $a ) ) ) {
                                $a = array( $a );
                        }
                        foreach ( $a as $row ) {
                                parent::insertArray( $table, $row, $fname, array() );
                        }
-                       $this->setIgnoreErrors( $oldIgnore );
+                       $this->ignoreErrors( $oldIgnore );
                        $retVal = true;
                } else {
                        $retVal = parent::insertArray( $table, $a, $fname, array() );
index 905518e..1b8d944 100644 (file)
@@ -70,6 +70,13 @@ $wgDBprefix         = ''; # Table name prefix
 #   password:  DB password
 #   type:      "mysql" or "pgsql"
 #   load:      ratio of DB_SLAVE load, must be >=0, the sum of all loads must be >0
+#   flags:     bit field
+#                 DBO_DEFAULT -- turns on DBO_TRX only if !$wgCommandLineMode (recommended)
+#                 DBO_DEBUG -- equivalent of $wgDebugDumpSql
+#                 DBO_TRX -- wrap entire request in a transaction
+#                 DBO_IGNORE -- ignore errors (not useful in LocalSettings.php)
+#                 DBO_NOBUFFER -- turn off buffering (not useful in LocalSettings.php)
+#
 # Leave at false to use the single-server variables above
 $wgDBservers           = false; 
 
@@ -139,6 +146,7 @@ $wgReadOnlyFile         = "{$wgUploadDirectory}/lock_yBgMBwiR";
 # used, as it may contain private data.
 $wgDebugLogFile         = '';
 $wgDebugRedirects              = false;
+$wgDebugRawPage         = false; # Avoid overlapping debug entries by leaving out CSS
 
 $wgDebugComments        = false;
 $wgReadOnly             = false;
@@ -160,7 +168,7 @@ $wgUseCategoryMagic         = true;
 # FIXME: need fixing
 $wgUseCategoryBrowser   = false;
 
-$wgEnablePersistentLC  = false;        # Persistent link cache in linkscc table; FAILS on MySQL 3.x
+$wgEnablePersistentLC  = false;        # Obsolete, do not use
 $wgCompressedPersistentLC = true; # use gzcompressed blobs
 
 $wgEnableParserCache = false; # requires that php was compiled --with-zlib
@@ -445,4 +453,8 @@ $wgAllowPageInfo = false;
 
 # Maximum indent level of toc.
 $wgMaxTocLevel = 999;
+
+# Recognise longitude/latitude coordinates
+$wgUseGeoMode = false;
+
 ?>
index a00ba73..3cf9ebe 100644 (file)
@@ -219,8 +219,13 @@ function wfUtf8ToHTML($string) {
 
 function wfDebug( $text, $logonly = false )
 {
-       global $wgOut, $wgDebugLogFile, $wgDebugComments, $wgProfileOnly;
+       global $wgOut, $wgDebugLogFile, $wgDebugComments, $wgProfileOnly, $wgDebugRawPage;
 
+       # Check for raw action using $_GET not $wgRequest, since the latter might not be initialised yet
+       if ( isset( $_GET['action'] ) && $_GET['action'] == 'raw' && !$wgDebugRawPage ) {
+               return;
+       }
+       
        if ( isset( $wgOut ) && $wgDebugComments && !$logonly ) {
                $wgOut->debug( $text );
        }
@@ -240,7 +245,7 @@ function wfLogDBError( $text ) {
 
 function logProfilingData()
 {
-       global $wgRequestTime, $wgDebugLogFile;
+       global $wgRequestTime, $wgDebugLogFile, $wgDebugRawPage, $wgRequest;
        global $wgProfiling, $wgProfileStack, $wgProfileLimit, $wgUser;
        $now = wfTime();
 
@@ -263,7 +268,7 @@ function logProfilingData()
                $log = sprintf( "%s\t%04.3f\t%s\n",
                  gmdate( 'YmdHis' ), $elapsed,
                  urldecode( $_SERVER['REQUEST_URI'] . $forward ) );
-               if ( '' != $wgDebugLogFile ) {
+               if ( '' != $wgDebugLogFile && ( $wgRequest->getVal('action') != 'raw' || $wgDebugRawPage ) ) {
                        error_log( $log . $prof, 3, $wgDebugLogFile );
                }
        }
@@ -360,7 +365,9 @@ function wfGo( $s )
 }
 
 # Just like exit() but makes a note of it.
-function wfAbruptExit(){
+# Commits open transactions except if the error parameter is set
+function wfAbruptExit( $error = false ){
+       global $wgLoadBalancer;
        static $called = false;
        if ( $called ){
                exit();
@@ -377,9 +384,16 @@ function wfAbruptExit(){
        } else { 
                wfDebug('WARNING: Abrupt exit\n');
        }
+       if ( !$error ) {
+               $wgLoadBalancer->closeAll();
+       }
        exit();
 }
 
+function wfErrorExit() {
+       wfAbruptExit( true );
+}
+
 function wfDebugDieBacktrace( $msg = '' ) {
        global $wgCommandLineMode;
 
@@ -773,11 +787,17 @@ function wfSetVar( &$dest, $source )
        return $temp;
 }
 
-# Sets dest to a reference to source and returns the original dest
-# Pity that doesn't work in PHP
-function &wfSetRef( &$dest, &$source )
-{
-       die( "You can't rebind a variable in the caller's scope" );
+# As for wfSetVar except setting a bit
+function wfSetBit( &$dest, $bit, $state = true ) {
+       $temp = (bool)($dest & $bit );
+       if ( !is_null( $state ) ) {
+               if ( $state ) {
+                       $dest |= $bit;
+               } else {
+                       $dest &= ~$bit;
+               }
+       }
+       return $temp;
 }
 
 # This function takes two arrays as input, and returns a CGI-style string, e.g.
index 31a1f48..50afb97 100644 (file)
@@ -85,6 +85,7 @@ class LinksUpdate {
                                        }
                                        $sql .= $dbw->addQuotes( $badTitle );
                                }
+                               $sql .= ")";
                                $dbw->query( $sql, $fname );
                        }
                } else {
index b52cabb..adefa2a 100644 (file)
@@ -29,7 +29,7 @@ define( "MASTER_WAIT_TIMEOUT", 15 ); # Time to wait for a slave to synchronise
 class LoadBalancer {
        /* private */ var $mServers, $mConnections, $mLoads;
        /* private */ var $mFailFunction;
-       /* private */ var $mForce, $mReadIndex, $mLastConn;
+       /* private */ var $mForce, $mReadIndex, $mLastIndex;
        /* private */ var $mWaitForFile, $mWaitForPos;
 
        function LoadBalancer()
@@ -39,7 +39,7 @@ class LoadBalancer {
                $this->mFailFunction = false;
                $this->mReadIndex = -1;
                $this->mForce = -1;
-               $this->mLastConn = false;
+               $this->mLastIndex = -1;
        }
 
        function newFromParams( $servers, $failFunction = false )
@@ -57,7 +57,7 @@ class LoadBalancer {
                $this->mWriteIndex = -1;
                $this->mForce = -1;
                $this->mConnections = array();
-               $this->mLastConn = false;
+               $this->mLastIndex = 1;
                $this->mLoads = array();
                $this->mWaitForFile = false;
                $this->mWaitForPos = false;
@@ -92,13 +92,14 @@ class LoadBalancer {
                return $i;
        }
 
-       function &getReader()
+       function getReaderIndex()
        {
+               $i = false;
                if ( $this->mForce >= 0 ) {
-                       $conn =& $this->getConnection( $this->mForce );
+                       $i = $this->mForce;
                } else {
                        if ( $this->mReadIndex >= 0 ) {
-                               $conn =& $this->getConnection( $this->mReadIndex );
+                               $i = $this->mReadIndex;
                        } else {
                                # $loads is $this->mLoads except with elements knocked out if they
                                # don't work
@@ -109,6 +110,8 @@ class LoadBalancer {
                                                wfDebug( "Using reader #$i: {$this->mServers[$i]['host']}\n" );
 
                                                $conn =& $this->getConnection( $i );
+                                               $this->mConnections[$i] =& $conn;
+
                                                if ( !$conn->isOpen() ) {
                                                        unset( $loads[$i] );
                                                }
@@ -116,14 +119,12 @@ class LoadBalancer {
                                } while ( $i !== false && !$conn->isOpen() );
                                if ( $conn->isOpen() ) {
                                        $this->mReadIndex = $i;
+                               } else {
+                                       $i = false;
                                }
                        }
                }
-               if ( $conn === false || !$conn->isOpen() ) {
-                       $this->reportConnectionError( $conn );
-                       $conn = false;
-               }
-               return $conn;
+               return $i;
        }
 
        # Set the master wait position
@@ -142,7 +143,10 @@ class LoadBalancer {
                $this->mWaitForPos = $pos;
 
                if ( $this->mReadIndex > 0 ) {
-                       $this->doWait( $this->mReadIndex );
+                       if ( !$this->doWait( $this->mReadIndex ) ) {
+                               # Use master instead
+                               $this->mReadIndex = 0;
+                       }
                } 
        }
 
@@ -156,7 +160,7 @@ class LoadBalancer {
                        list( $file, $pos ) = explode( ' ', $memcPos );
                        # If the saved position is later than the requested position, return now
                        if ( $file == $this->mWaitForFile && $this->mWaitForPos <= $pos ) {
-                               return;
+                               return true;
                        }
                }
 
@@ -166,16 +170,16 @@ class LoadBalancer {
 
                if ( $result == -1 || is_null( $result ) ) {
                        # Timed out waiting for slave, use master instead
-                       # This is not the ideal solution. If there are a large number of slaves, a slow
-                       # replicated write query will cause the master to be swamped with reads. However
-                       # that's a relatively graceful failure mode, so it will do for now.
                        wfDebug( "Timed out waiting for slave #$index pos {$this->mWaitForFile} {$this->mWaitForPos}\n" );
-                       $this->mReadIndex = 0;
+                       $retVal = false;
                } else {
+                       $retVal = true;
                        wfDebug( "Done\n" );
                }
+               return $retVal;
        }               
 
+       # Get a connection by index
        function &getConnection( $i, $fail = false )
        {
                /*
@@ -191,37 +195,55 @@ class LoadBalancer {
                }*/
 
                # Operation-based index
-               # Note, getReader() and getWriter() will re-enter this function
-               if ( $i == DB_SLAVE ) {
-                       $this->mLastConn =& $this->getReader();
+               if ( $i == DB_SLAVE ) { 
+                       # Note: re-entrant
+                       $i = $this->getReaderIndex();
                } elseif ( $i == DB_MASTER ) {
-                       $this->mLastConn =& $this->getWriter();
+                       $i = $this->getWriterIndex();
                } elseif ( $i == DB_LAST ) {
-                       # Just use $this->mLastConn, which should already be set
-                       if ( $this->mLastConn === false ) {
+                       # Just use $this->mLastIndex, which should already be set
+                       $i = $this->mLastIndex;
+                       if ( $i === -1 ) {
                                # Oh dear, not set, best to use the writer for safety
-                               $this->mLastConn =& $this->getWriter();
+                               $i = $this->getWriterIndex();
                        }
-               } else {
-                       # Explicit index
-                       if ( !array_key_exists( $i, $this->mConnections ) || !$this->mConnections[$i]->isOpen() ) {
-                               $this->mConnections[$i] = $this->makeConnection( $this->mServers[$i] );
-                               if ( $i != 0 && $this->mWaitForFile ) {
-                                       $this->doWait( $i );
+               } 
+               # Now we have an explicit index into the servers array
+               if ( !$this->isOpen( $i ) ) {
+                       $this->mConnections[$i] = $this->makeConnection( $this->mServers[$i] );
+                       
+                       if ( $i != 0 && $this->mWaitForFile ) {
+                               if ( !$this->doWait( $i ) ) {
+                                       # Error waiting for this slave, use master instead
+                                       $this->mReadIndex = 0;
+                                       $i = 0;
+                                       if ( !$this->isOpen( 0 ) ) {
+                                               $this->mConnections[0] = $this->makeConnection( $this->mServers[0] );
+                                       }
+                                       wfDebug( "Failed over to {$this->mConnections[0]->mServer}\n" );
                                }
                        }
-                       if ( !$this->mConnections[$i]->isOpen() ) {
-                               wfDebug( "Failed to connect to database $i at {$this->mServers[$i]['host']}\n" );
-                               if ( $fail ) {
-                                       $this->reportConnectionError( $this->mConnections[$i] );
-                               }
-                               $this->mConnections[$i] = false;
+               }
+               if ( !$this->isOpen( $i ) ) {
+                       wfDebug( "Failed to connect to database $i at {$this->mServers[$i]['host']}\n" );
+                       if ( $fail ) {
+                               $this->reportConnectionError( $this->mConnections[$i] );
                        }
-                       $this->mLastConn =& $this->mConnections[$i];
+                       $this->mConnections[$i] = false;
                }
-               return $this->mLastConn;
+               $this->mLastIndex = $i;
+
+               return $this->mConnections[$i];
        }
 
+       /* private */ function isOpen( $index ) {
+               if ( array_key_exists( $index, $this->mConnections ) && $this->mConnections[$index]->isOpen() ) {
+                       return true;
+               } else {
+                       return false;
+               }
+       }
+       
        /* private */ function makeConnection( &$server ) {
                        extract( $server );
                        # Get class for this database type
@@ -231,7 +253,7 @@ class LoadBalancer {
                        }
 
                        # Create object
-                       return new $class( $host, $user, $password, $dbname, 1 );
+                       return new $class( $host, $user, $password, $dbname, 1, $flags );
        }
        
        function reportConnectionError( &$conn )
@@ -247,14 +269,9 @@ class LoadBalancer {
                $conn->reportConnectionError();
        }
        
-       function &getWriter()
+       function getWriterIndex()
        {
-               $c =& $this->getConnection( 0 );
-               if ( $c === false || !$c->isOpen() ) {
-                       $this->reportConnectionError( $c );
-                       $c = false;
-               }
-               return $c;
+               return 0;
        }
 
        function force( $i )
@@ -300,4 +317,21 @@ class LoadBalancer {
                        $this->waitFor( $_SESSION['master_log_file'], $_SESSION['master_pos'] );
                }
        }
+
+       # Close all open connections
+       function closeAll() {
+               foreach( $this->mConnections as $i => $conn ) {
+                       if ( $this->isOpen( $i ) ) {
+                               $conn->close();
+                       }
+               }
+       }
+
+       function commitAll() {
+               foreach( $this->mConnections as $i => $conn ) {
+                       if ( $this->isOpen( $i ) ) {
+                               $conn->immediateCommit();
+                       }
+               }
+       }
 }
index e3fdab5..a72d24a 100644 (file)
@@ -4,29 +4,7 @@
 # "magic" behaviors of them based on index.  The textual
 # names of the namespaces are handled by Language.php.
 
-# Virtual namespaces; these don't appear in the page database:
-define("NS_MEDIA", -2);
-define("NS_SPECIAL", -1);
-
-# Real namespaces:
-define("NS_MAIN", 0);
-define("NS_TALK", 1);
-define("NS_USER", 2);
-define("NS_USER_TALK", 3);
-define("NS_WP", 4);
-define("NS_WIKIPEDIA", 4);
-define("NS_WP_TALK", 5);
-define("NS_WIKIPEDIA_TALK", 5);
-define("NS_IMAGE", 6);
-define("NS_IMAGE_TALK", 7);
-define("NS_MEDIAWIKI", 8);
-define("NS_MEDIAWIKI_TALK", 9);
-define("NS_TEMPLATE", 10);
-define("NS_TEMPLATE_TALK", 11);
-define("NS_HELP", 12);
-define("NS_HELP_TALK", 13);
-define("NS_CATEGORY", 14);
-define("NS_CATEGORY_TALK", 15);
+# Definitions of the NS_ constants are in Defines.php
 
 # These are synonyms for the names given in the language file
 # Users and translators should not change them
index 5f6fc1e..e58974e 100644 (file)
@@ -465,13 +465,14 @@ class OutputPage {
                $this->setRobotpolicy( "noindex,nofollow" );
                $this->setArticleRelated( false );
                $this->enableClientCache( false );
+               $this->mRedirect = "";
 
                $this->mBodytext = "";
                $this->addHTML( "<p>" . wfMsg( $msg ) . "</p>\n" );
                $this->returnToMain( false );
 
                $this->output();
-               wfAbruptExit();
+               wfErrorExit();
        }
 
        function sysopRequired()
@@ -534,6 +535,7 @@ class OutputPage {
                $this->setRobotpolicy( "noindex,nofollow" );
                $this->setArticleRelated( false );
                $this->enableClientCache( false );
+               $this->mRedirect = "";
 
                if ( $wgCommandLineMode ) {
                        $msg = wfMsgNoDB( "dberrortextcl" );
@@ -548,7 +550,7 @@ class OutputPage {
 
                if ( $wgCommandLineMode || !is_object( $wgUser )) {
                        print "$msg\n";
-                       wfAbruptExit();
+                       wfErrorExit();
                }
                $sk = $wgUser->getSkin();
                $shlink = $sk->makeKnownLink( wfMsgNoDB( "searchhelppage" ),
@@ -556,7 +558,7 @@ class OutputPage {
                $msg = str_replace( "$5", $shlink, $msg );
                $this->mBodytext = $msg;
                $this->output();
-               wfAbruptExit();
+               wfErrorExit();
        }
 
        function readOnlyPage( $source = null, $protected = false )
@@ -595,10 +597,11 @@ class OutputPage {
                $this->setRobotpolicy( "noindex,nofollow" );
                $this->setArticleRelated( false );
                $this->enableClientCache( false );
+               $this->mRedirect = "";
 
                $this->mBodytext = $message;
                $this->output();
-               wfAbruptExit();
+               wfErrorExit();
        }
 
        function unexpectedValueError( $name, $val )
index 747d1c3..02ae368 100644 (file)
@@ -72,7 +72,7 @@ global $wgMemc, $wgMagicWords, $wgMwRedir, $wgDebugLogFile;
 global $wgMessageCache, $wgUseMemCached, $wgUseDatabaseMessages;
 global $wgMsgCacheExpiry, $wgCommandLineMode;
 global $wgBlockCache, $wgParserCache, $wgParser, $wgDBConnections;
-global $wgLoadBalancer, $wgDBservers;
+global $wgLoadBalancer, $wgDBservers, $wgDebugDumpSql;
 global $wgDBserver, $wgDBuser, $wgDBpassword, $wgDBname, $wgDBtype;
 global $wgFullyInitialised;
 
@@ -157,7 +157,8 @@ if ( !$wgDBservers ) {
                'password' => $wgDBpassword,
                'dbname' => $wgDBname,
                'type' => $wgDBtype,
-               'load' => 1
+               'load' => 1,
+               'flags' => ($wgDebugDumpSql ? DBO_DEBUG : 0) | DBO_DEFAULT
        ));
 }
 $wgLoadBalancer = LoadBalancer::newFromParams( $wgDBservers );
index dd4d4c2..9cccc3f 100644 (file)
@@ -37,8 +37,8 @@ $wgSpecialPages = array_merge($wgSpecialPages, array (
        "Recentchangeslinked" => new UnlistedSpecialPage( "Recentchangeslinked" ),
        "Movepage"              => new UnlistedSpecialPage( "Movepage" ),
        "Blockme"       => new UnlistedSpecialPage( "Blockme" ),
-       "Geo"   =>      new SpecialPage( "Geo" ),
-       "Validate"      =>      new SpecialPage( "Validate" ),
+       "Geo"   =>      new UnlistedSpecialPage( "Geo" ),
+       "Validate"      =>      new UnlistedSpecialPage( "Validate" ),
        "Booksources"   => new SpecialPage( "Booksources" ),
        "Categories"    => new SpecialPage( "Categories" ),
        "Export"                => new SpecialPage( "Export" ),
@@ -49,7 +49,7 @@ $wgSpecialPages = array_merge($wgSpecialPages, array (
        "Asksql"                => new SpecialPage( "Asksql", "sysop" ),
        "Undelete"              => new SpecialPage( "Undelete", "sysop" ),
        "Makesysop"             => new SpecialPage( "Makesysop", "sysop" ),
-       "Import"                => new SpecialPage( "Import", "sysop" ),
+#      "Import"                => new SpecialPage( "Import", "sysop" ),
        "Lockdb"                => new SpecialPage( "Lockdb", "developer" ),
        "Unlockdb"              => new SpecialPage( "Unlockdb", "developer" )
 ));
index 7680dbb..85403d9 100644 (file)
--- a/index.php
+++ b/index.php
@@ -11,6 +11,7 @@ if(!file_exists("LocalSettings.php")) {
 }
 
 define( "MEDIAWIKI", true );
+require_once( "./includes/Defines.php" );
 require_once( "./LocalSettings.php" );
 require_once( "includes/Setup.php" );
 
@@ -54,8 +55,6 @@ if ( !is_null( $wgTitle ) && !$wgTitle->userCanRead() ) {
        exit;
 }
 
-$db =& wfGetDB( DB_MASTER );
-
 if ( $search = $wgRequest->getText( 'search' ) ) {
        $wgTitle = Title::makeTitle( NS_SPECIAL, "Search" );
        if( $wgRequest->getVal( 'fulltext' ) || !is_null( $wgRequest->getVal( 'offset' ) ) ) {
@@ -97,7 +96,6 @@ if ( $search = $wgRequest->getText( 'search' ) ) {
                $wgArticle = new Article( $wgTitle );
        }
 
-       $db->query("BEGIN");
        switch( $action ) {
                case "view":
                        $wgOut->setSquidMaxage( $wgSquidMaxage );
@@ -168,17 +166,23 @@ if ( $search = $wgRequest->getText( 'search' ) ) {
                default:
                        $wgOut->errorpage( "nosuchaction", "nosuchactiontext" );
        }
-       $db->query("COMMIT");
 }
 
-$wgLoadBalancer->saveMasterPos();
-$wgOut->output();
-
+# Deferred updates aren't really deferred anymore. It's important to report errors to the
+# user, and that means doing this before OutputPage::output(). Note that for page saves, 
+# the client will wait until the script exits anyway before following the redirect.
 foreach ( $wgDeferredUpdateList as $up ) {
-       $db->query("BEGIN");
        $up->doUpdate();
-       $db->query("COMMIT");
 }
+
+$wgLoadBalancer->saveMasterPos();
+
+# Now commit any transactions, so that unreported errors after output() don't roll back the whole thing
+$wgLoadBalancer->commitAll();
+
+$wgOut->output();
+
 logProfilingData();
+$wgLoadBalancer->closeAll();
 wfDebug( "Request ended normally\n" );
 ?>
index 285aba7..5a18a16 100644 (file)
@@ -14,6 +14,10 @@ function refreshLinks( $start ) {
 
        # Don't generate TeX PNGs (lack of a sensible current directory causes errors anyway)
        $wgUser->setOption("math", 3);
+       
+       # Turn on link cache in skin
+       $sk =& $wgUser->getSkin();
+       $sk->postParseLinkColour( false );
 
        for ($id = $start; $id <= $end; $id++) {
                if ( !($id % REPORTING_INTERVAL) ) {
index ce96eab..3bd3363 100644 (file)
@@ -50,8 +50,7 @@ $wgTitle = Title::newFromText( "Update script" );
 #
 print "Checking database for necessary updates...\n";
 
-$wgDatabase = Database::newFromParams( $wgDBserver, $wgDBadminuser, $wgDBadminpassword, $wgDBname, 
-       1, false, true, false);
+$wgDatabase = Database::newFromParams( $wgDBserver, $wgDBadminuser, $wgDBadminpassword, $wgDBname, 1 );
 if ( !$wgDatabase->isOpen() ) {
        print "Unable to connect to database: " . $wgDatabase->lastError() . "\n";
        exit();