Failed Conditions
Pull Request — 2.11.x (#3985)
by Grégoire
65:16
created

testSchemaDiffForeignKeys()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 41
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

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

761
        $this->schemaManager->alterTable(/** @scrutinizer ignore-type */ $tableDiff);
Loading history...
762
763
        $table       = $this->schemaManager->listTableDetails('test_fk_rename');
764
        $foreignKeys = $table->getForeignKeys();
765
766
        self::assertTrue($table->hasColumn('rename_fk_id'));
767
        self::assertCount(1, $foreignKeys);
768
        self::assertSame(['rename_fk_id'], array_map('strtolower', current($foreignKeys)->getColumns()));
769
    }
770
771
    /**
772
     * @group DBAL-1062
773
     */
774
    public function testRenameIndexUsedInForeignKeyConstraint() : void
775
    {
776
        if (! $this->schemaManager->getDatabasePlatform()->supportsForeignKeyConstraints()) {
777
            $this->markTestSkipped('This test is only supported on platforms that have foreign keys.');
778
        }
779
780
        $primaryTable = new Table('test_rename_index_primary');
781
        $primaryTable->addColumn('id', 'integer');
782
        $primaryTable->setPrimaryKey(['id']);
783
784
        $foreignTable = new Table('test_rename_index_foreign');
785
        $foreignTable->addColumn('fk', 'integer');
786
        $foreignTable->addIndex(['fk'], 'rename_index_fk_idx');
787
        $foreignTable->addForeignKeyConstraint(
788
            'test_rename_index_primary',
789
            ['fk'],
790
            ['id'],
791
            [],
792
            'fk_constraint'
793
        );
794
795
        $this->schemaManager->dropAndCreateTable($primaryTable);
796
        $this->schemaManager->dropAndCreateTable($foreignTable);
797
798
        $foreignTable2 = clone $foreignTable;
799
        $foreignTable2->renameIndex('rename_index_fk_idx', 'renamed_index_fk_idx');
800
801
        $comparator = new Comparator();
802
803
        $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

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

987
        $table = new Table($name, [], [], [], /** @scrutinizer ignore-type */ false, $options);
Loading history...
988
        $table->setSchemaConfig($this->schemaManager->createSchemaConfig());
989
        $table->addColumn('id', 'integer', ['notnull' => true]);
990
        $table->setPrimaryKey(['id']);
991
        $table->addColumn('test', 'string', ['length' => 255]);
992
        $table->addColumn('foreign_key_test', 'integer');
993
994
        return $table;
995
    }
996
997
    protected function getTestCompositeTable(string $name) : Table
998
    {
999
        $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

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