Failed Conditions
Push — master ( ea4232...3b6e69 )
by Grégoire
17:30 queued 17:24
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
cc 6
eloc 26
nc 6
nop 0
dl 0
loc 41
rs 8.8817
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\DBAL\Tests\Functional\Schema;
6
7
use Doctrine\Common\EventManager;
8
use Doctrine\DBAL\DBALException;
9
use Doctrine\DBAL\Driver\Statement;
10
use Doctrine\DBAL\Events;
11
use Doctrine\DBAL\Platforms\OraclePlatform;
12
use Doctrine\DBAL\Schema\AbstractAsset;
13
use Doctrine\DBAL\Schema\AbstractSchemaManager;
14
use Doctrine\DBAL\Schema\Column;
15
use Doctrine\DBAL\Schema\ColumnDiff;
16
use Doctrine\DBAL\Schema\Comparator;
17
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
18
use Doctrine\DBAL\Schema\Index;
19
use Doctrine\DBAL\Schema\Schema;
20
use Doctrine\DBAL\Schema\SchemaDiff;
21
use Doctrine\DBAL\Schema\Sequence;
22
use Doctrine\DBAL\Schema\Table;
23
use Doctrine\DBAL\Schema\TableDiff;
24
use Doctrine\DBAL\Schema\View;
25
use Doctrine\DBAL\Tests\FunctionalTestCase;
26
use Doctrine\DBAL\Types\BinaryType;
27
use Doctrine\DBAL\Types\DateTimeType;
28
use Doctrine\DBAL\Types\DecimalType;
29
use Doctrine\DBAL\Types\IntegerType;
30
use Doctrine\DBAL\Types\StringType;
31
use Doctrine\DBAL\Types\TextType;
32
use Doctrine\DBAL\Types\Type;
33
use ReflectionMethod;
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 assert;
40
use function count;
41
use function current;
42
use function end;
43
use function explode;
44
use function implode;
45
use function in_array;
46
use function is_string;
47
use function sprintf;
48
use function str_replace;
49
use function strcasecmp;
50
use function strtolower;
51
52
abstract class SchemaManagerFunctionalTestCase extends FunctionalTestCase
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
        assert(is_string($testClass));
63
64
        return strtolower(str_replace('SchemaManagerTest', '', $testClass));
65
    }
66
67
    protected function setUp() : void
68
    {
69
        parent::setUp();
70
71
        $dbms = $this->getPlatformName();
72
73
        if ($this->connection->getDatabasePlatform()->getName() !== $dbms) {
74
            self::markTestSkipped(static::class . ' requires the use of ' . $dbms);
75
        }
76
77
        $this->schemaManager = $this->connection->getSchemaManager();
78
    }
79
80
    protected function tearDown() : void
81
    {
82
        parent::tearDown();
83
84
        $this->schemaManager->tryMethod('dropTable', 'testschema.my_table_in_namespace');
85
86
        //TODO: SchemaDiff does not drop removed namespaces?
87
        try {
88
            //sql server versions below 2016 do not support 'IF EXISTS' so we have to catch the exception here
89
            $this->connection->exec('DROP SCHEMA testschema');
90
        } catch (DBALException $e) {
91
            return;
92
        }
93
    }
94
95
    /**
96
     * @group DBAL-1220
97
     */
98
    public function testDropsDatabaseWithActiveConnections() : void
99
    {
100
        if (! $this->schemaManager->getDatabasePlatform()->supportsCreateDropDatabase()) {
101
            self::markTestSkipped('Cannot drop Database client side with this Driver.');
102
        }
103
104
        $this->schemaManager->dropAndCreateDatabase('test_drop_database');
105
106
        $knownDatabases = $this->schemaManager->listDatabases();
107
        if ($this->connection->getDatabasePlatform() instanceof OraclePlatform) {
108
            self::assertContains('TEST_DROP_DATABASE', $knownDatabases);
109
        } else {
110
            self::assertContains('test_drop_database', $knownDatabases);
111
        }
112
113
        $params = $this->connection->getParams();
114
        if ($this->connection->getDatabasePlatform() instanceof OraclePlatform) {
115
            $params['user'] = 'test_drop_database';
116
        } else {
117
            $params['dbname'] = 'test_drop_database';
118
        }
119
120
        $user     = $params['user'] ?? '';
121
        $password = $params['password'] ?? '';
122
123
        $this->connection->getDriver()->connect($params, $user, $password);
124
125
        $this->schemaManager->dropDatabase('test_drop_database');
126
127
        self::assertNotContains('test_drop_database', $this->schemaManager->listDatabases());
128
    }
129
130
    /**
131
     * @group DBAL-195
132
     */
133
    public function testDropAndCreateSequence() : void
134
    {
135
        $platform = $this->connection->getDatabasePlatform();
136
137
        if (! $platform->supportsSequences()) {
138
            self::markTestSkipped(
139
                sprintf('The "%s" platform does not support sequences.', $platform->getName())
140
            );
141
        }
142
143
        $name = 'dropcreate_sequences_test_seq';
144
145
        $this->schemaManager->dropAndCreateSequence(new Sequence($name, 20, 10));
146
147
        self::assertTrue($this->hasElementWithName($this->schemaManager->listSequences(), $name));
148
    }
149
150
    /**
151
     * @param AbstractAsset[] $items
152
     */
153
    private function hasElementWithName(array $items, string $name) : bool
154
    {
155
        $filteredList = array_filter(
156
            $items,
157
            static function (AbstractAsset $item) use ($name) : bool {
158
                return $item->getShortestName($item->getNamespaceName()) === $name;
159
            }
160
        );
161
162
        return count($filteredList) === 1;
163
    }
164
165
    public function testListSequences() : void
166
    {
167
        $platform = $this->connection->getDatabasePlatform();
168
169
        if (! $platform->supportsSequences()) {
170
            self::markTestSkipped(
171
                sprintf('The "%s" platform does not support sequences.', $platform->getName())
172
            );
173
        }
174
175
        $this->schemaManager->createSequence(
176
            new Sequence('list_sequences_test_seq', 20, 10)
177
        );
178
179
        foreach ($this->schemaManager->listSequences() as $sequence) {
180
            if (strtolower($sequence->getName()) === 'list_sequences_test_seq') {
181
                self::assertSame(20, $sequence->getAllocationSize());
182
                self::assertSame(10, $sequence->getInitialValue());
183
184
                return;
185
            }
186
        }
187
188
        self::fail('Sequence was not found.');
189
    }
190
191
    public function testListDatabases() : void
192
    {
193
        if (! $this->schemaManager->getDatabasePlatform()->supportsCreateDropDatabase()) {
194
            self::markTestSkipped('Cannot drop Database client side with this Driver.');
195
        }
196
197
        $this->schemaManager->dropAndCreateDatabase('test_create_database');
198
        $databases = $this->schemaManager->listDatabases();
199
200
        $databases = array_map('strtolower', $databases);
201
202
        self::assertContains('test_create_database', $databases);
203
    }
204
205
    /**
206
     * @group DBAL-1058
207
     */
208
    public function testListNamespaceNames() : void
