Skip to content

Commit 95b660b

Browse files
authored
fix(database): throw QueryWasInvalid on database fetch failure (#1371)
1 parent 6bf5c06 commit 95b660b

File tree

6 files changed

+50
-25
lines changed

6 files changed

+50
-25
lines changed

packages/database/src/Config/DatabaseDialect.php

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
namespace Tempest\Database\Config;
66

7-
use PDOException;
7+
use Tempest\Database\Exceptions\QueryWasInvalid;
88

99
enum DatabaseDialect: string
1010
{
@@ -21,12 +21,14 @@ public function tableNotFoundCode(): string
2121
};
2222
}
2323

24-
public function isTableNotFoundError(PDOException $exception): bool
24+
public function isTableNotFoundError(QueryWasInvalid $queryWasInvalid): bool
2525
{
26+
$pdoException = $queryWasInvalid->pdoException;
27+
2628
return match ($this) {
27-
self::MYSQL => $exception->getCode() === '42S02' && str_contains($exception->getMessage(), 'table'),
28-
self::SQLITE => $exception->getCode() === 'HY000' && str_contains($exception->getMessage(), 'table'),
29-
self::POSTGRESQL => $exception->getCode() === '42P01' && str_contains($exception->getMessage(), 'relation'),
29+
self::MYSQL => $pdoException->getCode() === '42S02' && str_contains($pdoException->getMessage(), 'table'),
30+
self::SQLITE => $pdoException->getCode() === 'HY000' && str_contains($pdoException->getMessage(), 'table'),
31+
self::POSTGRESQL => $pdoException->getCode() === '42P01' && str_contains($pdoException->getMessage(), 'relation'),
3032
};
3133
}
3234
}

packages/database/src/Exceptions/QueryWasInvalid.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,12 @@
1111

