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