Completed
Pull Request — 2.10.x (#3762)
by Benjamin
61:17
created

testSchemaDiffForeignKeys()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 42
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

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