packages/ztd-query-sqlite/src/Shadow/Resolution/TableMutationResolver.php

1<?php
2
3declare(strict_types=1);
4
5namespace ZtdQuery\Platform\Sqlite\Shadow\Resolution;
6
7use ZtdQuery\Exception\UnknownSchemaException;
8use ZtdQuery\Exception\UnsupportedSqlException;
9use ZtdQuery\Platform\SchemaParser;
10use ZtdQuery\Platform\Sqlite\Sql\SqliteParser;
11use ZtdQuery\Schema\TableDefinitionRegistry;
12use ZtdQuery\Shadow\Mutation\ShadowMutation;
13use ZtdQuery\Shadow\Mutation\Table\CreateTableMutation;
14use ZtdQuery\Shadow\Mutation\Table\DropTableMutation;
15
16/**
17 * Resolves table lifecycle and ALTER operations.
18 *
19 * @visibility ZtdQuery\Platform\Sqlite
20 */
21final class TableMutationResolver
22{
23    /**
24     * Binds the collaborators used by this operation.
25     */
26    public function __construct(
27        private SqliteParser $parser,
28        private TableDefinitionRegistry $registry,
29        private SchemaParser $schemaParser
30    ) {
31    }
32
33    /**
34     * Resolves table lifecycle and ALTER operations.
35     * @throws UnsupportedSqlException
36     */
37    public function resolveCreateTable(string $sql): ShadowMutation
38    {
39        $tableName = $this->parser->extractTargetTable($sql);
40        if ($tableName === null) {
41            throw new UnsupportedSqlException($sql, 'Cannot resolve table name');
42        }
43
44        $ifNotExists = (bool) preg_match('/\bIF\s+NOT\s+EXISTS\b/i', $sql);
45
46        if (!$ifNotExists && $this->registry->has($tableName)) {
47            throw new UnsupportedSqlException($sql, 'Table already exists');
48        }
49
50        $definition = $this->schemaParser->parse($sql);
51
52        return new CreateTableMutation($tableName, $definition, $this->registry, $sql, $ifNotExists);
53    }
54
55    /**
56     * Resolves table lifecycle and ALTER operations.
57     * @throws UnknownSchemaException
58     * @throws UnsupportedSqlException
59     */
60    public function resolveDropTable(string $sql): ShadowMutation
61    {
62        $tableName = $this->parser->extractTargetTable($sql);
63        if ($tableName === null) {
64            throw new UnsupportedSqlException($sql, 'Cannot resolve table name');
65        }
66
67        $ifExists = (bool) preg_match('/\bIF\s+EXISTS\b/i', $sql);
68
69        if ($this->registry->isRemoved($tableName)) {
70            if ($ifExists) {
71                return new DropTableMutation($tableName, $this->registry, $sql, true);
72            }
73            throw new UnsupportedSqlException($sql, 'Table was removed from the virtual schema');
74        }
75
76        if (!$ifExists && !$this->registry->has($tableName)) {
77            throw new UnknownSchemaException($sql, $tableName, 'table');
78        }
79
80        return new DropTableMutation($tableName, $this->registry, $sql, $ifExists);
81    }
82
83    /**
84     * @throws UnsupportedSqlException
85     * @throws UnknownSchemaException
86     */
87    public function resolveAlterTable(string $sql): ShadowMutation
88    {
89        $tableName = $this->parser->extractTargetTable($sql);
90        if ($tableName === null) {
91            throw new UnsupportedSqlException($sql, 'Cannot resolve table name');
92        }
93
94        (new MutationTableLookup($this->registry))->assertTableWasNotRemoved($sql, $tableName);
95
96        if (!$this->registry->has($tableName)) {
97            throw new UnknownSchemaException($sql, $tableName, 'table');
98        }
99
100        $operation = (new \ZtdQuery\Platform\Sqlite\Sql\Alter\AlterOperationParser())->alterOperation($sql);
101        if ($operation === null) {
102            throw new UnsupportedSqlException($sql, 'Unsupported ALTER TABLE operation');
103        }
104
105        return match ($operation['kind']) {
106            'add' => (new \ZtdQuery\Platform\Sqlite\Shadow\Mutation\Table\Alter\AddColumnResolver($this->registry, $this->schemaParser))->resolveAlterAddColumn($sql, $tableName, $operation['clause']),
107            'drop' => (new \ZtdQuery\Platform\Sqlite\Shadow\Mutation\Table\Alter\DropColumnResolver($this->registry))->resolveAlterDropColumn($sql, $tableName, $operation['clause']),
108            'rename_table' => (new \ZtdQuery\Platform\Sqlite\Shadow\Mutation\Table\Alter\RenameResolver($this->registry))->resolveAlterRenameTable($sql, $tableName, $operation['clause']),
109            'rename_column' => (new \ZtdQuery\Platform\Sqlite\Shadow\Mutation\Table\Alter\RenameResolver($this->registry))->resolveAlterRenameColumn($sql, $tableName, $operation['clause']),
110        };
111    }
112}
113