Merge "mediawiki.action.view.rightClickEdit: Remove redundanat dom-ready handler"
[lhc/web/wiklou.git] / includes / libs / rdbms / database / Database.php
index c3e36da..523f7cd 100644 (file)
@@ -35,6 +35,7 @@ use BagOStuff;
 use HashBagOStuff;
 use LogicException;
 use InvalidArgumentException;
+use UnexpectedValueException;
 use Exception;
 use RuntimeException;
 
@@ -282,6 +283,8 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                                $this->flags |= self::DBO_TRX;
                        }
                }
+               // Disregard deprecated DBO_IGNORE flag (T189999)
+               $this->flags &= ~self::DBO_IGNORE;
 
                $this->sessionVars = $params['variables'];
 
@@ -537,32 +540,6 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                return $res;
        }
 
-       /**
-        * 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 lastErrno() and lastError() to handle the
-        * situation as appropriate.
-        *
-        * Do not use this function outside of the Database classes.
-        *
-        * @param null|bool $ignoreErrors
-        * @return bool The previous value of the flag.
-        */
-       protected function ignoreErrors( $ignoreErrors = null ) {
-               $res = $this->getFlag( self::DBO_IGNORE );
-               if ( $ignoreErrors !== null ) {
-                       // setFlag()/clearFlag() do not allow DBO_IGNORE changes for sanity
-                       if ( $ignoreErrors ) {
-                               $this->flags |= self::DBO_IGNORE;
-                       } else {
-                               $this->flags &= ~self::DBO_IGNORE;
-                       }
-               }
-
-               return $res;
-       }
-
        public function trxLevel() {
                return $this->trxLevel;
        }
