protected $mConn = null;
protected $mOpened = false;
- /** @var callable[] */
+ /** @var array[] List of (callable, method name) */
protected $mTrxIdleCallbacks = [];
- /** @var callable[] */
+ /** @var array[] List of (callable, method name) */
protected $mTrxPreCommitCallbacks = [];
+ /** @var array[] List of (callable, method name) */
+ protected $mTrxEndCallbacks = [];
protected $mTablePrefix;
protected $mSchema;
return false;
}
- final public function onTransactionIdle( $callback ) {
+ final public function onTransactionResolution( callable $callback ) {
+ if ( !$this->mTrxLevel ) {
+ throw new DBUnexpectedError( $this, "No transaction is active." );
+ }
+ $this->mTrxEndCallbacks[] = [ $callback, wfGetCaller() ];
+ }
+
+ final public function onTransactionIdle( callable $callback ) {
$this->mTrxIdleCallbacks[] = [ $callback, wfGetCaller() ];
if ( !$this->mTrxLevel ) {
- $this->runOnTransactionIdleCallbacks();
+ $this->runOnTransactionIdleCallbacks( self::TRIGGER_IDLE );
}
}
- final public function onTransactionPreCommitOrIdle( $callback ) {
+ final public function onTransactionPreCommitOrIdle( callable $callback ) {
if ( $this->mTrxLevel ) {
$this->mTrxPreCommitCallbacks[] = [ $callback, wfGetCaller() ];
} else {
/**
* Actually 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
do { // callbacks may add callbacks :)
- $callbacks = $this->mTrxIdleCallbacks;
+ $callbacks = array_merge(
+ $this->mTrxIdleCallbacks,
+ $this->mTrxEndCallbacks // include "transaction resolution" callbacks
+ );
$this->mTrxIdleCallbacks = []; // recursion guard
+ $this->mTrxEndCallbacks = []; // recursion guard
foreach ( $callbacks as $callback ) {
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.
*
+ * This method should not be used outside of Database/LoadBalancer
+ *
* @since 1.22
*/
- protected function runOnTransactionPreCommitCallbacks() {
+ public function runOnTransactionPreCommitCallbacks() {
$e = $ePrior = null; // last exception
do { // callbacks may add callbacks :)
$callbacks = $this->mTrxPreCommitCallbacks;
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 ) {
}
}
- final public function doAtomicSection( $fname, $callback ) {
- if ( !is_callable( $callback ) ) {
- throw new UnexpectedValueException( "Invalid callback." );
- };
-
+ final public function doAtomicSection( $fname, callable $callback ) {
$this->startAtomic( $fname );
try {
call_user_func_array( $callback, [ $this, $fname ] );
} else {
// The transaction was automatic and has done write operations
if ( $this->mTrxDoneWrites ) {
- wfDebug( "$fname: Automatic transaction with writes in progress" .
+ wfLogDBError( "$fname: Automatic transaction with writes in progress" .
" (from {$this->mTrxFname}), performing implicit commit!\n"
);
}
$this->getTransactionProfiler()->transactionWritingOut(
$this->mServer, $this->mDBname, $this->mTrxShortId, $writeTime );
}
- $this->runOnTransactionIdleCallbacks();
+
+ $this->runOnTransactionIdleCallbacks( self::TRIGGER_COMMIT );
}
- # Avoid fatals if close() was called
+ // Avoid fatals if close() was called
$this->assertOpen();
$this->doBegin( $fname );
}
}
- # Avoid fatals if close() was called
+ // Avoid fatals if close() was called
$this->assertOpen();
$this->runOnTransactionPreCommitCallbacks();
$this->getTransactionProfiler()->transactionWritingOut(
$this->mServer, $this->mDBname, $this->mTrxShortId, $writeTime );
}
- $this->runOnTransactionIdleCallbacks();
+
+ $this->runOnTransactionIdleCallbacks( self::TRIGGER_COMMIT );
}
/**
}
}
- # Avoid fatals if close() was called
+ // Avoid fatals if close() was called
$this->assertOpen();
$this->doRollback( $fname );
- $this->mTrxIdleCallbacks = []; // cancel
- $this->mTrxPreCommitCallbacks = []; // cancel
$this->mTrxAtomicLevels = [];
if ( $this->mTrxDoneWrites ) {
$this->getTransactionProfiler()->transactionWritingOut(
$this->mServer, $this->mDBname, $this->mTrxShortId );
}
+
+ $this->mTrxIdleCallbacks = []; // clear
+ $this->mTrxPreCommitCallbacks = []; // clear
+ $this->runOnTransactionIdleCallbacks( self::TRIGGER_ROLLBACK );
}
/**
if ( $this->mTrxLevel && $this->mTrxDoneWrites ) {
trigger_error( "Uncommitted DB writes (transaction from {$this->mTrxFname})." );
}
- if ( count( $this->mTrxIdleCallbacks ) || count( $this->mTrxPreCommitCallbacks ) ) {
+ $danglingCallbacks = array_merge(
+ $this->mTrxIdleCallbacks,
+ $this->mTrxPreCommitCallbacks,
+ $this->mTrxEndCallbacks
+ );
+ if ( $danglingCallbacks ) {
$callers = [];
- foreach ( $this->mTrxIdleCallbacks as $callbackInfo ) {
+ foreach ( $danglingCallbacks as $callbackInfo ) {
$callers[] = $callbackInfo[1];
}
$callers = implode( ', ', $callers );