packages/sql-faker/src/PostgreSql/Generation/Rewrite/Name/AnyRelationNameRule.php

1<?php
2
3declare(strict_types=1);
4
5namespace SqlFaker\PostgreSql\Generation\Rewrite\Name;
6
7use Override;
8use SqlFaker\Generation\Token\RewriteRule;
9use SqlFaker\Generation\Token\TerminalSequence;
10
11/**
12 * gram.y/makeRangeVarFromAnyName limits composite type relation names to three components.
13 * @see https://github.com/postgres/postgres/blob/REL_17_2/src/backend/parser/gram.y
14 */
15final class AnyRelationNameRule implements RewriteRule
16{
17    /**
18     * Limits only the three grammar actions constructing a RangeVar from any_name.
19     */
20    #[Override]
21    public function rewrite(TerminalSequence $sequence): TerminalSequence
22    {
23        foreach ($sequence->occurrences('any_name') as $id) {
24            $range = $sequence->range($id);
25            if ($range === null) {
26                continue;
27            }
28            $origin = $sequence->terminals[$range[0]];
29            $position = array_search('any_name', $origin->rules, true);
30            if ($position === false || $position === 0) {
31                continue;
32            }
33            $parent = $origin->ancestors[$position - 1];
34            $rule = $origin->rules[$position - 1];
35            if ($rule !== 'AlterCompositeTypeStmt'
36                && !($rule === 'DefineStmt' && $sequence->child($parent, 'OptTableFuncElementList') !== null)
37                && !($rule === 'RenameStmt' && $sequence->nameAt($range[1]) === 'RENAME' && $sequence->nameAt($range[1] + 1) === 'ATTRIBUTE')) {
38                continue;
39            }
40            $count = 0;
41            foreach (array_reverse($sequence->occurrences('attr_name')) as $attribute) {
42                $part = $sequence->range($attribute);
43                if ($part === null || $sequence->terminals[$part[0]]->ancestor('any_name') !== $id) {
44                    continue;
45                }
46                if (++$count > 2) {
47                    $sequence = $sequence->replace($part[0] - 1, $part[1] - $part[0] + 1, [], 'gram.y:makeRangeVarFromAnyName');
48                }
49            }
50        }
51        return $sequence;
52    }
53}
54