* @private
*/
var $mDb, //!< Database connection reference
- $mOptions; //!< SELECT options to be used (array)
+ $mOptions, //!< SELECT options to be used (array)
+ $mHasTransaction;//!< bool whether a transaction is open on this object (internal use only!)
/**@}}*/
/**
* Constructor
- **/
- public function __construct( ) {
+ **/
+ public function __construct( ) {
global $wgAntiLockFlags;
- parent::__construct( );
+ parent::__construct( );
if ( $wgAntiLockFlags & ALF_NO_LINK_LOCK ) {
$this->mOptions = array();
$this->mOptions = array( 'FOR UPDATE' );
}
$this->mDb = wfGetDB( DB_MASTER );
+ $this->mHasTransaction = false;
}
/**
- * Invalidate the cache of a list of pages from a single namespace
+ * Begin a database transaction.
+ *
+ * Because nested transactions are not supportred by the Database class, this implementation
+ * checked Database::trxLevel() and only opens a transaction if none is yet active.
+ */
+ public function beginTransaction() {
+ // NOTE: nested transactions are not supported, only start a transaction if none is open
+ if ( $this->mDb->trxLevel() === 0 ) {
+ $this->mDb->begin( get_class( $this ) . '::beginTransaction' );
+ $this->mHasTransaction = true;
+ }
+ }
+
+ /**
+ * Commit the database transaction started via beginTransaction (if any).
+ */
+ public function commitTransaction() {
+ if ( $this->mHasTransaction ) {
+ $this->mDb->commit( get_class( $this ) . '::commitTransaction' );
+ }
+ }
+
+ /**
+ * Abort the database transaction started via beginTransaction (if any).
+ */
+ public function abortTransaction() {
+ if ( $this->mHasTransaction ) {
+ $this->mDb->rollback( get_class( $this ) . '::abortTransaction' );
+ }
+ }
+
+ /**
+ * Invalidate the cache of a list of pages from a single namespace.
+ * This is intended for use by subclasses.
*
* @param $namespace Integer
* @param $dbkeys Array
*/
- public function invalidatePages( $namespace, $dbkeys ) {
+ protected function invalidatePages( $namespace, $dbkeys ) {
if ( !count( $dbkeys ) ) {
return;
}
}
/**
- * Conveniance method, calls doUpdate() on every element in the array.
+ * Begin an appropriate transaction, if any.
+ * This default implementation does nothing.
+ */
+ public function beginTransaction() {
+ //noop
+ }
+
+ /**
+ * Commit the transaction started via beginTransaction, if any.
+ * This default implementation does nothing.
+ */
+ public function commitTransaction() {
+ //noop
+ }
+
+ /**
+ * Abort / roll back the transaction started via beginTransaction, if any.
+ * This default implementation does nothing.
+ */
+ public function rollbackTransaction() {
+ //noop
+ }
+
+ /**
+ * Conveniance method, calls doUpdate() on every SecondaryDataUpdate in the array.
+ *
+ * This methods supports transactions logic by first calling beginTransaction()
+ * on all updates in the array, then calling doUpdate() on each, and, if all goes well,
+ * then calling commitTransaction() on each update. If an error occurrs,
+ * rollbackTransaction() will be called on any update object that had beginTranscation()
+ * called but not yet commitTransaction().
+ *
+ * This allows for limited transactional logic across multiple baceknds for storing
+ * secondary data.
*
* @static
- * @param $updates array
+ * @param $updates array a list of SecondaryDataUpdate instances
*/
public static function runUpdates( $updates ) {
if ( empty( $updates ) ) return; # nothing to do
- foreach ( $updates as $update ) {
- $update->doUpdate();
+ $open_transactions = array();
+ $exception = null;
+
+ /**
+ * @var $update SecondaryDataUpdate
+ * @var $trans SecondaryDataUpdate
+ */
+
+ try {
+ // begin transactions
+ foreach ( $updates as $update ) {
+ $update->beginTransaction();
+ $open_transactions[] = $update;
+ }
+
+ // do work
+ foreach ( $updates as $update ) {
+ $update->doUpdate();
+ }
+
+ // commit transactions
+ while ( count( $open_transactions ) > 0 ) {
+ $trans = array_pop( $open_transactions );
+ $trans->commitTransaction();
+ }
+ } catch ( Exception $ex ) {
+ $exception = $ex;
+ wfDebug( "Caught exception, will rethrow after rollback: " . $ex->getMessage() );
+ }
+
+ // rollback remaining transactions
+ while ( count( $open_transactions ) > 0 ) {
+ $trans = array_pop( $open_transactions );
+ $trans->rollbackTransaction();
+ }
+
+ if ( $exception ) {
+ throw $exception; // rethrow after cleanup
}
}
if ( $this->exists() ) {
# look at the revision's actual content model
$rev = $this->getRevision();
- return $rev->getContentModelName();
- } else {
- # use the default model for this page
- return $this->mTitle->getContentModelName();
+
+ if ( $rev !== null ) {
+ return $rev->getContentModelName();
+ } else {
+ wfWarn( "Page exists but has no revision!" );
+ }
}
+
+ # use the default model for this page
+ return $this->mTitle->getContentModelName();
}
/**