209
    {
210
        if (! $this->schemaManager->getDatabasePlatform()->supportsSchemas()) {
211
            self::markTestSkipped('Platform does not support schemas.');
212
        }
213
214
        // Currently dropping schemas is not supported, so we have to workaround here.
215
        $namespaces = $this->schemaManager->listNamespaceNames();
216
        $namespaces = array_map('strtolower', $namespaces);
217
218
        if (! in_array('test_create_schema', $namespaces, true)) {
219
            $this->connection->executeUpdate($this->schemaManager->getDatabasePlatform()->getCreateSchemaSQL('test_create_schema'));
220
221
            $namespaces = $this->schemaManager->listNamespaceNames();
222
            $namespaces = array_map('strtolower', $namespaces);
223
        }
224
225
        self::assertContains('test_create_schema', $namespaces);
226
    }
227
228
    public function testListTables() : void
229
    {
230
        $this->createTestTable('list_tables_test');
231
        $tables = $this->schemaManager->listTables();
232
233
        self::assertNotEmpty($tables, "List Tables has to find at least one table named 'list_tables_test'.");
234
235
        $foundTable = false;
236
        foreach ($tables as $table) {
237
            if (strtolower($table->getName()) !== 'list_tables_test') {
238
                continue;
239
            }
240
241
            $foundTable = true;
242
243
            self::assertTrue($table->hasColumn('id'));
244
            self::assertTrue($table->hasColumn('test'));
245
            self::assertTrue($table->hasColumn('foreign_key_test'));
246
        }
247
248
        self::assertTrue($foundTable, "The 'list_tables_test' table has to be found.");
249
    }
250
251
    public function createListTableColumns() : Table
252
    {
253
        $table = new Table('list_table_columns');
254
        $table->addColumn('id', 'integer', ['notnull' => true]);
255
        $table->addColumn('test', 'string', ['length' => 255, 'notnull' => false, 'default' => 'expected default']);
256
        $table->addColumn('foo', 'text', ['notnull' => true]);
257
        $table->addColumn('bar', 'decimal', ['precision' => 10, 'scale' => 4, 'notnull' => false]);
258
        $table->addColumn('baz1', 'datetime');
259
        $table->addColumn('baz2', 'time');
260
        $table->addColumn('baz3', 'date');
261
        $table->setPrimaryKey(['id']);
262
263
        return $table;
264
    }
265
266
    public function testListTableColumns() : void
267
    {
268
        $table = $this->createListTableColumns();
269
270
        $this->schemaManager->dropAndCreateTable($table);
271
272
        $columns     = $this->schemaManager->listTableColumns('list_table_columns');
273
        $columnsKeys = array_keys($columns);
274
275
        self::assertArrayHasKey('id', $columns);
276
        self::assertEquals(0, array_search('id', $columnsKeys, true));
277
        self::assertEquals('id', strtolower($columns['id']->getName()));
278
        self::assertInstanceOf(IntegerType::class, $columns['id']->getType());
279
        self::assertEquals(false, $columns['id']->getUnsigned());
280
        self::assertEquals(true, $columns['id']->getNotnull());
281
        self::assertEquals(null, $columns['id']->getDefault());
282
283
        self::assertArrayHasKey('test', $columns);
284
        self::assertEquals(1, array_search('test', $columnsKeys, true));
285
        self::assertEquals('test', strtolower($columns['test']->getName()));
286
        self::assertInstanceOf(StringType::class, $columns['test']->getType());
287
        self::assertEquals(255, $columns['test']->getLength());
288
        self::assertEquals(false, $columns['test']->getFixed());
289
        self::assertEquals(false, $columns['test']->getNotnull());
290
        self::assertEquals('expected default', $columns['test']->getDefault());
291
292
        self::assertEquals('foo', strtolower($columns['foo']->getName()));
293
        self::assertEquals(2, array_search('foo', $columnsKeys, true));
294
        self::assertInstanceOf(TextType::class, $columns['foo']->getType());
295
        self::assertEquals(false, $columns['foo']->getUnsigned());
296
        self::assertEquals(false, $columns['foo']->getFixed());
297
        self::assertEquals(true, $columns['foo']->getNotnull());
298
        self::assertEquals(null, $columns['foo']->getDefault());
299
300
        self::assertEquals('bar', strtolower($columns['bar']->getName()));
301
        self::assertEquals(3, array_search('bar', $columnsKeys, true));
302
        self::assertInstanceOf(DecimalType::class, $columns['bar']->getType());
303
        self::assertEquals(null, $columns['bar']->getLength());
304
        self::assertEquals(10, $columns['bar']->getPrecision());
305
        self::assertEquals(4, $columns['bar']->getScale());
306
        self::assertEquals(false, $columns['bar']->getUnsigned());
307
        self::assertEquals(false, $columns['bar']->getFixed());
308
        self::assertEquals(false, $columns['bar']->getNotnull());
309
        self::assertEquals(null, $columns['bar']->getDefault());
310
311
        self::assertEquals('baz1', strtolower($columns['baz1']->getName()));
312
        self::assertEquals(4, array_search('baz1', $columnsKeys, true));
313
        self::assertInstanceOf(DateTimeType::class, $columns['baz1']->getType());
314
        self::assertEquals(true, $columns['baz1']->getNotnull());
315
        self::assertEquals(null, $columns['baz1']->getDefault());
316
317
        self::assertEquals('baz2', strtolower($columns['baz2']->getName()));
318
        self::assertEquals(5, array_search('baz2', $columnsKeys, true));
319
        self::assertContains($columns['baz2']->getType()->getName(), ['time', 'date', 'datetime']);
320
        self::assertEquals(true, $columns['baz2']->getNotnull());
321
        self::assertEquals(null, $columns['baz2']->getDefault());
322
323
        self::assertEquals('baz3', strtolower($columns['baz3']->getName()));
324
        self::assertEquals(6, array_search('baz3', $columnsKeys, true));
325
        self::assertContains($columns['baz3']->getType()->getName(), ['time', 'date', 'datetime']);
326
        self::assertEquals(true, $columns['baz3']->getNotnull());
327
        self::assertEquals(null, $columns['baz3']->getDefault());
328
    }
329
330
    /**
331
     * @group DBAL-1078
332
     */
333
    public function testListTableColumnsWithFixedStringColumn() : void
334
    {
335
        $tableName = 'test_list_table_fixed_string';
336
337
        $table = new Table($tableName);
338
        $table->addColumn('column_char', 'string', ['fixed' => true, 'length' => 2]);
339
340
        $this->schemaManager->createTable($table);
341
342
        $columns = $this->schemaManager->listTableColumns($tableName);
343
344
        self::assertArrayHasKey('column_char', $columns);
345
        self::assertInstanceOf(StringType::class, $columns['column_char']->getType());
346
        self::assertTrue($columns['column_char']->getFixed());
347
        self::assertSame(2, $columns['column_char']->getLength());
348
    }
349
350
    public function testListTableColumnsDispatchEvent() : void
