From 9395de94c8a3fb99841e793c067525fa7d84ade9 Mon Sep 17 00:00:00 2001 From: Martins Sipenko Date: Wed, 1 Dec 2021 09:41:09 +0200 Subject: [PATCH] [Lock] Create tables in transaction only if supported by driver --- .../Lock/Store/DoctrineDbalStore.php | 36 ++++- .../Tests/Store/DoctrineDbalStoreTest.php | 127 ++++++++++++++++++ 2 files changed, 161 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Lock/Store/DoctrineDbalStore.php b/src/Symfony/Component/Lock/Store/DoctrineDbalStore.php index 1a94f3fc64711..c3272b64ea42b 100644 --- a/src/Symfony/Component/Lock/Store/DoctrineDbalStore.php +++ b/src/Symfony/Component/Lock/Store/DoctrineDbalStore.php @@ -92,8 +92,21 @@ public function save(Key $key) ParameterType::STRING, ]); } catch (TableNotFoundException $e) { - $this->createTable(); - $this->save($key); + if (!$this->conn->isTransactionActive() || $this->platformSupportsTableCreationInTransaction()) { + $this->createTable(); + } + + try { + $this->conn->executeStatement($sql, [ + $this->getHashedKey($key), + $this->getUniqueToken($key), + ], [ + ParameterType::STRING, + ParameterType::STRING, + ]); + } catch (DBALException $e) { + $this->putOffExpiration($key, $this->initialTtl); + } } catch (DBALException $e) { // the lock is already acquired. It could be us. Let's try to put off. $this->putOffExpiration($key, $this->initialTtl); @@ -235,4 +248,23 @@ private function getCurrentTimestampStatement(): string return (string) time(); } } + + /** + * Checks wether current platform supports table creation within transaction. + */ + private function platformSupportsTableCreationInTransaction(): bool + { + $platform = $this->conn->getDatabasePlatform(); + + switch (true) { + case $platform instanceof \Doctrine\DBAL\Platforms\PostgreSQLPlatform: + case $platform instanceof \Doctrine\DBAL\Platforms\PostgreSQL94Platform: + case $platform instanceof \Doctrine\DBAL\Platforms\SqlitePlatform: + case $platform instanceof \Doctrine\DBAL\Platforms\SQLServerPlatform: + case $platform instanceof \Doctrine\DBAL\Platforms\SQLServer2012Platform: + return true; + default: + return false; + } + } } diff --git a/src/Symfony/Component/Lock/Tests/Store/DoctrineDbalStoreTest.php b/src/Symfony/Component/Lock/Tests/Store/DoctrineDbalStoreTest.php index 6a89e49399b0c..4db2d2c614b38 100644 --- a/src/Symfony/Component/Lock/Tests/Store/DoctrineDbalStoreTest.php +++ b/src/Symfony/Component/Lock/Tests/Store/DoctrineDbalStoreTest.php @@ -11,11 +11,16 @@ namespace Symfony\Component\Lock\Tests\Store; +use Doctrine\DBAL\Connection; use Doctrine\DBAL\DriverManager; +use Doctrine\DBAL\Exception\TableNotFoundException; +use Doctrine\DBAL\Platforms\AbstractPlatform; use Symfony\Component\Lock\Key; use Symfony\Component\Lock\PersistingStoreInterface; use Symfony\Component\Lock\Store\DoctrineDbalStore; +class_exists(\Doctrine\DBAL\Platforms\PostgreSqlPlatform::class); + /** * @author Jérémy Derussé * @@ -87,4 +92,126 @@ public function provideDsn() yield ['sqlite3:///'.$dbFile.'3', $dbFile.'3']; yield ['sqlite://localhost/:memory:']; } + + /** + * @dataProvider providePlatforms + */ + public function testCreatesTableInTransaction(string $platform) + { + $conn = $this->createMock(Connection::class); + $conn->expects($this->exactly(3)) + ->method('executeStatement') + ->withConsecutive( + [$this->stringContains('INSERT INTO')], + [$this->matches('create sql stmt')], + [$this->stringContains('INSERT INTO')] + ) + ->will( + $this->onConsecutiveCalls( + $this->throwException( + $this->createMock(TableNotFoundException::class) + ), + 1, + 1 + ) + ); + + $conn->method('isTransactionActive') + ->willReturn(true); + + $platform = $this->createMock($platform); + $platform->method('getCreateTableSQL') + ->willReturn(['create sql stmt']); + + $conn->method('getDatabasePlatform') + ->willReturn($platform); + + $store = new DoctrineDbalStore($conn); + + $key = new Key(uniqid(__METHOD__, true)); + + $store->save($key); + } + + public function providePlatforms() + { + yield [\Doctrine\DBAL\Platforms\PostgreSQLPlatform::class]; + yield [\Doctrine\DBAL\Platforms\PostgreSQL94Platform::class]; + yield [\Doctrine\DBAL\Platforms\SqlitePlatform::class]; + yield [\Doctrine\DBAL\Platforms\SQLServerPlatform::class]; + yield [\Doctrine\DBAL\Platforms\SQLServer2012Platform::class]; + } + + public function testTableCreationInTransactionNotSupported() + { + $conn = $this->createMock(Connection::class); + $conn->expects($this->exactly(2)) + ->method('executeStatement') + ->withConsecutive( + [$this->stringContains('INSERT INTO')], + [$this->stringContains('INSERT INTO')] + ) + ->will( + $this->onConsecutiveCalls( + $this->throwException( + $this->createMock(TableNotFoundException::class) + ), + 1, + 1 + ) + ); + + $conn->method('isTransactionActive') + ->willReturn(true); + + $platform = $this->createMock(AbstractPlatform::class); + $platform->method('getCreateTableSQL') + ->willReturn(['create sql stmt']); + + $conn->expects($this->exactly(2)) + ->method('getDatabasePlatform'); + + $store = new DoctrineDbalStore($conn); + + $key = new Key(uniqid(__METHOD__, true)); + + $store->save($key); + } + + public function testCreatesTableOutsideTransaction() + { + $conn = $this->createMock(Connection::class); + $conn->expects($this->exactly(3)) + ->method('executeStatement') + ->withConsecutive( + [$this->stringContains('INSERT INTO')], + [$this->matches('create sql stmt')], + [$this->stringContains('INSERT INTO')] + ) + ->will( + $this->onConsecutiveCalls( + $this->throwException( + $this->createMock(TableNotFoundException::class) + ), + 1, + 1 + ) + ); + + $conn->method('isTransactionActive') + ->willReturn(false); + + $platform = $this->createMock(AbstractPlatform::class); + $platform->method('getCreateTableSQL') + ->willReturn(['create sql stmt']); + + $conn->method('getDatabasePlatform') + ->willReturn($platform); + + $store = new DoctrineDbalStore($conn); + + $key = new Key(uniqid(__METHOD__, true)); + + $store->save($key); + } } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy