protected $mServer, $mUser, $mPassword, $mDBname;
- /**
- * @var DatabaseBase
- */
protected $mConn = null;
protected $mOpened = false;
+ /** @var Array */
+ protected $trxIdleCallbacks = array();
+
protected $mTablePrefix;
protected $mFlags;
protected $mTrxLevel = 0;
}
}
+ /**
+ * Run an anonymous function as soon as there is no transaction pending.
+ * If there is a transaction and it is rolled back, then the callback is cancelled.
+ * Callbacks must commit any transactions that they begin.
+ *
+ * This is useful for updates to different systems or separate transactions are needed.
+ *
+ * @param Closure $callback
+ * @return void
+ */
+ final public function onTransactionIdle( Closure $callback ) {
+ if ( $this->mTrxLevel ) {
+ $this->trxIdleCallbacks[] = $callback;
+ } else {
+ $callback();
+ }
+ }
+
+ /**
+ * Actually run the "on transaction idle" callbacks
+ */
+ protected function runOnTransactionIdleCallbacks() {
+ $e = null; // last exception
+ do { // callbacks may add callbacks :)
+ $callbacks = $this->trxIdleCallbacks;
+ $this->trxIdleCallbacks = array(); // recursion guard
+ foreach ( $callbacks as $callback ) {
+ try {
+ $callback();
+ } catch ( Exception $e ) {}
+ }
+ } while ( count( $this->trxIdleCallbacks ) );
+
+ if ( $e instanceof Exception ) {
+ throw $e; // re-throw any last exception
+ }
+ }
+
/**
* Begin a transaction
*
* @param $fname string
*/
- public function begin( $fname = 'DatabaseBase::begin' ) {
+ final public function begin( $fname = 'DatabaseBase::begin' ) {
+ if ( $this->mTrxLevel ) { // implicit commit
+ $this->doCommit( $fname );
+ $this->runOnTransactionIdleCallbacks();
+ }
+ $this->doBegin( $fname );
+ }
+
+ /**
+ * @see DatabaseBase::begin()
+ * @param type $fname
+ */
+ protected function doBegin( $fname ) {
$this->query( 'BEGIN', $fname );
$this->mTrxLevel = 1;
}
*
* @param $fname string
*/
- public function commit( $fname = 'DatabaseBase::commit' ) {
+ final public function commit( $fname = 'DatabaseBase::commit' ) {
+ $this->doCommit( $fname );
+ $this->runOnTransactionIdleCallbacks();
+ }
+
+ /**
+ * @see DatabaseBase::commit()
+ * @param type $fname
+ */
+ protected function doCommit( $fname ) {
if ( $this->mTrxLevel ) {
$this->query( 'COMMIT', $fname );
$this->mTrxLevel = 0;
*
* @param $fname string
*/
- public function rollback( $fname = 'DatabaseBase::rollback' ) {
+ final public function rollback( $fname = 'DatabaseBase::rollback' ) {
+ $this->doRollback( $fname );
+ $this->trxIdleCallbacks = array(); // cancel
+ }
+
+ /**
+ * @see DatabaseBase::rollback()
+ * @param type $fname
+ */
+ protected function doRollback( $fname ) {
if ( $this->mTrxLevel ) {
$this->query( 'ROLLBACK', $fname, true );
$this->mTrxLevel = 0;
/**
* Start a transaction (mandatory)
*/
- public function begin( $fname = 'DatabaseIbm_db2::begin' ) {
+ protected function doBegin( $fname = 'DatabaseIbm_db2::begin' ) {
// BEGIN is implicit for DB2
// However, it requires that AutoCommit be off.
* End a transaction
* Must have a preceding begin()
*/
- public function commit( $fname = 'DatabaseIbm_db2::commit' ) {
+ protected function doCommit( $fname = 'DatabaseIbm_db2::commit' ) {
db2_commit( $this->mConn );
// Some MediaWiki code is still transaction-less (?).
/**
* Cancel a transaction
*/
- public function rollback( $fname = 'DatabaseIbm_db2::rollback' ) {
+ protected function doRollback( $fname = 'DatabaseIbm_db2::rollback' ) {
db2_rollback( $this->mConn );
// turn auto-commit back on
// not sure if this is appropriate
/**
* Begin a transaction, committing any previously open transaction
*/
- function begin( $fname = 'DatabaseMssql::begin' ) {
+ protected function doBegin( $fname = 'DatabaseMssql::begin' ) {
sqlsrv_begin_transaction( $this->mConn );
$this->mTrxLevel = 1;
}
/**
* End a transaction
*/
- function commit( $fname = 'DatabaseMssql::commit' ) {
+ protected function doCommit( $fname = 'DatabaseMssql::commit' ) {
sqlsrv_commit( $this->mConn );
$this->mTrxLevel = 0;
}
* Rollback a transaction.
* No-op on non-transactional databases.
*/
- function rollback( $fname = 'DatabaseMssql::rollback' ) {
+ protected function doRollback( $fname = 'DatabaseMssql::rollback' ) {
sqlsrv_rollback( $this->mConn );
$this->mTrxLevel = 0;
}
return $this->fieldInfoMulti ($table, $field);
}
- function begin( $fname = 'DatabaseOracle::begin' ) {
+ protected function doBegin( $fname = 'DatabaseOracle::begin' ) {
$this->mTrxLevel = 1;
$this->doQuery( 'SET CONSTRAINTS ALL DEFERRED' );
}
- function commit( $fname = 'DatabaseOracle::commit' ) {
+ protected function doCommit( $fname = 'DatabaseOracle::commit' ) {
if ( $this->mTrxLevel ) {
$ret = oci_commit( $this->mConn );
if ( !$ret ) {
}
}
- function rollback( $fname = 'DatabaseOracle::rollback' ) {
+ protected function doRollback( $fname = 'DatabaseOracle::rollback' ) {
if ( $this->mTrxLevel ) {
oci_rollback( $this->mConn );
$this->mTrxLevel = 0;