packages/sql-parser/tests/Unit/MySql/Lexer/QuotedScannerTest.php

1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\MySql\Lexer;
6
7use PHPUnit\Framework\Attributes\CoversClass;
8use PHPUnit\Framework\Attributes\Small;
9use PHPUnit\Framework\Attributes\UsesClass;
10use PHPUnit\Framework\TestCase;
11use SqlParser\Lexer\Cursor;
12use SqlParser\Lexer\Lexeme;
13use SqlParser\Lexer\LexicalException;
14use SqlParser\MySql\Lexer\KeywordTable;
15use SqlParser\MySql\Lexer\QuotedScanner;
16use SqlParser\MySql\Lexer\Scan;
17use SqlParser\MySql\MySqlVersion;
18use SqlParser\MySql\SqlMode;
19
20#[CoversClass(QuotedScanner::class)]
21#[UsesClass(Cursor::class)]
22#[UsesClass(Lexeme::class)]
23#[UsesClass(LexicalException::class)]
24#[UsesClass(KeywordTable::class)]
25#[UsesClass(MySqlVersion::class)]
26#[UsesClass(Scan::class)]
27#[UsesClass(SqlMode::class)]
28#[UsesClass(\SqlParser\Lexer\SourcePosition::class)]
29#[UsesClass(\SqlParser\Resource\SqlVersion::class)]
30#[UsesClass(\SqlParser\Resource\VersionRegistry::class)]
31#[UsesClass(\SqlParser\Lexer\SourceException::class)]
32#[Small]
33final class QuotedScannerTest extends TestCase
34{
35    public function testScanReadsStringsAndIdentifiers(): void
36    {
37        $scanner = new QuotedScanner();
38        $version = MySqlVersion::resolve();
39
40        self::assertSame('TEXT_STRING', $scanner->scan(new Scan(new Cursor("'it''s\\'x' y"), new KeywordTable([], []), new SqlMode(), $version))?->name);
41        self::assertSame('TEXT_STRING', $scanner->scan(new Scan(new Cursor('"x"'), new KeywordTable([], []), new SqlMode(), $version))?->name);
42        self::assertSame('IDENT_QUOTED', $scanner->scan(new Scan(new Cursor('"x"'), new KeywordTable([], []), new SqlMode(ansiQuotes: true), $version))?->name);
43        self::assertSame('`a``b`', $scanner->scan(new Scan(new Cursor('`a``b` c'), new KeywordTable([], []), new SqlMode(), $version))?->text);
44        self::assertNull($scanner->scan(new Scan(new Cursor('abc'), new KeywordTable([], []), new SqlMode(), $version)));
45    }
46
47    public function testScanReadsPrefixedLiterals(): void
48    {
49        $scanner = new QuotedScanner();
50        $version = MySqlVersion::resolve();
51
52        self::assertSame('NCHAR_STRING', $scanner->scan(new Scan(new Cursor("N'x'"), new KeywordTable([], []), new SqlMode(), $version))?->name);
53        self::assertSame('HEX_NUM', $scanner->scan(new Scan(new Cursor("x'ff'"), new KeywordTable([], []), new SqlMode(), $version))?->name);
54        self::assertSame('BIN_NUM', $scanner->scan(new Scan(new Cursor("B'01'"), new KeywordTable([], []), new SqlMode(), $version))?->name);
55        self::assertNull($scanner->scan(new Scan(new Cursor('xyz'), new KeywordTable([], []), new SqlMode(), $version)));
56    }
57
58    public function testScanRejectsAnOddHexadecimalLiteral(): void
59    {
60        $this->expectException(LexicalException::class);
61
62        (new QuotedScanner())->scan(new Scan(new Cursor("x'f'"), new KeywordTable([], []), new SqlMode(), MySqlVersion::resolve()));
63    }
64
65    public function testString(): void
66    {
67        $scan = new Scan(new Cursor("'a\\'b'"), new KeywordTable([], []), new SqlMode(noBackslashEscapes: true), MySqlVersion::resolve());
68
69        self::assertSame("'a\\'", (new QuotedScanner())->string($scan, 'TEXT_STRING', "'")->text);
70    }
71
72    public function testStringRejectsAnUnterminatedString(): void
73    {
74        $this->expectException(LexicalException::class);
75
76        (new QuotedScanner())->string(new Scan(new Cursor("'open"), new KeywordTable([], []), new SqlMode(), MySqlVersion::resolve()), 'TEXT_STRING', "'");
77    }
78
79    public function testQuotedIdentifier(): void
80    {
81        $scan = new Scan(new Cursor('`x` y'), new KeywordTable([], []), new SqlMode(), MySqlVersion::resolve());
82
83        self::assertSame('IDENT_QUOTED', (new QuotedScanner())->quotedIdentifier($scan, '`')->name);
84    }
85
86    public function testQuotedIdentifierRejectsAnUnterminatedIdentifier(): void
87    {
88        $this->expectException(LexicalException::class);
89
90        (new QuotedScanner())->quotedIdentifier(new Scan(new Cursor('`open'), new KeywordTable([], []), new SqlMode(), MySqlVersion::resolve()), '`');
91    }
92}
93