@@ -719,7 +696,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
 
        public function setFlag( $flag, $remember = self::REMEMBER_NOTHING ) {
                if ( ( $flag & self::DBO_IGNORE ) ) {
-                       throw new \UnexpectedValueException( "Modifying DBO_IGNORE is not allowed." );
+                       throw new UnexpectedValueException( "Modifying DBO_IGNORE is not allowed." );
                }
 
                if ( $remember === self::REMEMBER_PRIOR ) {
@@ -730,7 +707,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
 
        public function clearFlag( $flag, $remember = self::REMEMBER_NOTHING ) {
                if ( ( $flag & self::DBO_IGNORE ) ) {
-                       throw new \UnexpectedValueException( "Modifying DBO_IGNORE is not allowed." );
+                       throw new UnexpectedValueException( "Modifying DBO_IGNORE is not allowed." );
                }
 
                if ( $remember === self::REMEMBER_PRIOR ) {
@@ -912,6 +889,10 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
         */
        abstract protected function closeConnection();
 
+       /**
+        * @param string $error Fallback error message, used if none is given by DB
+        * @throws DBConnectionError
+        */
        public function reportConnectionError( $error = 'Unknown error' ) {
                $myError = $this->lastError();
                if ( $myError ) {
@@ -1287,8 +1268,19 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                return false;
        }
 
+       /**
+        * Report a query error. Log the error, and if neither the object ignore
+        * flag nor the $tempIgnore flag is set, throw a DBQueryError.
+        *
+        * @param string $error
+        * @param int $errno
+        * @param string $sql
+        * @param string $fname
+        * @param bool $tempIgnore
+        * @throws DBQueryError
+        */
        public function reportQueryError( $error, $errno, $sql, $fname, $tempIgnore = false ) {
-               if ( $this->ignoreErrors() || $tempIgnore ) {
+               if ( $tempIgnore ) {
                        $this->queryLogger->debug( "SQL ERROR (ignored): $error\n" );
                } else {
                        $sql1line = mb_substr( str_replace( "\n", "\\n", $sql ), 0, 5 * 1024 );
@@ -2518,7 +2510,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                }
 
                try {
-                       $this->startAtomic( $fname );
+                       $this->startAtomic( $fname, self::ATOMIC_CANCELABLE );
                        $affectedRowCount = 0;
                        foreach ( $rows as $row ) {
                                // Delete rows which collide with this one
@@ -2623,7 +2615,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
 
                $affectedRowCount = 0;
                try {
-                       $this->startAtomic( $fname );
+                       $this->startAtomic( $fname, self::ATOMIC_CANCELABLE );
                        # Update any existing conflicting row(s)
                        if ( $where !== false ) {
                                $ok = $this->update( $table, $set, $where, $fname );
@@ -2779,7 +2771,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
 
                try {
                        $affectedRowCount = 0;
-                       $this->startAtomic( $fname );
+                       $this->startAtomic( $fname, self::ATOMIC_CANCELABLE );
                        $rows = [];
                        $ok = true;
                        foreach ( $res as $row ) {
@@ -3095,7 +3087,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                        $this->trxPreCommitCallbacks[] = [ $callback, $fname ];
                } else {
                        // No transaction is active nor will start implicitly, so make one for this callback
-                       $this->startAtomic( __METHOD__ );
+                       $this->startAtomic( __METHOD__, self::ATOMIC_CANCELABLE );
                        try {
                                call_user_func( $callback );
                                $this->endAtomic( __METHOD__ );
@@ -3279,7 +3271,10 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                $this->query( 'ROLLBACK TO SAVEPOINT ' . $this->addIdentifierQuotes( $identifier ), $fname );
        }
 
-       final public function startAtomic( $fname = __METHOD__ ) {
+       final public function startAtomic(
+               $fname = __METHOD__, $cancelable = self::ATOMIC_NOT_CANCELABLE
+       ) {
+               $savepointId = $cancelable === self::ATOMIC_CANCELABLE ? 'n/a' : null;
                if ( !$this->trxLevel ) {
                        $this->begin( $fname, self::TRANSACTION_INTERNAL );
                        // If DBO_TRX is set, a series of startAtomic/endAtomic pairs will result
@@ -3287,8 +3282,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                        if ( !$this->getFlag( self::DBO_TRX ) ) {
                                $this->trxAutomaticAtomic = true;
                        }
-                       $savepointId = null;
-               } else {
+               } elseif ( $cancelable === self::ATOMIC_CANCELABLE ) {
                        $savepointId = 'wikimedia_rdbms_atomic' . ++$this->trxAtomicCounter;
                        if ( strlen( $savepointId ) > 30 ) { // 30 == Oracle's identifier length limit (pre 12c)
                                $this->queryLogger->warning(
@@ -3316,9 +3310,9 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                        throw new DBUnexpectedError( $this, "Invalid atomic section ended (got $fname)." );
                }
 
-               if ( !$savepointId ) {
+               if ( !$this->trxAtomicLevels && $this->trxAutomaticAtomic ) {
                        $this->commit( $fname, self::FLUSHING_INTERNAL );
-               } else {
+               } elseif ( $savepointId && $savepointId !== 'n/a' ) {
                        $this->doReleaseSavepoint( $savepointId, $fname );
                }
        }
@@ -3333,10 +3327,13 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                if ( $savedFname !== $fname ) {
                        throw new DBUnexpectedError( $this, "Invalid atomic section ended (got $fname)." );
                }
-
                if ( !$savepointId ) {
+                       throw new DBUnexpectedError( $this, "Uncancelable atomic section canceled (got $fname)." );
+               }
+
+               if ( !$this->trxAtomicLevels && $this->trxAutomaticAtomic ) {
                        $this->rollback( $fname, self::FLUSHING_INTERNAL );
-               } else {
+               } elseif ( $savepointId !== 'n/a' ) {
                        $this->doRollbackToSavepoint( $savepointId, $fname );
                }
 
@@ -3344,7 +3341,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
        }
 
        final public function doAtomicSection( $fname, callable $callback ) {
-               $this->startAtomic( $fname );
+               $this->startAtomic( $fname, self::ATOMIC_CANCELABLE );
                try {
                        $res = call_user_func_array( $callback, [ $this, $fname ] );
                } catch ( Exception $e ) {
@@ -3395,10 +3392,8 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                $this->trxWriteAdjQueryCount = 0;
                $this->trxWriteCallers = [];
                // First SELECT after BEGIN will establish the snapshot in REPEATABLE-READ.
-               // Get an estimate of the replica DB lag before then, treating estimate staleness
-               // as lag itself just to be safe
-               $status = $this->getApproximateLagStatus();
-               $this->trxReplicaLag = $status['lag'] + ( microtime( true ) - $status['since'] );
+               // Get an estimate of the replication lag before any such queries.
+               $this->trxReplicaLag = $this->getApproximateLagStatus()['lag'];
                // T147697: make explicitTrxActive() return true until begin() finishes. This way, no
                // caller will think its OK to muck around with the transaction just because startAtomic()
                // has not yet completed (e.g. setting trxAtomicLevels).