packages/sql-faker/src/PostgreSql/Generation/Lexeme/HashBoundLexemeGenerator.php

1<?php
2
3declare(strict_types=1);
4
5namespace SqlFaker\PostgreSql\Generation\Lexeme;
6
7use Override;
8use SqlFaker\Generation\Lexeme\Lexeme;
9use SqlFaker\Generation\Lexeme\LexemeCandidates;
10use SqlFaker\Generation\Lexeme\LexemeGenerator;
11use SqlFaker\Generation\Lexeme\LexemeInput;
12use SqlFaker\Generation\Lexeme\LexemeSequence;
13
14/**
15 * Implements the modulus/remainder name checks in PostgreSQL 17.2 gram.y/PartitionBoundSpec.
16 * @see https://github.com/postgres/postgres/blob/REL_17_2/src/backend/parser/gram.y
17 */
18final class HashBoundLexemeGenerator implements LexemeGenerator
19{
20    /**
21     * Both orders remain reachable; a previously selected name in the same bound is not repeated.
22     */
23    #[Override]
24    public function generate(LexemeInput $input): ?LexemeCandidates
25    {
26        if ($input->terminal()->name !== 'HASH_BOUND_NAME') {
27            return null;
28        }
29        $scope = $input->terminal()->ancestor('PartitionBoundSpec');
30        $taken = [];
31        foreach ($input->right->parts as $part) {
32            if ($part->lexeme->origin->name === 'HASH_BOUND_NAME'
33                && $part->lexeme->origin->ancestor('PartitionBoundSpec') === $scope) {
34                $taken[] = strtolower($part->lexeme->text);
35            }
36        }
37        $candidates = [];
38        foreach ($input->requested === null ? ['modulus', 'remainder'] : [$input->requested] as $word) {
39            $name = strtolower($word);
40            if (in_array($name, ['modulus', 'remainder'], true) && !in_array($name, $taken, true)) {
41                $candidates[] = new LexemeSequence([
42                    new Lexeme($word, 'identifier', $input->terminal(), 'gram.y:PartitionBoundSpec'),
43                ], 'postgresql.hash-bound:' . $word);
44            }
45        }
46        return LexemeCandidates::of(...$candidates);
47    }
48}
49