Failed Conditions
Pull Request — 2.10 (#3762)
by Benjamin
09:16
created

testSchemaDiffForeignKeys()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 34
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 21
c 0
b 0
f 0
dl 0
loc 34
rs 9.584
cc 2
nc 2
nop 0
1
<?php
2
3
namespace Doctrine\Tests\DBAL\Functional\Schema;
4
5
use Doctrine\Common\EventManager;
6
use Doctrine\DBAL\DBALException;
7
use Doctrine\DBAL\Driver\Connection;
8
use Doctrine\DBAL\Events;
9
use Doctrine\DBAL\Platforms\OraclePlatform;
10
use Doctrine\DBAL\Schema\AbstractAsset;
11
use Doctrine\DBAL\Schema\AbstractSchemaManager;
12
use Doctrine\DBAL\Schema\Column;
13
use Doctrine\DBAL\Schema\ColumnDiff;
14
use Doctrine\DBAL\Schema\Comparator;
15
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
16
use Doctrine\DBAL\Schema\Index;
17
use Doctrine\DBAL\Schema\Schema;
18
use Doctrine\DBAL\Schema\SchemaDiff;
19
use Doctrine\DBAL\Schema\Sequence;
20
use Doctrine\DBAL\Schema\Table;
21
use Doctrine\DBAL\Schema\TableDiff;
22
use Doctrine\DBAL\Schema\View;
23
use Doctrine\DBAL\Types\ArrayType;
24
use Doctrine\DBAL\Types\BinaryType;
25
use Doctrine\DBAL\Types\DateIntervalType;
26
use Doctrine\DBAL\Types\DateTimeType;
27
use Doctrine\DBAL\Types\DecimalType;
28
use Doctrine\DBAL\Types\IntegerType;
29
use Doctrine\DBAL\Types\ObjectType;
30
use Doctrine\DBAL\Types\StringType;
31
use Doctrine\DBAL\Types\TextType;
32
use Doctrine\DBAL\Types\Type;
33
use Doctrine\Tests\DbalFunctionalTestCase;
34
use function array_filter;
35
use function array_keys;
36
use function array_map;
37
use function array_search;
38
use function array_values;
39
use function count;
40
use function current;
41
use function end;
42
use function explode;
43
use function in_array;
44
use function sprintf;
45
use function str_replace;
46
use function strcasecmp;
47
use function strlen;
48
use function strtolower;
49
use function substr;
50
51
abstract class SchemaManagerFunctionalTestCase extends DbalFunctionalTestCase
52
{
53
    /** @var AbstractSchemaManager */
54
    protected $schemaManager;
55
56
    protected function getPlatformName() : string
57
    {
58
        $class     = static::class;
59
        $e         = explode('\\', $class);
60
        $testClass = end($e);
61
62
        return strtolower(str_replace('SchemaManagerTest', null, $testClass));
63
    }
64
65
    protected function setUp() : void
66
    {
67
        parent::setUp();
68
69
        $dbms = $this->getPlatformName();
70
71
        if ($this->connection->getDatabasePlatform()->getName() !== $dbms) {
72
            $this->markTestSkipped(static::class . ' requires the use of ' . $dbms);
73
        }
74
75
        $this->schemaManager = $this->connection->getSchemaManager();
76
    }
77
78
    protected function tearDown() : void
79
    {
80
        parent::tearDown();
81
82
        $this->schemaManager->tryMethod('dropTable', 'testschema.my_table_in_namespace');
83
84
        //TODO: SchemaDiff does not drop removed namespaces?
85
        try {
86
            //sql server versions below 2016 do not support 'IF EXISTS' so we have to catch the exception here
87
            $this->connection->exec('DROP SCHEMA testschema');
88
        } catch (DBALException $e) {
89
            return;
90
        }
91
    }
92
93
    /**
94
     * @group DBAL-1220
95
     */
96
    public function testDropsDatabaseWithActiveConnections() : void
97
    {
98
        if (! $this->schemaManager->getDatabasePlatform()->supportsCreateDropDatabase()) {
99
            $this->markTestSkipped('Cannot drop Database client side with this Driver.');
100
        }
101
102
        $this->schemaManager->dropAndCreateDatabase('test_drop_database');
103
104
        $knownDatabases = $this->schemaManager->listDatabases();
105
        if ($this->connection->getDatabasePlatform() instanceof OraclePlatform) {
106
            self::assertContains('TEST_DROP_DATABASE', $knownDatabases);
107
        } else {
108
            self::assertContains('test_drop_database', $knownDatabases);
109
        }
110
111
        $params = $this->connection->getParams();
112
        if ($this->connection->getDatabasePlatform() instanceof OraclePlatform) {
113
            $params['user'] = 'test_drop_database';
114
        } else {
115
            $params['dbname'] = 'test_drop_database';
116
        }
117
118
        $user     = $params['user'] ?? null;
119
        $password = $params['password'] ?? null;
120
121
        $connection = $this->connection->getDriver()->connect($params, $user, $password);
122
123
        self::assertInstanceOf(Connection::class, $connection);
124
125
        $this->schemaManager->dropDatabase('test_drop_database');
126
127
        self::assertNotContains('test_drop_database', $this->schemaManager->listDatabases());
128
    }
129
130
    /**
131
     * @group DBAL-195
132
     */
133
    public function testDropAndCreateSequence() : void
134
    {
135
        $platform = $this->connection->getDatabasePlatform();
136
137
        if (! $platform->supportsSequences()) {
138
            $this->markTestSkipped(
139
                sprintf('The "%s" platform does not support sequences.', $platform->getName())
140
            );
141
        }
142
143
        $name = 'dropcreate_sequences_test_seq';
144
145
        $this->schemaManager->dropAndCreateSequence(new Sequence($name, 20, 10));
146
147
        self::assertTrue($this->hasElementWithName($this->schemaManager->listSequences(), $name));
148
    }
149
150
    /**
151
     * @param AbstractAsset[] $items
152
     */
153
    private function hasElementWithName(array $items, string $name) : bool
154
    {
155
        $filteredList = array_filter(
156
            $items,
157
            static function (AbstractAsset $item) use ($name) : bool {
158
                return $item->getShortestName($item->getNamespaceName()) === $name;
159
            }
160
        );
161
162
        return count($filteredList) === 1;
163
    }
164
165
    public function testListSequences() : void
166
    {
167
        $platform = $this->connection->getDatabasePlatform();
168
169
        if (! $platform->supportsSequences()) {
170
            $this->markTestSkipped(
171
                sprintf('The "%s" platform does not support sequences.', $platform->getName())
172
            );
173
        }
174
175
        $sequence = new Sequence('list_sequences_test_seq', 20, 10);
176
        $this->schemaManager->createSequence($sequence);
177
178
        $sequences = $this->schemaManager->listSequences();
179
180
        self::assertIsArray($sequences, 'listSequences() should return an array.');
181
182
        $foundSequence = null;
183
        foreach ($sequences as $sequence) {
184
            self::assertInstanceOf(Sequence::class, $sequence, 'Array elements of listSequences() should be Sequence instances.');
185
            if (strtolower($sequence->getName()) !== 'list_sequences_test_seq') {
186
                continue;
187
            }
188
189
            $foundSequence = $sequence;
190
        }
191
192
        self::assertNotNull($foundSequence, "Sequence with name 'list_sequences_test_seq' was not found.");
193
        self::assertSame(20, $foundSequence->getAllocationSize(), 'Allocation Size is expected to be 20.');
194
        self::assertSame(10, $foundSequence->getInitialValue(), 'Initial Value is expected to be 10.');
195
    }
196
197
    public function testListDatabases() : void
198
    {
199
        if (! $this->schemaManager->getDatabasePlatform()->supportsCreateDropDatabase()) {
200
            $this->markTestSkipped('Cannot drop Database client side with this Driver.');
201
        }
202
203
        $this->schemaManager->dropAndCreateDatabase('test_create_database');
204
        $databases = $this->schemaManager->listDatabases();
205
206
        $databases = array_map('strtolower', $databases);
207
208
        self::assertContains('test_create_database', $databases);
209
    }
210
211
    /**
212
     * @group DBAL-1058
213
     */
214
    public function testListNamespaceNames() : void
215
    {
216
        if (! $this->schemaManager->getDatabasePlatform()->supportsSchemas()) {
217
            $this->markTestSkipped('Platform does not support schemas.');
218
        }
219
220
        // Currently dropping schemas is not supported, so we have to workaround here.
221
        $namespaces = $this->schemaManager->listNamespaceNames();
222
        $namespaces = array_map('strtolower', $namespaces);
223
224
        if (! in_array('test_create_schema', $namespaces)) {
225
            $this->connection->executeUpdate($this->schemaManager->getDatabasePlatform()->getCreateSchemaSQL('test_create_schema'));
226
227
            $namespaces = $this->schemaManager->listNamespaceNames();
228
            $namespaces = array_map('strtolower', $namespaces);
229
        }
230
231
        self::assertContains('test_create_schema', $namespaces);
232
    }
233
234
    public function testListTables() : void
235
    {
236
        $this->createTestTable('list_tables_test');
237
        $tables = $this->schemaManager->listTables();
238
239
        self::assertIsArray($tables);
240
        self::assertTrue(count($tables) > 0, "List Tables has to find at least one table named 'list_tables_test'.");
241
242
        $foundTable = false;
243
        foreach ($tables as $table) {
244
            self::assertInstanceOf(Table::class, $table);
245
            if (strtolower($table->getName()) !== 'list_tables_test') {
246
                continue;
247
            }
248
249
            $foundTable = true;
250
251
            self::assertTrue($table->hasColumn('id'));
252
            self::assertTrue($table->hasColumn('test'));
253
            self::assertTrue($table->hasColumn('foreign_key_test'));
254
        }
255
256
        self::assertTrue($foundTable, "The 'list_tables_test' table has to be found.");
257
    }
258
259
    public function createListTableColumns() : Table
260
    {
261
        $table = new Table('list_table_columns');
262
        $table->addColumn('id', 'integer', ['notnull' => true]);
263
        $table->addColumn('test', 'string', ['length' => 255, 'notnull' => false, 'default' => 'expected default']);
264
        $table->addColumn('foo', 'text', ['notnull' => true]);
265
        $table->addColumn('bar', 'decimal', ['precision' => 10, 'scale' => 4, 'notnull' => false]);
266
        $table->addColumn('baz1', 'datetime');
267
        $table->addColumn('baz2', 'time');
268
        $table->addColumn('baz3', 'date');
269
        $table->setPrimaryKey(['id']);
270
271
        return $table;
272
    }
273
274
    public function testListTableColumns() : void
275
    {
276
        $table = $this->createListTableColumns();
277
278
        $this->schemaManager->dropAndCreateTable($table);
279
280
        $columns     = $this->schemaManager->listTableColumns('list_table_columns');
281
        $columnsKeys = array_keys($columns);
282
283
        self::assertArrayHasKey('id', $columns);
284
        self::assertEquals(0, array_search('id', $columnsKeys));
285
        self::assertEquals('id', strtolower($columns['id']->getname()));
286
        self::assertInstanceOf(IntegerType::class, $columns['id']->gettype());
287
        self::assertEquals(false, $columns['id']->getunsigned());
288
        self::assertEquals(true, $columns['id']->getnotnull());
289
        self::assertEquals(null, $columns['id']->getdefault());
290
        self::assertIsArray($columns['id']->getPlatformOptions());
291
292
        self::assertArrayHasKey('test', $columns);
293
        self::assertEquals(1, array_search('test', $columnsKeys));
294
        self::assertEquals('test', strtolower($columns['test']->getname()));
295
        self::assertInstanceOf(StringType::class, $columns['test']->gettype());
296
        self::assertEquals(255, $columns['test']->getlength());
297
        self::assertEquals(false, $columns['test']->getfixed());
298
        self::assertEquals(false, $columns['test']->getnotnull());
299
        self::assertEquals('expected default', $columns['test']->getdefault());
300
        self::assertIsArray($columns['test']->getPlatformOptions());
301
302
        self::assertEquals('foo', strtolower($columns['foo']->getname()));
303
        self::assertEquals(2, array_search('foo', $columnsKeys));
304
        self::assertInstanceOf(TextType::class, $columns['foo']->gettype());
305
        self::assertEquals(false, $columns['foo']->getunsigned());
306
        self::assertEquals(false, $columns['foo']->getfixed());
307
        self::assertEquals(true, $columns['foo']->getnotnull());
308
        self::assertEquals(null, $columns['foo']->getdefault());
309
        self::assertIsArray($columns['foo']->getPlatformOptions());
310
311
        self::assertEquals('bar', strtolower($columns['bar']->getname()));
312
        self::assertEquals(3, array_search('bar', $columnsKeys));
313
        self::assertInstanceOf(DecimalType::class, $columns['bar']->gettype());
314
        self::assertEquals(null, $columns['bar']->getlength());
315
        self::assertEquals(10, $columns['bar']->getprecision());
316
        self::assertEquals(4, $columns['bar']->getscale());
317
        self::assertEquals(false, $columns['bar']->getunsigned());
318
        self::assertEquals(false, $columns['bar']->getfixed());
319
        self::assertEquals(false, $columns['bar']->getnotnull());
320
        self::assertEquals(null, $columns['bar']->getdefault());
321
        self::assertIsArray($columns['bar']->getPlatformOptions());
322
323
        self::assertEquals('baz1', strtolower($columns['baz1']->getname()));
324
        self::assertEquals(4, array_search('baz1', $columnsKeys));
325
        self::assertInstanceOf(DateTimeType::class, $columns['baz1']->gettype());
326
        self::assertEquals(true, $columns['baz1']->getnotnull());
327
        self::assertEquals(null, $columns['baz1']->getdefault());
328
        self::assertIsArray($columns['baz1']->getPlatformOptions());
329
330
        self::assertEquals('baz2', strtolower($columns['baz2']->getname()));
331
        self::assertEquals(5, array_search('baz2', $columnsKeys));
332
        self::assertContains($columns['baz2']->gettype()->getName(), ['time', 'date', 'datetime']);
333
        self::assertEquals(true, $columns['baz2']->getnotnull());
334
        self::assertEquals(null, $columns['baz2']->getdefault());
335
        self::assertIsArray($columns['baz2']->getPlatformOptions());
336
337
        self::assertEquals('baz3', strtolower($columns['baz3']->getname()));
338
        self::assertEquals(6, array_search('baz3', $columnsKeys));
339
        self::assertContains($columns['baz3']->gettype()->getName(), ['time', 'date', 'datetime']);
340
        self::assertEquals(true, $columns['baz3']->getnotnull());
341
        self::assertEquals(null, $columns['baz3']->getdefault());
342
        self::assertIsArray($columns['baz3']->getPlatformOptions());
343
    }
344
345
    /**
346
     * @group DBAL-1078
347
     */
348
    public function testListTableColumnsWithFixedStringColumn() : void
349
    {
350
        $tableName = 'test_list_table_fixed_string';
351
352
        $table = new Table($tableName);
353
        $table->addColumn('column_char', 'string', ['fixed' => true, 'length' => 2]);
354
355
        $this->schemaManager->createTable($table);
356
357
        $columns = $this->schemaManager->listTableColumns($tableName);
358
359
        self::assertArrayHasKey('column_char', $columns);
360
        self::assertInstanceOf(StringType::class, $columns['column_char']->getType());
361
        self::assertTrue($columns['column_char']->getFixed());
362
        self::assertSame(2, $columns['column_char']->getLength());
363
    }
364
365
    public function testListTableColumnsDispatchEvent() : void
366
    {
367
        $table = $this->createListTableColumns();
368
369
        $this->schemaManager->dropAndCreateTable($table);
370
371
        $listenerMock = $this->getMockBuilder($this->getMockClass('ListTableColumnsDispatchEventListener'))
372
            ->addMethods(['onSchemaColumnDefinition'])
373
            ->getMock();
374
375
        $listenerMock
376
            ->expects($this->exactly(7))
377
            ->method('onSchemaColumnDefinition');
378
379
        $oldEventManager = $this->schemaManager->getDatabasePlatform()->getEventManager();
380
381
        $eventManager = new EventManager();
382
        $eventManager->addEventListener([Events::onSchemaColumnDefinition], $listenerMock);
383
384
        $this->schemaManager->getDatabasePlatform()->setEventManager($eventManager);
385
386
        $this->schemaManager->listTableColumns('list_table_columns');
387
388
        $this->schemaManager->getDatabasePlatform()->setEventManager($oldEventManager);
389
    }
390
391
    public function testListTableIndexesDispatchEvent() : void
392
    {
393
        $table = $this->getTestTable('list_table_indexes_test');
394
        $table->addUniqueIndex(['test'], 'test_index_name');
395
        $table->addIndex(['id', 'test'], 'test_composite_idx');
396
397
        $this->schemaManager->dropAndCreateTable($table);
398
399
        $listenerMock = $this->getMockBuilder($this->getMockClass('ListTableIndexesDispatchEventListener'))
400
            ->addMethods(['onSchemaIndexDefinition'])
401
            ->getMock();
402
        $listenerMock
403
            ->expects($this->exactly(3))
404
            ->method('onSchemaIndexDefinition');
405
406
        $oldEventManager = $this->schemaManager->getDatabasePlatform()->getEventManager();
407
408
        $eventManager = new EventManager();
409
        $eventManager->addEventListener([Events::onSchemaIndexDefinition], $listenerMock);
410
411
        $this->schemaManager->getDatabasePlatform()->setEventManager($eventManager);
412
413
        $this->schemaManager->listTableIndexes('list_table_indexes_test');
414
415
        $this->schemaManager->getDatabasePlatform()->setEventManager($oldEventManager);
416
    }
417
418
    public function testDiffListTableColumns() : void
419
    {
420
        if ($this->schemaManager->getDatabasePlatform()->getName() === 'oracle') {
421
            $this->markTestSkipped('Does not work with Oracle, since it cannot detect DateTime, Date and Time differenecs (at the moment).');
422
        }
423
424
        $offlineTable = $this->createListTableColumns();
425
        $this->schemaManager->dropAndCreateTable($offlineTable);
426
        $onlineTable = $this->schemaManager->listTableDetails('list_table_columns');
427
428
        $comparator = new Comparator();
429
        $diff       = $comparator->diffTable($offlineTable, $onlineTable);
430
431
        self::assertFalse($diff, 'No differences should be detected with the offline vs online schema.');
432
    }
433
434
    public function testListTableIndexes() : void
435
    {
436
        $table = $this->getTestCompositeTable('list_table_indexes_test');
437
        $table->addUniqueIndex(['test'], 'test_index_name');
438
        $table->addIndex(['id', 'test'], 'test_composite_idx');
439
440
        $this->schemaManager->dropAndCreateTable($table);
441
442
        $tableIndexes = $this->schemaManager->listTableIndexes('list_table_indexes_test');
443
444
        self::assertEquals(3, count($tableIndexes));
445
446
        self::assertArrayHasKey('primary', $tableIndexes, 'listTableIndexes() has to return a "primary" array key.');
447
        self::assertEquals(['id', 'other_id'], array_map('strtolower', $tableIndexes['primary']->getColumns()));
448
        self::assertTrue($tableIndexes['primary']->isUnique());
449
        self::assertTrue($tableIndexes['primary']->isPrimary());
450
451
        self::assertEquals('test_index_name', strtolower($tableIndexes['test_index_name']->getName()));
452
        self::assertEquals(['test'], array_map('strtolower', $tableIndexes['test_index_name']->getColumns()));
453
        self::assertTrue($tableIndexes['test_index_name']->isUnique());
454
        self::assertFalse($tableIndexes['test_index_name']->isPrimary());
455
456
        self::assertEquals('test_composite_idx', strtolower($tableIndexes['test_composite_idx']->getName()));
457
        self::assertEquals(['id', 'test'], array_map('strtolower', $tableIndexes['test_composite_idx']->getColumns()));
458
        self::assertFalse($tableIndexes['test_composite_idx']->isUnique());
459
        self::assertFalse($tableIndexes['test_composite_idx']->isPrimary());
460
    }
461
462
    public function testDropAndCreateIndex() : void
463
    {
464
        $table = $this->getTestTable('test_create_index');
465
        $table->addUniqueIndex(['test'], 'test');
466
        $this->schemaManager->dropAndCreateTable($table);
467
468
        $this->schemaManager->dropAndCreateIndex($table->getIndex('test'), $table);
469
        $tableIndexes = $this->schemaManager->listTableIndexes('test_create_index');
470
        self::assertIsArray($tableIndexes);
471
472
        self::assertEquals('test', strtolower($tableIndexes['test']->getName()));
473
        self::assertEquals(['test'], array_map('strtolower', $tableIndexes['test']->getColumns()));
474
        self::assertTrue($tableIndexes['test']->isUnique());
475
        self::assertFalse($tableIndexes['test']->isPrimary());
476
    }
477
478
    public function testCreateTableWithForeignKeys() : void
479
    {
480
        if (! $this->schemaManager->getDatabasePlatform()->supportsForeignKeyConstraints()) {
481
            $this->markTestSkipped('Platform does not support foreign keys.');
482
        }
483
484
        $tableB = $this->getTestTable('test_foreign');
485
486
        $this->schemaManager->dropAndCreateTable($tableB);
487
488
        $tableA = $this->getTestTable('test_create_fk');
489
        $tableA->addForeignKeyConstraint('test_foreign', ['foreign_key_test'], ['id']);
490
491
        $this->schemaManager->dropAndCreateTable($tableA);
492
493
        $fkTable       = $this->schemaManager->listTableDetails('test_create_fk');
494
        $fkConstraints = $fkTable->getForeignKeys();
495
        self::assertEquals(1, count($fkConstraints), "Table 'test_create_fk1' has to have one foreign key.");
496
497
        $fkConstraint = current($fkConstraints);
498
        self::assertInstanceOf(ForeignKeyConstraint::class, $fkConstraint);
499
        self::assertEquals('test_foreign', strtolower($fkConstraint->getForeignTableName()));
500
        self::assertEquals(['foreign_key_test'], array_map('strtolower', $fkConstraint->getColumns()));
501
        self::assertEquals(['id'], array_map('strtolower', $fkConstraint->getForeignColumns()));
502
503
        self::assertTrue($fkTable->columnsAreIndexed($fkConstraint->getColumns()), 'The columns of a foreign key constraint should always be indexed.');
504
    }
505
506
    public function testListForeignKeys() : void
507
    {
508
        if (! $this->connection->getDatabasePlatform()->supportsForeignKeyConstraints()) {
509
            $this->markTestSkipped('Does not support foreign key constraints.');
510
        }
511
512
        $this->createTestTable('test_create_fk1');
513
        $this->createTestTable('test_create_fk2');
514
515
        $foreignKey = new ForeignKeyConstraint(
516
            ['foreign_key_test'],
517
            'test_create_fk2',
518
            ['id'],
519
            'foreign_key_test_fk',
520
            ['onDelete' => 'CASCADE']
521
        );
522
523
        $this->schemaManager->createForeignKey($foreignKey, 'test_create_fk1');
524
525
        $fkeys = $this->schemaManager->listTableForeignKeys('test_create_fk1');
526
527
        self::assertEquals(1, count($fkeys), "Table 'test_create_fk1' has to have one foreign key.");
528
529
        self::assertInstanceOf(ForeignKeyConstraint::class, $fkeys[0]);
530
        self::assertEquals(['foreign_key_test'], array_map('strtolower', $fkeys[0]->getLocalColumns()));
531
        self::assertEquals(['id'], array_map('strtolower', $fkeys[0]->getForeignColumns()));
532
        self::assertEquals('test_create_fk2', strtolower($fkeys[0]->getForeignTableName()));
533
534
        if (! $fkeys[0]->hasOption('onDelete')) {
535
            return;
536
        }
537
538
        self::assertEquals('CASCADE', $fkeys[0]->getOption('onDelete'));
539
    }
540
541
    protected function getCreateExampleViewSql() : void
542
    {
543
        $this->markTestSkipped('No Create Example View SQL was defined for this SchemaManager');
544
    }
545
546
    public function testCreateSchema() : void
547
    {
548
        $this->createTestTable('test_table');
549
550
        $schema = $this->schemaManager->createSchema();
551
        self::assertTrue($schema->hasTable('test_table'));
552
    }
553
554
    public function testAlterTableScenario() : void
555
    {
556
        if (! $this->schemaManager->getDatabasePlatform()->supportsAlterTable()) {
557
            $this->markTestSkipped('Alter Table is not supported by this platform.');
558
        }
559
560
        $alterTable = $this->createTestTable('alter_table');
561
        $this->createTestTable('alter_table_foreign');
562
563
        $table = $this->schemaManager->listTableDetails('alter_table');
564
        self::assertTrue($table->hasColumn('id'));
565
        self::assertTrue($table->hasColumn('test'));
566
        self::assertTrue($table->hasColumn('foreign_key_test'));
567
        self::assertEquals(0, count($table->getForeignKeys()));
568
        self::assertEquals(1, count($table->getIndexes()));
569
570
        $tableDiff                         = new TableDiff('alter_table');
571
        $tableDiff->fromTable              = $alterTable;
572
        $tableDiff->addedColumns['foo']    = new Column('foo', Type::getType('integer'));
573
        $tableDiff->removedColumns['test'] = $table->getColumn('test');
574
575
        $this->schemaManager->alterTable($tableDiff);
576
577
        $table = $this->schemaManager->listTableDetails('alter_table');
578
        self::assertFalse($table->hasColumn('test'));
579
        self::assertTrue($table->hasColumn('foo'));
580
581
        $tableDiff                 = new TableDiff('alter_table');
582
        $tableDiff->fromTable      = $table;
583
        $tableDiff->addedIndexes[] = new Index('foo_idx', ['foo']);
584
585
        $this->schemaManager->alterTable($tableDiff);
586
587
        $table = $this->schemaManager->listTableDetails('alter_table');
588
        self::assertEquals(2, count($table->getIndexes()));
589
        self::assertTrue($table->hasIndex('foo_idx'));
590
        self::assertEquals(['foo'], array_map('strtolower', $table->getIndex('foo_idx')->getColumns()));
591
        self::assertFalse($table->getIndex('foo_idx')->isPrimary());
592
        self::assertFalse($table->getIndex('foo_idx')->isUnique());
593
594
        $tableDiff                   = new TableDiff('alter_table');
595
        $tableDiff->fromTable        = $table;
596
        $tableDiff->changedIndexes[] = new Index('foo_idx', ['foo', 'foreign_key_test']);
597
598
        $this->schemaManager->alterTable($tableDiff);
599
600
        $table = $this->schemaManager->listTableDetails('alter_table');
601
        self::assertEquals(2, count($table->getIndexes()));
602
        self::assertTrue($table->hasIndex('foo_idx'));
603
        self::assertEquals(['foo', 'foreign_key_test'], array_map('strtolower', $table->getIndex('foo_idx')->getColumns()));
604
605
        $tableDiff                            = new TableDiff('alter_table');
606
        $tableDiff->fromTable                 = $table;
607
        $tableDiff->renamedIndexes['foo_idx'] = new Index('bar_idx', ['foo', 'foreign_key_test']);
608
609
        $this->schemaManager->alterTable($tableDiff);
610
611
        $table = $this->schemaManager->listTableDetails('alter_table');
612
        self::assertEquals(2, count($table->getIndexes()));
613
        self::assertTrue($table->hasIndex('bar_idx'));
614
        self::assertFalse($table->hasIndex('foo_idx'));
615
        self::assertEquals(['foo', 'foreign_key_test'], array_map('strtolower', $table->getIndex('bar_idx')->getColumns()));
616
        self::assertFalse($table->getIndex('bar_idx')->isPrimary());
617
        self::assertFalse($table->getIndex('bar_idx')->isUnique());
618
619
        $tableDiff                     = new TableDiff('alter_table');
620
        $tableDiff->fromTable          = $table;
621
        $tableDiff->removedIndexes[]   = new Index('bar_idx', ['foo', 'foreign_key_test']);
622
        $fk                            = new ForeignKeyConstraint(['foreign_key_test'], 'alter_table_foreign', ['id']);
623
        $tableDiff->addedForeignKeys[] = $fk;
624
625
        $this->schemaManager->alterTable($tableDiff);
626
        $table = $this->schemaManager->listTableDetails('alter_table');
627
628
        // dont check for index size here, some platforms automatically add indexes for foreign keys.
629
        self::assertFalse($table->hasIndex('bar_idx'));
630
631
        if (! $this->schemaManager->getDatabasePlatform()->supportsForeignKeyConstraints()) {
632
            return;
633
        }
634
635
        $fks = $table->getForeignKeys();
636
        self::assertCount(1, $fks);
637
        $foreignKey = current($fks);
638
        self::assertEquals('alter_table_foreign', strtolower($foreignKey->getForeignTableName()));
639
        self::assertEquals(['foreign_key_test'], array_map('strtolower', $foreignKey->getColumns()));
640
        self::assertEquals(['id'], array_map('strtolower', $foreignKey->getForeignColumns()));
641
    }
642
643
    public function testTableInNamespace() : void
644
    {
645
        if (! $this->schemaManager->getDatabasePlatform()->supportsSchemas()) {
646
            $this->markTestSkipped('Schema definition is not supported by this platform.');
647
        }
648
649
        //create schema
650
        $diff                  = new SchemaDiff();
651
        $diff->newNamespaces[] = 'testschema';
652
653
        foreach ($diff->toSql($this->schemaManager->getDatabasePlatform()) as $sql) {
654
            $this->connection->exec($sql);
655
        }
656
657
        //test if table is create in namespace
658
        $this->createTestTable('testschema.my_table_in_namespace');
659
        self::assertContains('testschema.my_table_in_namespace', $this->schemaManager->listTableNames());
660
661
        //tables without namespace should be created in default namespace
662
        //default namespaces are ignored in table listings
663
        $this->createTestTable('my_table_not_in_namespace');
664
        self::assertContains('my_table_not_in_namespace', $this->schemaManager->listTableNames());
665
    }
666
667
    public function testCreateAndListViews() : void
668
    {
669
        if (! $this->schemaManager->getDatabasePlatform()->supportsViews()) {
670
            $this->markTestSkipped('Views is not supported by this platform.');
671
        }
672
673
        $this->createTestTable('view_test_table');
674
675
        $name = 'doctrine_test_view';
676
        $sql  = 'SELECT * FROM view_test_table';
677
678
        $view = new View($name, $sql);
679
680
        $this->schemaManager->dropAndCreateView($view);
681
682
        self::assertTrue($this->hasElementWithName($this->schemaManager->listViews(), $name));
683
    }
684
685
    public function testAutoincrementDetection() : void
686
    {
687
        if (! $this->schemaManager->getDatabasePlatform()->supportsIdentityColumns()) {
688
            $this->markTestSkipped('This test is only supported on platforms that have autoincrement');
689
        }
690
691
        $table = new Table('test_autoincrement');
692
        $table->setSchemaConfig($this->schemaManager->createSchemaConfig());
693
        $table->addColumn('id', 'integer', ['autoincrement' => true]);
694
        $table->setPrimaryKey(['id']);
695
696
        $this->schemaManager->createTable($table);
697
698
        $inferredTable = $this->schemaManager->listTableDetails('test_autoincrement');
699
        self::assertTrue($inferredTable->hasColumn('id'));
700
        self::assertTrue($inferredTable->getColumn('id')->getAutoincrement());
701
    }
702
703
    /**
704
     * @group DBAL-792
705
     */
706
    public function testAutoincrementDetectionMulticolumns() : void
707
    {
708
        if (! $this->schemaManager->getDatabasePlatform()->supportsIdentityColumns()) {
709
            $this->markTestSkipped('This test is only supported on platforms that have autoincrement');
710
        }
711
712
        $table = new Table('test_not_autoincrement');
713
        $table->setSchemaConfig($this->schemaManager->createSchemaConfig());
714
        $table->addColumn('id', 'integer');
715
        $table->addColumn('other_id', 'integer');
716
        $table->setPrimaryKey(['id', 'other_id']);
717
718
        $this->schemaManager->createTable($table);
719
720
        $inferredTable = $this->schemaManager->listTableDetails('test_not_autoincrement');
721
        self::assertTrue($inferredTable->hasColumn('id'));
722
        self::assertFalse($inferredTable->getColumn('id')->getAutoincrement());
723
    }
724
725
    /**
726
     * @group DDC-887
727
     */
728
    public function testUpdateSchemaWithForeignKeyRenaming() : void
729
    {
730
        if (! $this->schemaManager->getDatabasePlatform()->supportsForeignKeyConstraints()) {
731
            $this->markTestSkipped('This test is only supported on platforms that have foreign keys.');
732
        }
733
734
        $table = new Table('test_fk_base');
735
        $table->addColumn('id', 'integer');
736
        $table->setPrimaryKey(['id']);
737
738
        $tableFK = new Table('test_fk_rename');
739
        $tableFK->setSchemaConfig($this->schemaManager->createSchemaConfig());
740
        $tableFK->addColumn('id', 'integer');
741
        $tableFK->addColumn('fk_id', 'integer');
742
        $tableFK->setPrimaryKey(['id']);
743
        $tableFK->addIndex(['fk_id'], 'fk_idx');
744
        $tableFK->addForeignKeyConstraint('test_fk_base', ['fk_id'], ['id']);
745
746
        $this->schemaManager->createTable($table);
747
        $this->schemaManager->createTable($tableFK);
748
749
        $tableFKNew = new Table('test_fk_rename');
750
        $tableFKNew->setSchemaConfig($this->schemaManager->createSchemaConfig());
751
        $tableFKNew->addColumn('id', 'integer');
752
        $tableFKNew->addColumn('rename_fk_id', 'integer');
753
        $tableFKNew->setPrimaryKey(['id']);
754
        $tableFKNew->addIndex(['rename_fk_id'], 'fk_idx');
755
        $tableFKNew->addForeignKeyConstraint('test_fk_base', ['rename_fk_id'], ['id']);
756
757
        $c         = new Comparator();
758
        $tableDiff = $c->diffTable($tableFK, $tableFKNew);
759
760
        $this->schemaManager->alterTable($tableDiff);
0 ignored issues
show
Bug introduced by
It seems like $tableDiff can also be of type false; however, parameter $tableDiff of Doctrine\DBAL\Schema\Abs...maManager::alterTable() does only seem to accept Doctrine\DBAL\Schema\TableDiff, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

760
        $this->schemaManager->alterTable(/** @scrutinizer ignore-type */ $tableDiff);
Loading history...
761
762
        $table       = $this->schemaManager->listTableDetails('test_fk_rename');
763
        $foreignKeys = $table->getForeignKeys();
764
765
        self::assertTrue($table->hasColumn('rename_fk_id'));
766
        self::assertCount(1, $foreignKeys);
767
        self::assertSame(['rename_fk_id'], array_map('strtolower', current($foreignKeys)->getColumns()));
768
    }
769
770
    /**
771
     * @group DBAL-1062
772
     */
773
    public function testRenameIndexUsedInForeignKeyConstraint() : void
774
    {
775
        if (! $this->schemaManager->getDatabasePlatform()->supportsForeignKeyConstraints()) {
776
            $this->markTestSkipped('This test is only supported on platforms that have foreign keys.');
777
        }
778
779
        $primaryTable = new Table('test_rename_index_primary');
780
        $primaryTable->addColumn('id', 'integer');
781
        $primaryTable->setPrimaryKey(['id']);
782
783
        $foreignTable = new Table('test_rename_index_foreign');
784
        $foreignTable->addColumn('fk', 'integer');
785
        $foreignTable->addIndex(['fk'], 'rename_index_fk_idx');
786
        $foreignTable->addForeignKeyConstraint(
787
            'test_rename_index_primary',
788
            ['fk'],
789
            ['id'],
790
            [],
791
            'fk_constraint'
792
        );
793
794
        $this->schemaManager->dropAndCreateTable($primaryTable);
795
        $this->schemaManager->dropAndCreateTable($foreignTable);
796
797
        $foreignTable2 = clone $foreignTable;
798
        $foreignTable2->renameIndex('rename_index_fk_idx', 'renamed_index_fk_idx');
799
800
        $comparator = new Comparator();
801
802
        $this->schemaManager->alterTable($comparator->diffTable($foreignTable, $foreignTable2));
0 ignored issues
show
Bug introduced by
It seems like $comparator->diffTable($...nTable, $foreignTable2) can also be of type false; however, parameter $tableDiff of Doctrine\DBAL\Schema\Abs...maManager::alterTable() does only seem to accept Doctrine\DBAL\Schema\TableDiff, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

802
        $this->schemaManager->alterTable(/** @scrutinizer ignore-type */ $comparator->diffTable($foreignTable, $foreignTable2));
Loading history...
803
804
        $foreignTable = $this->schemaManager->listTableDetails('test_rename_index_foreign');
805
806
        self::assertFalse($foreignTable->hasIndex('rename_index_fk_idx'));
807
        self::assertTrue($foreignTable->hasIndex('renamed_index_fk_idx'));
808
        self::assertTrue($foreignTable->hasForeignKey('fk_constraint'));
809
    }
810
811
    /**
812
     * @group DBAL-42
813
     */
814
    public function testGetColumnComment() : void
815
    {
816
        if (! $this->connection->getDatabasePlatform()->supportsInlineColumnComments() &&
817
             ! $this->connection->getDatabasePlatform()->supportsCommentOnStatement() &&
818
            $this->connection->getDatabasePlatform()->getName() !== 'mssql') {
819
            $this->markTestSkipped('Database does not support column comments.');
820
        }
821
822
        $table = new Table('column_comment_test');
823
        $table->addColumn('id', 'integer', ['comment' => 'This is a comment']);
824
        $table->setPrimaryKey(['id']);
825
826
        $this->schemaManager->createTable($table);
827
828
        $columns = $this->schemaManager->listTableColumns('column_comment_test');
829
        self::assertEquals(1, count($columns));
830
        self::assertEquals('This is a comment', $columns['id']->getComment());
831
832
        $tableDiff                       = new TableDiff('column_comment_test');
833
        $tableDiff->fromTable            = $table;
834
        $tableDiff->changedColumns['id'] = new ColumnDiff(
835
            'id',
836
            new Column(
837
                'id',
838
                Type::getType('integer')
839
            ),
840
            ['comment'],
841
            new Column(
842
                'id',
843
                Type::getType('integer'),
844
                ['comment' => 'This is a comment']
845
            )
846
        );
847
848
        $this->schemaManager->alterTable($tableDiff);
849
850
        $columns = $this->schemaManager->listTableColumns('column_comment_test');
851
        self::assertEquals(1, count($columns));
852
        self::assertEmpty($columns['id']->getComment());
853
    }
854
855
    /**
856
     * @group DBAL-42
857
     */
858
    public function testAutomaticallyAppendCommentOnMarkedColumns() : void
859
    {
860
        if (! $this->connection->getDatabasePlatform()->supportsInlineColumnComments() &&
861
             ! $this->connection->getDatabasePlatform()->supportsCommentOnStatement() &&
862
            $this->connection->getDatabasePlatform()->getName() !== 'mssql') {
863
            $this->markTestSkipped('Database does not support column comments.');
864
        }
865
866
        $table = new Table('column_comment_test2');
867
        $table->addColumn('id', 'integer', ['comment' => 'This is a comment']);
868
        $table->addColumn('obj', 'object', ['comment' => 'This is a comment']);
869
        $table->addColumn('arr', 'array', ['comment' => 'This is a comment']);
870
        $table->setPrimaryKey(['id']);
871
872
        $this->schemaManager->createTable($table);
873
874
        $columns = $this->schemaManager->listTableColumns('column_comment_test2');
875
        self::assertEquals(3, count($columns));
876
        self::assertEquals('This is a comment', $columns['id']->getComment());
877
        self::assertEquals('This is a comment', $columns['obj']->getComment(), 'The Doctrine2 Typehint should be stripped from comment.');
878
        self::assertInstanceOf(ObjectType::class, $columns['obj']->getType(), 'The Doctrine2 should be detected from comment hint.');
879
        self::assertEquals('This is a comment', $columns['arr']->getComment(), 'The Doctrine2 Typehint should be stripped from comment.');
880
        self::assertInstanceOf(ArrayType::class, $columns['arr']->getType(), 'The Doctrine2 should be detected from comment hint.');
881
    }
882
883
    /**
884
     * @group DBAL-1228
885
     */
886
    public function testCommentHintOnDateIntervalTypeColumn() : void
887
    {
888
        if (! $this->connection->getDatabasePlatform()->supportsInlineColumnComments() &&
889
            ! $this->connection->getDatabasePlatform()->supportsCommentOnStatement() &&
890
            $this->connection->getDatabasePlatform()->getName() !== 'mssql') {
891
            $this->markTestSkipped('Database does not support column comments.');
892
        }
893
894
        $table = new Table('column_dateinterval_comment');
895
        $table->addColumn('id', 'integer', ['comment' => 'This is a comment']);
896
        $table->addColumn('date_interval', 'dateinterval', ['comment' => 'This is a comment']);
897
        $table->setPrimaryKey(['id']);
898
899
        $this->schemaManager->createTable($table);
900
901
        $columns = $this->schemaManager->listTableColumns('column_dateinterval_comment');
902
        self::assertEquals(2, count($columns));
903
        self::assertEquals('This is a comment', $columns['id']->getComment());
904
        self::assertEquals('This is a comment', $columns['date_interval']->getComment(), 'The Doctrine2 Typehint should be stripped from comment.');
905
        self::assertInstanceOf(DateIntervalType::class, $columns['date_interval']->getType(), 'The Doctrine2 should be detected from comment hint.');
906
    }
907
908
    /**
909
     * @group DBAL-825
910
     */
911
    public function testChangeColumnsTypeWithDefaultValue() : void
912
    {
913
        $tableName = 'column_def_change_type';
914
        $table     = new Table($tableName);
915
916
        $table->addColumn('col_int', 'smallint', ['default' => 666]);
917
        $table->addColumn('col_string', 'string', ['default' => 'foo']);
918
919
        $this->schemaManager->dropAndCreateTable($table);
920
921
        $tableDiff                            = new TableDiff($tableName);
922
        $tableDiff->fromTable                 = $table;
923
        $tableDiff->changedColumns['col_int'] = new ColumnDiff(
924
            'col_int',
925
            new Column('col_int', Type::getType('integer'), ['default' => 666]),
926
            ['type'],
927
            new Column('col_int', Type::getType('smallint'), ['default' => 666])
928
        );
929
930
        $tableDiff->changedColumns['col_string'] = new ColumnDiff(
931
            'col_string',
932
            new Column('col_string', Type::getType('string'), ['default' => 'foo', 'fixed' => true]),
933
            ['fixed'],
934
            new Column('col_string', Type::getType('string'), ['default' => 'foo'])
935
        );
936
937
        $this->schemaManager->alterTable($tableDiff);
938
939
        $columns = $this->schemaManager->listTableColumns($tableName);
940
941
        self::assertInstanceOf(IntegerType::class, $columns['col_int']->getType());
942
        self::assertEquals(666, $columns['col_int']->getDefault());
943
944
        self::assertInstanceOf(StringType::class, $columns['col_string']->getType());
945
        self::assertEquals('foo', $columns['col_string']->getDefault());
946
    }
947
948
    /**
949
     * @group DBAL-197
950
     */
951
    public function testListTableWithBlob() : void
952
    {
953
        $table = new Table('test_blob_table');
954
        $table->addColumn('id', 'integer', ['comment' => 'This is a comment']);
955
        $table->addColumn('binarydata', 'blob', []);
956
        $table->setPrimaryKey(['id']);
957
958
        $this->schemaManager->createTable($table);
959
960
        $created = $this->schemaManager->listTableDetails('test_blob_table');
961
962
        self::assertTrue($created->hasColumn('id'));
963
        self::assertTrue($created->hasColumn('binarydata'));
964
        self::assertTrue($created->hasPrimaryKey());
965
    }
966
967
    /**
968
     * @param mixed[] $data
969
     */
970
    protected function createTestTable(string $name = 'test_table', array $data = []) : Table
971
    {
972
        $options = $data['options'] ?? [];
973
974
        $table = $this->getTestTable($name, $options);
975
976
        $this->schemaManager->dropAndCreateTable($table);
977
978
        return $table;
979
    }
980
981
    /**
982
     * @param mixed[] $options
983
     */
984
    protected function getTestTable(string $name, array $options = []) : Table
985
    {
986
        $table = new Table($name, [], [], [], false, $options);
0 ignored issues
show
Bug introduced by
false of type false is incompatible with the type integer expected by parameter $idGeneratorType of Doctrine\DBAL\Schema\Table::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

986
        $table = new Table($name, [], [], [], /** @scrutinizer ignore-type */ false, $options);
Loading history...
987
        $table->setSchemaConfig($this->schemaManager->createSchemaConfig());
988
        $table->addColumn('id', 'integer', ['notnull' => true]);
989
        $table->setPrimaryKey(['id']);
990
        $table->addColumn('test', 'string', ['length' => 255]);
991
        $table->addColumn('foreign_key_test', 'integer');
992
993
        return $table;
994
    }
995
996
    protected function getTestCompositeTable(string $name) : Table
997
    {
998
        $table = new Table($name, [], [], [], false, []);
0 ignored issues
show
Bug introduced by
false of type false is incompatible with the type integer expected by parameter $idGeneratorType of Doctrine\DBAL\Schema\Table::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

998
        $table = new Table($name, [], [], [], /** @scrutinizer ignore-type */ false, []);
Loading history...
999
        $table->setSchemaConfig($this->schemaManager->createSchemaConfig());
1000
        $table->addColumn('id', 'integer', ['notnull' => true]);
1001
        $table->addColumn('other_id', 'integer', ['notnull' => true]);
1002
        $table->setPrimaryKey(['id', 'other_id']);
1003
        $table->addColumn('test', 'string', ['length' => 255]);
1004
1005
        return $table;
1006
    }
1007
1008
    /**
1009
     * @param Table[] $tables
1010
     */
1011
    protected function assertHasTable(array $tables) : void
1012
    {
1013
        $foundTable = false;
1014
        foreach ($tables as $table) {
1015
            self::assertInstanceOf(Table::class, $table, 'No Table instance was found in tables array.');
1016
            if (strtolower($table->getName()) !== 'list_tables_test_new_name') {
1017
                continue;
1018
            }
1019
1020
            $foundTable = true;
1021
        }
1022
        self::assertTrue($foundTable, 'Could not find new table');
1023
    }
1024
1025
    public function testListForeignKeysComposite() : void
1026
    {
1027
        if (! $this->connection->getDatabasePlatform()->supportsForeignKeyConstraints()) {
1028
            $this->markTestSkipped('Does not support foreign key constraints.');
1029
        }
1030
1031
        $this->schemaManager->createTable($this->getTestTable('test_create_fk3'));
1032
        $this->schemaManager->createTable($this->getTestCompositeTable('test_create_fk4'));
1033
1034
        $foreignKey = new ForeignKeyConstraint(
1035
            ['id', 'foreign_key_test'],
1036
            'test_create_fk4',
1037
            ['id', 'other_id'],
1038
            'foreign_key_test_fk2'
1039
        );
1040
1041
        $this->schemaManager->createForeignKey($foreignKey, 'test_create_fk3');
1042
1043
        $fkeys = $this->schemaManager->listTableForeignKeys('test_create_fk3');
1044
1045
        self::assertEquals(1, count($fkeys), "Table 'test_create_fk3' has to have one foreign key.");
1046
1047
        self::assertInstanceOf(ForeignKeyConstraint::class, $fkeys[0]);
1048
        self::assertEquals(['id', 'foreign_key_test'], array_map('strtolower', $fkeys[0]->getLocalColumns()));
1049
        self::assertEquals(['id', 'other_id'], array_map('strtolower', $fkeys[0]->getForeignColumns()));
1050
    }
1051
1052
    /**
1053
     * @group DBAL-44
1054
     */
1055
    public function testColumnDefaultLifecycle() : void
1056
    {
1057
        $table = new Table('col_def_lifecycle');
1058
        $table->addColumn('id', 'integer', ['autoincrement' => true]);
1059
        $table->addColumn('column1', 'string', ['default' => null]);
1060
        $table->addColumn('column2', 'string', ['default' => false]);
1061
        $table->addColumn('column3', 'string', ['default' => true]);
1062
        $table->addColumn('column4', 'string', ['default' => 0]);
1063
        $table->addColumn('column5', 'string', ['default' => '']);
1064
        $table->addColumn('column6', 'string', ['default' => 'def']);
1065
        $table->addColumn('column7', 'integer', ['default' => 0]);
1066
        $table->setPrimaryKey(['id']);
1067
1068
        $this->schemaManager->dropAndCreateTable($table);
1069
1070
        $columns = $this->schemaManager->listTableColumns('col_def_lifecycle');
1071
1072
        self::assertNull($columns['id']->getDefault());
1073
        self::assertNull($columns['column1']->getDefault());
1074
        self::assertSame('', $columns['column2']->getDefault());
1075
        self::assertSame('1', $columns['column3']->getDefault());
1076
        self::assertSame('0', $columns['column4']->getDefault());
1077
        self::assertSame('', $columns['column5']->getDefault());
1078
        self::assertSame('def', $columns['column6']->getDefault());
1079
        self::assertSame('0', $columns['column7']->getDefault());
1080
1081
        $diffTable = clone $table;
1082
1083
        $diffTable->changeColumn('column1', ['default' => false]);
1084
        $diffTable->changeColumn('column2', ['default' => null]);
1085
        $diffTable->changeColumn('column3', ['default' => false]);
1086
        $diffTable->changeColumn('column4', ['default' => null]);
1087
        $diffTable->changeColumn('column5', ['default' => false]);
1088
        $diffTable->changeColumn('column6', ['default' => 666]);
1089
        $diffTable->changeColumn('column7', ['default' => null]);
1090
1091
        $comparator = new Comparator();
1092
1093
        $this->schemaManager->alterTable($comparator->diffTable($table, $diffTable));
1094
1095
        $columns = $this->schemaManager->listTableColumns('col_def_lifecycle');
1096
1097
        self::assertSame('', $columns['column1']->getDefault());
1098
        self::assertNull($columns['column2']->getDefault());
1099
        self::assertSame('', $columns['column3']->getDefault());
1100
        self::assertNull($columns['column4']->getDefault());
1101
        self::assertSame('', $columns['column5']->getDefault());
1102
        self::assertSame('666', $columns['column6']->getDefault());
1103
        self::assertNull($columns['column7']->getDefault());
1104
    }
1105
1106
    public function testListTableWithBinary() : void
1107
    {
1108
        $tableName = 'test_binary_table';
1109
1110
        $table = new Table($tableName);
1111
        $table->addColumn('id', 'integer');
1112
        $table->addColumn('column_varbinary', 'binary', []);
1113
        $table->addColumn('column_binary', 'binary', ['fixed' => true]);
1114
        $table->setPrimaryKey(['id']);
1115
1116
        $this->schemaManager->createTable($table);
1117
1118
        $table = $this->schemaManager->listTableDetails($tableName);
1119
1120
        self::assertInstanceOf(BinaryType::class, $table->getColumn('column_varbinary')->getType());
1121
        self::assertFalse($table->getColumn('column_varbinary')->getFixed());
1122
1123
        self::assertInstanceOf(BinaryType::class, $table->getColumn('column_binary')->getType());
1124
        self::assertTrue($table->getColumn('column_binary')->getFixed());
1125
    }
1126
1127
    public function testListTableDetailsWithFullQualifiedTableName() : void
1128
    {
1129
        if (! $this->schemaManager->getDatabasePlatform()->supportsSchemas()) {
1130
            $this->markTestSkipped('Test only works on platforms that support schemas.');
1131
        }
1132
1133
        $defaultSchemaName = $this->schemaManager->getDatabasePlatform()->getDefaultSchemaName();
1134
        $primaryTableName  = 'primary_table';
1135
        $foreignTableName  = 'foreign_table';
1136
1137
        $table = new Table($foreignTableName);
1138
        $table->addColumn('id', 'integer', ['autoincrement' => true]);
1139
        $table->setPrimaryKey(['id']);
1140
1141
        $this->schemaManager->dropAndCreateTable($table);
1142
1143
        $table = new Table($primaryTableName);
1144
        $table->addColumn('id', 'integer', ['autoincrement' => true]);
1145
        $table->addColumn('foo', 'integer');
1146
        $table->addColumn('bar', 'string');
1147
        $table->addForeignKeyConstraint($foreignTableName, ['foo'], ['id']);
1148
        $table->addIndex(['bar']);
1149
        $table->setPrimaryKey(['id']);
1150
1151
        $this->schemaManager->dropAndCreateTable($table);
1152
1153
        self::assertEquals(
1154
            $this->schemaManager->listTableColumns($primaryTableName),
1155
            $this->schemaManager->listTableColumns($defaultSchemaName . '.' . $primaryTableName)
1156
        );
1157
        self::assertEquals(
1158
            $this->schemaManager->listTableIndexes($primaryTableName),
1159
            $this->schemaManager->listTableIndexes($defaultSchemaName . '.' . $primaryTableName)
1160
        );
1161
        self::assertEquals(
1162
            $this->schemaManager->listTableForeignKeys($primaryTableName),
1163
            $this->schemaManager->listTableForeignKeys($defaultSchemaName . '.' . $primaryTableName)
1164
        );
1165
    }
1166
1167
    public function testCommentStringsAreQuoted() : void
1168
    {
1169
        if (! $this->connection->getDatabasePlatform()->supportsInlineColumnComments() &&
1170
            ! $this->connection->getDatabasePlatform()->supportsCommentOnStatement() &&
1171
            $this->connection->getDatabasePlatform()->getName() !== 'mssql') {
1172
            $this->markTestSkipped('Database does not support column comments.');
1173
        }
1174
1175
        $table = new Table('my_table');
1176
        $table->addColumn('id', 'integer', ['comment' => "It's a comment with a quote"]);
1177
        $table->setPrimaryKey(['id']);
1178
1179
        $this->schemaManager->createTable($table);
1180
1181
        $columns = $this->schemaManager->listTableColumns('my_table');
1182
        self::assertEquals("It's a comment with a quote", $columns['id']->getComment());
1183
    }
1184
1185
    public function testCommentNotDuplicated() : void
1186
    {
1187
        if (! $this->connection->getDatabasePlatform()->supportsInlineColumnComments()) {
1188
            $this->markTestSkipped('Database does not support column comments.');
1189
        }
1190
1191
        $options          = [
1192
            'type' => Type::getType('integer'),
1193
            'default' => 0,
1194
            'notnull' => true,
1195
            'comment' => 'expected+column+comment',
1196
        ];
1197
        $columnDefinition = substr($this->connection->getDatabasePlatform()->getColumnDeclarationSQL('id', $options), strlen('id') + 1);
1198
1199
        $table = new Table('my_table');
1200
        $table->addColumn('id', 'integer', ['columnDefinition' => $columnDefinition, 'comment' => 'unexpected_column_comment']);
1201
        $sql = $this->connection->getDatabasePlatform()->getCreateTableSQL($table);
1202
1203
        self::assertStringContainsString('expected+column+comment', $sql[0]);
1204
        self::assertStringNotContainsString('unexpected_column_comment', $sql[0]);
1205
    }
1206
1207
    /**
1208
     * @group DBAL-1009
1209
     * @dataProvider getAlterColumnComment
1210
     */
1211
    public function testAlterColumnComment(
1212
        ?string $comment1,
1213
        ?string $expectedComment1,
1214
        ?string $comment2,
1215
        ?string $expectedComment2
1216
    ) : void {
1217
        if (! $this->connection->getDatabasePlatform()->supportsInlineColumnComments() &&
1218
            ! $this->connection->getDatabasePlatform()->supportsCommentOnStatement() &&
1219
            $this->connection->getDatabasePlatform()->getName() !== 'mssql') {
1220
            $this->markTestSkipped('Database does not support column comments.');
1221
        }
1222
1223
        $offlineTable = new Table('alter_column_comment_test');
1224
        $offlineTable->addColumn('comment1', 'integer', ['comment' => $comment1]);
1225
        $offlineTable->addColumn('comment2', 'integer', ['comment' => $comment2]);
1226
        $offlineTable->addColumn('no_comment1', 'integer');
1227
        $offlineTable->addColumn('no_comment2', 'integer');
1228
        $this->schemaManager->dropAndCreateTable($offlineTable);
1229
1230
        $onlineTable = $this->schemaManager->listTableDetails('alter_column_comment_test');
1231
1232
        self::assertSame($expectedComment1, $onlineTable->getColumn('comment1')->getComment());
1233
        self::assertSame($expectedComment2, $onlineTable->getColumn('comment2')->getComment());
1234
        self::assertNull($onlineTable->getColumn('no_comment1')->getComment());
1235
        self::assertNull($onlineTable->getColumn('no_comment2')->getComment());
1236
1237
        $onlineTable->changeColumn('comment1', ['comment' => $comment2]);
1238
        $onlineTable->changeColumn('comment2', ['comment' => $comment1]);
1239
        $onlineTable->changeColumn('no_comment1', ['comment' => $comment1]);
1240
        $onlineTable->changeColumn('no_comment2', ['comment' => $comment2]);
1241
1242
        $comparator = new Comparator();
1243
1244
        $tableDiff = $comparator->diffTable($offlineTable, $onlineTable);
1245
1246
        self::assertInstanceOf(TableDiff::class, $tableDiff);
1247
1248
        $this->schemaManager->alterTable($tableDiff);
1249
1250
        $onlineTable = $this->schemaManager->listTableDetails('alter_column_comment_test');
1251
1252
        self::assertSame($expectedComment2, $onlineTable->getColumn('comment1')->getComment());
1253
        self::assertSame($expectedComment1, $onlineTable->getColumn('comment2')->getComment());
1254
        self::assertSame($expectedComment1, $onlineTable->getColumn('no_comment1')->getComment());
1255
        self::assertSame($expectedComment2, $onlineTable->getColumn('no_comment2')->getComment());
1256
    }
1257
1258
    /**
1259
     * @return mixed[][]
1260
     */
1261
    public static function getAlterColumnComment() : iterable
1262
    {
1263
        return [
1264
            [null, null, ' ', ' '],
1265
            [null, null, '0', '0'],
1266
            [null, null, 'foo', 'foo'],
1267
1268
            ['', null, ' ', ' '],
1269
            ['', null, '0', '0'],
1270
            ['', null, 'foo', 'foo'],
1271
1272
            [' ', ' ', '0', '0'],
1273
            [' ', ' ', 'foo', 'foo'],
1274
1275
            ['0', '0', 'foo', 'foo'],
1276
        ];
1277
    }
1278
1279
    /**
1280
     * @group DBAL-1095
1281
     */
1282
    public function testDoesNotListIndexesImplicitlyCreatedByForeignKeys() : void
1283
    {
1284
        if (! $this->schemaManager->getDatabasePlatform()->supportsForeignKeyConstraints()) {
1285
            $this->markTestSkipped('This test is only supported on platforms that have foreign keys.');
1286
        }
1287
1288
        $primaryTable = new Table('test_list_index_impl_primary');
1289
        $primaryTable->addColumn('id', 'integer');
1290
        $primaryTable->setPrimaryKey(['id']);
1291
1292
        $foreignTable = new Table('test_list_index_impl_foreign');
1293
        $foreignTable->addColumn('fk1', 'integer');
1294
        $foreignTable->addColumn('fk2', 'integer');
1295
        $foreignTable->addIndex(['fk1'], 'explicit_fk1_idx');
1296
        $foreignTable->addForeignKeyConstraint('test_list_index_impl_primary', ['fk1'], ['id']);
1297
        $foreignTable->addForeignKeyConstraint('test_list_index_impl_primary', ['fk2'], ['id']);
1298
1299
        $this->schemaManager->dropAndCreateTable($primaryTable);
1300
        $this->schemaManager->dropAndCreateTable($foreignTable);
1301
1302
        $indexes = $this->schemaManager->listTableIndexes('test_list_index_impl_foreign');
1303
1304
        self::assertCount(2, $indexes);
1305
        self::assertArrayHasKey('explicit_fk1_idx', $indexes);
1306
        self::assertArrayHasKey('idx_3d6c147fdc58d6c', $indexes);
1307
    }
1308
1309
    /**
1310
     * @after
1311
     */
1312
    public function removeJsonArrayTable() : void
1313
    {
1314
        if (! $this->schemaManager->tablesExist(['json_array_test'])) {
1315
            return;
1316
        }
1317
1318
        $this->schemaManager->dropTable('json_array_test');
1319
    }
1320
1321
    /**
1322
     * @group 2782
1323
     * @group 6654
1324
     */
1325
    public function testComparatorShouldReturnFalseWhenLegacyJsonArrayColumnHasComment() : void
1326
    {
1327
        $table = new Table('json_array_test');
1328
        $table->addColumn('parameters', 'json_array');
1329
1330
        $this->schemaManager->createTable($table);
1331
1332
        $comparator = new Comparator();
1333
        $tableDiff  = $comparator->diffTable($this->schemaManager->listTableDetails('json_array_test'), $table);
1334
1335
        self::assertFalse($tableDiff);
1336
    }
1337
1338
    /**
1339
     * @group 2782
1340
     * @group 6654
1341
     */
1342
    public function testComparatorShouldModifyOnlyTheCommentWhenUpdatingFromJsonArrayTypeOnLegacyPlatforms() : void
1343
    {
1344
        if ($this->schemaManager->getDatabasePlatform()->hasNativeJsonType()) {
1345
            $this->markTestSkipped('This test is only supported on platforms that do not have native JSON type.');
1346
        }
1347
1348
        $table = new Table('json_array_test');
1349
        $table->addColumn('parameters', 'json_array');
1350
1351
        $this->schemaManager->createTable($table);
1352
1353
        $table = new Table('json_array_test');
1354
        $table->addColumn('parameters', 'json');
1355
1356
        $comparator = new Comparator();
1357
        $tableDiff  = $comparator->diffTable($this->schemaManager->listTableDetails('json_array_test'), $table);
1358
1359
        self::assertInstanceOf(TableDiff::class, $tableDiff);
1360
1361
        $changedColumn = $tableDiff->changedColumns['parameters'] ?? $tableDiff->changedColumns['PARAMETERS'];
1362
1363
        self::assertSame(['comment'], $changedColumn->changedProperties);
1364
    }
1365
1366
    /**
1367
     * @group 2782
1368
     * @group 6654
1369
     */
1370
    public function testComparatorShouldAddCommentToLegacyJsonArrayTypeThatDoesNotHaveIt() : void
1371
    {
1372
        if (! $this->schemaManager->getDatabasePlatform()->hasNativeJsonType()) {
1373
            $this->markTestSkipped('This test is only supported on platforms that have native JSON type.');
1374
        }
1375
1376
        $this->connection->executeQuery('CREATE TABLE json_array_test (parameters JSON NOT NULL)');
1377
1378
        $table = new Table('json_array_test');
1379
        $table->addColumn('parameters', 'json_array');
1380
1381
        $comparator = new Comparator();
1382
        $tableDiff  = $comparator->diffTable($this->schemaManager->listTableDetails('json_array_test'), $table);
1383
1384
        self::assertInstanceOf(TableDiff::class, $tableDiff);
1385
        self::assertSame(['comment'], $tableDiff->changedColumns['parameters']->changedProperties);
1386
    }
1387
1388
    /**
1389
     * @group 2782
1390
     * @group 6654
1391
     */
1392
    public function testComparatorShouldReturnAllChangesWhenUsingLegacyJsonArrayType() : void
1393
    {
1394
        if (! $this->schemaManager->getDatabasePlatform()->hasNativeJsonType()) {
1395
            $this->markTestSkipped('This test is only supported on platforms that have native JSON type.');
1396
        }
1397
1398
        $this->connection->executeQuery('CREATE TABLE json_array_test (parameters JSON DEFAULT NULL)');
1399
1400
        $table = new Table('json_array_test');
1401
        $table->addColumn('parameters', 'json_array');
1402
1403
        $comparator = new Comparator();
1404
        $tableDiff  = $comparator->diffTable($this->schemaManager->listTableDetails('json_array_test'), $table);
1405
1406
        self::assertInstanceOf(TableDiff::class, $tableDiff);
1407
        self::assertSame(['notnull', 'comment'], $tableDiff->changedColumns['parameters']->changedProperties);
1408
    }
1409
1410
    /**
1411
     * @group 2782
1412
     * @group 6654
1413
     */
1414
    public function testComparatorShouldReturnAllChangesWhenUsingLegacyJsonArrayTypeEvenWhenPlatformHasJsonSupport() : void
1415
    {
1416
        if (! $this->schemaManager->getDatabasePlatform()->hasNativeJsonType()) {
1417
            $this->markTestSkipped('This test is only supported on platforms that have native JSON type.');
1418
        }
1419
1420
        $this->connection->executeQuery('CREATE TABLE json_array_test (parameters JSON DEFAULT NULL)');
1421
1422
        $table = new Table('json_array_test');
1423
        $table->addColumn('parameters', 'json_array');
1424
1425
        $comparator = new Comparator();
1426
        $tableDiff  = $comparator->diffTable($this->schemaManager->listTableDetails('json_array_test'), $table);
1427
1428
        self::assertInstanceOf(TableDiff::class, $tableDiff);
1429
        self::assertSame(['notnull', 'comment'], $tableDiff->changedColumns['parameters']->changedProperties);
1430
    }
1431
1432
    /**
1433
     * @group 2782
1434
     * @group 6654
1435
     */
1436
    public function testComparatorShouldNotAddCommentToJsonTypeSinceItIsTheDefaultNow() : void
1437
    {
1438
        if (! $this->schemaManager->getDatabasePlatform()->hasNativeJsonType()) {
1439
            $this->markTestSkipped('This test is only supported on platforms that have native JSON type.');
1440
        }
1441
1442
        $this->connection->executeQuery('CREATE TABLE json_test (parameters JSON NOT NULL)');
1443
1444
        $table = new Table('json_test');
1445
        $table->addColumn('parameters', 'json');
1446
1447
        $comparator = new Comparator();
1448
        $tableDiff  = $comparator->diffTable($this->schemaManager->listTableDetails('json_test'), $table);
1449
1450
        self::assertFalse($tableDiff);
1451
    }
1452
1453
    /**
1454
     * @dataProvider commentsProvider
1455
     * @group 2596
1456
     */
1457
    public function testExtractDoctrineTypeFromComment(string $comment, string $expected, string $currentType) : void
1458
    {
1459
        $result = $this->schemaManager->extractDoctrineTypeFromComment($comment, $currentType);
1460
1461
        self::assertSame($expected, $result);
1462
    }
1463
1464
    /**
1465
     * @return string[][]
1466
     */
1467
    public function commentsProvider() : array
1468
    {
1469
        $currentType = 'current type';
1470
1471
        return [
1472
            'invalid custom type comments'      => ['should.return.current.type', $currentType, $currentType],
1473
            'valid doctrine type'               => ['(DC2Type:guid)', 'guid', $currentType],
1474
            'valid with dots'                   => ['(DC2Type:type.should.return)', 'type.should.return', $currentType],
1475
            'valid with namespace'              => ['(DC2Type:Namespace\Class)', 'Namespace\Class', $currentType],
1476
            'valid with extra closing bracket'  => ['(DC2Type:should.stop)).before)', 'should.stop', $currentType],
1477
            'valid with extra opening brackets' => ['(DC2Type:should((.stop)).before)', 'should((.stop', $currentType],
1478
        ];
1479
    }
1480
1481
    public function testCreateAndListSequences() : void
1482
    {
1483
        if (! $this->schemaManager->getDatabasePlatform()->supportsSequences()) {
1484
            self::markTestSkipped('This test is only supported on platforms that support sequences.');
1485
        }
1486
1487
        $sequence1Name           = 'sequence_1';
1488
        $sequence1AllocationSize = 1;
1489
        $sequence1InitialValue   = 2;
1490
        $sequence2Name           = 'sequence_2';
1491
        $sequence2AllocationSize = 3;
1492
        $sequence2InitialValue   = 4;
1493
        $sequence1               = new Sequence($sequence1Name, $sequence1AllocationSize, $sequence1InitialValue);
1494
        $sequence2               = new Sequence($sequence2Name, $sequence2AllocationSize, $sequence2InitialValue);
1495
1496
        $this->schemaManager->createSequence($sequence1);
1497
        $this->schemaManager->createSequence($sequence2);
1498
1499
        /** @var Sequence[] $actualSequences */
1500
        $actualSequences = [];
1501
        foreach ($this->schemaManager->listSequences() as $sequence) {
1502
            $actualSequences[$sequence->getName()] = $sequence;
1503
        }
1504
1505
        $actualSequence1 = $actualSequences[$sequence1Name];
1506
        $actualSequence2 = $actualSequences[$sequence2Name];
1507
1508
        self::assertSame($sequence1Name, $actualSequence1->getName());
1509
        self::assertEquals($sequence1AllocationSize, $actualSequence1->getAllocationSize());
1510
        self::assertEquals($sequence1InitialValue, $actualSequence1->getInitialValue());
1511
1512
        self::assertSame($sequence2Name, $actualSequence2->getName());
1513
        self::assertEquals($sequence2AllocationSize, $actualSequence2->getAllocationSize());
1514
        self::assertEquals($sequence2InitialValue, $actualSequence2->getInitialValue());
1515
    }
1516
1517
    /**
1518
     * @group #3086
1519
     */
1520
    public function testComparisonWithAutoDetectedSequenceDefinition() : void
1521
    {
1522
        if (! $this->schemaManager->getDatabasePlatform()->supportsSequences()) {
1523
            self::markTestSkipped('This test is only supported on platforms that support sequences.');
1524
        }
1525
1526
        $sequenceName           = 'sequence_auto_detect_test';
1527
        $sequenceAllocationSize = 5;
1528
        $sequenceInitialValue   = 10;
1529
        $sequence               = new Sequence($sequenceName, $sequenceAllocationSize, $sequenceInitialValue);
1530
1531
        $this->schemaManager->dropAndCreateSequence($sequence);
1532
1533
        $createdSequence = array_values(
1534
            array_filter(
1535
                $this->schemaManager->listSequences(),
1536
                static function (Sequence $sequence) use ($sequenceName) : bool {
1537
                    return strcasecmp($sequence->getName(), $sequenceName) === 0;
1538
                }
1539
            )
1540
        )[0] ?? null;
1541
1542
        self::assertNotNull($createdSequence);
1543
1544
        $comparator = new Comparator();
1545
        $tableDiff  = $comparator->diffSequence($createdSequence, $sequence);
1546
1547
        self::assertFalse($tableDiff);
1548
    }
1549
1550
    /**
1551
     * @group DBAL-2921
1552
     */
1553
    public function testPrimaryKeyAutoIncrement() : void
1554
    {
1555
        $table = new Table('test_pk_auto_increment');
1556
        $table->addColumn('id', 'integer', ['autoincrement' => true]);
1557
        $table->addColumn('text', 'string');
1558
        $table->setPrimaryKey(['id']);
1559
        $this->schemaManager->dropAndCreateTable($table);
1560
1561
        $this->connection->insert('test_pk_auto_increment', ['text' => '1']);
1562
1563
        $query = $this->connection->query('SELECT id FROM test_pk_auto_increment WHERE text = \'1\'');
1564
        $query->execute();
1565
        $lastUsedIdBeforeDelete = (int) $query->fetchColumn();
1566
1567
        $this->connection->query('DELETE FROM test_pk_auto_increment');
1568
1569
        $this->connection->insert('test_pk_auto_increment', ['text' => '2']);
1570
1571
        $query = $this->connection->query('SELECT id FROM test_pk_auto_increment WHERE text = \'2\'');
1572
        $query->execute();
1573
        $lastUsedIdAfterDelete = (int) $query->fetchColumn();
1574
1575
        $this->assertGreaterThan($lastUsedIdBeforeDelete, $lastUsedIdAfterDelete);
1576
    }
1577
1578
    public function testGenerateAnIndexWithPartialColumnLength() : void
1579
    {
1580
        if (! $this->schemaManager->getDatabasePlatform()->supportsColumnLengthIndexes()) {
1581
            self::markTestSkipped('This test is only supported on platforms that support indexes with column length definitions.');
1582
        }
1583
1584
        $table = new Table('test_partial_column_index');
1585
        $table->addColumn('long_column', 'string', ['length' => 40]);
1586
        $table->addColumn('standard_column', 'integer');
1587
        $table->addIndex(['long_column'], 'partial_long_column_idx', [], ['lengths' => [4]]);
1588
        $table->addIndex(['standard_column', 'long_column'], 'standard_and_partial_idx', [], ['lengths' => [null, 2]]);
1589
1590
        $expected = $table->getIndexes();
1591
1592
        $this->schemaManager->dropAndCreateTable($table);
1593
1594
        $onlineTable = $this->schemaManager->listTableDetails('test_partial_column_index');
1595
        self::assertEquals($expected, $onlineTable->getIndexes());
1596
    }
1597
1598
    public function testCommentInTable() : void
1599
    {
1600
        $table = new Table('table_with_comment');
1601
        $table->addColumn('id', 'integer');
1602
        $table->setComment('Foo with control characters \'\\');
1603
        $this->schemaManager->dropAndCreateTable($table);
1604
1605
        $table = $this->schemaManager->listTableDetails('table_with_comment');
1606
        self::assertSame('Foo with control characters \'\\', $table->getComment());
1607
    }
1608
1609
    public function testSchemaDiffForeignKeys() : void
1610
    {
1611
        $schemaManager = $this->connection->getSchemaManager();
1612
1613
        $table1 = new Table('child');
1614
        $table1->addColumn('id', 'integer', ['autoincrement' => true]);
1615
        $table1->addColumn('parent_id', 'integer');
1616
        $table1->setPrimaryKey(['id']);
1617
        $table1->addForeignKeyConstraint('parent', ['parent_id'], ['id']);
1618
1619
        $table2 = new Table('parent');
1620
        $table2->addColumn('id', 'integer', ['autoincrement' => true]);
1621
        $table2->setPrimaryKey(['id']);
1622
1623
        $diff = new SchemaDiff([$table1, $table2]);
1624
        $sqls = $diff->toSql($this->connection->getDatabasePlatform());
1625
1626
        foreach ($sqls as $sql) {
1627
            $this->connection->exec($sql);
1628
        }
1629
1630
        $schema = new Schema([
1631
            $schemaManager->listTableDetails('child'),
1632
            $schemaManager->listTableDetails('parent'),
1633
        ]);
1634
1635
        $this->assertCount(1, $schema->getTable('child')->getForeignKeys());
1636
1637
        $offlineSchema = new Schema([$table1, $table2]);
1638
1639
        $diff = Comparator::compareSchemas($offlineSchema, $schema);
1640
        $sqls = $diff->toSql($this->connection->getDatabasePlatform());
1641
1642
        $this->assertEquals([], $sqls);
1643
    }
1644
}
1645