Failed Conditions
Push — master ( ac0e13...24dbc4 )
by Sergei
22s queued 15s
created

testListTableColumns()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 62
Code Lines 51

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 51
nc 1
nop 0
dl 0
loc 62
rs 9.069
c 1
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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