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