351
    {
352
        $table = $this->createListTableColumns();
353
354
        $this->schemaManager->dropAndCreateTable($table);
355
356
        $listenerMock = $this->getMockBuilder($this->getMockClass('ListTableColumnsDispatchEventListener'))
357
            ->addMethods(['onSchemaColumnDefinition'])
358
            ->getMock();
359
360
        $listenerMock
361
            ->expects(self::exactly(7))
362
            ->method('onSchemaColumnDefinition');
363
364
        $oldEventManager = $this->schemaManager->getDatabasePlatform()->getEventManager();
365
        assert($oldEventManager instanceof EventManager);
366
367
        $eventManager = new EventManager();
368
        $eventManager->addEventListener([Events::onSchemaColumnDefinition], $listenerMock);
369
370
        $this->schemaManager->getDatabasePlatform()->setEventManager($eventManager);
371
372
        $this->schemaManager->listTableColumns('list_table_columns');
373
374
        $this->schemaManager->getDatabasePlatform()->setEventManager($oldEventManager);
375
    }
376
377
    public function testListTableIndexesDispatchEvent() : void
378
    {
379
        $table = $this->getTestTable('list_table_indexes_test');
380
        $table->addUniqueIndex(['test'], 'test_index_name');
381
        $table->addIndex(['id', 'test'], 'test_composite_idx');
382
383
        $this->schemaManager->dropAndCreateTable($table);
384
385
        $listenerMock = $this->getMockBuilder($this->getMockClass('ListTableIndexesDispatchEventListener'))
386
            ->addMethods(['onSchemaIndexDefinition'])
387
            ->getMock();
388
        $listenerMock
389
            ->expects(self::exactly(3))
390
            ->method('onSchemaIndexDefinition');
391
392
        $oldEventManager = $this->schemaManager->getDatabasePlatform()->getEventManager();
393
        assert($oldEventManager instanceof EventManager);
394
395
        $eventManager = new EventManager();
396
        $eventManager->addEventListener([Events::onSchemaIndexDefinition], $listenerMock);
397
398
        $this->schemaManager->getDatabasePlatform()->setEventManager($eventManager);
399
400
        $this->schemaManager->listTableIndexes('list_table_indexes_test');
401
402
        $this->schemaManager->getDatabasePlatform()->setEventManager($oldEventManager);
403
    }
404
405
    public function testDiffListTableColumns() : void
406
    {
407
        if ($this->schemaManager->getDatabasePlatform()->getName() === 'oracle') {
408
            self::markTestSkipped('Does not work with Oracle, since it cannot detect DateTime, Date and Time differenecs (at the moment).');
409
        }
410
411
        $offlineTable = $this->createListTableColumns();
412
        $this->schemaManager->dropAndCreateTable($offlineTable);
413
        $onlineTable = $this->schemaManager->listTableDetails('list_table_columns');
414
415
        $comparator = new Comparator();
416
        $diff       = $comparator->diffTable($offlineTable, $onlineTable);
417
418
        self::assertNull($diff, 'No differences should be detected with the offline vs online schema.');
419
    }
420
421
    public function testListTableIndexes() : void
422
    {
423
        $table = $this->getTestCompositeTable('list_table_indexes_test');
424
        $table->addUniqueIndex(['test'], 'test_index_name');
425
        $table->addIndex(['id', 'test'], 'test_composite_idx');
426
427
        $this->schemaManager->dropAndCreateTable($table);
428
429
        $tableIndexes = $this->schemaManager->listTableIndexes('list_table_indexes_test');
430
431
        self::assertEquals(3, count($tableIndexes));
432
433
        self::assertArrayHasKey('primary', $tableIndexes, 'listTableIndexes() has to return a "primary" array key.');
434
        self::assertEquals(['id', 'other_id'], array_map('strtolower', $tableIndexes['primary']->getColumns()));
435
        self::assertTrue($tableIndexes['primary']->isUnique());
436
        self::assertTrue($tableIndexes['primary']->isPrimary());
437
438
        self::assertEquals('test_index_name', strtolower($tableIndexes['test_index_name']->getName()));
439
        self::assertEquals(['test'], array_map('strtolower', $tableIndexes['test_index_name']->getColumns()));
440
        self::assertTrue($tableIndexes['test_index_name']->isUnique());
441
        self::assertFalse($tableIndexes['test_index_name']->isPrimary());
442
443
        self::assertEquals('test_composite_idx', strtolower($tableIndexes['test_composite_idx']->getName()));
444
        self::assertEquals(['id', 'test'], array_map('strtolower', $tableIndexes['test_composite_idx']->getColumns()));
445
        self::assertFalse($tableIndexes['test_composite_idx']->isUnique());
446
        self::assertFalse($tableIndexes['test_composite_idx']->isPrimary());
447
    }
448
449
    public function testDropAndCreateIndex() : void
450
    {
451
        $table = $this->getTestTable('test_create_index');
452
        $table->addUniqueIndex(['test'], 'test');
453
        $this->schemaManager->dropAndCreateTable($table);
454
455
        $this->schemaManager->dropAndCreateIndex($table->getIndex('test'), $table);
456
        $tableIndexes = $this->schemaManager->listTableIndexes('test_create_index');
457
458
        self::assertEquals('test', strtolower($tableIndexes['test']->getName()));
459
        self::assertEquals(['test'], array_map('strtolower', $tableIndexes['test']->getColumns()));
460
        self::assertTrue($tableIndexes['test']->isUnique());
461
        self::assertFalse($tableIndexes['test']->isPrimary());
462
    }
463
464
    public function testCreateTableWithForeignKeys() : void
465
    {
466
        if (! $this->schemaManager->getDatabasePlatform()->supportsForeignKeyConstraints()) {
467
            self::markTestSkipped('Platform does not support foreign keys.');
468
        }
469
470
        $tableB = $this->getTestTable('test_foreign');
471
472
        $this->schemaManager->dropAndCreateTable($tableB);
473
474
        $tableA = $this->getTestTable('test_create_fk');
475
        $tableA->addForeignKeyConstraint('test_foreign', ['foreign_key_test'], ['id']);
476
477
        $this->schemaManager->dropAndCreateTable($tableA);
478
479
        $fkTable       = $this->schemaManager->listTableDetails('test_create_fk');
480
        $fkConstraints = $fkTable->getForeignKeys();
481
        self::assertEquals(1, count($fkConstraints), "Table 'test_create_fk1' has to have one foreign key.");
482
483
        $fkConstraint = current($fkConstraints);
484
        self::assertInstanceOf(ForeignKeyConstraint::class, $fkConstraint);
485
        self::assertEquals('test_foreign', strtolower($fkConstraint->getForeignTableName()));
486
        self::assertEquals(['foreign_key_test'], array_map('strtolower', $fkConstraint->getColumns()));
487
        self::assertEquals(['id'], array_map('strtolower', $fkConstraint->getForeignColumns()));
488
489
        self::assertTrue($fkTable->columnsAreIndexed($fkConstraint->getColumns()), 'The columns of a foreign key constraint should always be indexed.');
490
    }
491
492
    public function testListForeignKeys() : void
