packages/ztd-query-sqlite/tests/Unit/Sql/Statement/StatementStructureTest.php
1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\Sql\Statement;
6
7use PHPUnit\Framework\Attributes\CoversClass;
8use PHPUnit\Framework\Attributes\UsesClass;
9use PHPUnit\Framework\TestCase;
10use ZtdQuery\Platform\Sqlite\Sql\Statement\StatementStructure;
11
12#[CoversClass(StatementStructure::class)]
13#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\Lexing\LiteralMasker::class)]
14#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\Lexing\QuotedSpan::class)]
15#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\Relation\RelationSourceParser::class)]
16#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\SqliteLexerProfile::class)]
17#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\SqliteLexicalMasker::class)]
18#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\Relation\SqliteSelectRelationParser::class)]
19final class StatementStructureTest extends TestCase
20{
21 public function testSplitStatementsSplitSingleStatement(): void
22 {
23 $parser = new StatementStructure();
24 $result = $parser->splitStatements('SELECT * FROM users');
25 self::assertCount(1, $result);
26 self::assertSame('SELECT * FROM users', $result[0]);
27 }
28
29 public function testSplitStatementsSplitMultipleStatements(): void
30 {
31 $parser = new StatementStructure();
32 $result = $parser->splitStatements('SELECT 1; SELECT 2');
33 self::assertCount(2, $result);
34 self::assertSame('SELECT 1', $result[0]);
35 self::assertSame('SELECT 2', $result[1]);
36 }
37
38 public function testSplitStatementsIgnoresSemicolonInString(): void
39 {
40 $parser = new StatementStructure();
41 $result = $parser->splitStatements("SELECT 'a;b' FROM t");
42 self::assertCount(1, $result);
43 }
44
45 public function testSplitStatementsIgnoresSemicolonInParentheses(): void
46 {
47 $parser = new StatementStructure();
48 $result = $parser->splitStatements('SELECT (1;2) FROM t');
49 self::assertCount(1, $result);
50 }
51
52 public function testExtractSelectTables(): void
53 {
54 $parser = new StatementStructure();
55 $tables = $parser->extractSelectTables('SELECT * FROM users');
56 self::assertSame(['users'], $tables);
57 }
58
59 public function testExtractSelectTablesWithJoin(): void
60 {
61 $parser = new StatementStructure();
62 $tables = $parser->extractSelectTables('SELECT * FROM users JOIN orders ON users.id = orders.user_id');
63 self::assertContains('users', $tables);
64 self::assertContains('orders', $tables);
65 }
66
67 public function testExtractSelectTablesDeduplicatesAndReindexesInFirstSeenOrder(): void
68 {
69 $parser = new StatementStructure();
70
71 self::assertSame(
72 ['users', 'orders'],
73 $parser->extractSelectTables(
74 'SELECT * FROM users UNION SELECT * FROM users UNION SELECT * FROM orders'
75 ),
76 );
77 }
78
79 public function testExtractSelectTablesExtractSelectTableAfterBlockComment(): void
80 {
81 $parser = new StatementStructure();
82 self::assertSame(['my_table'], $parser->extractSelectTables('SELECT * FROM /* table */ my_table'));
83 }
84
85 public function testExtractSelectTablesExtractSelectTableIgnoresKeywordsInsideLineComment(): void
86 {
87 $parser = new StatementStructure();
88 $sql = "-- SELECT * FROM other_table WHERE DELETE UPDATE INSERT\nSELECT * FROM items ORDER BY id";
89 self::assertSame(['items'], $parser->extractSelectTables($sql));
90 }
91
92 public function testExtractSelectTablesExtractSelectTableIgnoresFromInsideStringLiteral(): void
93 {
94 $parser = new StatementStructure();
95 $sql = "SELECT id, 'from other_table' AS label FROM items LIMIT 1";
96 self::assertSame(['items'], $parser->extractSelectTables($sql));
97 }
98
99 public function testExtractSelectTablesExtractSelectTableIgnoresJoinInsideStringLiteral(): void
100 {
101 $parser = new StatementStructure();
102 $sql = "SELECT id, 'JOIN other_table' AS label FROM items LIMIT 1";
103 self::assertSame(['items'], $parser->extractSelectTables($sql));
104 }
105
106 public function testSplitStatementsIgnoresSemicolonInDoubleQuote(): void
107 {
108 $parser = new StatementStructure();
109 $result = $parser->splitStatements('SELECT "a;b" FROM t');
110 self::assertCount(1, $result);
111 }
112
113 public function testSplitStatementsHandlesLineComment(): void
114 {
115 $parser = new StatementStructure();
116 $result = $parser->splitStatements("SELECT 1; -- comment\nSELECT 2");
117 self::assertCount(2, $result);
118 }
119
120 public function testSplitStatementsHandlesBlockComment(): void
121 {
122 $parser = new StatementStructure();
123 $result = $parser->splitStatements('SELECT 1; /* comment */ SELECT 2');
124 self::assertCount(2, $result);
125 }
126
127 public function testSplitStatementsHandlesUnclosedBlockComment(): void
128 {
129 $parser = new StatementStructure();
130 $result = $parser->splitStatements('SELECT 1 /* unclosed comment');
131 self::assertCount(1, $result);
132 }
133
134 public function testSplitStatementsHandlesLineCommentAtEnd(): void
135 {
136 $parser = new StatementStructure();
137 $result = $parser->splitStatements('SELECT 1 -- trailing comment');
138 self::assertCount(1, $result);
139 }
140
141 public function testSplitStatementsHandlesEscapedSingleQuote(): void
142 {
143 $parser = new StatementStructure();
144 $result = $parser->splitStatements("SELECT 'it''s'; SELECT 2");
145 self::assertCount(2, $result);
146 }
147
148 public function testSplitStatementsHandlesEscapedDoubleQuote(): void
149 {
150 $parser = new StatementStructure();
151 $result = $parser->splitStatements('SELECT "col""name"; SELECT 2');
152 self::assertCount(2, $result);
153 }
154
155 public function testSplitStatementsHandlesNestedParens(): void
156 {
157 $parser = new StatementStructure();
158 $result = $parser->splitStatements('SELECT (1 + (2 + 3)); SELECT 2');
159 self::assertCount(2, $result);
160 }
161
162 public function testSplitStatementsEmptyInput(): void
163 {
164 $parser = new StatementStructure();
165 $result = $parser->splitStatements('');
166 self::assertSame([], $result);
167 }
168
169 public function testSplitStatementsTrailingSemicolon(): void
170 {
171 $parser = new StatementStructure();
172 $result = $parser->splitStatements('SELECT 1;');
173 self::assertCount(1, $result);
174 }
175
176 public function testExtractSelectTablesWithAlias(): void
177 {
178 $parser = new StatementStructure();
179 $tables = $parser->extractSelectTables('SELECT * FROM users u');
180 self::assertContains('users', $tables);
181 }
182
183 public function testExtractSelectTablesWithAsAlias(): void
184 {
185 $parser = new StatementStructure();
186 $tables = $parser->extractSelectTables('SELECT * FROM users AS u');
187 self::assertContains('users', $tables);
188 }
189
190 public function testExtractSelectTablesMultiple(): void
191 {
192 $parser = new StatementStructure();
193 $tables = $parser->extractSelectTables('SELECT * FROM users, orders');
194 self::assertContains('users', $tables);
195 self::assertContains('orders', $tables);
196 }
197
198 public function testExtractSelectTablesWithWhere(): void
199 {
200 $parser = new StatementStructure();
201 $tables = $parser->extractSelectTables('SELECT * FROM users WHERE id = 1');
202 self::assertSame(['users'], $tables);
203 }
204
205 public function testExtractSelectTablesWithGroupBy(): void
206 {
207 $parser = new StatementStructure();
208 $tables = $parser->extractSelectTables('SELECT * FROM users GROUP BY name');
209 self::assertSame(['users'], $tables);
210 }
211
212 public function testExtractSelectTablesWithOrderBy(): void
213 {
214 $parser = new StatementStructure();
215 $tables = $parser->extractSelectTables('SELECT * FROM users ORDER BY id');
216 self::assertSame(['users'], $tables);
217 }
218
219 public function testExtractSelectTablesWithLimit(): void
220 {
221 $parser = new StatementStructure();
222 $tables = $parser->extractSelectTables('SELECT * FROM users LIMIT 10');
223 self::assertSame(['users'], $tables);
224 }
225
226 public function testExtractSelectTablesQuoted(): void
227 {
228 $parser = new StatementStructure();
229 $tables = $parser->extractSelectTables('SELECT * FROM "users"');
230 self::assertContains('users', $tables);
231 }
232
233 public function testExtractSelectTablesNoFrom(): void
234 {
235 $parser = new StatementStructure();
236 $tables = $parser->extractSelectTables('SELECT 1');
237 self::assertSame([], $tables);
238 }
239
240 public function testExtractSelectTablesWithLeftJoin(): void
241 {
242 $parser = new StatementStructure();
243 $tables = $parser->extractSelectTables('SELECT * FROM users LEFT JOIN orders ON users.id = orders.user_id');
244 self::assertContains('users', $tables);
245 self::assertContains('orders', $tables);
246 }
247
248 public function testExtractSelectTablesWithInnerJoin(): void
249 {
250 $parser = new StatementStructure();
251 $tables = $parser->extractSelectTables('SELECT * FROM users INNER JOIN orders ON users.id = orders.user_id');
252 self::assertContains('users', $tables);
253 self::assertContains('orders', $tables);
254 }
255
256 public function testExtractSelectTablesWithHaving(): void
257 {
258 $parser = new StatementStructure();
259 $tables = $parser->extractSelectTables('SELECT * FROM users HAVING count(*) > 1');
260 self::assertSame(['users'], $tables);
261 }
262
263 public function testExtractSelectTablesWithUnion(): void
264 {
265 $parser = new StatementStructure();
266 $tables = $parser->extractSelectTables('SELECT * FROM users UNION SELECT * FROM admins');
267 self::assertContains('users', $tables);
268 }
269
270 public function testSplitStatementsEmpty(): void
271 {
272 $parser = new StatementStructure();
273 $result = $parser->splitStatements(' ; ; ');
274 self::assertSame([], $result);
275 }
276
277 public function testExtractSelectTablesEmptyExpr(): void
278 {
279 $parser = new StatementStructure();
280 $tables = $parser->extractSelectTables('SELECT * FROM , users');
281 self::assertContains('users', $tables);
282 }
283
284 public function testExtractSelectTablesLowercase(): void
285 {
286 $parser = new StatementStructure();
287 $tables = $parser->extractSelectTables('select * from users where id = 1');
288 self::assertSame(['users'], $tables);
289 }
290
291 public function testExtractSelectTablesWithJoinLowercase(): void
292 {
293 $parser = new StatementStructure();
294 $tables = $parser->extractSelectTables('select * from users join orders on users.id = orders.user_id');
295 self::assertContains('users', $tables);
296 self::assertContains('orders', $tables);
297 }
298
299 public function testExtractSelectTablesWithGroupByLowercase(): void
300 {
301 $parser = new StatementStructure();
302 $tables = $parser->extractSelectTables('select * from users group by name');
303 self::assertSame(['users'], $tables);
304 }
305
306 public function testExtractSelectTablesWithOrderByLowercase(): void
307 {
308 $parser = new StatementStructure();
309 $tables = $parser->extractSelectTables('select * from users order by id');
310 self::assertSame(['users'], $tables);
311 }
312
313 public function testExtractSelectTablesWithLimitLowercase(): void
314 {
315 $parser = new StatementStructure();
316 $tables = $parser->extractSelectTables('select * from users limit 10');
317 self::assertSame(['users'], $tables);
318 }
319
320 public function testExtractSelectTablesLeftJoinLowercase(): void
321 {
322 $parser = new StatementStructure();
323 $tables = $parser->extractSelectTables('select * from users left join orders on users.id = orders.user_id');
324 self::assertContains('users', $tables);
325 self::assertContains('orders', $tables);
326 }
327
328 public function testExtractSelectTablesCrossJoin(): void
329 {
330 $parser = new StatementStructure();
331 $tables = $parser->extractSelectTables('SELECT * FROM users CROSS JOIN orders');
332 self::assertContains('users', $tables);
333 }
334
335 public function testExtractSelectTablesNaturalJoin(): void
336 {
337 $parser = new StatementStructure();
338 $tables = $parser->extractSelectTables('SELECT * FROM users NATURAL JOIN orders');
339 self::assertContains('users', $tables);
340 }
341
342 public function testExtractSelectTablesRightJoin(): void
343 {
344 $parser = new StatementStructure();
345 $tables = $parser->extractSelectTables('SELECT * FROM users RIGHT JOIN orders ON 1=1');
346 self::assertContains('users', $tables);
347 self::assertContains('orders', $tables);
348 }
349
350 public function testSplitStatementsWithEscapedSingleQuotes(): void
351 {
352 $parser = new StatementStructure();
353 $result = $parser->splitStatements("SELECT 'it''s'; SELECT 2");
354 self::assertCount(2, $result);
355 self::assertStringContainsString("it''s", $result[0]);
356 self::assertSame('SELECT 2', $result[1]);
357 }
358
359 public function testSplitStatementsWithEscapedDoubleQuotes(): void
360 {
361 $parser = new StatementStructure();
362 $result = $parser->splitStatements('SELECT "a""b"; SELECT 2');
363 self::assertCount(2, $result);
364 self::assertStringContainsString('a""b', $result[0]);
365 self::assertSame('SELECT 2', $result[1]);
366 }
367
368 public function testSplitStatementsWithLineComment(): void
369 {
370 $parser = new StatementStructure();
371 $result = $parser->splitStatements("SELECT 1 -- comment\n; SELECT 2");
372 self::assertCount(2, $result);
373 }
374
375 public function testSplitStatementsWithLineCommentNoNewline(): void
376 {
377 $parser = new StatementStructure();
378 $result = $parser->splitStatements('SELECT 1 -- comment at end');
379 self::assertCount(1, $result);
380 self::assertStringContainsString('SELECT 1', $result[0]);
381 self::assertStringContainsString('-- comment at end', $result[0]);
382 }
383
384 public function testSplitStatementsWithBlockComment(): void
385 {
386 $parser = new StatementStructure();
387 $result = $parser->splitStatements('SELECT /* block */ 1; SELECT 2');
388 self::assertCount(2, $result);
389 self::assertStringContainsString('/* block */', $result[0]);
390 }
391
392 public function testSplitStatementsWithUnclosedBlockComment(): void
393 {
394 $parser = new StatementStructure();
395 $result = $parser->splitStatements('SELECT 1 /* unclosed');
396 self::assertCount(1, $result);
397 self::assertStringContainsString('/* unclosed', $result[0]);
398 }
399
400 public function testSplitStatementsParenthesesNestedDepth(): void
401 {
402 $parser = new StatementStructure();
403 $result = $parser->splitStatements('SELECT (1, (2, 3)); SELECT 4');
404 self::assertCount(2, $result);
405 self::assertStringContainsString('(1, (2, 3))', $result[0]);
406 }
407
408 public function testSplitStatementsClosingParenAtZeroDepth(): void
409 {
410 $parser = new StatementStructure();
411 $result = $parser->splitStatements('SELECT 1) ; SELECT 2');
412 self::assertCount(2, $result);
413 }
414
415 public function testSplitStatementsEmptyStatements(): void
416 {
417 $parser = new StatementStructure();
418 $result = $parser->splitStatements(';;; ');
419 self::assertSame([], $result);
420 }
421
422 public function testSplitStatementsSemicolonInsideParens(): void
423 {
424 $parser = new StatementStructure();
425 $result = $parser->splitStatements("SELECT (';')");
426 self::assertCount(1, $result);
427 }
428
429 public function testExtractSelectTablesWithJoinV2(): void
430 {
431 $parser = new StatementStructure();
432 $tables = $parser->extractSelectTables('SELECT * FROM users JOIN orders ON users.id = orders.user_id');
433 self::assertContains('users', $tables);
434 self::assertContains('orders', $tables);
435 }
436
437 public function testExtractSelectTablesWithAliasV2(): void
438 {
439 $parser = new StatementStructure();
440 $tables = $parser->extractSelectTables('SELECT * FROM users AS u');
441 self::assertContains('users', $tables);
442 }
443
444 public function testExtractSelectTablesWithImplicitAlias(): void
445 {
446 $parser = new StatementStructure();
447 $tables = $parser->extractSelectTables('SELECT * FROM users u');
448 self::assertContains('users', $tables);
449 }
450
451 public function testExtractSelectTablesMultipleV2(): void
452 {
453 $parser = new StatementStructure();
454 $tables = $parser->extractSelectTables('SELECT * FROM users, orders WHERE users.id = orders.user_id');
455 self::assertContains('users', $tables);
456 self::assertContains('orders', $tables);
457 }
458
459 public function testExtractSelectTablesEmptyFrom(): void
460 {
461 $parser = new StatementStructure();
462 $tables = $parser->extractSelectTables('SELECT 1');
463 self::assertSame([], $tables);
464 }
465
466 public function testExtractSelectTablesQuotedTable(): void
467 {
468 $parser = new StatementStructure();
469 $tables = $parser->extractSelectTables('SELECT * FROM "my table"');
470 self::assertNotEmpty($tables);
471 }
472
473 public function testExtractSelectTablesWithGroupByV2(): void
474 {
475 $parser = new StatementStructure();
476 $tables = $parser->extractSelectTables('SELECT name, COUNT(*) FROM users GROUP BY name');
477 self::assertContains('users', $tables);
478 }
479
480 public function testExtractSelectTablesWithHavingV2(): void
481 {
482 $parser = new StatementStructure();
483 $tables = $parser->extractSelectTables('SELECT name, COUNT(*) FROM users HAVING COUNT(*) > 1');
484 self::assertContains('users', $tables);
485 }
486
487 public function testExtractSelectTablesWithOrderByV2(): void
488 {
489 $parser = new StatementStructure();
490 $tables = $parser->extractSelectTables('SELECT * FROM users ORDER BY name');
491 self::assertContains('users', $tables);
492 }
493
494 public function testExtractSelectTablesWithLimitV2(): void
495 {
496 $parser = new StatementStructure();
497 $tables = $parser->extractSelectTables('SELECT * FROM users LIMIT 10');
498 self::assertContains('users', $tables);
499 }
500
501 public function testExtractSelectTablesWithUnionV2(): void
502 {
503 $parser = new StatementStructure();
504 $tables = $parser->extractSelectTables('SELECT * FROM users UNION SELECT * FROM admins');
505 self::assertContains('users', $tables);
506 }
507
508 public function testExtractSelectTablesLeftJoin(): void
509 {
510 $parser = new StatementStructure();
511 $tables = $parser->extractSelectTables('SELECT * FROM users LEFT JOIN orders ON users.id = orders.uid');
512 self::assertContains('orders', $tables);
513 }
514
515 public function testExtractSelectTablesInnerJoin(): void
516 {
517 $parser = new StatementStructure();
518 $tables = $parser->extractSelectTables('SELECT * FROM users INNER JOIN orders ON 1=1');
519 self::assertContains('orders', $tables);
520 }
521
522 public function testExtractSelectTablesCrossJoinV2(): void
523 {
524 $parser = new StatementStructure();
525 $tables = $parser->extractSelectTables('SELECT * FROM users CROSS JOIN orders');
526 self::assertContains('orders', $tables);
527 }
528
529 public function testExtractSelectTablesNaturalJoinV2(): void
530 {
531 $parser = new StatementStructure();
532 $tables = $parser->extractSelectTables('SELECT * FROM users NATURAL JOIN orders');
533 self::assertContains('orders', $tables);
534 }
535
536 public function testSplitStatementsOnlySemicolons(): void
537 {
538 $parser = new StatementStructure();
539 self::assertSame([], $parser->splitStatements(';'));
540 }
541
542 public function testSplitStatementsSingleNoSemicolon(): void
543 {
544 $parser = new StatementStructure();
545 $result = $parser->splitStatements('SELECT 1');
546 self::assertCount(1, $result);
547 self::assertSame('SELECT 1', $result[0]);
548 }
549
550 public function testExtractSelectTablesExtractTableFromExprEmpty(): void
551 {
552 $parser = new StatementStructure();
553 $tables = $parser->extractSelectTables('SELECT * FROM ');
554 self::assertSame([], $tables);
555 }
556
557 public function testExtractSelectTablesWithQuotedJoinTable(): void
558 {
559 $parser = new StatementStructure();
560 $tables = $parser->extractSelectTables('SELECT * FROM t JOIN "other table" ON t.id = "other table".tid');
561 self::assertContains('other table', $tables);
562 }
563
564 public function testExtractSelectTablesJoinEmptyTable(): void
565 {
566 $parser = new StatementStructure();
567 $tables = $parser->extractSelectTables('SELECT * FROM t');
568 self::assertContains('t', $tables);
569 }
570
571 public function testSplitStatementsPreservesDoubleQuoteContent(): void
572 {
573 $parser = new StatementStructure();
574 $result = $parser->splitStatements('SELECT "col;name" FROM t');
575 self::assertCount(1, $result);
576 self::assertStringContainsString('"col;name"', $result[0]);
577 }
578
579 public function testSplitStatementsPreservesSingleQuoteContent(): void
580 {
581 $parser = new StatementStructure();
582 $result = $parser->splitStatements("SELECT 'val;ue' FROM t");
583 self::assertCount(1, $result);
584 self::assertStringContainsString("'val;ue'", $result[0]);
585 }
586
587 public function testExtractSelectTablesCaseInsensitive(): void
588 {
589 $parser = new StatementStructure();
590 $tables = $parser->extractSelectTables('select * from users where id = 1');
591 self::assertContains('users', $tables);
592 }
593
594 public function testExtractSelectTablesExtractTableFromExprAlias(): void
595 {
596 $parser = new StatementStructure();
597 $tables = $parser->extractSelectTables('SELECT * FROM users AS u');
598 self::assertContains('users', $tables);
599 }
600
601 public function testExtractSelectTablesExtractTableFromExprSpaceAlias(): void
602 {
603 $parser = new StatementStructure();
604 $tables = $parser->extractSelectTables('SELECT * FROM users u');
605 self::assertContains('users', $tables);
606 }
607
608 public function testExtractSelectTablesExtractTableFromExprEmptyEntry(): void
609 {
610 $parser = new StatementStructure();
611 $tables = $parser->extractSelectTables('SELECT 1');
612 self::assertSame([], $tables);
613 }
614
615 public function testSplitStatementsDoubleQuoteEscape(): void
616 {
617 $parser = new StatementStructure();
618 $result = $parser->splitStatements('SELECT "col""name"; SELECT 1');
619 self::assertCount(2, $result);
620 }
621
622 public function testSplitStatementsSingleQuoteEscape(): void
623 {
624 $parser = new StatementStructure();
625 $result = $parser->splitStatements("SELECT 'it''s'; SELECT 1");
626 self::assertCount(2, $result);
627 }
628
629 public function testSplitStatementsLineComment(): void
630 {
631 $parser = new StatementStructure();
632 $result = $parser->splitStatements("SELECT 1; -- comment\nSELECT 2");
633 self::assertCount(2, $result);
634 }
635
636 public function testSplitStatementsBlockComment(): void
637 {
638 $parser = new StatementStructure();
639 $result = $parser->splitStatements('SELECT 1; /* block */ SELECT 2');
640 self::assertCount(2, $result);
641 }
642
643 public function testSplitStatementsUnclosedBlockComment(): void
644 {
645 $parser = new StatementStructure();
646 $result = $parser->splitStatements('SELECT 1 /* unclosed block');
647 self::assertCount(1, $result);
648 }
649
650 public function testSplitStatementsUnclosedLineComment(): void
651 {
652 $parser = new StatementStructure();
653 $result = $parser->splitStatements('SELECT 1 -- end');
654 self::assertCount(1, $result);
655 }
656
657 public function testSplitStatementsParenthesizedSubquery(): void
658 {
659 $parser = new StatementStructure();
660 $result = $parser->splitStatements('SELECT (SELECT 1; SELECT 2)');
661 self::assertCount(1, $result);
662 }
663
664 public function testExtractSelectTablesFromMultipleTables(): void
665 {
666 $parser = new StatementStructure();
667 $tables = $parser->extractSelectTables('SELECT * FROM users, orders WHERE users.id = orders.user_id');
668 self::assertContains('users', $tables);
669 self::assertContains('orders', $tables);
670 }
671
672 public function testExtractSelectTablesWithJoinSkipsJoined(): void
673 {
674 $parser = new StatementStructure();
675 $tables = $parser->extractSelectTables('SELECT * FROM users JOIN orders ON users.id = orders.user_id');
676 self::assertContains('users', $tables);
677 }
678
679 public function testExtractSelectTablesReturnsEmptyForSubqueryOnly(): void
680 {
681 $parser = new StatementStructure();
682 $tables = $parser->extractSelectTables('SELECT * FROM (SELECT 1)');
683 self::assertSame([], $tables);
684 }
685
686 public function testExtractSelectTablesMultiline(): void
687 {
688 $parser = new StatementStructure();
689 $tables = $parser->extractSelectTables("SELECT * FROM users,\norders WHERE id = 1");
690 self::assertContains('users', $tables);
691 }
692
693 public function testExtractSelectTablesSelectTablesComeFromSelectScopesOnly(): void
694 {
695 $parser = new StatementStructure();
696 $sql = 'SELECT printf(\'from %s\', name) FROM events WHERE id IN (SELECT event_id FROM archived_events)';
697
698 self::assertSame(['events', 'archived_events'], $parser->extractSelectTables($sql));
699 }
700
701 public function testStatementTailFindsMainStatementAfterWith(): void
702 {
703 $parser = new StatementStructure();
704 self::assertSame('UPDATE users SET id = 2', $parser->statementTail('WITH t AS (SELECT 1) UPDATE users SET id = 2', ['UPDATE']));
705 self::assertSame('SELECT 1', $parser->statementTail('/* hint */ SELECT 1', ['UPDATE']));
706 }
707
708}
709