Completed
Pull Request — master (#3512)
by David
16:25
created

testCommentInTable()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

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

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

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

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

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

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

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

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

989
        $table = new Table($name, [], [], [], /** @scrutinizer ignore-type */ false, []);
Loading history...
990
        $table->setSchemaConfig($this->schemaManager->createSchemaConfig());
991
        $table->addColumn('id', 'integer', ['notnull' => true]);
992
        $table->addColumn('other_id', 'integer', ['notnull' => true]);
993
        $table->setPrimaryKey(['id', 'other_id']);
994
        $table->addColumn('test', 'string', ['length' => 255]);
995
996
        return $table;
997
    }
998
999
    protected function assertHasTable($tables, $tableName)
0 ignored issues
show
Unused Code introduced by
The parameter $tableName is not used and could be removed. ( Ignorable by Annotation )

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

999
    protected function assertHasTable($tables, /** @scrutinizer ignore-unused */ $tableName)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

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