493
    {
494
        if (! $this->connection->getDatabasePlatform()->supportsForeignKeyConstraints()) {
495
            self::markTestSkipped('Does not support foreign key constraints.');
496
        }
497
498
        $this->createTestTable('test_create_fk1');
499
        $this->createTestTable('test_create_fk2');
500
501
        $foreignKey = new ForeignKeyConstraint(
502
            ['foreign_key_test'],
503
            'test_create_fk2',
504
            ['id'],
505
            'foreign_key_test_fk',
506
            ['onDelete' => 'CASCADE']
507
        );
508
509
        $this->schemaManager->createForeignKey($foreignKey, 'test_create_fk1');
510
511
        $fkeys = $this->schemaManager->listTableForeignKeys('test_create_fk1');
512
513
        self::assertCount(1, $fkeys, "Table 'test_create_fk1' has to have one foreign key.");
514
515
        self::assertEquals(['foreign_key_test'], array_map('strtolower', $fkeys[0]->getLocalColumns()));
516
        self::assertEquals(['id'], array_map('strtolower', $fkeys[0]->getForeignColumns()));
517
        self::assertEquals('test_create_fk2', strtolower($fkeys[0]->getForeignTableName()));
518
519
        if (! $fkeys[0]->hasOption('onDelete')) {
520
            return;
521
        }
522
523
        self::assertEquals('CASCADE', $fkeys[0]->getOption('onDelete'));
524
    }
525
526
    protected function getCreateExampleViewSql() : void
527
    {
528
        self::markTestSkipped('No Create Example View SQL was defined for this SchemaManager');
529
    }
530
531
    public function testCreateSchema() : void
532
    {
533
        $this->createTestTable('test_table');
534
535
        $schema = $this->schemaManager->createSchema();
536
        self::assertTrue($schema->hasTable('test_table'));
537
    }
538
539
    public function testAlterTableScenario() : void
540
    {
541
        if (! $this->schemaManager->getDatabasePlatform()->supportsAlterTable()) {
542
            self::markTestSkipped('Alter Table is not supported by this platform.');
543
        }
544
545
        $alterTable = $this->createTestTable('alter_table');
546
        $this->createTestTable('alter_table_foreign');
547
548
        $table = $this->schemaManager->listTableDetails('alter_table');
549
        self::assertTrue($table->hasColumn('id'));
550
        self::assertTrue($table->hasColumn('test'));
551
        self::assertTrue($table->hasColumn('foreign_key_test'));
552
        self::assertEquals(0, count($table->getForeignKeys()));
553
        self::assertEquals(1, count($table->getIndexes()));
554
555
        $tableDiff                         = new TableDiff('alter_table');
556
        $tableDiff->fromTable              = $alterTable;
557
        $tableDiff->addedColumns['foo']    = new Column('foo', Type::getType('integer'));
558
        $tableDiff->removedColumns['test'] = $table->getColumn('test');
559
560
        $this->schemaManager->alterTable($tableDiff);
561
562
        $table = $this->schemaManager->listTableDetails('alter_table');
563
        self::assertFalse($table->hasColumn('test'));
564
        self::assertTrue($table->hasColumn('foo'));
565
566
        $tableDiff                          = new TableDiff('alter_table');
567
        $tableDiff->fromTable               = $table;
568
        $tableDiff->addedIndexes['foo_idx'] = new Index('foo_idx', ['foo']);
569
570
        $this->schemaManager->alterTable($tableDiff);
571
572
        $table = $this->schemaManager->listTableDetails('alter_table');
573
        self::assertEquals(2, count($table->getIndexes()));
574
        self::assertTrue($table->hasIndex('foo_idx'));
575
        self::assertEquals(['foo'], array_map('strtolower', $table->getIndex('foo_idx')->getColumns()));
576
        self::assertFalse($table->getIndex('foo_idx')->isPrimary());
577
        self::assertFalse($table->getIndex('foo_idx')->isUnique());
578
579
        $tableDiff                            = new TableDiff('alter_table');
580
        $tableDiff->fromTable                 = $table;
581
        $tableDiff->changedIndexes['foo_idx'] = new Index('foo_idx', ['foo', 'foreign_key_test']);
582
583
        $this->schemaManager->alterTable($tableDiff);
584
585
        $table = $this->schemaManager->listTableDetails('alter_table');
586
        self::assertEquals(2, count($table->getIndexes()));
587
        self::assertTrue($table->hasIndex('foo_idx'));
588
        self::assertEquals(['foo', 'foreign_key_test'], array_map('strtolower', $table->getIndex('foo_idx')->getColumns()));
589
590
        $tableDiff                            = new TableDiff('alter_table');
591
        $tableDiff->fromTable                 = $table;
592
        $tableDiff->renamedIndexes['foo_idx'] = new Index('bar_idx', ['foo', 'foreign_key_test']);
593
594
        $this->schemaManager->alterTable($tableDiff);
595
596
        $table = $this->schemaManager->listTableDetails('alter_table');
597
        self::assertEquals(2, count($table->getIndexes()));
598
        self::assertTrue($table->hasIndex('bar_idx'));
599
        self::assertFalse($table->hasIndex('foo_idx'));
600
        self::assertEquals(['foo', 'foreign_key_test'], array_map('strtolower', $table->getIndex('bar_idx')->getColumns()));
601
        self::assertFalse($table->getIndex('bar_idx')->isPrimary());
602
        self::assertFalse($table->getIndex('bar_idx')->isUnique());
603
604
        $tableDiff                            = new TableDiff('alter_table');
605
        $tableDiff->fromTable                 = $table;
606
        $tableDiff->removedIndexes['bar_idx'] = new Index('bar_idx', ['foo', 'foreign_key_test']);
607
        $fk                                   = new ForeignKeyConstraint(['foreign_key_test'], 'alter_table_foreign', ['id']);
608
        $tableDiff->addedForeignKeys[]        = $fk;
609
610
        $this->schemaManager->alterTable($tableDiff);
611
        $table = $this->schemaManager->listTableDetails('alter_table');
612
613
        // dont check for index size here, some platforms automatically add indexes for foreign keys.
614
        self::assertFalse($table->hasIndex('bar_idx'));
615
616
        if (! $this->schemaManager->getDatabasePlatform()->supportsForeignKeyConstraints()) {
617
            return;
618
        }
619
620
        $fks = $table->getForeignKeys();
621
        self::assertCount(1, $fks);
622
        $foreignKey = current($fks);
623
        self::assertEquals('alter_table_foreign', strtolower($foreignKey->getForeignTableName()));
624
        self::assertEquals(['foreign_key_test'], array_map('strtolower', $foreignKey->getColumns()));
625
        self::assertEquals(['id'], array_map('strtolower', $foreignKey->getForeignColumns()));
626
    }
627
628
    public function testTableInNamespace() : void
629
    {
630
        if (! $this->schemaManager->getDatabasePlatform()->supportsSchemas()) {
631
            self::markTestSkipped('Schema definition is not supported by this platform.');
632
        }
633
634
        //create schema
635
        $diff                              = new SchemaDiff();
636
        $diff->newNamespaces['testschema'] = 'testschema';
637
638
        foreach ($diff->toSql($this->schemaManager->getDatabasePlatform()) as $sql) {
639
            $this->connection->exec($sql);
640
        }
641
642
        //test if table is create in namespace
643
        $this->createTestTable('testschema.my_table_in_namespace');
644
        self::assertContains('testschema.my_table_in_namespace', $this->schemaManager->listTableNames());
645
646
        //tables without namespace should be created in default namespace
647
        //default namespaces are ignored in table listings
648
        $this->createTestTable('my_table_not_in_namespace');
649
        self::assertContains('my_table_not_in_namespace', $this->schemaManager->listTableNames());
650
    }
651
652
    public function testCreateAndListViews() : void
