From e9eb3fa3e2b911c110a345a099ec6622e44fc2bb Mon Sep 17 00:00:00 2001 From: Benjamin Morel Date: Sun, 8 Nov 2020 23:17:59 +0100 Subject: [PATCH] Test case --- .../Tests/DBAL/Functional/LockModeTest.php | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 tests/Doctrine/Tests/DBAL/Functional/LockModeTest.php diff --git a/tests/Doctrine/Tests/DBAL/Functional/LockModeTest.php b/tests/Doctrine/Tests/DBAL/Functional/LockModeTest.php new file mode 100644 index 00000000000..e1434406d9c --- /dev/null +++ b/tests/Doctrine/Tests/DBAL/Functional/LockModeTest.php @@ -0,0 +1,100 @@ +connection->getDriver() instanceof OCI8\Driver) { + // https://github.com/doctrine/dbal/issues/4417 + self::markTestSkipped('This test fails on OCI8 for a currently unknown reason'); + } + + if ($this->connection->getDatabasePlatform() instanceof SQLServerPlatform) { + // Use row versioning instead of locking on SQL Server (if we don't, the second connection will block when + // attempting to read the row created by the first connection, instead of reading the previous version); + // for some reason we cannot set READ_COMMITTED_SNAPSHOT ON when not running this test in isolation, + // there may be another connection active at this point; temporarily forcing to SINGLE_USER does the trick. + $name = $this->connection->fetchOne('SELECT db_name()'); + $this->connection->executeStatement('ALTER DATABASE ' . $name . ' SET SINGLE_USER WITH ROLLBACK IMMEDIATE'); + $this->connection->executeStatement('ALTER DATABASE ' . $name . ' SET READ_COMMITTED_SNAPSHOT ON'); + $this->connection->executeStatement('ALTER DATABASE ' . $name . ' SET MULTI_USER'); + } + + $table = new Table('users'); + $table->addColumn('id', 'integer'); + $table->setPrimaryKey(['id']); + + $this->connection->getSchemaManager()->createTable($table); + + $this->connection2 = TestUtil::getConnection(); + + if ($this->connection2->getSchemaManager()->tablesExist('users')) { + return; + } + + if ($this->connection2->getDatabasePlatform() instanceof SqlitePlatform) { + self::markTestSkipped('This test cannot run on SQLite using an in-memory database'); + } + + self::fail('Separate connections do not seem to talk to the same database'); + } + + public function tearDown(): void + { + parent::tearDown(); + + $this->connection2->close(); + + $this->connection->getSchemaManager()->dropTable('users'); + + if (! $this->connection->getDatabasePlatform() instanceof SQLServerPlatform) { + return; + } + + $name = $this->connection->fetchOne('SELECT db_name()'); + $this->connection->executeStatement('ALTER DATABASE ' . $name . ' SET READ_COMMITTED_SNAPSHOT OFF'); + } + + public function testLockModeNoneDoesNotBreakTransactionIsolation(): void + { + try { + $this->connection->setTransactionIsolation(TransactionIsolationLevel::READ_COMMITTED); + $this->connection2->setTransactionIsolation(TransactionIsolationLevel::READ_COMMITTED); + } catch (Exception $e) { + self::markTestSkipped('This test must be able to set a transaction isolation level'); + } + + $this->connection->beginTransaction(); + $this->connection2->beginTransaction(); + + $this->connection->insert('users', ['id' => 1]); + + $query = 'SELECT id FROM users'; + $query = $this->connection2->getDatabasePlatform()->appendLockHint($query, LockMode::NONE); + + self::assertSame([], $this->connection2->fetchAllNumeric($query)); + + $this->connection->commit(); + $this->connection2->commit(); + } +}