packages/ztd-query-sqlite/tests/Unit/Shadow/Resolution/TableMutationResolverTest.php
1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\Shadow\Resolution;
6
7use PHPUnit\Framework\Attributes\CoversClass;
8use PHPUnit\Framework\Attributes\UsesClass;
9use PHPUnit\Framework\TestCase;
10use ZtdQuery\Exception\UnknownSchemaException;
11use ZtdQuery\Exception\UnsupportedSqlException;
12use ZtdQuery\Platform\Sqlite\Schema\SqliteSchemaParser;
13use ZtdQuery\Platform\Sqlite\Shadow\Mutation\Table\AlterTableMutation;
14use ZtdQuery\Platform\Sqlite\Shadow\Resolution\TableMutationResolver;
15use ZtdQuery\Platform\Sqlite\Sql\SqliteIdentifierQuoter;
16use ZtdQuery\Platform\Sqlite\Sql\SqliteLexicalMasker;
17use ZtdQuery\Platform\Sqlite\Sql\SqliteParser;
18use ZtdQuery\Schema\ColumnDeclaration;
19use ZtdQuery\Schema\ColumnTypeFamily;
20use ZtdQuery\Schema\TableDefinition;
21use ZtdQuery\Schema\TableDefinitionRegistry;
22use ZtdQuery\Shadow\Mutation\Table\CreateTableMutation;
23use ZtdQuery\Shadow\Mutation\Table\DropTableMutation;
24use ZtdQuery\Shadow\ShadowStore;
25
26#[CoversClass(TableMutationResolver::class)]
27#[UsesClass(AlterTableMutation::class)]
28#[UsesClass(\ZtdQuery\Platform\Sqlite\Shadow\Mutation\Table\Alter\AddColumnResolver::class)]
29#[UsesClass(\ZtdQuery\Platform\Sqlite\Shadow\Mutation\Table\Alter\AlteredTableProjection::class)]
30#[UsesClass(\ZtdQuery\Platform\Sqlite\Shadow\Mutation\Table\Alter\ColumnDefinitionEditor::class)]
31#[UsesClass(\ZtdQuery\Platform\Sqlite\Shadow\Mutation\Table\Alter\DropColumnResolver::class)]
32#[UsesClass(\ZtdQuery\Platform\Sqlite\Shadow\Mutation\Table\Alter\RenameResolver::class)]
33#[UsesClass(\ZtdQuery\Platform\Sqlite\Shadow\Resolution\MutationTableLookup::class)]
34#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\Alter\AlterOperationParser::class)]
35#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\Dml\Expression\AssignmentColumnParser::class)]
36#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\Dml\Expression\AssignmentParser::class)]
37#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\Dml\Expression\ValueListParser::class)]
38#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\Dml\Insert\InsertClauseParser::class)]
39#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\Lexing\ExpressionSpan::class)]
40#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\Lexing\IdentifierDecoder::class)]
41#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\Lexing\LiteralMasker::class)]
42#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\Lexing\OpaqueSqlSpan::class)]
43#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\Lexing\QuotedSpan::class)]
44#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\Lexing\TopLevelKeywordScanner::class)]
45#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\Relation\RelationSourceParser::class)]
46#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\Statement\StatementClassifier::class)]
47#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\Statement\StatementStructure::class)]
48#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\Statement\TargetTableParser::class)]
49#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\Dml\Update\UpdateClauseParser::class)]
50#[UsesClass(\ZtdQuery\Platform\Sqlite\Schema\Create\ColumnDefinitionParser::class)]
51#[UsesClass(\ZtdQuery\Platform\Sqlite\Schema\Create\TableBodyParser::class)]
52#[UsesClass(\ZtdQuery\Platform\Sqlite\Schema\Create\TableDefinitionBuilder::class)]
53#[UsesClass(\ZtdQuery\Platform\Sqlite\Schema\Create\VirtualTableParser::class)]
54#[UsesClass(\ZtdQuery\Platform\Sqlite\Schema\Key\ForeignKeyEntryParser::class)]
55#[UsesClass(\ZtdQuery\Platform\Sqlite\Schema\Key\ForeignKeyTokens::class)]
56#[UsesClass(\ZtdQuery\Platform\Sqlite\Schema\SqliteColumnTypeMapper::class)]
57#[UsesClass(\ZtdQuery\Platform\Sqlite\Schema\Key\SqliteForeignKeyDefinitionParser::class)]
58#[UsesClass(SqliteIdentifierQuoter::class)]
59#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\SqliteLexerProfile::class)]
60#[UsesClass(SqliteLexicalMasker::class)]
61#[UsesClass(SqliteParser::class)]
62#[UsesClass(SqliteSchemaParser::class)]
63#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\Relation\SqliteSelectRelationParser::class)]
64final class TableMutationResolverTest extends TestCase
65{
66 public function testResolveCreateTable(): void
67 {
68 $resolver = new TableMutationResolver(new SqliteParser(), new TableDefinitionRegistry(), new SqliteSchemaParser());
69 $mutation = $resolver->resolveCreateTable('CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)');
70 self::assertInstanceOf(CreateTableMutation::class, $mutation);
71 }
72
73 public function testResolveCreateTableAlreadyExistsThrows(): void
74 {
75 $registry = new TableDefinitionRegistry();
76 $registry->register('users', new TableDefinition(['id', 'name', 'email'], ['id' => 'INTEGER', 'name' => 'TEXT', 'email' => 'TEXT'], ['id'], ['id', 'name'], []));
77 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
78 $this->expectException(UnsupportedSqlException::class);
79 $resolver->resolveCreateTable('CREATE TABLE users (id INTEGER PRIMARY KEY)');
80 }
81
82 public function testResolveCreateTableIfNotExists(): void
83 {
84 $registry = new TableDefinitionRegistry();
85 $registry->register('users', new TableDefinition(['id', 'name', 'email'], ['id' => 'INTEGER', 'name' => 'TEXT', 'email' => 'TEXT'], ['id'], ['id', 'name'], []));
86 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
87 $mutation = $resolver->resolveCreateTable('CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY)');
88 self::assertInstanceOf(CreateTableMutation::class, $mutation);
89 }
90
91 public function testResolveDropTable(): void
92 {
93 $registry = new TableDefinitionRegistry();
94 $registry->register('users', new TableDefinition(['id', 'name', 'email'], ['id' => 'INTEGER', 'name' => 'TEXT', 'email' => 'TEXT'], ['id'], ['id', 'name'], []));
95 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
96 $mutation = $resolver->resolveDropTable('DROP TABLE users');
97 self::assertInstanceOf(DropTableMutation::class, $mutation);
98 }
99
100 public function testResolveDropTableUnknownThrows(): void
101 {
102 $resolver = new TableMutationResolver(new SqliteParser(), new TableDefinitionRegistry(), new SqliteSchemaParser());
103 $this->expectException(UnknownSchemaException::class);
104 $resolver->resolveDropTable('DROP TABLE unknown_table');
105 }
106
107 public function testResolveDropTableIfExists(): void
108 {
109 $resolver = new TableMutationResolver(new SqliteParser(), new TableDefinitionRegistry(), new SqliteSchemaParser());
110 $mutation = $resolver->resolveDropTable('DROP TABLE IF EXISTS unknown_table');
111 self::assertInstanceOf(DropTableMutation::class, $mutation);
112 }
113
114 public function testResolveAlterTableAddColumn(): void
115 {
116 $registry = new TableDefinitionRegistry();
117 $registry->register('users', new TableDefinition(['id', 'name', 'email'], ['id' => 'INTEGER', 'name' => 'TEXT', 'email' => 'TEXT'], ['id'], ['id', 'name'], []));
118 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
119 $mutation = $resolver->resolveAlterTable('ALTER TABLE users ADD COLUMN phone TEXT');
120 self::assertInstanceOf(AlterTableMutation::class, $mutation);
121 }
122
123 public function testResolveAlterTableDropColumn(): void
124 {
125 $registry = new TableDefinitionRegistry();
126 $registry->register('users', new TableDefinition(['id', 'name', 'email'], ['id' => 'INTEGER', 'name' => 'TEXT', 'email' => 'TEXT'], ['id'], ['id', 'name'], []));
127 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
128 $mutation = $resolver->resolveAlterTable('ALTER TABLE users DROP COLUMN email');
129 self::assertInstanceOf(AlterTableMutation::class, $mutation);
130 }
131
132 public function testResolveAlterTableRenameColumn(): void
133 {
134 $registry = new TableDefinitionRegistry();
135 $registry->register('users', new TableDefinition(['id', 'name', 'email'], ['id' => 'INTEGER', 'name' => 'TEXT', 'email' => 'TEXT'], ['id'], ['id', 'name'], []));
136 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
137 $mutation = $resolver->resolveAlterTable('ALTER TABLE users RENAME COLUMN name TO full_name');
138 self::assertInstanceOf(AlterTableMutation::class, $mutation);
139 }
140
141 public function testResolveAlterTableUnknownThrows(): void
142 {
143 $resolver = new TableMutationResolver(new SqliteParser(), new TableDefinitionRegistry(), new SqliteSchemaParser());
144 $this->expectException(UnknownSchemaException::class);
145 $resolver->resolveAlterTable('ALTER TABLE unknown_table ADD COLUMN x TEXT');
146 }
147
148 public function testResolveCreateTableWithoutTargetThrows(): void
149 {
150 $resolver = new TableMutationResolver(new SqliteParser(), new TableDefinitionRegistry(), new SqliteSchemaParser());
151 $this->expectException(UnsupportedSqlException::class);
152 $resolver->resolveCreateTable('CREATE TABLE');
153 }
154
155 public function testResolveDropTableWithoutTargetThrows(): void
156 {
157 $resolver = new TableMutationResolver(new SqliteParser(), new TableDefinitionRegistry(), new SqliteSchemaParser());
158 $this->expectException(UnsupportedSqlException::class);
159 $resolver->resolveDropTable('DROP TABLE');
160 }
161
162 public function testResolveAlterTableRenameTable(): void
163 {
164 $registry = new TableDefinitionRegistry();
165 $registry->register('users', new TableDefinition(['id', 'name'], ['id' => 'INTEGER', 'name' => 'TEXT'], ['id'], ['id'], []));
166 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
167 $mutation = $resolver->resolveAlterTable('ALTER TABLE users RENAME TO people');
168 self::assertInstanceOf(AlterTableMutation::class, $mutation);
169 }
170
171 public function testResolveAlterTableUnsupportedOperationThrows(): void
172 {
173 $registry = new TableDefinitionRegistry();
174 $registry->register('users', new TableDefinition(['id', 'name'], ['id' => 'INTEGER', 'name' => 'TEXT'], ['id'], ['id'], []));
175 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
176 $this->expectException(UnsupportedSqlException::class);
177 $resolver->resolveAlterTable('ALTER TABLE users SOMETHING WEIRD');
178 }
179
180 public function testResolveAlterTableAddColumnWithType(): void
181 {
182 $registry = new TableDefinitionRegistry();
183 $registry->register('users', new TableDefinition(['id', 'name'], ['id' => 'INTEGER', 'name' => 'TEXT'], ['id'], ['id'], []));
184 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
185 $mutation = $resolver->resolveAlterTable('ALTER TABLE users ADD COLUMN age INTEGER');
186 self::assertInstanceOf(AlterTableMutation::class, $mutation);
187 }
188
189 public function testResolveAlterTableAddColumnWithoutType(): void
190 {
191 $registry = new TableDefinitionRegistry();
192 $registry->register('users', new TableDefinition(['id', 'name'], ['id' => 'INTEGER', 'name' => 'TEXT'], ['id'], ['id'], []));
193 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
194 $mutation = $resolver->resolveAlterTable('ALTER TABLE users ADD COLUMN notes');
195 self::assertInstanceOf(AlterTableMutation::class, $mutation);
196 }
197
198 public function testResolveAlterTableDropColumnUpdatesSchema(): void
199 {
200 $registry = new TableDefinitionRegistry();
201 $registry->register('users', new TableDefinition(['id', 'name', 'email'], ['id' => 'INTEGER', 'name' => 'TEXT', 'email' => 'TEXT'], ['id'], ['id', 'name'], ['email_UNIQUE' => ['email']]));
202 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
203 $mutation = $resolver->resolveAlterTable('ALTER TABLE users DROP COLUMN email');
204 self::assertInstanceOf(AlterTableMutation::class, $mutation);
205 }
206
207 public function testResolveAlterTableRenameColumnUpdatesSchema(): void
208 {
209 $registry = new TableDefinitionRegistry();
210 $registry->register('users', new TableDefinition(['id', 'name'], ['id' => 'INTEGER', 'name' => 'TEXT'], ['id'], ['id', 'name'], ['name_UNIQUE' => ['name']]));
211 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
212 $mutation = $resolver->resolveAlterTable('ALTER TABLE users RENAME COLUMN name TO full_name');
213 self::assertInstanceOf(AlterTableMutation::class, $mutation);
214 }
215
216 public function testResolveAlterTableWithoutTargetThrows(): void
217 {
218 $resolver = new TableMutationResolver(new SqliteParser(), new TableDefinitionRegistry(), new SqliteSchemaParser());
219 $this->expectException(UnsupportedSqlException::class);
220 $resolver->resolveAlterTable('ALTER TABLE');
221 }
222
223 public function testResolveAlterTableAddWithoutColumnKeyword(): void
224 {
225 $registry = new TableDefinitionRegistry();
226 $registry->register('users', new TableDefinition(['id', 'name'], ['id' => 'INTEGER', 'name' => 'TEXT'], ['id'], ['id'], []));
227 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
228 $mutation = $resolver->resolveAlterTable('ALTER TABLE users ADD phone TEXT');
229 self::assertInstanceOf(AlterTableMutation::class, $mutation);
230 }
231
232 public function testResolveAlterTableRenameWithoutColumnKeyword(): void
233 {
234 $registry = new TableDefinitionRegistry();
235 $registry->register('users', new TableDefinition(['id', 'name'], ['id' => 'INTEGER', 'name' => 'TEXT'], ['id'], ['id'], []));
236 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
237 $mutation = $resolver->resolveAlterTable('ALTER TABLE users RENAME name TO full_name');
238 self::assertInstanceOf(AlterTableMutation::class, $mutation);
239 }
240
241 public function testResolveAlterTableAddColumnUnknownTableThrows(): void
242 {
243 $registry = new TableDefinitionRegistry();
244 $registry->register('users', new TableDefinition(['id'], [], [], [], []));
245 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
246 $mutation = $resolver->resolveAlterTable('ALTER TABLE users ADD COLUMN x');
247 self::assertInstanceOf(AlterTableMutation::class, $mutation);
248 }
249
250 public function testResolveAlterTableDropColumnUnknownThrows(): void
251 {
252 $registry = new TableDefinitionRegistry();
253 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
254 $this->expectException(UnknownSchemaException::class);
255 $resolver->resolveAlterTable('ALTER TABLE missing DROP COLUMN x');
256 }
257
258 public function testResolveAlterTableRenameColumnUnknownThrows(): void
259 {
260 $registry = new TableDefinitionRegistry();
261 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
262 $this->expectException(UnknownSchemaException::class);
263 $resolver->resolveAlterTable('ALTER TABLE missing RENAME COLUMN x TO y');
264 }
265
266 public function testResolveCreateTableIfNotExistsLowercase(): void
267 {
268 $resolver = new TableMutationResolver(new SqliteParser(), new TableDefinitionRegistry(), new SqliteSchemaParser());
269 $mutation = $resolver->resolveCreateTable('create table if not exists t (id integer primary key)');
270 self::assertInstanceOf(CreateTableMutation::class, $mutation);
271 }
272
273 public function testResolveDropTableIfExistsLowercase(): void
274 {
275 $resolver = new TableMutationResolver(new SqliteParser(), new TableDefinitionRegistry(), new SqliteSchemaParser());
276 $mutation = $resolver->resolveDropTable('drop table if exists nonexistent');
277 self::assertInstanceOf(DropTableMutation::class, $mutation);
278 }
279
280 public function testResolveAlterTableAddColumnLowercase(): void
281 {
282 $registry = new TableDefinitionRegistry();
283 $registry->register('users', new TableDefinition(['id'], ['id' => 'INTEGER'], ['id'], [], []));
284 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
285 $mutation = $resolver->resolveAlterTable('alter table users add column email text');
286 self::assertInstanceOf(AlterTableMutation::class, $mutation);
287 }
288
289 public function testResolveAlterTableDropColumnLowercase(): void
290 {
291 $registry = new TableDefinitionRegistry();
292 $registry->register('users', new TableDefinition(['id', 'email'], ['id' => 'INTEGER', 'email' => 'TEXT'], ['id'], [], []));
293 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
294 $mutation = $resolver->resolveAlterTable('alter table users drop column email');
295 self::assertInstanceOf(AlterTableMutation::class, $mutation);
296 }
297
298 public function testResolveAlterTableRenameToLowercase(): void
299 {
300 $registry = new TableDefinitionRegistry();
301 $registry->register('users', new TableDefinition(['id'], ['id' => 'INTEGER'], ['id'], [], []));
302 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
303 $mutation = $resolver->resolveAlterTable('alter table users rename to people');
304 self::assertInstanceOf(AlterTableMutation::class, $mutation);
305 }
306
307 public function testResolveAlterTableRenameColumnLowercase(): void
308 {
309 $registry = new TableDefinitionRegistry();
310 $registry->register('users', new TableDefinition(['id', 'name'], ['id' => 'INTEGER', 'name' => 'TEXT'], ['id'], [], []));
311 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
312 $mutation = $resolver->resolveAlterTable('alter table users rename column name to full_name');
313 self::assertInstanceOf(AlterTableMutation::class, $mutation);
314 }
315
316 public function testResolveAlterTableAddColumnNoKeyword(): void
317 {
318 $registry = new TableDefinitionRegistry();
319 $registry->register('t', new TableDefinition(['id'], ['id' => 'INTEGER'], ['id'], [], []));
320 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
321 $mutation = $resolver->resolveAlterTable('ALTER TABLE t ADD email TEXT');
322 self::assertInstanceOf(AlterTableMutation::class, $mutation);
323 }
324
325 public function testResolveAlterTableAddColumnWithParenthesizedType(): void
326 {
327 $registry = new TableDefinitionRegistry();
328 $registry->register('t', new TableDefinition(['id'], ['id' => 'INTEGER'], ['id'], [], []));
329 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
330 $mutation = $resolver->resolveAlterTable('ALTER TABLE t ADD COLUMN price DECIMAL(10,2)');
331 self::assertInstanceOf(AlterTableMutation::class, $mutation);
332 }
333
334 public function testResolveAlterTableAddColumnWithPrimaryKeyword(): void
335 {
336 $registry = new TableDefinitionRegistry();
337 $registry->register('t', new TableDefinition(['id'], ['id' => 'INTEGER'], ['id'], [], []));
338 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
339 $mutation = $resolver->resolveAlterTable('ALTER TABLE t ADD COLUMN note PRIMARY KEY');
340 self::assertInstanceOf(AlterTableMutation::class, $mutation);
341 }
342
343 public function testResolveAlterTableAddColumnVerifyNewColumns(): void
344 {
345 $registry = new TableDefinitionRegistry();
346 $registry->register('users', new TableDefinition(['id', 'name'], ['id' => 'INTEGER', 'name' => 'TEXT'], ['id'], ['id'], []));
347 $store = new ShadowStore();
348 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
349 $mutation = $resolver->resolveAlterTable('ALTER TABLE users ADD COLUMN phone TEXT');
350 self::assertInstanceOf(AlterTableMutation::class, $mutation);
351 self::assertSame('users', $mutation->tableName());
352 $registry->unregister('users');
353 $mutation->apply($store, []);
354 $def = $registry->get('users');
355 self::assertNotNull($def);
356 self::assertContains('phone', $def->columns);
357 self::assertContains('id', $def->columns);
358 self::assertContains('name', $def->columns);
359 self::assertSame('TEXT', $def->columnTypes['phone']);
360 }
361
362 public function testResolveAlterTableDropColumnVerifyRemovedFromAllLists(): void
363 {
364 $registry = new TableDefinitionRegistry();
365 $registry->register('users', new TableDefinition(['id', 'name', 'email'], ['id' => 'INTEGER', 'name' => 'TEXT', 'email' => 'TEXT'], ['id'], ['id', 'name'], ['email_unique' => ['email']]));
366 $store = new ShadowStore();
367 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
368 $mutation = $resolver->resolveAlterTable('ALTER TABLE users DROP COLUMN email');
369 self::assertInstanceOf(AlterTableMutation::class, $mutation);
370 self::assertSame('users', $mutation->tableName());
371 $registry->unregister('users');
372 $mutation->apply($store, []);
373 $def = $registry->get('users');
374 self::assertNotNull($def);
375 self::assertNotContains('email', $def->columns);
376 self::assertContains('id', $def->columns);
377 self::assertContains('name', $def->columns);
378 self::assertArrayNotHasKey('email', $def->columnTypes);
379 self::assertNotContains('email', $def->notNullColumns);
380 }
381
382 public function testResolveAlterTableRenameColumnVerifyAllUpdated(): void
383 {
384 $registry = new TableDefinitionRegistry();
385 $registry->register('users', new TableDefinition(['id', 'name'], ['id' => 'INTEGER', 'name' => 'TEXT'], ['id'], ['id', 'name'], ['name_unique' => ['name']]));
386 $store = new ShadowStore();
387 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
388 $mutation = $resolver->resolveAlterTable('ALTER TABLE users RENAME COLUMN name TO full_name');
389 self::assertInstanceOf(AlterTableMutation::class, $mutation);
390 self::assertSame('users', $mutation->tableName());
391 $registry->unregister('users');
392 $mutation->apply($store, []);
393 $def = $registry->get('users');
394 self::assertNotNull($def);
395 self::assertContains('full_name', $def->columns);
396 self::assertNotContains('name', $def->columns);
397 self::assertArrayHasKey('full_name', $def->columnTypes);
398 self::assertArrayNotHasKey('name', $def->columnTypes);
399 self::assertSame('TEXT', $def->columnTypes['full_name']);
400 self::assertContains('full_name', $def->notNullColumns);
401 self::assertNotContains('name', $def->notNullColumns);
402 }
403
404 public function testResolveAlterTableRenameColumnPrimaryKeysUpdated(): void
405 {
406 $registry = new TableDefinitionRegistry();
407 $registry->register('t', new TableDefinition(['id', 'code'], ['id' => 'INTEGER', 'code' => 'TEXT'], ['id', 'code'], [], []));
408 $store = new ShadowStore();
409 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
410 $mutation = $resolver->resolveAlterTable('ALTER TABLE t RENAME COLUMN code TO code_new');
411 self::assertInstanceOf(AlterTableMutation::class, $mutation);
412 $registry->unregister('t');
413 $mutation->apply($store, []);
414 $def = $registry->get('t');
415 self::assertNotNull($def);
416 self::assertContains('code_new', $def->primaryKeys);
417 self::assertNotContains('code', $def->primaryKeys);
418 }
419
420 public function testResolveAlterTableRenameColumnUniqueConstraintsUpdated(): void
421 {
422 $registry = new TableDefinitionRegistry();
423 $registry->register('t', new TableDefinition(['id', 'email'], ['id' => 'INTEGER', 'email' => 'TEXT'], ['id'], [], ['email_uq' => ['email']]));
424 $store = new ShadowStore();
425 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
426 $mutation = $resolver->resolveAlterTable('ALTER TABLE t RENAME COLUMN email TO mail');
427 $registry->unregister('t');
428 $mutation->apply($store, []);
429 $def = $registry->get('t');
430 self::assertNotNull($def);
431 self::assertSame(['mail'], $def->uniqueConstraints['email_uq']);
432 }
433
434 public function testResolveAlterTableRenameColumnTypedColumnsUpdated(): void
435 {
436 $registry = new TableDefinitionRegistry();
437 $registry->register('t', new TableDefinition(['id', 'val'], ['id' => 'INTEGER', 'val' => 'REAL'], ['id'], [], [], ['id' => new ColumnDeclaration(ColumnTypeFamily::INTEGER, 'INTEGER'), 'val' => new ColumnDeclaration(ColumnTypeFamily::FLOAT, 'REAL')]));
438 $store = new ShadowStore();
439 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
440 $mutation = $resolver->resolveAlterTable('ALTER TABLE t RENAME COLUMN val TO value');
441 $registry->unregister('t');
442 $mutation->apply($store, []);
443 $def = $registry->get('t');
444 self::assertNotNull($def);
445 self::assertArrayHasKey('value', $def->typedColumns);
446 self::assertArrayNotHasKey('val', $def->typedColumns);
447 }
448
449 public function testResolveAlterTableDropColumnPreservesPrimaryKeys(): void
450 {
451 $registry = new TableDefinitionRegistry();
452 $registry->register('t', new TableDefinition(['id', 'name', 'email'], ['id' => 'INTEGER', 'name' => 'TEXT', 'email' => 'TEXT'], ['id'], ['id'], []));
453 $store = new ShadowStore();
454 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
455 $mutation = $resolver->resolveAlterTable('ALTER TABLE t DROP COLUMN email');
456 $registry->unregister('t');
457 $mutation->apply($store, []);
458 $def = $registry->get('t');
459 self::assertNotNull($def);
460 self::assertSame(['id'], $def->primaryKeys);
461 }
462
463 public function testResolveAlterTableDropColumnRemovesPrimaryKeyIfDropped(): void
464 {
465 $registry = new TableDefinitionRegistry();
466 $registry->register('t', new TableDefinition(['id', 'code'], ['id' => 'INTEGER', 'code' => 'TEXT'], ['id', 'code'], [], []));
467 $store = new ShadowStore();
468 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
469 $mutation = $resolver->resolveAlterTable('ALTER TABLE t DROP COLUMN code');
470 $registry->unregister('t');
471 $mutation->apply($store, []);
472 $def = $registry->get('t');
473 self::assertNotNull($def);
474 self::assertSame(['id'], $def->primaryKeys);
475 self::assertNotContains('code', $def->primaryKeys);
476 }
477
478 public function testResolveAlterTableDropColumnRemovesUniqueConstraintIfEmpty(): void
479 {
480 $registry = new TableDefinitionRegistry();
481 $registry->register('t', new TableDefinition(['id', 'email'], ['id' => 'INTEGER', 'email' => 'TEXT'], ['id'], [], ['email_uq' => ['email']]));
482 $store = new ShadowStore();
483 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
484 $mutation = $resolver->resolveAlterTable('ALTER TABLE t DROP COLUMN email');
485 $registry->unregister('t');
486 $mutation->apply($store, []);
487 $def = $registry->get('t');
488 self::assertNotNull($def);
489 self::assertArrayNotHasKey('email_uq', $def->uniqueConstraints);
490 }
491
492 public function testResolveAlterTableDropColumnPreservesPartialUniqueConstraint(): void
493 {
494 $registry = new TableDefinitionRegistry();
495 $registry->register('t', new TableDefinition(['id', 'name', 'email'], ['id' => 'INTEGER', 'name' => 'TEXT', 'email' => 'TEXT'], ['id'], [], ['name_email_uq' => ['name', 'email']]));
496 $store = new ShadowStore();
497 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
498 $mutation = $resolver->resolveAlterTable('ALTER TABLE t DROP COLUMN email');
499 $registry->unregister('t');
500 $mutation->apply($store, []);
501 $def = $registry->get('t');
502 self::assertNotNull($def);
503 self::assertArrayHasKey('name_email_uq', $def->uniqueConstraints);
504 self::assertSame(['name'], $def->uniqueConstraints['name_email_uq']);
505 }
506
507 public function testResolveAlterTableAddColumnVerifyParenthesizedType(): void
508 {
509 $registry = new TableDefinitionRegistry();
510 $registry->register('t', new TableDefinition(['id'], ['id' => 'INTEGER'], ['id'], [], []));
511 $store = new ShadowStore();
512 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
513 $mutation = $resolver->resolveAlterTable('ALTER TABLE t ADD COLUMN price DECIMAL(10,2)');
514 $registry->unregister('t');
515 $mutation->apply($store, []);
516 $def = $registry->get('t');
517 self::assertNotNull($def);
518 self::assertContains('price', $def->columns);
519 self::assertSame('DECIMAL(10,2)', $def->columnTypes['price']);
520 }
521
522 public function testResolveCreateTableTableName(): void
523 {
524 $resolver = new TableMutationResolver(new SqliteParser(), new TableDefinitionRegistry(), new SqliteSchemaParser());
525 $mutation = $resolver->resolveCreateTable('CREATE TABLE orders (id INTEGER PRIMARY KEY)');
526 self::assertInstanceOf(CreateTableMutation::class, $mutation);
527 self::assertSame('orders', $mutation->tableName());
528 }
529
530 public function testResolveDropTableTableName(): void
531 {
532 $registry = new TableDefinitionRegistry();
533 $registry->register('users', new TableDefinition(['id'], ['id' => 'INTEGER'], ['id'], [], []));
534 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
535 $mutation = $resolver->resolveDropTable('DROP TABLE users');
536 self::assertInstanceOf(DropTableMutation::class, $mutation);
537 self::assertSame('users', $mutation->tableName());
538 }
539
540 public function testResolveAlterTableRenameToDropsOldTable(): void
541 {
542 $registry = new TableDefinitionRegistry();
543 $registry->register('old_t', new TableDefinition(['id'], ['id' => 'INTEGER'], ['id'], [], []));
544 $store = new ShadowStore();
545 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
546 $mutation = $resolver->resolveAlterTable('ALTER TABLE old_t RENAME TO new_t');
547 self::assertInstanceOf(AlterTableMutation::class, $mutation);
548 self::assertSame('old_t', $mutation->tableName());
549 }
550
551 public function testResolveAlterTableDropColumnTypedColumnsUpdated(): void
552 {
553 $registry = new TableDefinitionRegistry();
554 $registry->register('t', new TableDefinition(['id', 'val'], ['id' => 'INTEGER', 'val' => 'TEXT'], ['id'], [], [], ['id' => new ColumnDeclaration(ColumnTypeFamily::INTEGER, 'INTEGER'), 'val' => new ColumnDeclaration(ColumnTypeFamily::TEXT, 'TEXT')]));
555 $store = new ShadowStore();
556 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
557 $mutation = $resolver->resolveAlterTable('ALTER TABLE t DROP COLUMN val');
558 $registry->unregister('t');
559 $mutation->apply($store, []);
560 $def = $registry->get('t');
561 self::assertNotNull($def);
562 self::assertArrayNotHasKey('val', $def->typedColumns);
563 self::assertArrayHasKey('id', $def->typedColumns);
564 }
565
566 public function testResolveAlterTableAddColumnWithTypedColumns(): void
567 {
568 $registry = new TableDefinitionRegistry();
569 $registry->register('t', new TableDefinition(['id'], ['id' => 'INTEGER'], ['id'], [], [], ['id' => new ColumnDeclaration(ColumnTypeFamily::INTEGER, 'INTEGER')]));
570 $store = new ShadowStore();
571 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
572 $mutation = $resolver->resolveAlterTable('ALTER TABLE t ADD COLUMN val TEXT');
573 $registry->unregister('t');
574 $mutation->apply($store, []);
575 $def = $registry->get('t');
576 self::assertNotNull($def);
577 self::assertArrayHasKey('val', $def->typedColumns);
578 }
579
580 public function testResolveAlterTableDropColumnNotNullUpdated(): void
581 {
582 $registry = new TableDefinitionRegistry();
583 $registry->register('t', new TableDefinition(['id', 'name', 'email'], ['id' => 'INTEGER', 'name' => 'TEXT', 'email' => 'TEXT'], ['id'], ['id', 'name', 'email'], []));
584 $store = new ShadowStore();
585 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
586 $mutation = $resolver->resolveAlterTable('ALTER TABLE t DROP COLUMN name');
587 $registry->unregister('t');
588 $mutation->apply($store, []);
589 $def = $registry->get('t');
590 self::assertNotNull($def);
591 self::assertNotContains('name', $def->notNullColumns);
592 self::assertContains('id', $def->notNullColumns);
593 self::assertContains('email', $def->notNullColumns);
594 }
595
596 public function testResolveAlterTableRenameColumnNotNullUpdated(): void
597 {
598 $registry = new TableDefinitionRegistry();
599 $registry->register('t', new TableDefinition(['id', 'name'], ['id' => 'INTEGER', 'name' => 'TEXT'], ['id'], ['id', 'name'], []));
600 $store = new ShadowStore();
601 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
602 $mutation = $resolver->resolveAlterTable('ALTER TABLE t RENAME COLUMN name TO full_name');
603 $registry->unregister('t');
604 $mutation->apply($store, []);
605 $def = $registry->get('t');
606 self::assertNotNull($def);
607 self::assertContains('full_name', $def->notNullColumns);
608 self::assertNotContains('name', $def->notNullColumns);
609 }
610
611 public function testResolveCreateTableIfNotExistsLowercaseDoesNotThrow(): void
612 {
613 $store = new ShadowStore();
614 $registry = new TableDefinitionRegistry();
615 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
616 $mutation = $resolver->resolveCreateTable('CREATE TABLE if not exists t (id INTEGER)');
617 self::assertInstanceOf(CreateTableMutation::class, $mutation);
618 }
619
620 public function testResolveCreateTableAlreadyExistsNoIfNotExistsThrows(): void
621 {
622 $store = new ShadowStore();
623 $registry = new TableDefinitionRegistry();
624 $registry->register('t', new TableDefinition(['id'], ['id' => 'INTEGER'], ['id'], [], []));
625 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
626 $this->expectException(UnsupportedSqlException::class);
627 $resolver->resolveCreateTable('CREATE TABLE t (id INTEGER)');
628 }
629
630 public function testResolveAlterTableOnNonExistentTableThrows(): void
631 {
632 $store = new ShadowStore();
633 $registry = new TableDefinitionRegistry();
634 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
635 $this->expectException(UnknownSchemaException::class);
636 $resolver->resolveAlterTable('ALTER TABLE nonexistent ADD COLUMN a INTEGER');
637 }
638
639 public function testResolveAlterTableUnsupportedOperationThrowsForUnknownAction(): void
640 {
641 $store = new ShadowStore();
642 $registry = new TableDefinitionRegistry();
643 $registry->register('t', new TableDefinition(['id'], ['id' => 'INTEGER'], ['id'], [], []));
644 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
645 $this->expectException(UnsupportedSqlException::class);
646 $resolver->resolveAlterTable('ALTER TABLE t WHATEVER');
647 }
648
649 public function testResolveAlterTableAddColumnWithoutColumnKeyword(): void
650 {
651 $store = new ShadowStore();
652 $registry = new TableDefinitionRegistry();
653 $registry->register('t', new TableDefinition(['id'], ['id' => 'INTEGER'], ['id'], [], []));
654 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
655 $mutation = $resolver->resolveAlterTable('ALTER TABLE t ADD name TEXT');
656 $registry->unregister('t');
657 $mutation->apply($store, []);
658 $def = $registry->get('t');
659 self::assertNotNull($def);
660 self::assertContains('name', $def->columns);
661 }
662
663 public function testResolveAlterTableRenameColumnWithoutColumnKeyword(): void
664 {
665 $store = new ShadowStore();
666 $registry = new TableDefinitionRegistry();
667 $registry->register('t', new TableDefinition(['id', 'old_name'], ['id' => 'INTEGER', 'old_name' => 'TEXT'], ['id'], [], []));
668 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
669 $mutation = $resolver->resolveAlterTable('ALTER TABLE t RENAME old_name TO new_name');
670 $registry->unregister('t');
671 $mutation->apply($store, []);
672 $def = $registry->get('t');
673 self::assertNotNull($def);
674 self::assertContains('new_name', $def->columns);
675 self::assertNotContains('old_name', $def->columns);
676 }
677
678 public function testResolveAlterTableAddColumnAppliesNewColumnToDefinition(): void
679 {
680 $store = new ShadowStore();
681 $registry = new TableDefinitionRegistry();
682 $registry->register('t', new TableDefinition(['id'], ['id' => 'INTEGER'], ['id'], [], []));
683 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
684 $mutation = $resolver->resolveAlterTable('ALTER TABLE t ADD COLUMN status TEXT');
685 $registry->unregister('t');
686 $mutation->apply($store, []);
687 $def = $registry->get('t');
688 self::assertNotNull($def);
689 self::assertContains('status', $def->columns);
690 }
691
692 public function testResolveAlterTableAddColumnAppliesParenthesizedType(): void
693 {
694 $store = new ShadowStore();
695 $registry = new TableDefinitionRegistry();
696 $registry->register('t', new TableDefinition(['id'], ['id' => 'INTEGER'], ['id'], [], []));
697 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
698 $mutation = $resolver->resolveAlterTable('ALTER TABLE t ADD COLUMN price DECIMAL(10,2)');
699 $registry->unregister('t');
700 $mutation->apply($store, []);
701 $def = $registry->get('t');
702 self::assertNotNull($def);
703 self::assertContains('price', $def->columns);
704 self::assertSame('DECIMAL(10,2)', $def->columnTypes['price']);
705 }
706
707 public function testResolveDropTableIfExistsOnNonExistentTableDoesNotThrow(): void
708 {
709 $store = new ShadowStore();
710 $registry = new TableDefinitionRegistry();
711 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
712 $mutation = $resolver->resolveDropTable('DROP TABLE IF EXISTS nonexistent');
713 self::assertInstanceOf(DropTableMutation::class, $mutation);
714 }
715
716 public function testResolveDropTableNonExistentWithoutIfExistsThrows(): void
717 {
718 $store = new ShadowStore();
719 $registry = new TableDefinitionRegistry();
720 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
721 $this->expectException(UnknownSchemaException::class);
722 $resolver->resolveDropTable('DROP TABLE nonexistent');
723 }
724
725 public function testResolveCreateTableIfNotExistsExistingTableNoError(): void
726 {
727 $store = new ShadowStore();
728 $registry = new TableDefinitionRegistry();
729 $registry->register('t', new TableDefinition(['id'], ['id' => 'INTEGER'], ['id'], [], []));
730 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
731 $mutation = $resolver->resolveCreateTable('CREATE TABLE IF NOT EXISTS t (id INTEGER)');
732 self::assertInstanceOf(CreateTableMutation::class, $mutation);
733 }
734
735 public function testResolveDropTableExistingReturnsDropMutation(): void
736 {
737 $store = new ShadowStore();
738 $registry = new TableDefinitionRegistry();
739 $registry->register('t', new TableDefinition(['id'], ['id' => 'INTEGER'], ['id'], [], []));
740 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
741 $mutation = $resolver->resolveDropTable('DROP TABLE t');
742 self::assertInstanceOf(DropTableMutation::class, $mutation);
743 self::assertSame('t', $mutation->tableName());
744 }
745
746 public function testResolveAlterTableWithAddWithoutColumnKeywordDetected(): void
747 {
748 $store = new ShadowStore();
749 $registry = new TableDefinitionRegistry();
750 $registry->register('t', new TableDefinition(['id'], ['id' => 'INTEGER'], ['id'], [], []));
751 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
752 $mutation = $resolver->resolveAlterTable('ALTER TABLE t ADD email TEXT');
753 $registry->unregister('t');
754 $mutation->apply($store, []);
755 $def = $registry->get('t');
756 self::assertNotNull($def);
757 self::assertContains('email', $def->columns);
758 self::assertSame('TEXT', $def->columnTypes['email']);
759 }
760
761 public function testResolveCreateTableCaseInsensitiveIfNotExists(): void
762 {
763 $store = new ShadowStore();
764 $registry = new TableDefinitionRegistry();
765 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
766 $mutation = $resolver->resolveCreateTable('create table if not exists t (id integer primary key)');
767 self::assertInstanceOf(CreateTableMutation::class, $mutation);
768 }
769
770 public function testResolveAlterTableCaseInsensitiveAddColumn(): void
771 {
772 $store = new ShadowStore();
773 $registry = new TableDefinitionRegistry();
774 $registry->register('t', new TableDefinition(['id'], ['id' => 'INTEGER'], ['id'], [], []));
775 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
776 $mutation = $resolver->resolveAlterTable('alter table t add column name text');
777 $registry->unregister('t');
778 $mutation->apply($store, []);
779 $def = $registry->get('t');
780 self::assertNotNull($def);
781 self::assertContains('name', $def->columns);
782 }
783
784 public function testResolveAlterTableCaseInsensitiveDropColumn(): void
785 {
786 $store = new ShadowStore();
787 $registry = new TableDefinitionRegistry();
788 $registry->register('t', new TableDefinition(['id', 'name'], ['id' => 'INTEGER', 'name' => 'TEXT'], ['id'], ['id'], ['idx_name' => ['name']]));
789 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
790 $mutation = $resolver->resolveAlterTable('alter table t drop column name');
791 $registry->unregister('t');
792 $mutation->apply($store, []);
793 $def = $registry->get('t');
794 self::assertNotNull($def);
795 self::assertNotContains('name', $def->columns);
796 }
797
798 public function testResolveAlterTableCaseInsensitiveRenameColumn(): void
799 {
800 $store = new ShadowStore();
801 $registry = new TableDefinitionRegistry();
802 $registry->register('t', new TableDefinition(['id', 'name'], ['id' => 'INTEGER', 'name' => 'TEXT'], ['id'], ['name'], ['idx_name' => ['name']]));
803 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
804 $mutation = $resolver->resolveAlterTable('alter table t rename column name to full_name');
805 $registry->unregister('t');
806 $mutation->apply($store, []);
807 $def = $registry->get('t');
808 self::assertNotNull($def);
809 self::assertContains('full_name', $def->columns);
810 self::assertNotContains('name', $def->columns);
811 }
812
813 public function testResolveAlterTableCaseInsensitiveRenameTo(): void
814 {
815 $store = new ShadowStore();
816 $registry = new TableDefinitionRegistry();
817 $registry->register('t', new TableDefinition(['id'], ['id' => 'INTEGER'], ['id'], [], []));
818 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
819 $mutation = $resolver->resolveAlterTable('alter table t rename to t2');
820 self::assertInstanceOf(AlterTableMutation::class, $mutation);
821 }
822
823 public function testResolveDropTableCaseInsensitiveIfExists(): void
824 {
825 $store = new ShadowStore();
826 $registry = new TableDefinitionRegistry();
827 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
828 $mutation = $resolver->resolveDropTable('drop table if exists nonexistent');
829 self::assertInstanceOf(DropTableMutation::class, $mutation);
830 }
831
832 public function testResolveCreateTableIfNotExistsExistingTableLowercase(): void
833 {
834 $registry = new TableDefinitionRegistry();
835 $registry->register('t', new TableDefinition(['id'], ['id' => 'INTEGER'], ['id'], [], []));
836 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
837 $mutation = $resolver->resolveCreateTable('create table if not exists t (id integer primary key)');
838 self::assertInstanceOf(CreateTableMutation::class, $mutation);
839 }
840
841 public function testResolveDropTableIfExistsLowercaseRegex(): void
842 {
843 $registry = new TableDefinitionRegistry();
844 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
845 $mutation = $resolver->resolveDropTable('drop table if exists nonexistent');
846 self::assertInstanceOf(DropTableMutation::class, $mutation);
847 }
848
849 public function testResolveCreateTableIfNotExistsWithoutExistingTable(): void
850 {
851 $resolver = new TableMutationResolver(new SqliteParser(), new TableDefinitionRegistry(), new SqliteSchemaParser());
852 $mutation = $resolver->resolveCreateTable('CREATE TABLE IF NOT EXISTS t (id INTEGER PRIMARY KEY)');
853 self::assertInstanceOf(CreateTableMutation::class, $mutation);
854 self::assertSame('t', $mutation->tableName());
855 }
856
857 public function testResolveCreateTableWithoutIfNotExistsAlreadyExistsThrows(): void
858 {
859 $registry = new TableDefinitionRegistry();
860 $registry->register('t', new TableDefinition(['id'], ['id' => 'INTEGER'], ['id'], [], []));
861 $resolver = new TableMutationResolver(new SqliteParser(), $registry, new SqliteSchemaParser());
862 $this->expectException(UnsupportedSqlException::class);
863 $resolver->resolveCreateTable('CREATE TABLE t (id INTEGER PRIMARY KEY)');
864 }
865
866}
867