653
    {
654
        if (! $this->schemaManager->getDatabasePlatform()->supportsViews()) {
655
            self::markTestSkipped('Views is not supported by this platform.');
656
        }
657
658
        $this->createTestTable('view_test_table');
659
660
        $name = 'doctrine_test_view';
661
        $sql  = 'SELECT * FROM view_test_table';
662
663
        $view = new View($name, $sql);
664
665
        $this->schemaManager->dropAndCreateView($view);
666
667
        self::assertTrue($this->hasElementWithName($this->schemaManager->listViews(), $name));
668
    }
669
670
    public function testAutoincrementDetection() : void
671
    {
672
        if (! $this->schemaManager->getDatabasePlatform()->supportsIdentityColumns()) {
673
            self::markTestSkipped('This test is only supported on platforms that have autoincrement');
674
        }
675
676
        $table = new Table('test_autoincrement');
677
        $table->setSchemaConfig($this->schemaManager->createSchemaConfig());
678
        $table->addColumn('id', 'integer', ['autoincrement' => true]);
679
        $table->setPrimaryKey(['id']);
680
681
        $this->schemaManager->createTable($table);
682
683
        $inferredTable = $this->schemaManager->listTableDetails('test_autoincrement');
684
        self::assertTrue($inferredTable->hasColumn('id'));
685
        self::assertTrue($inferredTable->getColumn('id')->getAutoincrement());
686
    }
687
688
    /**
689
     * @group DBAL-792
690
     */
691
    public function testAutoincrementDetectionMulticolumns() : void
692
    {
693
        if (! $this->schemaManager->getDatabasePlatform()->supportsIdentityColumns()) {
694
            self::markTestSkipped('This test is only supported on platforms that have autoincrement');
695
        }
696
697
        $table = new Table('test_not_autoincrement');
698
        $table->setSchemaConfig($this->schemaManager->createSchemaConfig());
699
        $table->addColumn('id', 'integer');
700
        $table->addColumn('other_id', 'integer');
701
        $table->setPrimaryKey(['id', 'other_id']);
702
703
        $this->schemaManager->createTable($table);
704
705
        $inferredTable = $this->schemaManager->listTableDetails('test_not_autoincrement');
706
        self::assertTrue($inferredTable->hasColumn('id'));
707
        self::assertFalse($inferredTable->getColumn('id')->getAutoincrement());
708
    }
709
710
    /**
711
     * @group DDC-887
712
     */
713
    public function testUpdateSchemaWithForeignKeyRenaming() : void
714
    {
715
        if (! $this->schemaManager->getDatabasePlatform()->supportsForeignKeyConstraints()) {
716
            self::markTestSkipped('This test is only supported on platforms that have foreign keys.');
717
        }
718
719
        $table = new Table('test_fk_base');
720
        $table->addColumn('id', 'integer');
721
        $table->setPrimaryKey(['id']);
722
723
        $tableFK = new Table('test_fk_rename');
724
        $tableFK->setSchemaConfig($this->schemaManager->createSchemaConfig());
725
        $tableFK->addColumn('id', 'integer');
726
        $tableFK->addColumn('fk_id', 'integer');
727
        $tableFK->setPrimaryKey(['id']);
728
        $tableFK->addIndex(['fk_id'], 'fk_idx');
729
        $tableFK->addForeignKeyConstraint('test_fk_base', ['fk_id'], ['id']);
730
731
        $this->schemaManager->createTable($table);
732
        $this->schemaManager->createTable($tableFK);
733
734
        $tableFKNew = new Table('test_fk_rename');
735
        $tableFKNew->setSchemaConfig($this->schemaManager->createSchemaConfig());
736
        $tableFKNew->addColumn('id', 'integer');
737
        $tableFKNew->addColumn('rename_fk_id', 'integer');
738
        $tableFKNew->setPrimaryKey(['id']);
739
        $tableFKNew->addIndex(['rename_fk_id'], 'fk_idx');
740
        $tableFKNew->addForeignKeyConstraint('test_fk_base', ['rename_fk_id'], ['id']);
741
742
        $c         = new Comparator();
743
        $tableDiff = $c->diffTable($tableFK, $tableFKNew);
744
745
        self::assertNotNull($tableDiff);
746
747
        $this->schemaManager->alterTable($tableDiff);
748
749
        $table       = $this->schemaManager->listTableDetails('test_fk_rename');
750
        $foreignKeys = $table->getForeignKeys();
751
752
        self::assertTrue($table->hasColumn('rename_fk_id'));
753
        self::assertCount(1, $foreignKeys);
754
        self::assertSame(['rename_fk_id'], array_map('strtolower', current($foreignKeys)->getColumns()));
755
    }
756
757
    /**
758
     * @group DBAL-1062
759
     */
760
    public function testRenameIndexUsedInForeignKeyConstraint() : void
761
    {
762
        if (! $this->schemaManager->getDatabasePlatform()->supportsForeignKeyConstraints()) {
763
            self::markTestSkipped('This test is only supported on platforms that have foreign keys.');
764
        }
765
766
        $primaryTable = new Table('test_rename_index_primary');
767
        $primaryTable->addColumn('id', 'integer');
768
        $primaryTable->setPrimaryKey(['id']);
769
770
        $foreignTable = new Table('test_rename_index_foreign');
771
        $foreignTable->addColumn('fk', 'integer');
772
        $foreignTable->addIndex(['fk'], 'rename_index_fk_idx');
773
        $foreignTable->addForeignKeyConstraint(
774
            'test_rename_index_primary',
775
            ['fk'],
776
            ['id'],
777
            [],
778
            'fk_constraint'
779
        );
780
781
        $this->schemaManager->dropAndCreateTable($primaryTable);
782
        $this->schemaManager->dropAndCreateTable($foreignTable);
783
784
        $foreignTable2 = clone $foreignTable;
785
        $foreignTable2->renameIndex('rename_index_fk_idx', 'renamed_index_fk_idx');
786
787
        $comparator = new Comparator();
788
789
        $diff = $comparator->diffTable($foreignTable, $foreignTable2);
790
791
        self::assertNotNull($diff);
792
793
        $this->schemaManager->alterTable($diff);
794
795
        $foreignTable = $this->schemaManager->listTableDetails('test_rename_index_foreign');
796
797
        self::assertFalse($foreignTable->hasIndex('rename_index_fk_idx'));
798
        self::assertTrue($foreignTable->hasIndex('renamed_index_fk_idx'));
799
        self::assertTrue($foreignTable->hasForeignKey('fk_constraint'));
800
    }
801
802
    /**
803
     * @group DBAL-825
804
     */
805
    public function testChangeColumnsTypeWithDefaultValue() : void
