packages/ztd-query-core/src/Schema/TableDefinition.php
1<?php
2
3declare(strict_types=1);
4
5namespace ZtdQuery\Schema;
6
7use ZtdQuery\Schema\Key\CandidateKeySet;
8use ZtdQuery\Schema\Key\ForeignKeyDefinition;
9use ZtdQuery\Schema\Key\IdentityGenerationStrategy;
10use ZtdQuery\Schema\Key\PartialUniqueIndex;
11use ZtdQuery\Schema\Partition\TablePartitioning;
12use ZtdQuery\Schema\Partition\TablePartitionKey;
13use ZtdQuery\Schema\Partition\TablePartitionRelation;
14
15/**
16 * Structured representation of a table's schema metadata.
17 *
18 * Values remain opaque to the neutral core until a platform interprets them.
19 * This also preserves driver values such as LOB streams and caller literals.
20 *
21 * @phpstan-type RowValue mixed
22 * @phpstan-type Row array<string, RowValue>
23 */
24final class TableDefinition
25{
26 /**
27 * @var array<string, ColumnDeclaration>
28 */
29 public readonly array $typedColumns;
30
31 /**
32 * @var array<string, string>
33 */
34 public readonly array $columnDefaults;
35
36 /**
37 * @var array<string, IdentityGenerationStrategy>
38 */
39 public readonly array $identityStrategies;
40
41 /**
42 * @var array<string, string>
43 */
44 public readonly array $generatedExpressions;
45
46 /**
47 * @var array<string, ForeignKeyDefinition>
48 */
49 public readonly array $foreignKeys;
50
51 /**
52 * @var TablePartitioning|null How the table is divided, or null when it is not
53 */
54 public readonly ?TablePartitioning $partitioning;
55
56 /**
57 * @var TablePartitionKey|null What the division is by, or null when there is none
58 */
59 public readonly ?TablePartitionKey $partitionKey;
60
61 /**
62 * @var TablePartitionRelation|null The table this one is a partition of, or null when it is not one
63 */
64 public readonly ?TablePartitionRelation $partitionRelation;
65
66 /**
67 * @var array<string, PartialUniqueIndex>
68 */
69 public readonly array $partialUniqueIndexes;
70
71 /**
72 * @param list<string> $columns Column names in declaration order.
73 * @param array<string, string> $columnTypes Column name => MySQL type string.
74 * @param list<string> $primaryKeys Primary key column names.
75 * @param list<string> $notNullColumns Columns with NOT NULL constraint.
76 * @param array<string, list<string>> $uniqueConstraints Key name => column list.
77 * @param array<string, ColumnDeclaration> $typedColumns Column name => structured ColumnDeclaration.
78 * @param array<string, string> $columnDefaults Column name => SQL default expression.
79 * @param array<string, IdentityGenerationStrategy> $identityStrategies Column name => shadow generation strategy.
80 * @param array<string, string> $generatedExpressions Column name => database generated expression.
81 * @param array<string, ForeignKeyDefinition> $foreignKeys Constraint name => foreign-key definition.
82 * @param TablePartitioning|null $partitioning Named partition selection predicates.
83 * @param TablePartitionKey|null $partitionKey Declarative partition key metadata.
84 * @param TablePartitionRelation|null $partitionRelation Parent partition relationship.
85 * @param array<string, PartialUniqueIndex> $partialUniqueIndexes Partial unique indexes keyed by name.
86 */
87 public function __construct(
88 public readonly array $columns,
89 public readonly array $columnTypes,
90 public readonly array $primaryKeys,
91 public readonly array $notNullColumns,
92 public readonly array $uniqueConstraints,
93 array $typedColumns = [],
94 array $columnDefaults = [],
95 array $identityStrategies = [],
96 array $generatedExpressions = [],
97 array $foreignKeys = [],
98 ?TablePartitioning $partitioning = null,
99 ?TablePartitionKey $partitionKey = null,
100 ?TablePartitionRelation $partitionRelation = null,
101 array $partialUniqueIndexes = [],
102 ) {
103 $this->typedColumns = $typedColumns;
104 $this->columnDefaults = $columnDefaults;
105 $this->identityStrategies = $identityStrategies;
106 $this->generatedExpressions = $generatedExpressions;
107 $this->foreignKeys = $foreignKeys;
108 $this->partitioning = $partitioning;
109 $this->partitionKey = $partitionKey;
110 $this->partitionRelation = $partitionRelation;
111 $this->partialUniqueIndexes = $partialUniqueIndexes;
112 }
113
114 /**
115 * Candidate keys.
116 *
117 * @return CandidateKeySet
118 */
119 public function candidateKeys(): CandidateKeySet
120 {
121 return CandidateKeySet::fromSchema($this->primaryKeys, $this->uniqueConstraints);
122 }
123
124 /**
125 * With partitioning.
126 *
127 * @param ?TablePartitioning $partitioning
128 * @return self
129 */
130 public function withPartitioning(?TablePartitioning $partitioning): self
131 {
132 return new self(
133 $this->columns,
134 $this->columnTypes,
135 $this->primaryKeys,
136 $this->notNullColumns,
137 $this->uniqueConstraints,
138 $this->typedColumns,
139 $this->columnDefaults,
140 $this->identityStrategies,
141 $this->generatedExpressions,
142 $this->foreignKeys,
143 $partitioning,
144 $this->partitionKey,
145 $this->partitionRelation,
146 $this->partialUniqueIndexes,
147 );
148 }
149
150 /**
151 * With partition key.
152 *
153 * @param ?TablePartitionKey $partitionKey
154 * @return self
155 */
156 public function withPartitionKey(?TablePartitionKey $partitionKey): self
157 {
158 return new self(
159 $this->columns,
160 $this->columnTypes,
161 $this->primaryKeys,
162 $this->notNullColumns,
163 $this->uniqueConstraints,
164 $this->typedColumns,
165 $this->columnDefaults,
166 $this->identityStrategies,
167 $this->generatedExpressions,
168 $this->foreignKeys,
169 $this->partitioning,
170 $partitionKey,
171 $this->partitionRelation,
172 $this->partialUniqueIndexes,
173 );
174 }
175
176 /**
177 * With partition relation.
178 *
179 * @param ?TablePartitionRelation $partitionRelation
180 * @return self
181 */
182 public function withPartitionRelation(?TablePartitionRelation $partitionRelation): self
183 {
184 return new self(
185 $this->columns,
186 $this->columnTypes,
187 $this->primaryKeys,
188 $this->notNullColumns,
189 $this->uniqueConstraints,
190 $this->typedColumns,
191 $this->columnDefaults,
192 $this->identityStrategies,
193 $this->generatedExpressions,
194 $this->foreignKeys,
195 $this->partitioning,
196 $this->partitionKey,
197 $partitionRelation,
198 $this->partialUniqueIndexes,
199 );
200 }
201
202 /**
203 * With partial unique index.
204 *
205 * @param PartialUniqueIndex $index
206 * @return self
207 */
208 public function withPartialUniqueIndex(PartialUniqueIndex $index): self
209 {
210 $indexes = $this->partialUniqueIndexes;
211 $indexes[$index->name] = $index;
212
213 return new self(
214 $this->columns,
215 $this->columnTypes,
216 $this->primaryKeys,
217 $this->notNullColumns,
218 $this->uniqueConstraints,
219 $this->typedColumns,
220 $this->columnDefaults,
221 $this->identityStrategies,
222 $this->generatedExpressions,
223 $this->foreignKeys,
224 $this->partitioning,
225 $this->partitionKey,
226 $this->partitionRelation,
227 $indexes,
228 );
229 }
230}
231