Skip to content

Commit a6d7d82

Browse files
committed
[Cache][Lock] Fix PDO store not creating table + add tests
1 parent 23146b4 commit a6d7d82

File tree

3 files changed

+91
-16
lines changed

3 files changed

+91
-16
lines changed

Store/PdoStore.php

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ public function save(Key $key)
115115
try {
116116
$stmt = $conn->prepare($sql);
117117
} catch (\PDOException $e) {
118-
if (!$conn->inTransaction() || \in_array($this->driver, ['pgsql', 'sqlite', 'sqlsrv'], true)) {
118+
if ($this->isTableMissing($e) && (!$conn->inTransaction() || \in_array($this->driver, ['pgsql', 'sqlite', 'sqlsrv'], true))) {
119119
$this->createTable();
120120
}
121121
$stmt = $conn->prepare($sql);
@@ -127,8 +127,18 @@ public function save(Key $key)
127127
try {
128128
$stmt->execute();
129129
} catch (\PDOException $e) {
130-
// the lock is already acquired. It could be us. Let's try to put off.
131-
$this->putOffExpiration($key, $this->initialTtl);
130+
if ($this->isTableMissing($e) && (!$conn->inTransaction() || \in_array($this->driver, ['pgsql', 'sqlite', 'sqlsrv'], true))) {
131+
$this->createTable();
132+
133+
try {
134+
$stmt->execute();
135+
} catch (\PDOException $e) {
136+
$this->putOffExpiration($key, $this->initialTtl);
137+
}
138+
} else {
139+
// the lock is already acquired. It could be us. Let's try to put off.
140+
$this->putOffExpiration($key, $this->initialTtl);
141+
}
132142
}
133143

134144
$this->randomlyPrune();
@@ -316,4 +326,21 @@ private function getCurrentTimestampStatement(): string
316326
return (string) time();
317327
}
318328
}
329+
330+
private function isTableMissing(\PDOException $exception): bool
331+
{
332+
$driver = $this->getDriver();
333+
$code = $exception->getCode();
334+
335+
switch (true) {
336+
case 'pgsql' === $driver && '42P01' === $code:
337+
case 'sqlite' === $driver && str_contains($exception->getMessage(), 'no such table:'):
338+
case 'oci' === $driver && 942 === $code:
339+
case 'sqlsrv' === $driver && 208 === $code:
340+
case 'mysql' === $driver && 1146 === $code:
341+
return true;
342+
default:
343+
return false;
344+
}
345+
}
319346
}

Tests/Store/DoctrineDbalStoreTest.php

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,9 @@ public function testAbortAfterExpiration()
7979
}
8080

8181
/**
82-
* @dataProvider provideDsn
82+
* @dataProvider provideDsnWithSQLite
8383
*/
84-
public function testDsn(string $dsn, string $file = null)
84+
public function testDsnWithSQLite(string $dsn, string $file = null)
8585
{
8686
$key = new Key(uniqid(__METHOD__, true));
8787

@@ -97,12 +97,36 @@ public function testDsn(string $dsn, string $file = null)
9797
}
9898
}
9999

100-
public static function provideDsn()
100+
public static function provideDsnWithSQLite()
101101
{
102102
$dbFile = tempnam(sys_get_temp_dir(), 'sf_sqlite_cache');
103-
yield ['sqlite://localhost/'.$dbFile.'1', $dbFile.'1'];
104-
yield ['sqlite3:///'.$dbFile.'3', $dbFile.'3'];
105-
yield ['sqlite://localhost/:memory:'];
103+
yield 'SQLite file' => ['sqlite://localhost/'.$dbFile.'1', $dbFile.'1'];
104+
yield 'SQLite3 file' => ['sqlite3:///'.$dbFile.'3', $dbFile.'3'];
105+
yield 'SQLite in memory' => ['sqlite://localhost/:memory:'];
106+
}
107+
108+
/**
109+
* @requires extension pdo_pgsql
110+
*
111+
* @group integration
112+
*/
113+
public function testDsnWithPostgreSQL()
114+
{
115+
if (!$host = getenv('POSTGRES_HOST')) {
116+
$this->markTestSkipped('Missing POSTGRES_HOST env variable');
117+
}
118+
119+
$key = new Key(uniqid(__METHOD__, true));
120+
121+
try {
122+
$store = new DoctrineDbalStore('pgsql://postgres:password@'.$host);
123+
124+
$store->save($key);
125+
$this->assertTrue($store->exists($key));
126+
} finally {
127+
$pdo = new \PDO('pgsql:host='.$host.';user=postgres;password=password');
128+
$pdo->exec('DROP TABLE IF EXISTS lock_keys');
129+
}
106130
}
107131

108132
/**

Tests/Store/PdoStoreTest.php

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@
2020
* @author Jérémy Derussé <jeremy@derusse.com>
2121
*
2222
* @requires extension pdo_sqlite
23-
*
24-
* @group integration
2523
*/
2624
class PdoStoreTest extends AbstractStoreTestCase
2725
{
@@ -78,9 +76,9 @@ public function testInvalidTtlConstruct()
7876
}
7977

8078
/**
81-
* @dataProvider provideDsn
79+
* @dataProvider provideDsnWithSQLite
8280
*/
83-
public function testDsn(string $dsn, string $file = null)
81+
public function testDsnWithSQLite(string $dsn, string $file = null)
8482
{
8583
$key = new Key(uniqid(__METHOD__, true));
8684

@@ -96,10 +94,36 @@ public function testDsn(string $dsn, string $file = null)
9694
}
9795
}
9896

99-
public static function provideDsn()
97+
public static function provideDsnWithSQLite()
10098
{
10199
$dbFile = tempnam(sys_get_temp_dir(), 'sf_sqlite_cache');
102-
yield ['sqlite:'.$dbFile.'2', $dbFile.'2'];
103-
yield ['sqlite::memory:'];
100+
yield 'SQLite file' => ['sqlite:'.$dbFile.'2', $dbFile.'2'];
101+
yield 'SQLite in memory' => ['sqlite::memory:'];
102+
}
103+
104+
/**
105+
* @requires extension pdo_pgsql
106+
*
107+
* @group integration
108+
*/
109+
public function testDsnWithPostgreSQL()
110+
{
111+
if (!$host = getenv('POSTGRES_HOST')) {
112+
$this->markTestSkipped('Missing POSTGRES_HOST env variable');
113+
}
114+
115+
$key = new Key(uniqid(__METHOD__, true));
116+
117+
$dsn = 'pgsql:host='.$host.';user=postgres;password=password';
118+
119+
try {
120+
$store = new PdoStore($dsn);
121+
122+
$store->save($key);
123+
$this->assertTrue($store->exists($key));
124+
} finally {
125+
$pdo = new \PDO($dsn);
126+
$pdo->exec('DROP TABLE IF EXISTS lock_keys');
127+
}
104128
}
105129
}

0 commit comments

Comments
 (0)