806
    {
807
        $tableName = 'column_def_change_type';
808
        $table     = new Table($tableName);
809
810
        $table->addColumn('col_int', 'smallint', ['default' => 666]);
811
        $table->addColumn('col_string', 'string', [
812
            'length' => 3,
813
            'default' => 'foo',
814
        ]);
815
816
        $this->schemaManager->dropAndCreateTable($table);
817
818
        $tableDiff                            = new TableDiff($tableName);
819
        $tableDiff->fromTable                 = $table;
820
        $tableDiff->changedColumns['col_int'] = new ColumnDiff(
821
            'col_int',
822
            new Column('col_int', Type::getType('integer'), ['default' => 666]),
823
            ['type'],
824
            new Column('col_int', Type::getType('smallint'), ['default' => 666])
825
        );
826
827
        $tableDiff->changedColumns['col_string'] = new ColumnDiff(
828
            'col_string',
829
            new Column('col_string', Type::getType('string'), [
830
                'length' => 3,
831
                'fixed' => true,
832
                'default' => 'foo',
833
            ]),
834
            ['fixed'],
835
            new Column('col_string', Type::getType('string'), [
836
                'length' => 3,
837
                'default' => 'foo',
838
            ])
839
        );
840
841
        $this->schemaManager->alterTable($tableDiff);
842
843
        $columns = $this->schemaManager->listTableColumns($tableName);
844
845
        self::assertInstanceOf(IntegerType::class, $columns['col_int']->getType());
846
        self::assertEquals(666, $columns['col_int']->getDefault());
847
848
        self::assertInstanceOf(StringType::class, $columns['col_string']->getType());
849
        self::assertEquals('foo', $columns['col_string']->getDefault());
850
    }
851
852
    /**
853
     * @group DBAL-197
854
     */
855
    public function testListTableWithBlob() : void
856
    {
857
        $table = new Table('test_blob_table');
858
        $table->addColumn('id', 'integer');
859
        $table->addColumn('binarydata', 'blob', []);
860
        $table->setPrimaryKey(['id']);
861
862
        $this->schemaManager->createTable($table);
863
864
        $created = $this->schemaManager->listTableDetails('test_blob_table');
865
866
        self::assertTrue($created->hasColumn('id'));
867
        self::assertTrue($created->hasColumn('binarydata'));
868
        self::assertTrue($created->hasPrimaryKey());
869
    }
870
871
    /**
872
     * @param mixed[] $data
873
     */
874
    protected function createTestTable(string $name = 'test_table', array $data = []) : Table
875
    {
876
        $options = $data['options'] ?? [];
877
878
        $table = $this->getTestTable($name, $options);
879
880
        $this->schemaManager->dropAndCreateTable($table);
881
882
        return $table;
883
    }
884
885
    /**
886
     * @param mixed[] $options
887
     */
888
    protected function getTestTable(string $name, array $options = []) : Table
889
    {
890
        $table = new Table($name, [], [], [], [], $options);
891
        $table->setSchemaConfig($this->schemaManager->createSchemaConfig());
892
        $table->addColumn('id', 'integer', ['notnull' => true]);
893
        $table->setPrimaryKey(['id']);
894
        $table->addColumn('test', 'string', ['length' => 255]);
895
        $table->addColumn('foreign_key_test', 'integer');
896
897
        return $table;
898
    }
899
900
    protected function getTestCompositeTable(string $name) : Table
901
    {
902
        $table = new Table($name, [], [], [], [], []);
903
        $table->setSchemaConfig($this->schemaManager->createSchemaConfig());
904
        $table->addColumn('id', 'integer', ['notnull' => true]);
905
        $table->addColumn('other_id', 'integer', ['notnull' => true]);
906
        $table->setPrimaryKey(['id', 'other_id']);
907
        $table->addColumn('test', 'string', ['length' => 255]);
908
909
        return $table;
910
    }
911
912
    /**
913
     * @param Table[] $tables
914
     */
915
    protected function assertHasTable(array $tables) : void
916
    {
917
        $foundTable = false;
918
919
        foreach ($tables as $table) {
920
            if (strtolower($table->getName()) !== 'list_tables_test_new_name') {
921
                continue;
922
            }
923
924
            $foundTable = true;
925
        }
926
927
        self::assertTrue($foundTable, 'Could not find new table');
928
    }
929
930
    public function testListForeignKeysComposite() : void
931
    {
932
        if (! $this->connection->getDatabasePlatform()->supportsForeignKeyConstraints()) {
933
            self::markTestSkipped('Does not support foreign key constraints.');
934
        }
935
936
        $this->schemaManager->createTable($this->getTestTable('test_create_fk3'));
937
        $this->schemaManager->createTable($this->getTestCompositeTable('test_create_fk4'));
938
939
        $foreignKey = new ForeignKeyConstraint(
940
            ['id', 'foreign_key_test'],
941
            'test_create_fk4',
942
            ['id', 'other_id'],
943
            'foreign_key_test_fk2'
944
        );
945
946
        $this->schemaManager->createForeignKey($foreignKey, 'test_create_fk3');
947
948
        $fkeys = $this->schemaManager->listTableForeignKeys('test_create_fk3');
949
950
        self::assertCount(1, $fkeys, "Table 'test_create_fk3' has to have one foreign key.");
951
952
        self::assertEquals(['id', 'foreign_key_test'], array_map('strtolower', $fkeys[0]->getLocalColumns()));
953
        self::assertEquals(['id', 'other_id'], array_map('strtolower', $fkeys[0]->getForeignColumns()));
954
    }
955
956
    /**
957
     * @group DBAL-44
958
     */
959
    public function testColumnDefaultLifecycle() : void
960
    {
961
        $table = new Table('col_def_lifecycle');
962
        $table->addColumn('id', 'integer', ['autoincrement' => true]);
963
        $table->addColumn('column1', 'string', [
964
            'length' => 1,
965
            'default' => null,
966
        ]);
967
        $table->addColumn('column2', 'string', [
968
            'length' => 1,
969
            'default' => '',
970
        ]);
971
        $table->addColumn('column3', 'string', [
972
            'length' => 8,
973
            'default' => 'default1',
974
        ]);
975
        $table->addColumn('column4', 'integer', [
976
            'length' => 1,
977
            'default' => 0,
978
        ]);
979
        $table->setPrimaryKey(['id']);
980
981
        $this->schemaManager->dropAndCreateTable($table);
982
983
        $columns = $this->schemaManager->listTableColumns('col_def_lifecycle');
984
985
        self::assertNull($columns['id']->getDefault());
986
        self::assertNull($columns['column1']->getDefault());
987
        self::assertSame('', $columns['column2']->getDefault());
988
        self::assertSame('default1', $columns['column3']->getDefault());
989
        self::assertSame('0', $columns['column4']->getDefault());
990
991
        $diffTable = clone $table;
992
993
        $diffTable->changeColumn('column1', ['default' => '']);
994
        $diffTable->changeColumn('column2', ['default' => null]);
995
        $diffTable->changeColumn('column3', ['default' => 'default2']);
996
        $diffTable->changeColumn('column4', ['default' => null]);
997
998
        $comparator = new Comparator();
999
1000
        $diff = $comparator->diffTable($table, $diffTable);
1001
1002
        self::assertNotNull($diff);
1003
1004
        $this->schemaManager->alterTable($diff);
1005
1006
        $columns = $this->schemaManager->listTableColumns('col_def_lifecycle');
1007
1008
        self::assertSame('', $columns['column1']->getDefault());
1009
        self::assertNull($columns['column2']->getDefault());
1010
        self::assertSame('default2', $columns['column3']->getDefault());
1011
        self::assertNull($columns['column4']->getDefault());
1012
    }
1013
1014
    public function testListTableWithBinary() : void
