packages/sql-faker/tests/Unit/PostgreSql/Generation/Rewrite/Query/SelectOptionsRuleTest.php
1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\SqlFaker\PostgreSql\Generation\Rewrite\Query;
6
7use PHPUnit\Framework\Attributes\CoversClass;
8use PHPUnit\Framework\Attributes\DataProvider;
9use PHPUnit\Framework\Attributes\UsesClass;
10use PHPUnit\Framework\TestCase;
11use SqlFaker\Generation\Token\ProductionOccurrence;
12use SqlFaker\Generation\Token\TerminalOccurrence;
13use SqlFaker\Generation\Token\TerminalSequence;
14use SqlFaker\PostgreSql\Generation\Rewrite\Query\SelectOptionsRule;
15
16#[CoversClass(SelectOptionsRule::class)]
17#[UsesClass(ProductionOccurrence::class)]
18#[UsesClass(TerminalOccurrence::class)]
19#[UsesClass(TerminalSequence::class)]
20final class SelectOptionsRuleTest extends TestCase
21{
22 #[DataProvider('providerOptions')]
23 public function testRewriteScopesOptionsToTheSameSelectNode(string $innerRule, string $outerRule, string $boundary, bool $removed): void
24 {
25 $body = new TerminalOccurrence('SELECT', 10, [0, 1, 2, 3], ['select_no_parens', $boundary, 'select_no_parens', 'simple_select']);
26 $inner = new TerminalOccurrence('INNER', 11, [0, 1, 2, 4], ['select_no_parens', $boundary, 'select_no_parens', $innerRule]);
27 $outer = new TerminalOccurrence('OUTER', 12, [0, 5], ['select_no_parens', $outerRule]);
28 $terminals = [$body, $inner, $outer];
29 $input = new TerminalSequence($terminals, $terminals, [], [
30 new ProductionOccurrence(0, null, 'select_no_parens', 0),
31 new ProductionOccurrence(1, 0, $boundary, 0),
32 new ProductionOccurrence(2, 1, 'select_no_parens', 0),
33 new ProductionOccurrence(3, 2, 'simple_select', 0),
34 new ProductionOccurrence(4, 2, $innerRule, 0),
35 new ProductionOccurrence(5, 0, $outerRule, 0),
36 ]);
37 $rule = new SelectOptionsRule();
38 $result = $rule->rewrite($input);
39 self::assertSame($removed ? [$body, $inner] : $terminals, $result->terminals);
40 self::assertSame($input->original, $result->original);
41 self::assertCount(count($result->terminals), array_unique(array_map(static fn ($terminal): int => $terminal->id, $result->terminals)));
42 self::assertSame($input->productions, $result->productions);
43 self::assertSame($result, $rule->rewrite($result));
44 }
45
46 /**
47 * @return iterable<array{string, string, string, bool}>
48 */
49 public static function providerOptions(): iterable
50 {
51 foreach ([['sort_clause', 'opt_sort_clause'], ['opt_sort_clause', 'sort_clause'], ['select_limit', 'opt_select_limit'], ['opt_select_limit', 'select_limit'], ['with_clause', 'with_clause']] as [$inner, $outer]) {
52 foreach (['select_clause', 'select_with_parens'] as $wrapper) {
53 yield [$inner, $outer, $wrapper, true];
54 }
55 foreach (['simple_select', 'from_clause', 'a_expr', 'with_clause'] as $boundary) {
56 yield [$inner, $outer, $boundary, false];
57 }
58 }
59 yield ['sort_clause', 'select_limit', 'select_with_parens', false];
60 yield ['with_clause', 'sort_clause', 'select_with_parens', false];
61 }
62
63 public function testRewriteIgnoresEmptyAndUnscopedClauses(): void
64 {
65 $input = new TerminalSequence([], [], [], [
66 new ProductionOccurrence(0, null, 'select_no_parens', 0),
67 new ProductionOccurrence(1, 0, 'opt_sort_clause', 0),
68 new ProductionOccurrence(2, null, 'sort_clause', 0),
69 ]);
70 self::assertSame($input, (new SelectOptionsRule())->rewrite($input));
71 }
72
73 public function testQueryFollowsOnlySelectWrappersAndResolvesOptionalClauses(): void
74 {
75 $nodes = [
76 new ProductionOccurrence(0, null, 'SelectStmt', 0),
77 new ProductionOccurrence(1, 0, 'select_no_parens', 0),
78 new ProductionOccurrence(2, 1, 'select_clause', 0),
79 new ProductionOccurrence(3, 2, 'select_with_parens', 0),
80 new ProductionOccurrence(4, 3, 'select_no_parens', 0),
81 new ProductionOccurrence(5, 4, 'opt_sort_clause', 0),
82 new ProductionOccurrence(6, 5, 'sort_clause', 0),
83 new ProductionOccurrence(7, 4, 'opt_select_limit', 0),
84 new ProductionOccurrence(8, 7, 'select_limit', 0),
85 ];
86 $rule = new SelectOptionsRule();
87 self::assertSame(1, $rule->query($nodes, $nodes[6]));
88 self::assertSame(1, $rule->query($nodes, $nodes[8]));
89 self::assertNull($rule->query($nodes, $nodes[0]));
90 self::assertNull($rule->query($nodes, $nodes[1]));
91 self::assertNull($rule->query([], $nodes[6]));
92 }
93}
94