packages/ztd-query-sqlite/tests/Unit/Sql/Attach/SqliteInMemoryAttachStatementTest.php
1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\Sql\Attach;
6
7use Generator;
8use PHPUnit\Framework\Attributes\CoversClass;
9use PHPUnit\Framework\Attributes\DataProvider;
10use PHPUnit\Framework\Attributes\UsesClass;
11use PHPUnit\Framework\TestCase;
12use ZtdQuery\Platform\Sqlite\Sql\Attach\SqliteInMemoryAttachStatement;
13
14#[CoversClass(SqliteInMemoryAttachStatement::class)]
15#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\Attach\AttachTokens::class)]
16#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\SqliteLexerProfile::class)]
17final class SqliteInMemoryAttachStatementTest extends TestCase
18{
19 public function testIsSafeAcceptsOnlyLiteralInMemoryAttachments(): void
20 {
21 self::assertTrue(SqliteInMemoryAttachStatement::isSafe("ATTACH ':memory:' AS db2"));
22 self::assertTrue(SqliteInMemoryAttachStatement::isSafe("ATTACH DATABASE ':memory:' AS \"db2\";"));
23 self::assertTrue(SqliteInMemoryAttachStatement::isSafe("ATTACH /* safe */ ':memory:' AS [db2]"));
24 self::assertTrue(SqliteInMemoryAttachStatement::isSafe("attach ':memory:' as `db2`"));
25 self::assertTrue(SqliteInMemoryAttachStatement::isSafe("ATTACH ':memory:' AS \"db alias\""));
26 }
27
28 public function testRejectsPersistentOrDynamicAttachments(): void
29 {
30 self::assertFalse(SqliteInMemoryAttachStatement::isSafe("ATTACH 'test.sqlite' AS db2"));
31 self::assertFalse(SqliteInMemoryAttachStatement::isSafe("ATTACH ('file:' || :name) AS db2"));
32 self::assertFalse(SqliteInMemoryAttachStatement::isSafe("ATTACH ':memory:' AS db2; DELETE FROM users"));
33 self::assertFalse(SqliteInMemoryAttachStatement::isSafe('DETACH DATABASE db2'));
34 }
35
36 #[DataProvider('providerIncompleteOrExtendedAttachmentShapes')]
37 public function testRejectsIncompleteOrExtendedAttachmentShapes(string $sql): void
38 {
39 self::assertFalse(SqliteInMemoryAttachStatement::isSafe($sql));
40 }
41
42 /**
43 * @return Generator<string, array{string}>
44 */
45 public static function providerIncompleteOrExtendedAttachmentShapes(): Generator
46 {
47 yield 'empty' => [''];
48 yield 'keyword only' => ['ATTACH'];
49 yield 'path without alias' => ["ATTACH ':memory:'"];
50 yield 'missing alias' => ["ATTACH ':memory:' AS"];
51 yield 'database missing alias' => ["ATTACH DATABASE ':memory:' AS"];
52 yield 'missing as' => ["ATTACH ':memory:' db2"];
53 yield 'extra token' => ["ATTACH ':memory:' AS db2 extra"];
54 yield 'trailing closing parenthesis' => ["ATTACH ':memory:' AS db2 )"];
55 yield 'string alias' => ["ATTACH ':memory:' AS 'db2'"];
56 yield 'empty bracket alias' => ["ATTACH ':memory:' AS []"];
57 yield 'unterminated bracket alias' => ["ATTACH ':memory:' AS [db2"];
58 yield 'numeric bracket alias' => ["ATTACH ':memory:' AS [42]"];
59 yield 'second statement' => ["ATTACH ':memory:' AS db2; ATTACH ':memory:' AS db3"];
60 }
61}
62