1015
    {
1016
        $tableName = 'test_binary_table';
1017
1018
        $table = new Table($tableName);
1019
        $table->addColumn('id', 'integer');
1020
        $table->addColumn('column_varbinary', 'binary', ['length' => 16]);
1021
        $table->addColumn('column_binary', 'binary', ['fixed' => true]);
1022
        $table->setPrimaryKey(['id']);
1023
1024
        $this->schemaManager->createTable($table);
1025
1026
        $table = $this->schemaManager->listTableDetails($tableName);
1027
1028
        self::assertInstanceOf(BinaryType::class, $table->getColumn('column_varbinary')->getType());
1029
        self::assertFalse($table->getColumn('column_varbinary')->getFixed());
1030
1031
        self::assertInstanceOf(BinaryType::class, $table->getColumn('column_binary')->getType());
1032
        self::assertTrue($table->getColumn('column_binary')->getFixed());
1033
    }
1034
1035
    public function testListTableDetailsWithFullQualifiedTableName() : void
1036
    {
1037
        if (! $this->schemaManager->getDatabasePlatform()->supportsSchemas()) {
1038
            self::markTestSkipped('Test only works on platforms that support schemas.');
1039
        }
1040
1041
        $defaultSchemaName = $this->schemaManager->getDatabasePlatform()->getDefaultSchemaName();
1042
        $primaryTableName  = 'primary_table';
1043
        $foreignTableName  = 'foreign_table';
1044
1045
        $table = new Table($foreignTableName);
1046
        $table->addColumn('id', 'integer', ['autoincrement' => true]);
1047
        $table->setPrimaryKey(['id']);
1048
1049
        $this->schemaManager->dropAndCreateTable($table);
1050
1051
        $table = new Table($primaryTableName);
1052
        $table->addColumn('id', 'integer', ['autoincrement' => true]);
1053
        $table->addColumn('foo', 'integer');
1054
        $table->addColumn('bar', 'string', ['length' => 32]);
1055
        $table->addForeignKeyConstraint($foreignTableName, ['foo'], ['id']);
1056
        $table->addIndex(['bar']);
1057
        $table->setPrimaryKey(['id']);
1058
1059
        $this->schemaManager->dropAndCreateTable($table);
1060
1061
        self::assertEquals(
1062
            $this->schemaManager->listTableColumns($primaryTableName),
1063
            $this->schemaManager->listTableColumns($defaultSchemaName . '.' . $primaryTableName)
1064
        );
1065
        self::assertEquals(
1066
            $this->schemaManager->listTableIndexes($primaryTableName),
1067
            $this->schemaManager->listTableIndexes($defaultSchemaName . '.' . $primaryTableName)
1068
        );
1069
        self::assertEquals(
1070
            $this->schemaManager->listTableForeignKeys($primaryTableName),
1071
            $this->schemaManager->listTableForeignKeys($defaultSchemaName . '.' . $primaryTableName)
1072
        );
1073
    }
1074
1075
    /**
1076
     * @group DBAL-1095
1077
     */
1078
    public function testDoesNotListIndexesImplicitlyCreatedByForeignKeys() : void
1079
    {
1080
        if (! $this->schemaManager->getDatabasePlatform()->supportsForeignKeyConstraints()) {
1081
            self::markTestSkipped('This test is only supported on platforms that have foreign keys.');
1082
        }
1083
1084
        $primaryTable = new Table('test_list_index_impl_primary');
1085
        $primaryTable->addColumn('id', 'integer');
1086
        $primaryTable->setPrimaryKey(['id']);
1087
1088
        $foreignTable = new Table('test_list_index_impl_foreign');
1089
        $foreignTable->addColumn('fk1', 'integer');
1090
        $foreignTable->addColumn('fk2', 'integer');
1091
        $foreignTable->addIndex(['fk1'], 'explicit_fk1_idx');
1092
        $foreignTable->addForeignKeyConstraint('test_list_index_impl_primary', ['fk1'], ['id']);
1093
        $foreignTable->addForeignKeyConstraint('test_list_index_impl_primary', ['fk2'], ['id']);
1094
1095
        $this->schemaManager->dropAndCreateTable($primaryTable);
1096
        $this->schemaManager->dropAndCreateTable($foreignTable);
1097
1098
        $indexes = $this->schemaManager->listTableIndexes('test_list_index_impl_foreign');
1099
1100
        self::assertCount(2, $indexes);
1101
        self::assertArrayHasKey('explicit_fk1_idx', $indexes);
1102
        self::assertArrayHasKey('idx_3d6c147fdc58d6c', $indexes);
1103
    }
1104
1105
    /**
1106
     * @group 2782
1107
     * @group 6654
1108
     */
1109
    public function testComparatorShouldNotAddCommentToJsonTypeSinceItIsTheDefaultNow() : void
1110
    {
1111
        if (! $this->schemaManager->getDatabasePlatform()->hasNativeJsonType()) {
1112
            self::markTestSkipped('This test is only supported on platforms that have native JSON type.');
1113
        }
1114
1115
        $this->connection->executeQuery('CREATE TABLE json_test (parameters JSON NOT NULL)');
1116
1117
        $table = new Table('json_test');
1118
        $table->addColumn('parameters', 'json');
1119
1120
        $comparator = new Comparator();
1121
        $tableDiff  = $comparator->diffTable($this->schemaManager->listTableDetails('json_test'), $table);
1122
1123
        self::assertNull($tableDiff);
1124
    }
1125
1126
    /**
1127
     * @dataProvider commentsProvider
1128
     * @group 2596
1129
     */
1130
    public function testExtractDoctrineTypeFromComment(string $comment, ?string $expectedType) : void
1131
    {
1132
        $re = new ReflectionMethod($this->schemaManager, 'extractDoctrineTypeFromComment');
1133
        $re->setAccessible(true);
1134
1135
        self::assertSame($expectedType, $re->invokeArgs($this->schemaManager, [&$comment]));
1136
    }
1137
1138
    /**
1139
     * @return mixed[][]
1140
     */
1141
    public static function commentsProvider() : iterable
1142
    {
1143
        return [
1144
            'invalid custom type comments'      => ['should.return.null', null],
1145
            'valid doctrine type'               => ['(DC2Type:guid)', 'guid'],
1146
            'valid with dots'                   => ['(DC2Type:type.should.return)', 'type.should.return'],
1147
            'valid with namespace'              => ['(DC2Type:Namespace\Class)', 'Namespace\Class'],
1148
            'valid with extra closing bracket'  => ['(DC2Type:should.stop)).before)', 'should.stop'],
1149
            'valid with extra opening brackets' => ['(DC2Type:should((.stop)).before)', 'should((.stop'],
1150
        ];
1151
    }
1152
1153
    public function testCreateAndListSequences() : void
