Enforce lagged-slave read-only mode on the DB layer
[lhc/web/wiklou.git] / includes / db / Database.php
index 1e54f55..2c2d37f 100644 (file)
@@ -124,9 +124,9 @@ abstract class DatabaseBase implements IDatabase {
        /**
         * Array of levels of atomicity within transactions
         *
-        * @var SplStack
+        * @var array
         */
-       private $mTrxAtomicLevels;
+       private $mTrxAtomicLevels = array();
 
        /**
         * Record if the current transaction was started implicitly by DatabaseBase::startAtomic
@@ -322,24 +322,6 @@ abstract class DatabaseBase implements IDatabase {
                }
        }
 
-       /**
-        * Set lag time in seconds for a fake slave
-        *
-        * @param mixed $lag Valid values for this parameter are determined by the
-        *   subclass, but should be a PHP scalar or array that would be sensible
-        *   as part of $wgLBFactoryConf.
-        */
-       public function setFakeSlaveLag( $lag ) {
-       }
-
-       /**
-        * Make this connection a fake master
-        *
-        * @param bool $enabled
-        */
-       public function setFakeMaster( $enabled = true ) {
-       }
-
        /**
         * @return TransactionProfiler
         */
@@ -627,8 +609,6 @@ abstract class DatabaseBase implements IDatabase {
        function __construct( array $params ) {
                global $wgDBprefix, $wgDBmwschema, $wgCommandLineMode, $wgDebugDBTransactions;
 
-               $this->mTrxAtomicLevels = new SplStack;
-
                $server = $params['host'];
                $user = $params['user'];
                $password = $params['password'];
@@ -952,9 +932,9 @@ abstract class DatabaseBase implements IDatabase {
 
                $isWriteQuery = $this->isWriteQuery( $sql );
                if ( $isWriteQuery ) {
-                       if ( !$this->mDoneWrites ) {
-                               wfDebug( __METHOD__ . ': Writes done: ' .
-                                       DatabaseBase::generalizeSQL( $sql ) . "\n" );
+                       $reason = $this->getLBInfo( 'readOnlyReason' );
+                       if ( is_string( $reason ) ) {
+                               throw new DBReadOnlyError( $this, "Database is read-only: $reason" );
                        }
                        # Set a flag indicating that writes have been done
                        $this->mDoneWrites = microtime( true );
@@ -1079,8 +1059,8 @@ abstract class DatabaseBase implements IDatabase {
                $res = $this->resultObject( $ret );
 
                // Destroy profile sections in the opposite order to their creation
-               $queryProfSection = false;
-               $totalProfSection = false;
+               ScopedCallback::consume( $queryProfSection );
+               ScopedCallback::consume( $totalProfSection );
 
                if ( $isWriteQuery && $this->mTrxLevel ) {
                        $this->mTrxWriteDuration += $queryRuntime;
@@ -1250,6 +1230,7 @@ abstract class DatabaseBase implements IDatabase {
         * @param string|array $options The query options. See DatabaseBase::select() for details.
         *
         * @return bool|mixed The value from the field, or false on failure.
+        * @throws DBUnexpectedError
         */
        public function selectField(
                $table, $var, $cond = '', $fname = __METHOD__, $options = array()
@@ -1347,9 +1328,9 @@ abstract class DatabaseBase implements IDatabase {
                $preLimitTail .= $this->makeOrderBy( $options );
 
                // if (isset($options['LIMIT'])) {
-               //      $tailOpts .= $this->limitResult('', $options['LIMIT'],
-               //              isset($options['OFFSET']) ? $options['OFFSET']
-               //              : false);
+               //      $tailOpts .= $this->limitResult('', $options['LIMIT'],
+               //              isset($options['OFFSET']) ? $options['OFFSET']
+               //              : false);
                // }
 
                if ( isset( $noKeyOptions['FOR UPDATE'] ) ) {
@@ -2437,7 +2418,7 @@ abstract class DatabaseBase implements IDatabase {
                if ( !$alias || (string)$alias === (string)$name ) {
                        return $name;
                } else {
-                       return $name . ' AS ' . $alias; //PostgreSQL needs AS
+                       return $name . ' AS ' . $alias; // PostgreSQL needs AS
                }
        }
 
@@ -3211,11 +3192,6 @@ abstract class DatabaseBase implements IDatabase {
                $args = func_get_args();
                $function = array_shift( $args );
                $tries = self::DEADLOCK_TRIES;
-               if ( is_array( $function ) ) {
-                       $fname = $function[0];
-               } else {
-                       $fname = $function;
-               }
 
                $this->begin( __METHOD__ );
 
@@ -3417,7 +3393,7 @@ abstract class DatabaseBase implements IDatabase {
                        }
                }
 
-               $this->mTrxAtomicLevels->push( $fname );
+               $this->mTrxAtomicLevels[] = $fname;
        }
 
        /**
@@ -3435,13 +3411,13 @@ abstract class DatabaseBase implements IDatabase {
                if ( !$this->mTrxLevel ) {
                        throw new DBUnexpectedError( $this, 'No atomic transaction is open.' );
                }
-               if ( $this->mTrxAtomicLevels->isEmpty() ||
-                       $this->mTrxAtomicLevels->pop() !== $fname
+               if ( !$this->mTrxAtomicLevels ||
+                       array_pop( $this->mTrxAtomicLevels ) !== $fname
                ) {
                        throw new DBUnexpectedError( $this, 'Invalid atomic section ended.' );
                }
 
-               if ( $this->mTrxAtomicLevels->isEmpty() && $this->mTrxAutomaticAtomic ) {
+               if ( !$this->mTrxAtomicLevels && $this->mTrxAutomaticAtomic ) {
                        $this->commit( $fname, 'flush' );
                }
        }
@@ -3465,7 +3441,7 @@ abstract class DatabaseBase implements IDatabase {
                global $wgDebugDBTransactions;
 
                if ( $this->mTrxLevel ) { // implicit commit
-                       if ( !$this->mTrxAtomicLevels->isEmpty() ) {
+                       if ( $this->mTrxAtomicLevels ) {
                                // If the current transaction was an automatic atomic one, then we definitely have
                                // a problem. Same if there is any unclosed atomic level.
                                throw new DBUnexpectedError( $this,
@@ -3513,7 +3489,7 @@ abstract class DatabaseBase implements IDatabase {
                $this->mTrxDoneWrites = false;
                $this->mTrxAutomatic = false;
                $this->mTrxAutomaticAtomic = false;
-               $this->mTrxAtomicLevels = new SplStack;
+               $this->mTrxAtomicLevels = array();
                $this->mTrxIdleCallbacks = array();
                $this->mTrxPreCommitCallbacks = array();
                $this->mTrxShortId = wfRandomString( 12 );
@@ -3546,7 +3522,7 @@ abstract class DatabaseBase implements IDatabase {
         * @throws DBUnexpectedError
         */
        final public function commit( $fname = __METHOD__, $flush = '' ) {
-               if ( !$this->mTrxAtomicLevels->isEmpty() ) {
+               if ( $this->mTrxLevel && $this->mTrxAtomicLevels ) {
                        // There are still atomic sections open. This cannot be ignored
                        throw new DBUnexpectedError(
                                $this,
@@ -3632,7 +3608,7 @@ abstract class DatabaseBase implements IDatabase {
                $this->doRollback( $fname );
                $this->mTrxIdleCallbacks = array(); // cancel
                $this->mTrxPreCommitCallbacks = array(); // cancel
-               $this->mTrxAtomicLevels = new SplStack;
+               $this->mTrxAtomicLevels = array();
                if ( $this->mTrxDoneWrites ) {
                        $this->getTransactionProfiler()->transactionWritingOut(
                                $this->mServer, $this->mDBname, $this->mTrxShortId );
@@ -3766,14 +3742,13 @@ abstract class DatabaseBase implements IDatabase {
         * Once upon a time, DatabaseBase::query() returned a bare MySQL result
         * resource, and it was necessary to call this function to convert it to
         * a wrapper. Nowadays, raw database objects are never exposed to external
-        * callers, so this is unnecessary in external code. For compatibility with
-        * old code, ResultWrapper objects are passed through unaltered.
+        * callers, so this is unnecessary in external code.
         *
-        * @param bool|ResultWrapper|resource $result
+        * @param bool|ResultWrapper|resource|object $result
         * @return bool|ResultWrapper
         */
-       public function resultObject( $result ) {
-               if ( empty( $result ) ) {
+       protected function resultObject( $result ) {
+               if ( !$result ) {
                        return false;
                } elseif ( $result instanceof ResultWrapper ) {
                        return $result;