X-Git-Url: http://git.cyclocoop.org/?a=blobdiff_plain;f=includes%2Flibs%2Frdbms%2Fdatabase%2FDatabase.php;h=92b94716d87ac2d9f0919e70d25f161153c618be;hb=2b4c62b597188116e2ae41f1547f81ed1ec5104f;hp=c86adacfda15eee988b0e4db994c70b116372c59;hpb=f8b48c99bfa6ec13524abcb946559603ece2450b;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/libs/rdbms/database/Database.php b/includes/libs/rdbms/database/Database.php index c86adacfda..92b94716d8 100644 --- a/includes/libs/rdbms/database/Database.php +++ b/includes/libs/rdbms/database/Database.php @@ -105,9 +105,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware /** @var array Map of (table name => 1) for TEMPORARY tables */ protected $sessionTempTables = []; - /** @var int Whether there is an active transaction (1 or 0) */ - protected $trxLevel = 0; - /** @var string Hexidecimal string if a transaction is active or empty string otherwise */ + /** @var string ID of the active transaction or the empty string otherwise */ protected $trxShortId = ''; /** @var int Transaction status */ protected $trxStatus = self::STATUS_TRX_NONE; @@ -512,12 +510,12 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware return $res; } - public function trxLevel() { - return $this->trxLevel; + final public function trxLevel() { + return ( $this->trxShortId != '' ) ? 1 : 0; } public function trxTimestamp() { - return $this->trxLevel ? $this->trxTimestamp : null; + return $this->trxLevel() ? $this->trxTimestamp : null; } /** @@ -620,11 +618,11 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware } public function writesPending() { - return $this->trxLevel && $this->trxDoneWrites; + return $this->trxLevel() && $this->trxDoneWrites; } public function writesOrCallbacksPending() { - return $this->trxLevel && ( + return $this->trxLevel() && ( $this->trxDoneWrites || $this->trxIdleCallbacks || $this->trxPreCommitCallbacks || @@ -634,7 +632,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware } public function preCommitCallbacksPending() { - return $this->trxLevel && $this->trxPreCommitCallbacks; + return $this->trxLevel() && $this->trxPreCommitCallbacks; } /** @@ -652,7 +650,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware } public function pendingWriteQueryDuration( $type = self::ESTIMATE_TOTAL ) { - if ( !$this->trxLevel ) { + if ( !$this->trxLevel() ) { return false; } elseif ( !$this->trxDoneWrites ) { return 0.0; @@ -682,7 +680,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware } public function pendingWriteCallers() { - return $this->trxLevel ? $this->trxWriteCallers : []; + return $this->trxLevel() ? $this->trxWriteCallers : []; } public function pendingWriteRowsAffected() { @@ -871,7 +869,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware // This should mostly do nothing if the connection is already closed if ( $this->conn ) { // Roll back any dangling transaction first - if ( $this->trxLevel ) { + if ( $this->trxLevel() ) { if ( $this->trxAtomicLevels ) { // Cannot let incomplete atomic sections be committed $levels = $this->flatAtomicSectionList(); @@ -996,8 +994,8 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware * For SELECT queries, this returns either: * - a) A driver-specific value/resource, only on success. This can be iterated * over by calling fetchObject()/fetchRow() until there are no more rows. - * Alternatively, the result can be passed to resultObject() to obtain a - * ResultWrapper instance which can then be iterated over via "foreach". + * Alternatively, the result can be passed to resultObject() to obtain an + * IResultWrapper instance which can then be iterated over via "foreach". * - b) False, on any query failure * * For non-SELECT queries, this returns either: @@ -1070,7 +1068,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware protected function isTransactableQuery( $sql ) { return !in_array( $this->getQueryVerb( $sql ), - [ 'BEGIN', 'ROLLBACK', 'COMMIT', 'SET', 'SHOW', 'CREATE', 'ALTER', 'USE' ], + [ 'BEGIN', 'ROLLBACK', 'COMMIT', 'SET', 'SHOW', 'CREATE', 'ALTER', 'USE', 'SHOW' ], true ); } @@ -1078,9 +1076,12 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware /** * @param string $sql A SQL query * @param bool $pseudoPermanent Treat any table from CREATE TEMPORARY as pseudo-permanent - * @return int|null A self::TEMP_* constant for temp table operations or null otherwise + * @return array A n-tuple of: + * - int|null: A self::TEMP_* constant for temp table operations or null otherwise + * - string|null: The name of the new temporary table $sql creates, or null + * - string|null: The name of the temporary table that $sql drops, or null */ - protected function registerTempTableWrite( $sql, $pseudoPermanent ) { + protected function getTempWrites( $sql, $pseudoPermanent ) { static $qt = '[`"\']?(\w+)[`"\']?'; // quoted table if ( preg_match( @@ -1089,33 +1090,46 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware $matches ) ) { $type = $pseudoPermanent ? self::$TEMP_PSEUDO_PERMANENT : self::$TEMP_NORMAL; - $this->sessionTempTables[$matches[1]] = $type; - return $type; + return [ $type, $matches[1], null ]; } elseif ( preg_match( '/^DROP\s+(?:TEMPORARY\s+)?TABLE\s+(?:IF\s+EXISTS\s+)?' . $qt . '/i', $sql, $matches ) ) { - $type = $this->sessionTempTables[$matches[1]] ?? null; - unset( $this->sessionTempTables[$matches[1]] ); - - return $type; + return [ $this->sessionTempTables[$matches[1]] ?? null, null, $matches[1] ]; } elseif ( preg_match( '/^TRUNCATE\s+(?:TEMPORARY\s+)?TABLE\s+(?:IF\s+EXISTS\s+)?' . $qt . '/i', $sql, $matches ) ) { - return $this->sessionTempTables[$matches[1]] ?? null; + return [ $this->sessionTempTables[$matches[1]] ?? null, null, null ]; } elseif ( preg_match( '/^(?:(?:INSERT|REPLACE)\s+(?:\w+\s+)?INTO|UPDATE|DELETE\s+FROM)\s+' . $qt . '/i', $sql, $matches ) ) { - return $this->sessionTempTables[$matches[1]] ?? null; + return [ $this->sessionTempTables[$matches[1]] ?? null, null, null ]; } - return null; + return [ null, null, null ]; + } + + /** + * @param IResultWrapper|bool $ret + * @param int|null $tmpType TEMP_NORMAL or TEMP_PSEUDO_PERMANENT + * @param string|null $tmpNew Name of created temp table + * @param string|null $tmpDel Name of dropped temp table + */ + protected function registerTempWrites( $ret, $tmpType, $tmpNew, $tmpDel ) { + if ( $ret !== false ) { + if ( $tmpNew !== null ) { + $this->sessionTempTables[$tmpNew] = $tmpType; + } + if ( $tmpDel !== null ) { + unset( $this->sessionTempTables[$tmpDel] ); + } + } } public function query( $sql, $fname = __METHOD__, $flags = 0 ) { @@ -1158,31 +1172,35 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware final protected function executeQuery( $sql, $fname, $flags ) { $this->assertHasConnectionHandle(); - $priorTransaction = $this->trxLevel; + $priorTransaction = $this->trxLevel(); if ( $this->isWriteQuery( $sql ) ) { - # In theory, non-persistent writes are allowed in read-only mode, but due to things - # like https://bugs.mysql.com/bug.php?id=33669 that might not work anyway... + // In theory, non-persistent writes are allowed in read-only mode, but due to things + // like https://bugs.mysql.com/bug.php?id=33669 that might not work anyway... $this->assertIsWritableMaster(); - # Do not treat temporary table writes as "meaningful writes" since they are only - # visible to one session and are not permanent. Profile them as reads. Integration - # tests can override this behavior via $flags. + // Do not treat temporary table writes as "meaningful writes" since they are only + // visible to one session and are not permanent. Profile them as reads. Integration + // tests can override this behavior via $flags. $pseudoPermanent = $this->hasFlags( $flags, self::QUERY_PSEUDO_PERMANENT ); - $tableType = $this->registerTempTableWrite( $sql, $pseudoPermanent ); - $isPermWrite = ( $tableType !== self::$TEMP_NORMAL ); - # DBConnRef uses QUERY_REPLICA_ROLE to enforce the replica role for raw SQL queries + list( $tmpType, $tmpNew, $tmpDel ) = $this->getTempWrites( $sql, $pseudoPermanent ); + $isPermWrite = ( $tmpType !== self::$TEMP_NORMAL ); + // DBConnRef uses QUERY_REPLICA_ROLE to enforce the replica role for raw SQL queries if ( $isPermWrite && $this->hasFlags( $flags, self::QUERY_REPLICA_ROLE ) ) { throw new DBReadOnlyRoleError( $this, "Cannot write; target role is DB_REPLICA" ); } } else { + // No permanent writes in this query $isPermWrite = false; + // No temporary tables written to either + list( $tmpType, $tmpNew, $tmpDel ) = [ null, null, null ]; } // Add trace comment to the begin of the sql string, right after the operator. // Or, for one-word queries (like "BEGIN" or COMMIT") add it to the end (T44598) $commentedSql = preg_replace( '/\s|$/', " /* $fname {$this->agent} */ ", $sql, 1 ); - // Send the query to the server and fetch any corresponding errors + // Send the query to the server and fetch any corresponding errors. + // This also doubles as a "ping" to see if the connection was dropped. list( $ret, $err, $errno, $recoverableSR, $recoverableCL, $reconnected ) = $this->executeQueryAttempt( $sql, $commentedSql, $isPermWrite, $fname, $flags ); @@ -1194,6 +1212,9 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware $this->executeQueryAttempt( $sql, $commentedSql, $isPermWrite, $fname, $flags ); } + // Register creation and dropping of temporary tables + $this->registerTempWrites( $ret, $tmpType, $tmpNew, $tmpDel ); + $corruptedTrx = false; if ( $ret === false ) { @@ -1248,7 +1269,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware // Keep track of whether the transaction has write queries pending if ( $isPermWrite ) { $this->lastWriteTime = microtime( true ); - if ( $this->trxLevel && !$this->trxDoneWrites ) { + if ( $this->trxLevel() && !$this->trxDoneWrites ) { $this->trxDoneWrites = true; $this->trxProfiler->transactionWritingIn( $this->server, $this->getDomainID(), $this->trxShortId ); @@ -1278,7 +1299,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware if ( $ret !== false ) { $this->lastPing = $startTime; - if ( $isPermWrite && $this->trxLevel ) { + if ( $isPermWrite && $this->trxLevel() ) { $this->updateTrxWriteQueryTime( $sql, $queryRuntime, $this->affectedRows() ); $this->trxWriteCallers[] = $fname; } @@ -1327,7 +1348,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware */ private function beginIfImplied( $sql, $fname ) { if ( - !$this->trxLevel && + !$this->trxLevel() && $this->getFlag( self::DBO_TRX ) && $this->isTransactableQuery( $sql ) ) { @@ -1457,7 +1478,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware // https://www.postgresql.org/docs/9.4/static/functions-admin.html#FUNCTIONS-ADVISORY-LOCKS $this->sessionNamedLocks = []; // Session loss implies transaction loss - $this->trxLevel = 0; + $oldTrxShortId = $this->consumeTrxShortId(); $this->trxAtomicCounter = 0; $this->trxIdleCallbacks = []; // T67263; transaction already lost $this->trxPreCommitCallbacks = []; // T67263; transaction already lost @@ -1466,7 +1487,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware $this->trxProfiler->transactionWritingOut( $this->server, $this->getDomainID(), - $this->trxShortId, + $oldTrxShortId, $this->pendingWriteQueryDuration( self::ESTIMATE_TOTAL ), $this->trxWriteAffectedRows ); @@ -1492,6 +1513,18 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware } } + /** + * Reset the transaction ID and return the old one + * + * @return string The old transaction ID or the empty string if there wasn't one + */ + private function consumeTrxShortId() { + $old = $this->trxShortId; + $this->trxShortId = ''; + + return $old; + } + /** * Checks whether the cause of the error is detected to be a timeout. * @@ -1989,7 +2022,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware public function lockForUpdate( $table, $conds = '', $fname = __METHOD__, $options = [], $join_conds = [] ) { - if ( !$this->trxLevel && !$this->getFlag( self::DBO_TRX ) ) { + if ( !$this->trxLevel() && !$this->getFlag( self::DBO_TRX ) ) { throw new DBUnexpectedError( $this, __METHOD__ . ': no transaction is active nor is DBO_TRX set' @@ -3336,21 +3369,21 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware } final public function onTransactionResolution( callable $callback, $fname = __METHOD__ ) { - if ( !$this->trxLevel ) { + if ( !$this->trxLevel() ) { throw new DBUnexpectedError( $this, "No transaction is active." ); } $this->trxEndCallbacks[] = [ $callback, $fname, $this->currentAtomicSectionId() ]; } final public function onTransactionCommitOrIdle( callable $callback, $fname = __METHOD__ ) { - if ( !$this->trxLevel && $this->getTransactionRoundId() ) { + if ( !$this->trxLevel() && $this->getTransactionRoundId() ) { // Start an implicit transaction similar to how query() does $this->begin( __METHOD__, self::TRANSACTION_INTERNAL ); $this->trxAutomatic = true; } $this->trxIdleCallbacks[] = [ $callback, $fname, $this->currentAtomicSectionId() ]; - if ( !$this->trxLevel ) { + if ( !$this->trxLevel() ) { $this->runOnTransactionIdleCallbacks( self::TRIGGER_IDLE ); } } @@ -3360,13 +3393,13 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware } final public function onTransactionPreCommitOrIdle( callable $callback, $fname = __METHOD__ ) { - if ( !$this->trxLevel && $this->getTransactionRoundId() ) { + if ( !$this->trxLevel() && $this->getTransactionRoundId() ) { // Start an implicit transaction similar to how query() does $this->begin( __METHOD__, self::TRANSACTION_INTERNAL ); $this->trxAutomatic = true; } - if ( $this->trxLevel ) { + if ( $this->trxLevel() ) { $this->trxPreCommitCallbacks[] = [ $callback, $fname, $this->currentAtomicSectionId() ]; } else { // No transaction is active nor will start implicitly, so make one for this callback @@ -3382,7 +3415,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware } final public function onAtomicSectionCancel( callable $callback, $fname = __METHOD__ ) { - if ( !$this->trxLevel || !$this->trxAtomicLevels ) { + if ( !$this->trxLevel() || !$this->trxAtomicLevels ) { throw new DBUnexpectedError( $this, "No atomic section is open (got $fname)." ); } $this->trxSectionCancelCallbacks[] = [ $callback, $fname, $this->currentAtomicSectionId() ]; @@ -3392,7 +3425,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware * @return AtomicSectionIdentifier|null ID of the topmost atomic section level */ private function currentAtomicSectionId() { - if ( $this->trxLevel && $this->trxAtomicLevels ) { + if ( $this->trxLevel() && $this->trxAtomicLevels ) { $levelInfo = end( $this->trxAtomicLevels ); return $levelInfo[1]; @@ -3518,7 +3551,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware * @throws Exception */ public function runOnTransactionIdleCallbacks( $trigger ) { - if ( $this->trxLevel ) { // sanity + if ( $this->trxLevel() ) { // sanity throw new DBUnexpectedError( $this, __METHOD__ . ': a transaction is still open.' ); } @@ -3616,7 +3649,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware * Actually run any "atomic section cancel" callbacks. * * @param int $trigger IDatabase::TRIGGER_* constant - * @param AtomicSectionIdentifier[]|null $sectionId Section IDs to cancel, + * @param AtomicSectionIdentifier[]|null $sectionIds Section IDs to cancel, * null on transaction rollback */ private function runOnAtomicSectionCancelCallbacks( @@ -3749,7 +3782,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware ) { $savepointId = $cancelable === self::ATOMIC_CANCELABLE ? self::$NOT_APPLICABLE : null; - if ( !$this->trxLevel ) { + if ( !$this->trxLevel() ) { $this->begin( $fname, self::TRANSACTION_INTERNAL ); // sets trxAutomatic // If DBO_TRX is set, a series of startAtomic/endAtomic pairs will result // in all changes being in one transaction to keep requests transactional. @@ -3775,7 +3808,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware } final public function endAtomic( $fname = __METHOD__ ) { - if ( !$this->trxLevel || !$this->trxAtomicLevels ) { + if ( !$this->trxLevel() || !$this->trxAtomicLevels ) { throw new DBUnexpectedError( $this, "No atomic section is open (got $fname)." ); } @@ -3811,7 +3844,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware final public function cancelAtomic( $fname = __METHOD__, AtomicSectionIdentifier $sectionId = null ) { - if ( !$this->trxLevel || !$this->trxAtomicLevels ) { + if ( !$this->trxLevel() || !$this->trxAtomicLevels ) { throw new DBUnexpectedError( $this, "No atomic section is open (got $fname)." ); } @@ -3916,7 +3949,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware } // Protect against mismatched atomic section, transaction nesting, and snapshot loss - if ( $this->trxLevel ) { + if ( $this->trxLevel() ) { if ( $this->trxAtomicLevels ) { $levels = $this->flatAtomicSectionList(); $msg = "$fname: Got explicit BEGIN while atomic section(s) $levels are open."; @@ -3936,6 +3969,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware $this->assertHasConnectionHandle(); $this->doBegin( $fname ); + $this->trxShortId = sprintf( '%06x', mt_rand( 0, 0xffffff ) ); $this->trxStatus = self::STATUS_TRX_OK; $this->trxStatusIgnoredCause = null; $this->trxAtomicCounter = 0; @@ -3944,7 +3978,6 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware $this->trxDoneWrites = false; $this->trxAutomaticAtomic = false; $this->trxAtomicLevels = []; - $this->trxShortId = sprintf( '%06x', mt_rand( 0, 0xffffff ) ); $this->trxWriteDuration = 0.0; $this->trxWriteQueryCount = 0; $this->trxWriteAffectedRows = 0; @@ -3966,10 +3999,10 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware * * @see Database::begin() * @param string $fname + * @throws DBError */ protected function doBegin( $fname ) { $this->query( 'BEGIN', $fname ); - $this->trxLevel = 1; } final public function commit( $fname = __METHOD__, $flush = self::FLUSHING_ONE ) { @@ -3978,7 +4011,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware throw new DBUnexpectedError( $this, "$fname: invalid flush parameter '$flush'." ); } - if ( $this->trxLevel && $this->trxAtomicLevels ) { + if ( $this->trxLevel() && $this->trxAtomicLevels ) { // There are still atomic sections open; this cannot be ignored $levels = $this->flatAtomicSectionList(); throw new DBUnexpectedError( @@ -3988,7 +4021,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware } if ( $flush === self::FLUSHING_INTERNAL || $flush === self::FLUSHING_ALL_PEERS ) { - if ( !$this->trxLevel ) { + if ( !$this->trxLevel() ) { return; // nothing to do } elseif ( !$this->trxAutomatic ) { throw new DBUnexpectedError( @@ -3996,7 +4029,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware "$fname: Flushing an explicit transaction, getting out of sync." ); } - } elseif ( !$this->trxLevel ) { + } elseif ( !$this->trxLevel() ) { $this->queryLogger->error( "$fname: No transaction to commit, something got out of sync." ); return; // nothing to do @@ -4013,6 +4046,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware $writeTime = $this->pendingWriteQueryDuration( self::ESTIMATE_DB_APPLY ); $this->doCommit( $fname ); + $oldTrxShortId = $this->consumeTrxShortId(); $this->trxStatus = self::STATUS_TRX_NONE; if ( $this->trxDoneWrites ) { @@ -4020,7 +4054,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware $this->trxProfiler->transactionWritingOut( $this->server, $this->getDomainID(), - $this->trxShortId, + $oldTrxShortId, $writeTime, $this->trxWriteAffectedRows ); @@ -4038,16 +4072,16 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware * * @see Database::commit() * @param string $fname + * @throws DBError */ protected function doCommit( $fname ) { - if ( $this->trxLevel ) { + if ( $this->trxLevel() ) { $this->query( 'COMMIT', $fname ); - $this->trxLevel = 0; } } final public function rollback( $fname = __METHOD__, $flush = self::FLUSHING_ONE ) { - $trxActive = $this->trxLevel; + $trxActive = $this->trxLevel(); if ( $flush !== self::FLUSHING_INTERNAL && $flush !== self::FLUSHING_ALL_PEERS @@ -4063,6 +4097,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware $this->assertHasConnectionHandle(); $this->doRollback( $fname ); + $oldTrxShortId = $this->consumeTrxShortId(); $this->trxStatus = self::STATUS_TRX_NONE; $this->trxAtomicLevels = []; // Estimate the RTT via a query now that trxStatus is OK @@ -4072,7 +4107,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware $this->trxProfiler->transactionWritingOut( $this->server, $this->getDomainID(), - $this->trxShortId, + $oldTrxShortId, $writeTime, $this->trxWriteAffectedRows ); @@ -4106,13 +4141,13 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware * * @see Database::rollback() * @param string $fname + * @throws DBError */ protected function doRollback( $fname ) { - if ( $this->trxLevel ) { + if ( $this->trxLevel() ) { # Disconnects cause rollback anyway, so ignore those errors $ignoreErrors = true; $this->query( 'ROLLBACK', $fname, $ignoreErrors ); - $this->trxLevel = 0; } } @@ -4130,7 +4165,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware } public function explicitTrxActive() { - return $this->trxLevel && ( $this->trxAtomicLevels || !$this->trxAutomatic ); + return $this->trxLevel() && ( $this->trxAtomicLevels || !$this->trxAutomatic ); } public function duplicateTableStructure( @@ -4173,9 +4208,8 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware abstract protected function fetchAffectedRowCount(); /** - * Take the result from a query, and wrap it in a ResultWrapper if - * necessary. Boolean values are passed through as is, to indicate success - * of write queries or failure. + * Take a query result and wrap it in an iterable result wrapper if necessary. + * Booleans are passed through as-is to indicate success/failure of write queries. * * Once upon a time, Database::query() returned a bare MySQL result * resource, and it was necessary to call this function to convert it to @@ -4187,12 +4221,11 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware */ protected function resultObject( $result ) { if ( !$result ) { - return false; - } elseif ( $result instanceof ResultWrapper ) { + return false; // failed query + } elseif ( $result instanceof IResultWrapper ) { return $result; } elseif ( $result === true ) { - // Successful write query - return $result; + return $result; // succesful write query } else { return new ResultWrapper( $this, $result ); } @@ -4282,7 +4315,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware * @since 1.27 */ final protected function getRecordedTransactionLagStatus() { - return ( $this->trxLevel && $this->trxReplicaLag !== null ) + return ( $this->trxLevel() && $this->trxReplicaLag !== null ) ? [ 'lag' => $this->trxReplicaLag, 'since' => $this->trxTimestamp() ] : null; } @@ -4830,7 +4863,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware * Run a few simple sanity checks and close dangling connections */ public function __destruct() { - if ( $this->trxLevel && $this->trxDoneWrites ) { + if ( $this->trxLevel() && $this->trxDoneWrites ) { trigger_error( "Uncommitted DB writes (transaction from {$this->trxFname})." ); }