1154
    {
1155
        if (! $this->schemaManager->getDatabasePlatform()->supportsSequences()) {
1156
            self::markTestSkipped('This test is only supported on platforms that support sequences.');
1157
        }
1158
1159
        $sequence1Name           = 'sequence_1';
1160
        $sequence1AllocationSize = 1;
1161
        $sequence1InitialValue   = 2;
1162
        $sequence2Name           = 'sequence_2';
1163
        $sequence2AllocationSize = 3;
1164
        $sequence2InitialValue   = 4;
1165
        $sequence1               = new Sequence($sequence1Name, $sequence1AllocationSize, $sequence1InitialValue);
1166
        $sequence2               = new Sequence($sequence2Name, $sequence2AllocationSize, $sequence2InitialValue);
1167
1168
        $this->schemaManager->createSequence($sequence1);
1169
        $this->schemaManager->createSequence($sequence2);
1170
1171
        /** @var Sequence[] $actualSequences */
1172
        $actualSequences = [];
1173
        foreach ($this->schemaManager->listSequences() as $sequence) {
1174
            $actualSequences[$sequence->getName()] = $sequence;
1175
        }
1176
1177
        $actualSequence1 = $actualSequences[$sequence1Name];
1178
        $actualSequence2 = $actualSequences[$sequence2Name];
1179
1180
        self::assertSame($sequence1Name, $actualSequence1->getName());
1181
        self::assertEquals($sequence1AllocationSize, $actualSequence1->getAllocationSize());
1182
        self::assertEquals($sequence1InitialValue, $actualSequence1->getInitialValue());
1183
1184
        self::assertSame($sequence2Name, $actualSequence2->getName());
1185
        self::assertEquals($sequence2AllocationSize, $actualSequence2->getAllocationSize());
1186
        self::assertEquals($sequence2InitialValue, $actualSequence2->getInitialValue());
1187
    }
1188
1189
    /**
1190
     * @group #3086
1191
     */
1192
    public function testComparisonWithAutoDetectedSequenceDefinition() : void
1193
    {
1194
        if (! $this->schemaManager->getDatabasePlatform()->supportsSequences()) {
1195
            self::markTestSkipped('This test is only supported on platforms that support sequences.');
1196
        }
1197
1198
        $sequenceName           = 'sequence_auto_detect_test';
1199
        $sequenceAllocationSize = 5;
1200
        $sequenceInitialValue   = 10;
1201
        $sequence               = new Sequence($sequenceName, $sequenceAllocationSize, $sequenceInitialValue);
1202
1203
        $this->schemaManager->dropAndCreateSequence($sequence);
1204
1205
        $createdSequence = array_values(
1206
            array_filter(
1207
                $this->schemaManager->listSequences(),
1208
                static function (Sequence $sequence) use ($sequenceName) : bool {
1209
                    return strcasecmp($sequence->getName(), $sequenceName) === 0;
1210
                }
1211
            )
1212
        )[0] ?? null;
1213
1214
        self::assertNotNull($createdSequence);
1215
1216
        $comparator = new Comparator();
1217
        $tableDiff  = $comparator->diffSequence($createdSequence, $sequence);
1218
1219
        self::assertFalse($tableDiff);
1220
    }
1221
1222
    /**
1223
     * @group DBAL-2921
1224
     */
1225
    public function testPrimaryKeyAutoIncrement() : void
1226
    {
1227
        $table = new Table('test_pk_auto_increment');
1228
        $table->addColumn('id', 'integer', ['autoincrement' => true]);
1229
        $table->addColumn('text', 'string', ['length' => 1]);
1230
        $table->setPrimaryKey(['id']);
1231
        $this->schemaManager->dropAndCreateTable($table);
1232
1233
        $this->connection->insert('test_pk_auto_increment', ['text' => '1']);
1234
1235
        $query = $this->connection->query('SELECT id FROM test_pk_auto_increment WHERE text = \'1\'');
1236
        assert($query instanceof Statement);
1237
1238
        $query->execute();
1239
        $lastUsedIdBeforeDelete = (int) $query->fetchColumn();
1240
1241
        $this->connection->query('DELETE FROM test_pk_auto_increment');
1242
1243
        $this->connection->insert('test_pk_auto_increment', ['text' => '2']);
1244
1245
        $query = $this->connection->query('SELECT id FROM test_pk_auto_increment WHERE text = \'2\'');
1246
        assert($query instanceof Statement);
1247
1248
        $query->execute();
1249
        $lastUsedIdAfterDelete = (int) $query->fetchColumn();
1250
1251
        self::assertGreaterThan($lastUsedIdBeforeDelete, $lastUsedIdAfterDelete);
1252
    }
1253
1254
    public function testGenerateAnIndexWithPartialColumnLength() : void
1255
    {
1256
        if (! $this->schemaManager->getDatabasePlatform()->supportsColumnLengthIndexes()) {
1257
            self::markTestSkipped('This test is only supported on platforms that support indexes with column length definitions.');
1258
        }
1259
1260
        $table = new Table('test_partial_column_index');
1261
        $table->addColumn('long_column', 'string', ['length' => 40]);
1262
        $table->addColumn('standard_column', 'integer');
1263
        $table->addIndex(['long_column'], 'partial_long_column_idx', [], ['lengths' => [4]]);
1264
        $table->addIndex(['standard_column', 'long_column'], 'standard_and_partial_idx', [], ['lengths' => [null, 2]]);
1265
1266
        $expected = $table->getIndexes();
1267
1268
        $this->schemaManager->dropAndCreateTable($table);
1269
1270
        $onlineTable = $this->schemaManager->listTableDetails('test_partial_column_index');
1271
        self::assertEquals($expected, $onlineTable->getIndexes());
1272
    }
1273
1274
    public function testCommentInTable() : void
1275
    {
1276
        $table = new Table('table_with_comment');
1277
        $table->addColumn('id', 'integer');
1278
        $table->setComment('Foo with control characters \'\\');
1279
        $this->schemaManager->dropAndCreateTable($table);
1280
1281
        $table = $this->schemaManager->listTableDetails('table_with_comment');
1282
        self::assertSame('Foo with control characters \'\\', $table->getComment());
1283
    }
1284
1285
    public function testSchemaDiffForeignKeys() : void
1286
    {
1287
        $schemaManager = $this->connection->getSchemaManager();
1288
        $platform      = $this->connection->getDatabasePlatform();
1289
1290
        $table1 = new Table('child');
1291
        $table1->addColumn('id', 'integer', ['autoincrement' => true]);
1292
        $table1->addColumn('parent_id', 'integer');
1293
        $table1->setPrimaryKey(['id']);
1294
        $table1->addForeignKeyConstraint('parent', ['parent_id'], ['id']);
1295
1296
        $table2 = new Table('parent');
1297
        $table2->addColumn('id', 'integer', ['autoincrement' => true]);
1298
        $table2->setPrimaryKey(['id']);
1299
1300
        $diff = new SchemaDiff(['table1' => $table1, 'table2' => $table2]);
1301
        $sqls = $diff->toSql($platform);
1302
1303
        foreach ($sqls as $sql) {
1304
            $this->connection->exec($sql);
1305
        }
1306
1307
        $schema = new Schema([
1308
            $schemaManager->listTableDetails('child'),
1309
            $schemaManager->listTableDetails('parent'),
1310
        ]);
1311
1312
        self::assertCount(1, $schema->getTable('child')->getForeignKeys());
1313
1314
        $offlineSchema = new Schema([$table1, $table2]);
1315
1316
        $diff = Comparator::compareSchemas($offlineSchema, $schema);
1317
1318
        foreach ($diff->changedTables as $table) {
1319
            if (count($table->changedForeignKeys) <= 0 && count($table->addedForeignKeys) <= 0 && count($table->removedForeignKeys) <= 0) {
1320
                continue;
1321
            }
1322
1323
            self::fail(
1324
                'No changes on foreigh keys should be detected, but we have: ' .
1325
                implode(', ', $diff->toSql($platform))
1326
            );
1327
        }
1328
    }
1329
}
1330