use Wikimedia\Rdbms\DatabaseSqlite;
use Wikimedia\Rdbms\DatabasePostgres;
use Wikimedia\Rdbms\DatabaseMssql;
+use Wikimedia\Rdbms\DBUnexpectedError;
class DatabaseTest extends PHPUnit\Framework\TestCase {
}
/**
- * @covers Wikimedia\Rdbms\Database::onTransactionIdle
+ * @covers Wikimedia\Rdbms\Database::onTransactionCommitOrIdle
* @covers Wikimedia\Rdbms\Database::runOnTransactionIdleCallbacks
*/
public function testTransactionIdle() {
$flagSet = $db->getFlag( DBO_TRX );
};
- $db->onTransactionIdle( $callback, __METHOD__ );
+ $db->onTransactionCommitOrIdle( $callback, __METHOD__ );
$this->assertTrue( $called, 'Callback reached' );
$this->assertFalse( $flagSet, 'DBO_TRX off in callback' );
$this->assertFalse( $db->getFlag( DBO_TRX ), 'DBO_TRX still default' );
$flagSet = null;
$called = false;
$db->startAtomic( __METHOD__ );
- $db->onTransactionIdle( $callback, __METHOD__ );
+ $db->onTransactionCommitOrIdle( $callback, __METHOD__ );
$this->assertFalse( $called, 'Callback not reached during TRX' );
$db->endAtomic( __METHOD__ );
$this->assertFalse( $db->getFlag( DBO_TRX ), 'DBO_TRX restored to default' );
$db->clearFlag( DBO_TRX );
- $db->onTransactionIdle(
+ $db->onTransactionCommitOrIdle(
function ( $trigger, IDatabase $db ) {
$db->setFlag( DBO_TRX );
},
}
/**
- * @covers Wikimedia\Rdbms\Database::onTransactionIdle
+ * @covers Wikimedia\Rdbms\Database::onTransactionCommitOrIdle
* @covers Wikimedia\Rdbms\Database::runOnTransactionIdleCallbacks
*/
public function testTransactionIdle_TRX() {
$flagSet = $db->getFlag( DBO_TRX );
};
- $db->onTransactionIdle( $callback, __METHOD__ );
+ $db->onTransactionCommitOrIdle( $callback, __METHOD__ );
$this->assertTrue( $called, 'Called when idle if DBO_TRX is set' );
$this->assertFalse( $flagSet, 'DBO_TRX off in callback' );
$this->assertTrue( $db->getFlag( DBO_TRX ), 'DBO_TRX still default' );
$called = false;
$lbFactory->beginMasterChanges( __METHOD__ );
- $db->onTransactionIdle( $callback, __METHOD__ );
+ $db->onTransactionCommitOrIdle( $callback, __METHOD__ );
$this->assertFalse( $called, 'Not called when lb-transaction is active' );
$lbFactory->commitMasterChanges( __METHOD__ );
$called = false;
$lbFactory->beginMasterChanges( __METHOD__ );
- $db->onTransactionIdle( $callback, __METHOD__ );
+ $db->onTransactionCommitOrIdle( $callback, __METHOD__ );
$this->assertFalse( $called, 'Not called when lb-transaction is active' );
$lbFactory->rollbackMasterChanges( __METHOD__ );
$lbFactory->commitMasterChanges( __METHOD__ );
$this->assertFalse( $called, 'Not called in next round commit' );
+
+ $db->setFlag( DBO_TRX );
+ try {
+ $db->onTransactionCommitOrIdle( function () {
+ throw new RuntimeException( 'test' );
+ } );
+ $this->fail( "Exception not thrown" );
+ } catch ( RuntimeException $e ) {
+ $this->assertTrue( $db->getFlag( DBO_TRX ) );
+ }
}
/**
$called = true;
$db->setFlag( DBO_TRX );
} );
- $db->rollback( __METHOD__, IDatabase::FLUSHING_ALL_PEERS );
+ $db->rollback( __METHOD__ );
$this->assertFalse( $db->getFlag( DBO_TRX ), 'DBO_TRX restored to default' );
$this->assertTrue( $called, 'Callback reached' );
}
'numFields', 'numRows',
'open',
'strencode',
+ 'tableExists'
];
$db = $this->getMockBuilder( Database::class )
->disableOriginalConstructor()
$this->assertEquals( true, $db->lockIsFree( 'x', __METHOD__ ) );
$db->clearFlag( DBO_TRX );
+ // Pending writes with DBO_TRX
$this->assertEquals( 0, $db->trxLevel() );
-
+ $this->assertTrue( $db->lockIsFree( 'meow', __METHOD__ ) );
$db->setFlag( DBO_TRX );
+ $db->query( "DELETE FROM test WHERE t = 1" ); // trigger DBO_TRX transaction before lock
try {
- $this->badLockingMethodImplicit( $db );
- } catch ( RunTimeException $e ) {
- $this->assertTrue( $db->trxLevel() > 0, "Transaction not committed." );
+ $lock = $db->getScopedLockAndFlush( 'meow', __METHOD__, 1 );
+ $this->fail( "Exception not reached" );
+ } catch ( DBUnexpectedError $e ) {
+ $this->assertEquals( 1, $db->trxLevel(), "Transaction not committed." );
+ $this->assertTrue( $db->lockIsFree( 'meow', __METHOD__ ), 'Lock not acquired' );
}
- $db->clearFlag( DBO_TRX );
$db->rollback( __METHOD__, IDatabase::FLUSHING_ALL_PEERS );
- $this->assertTrue( $db->lockIsFree( 'meow', __METHOD__ ) );
-
+ // Pending writes without DBO_TRX
+ $db->clearFlag( DBO_TRX );
+ $this->assertEquals( 0, $db->trxLevel() );
+ $this->assertTrue( $db->lockIsFree( 'meow2', __METHOD__ ) );
+ $db->begin( __METHOD__ );
+ $db->query( "DELETE FROM test WHERE t = 1" ); // trigger DBO_TRX transaction before lock
try {
- $this->badLockingMethodExplicit( $db );
- } catch ( RunTimeException $e ) {
- $this->assertTrue( $db->trxLevel() > 0, "Transaction not committed." );
+ $lock = $db->getScopedLockAndFlush( 'meow2', __METHOD__, 1 );
+ $this->fail( "Exception not reached" );
+ } catch ( DBUnexpectedError $e ) {
+ $this->assertEquals( 1, $db->trxLevel(), "Transaction not committed." );
+ $this->assertTrue( $db->lockIsFree( 'meow2', __METHOD__ ), 'Lock not acquired' );
}
+ $db->rollback( __METHOD__ );
+ // No pending writes, with DBO_TRX
+ $db->setFlag( DBO_TRX );
+ $this->assertEquals( 0, $db->trxLevel() );
+ $this->assertTrue( $db->lockIsFree( 'wuff', __METHOD__ ) );
+ $db->query( "SELECT 1", __METHOD__ );
+ $this->assertEquals( 1, $db->trxLevel() );
+ $lock = $db->getScopedLockAndFlush( 'wuff', __METHOD__, 1 );
+ $this->assertEquals( 0, $db->trxLevel() );
+ $this->assertFalse( $db->lockIsFree( 'wuff', __METHOD__ ), 'Lock already acquired' );
$db->rollback( __METHOD__, IDatabase::FLUSHING_ALL_PEERS );
- $this->assertTrue( $db->lockIsFree( 'meow', __METHOD__ ) );
- }
-
- private function badLockingMethodImplicit( IDatabase $db ) {
- $lock = $db->getScopedLockAndFlush( 'meow', __METHOD__, 1 );
- $db->query( "SELECT 1" ); // trigger DBO_TRX
- throw new RunTimeException( "Uh oh!" );
- }
-
- private function badLockingMethodExplicit( IDatabase $db ) {
- $lock = $db->getScopedLockAndFlush( 'meow', __METHOD__, 1 );
+ // No pending writes, without DBO_TRX
+ $db->clearFlag( DBO_TRX );
+ $this->assertEquals( 0, $db->trxLevel() );
+ $this->assertTrue( $db->lockIsFree( 'wuff2', __METHOD__ ) );
$db->begin( __METHOD__ );
- throw new RunTimeException( "Uh oh!" );
+ try {
+ $lock = $db->getScopedLockAndFlush( 'wuff2', __METHOD__, 1 );
+ $this->fail( "Exception not reached" );
+ } catch ( DBUnexpectedError $e ) {
+ $this->assertEquals( 1, $db->trxLevel(), "Transaction not committed." );
+ $this->assertFalse( $db->lockIsFree( 'wuff2', __METHOD__ ), 'Lock not acquired' );
+ }
+ $db->rollback( __METHOD__ );
}
/**