packages/ztd-query-postgres/src/Rewrite/Sampling/PgSqlTableSampleRewriter.php

1<?php
2
3declare(strict_types=1);
4
5namespace ZtdQuery\Platform\Postgres\Rewrite\Sampling;
6
7use ZtdQuery\Exception\UnsupportedSqlException;
8use ZtdQuery\Platform\Postgres\Sql\PgSqlIdentifierQuoter;
9use ZtdQuery\Platform\Postgres\Sql\Sampling\PgSqlTableSample;
10use ZtdQuery\Platform\Postgres\Sql\Sampling\PgSqlTableSampleParser;
11
12/**
13 * Table sample rewriter for PostgreSQL queries.
14 */
15final class PgSqlTableSampleRewriter
16{
17    private PgSqlTableSampleParser $parser;
18    private PgSqlIdentifierQuoter $quoter;
19
20    /**
21     * Initializes the collaborators and state used by this table sample rewriter.
22     */
23    public function __construct()
24    {
25        $this->parser = new PgSqlTableSampleParser();
26        $this->quoter = new PgSqlIdentifierQuoter();
27    }
28
29    /**
30     * @param array<string, array<string, mixed>> $tables
31     * @throws UnsupportedSqlException
32     */
33    public function rewrite(string $sql, array $tables): string
34    {
35        $samples = $this->parser->parse($sql);
36        usort($samples, static fn (PgSqlTableSample $left, PgSqlTableSample $right): int => $right->startOffset <=> $left->startOffset);
37
38        foreach ($samples as $index => $sample) {
39            $columns = (new TableColumns())->columns($sample->tableName, $tables);
40            if ($columns === []) {
41                throw new UnsupportedSqlException(
42                    $sql,
43                    "Cannot determine columns for TABLESAMPLE source '{$sample->tableName}'",
44                );
45            }
46            $replacement = (new SampleProjection($this->quoter))->replacement($sample, $columns, $index);
47            $sql = substr_replace(
48                $sql,
49                $replacement,
50                $sample->startOffset,
51                $sample->endOffset - $sample->startOffset,
52            );
53        }
54
55        return $sql;
56    }
57}
58