+ /**
+ * @expectedException \Wikimedia\Rdbms\DBTransactionStateError
+ */
+ public function testTransactionErrorState1() {
+ $wrapper = TestingAccessWrapper::newFromObject( $this->database );
+
+ $this->database->begin( __METHOD__ );
+ $wrapper->trxStatus = Database::STATUS_TRX_ERROR;
+ $this->database->delete( 'x', [ 'field' => 3 ], __METHOD__ );
+ $this->database->commit( __METHOD__ );
+ }
+
+ /**
+ * @covers \Wikimedia\Rdbms\Database::query
+ */
+ public function testTransactionErrorState2() {
+ $wrapper = TestingAccessWrapper::newFromObject( $this->database );
+
+ $this->database->startAtomic( __METHOD__ );
+ $wrapper->trxStatus = Database::STATUS_TRX_ERROR;
+ $this->database->rollback( __METHOD__ );
+ $this->assertEquals( 0, $this->database->trxLevel() );
+ $this->assertEquals( Database::STATUS_TRX_NONE, $wrapper->trxStatus() );
+ $this->assertLastSql( 'BEGIN; ROLLBACK' );
+
+ $this->database->startAtomic( __METHOD__ );
+ $this->assertEquals( Database::STATUS_TRX_OK, $wrapper->trxStatus() );
+ $this->database->delete( 'x', [ 'field' => 1 ], __METHOD__ );
+ $this->database->endAtomic( __METHOD__ );
+ $this->assertEquals( Database::STATUS_TRX_NONE, $wrapper->trxStatus() );
+ $this->assertLastSql( 'BEGIN; DELETE FROM x WHERE field = \'1\'; COMMIT' );
+ $this->assertEquals( 0, $this->database->trxLevel(), 'Use after rollback()' );
+
+ $this->database->begin( __METHOD__ );
+ $this->database->startAtomic( __METHOD__, Database::ATOMIC_CANCELABLE );
+ $this->database->update( 'y', [ 'a' => 1 ], [ 'field' => 1 ], __METHOD__ );
+ $wrapper->trxStatus = Database::STATUS_TRX_ERROR;
+ $this->database->cancelAtomic( __METHOD__ );
+ $this->assertEquals( Database::STATUS_TRX_OK, $wrapper->trxStatus() );
+ $this->database->startAtomic( __METHOD__ );
+ $this->database->delete( 'y', [ 'field' => 1 ], __METHOD__ );
+ $this->database->endAtomic( __METHOD__ );
+ $this->database->commit( __METHOD__ );
+ // phpcs:ignore Generic.Files.LineLength
+ $this->assertLastSql( 'BEGIN; SAVEPOINT wikimedia_rdbms_atomic1; UPDATE y SET a = \'1\' WHERE field = \'1\'; ROLLBACK TO SAVEPOINT wikimedia_rdbms_atomic1; DELETE FROM y WHERE field = \'1\'; COMMIT' );
+ $this->assertEquals( 0, $this->database->trxLevel(), 'Use after rollback()' );
+
+ // Next transaction
+ $this->database->startAtomic( __METHOD__ );
+ $this->assertEquals( Database::STATUS_TRX_OK, $wrapper->trxStatus() );
+ $this->database->delete( 'x', [ 'field' => 3 ], __METHOD__ );
+ $this->database->endAtomic( __METHOD__ );
+ $this->assertEquals( Database::STATUS_TRX_NONE, $wrapper->trxStatus() );
+ $this->assertLastSql( 'BEGIN; DELETE FROM x WHERE field = \'3\'; COMMIT' );
+ $this->assertEquals( 0, $this->database->trxLevel() );
+ }
+
+ /**
+ * @covers \Wikimedia\Rdbms\Database::close
+ */
+ public function testPrematureClose1() {
+ $fname = __METHOD__;
+ $this->database->begin( __METHOD__ );
+ $this->database->onTransactionIdle( function () use ( $fname ) {
+ $this->database->query( 'SELECT 1', $fname );
+ } );
+ $this->database->delete( 'x', [ 'field' => 3 ], __METHOD__ );
+ $this->database->close();
+
+ $this->assertFalse( $this->database->isOpen() );
+ $this->assertLastSql( 'BEGIN; DELETE FROM x WHERE field = \'3\'; COMMIT; SELECT 1' );
+ $this->assertEquals( 0, $this->database->trxLevel() );
+ }
+
+ /**
+ * @covers \Wikimedia\Rdbms\Database::close
+ */
+ public function testPrematureClose2() {
+ try {
+ $fname = __METHOD__;
+ $this->database->startAtomic( __METHOD__ );
+ $this->database->onTransactionIdle( function () use ( $fname ) {
+ $this->database->query( 'SELECT 1', $fname );
+ } );
+ $this->database->delete( 'x', [ 'field' => 3 ], __METHOD__ );
+ $this->database->close();
+ $this->fail( 'Expected exception not thrown' );
+ } catch ( DBUnexpectedError $ex ) {
+ $this->assertSame(
+ 'Wikimedia\Rdbms\Database::close: atomic sections ' .
+ 'DatabaseSQLTest::testPrematureClose2 are still open.',
+ $ex->getMessage()
+ );
+ }
+
+ $this->assertFalse( $this->database->isOpen() );
+ $this->assertLastSql( 'BEGIN; DELETE FROM x WHERE field = \'3\'; ROLLBACK' );
+ $this->assertEquals( 0, $this->database->trxLevel() );
+ }