1212
final class QueryWasInvalid extends Exception
1313
{
14+
public readonly PDOException $pdoException;
15+
1416
public function __construct(Query $query, array $bindings, PDOException $previous)
1517
{
18+
$this->pdoException = $previous;
19+
1620
$message = $previous->getMessage();
1721

1822
$message .= PHP_EOL . PHP_EOL . $query->toSql() . PHP_EOL;

packages/database/src/GenericDatabase.php

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,15 @@ public function fetch(BuildsQuery|Query $query): array
8282

8383
$bindings = $this->resolveBindings($query);
8484

85-
$pdoQuery = $this->connection->prepare($query->toSql());
85+
try {
86+
$pdoQuery = $this->connection->prepare($query->toSql());
8687

87-
$pdoQuery->execute($bindings);
88+
$pdoQuery->execute($bindings);
8889

89-
return $pdoQuery->fetchAll(PDO::FETCH_NAMED);
90+
return $pdoQuery->fetchAll(PDO::FETCH_NAMED);
91+
} catch (PDOException $pdoException) {
92+
throw new QueryWasInvalid($query, $bindings, $pdoException);
93+
}
9094
}
9195

9296
public function fetchFirst(BuildsQuery|Query $query): ?array

packages/database/src/Migrations/MigrationManager.php

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
namespace Tempest\Database\Migrations;
66

7-
use PDOException;
87
use Tempest\Container\Container;
98
use Tempest\Database\Builder\ModelDefinition;
109
use Tempest\Database\Config\DatabaseDialect;
@@ -48,12 +47,12 @@ public function up(): void
4847
{
4948
try {
5049
$existingMigrations = Migration::select()->onDatabase($this->onDatabase)->all();
51-
} catch (PDOException $pdoException) {
52-
if ($this->dialect->isTableNotFoundError($pdoException)) {
50+
} catch (QueryWasInvalid $queryWasInvalid) {
51+
if ($this->dialect->isTableNotFoundError($queryWasInvalid)) {
5352
$this->executeUp(new CreateMigrationsTable());
5453
$existingMigrations = Migration::select()->onDatabase($this->onDatabase)->all();
5554
} else {
56-
throw $pdoException;
55+
throw $queryWasInvalid;
5756
}
5857
}
5958

@@ -75,9 +74,9 @@ public function down(): void
7574
{
7675
try {
7776
$existingMigrations = Migration::select()->onDatabase($this->onDatabase)->all();
78-
} catch (PDOException $pdoException) {
79-
if (! $this->dialect->isTableNotFoundError($pdoException)) {
80-
throw $pdoException;
77+
} catch (QueryWasInvalid $queryWasInvalid) {
78+
if (! $this->dialect->isTableNotFoundError($queryWasInvalid)) {
79+
throw $queryWasInvalid;
8180
}
8281

8382
event(new MigrationFailed(
@@ -132,7 +131,7 @@ public function rehashAll(): void
132131
$existingMigrations = Migration::select()
133132
->onDatabase($this->onDatabase)
134133
->all();
135-
} catch (PDOException) {
134+
} catch (QueryWasInvalid) {
136135
return;
137136
}
138137

@@ -163,7 +162,7 @@ public function validate(): void
163162
{
164163
try {
165164
$existingMigrations = Migration::select()->onDatabase($this->onDatabase)->all();
166-
} catch (PDOException) {
165+
} catch (QueryWasInvalid) {
167166
return;
168167
}
169168

@@ -231,10 +230,10 @@ public function executeUp(MigrationInterface $migration): void
231230
hash: $this->getMigrationHash($migration),
232231
),
233232
);
234-
} catch (PDOException $pdoException) {
235-
event(new MigrationFailed($migration->name, $pdoException));
233+
} catch (QueryWasInvalid $queryWasInvalid) {
234+
event(new MigrationFailed($migration->name, $queryWasInvalid));
236235

237-
throw $pdoException;
236+
throw $queryWasInvalid;
238237
}
239238

240239
event(new MigrationMigrated($migration->name));
@@ -264,13 +263,13 @@ public function executeDown(MigrationInterface $migration): void
264263

265264
// Disable foreign key checks
266265
new SetForeignKeyChecksStatement(enable: true)->execute($this->dialect, $this->onDatabase);
267-
} catch (PDOException $pdoException) {
266+
} catch (QueryWasInvalid $queryWasInvalid) {
268267
// Disable foreign key checks
269268
new SetForeignKeyChecksStatement(enable: true)->execute($this->dialect, $this->onDatabase);
270269

271-
event(new MigrationFailed($migration->name, $pdoException));
270+
event(new MigrationFailed($migration->name, $queryWasInvalid));
272271

273-
throw $pdoException;
272+
throw $queryWasInvalid;
274273
}
275274

276275
try {

tests/Integration/Database/GenericDatabaseTest.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use Exception;
88
use Tempest\Database\Database;
9+
use Tempest\Database\Exceptions\QueryWasInvalid;
910
use Tempest\Database\Migrations\CreateMigrationsTable;
1011
use Tempest\Database\Query;
1112
use Tests\Tempest\Fixtures\Migrations\CreateAuthorTable;
@@ -67,4 +68,18 @@ public function test_query_with_semicolons(): void
6768

6869
$this->assertSame(1, query(Publisher::class)->count()->execute());
6970
}
71+
72+
public function test_query_was_invalid_exception_is_thrown_on_fetch(): void
73+
{
74+
$this->assertException(QueryWasInvalid::class, function (): void {
75+
query('books')->select()->orderBy('title DES')->first();
76+
});
77+
}
78+
79+
public function test_query_was_invalid_exception_is_thrown_on_execute(): void
80+
{
81+
$this->assertException(QueryWasInvalid::class, function (): void {
82+
query('books')->update(title: 'Timeline Taxi')->where('title = ?')->execute();
83+
});
84+
}
7085
}

tests/Integration/Database/MultiDatabaseTest.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use Tempest\Database\Config\SQLiteConfig;
1010
use Tempest\Database\Database;
1111
use Tempest\Database\DatabaseInitializer;
12+
use Tempest\Database\Exceptions\QueryWasInvalid;
1213
use Tempest\Database\Id;
1314
use Tempest\Database\Migrations\CreateMigrationsTable;
1415
use Tempest\Database\Migrations\Migration;
@@ -321,7 +322,7 @@ public function test_migrate_fresh_seed_on_selected_database(): void
321322
query(Book::class)->select()->onDatabase('main')->first()->title,
322323
);
323324

324-
$this->assertException(PDOException::class, function (): void {
325+
$this->assertException(QueryWasInvalid::class, function (): void {
325326
query(Book::class)->select()->onDatabase('backup')->first();
326327
});
327328

@@ -343,7 +344,7 @@ private function assertTableExists(string $tableName, string $onDatabase): void
343344
private function assertTableDoesNotExist(string $tableName, string $onDatabase): void
344345
{
345346
$this->assertException(
346-
PDOException::class,
347+
QueryWasInvalid::class,
347348
fn () => query($tableName)->count()->onDatabase($onDatabase)->execute(),
348349
);
349350
}

0 commit comments

Comments
 (0)