packages/sql-faker/src/MySql/Generation/Rewrite/SubqueryContextRule.php

1<?php
2
3declare(strict_types=1);
4
5namespace SqlFaker\MySql\Generation\Rewrite;
6
7use Override;
8use SqlFaker\Generation\Token\RewriteRule;
9use SqlFaker\Generation\Token\TerminalSequence;
10
11/**
12 * PT_subquery and partition contextualization forbid subqueries in these statement expressions.
13 * @see https://github.com/mysql/mysql-server/blob/mysql-8.4.7/sql/parse_tree_nodes.cc
14 */
15final class SubqueryContextRule implements RewriteRule
16{
17    /**
18     * Replaces only the operand containing a forbidden subquery; ordinary SELECT subqueries remain intact.
19     */
20    #[Override]
21    public function rewrite(TerminalSequence $sequence): TerminalSequence
22    {
23        foreach ($sequence->occurrences('subquery') as $id) {
24            $range = $sequence->range($id);
25            if ($range === null) {
26                continue;
27            }
28            $origin = $sequence->terminals[$range[0]];
29            if (array_intersect(['handler_stmt', 'purge', 'install_stmt', 'part_type_def', 'opt_sub_part'], $origin->rules) === []) {
30                continue;
31            }
32            $position = array_search($id, $origin->ancestors, true);
33            for ($index = ($position === false ? 0 : $position) - 1; $index >= 0; --$index) {
34                if (!in_array($origin->rules[$index], ['expr', 'bool_pri', 'predicate', 'bit_expr', 'simple_expr'], true)) {
35                    continue;
36                }
37                $operand = $origin->ancestors[$index];
38                $scope = $sequence->range($operand);
39                if ($scope !== null) {
40                    $sequence = $sequence->replace($scope[0], $scope[1] - $scope[0], [
41                        $sequence->insertedFor('NUM', $operand, 'sql/parse_tree_nodes.cc:PT_subquery'),
42                    ], 'sql/parse_tree_nodes.cc:PT_subquery');
43                }
44                break;
45            }
46        }
47        return $sequence;
48    }
49}
50