Skip to content

Commit 9395de9

Browse files
martinssipenkofabpot
authored andcommitted
[Lock] Create tables in transaction only if supported by driver
1 parent 446686a commit 9395de9

File tree

2 files changed

+161
-2
lines changed

2 files changed

+161
-2
lines changed

src/Symfony/Component/Lock/Store/DoctrineDbalStore.php

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,21 @@ public function save(Key $key)
9292
ParameterType::STRING,
9393
]);
9494
} catch (TableNotFoundException $e) {
95-
$this->createTable();
96-
$this->save($key);
95+
if (!$this->conn->isTransactionActive() || $this->platformSupportsTableCreationInTransaction()) {
96+
$this->createTable();
97+
}
98+
99+
try {
100+
$this->conn->executeStatement($sql, [
101+
$this->getHashedKey($key),
102+
$this->getUniqueToken($key),
103+
], [
104+
ParameterType::STRING,
105+
ParameterType::STRING,
106+
]);
107+
} catch (DBALException $e) {
108+
$this->putOffExpiration($key, $this->initialTtl);
109+
}
97110
} catch (DBALException $e) {
98111
// the lock is already acquired. It could be us. Let's try to put off.
99112
$this->putOffExpiration($key, $this->initialTtl);
@@ -235,4 +248,23 @@ private function getCurrentTimestampStatement(): string
235248
return (string) time();
236249
}
237250
}
251+
252+
/**
253+
* Checks wether current platform supports table creation within transaction.
254+
*/
255+
private function platformSupportsTableCreationInTransaction(): bool
256+
{
257+
$platform = $this->conn->getDatabasePlatform();
258+
259+
switch (true) {
260+
case $platform instanceof \Doctrine\DBAL\Platforms\PostgreSQLPlatform:
261+
case $platform instanceof \Doctrine\DBAL\Platforms\PostgreSQL94Platform:
262+
case $platform instanceof \Doctrine\DBAL\Platforms\SqlitePlatform:
263+
case $platform instanceof \Doctrine\DBAL\Platforms\SQLServerPlatform:
264+
case $platform instanceof \Doctrine\DBAL\Platforms\SQLServer2012Platform:
265+
return true;
266+
default:
267+
return false;
268+
}
269+
}
238270
}

src/Symfony/Component/Lock/Tests/Store/DoctrineDbalStoreTest.php

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,16 @@
1111

1212
namespace Symfony\Component\Lock\Tests\Store;
1313

14+
use Doctrine\DBAL\Connection;
1415
use Doctrine\DBAL\DriverManager;
16+
use Doctrine\DBAL\Exception\TableNotFoundException;
17+
use Doctrine\DBAL\Platforms\AbstractPlatform;
1518
use Symfony\Component\Lock\Key;
1619
use Symfony\Component\Lock\PersistingStoreInterface;
1720
use Symfony\Component\Lock\Store\DoctrineDbalStore;
1821

22+
class_exists(\Doctrine\DBAL\Platforms\PostgreSqlPlatform::class);
23+
1924
/**
2025
* @author Jérémy Derussé <jeremy@derusse.com>
2126
*
@@ -87,4 +92,126 @@ public function provideDsn()
8792
yield ['sqlite3:///'.$dbFile.'3', $dbFile.'3'];
8893
yield ['sqlite://localhost/:memory:'];
8994
}
95+
96+
/**
97+
* @dataProvider providePlatforms
98+
*/
99+
public function testCreatesTableInTransaction(string $platform)
100+
{
101+
$conn = $this->createMock(Connection::class);
102+
$conn->expects($this->exactly(3))
103+
->method('executeStatement')
104+
->withConsecutive(
105+
[$this->stringContains('INSERT INTO')],
106+
[$this->matches('create sql stmt')],
107+
[$this->stringContains('INSERT INTO')]
108+
)
109+
->will(
110+
$this->onConsecutiveCalls(
111+
$this->throwException(
112+
$this->createMock(TableNotFoundException::class)
113+
),
114+
1,
115+
1
116+
)
117+
);
118+
119+
$conn->method('isTransactionActive')
120+
->willReturn(true);
121+
122+
$platform = $this->createMock($platform);
123+
$platform->method('getCreateTableSQL')
124+
->willReturn(['create sql stmt']);
125+
126+
$conn->method('getDatabasePlatform')
127+
->willReturn($platform);
128+
129+
$store = new DoctrineDbalStore($conn);
130+
131+
$key = new Key(uniqid(__METHOD__, true));
132+
133+
$store->save($key);
134+
}
135+
136+
public function providePlatforms()
137+
{
138+
yield [\Doctrine\DBAL\Platforms\PostgreSQLPlatform::class];
139+
yield [\Doctrine\DBAL\Platforms\PostgreSQL94Platform::class];
140+
yield [\Doctrine\DBAL\Platforms\SqlitePlatform::class];
141+
yield [\Doctrine\DBAL\Platforms\SQLServerPlatform::class];
142+
yield [\Doctrine\DBAL\Platforms\SQLServer2012Platform::class];
143+
}
144+
145+
public function testTableCreationInTransactionNotSupported()
146+
{
147+
$conn = $this->createMock(Connection::class);
148+
$conn->expects($this->exactly(2))
149+
->method('executeStatement')
150+
->withConsecutive(
151+
[$this->stringContains('INSERT INTO')],
152+
[$this->stringContains('INSERT INTO')]
153+
)
154+
->will(
155+
$this->onConsecutiveCalls(
156+
$this->throwException(
157+
$this->createMock(TableNotFoundException::class)
158+
),
159+
1,
160+
1
161+
)
162+
);
163+
164+
$conn->method('isTransactionActive')
165+
->willReturn(true);
166+
167+
$platform = $this->createMock(AbstractPlatform::class);
168+
$platform->method('getCreateTableSQL')
169+
->willReturn(['create sql stmt']);
170+
171+
$conn->expects($this->exactly(2))
172+
->method('getDatabasePlatform');
173+
174+
$store = new DoctrineDbalStore($conn);
175+
176+
$key = new Key(uniqid(__METHOD__, true));
177+
178+
$store->save($key);
179+
}
180+
181+
public function testCreatesTableOutsideTransaction()
182+
{
183+
$conn = $this->createMock(Connection::class);
184+
$conn->expects($this->exactly(3))
185+
->method('executeStatement')
186+
->withConsecutive(
187+
[$this->stringContains('INSERT INTO')],
188+
[$this->matches('create sql stmt')],
189+
[$this->stringContains('INSERT INTO')]
190+
)
191+
->will(
192+
$this->onConsecutiveCalls(
193+
$this->throwException(
194+
$this->createMock(TableNotFoundException::class)
195+
),
196+
1,
197+
1
198+
)
199+
);
200+
201+
$conn->method('isTransactionActive')
202+
->willReturn(false);
203+
204+
$platform = $this->createMock(AbstractPlatform::class);
205+
$platform->method('getCreateTableSQL')
206+
->willReturn(['create sql stmt']);
207+
208+
$conn->method('getDatabasePlatform')
209+
->willReturn($platform);
210+
211+
$store = new DoctrineDbalStore($conn);
212+
213+
$key = new Key(uniqid(__METHOD__, true));
214+
215+
$store->save($key);
216+
}
90217
}

0 commit comments

Comments
 (0)
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