}
public function close() {
- if ( count( $this->mTrxIdleCallbacks ) ) { // sanity
- throw new MWException( "Transaction idle callbacks still pending." );
- }
if ( $this->mConn ) {
if ( $this->trxLevel() ) {
if ( !$this->mTrxAutomatic ) {
$closed = $this->closeConnection();
$this->mConn = false;
+ } elseif ( $this->mTrxIdleCallbacks || $this->mTrxEndCallbacks ) { // sanity
+ throw new MWException( "Transaction callbacks still pending." );
} else {
$closed = true;
}
final public function onTransactionIdle( callable $callback ) {
$this->mTrxIdleCallbacks[] = [ $callback, wfGetCaller() ];
if ( !$this->mTrxLevel ) {
- $this->runOnTransactionIdleCallbacks();
+ $this->runOnTransactionIdleCallbacks( self::TRIGGER_IDLE );
}
}
if ( $this->mTrxLevel ) {
$this->mTrxPreCommitCallbacks[] = [ $callback, wfGetCaller() ];
} else {
- $this->onTransactionIdle( $callback ); // this will trigger immediately
+ // If no transaction is active, then make one for this callback
+ $this->begin( __METHOD__ );
+ try {
+ call_user_func( $callback );
+ $this->commit( __METHOD__ );
+ } catch ( Exception $e ) {
+ $this->rollback( __METHOD__ );
+ throw $e;
+ }
}
}
/**
- * Actually any "on transaction idle" callbacks.
+ * Actually run and consume any "on transaction idle" callbacks.
*
+ * @param integer $trigger IDatabase::TRIGGER_* constant
* @since 1.20
*/
- protected function runOnTransactionIdleCallbacks() {
+ protected function runOnTransactionIdleCallbacks( $trigger ) {
$autoTrx = $this->getFlag( DBO_TRX ); // automatic begin() enabled?
$e = $ePrior = null; // last exception
try {
list( $phpCallback ) = $callback;
$this->clearFlag( DBO_TRX ); // make each query its own transaction
- call_user_func( $phpCallback );
+ call_user_func_array( $phpCallback, [ $trigger ] );
if ( $autoTrx ) {
$this->setFlag( DBO_TRX ); // restore automatic begin()
} else {
}
/**
- * Actually any "on transaction pre-commit" callbacks.
+ * Actually run and consume any "on transaction pre-commit" callbacks.
*
* This method should not be used outside of Database/LoadBalancer
*
final public function endAtomic( $fname = __METHOD__ ) {
if ( !$this->mTrxLevel ) {
- throw new DBUnexpectedError( $this, 'No atomic transaction is open.' );
+ throw new DBUnexpectedError( $this, "No atomic transaction is open (got $fname)." );
}
if ( !$this->mTrxAtomicLevels ||
array_pop( $this->mTrxAtomicLevels ) !== $fname
) {
- throw new DBUnexpectedError( $this, 'Invalid atomic section ended.' );
+ throw new DBUnexpectedError( $this, "Invalid atomic section ended (got $fname)." );
}
if ( !$this->mTrxAtomicLevels && $this->mTrxAutomaticAtomic ) {
$this->mServer, $this->mDBname, $this->mTrxShortId, $writeTime );
}
- $this->runOnTransactionIdleCallbacks();
+ $this->runOnTransactionIdleCallbacks( self::TRIGGER_COMMIT );
}
// Avoid fatals if close() was called
$this->mTrxAutomatic = false;
$this->mTrxAutomaticAtomic = false;
$this->mTrxAtomicLevels = [];
- $this->mTrxIdleCallbacks = [];
- $this->mTrxPreCommitCallbacks = [];
$this->mTrxShortId = wfRandomString( 12 );
$this->mTrxWriteDuration = 0.0;
$this->mTrxWriteCallers = [];
$this->mServer, $this->mDBname, $this->mTrxShortId, $writeTime );
}
- $this->runOnTransactionIdleCallbacks();
+ $this->runOnTransactionIdleCallbacks( self::TRIGGER_COMMIT );
}
/**
$this->mTrxIdleCallbacks = []; // clear
$this->mTrxPreCommitCallbacks = []; // clear
- $this->runOnTransactionIdleCallbacks();
+ $this->runOnTransactionIdleCallbacks( self::TRIGGER_ROLLBACK );
}
/**