packages/sql-faker/tests/Unit/PostgreSql/Generation/Rewrite/RewriteDefinitionsTest.php
1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\SqlFaker\PostgreSql\Generation\Rewrite;
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\RewriteDefinitions;
15
16#[CoversClass(RewriteDefinitions::class)]
17#[UsesClass(\SqlFaker\PostgreSql\Generation\Rewrite\Routine\SubstringRule::class)]
18#[UsesClass(TerminalSequence::class)]
19#[UsesClass(\SqlFaker\Generation\Token\TerminalMappingRule::class)]
20#[UsesClass(TerminalOccurrence::class)]
21#[UsesClass(\SqlFaker\Generation\Token\TokenRewriter::class)]
22#[UsesClass(\SqlFaker\PostgreSql\Generation\Rewrite\Option\UniqueOptionRule::class)]
23#[UsesClass(\SqlFaker\PostgreSql\Generation\Rewrite\CopySourceRule::class)]
24#[UsesClass(\SqlFaker\PostgreSql\Generation\Rewrite\FetchWithTiesRule::class)]
25#[UsesClass(\SqlFaker\PostgreSql\Generation\Rewrite\Name\FunctionNameRule::class)]
26#[UsesClass(\SqlFaker\PostgreSql\Generation\Rewrite\HashPartitionBoundRule::class)]
27#[UsesClass(\SqlFaker\PostgreSql\Generation\Rewrite\LimitOffsetRule::class)]
28#[UsesClass(\SqlFaker\PostgreSql\Generation\Rewrite\LookaheadRule::class)]
29#[UsesClass(\SqlFaker\PostgreSql\Generation\Rewrite\OperatorArgumentsRule::class)]
30#[UsesClass(\SqlFaker\PostgreSql\Generation\Rewrite\OverlapsArgumentsRule::class)]
31#[UsesClass(\SqlFaker\PostgreSql\Generation\Rewrite\PublicationObjectRule::class)]
32#[UsesClass(\SqlFaker\PostgreSql\Generation\Rewrite\Name\RelationNameRule::class)]
33#[UsesClass(\SqlFaker\PostgreSql\Generation\Rewrite\TimeZoneIntervalRule::class)]
34#[UsesClass(\SqlFaker\PostgreSql\Generation\Rewrite\WindowFrameRule::class)]
35#[UsesClass(\SqlFaker\PostgreSql\Generation\Lookahead\PgLookahead::class)]
36#[UsesClass(ProductionOccurrence::class)]
37#[UsesClass(\SqlFaker\PostgreSql\Generation\Rewrite\Expression\ExpressionGroupingRule::class)]
38#[UsesClass(\SqlFaker\PostgreSql\Generation\Rewrite\ConstraintAttributesRule::class)]
39#[UsesClass(\SqlFaker\PostgreSql\Generation\Rewrite\Name\AnyRelationNameRule::class)]
40#[UsesClass(\SqlFaker\PostgreSql\Generation\Rewrite\Name\IndirectionStarRule::class)]
41#[UsesClass(\SqlFaker\PostgreSql\Generation\Rewrite\GeneratedColumnRule::class)]
42#[UsesClass(\SqlFaker\PostgreSql\Generation\Rewrite\Query\SelectOptionsRule::class)]
43#[UsesClass(\SqlFaker\PostgreSql\Generation\Rewrite\Routine\TableFunctionRule::class)]
44#[UsesClass(\SqlFaker\PostgreSql\Generation\Rewrite\Routine\TypeModifierRule::class)]
45#[UsesClass(\SqlFaker\PostgreSql\Generation\Rewrite\Column\IdentityOptionRule::class)]
46#[UsesClass(\SqlFaker\PostgreSql\Generation\Rewrite\Query\IntoClauseRule::class)]
47#[UsesClass(\SqlFaker\PostgreSql\Generation\Rewrite\Routine\WithinGroupRule::class)]
48#[UsesClass(\SqlFaker\PostgreSql\Generation\Rewrite\Name\ColumnNameRule::class)]
49#[UsesClass(\SqlFaker\PostgreSql\Generation\Rewrite\Name\AliasRule::class)]
50#[UsesClass(\SqlFaker\PostgreSql\Generation\Rewrite\Routine\RangeFunctionOrdinalityRule::class)]
51#[UsesClass(\SqlFaker\PostgreSql\Generation\Rewrite\Routine\JsonOptionsRule::class)]
52#[UsesClass(\SqlFaker\PostgreSql\Generation\Rewrite\Column\ConstraintCapabilitiesRule::class)]
53#[UsesClass(\SqlFaker\PostgreSql\Generation\Rewrite\Column\ForeignKeyActionRule::class)]
54#[UsesClass(\SqlFaker\PostgreSql\Generation\Rewrite\Query\SchemaElementsRule::class)]
55#[UsesClass(\SqlFaker\PostgreSql\Generation\Rewrite\Query\ParserOptionsRule::class)]
56#[UsesClass(\SqlFaker\PostgreSql\Generation\Rewrite\Routine\JsonTablePathRule::class)]
57#[UsesClass(\SqlFaker\PostgreSql\Generation\Rewrite\Routine\AggregateArgumentRule::class)]
58#[UsesClass(\SqlFaker\PostgreSql\Generation\Rewrite\Name\ParserNameRule::class)]
59#[UsesClass(\SqlFaker\PostgreSql\Generation\Rewrite\Column\NumericContextRule::class)]
60final class RewriteDefinitionsTest extends TestCase
61{
62 public function testUniqueOptionsKeepsOneDefaultNamespacePerXmlTable(): void
63 {
64 $first = new TerminalOccurrence('DEFAULT', 10, [0, 1], ['xmltable', 'xml_namespace_el']);
65 $firstValue = new TerminalOccurrence('SCONST', 11, [0, 1], ['xmltable', 'xml_namespace_el']);
66 $comma = new TerminalOccurrence(',', 12, [0], ['xmltable']);
67 $duplicate = new TerminalOccurrence('DEFAULT', 13, [0, 2], ['xmltable', 'xml_namespace_el']);
68 $duplicateValue = new TerminalOccurrence('SCONST', 14, [0, 2], ['xmltable', 'xml_namespace_el']);
69 $other = new TerminalOccurrence('DEFAULT', 15, [3, 4], ['xmltable', 'xml_namespace_el']);
70 $otherValue = new TerminalOccurrence('SCONST', 16, [3, 4], ['xmltable', 'xml_namespace_el']);
71 $terminals = [$first, $firstValue, $comma, $duplicate, $duplicateValue, $other, $otherValue];
72 $input = new TerminalSequence($terminals, $terminals, [], [
73 new ProductionOccurrence(0, null, 'xmltable', 1),
74 new ProductionOccurrence(1, 0, 'xml_namespace_el', 1),
75 new ProductionOccurrence(2, 0, 'xml_namespace_el', 1),
76 new ProductionOccurrence(3, null, 'xmltable', 1),
77 new ProductionOccurrence(4, 3, 'xml_namespace_el', 1),
78 ]);
79 $rule = (new RewriteDefinitions())->uniqueOptions();
80 $result = $rule->rewrite($input);
81 self::assertSame([$first, $firstValue, $other, $otherValue], $result->terminals);
82 self::assertSame($input->original, $result->original);
83 self::assertSame($input->productions, $result->productions);
84 self::assertSame($result, $rule->rewrite($result));
85 }
86
87 public function testCreateComposesTheDeclaredSourceRules(): void
88 {
89 $input = TerminalSequence::fromNames(['NOT', 'LIKE']);
90 $result = (new RewriteDefinitions())->create()->rewrite($input);
91 self::assertSame(['NOT_LA', 'LIKE'], $result->names());
92 self::assertSame($input->original, $result->original);
93 self::assertCount(count($result->terminals), array_unique(array_map(static fn ($terminal): int => $terminal->id, $result->terminals)));
94 }
95
96 #[DataProvider('providerColumnScopes')]
97 public function testCreateKeepsOneCollationPerSourceColumnQualifierList(string $scope): void
98 {
99 $first = new TerminalOccurrence('COLLATE', 10, [0, 1], [$scope, 'ColConstraint']);
100 $firstName = new TerminalOccurrence('IDENT', 11, [0, 1], [$scope, 'ColConstraint']);
101 $duplicate = new TerminalOccurrence('COLLATE', 12, [0, 2], [$scope, 'ColConstraint']);
102 $duplicateName = new TerminalOccurrence('IDENT', 13, [0, 2], [$scope, 'ColConstraint']);
103 $other = new TerminalOccurrence('COLLATE', 14, [3, 4], [$scope, 'ColConstraint']);
104 $expression = new TerminalOccurrence('COLLATE', 15, [0, 5], [$scope, 'a_expr']);
105 $terminals = [$first, $firstName, $duplicate, $duplicateName, $other, $expression];
106 $input = new TerminalSequence($terminals, $terminals, [], [
107 new ProductionOccurrence(0, null, $scope, 0), new ProductionOccurrence(1, 0, 'ColConstraint', 0),
108 new ProductionOccurrence(2, 0, 'ColConstraint', 0), new ProductionOccurrence(3, null, $scope, 0),
109 new ProductionOccurrence(4, 3, 'ColConstraint', 0), new ProductionOccurrence(5, 0, 'a_expr', 0),
110 ]);
111 $rule = (new RewriteDefinitions())->create();
112 $result = $rule->rewrite($input);
113 self::assertSame([$first, $firstName, $other, $expression], $result->terminals);
114 self::assertSame($input->original, $result->original);
115 self::assertCount(count($result->terminals), array_unique(array_map(static fn ($terminal): int => $terminal->id, $result->terminals)));
116 self::assertSame($input->productions, $result->productions);
117 self::assertSame($result, $rule->rewrite($result));
118 }
119
120 /**
121 * @return iterable<string, array{string}>
122 */
123 public static function providerColumnScopes(): iterable
124 {
125 yield 'column definition' => ['columnDef'];
126 yield 'partition column options' => ['columnOptions'];
127 yield 'domain' => ['CreateDomainStmt'];
128 }
129}
130