Failed Conditions
Pull Request — develop (#3518)
by Michael
29:00 queued 25:29
created

SchemaManagerFunctionalTestCase   F

Complexity

Total Complexity 107

Size/Duplication

Total Lines 1407
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 107
eloc 796
dl 0
loc 1407
rs 1.804
c 0
b 0
f 0

54 Methods

Rating   Name   Duplication   Size   Complexity  
A testAutoincrementDetection() 0 16 2
A testCreateAndListViews() 0 16 2
A testUpdateSchemaWithForeignKeyRenaming() 0 40 2
A testAutomaticallyAppendCommentOnMarkedColumns() 0 23 4
A testCommentHintOnDateIntervalTypeColumn() 0 20 4
A testTableInNamespace() 0 22 3
A testRenameIndexUsedInForeignKeyConstraint() 0 36 2
A testGetColumnComment() 0 39 4
A testChangeColumnsTypeWithDefaultValue() 0 35 1
A testListTableWithBlob() 0 14 1
A testAutoincrementDetectionMulticolumns() 0 17 2
A createTestTable() 0 9 1
A testListForeignKeysComposite() 0 25 2
A testListTableDetailsWithFullQualifiedTableName() 0 37 2
A testListTableWithBinary() 0 19 1
A testCommentStringsAreQuoted() 0 16 4
A testColumnDefaultLifecycle() 0 49 1
A testCommentNotDuplicated() 0 20 2
A testDropAndCreateIndex() 0 14 1
A getCreateExampleViewSql() 0 3 1
A createListTableColumns() 0 13 1
A testDiffListTableColumns() 0 14 2
A testCreateTableWithForeignKeys() 0 26 2
A testCreateSchema() 0 6 1
A testListTableColumnsDispatchEvent() 0 24 1
A testListNamespaceNames() 0 18 3
A testListDatabases() 0 12 2
B testAlterTableScenario() 0 87 3
A testDropAndCreateSequence() 0 11 2
B testListTableColumns() 0 69 1
A testListSequences() 0 26 4
A hasElementWithName() 0 10 1
A testListTableColumnsWithFixedStringColumn() 0 15 1
A testListTableIndexes() 0 26 1
A testDropsDatabaseWithActiveConnections() 0 32 4
A testListForeignKeys() 0 33 3
A testListTableIndexesDispatchEvent() 0 26 1
A testListTables() 0 23 3
A getPlatformName() 0 7 1
A testDoesNotListIndexesImplicitlyCreatedByForeignKeys() 0 25 2
A getAlterColumnComment() 0 15 1
A assertHasTable() 0 15 3
A getTestCompositeTable() 0 10 1
A tearDown() 0 12 2
A testAlterColumnComment() 0 41 4
A getTestTable() 0 10 1
A setUp() 0 11 2
A testComparisonWithAutoDetectedSequenceDefinition() 0 28 2
A testComparatorShouldNotAddCommentToJsonTypeSinceItIsTheDefaultNow() 0 15 2
A testCreateAndListSequences() 0 34 3
A testPrimaryKeyAutoIncrement() 0 23 1
A testGenerateAnIndexWithPartialColumnLength() 0 18 2
A commentsProvider() 0 9 1
A testExtractDoctrineTypeFromComment() 0 6 1

How to fix   Complexity   

Complex Class

Complex classes like SchemaManagerFunctionalTestCase often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use SchemaManagerFunctionalTestCase, and based on these observations, apply Extract Interface, too.

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

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

796
        $this->schemaManager->alterTable(/** @scrutinizer ignore-type */ $comparator->diffTable($foreignTable, $foreignTable2));
Loading history...
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()
809
    {
810
        if (! $this->connection->getDatabasePlatform()->supportsInlineColumnComments() &&
811
             ! $this->connection->getDatabasePlatform()->supportsCommentOnStatement() &&
812
            $this->connection->getDatabasePlatform()->getName() !== 'mssql') {
813
            $this->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()
853
    {
854
        if (! $this->connection->getDatabasePlatform()->supportsInlineColumnComments() &&
855
             ! $this->connection->getDatabasePlatform()->supportsCommentOnStatement() &&
856
            $this->connection->getDatabasePlatform()->getName() !== 'mssql') {
857
            $this->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()
881
    {
882
        if (! $this->connection->getDatabasePlatform()->supportsInlineColumnComments() &&
883
            ! $this->connection->getDatabasePlatform()->supportsCommentOnStatement() &&
884
            $this->connection->getDatabasePlatform()->getName() !== 'mssql') {
885
            $this->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()
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', ['default' => 'foo']);
912
913
        $this->schemaManager->dropAndCreateTable($table);
914
915
        $tableDiff                            = new TableDiff($tableName);
916
        $tableDiff->fromTable                 = $table;
917
        $tableDiff->changedColumns['col_int'] = new ColumnDiff(
918
            'col_int',
919
            new Column('col_int', Type::getType('integer'), ['default' => 666]),
920
            ['type'],
921
            new Column('col_int', Type::getType('smallint'), ['default' => 666])
922
        );
923
924
        $tableDiff->changedColumns['col_string'] = new ColumnDiff(
925
            'col_string',
926
            new Column('col_string', Type::getType('string'), ['default' => 'foo', 'fixed' => true]),
927
            ['fixed'],
928
            new Column('col_string', Type::getType('string'), ['default' => 'foo'])
929
        );
930
931
        $this->schemaManager->alterTable($tableDiff);
932
933
        $columns = $this->schemaManager->listTableColumns($tableName);
934
935
        self::assertInstanceOf(IntegerType::class, $columns['col_int']->getType());
936
        self::assertEquals(666, $columns['col_int']->getDefault());
937
938
        self::assertInstanceOf(StringType::class, $columns['col_string']->getType());
939
        self::assertEquals('foo', $columns['col_string']->getDefault());
940
    }
941
942
    /**
943
     * @group DBAL-197
944
     */
945
    public function testListTableWithBlob()
946
    {
947
        $table = new Table('test_blob_table');
948
        $table->addColumn('id', 'integer', ['comment' => 'This is a comment']);
949
        $table->addColumn('binarydata', 'blob', []);
950
        $table->setPrimaryKey(['id']);
951
952
        $this->schemaManager->createTable($table);
953
954
        $created = $this->schemaManager->listTableDetails('test_blob_table');
955
956
        self::assertTrue($created->hasColumn('id'));
957
        self::assertTrue($created->hasColumn('binarydata'));
958
        self::assertTrue($created->hasPrimaryKey());
959
    }
960
961
    /**
962
     * @param string  $name
963
     * @param mixed[] $data
964
     *
965
     * @return Table
966
     */
967
    protected function createTestTable($name = 'test_table', array $data = [])
968
    {
969
        $options = $data['options'] ?? [];
970
971
        $table = $this->getTestTable($name, $options);
972
973
        $this->schemaManager->dropAndCreateTable($table);
974
975
        return $table;
976
    }
977
978
    protected function getTestTable($name, $options = [])
979
    {
980
        $table = new Table($name, [], [], [], [], $options);
981
        $table->setSchemaConfig($this->schemaManager->createSchemaConfig());
982
        $table->addColumn('id', 'integer', ['notnull' => true]);
983
        $table->setPrimaryKey(['id']);
984
        $table->addColumn('test', 'string', ['length' => 255]);
985
        $table->addColumn('foreign_key_test', 'integer');
986
987
        return $table;
988
    }
989
990
    protected function getTestCompositeTable($name)
991
    {
992
        $table = new Table($name, [], [], [], [], []);
993
        $table->setSchemaConfig($this->schemaManager->createSchemaConfig());
994
        $table->addColumn('id', 'integer', ['notnull' => true]);
995
        $table->addColumn('other_id', 'integer', ['notnull' => true]);
996
        $table->setPrimaryKey(['id', 'other_id']);
997
        $table->addColumn('test', 'string', ['length' => 255]);
998
999
        return $table;
1000
    }
1001
1002
    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

1002
    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...
1003
    {
1004
        $foundTable = false;
1005
1006
        foreach ($tables as $table) {
1007
            self::assertInstanceOf(Table::class, $table, 'No Table instance was found in tables array.');
1008
1009
            if (strtolower($table->getName()) !== 'list_tables_test_new_name') {
1010
                continue;
1011
            }
1012
1013
            $foundTable = true;
1014
        }
1015
1016
        self::assertTrue($foundTable, 'Could not find new table');
1017
    }
1018
1019
    public function testListForeignKeysComposite()
1020
    {
1021
        if (! $this->connection->getDatabasePlatform()->supportsForeignKeyConstraints()) {
1022
            $this->markTestSkipped('Does not support foreign key constraints.');
1023
        }
1024
1025
        $this->schemaManager->createTable($this->getTestTable('test_create_fk3'));
1026
        $this->schemaManager->createTable($this->getTestCompositeTable('test_create_fk4'));
1027
1028
        $foreignKey = new ForeignKeyConstraint(
1029
            ['id', 'foreign_key_test'],
1030
            'test_create_fk4',
1031
            ['id', 'other_id'],
1032
            'foreign_key_test_fk2'
1033
        );
1034
1035
        $this->schemaManager->createForeignKey($foreignKey, 'test_create_fk3');
1036
1037
        $fkeys = $this->schemaManager->listTableForeignKeys('test_create_fk3');
1038
1039
        self::assertEquals(1, count($fkeys), "Table 'test_create_fk3' has to have one foreign key.");
1040
1041
        self::assertInstanceOf(ForeignKeyConstraint::class, $fkeys[0]);
1042
        self::assertEquals(['id', 'foreign_key_test'], array_map('strtolower', $fkeys[0]->getLocalColumns()));
1043
        self::assertEquals(['id', 'other_id'], array_map('strtolower', $fkeys[0]->getForeignColumns()));
1044
    }
1045
1046
    /**
1047
     * @group DBAL-44
1048
     */
1049
    public function testColumnDefaultLifecycle()
1050
    {
1051
        $table = new Table('col_def_lifecycle');
1052
        $table->addColumn('id', 'integer', ['autoincrement' => true]);
1053
        $table->addColumn('column1', 'string', ['default' => null]);
1054
        $table->addColumn('column2', 'string', ['default' => false]);
1055
        $table->addColumn('column3', 'string', ['default' => true]);
1056
        $table->addColumn('column4', 'string', ['default' => 0]);
1057
        $table->addColumn('column5', 'string', ['default' => '']);
1058
        $table->addColumn('column6', 'string', ['default' => 'def']);
1059
        $table->addColumn('column7', 'integer', ['default' => 0]);
1060
        $table->setPrimaryKey(['id']);
1061
1062
        $this->schemaManager->dropAndCreateTable($table);
1063
1064
        $columns = $this->schemaManager->listTableColumns('col_def_lifecycle');
1065
1066
        self::assertNull($columns['id']->getDefault());
1067
        self::assertNull($columns['column1']->getDefault());
1068
        self::assertSame('', $columns['column2']->getDefault());
1069
        self::assertSame('1', $columns['column3']->getDefault());
1070
        self::assertSame('0', $columns['column4']->getDefault());
1071
        self::assertSame('', $columns['column5']->getDefault());
1072
        self::assertSame('def', $columns['column6']->getDefault());
1073
        self::assertSame('0', $columns['column7']->getDefault());
1074
1075
        $diffTable = clone $table;
1076
1077
        $diffTable->changeColumn('column1', ['default' => false]);
1078
        $diffTable->changeColumn('column2', ['default' => null]);
1079
        $diffTable->changeColumn('column3', ['default' => false]);
1080
        $diffTable->changeColumn('column4', ['default' => null]);
1081
        $diffTable->changeColumn('column5', ['default' => false]);
1082
        $diffTable->changeColumn('column6', ['default' => 666]);
1083
        $diffTable->changeColumn('column7', ['default' => null]);
1084
1085
        $comparator = new Comparator();
1086
1087
        $this->schemaManager->alterTable($comparator->diffTable($table, $diffTable));
1088
1089
        $columns = $this->schemaManager->listTableColumns('col_def_lifecycle');
1090
1091
        self::assertSame('', $columns['column1']->getDefault());
1092
        self::assertNull($columns['column2']->getDefault());
1093
        self::assertSame('', $columns['column3']->getDefault());
1094
        self::assertNull($columns['column4']->getDefault());
1095
        self::assertSame('', $columns['column5']->getDefault());
1096
        self::assertSame('666', $columns['column6']->getDefault());
1097
        self::assertNull($columns['column7']->getDefault());
1098
    }
1099
1100
    public function testListTableWithBinary()
1101
    {
1102
        $tableName = 'test_binary_table';
1103
1104
        $table = new Table($tableName);
1105
        $table->addColumn('id', 'integer');
1106
        $table->addColumn('column_varbinary', 'binary', []);
1107
        $table->addColumn('column_binary', 'binary', ['fixed' => true]);
1108
        $table->setPrimaryKey(['id']);
1109
1110
        $this->schemaManager->createTable($table);
1111
1112
        $table = $this->schemaManager->listTableDetails($tableName);
1113
1114
        self::assertInstanceOf(BinaryType::class, $table->getColumn('column_varbinary')->getType());
1115
        self::assertFalse($table->getColumn('column_varbinary')->getFixed());
1116
1117
        self::assertInstanceOf(BinaryType::class, $table->getColumn('column_binary')->getType());
1118
        self::assertTrue($table->getColumn('column_binary')->getFixed());
1119
    }
1120
1121
    public function testListTableDetailsWithFullQualifiedTableName()
1122
    {
1123
        if (! $this->schemaManager->getDatabasePlatform()->supportsSchemas()) {
1124
            $this->markTestSkipped('Test only works on platforms that support schemas.');
1125
        }
1126
1127
        $defaultSchemaName = $this->schemaManager->getDatabasePlatform()->getDefaultSchemaName();
1128
        $primaryTableName  = 'primary_table';
1129
        $foreignTableName  = 'foreign_table';
1130
1131
        $table = new Table($foreignTableName);
1132
        $table->addColumn('id', 'integer', ['autoincrement' => true]);
1133
        $table->setPrimaryKey(['id']);
1134
1135
        $this->schemaManager->dropAndCreateTable($table);
1136
1137
        $table = new Table($primaryTableName);
1138
        $table->addColumn('id', 'integer', ['autoincrement' => true]);
1139
        $table->addColumn('foo', 'integer');
1140
        $table->addColumn('bar', 'string');
1141
        $table->addForeignKeyConstraint($foreignTableName, ['foo'], ['id']);
1142
        $table->addIndex(['bar']);
1143
        $table->setPrimaryKey(['id']);
1144
1145
        $this->schemaManager->dropAndCreateTable($table);
1146
1147
        self::assertEquals(
1148
            $this->schemaManager->listTableColumns($primaryTableName),
1149
            $this->schemaManager->listTableColumns($defaultSchemaName . '.' . $primaryTableName)
1150
        );
1151
        self::assertEquals(
1152
            $this->schemaManager->listTableIndexes($primaryTableName),
1153
            $this->schemaManager->listTableIndexes($defaultSchemaName . '.' . $primaryTableName)
1154
        );
1155
        self::assertEquals(
1156
            $this->schemaManager->listTableForeignKeys($primaryTableName),
1157
            $this->schemaManager->listTableForeignKeys($defaultSchemaName . '.' . $primaryTableName)
1158
        );
1159
    }
1160
1161
    public function testCommentStringsAreQuoted()
1162
    {
1163
        if (! $this->connection->getDatabasePlatform()->supportsInlineColumnComments() &&
1164
            ! $this->connection->getDatabasePlatform()->supportsCommentOnStatement() &&
1165
            $this->connection->getDatabasePlatform()->getName() !== 'mssql') {
1166
            $this->markTestSkipped('Database does not support column comments.');
1167
        }
1168
1169
        $table = new Table('my_table');
1170
        $table->addColumn('id', 'integer', ['comment' => "It's a comment with a quote"]);
1171
        $table->setPrimaryKey(['id']);
1172
1173
        $this->schemaManager->createTable($table);
1174
1175
        $columns = $this->schemaManager->listTableColumns('my_table');
1176
        self::assertEquals("It's a comment with a quote", $columns['id']->getComment());
1177
    }
1178
1179
    public function testCommentNotDuplicated()
1180
    {
1181
        if (! $this->connection->getDatabasePlatform()->supportsInlineColumnComments()) {
1182
            $this->markTestSkipped('Database does not support column comments.');
1183
        }
1184
1185
        $options          = [
1186
            'type' => Type::getType('integer'),
1187
            'default' => 0,
1188
            'notnull' => true,
1189
            'comment' => 'expected+column+comment',
1190
        ];
1191
        $columnDefinition = substr($this->connection->getDatabasePlatform()->getColumnDeclarationSQL('id', $options), strlen('id') + 1);
1192
1193
        $table = new Table('my_table');
1194
        $table->addColumn('id', 'integer', ['columnDefinition' => $columnDefinition, 'comment' => 'unexpected_column_comment']);
1195
        $sql = $this->connection->getDatabasePlatform()->getCreateTableSQL($table);
1196
1197
        self::assertStringContainsString('expected+column+comment', $sql[0]);
1198
        self::assertStringNotContainsString('unexpected_column_comment', $sql[0]);
1199
    }
1200
1201
    /**
1202
     * @group DBAL-1009
1203
     * @dataProvider getAlterColumnComment
1204
     */
1205
    public function testAlterColumnComment($comment1, $expectedComment1, $comment2, $expectedComment2)
1206
    {
1207
        if (! $this->connection->getDatabasePlatform()->supportsInlineColumnComments() &&
1208
            ! $this->connection->getDatabasePlatform()->supportsCommentOnStatement() &&
1209
            $this->connection->getDatabasePlatform()->getName() !== 'mssql') {
1210
            $this->markTestSkipped('Database does not support column comments.');
1211
        }
1212
1213
        $offlineTable = new Table('alter_column_comment_test');
1214
        $offlineTable->addColumn('comment1', 'integer', ['comment' => $comment1]);
1215
        $offlineTable->addColumn('comment2', 'integer', ['comment' => $comment2]);
1216
        $offlineTable->addColumn('no_comment1', 'integer');
1217
        $offlineTable->addColumn('no_comment2', 'integer');
1218
        $this->schemaManager->dropAndCreateTable($offlineTable);
1219
1220
        $onlineTable = $this->schemaManager->listTableDetails('alter_column_comment_test');
1221
1222
        self::assertSame($expectedComment1, $onlineTable->getColumn('comment1')->getComment());
1223
        self::assertSame($expectedComment2, $onlineTable->getColumn('comment2')->getComment());
1224
        self::assertNull($onlineTable->getColumn('no_comment1')->getComment());
1225
        self::assertNull($onlineTable->getColumn('no_comment2')->getComment());
1226
1227
        $onlineTable->changeColumn('comment1', ['comment' => $comment2]);
1228
        $onlineTable->changeColumn('comment2', ['comment' => $comment1]);
1229
        $onlineTable->changeColumn('no_comment1', ['comment' => $comment1]);
1230
        $onlineTable->changeColumn('no_comment2', ['comment' => $comment2]);
1231
1232
        $comparator = new Comparator();
1233
1234
        $tableDiff = $comparator->diffTable($offlineTable, $onlineTable);
1235
1236
        self::assertInstanceOf(TableDiff::class, $tableDiff);
1237
1238
        $this->schemaManager->alterTable($tableDiff);
1239
1240
        $onlineTable = $this->schemaManager->listTableDetails('alter_column_comment_test');
1241
1242
        self::assertSame($expectedComment2, $onlineTable->getColumn('comment1')->getComment());
1243
        self::assertSame($expectedComment1, $onlineTable->getColumn('comment2')->getComment());
1244
        self::assertSame($expectedComment1, $onlineTable->getColumn('no_comment1')->getComment());
1245
        self::assertSame($expectedComment2, $onlineTable->getColumn('no_comment2')->getComment());
1246
    }
1247
1248
    public function getAlterColumnComment()
1249
    {
1250
        return [
1251
            [null, null, ' ', ' '],
1252
            [null, null, '0', '0'],
1253
            [null, null, 'foo', 'foo'],
1254
1255
            ['', null, ' ', ' '],
1256
            ['', null, '0', '0'],
1257
            ['', null, 'foo', 'foo'],
1258
1259
            [' ', ' ', '0', '0'],
1260
            [' ', ' ', 'foo', 'foo'],
1261
1262
            ['0', '0', 'foo', 'foo'],
1263
        ];
1264
    }
1265
1266
    /**
1267
     * @group DBAL-1095
1268
     */
1269
    public function testDoesNotListIndexesImplicitlyCreatedByForeignKeys()
1270
    {
1271
        if (! $this->schemaManager->getDatabasePlatform()->supportsForeignKeyConstraints()) {
1272
            $this->markTestSkipped('This test is only supported on platforms that have foreign keys.');
1273
        }
1274
1275
        $primaryTable = new Table('test_list_index_impl_primary');
1276
        $primaryTable->addColumn('id', 'integer');
1277
        $primaryTable->setPrimaryKey(['id']);
1278
1279
        $foreignTable = new Table('test_list_index_impl_foreign');
1280
        $foreignTable->addColumn('fk1', 'integer');
1281
        $foreignTable->addColumn('fk2', 'integer');
1282
        $foreignTable->addIndex(['fk1'], 'explicit_fk1_idx');
1283
        $foreignTable->addForeignKeyConstraint('test_list_index_impl_primary', ['fk1'], ['id']);
1284
        $foreignTable->addForeignKeyConstraint('test_list_index_impl_primary', ['fk2'], ['id']);
1285
1286
        $this->schemaManager->dropAndCreateTable($primaryTable);
1287
        $this->schemaManager->dropAndCreateTable($foreignTable);
1288
1289
        $indexes = $this->schemaManager->listTableIndexes('test_list_index_impl_foreign');
1290
1291
        self::assertCount(2, $indexes);
1292
        self::assertArrayHasKey('explicit_fk1_idx', $indexes);
1293
        self::assertArrayHasKey('idx_3d6c147fdc58d6c', $indexes);
1294
    }
1295
1296
    /**
1297
     * @group 2782
1298
     * @group 6654
1299
     */
1300
    public function testComparatorShouldNotAddCommentToJsonTypeSinceItIsTheDefaultNow() : void
1301
    {
1302
        if (! $this->schemaManager->getDatabasePlatform()->hasNativeJsonType()) {
1303
            $this->markTestSkipped('This test is only supported on platforms that have native JSON type.');
1304
        }
1305
1306
        $this->connection->executeQuery('CREATE TABLE json_test (parameters JSON NOT NULL)');
1307
1308
        $table = new Table('json_test');
1309
        $table->addColumn('parameters', 'json');
1310
1311
        $comparator = new Comparator();
1312
        $tableDiff  = $comparator->diffTable($this->schemaManager->listTableDetails('json_test'), $table);
1313
1314
        self::assertFalse($tableDiff);
1315
    }
1316
1317
    /**
1318
     * @dataProvider commentsProvider
1319
     * @group 2596
1320
     */
1321
    public function testExtractDoctrineTypeFromComment(?string $comment, ?string $expectedType) : void
1322
    {
1323
        $re = new ReflectionMethod($this->schemaManager, 'extractDoctrineTypeFromComment');
1324
        $re->setAccessible(true);
1325
1326
        self::assertSame($expectedType, $re->invokeArgs($this->schemaManager, [&$comment]));
1327
    }
1328
1329
    /**
1330
     * @return mixed[][]
1331
     */
1332
    public static function commentsProvider() : iterable
1333
    {
1334
        return [
1335
            'invalid custom type comments'      => ['should.return.null', null],
1336
            'valid doctrine type'               => ['(DC2Type:guid)', 'guid'],
1337
            'valid with dots'                   => ['(DC2Type:type.should.return)', 'type.should.return'],
1338
            'valid with namespace'              => ['(DC2Type:Namespace\Class)', 'Namespace\Class'],
1339
            'valid with extra closing bracket'  => ['(DC2Type:should.stop)).before)', 'should.stop'],
1340
            'valid with extra opening brackets' => ['(DC2Type:should((.stop)).before)', 'should((.stop'],
1341
        ];
1342
    }
1343
1344
    public function testCreateAndListSequences() : void
1345
    {
1346
        if (! $this->schemaManager->getDatabasePlatform()->supportsSequences()) {
1347
            self::markTestSkipped('This test is only supported on platforms that support sequences.');
1348
        }
1349
1350
        $sequence1Name           = 'sequence_1';
1351
        $sequence1AllocationSize = 1;
1352
        $sequence1InitialValue   = 2;
1353
        $sequence2Name           = 'sequence_2';
1354
        $sequence2AllocationSize = 3;
1355
        $sequence2InitialValue   = 4;
1356
        $sequence1               = new Sequence($sequence1Name, $sequence1AllocationSize, $sequence1InitialValue);
1357
        $sequence2               = new Sequence($sequence2Name, $sequence2AllocationSize, $sequence2InitialValue);
1358
1359
        $this->schemaManager->createSequence($sequence1);
1360
        $this->schemaManager->createSequence($sequence2);
1361
1362
        /** @var Sequence[] $actualSequences */
1363
        $actualSequences = [];
1364
        foreach ($this->schemaManager->listSequences() as $sequence) {
1365
            $actualSequences[$sequence->getName()] = $sequence;
1366
        }
1367
1368
        $actualSequence1 = $actualSequences[$sequence1Name];
1369
        $actualSequence2 = $actualSequences[$sequence2Name];
1370
1371
        self::assertSame($sequence1Name, $actualSequence1->getName());
1372
        self::assertEquals($sequence1AllocationSize, $actualSequence1->getAllocationSize());
1373
        self::assertEquals($sequence1InitialValue, $actualSequence1->getInitialValue());
1374
1375
        self::assertSame($sequence2Name, $actualSequence2->getName());
1376
        self::assertEquals($sequence2AllocationSize, $actualSequence2->getAllocationSize());
1377
        self::assertEquals($sequence2InitialValue, $actualSequence2->getInitialValue());
1378
    }
1379
1380
    /**
1381
     * @group #3086
1382
     */
1383
    public function testComparisonWithAutoDetectedSequenceDefinition() : void
1384
    {
1385
        if (! $this->schemaManager->getDatabasePlatform()->supportsSequences()) {
1386
            self::markTestSkipped('This test is only supported on platforms that support sequences.');
1387
        }
1388
1389
        $sequenceName           = 'sequence_auto_detect_test';
1390
        $sequenceAllocationSize = 5;
1391
        $sequenceInitialValue   = 10;
1392
        $sequence               = new Sequence($sequenceName, $sequenceAllocationSize, $sequenceInitialValue);
1393
1394
        $this->schemaManager->dropAndCreateSequence($sequence);
1395
1396
        $createdSequence = array_values(
1397
            array_filter(
1398
                $this->schemaManager->listSequences(),
1399
                static function (Sequence $sequence) use ($sequenceName) : bool {
1400
                    return strcasecmp($sequence->getName(), $sequenceName) === 0;
1401
                }
1402
            )
1403
        )[0] ?? null;
1404
1405
        self::assertNotNull($createdSequence);
1406
1407
        $comparator = new Comparator();
1408
        $tableDiff  = $comparator->diffSequence($createdSequence, $sequence);
1409
1410
        self::assertFalse($tableDiff);
1411
    }
1412
1413
    /**
1414
     * @group DBAL-2921
1415
     */
1416
    public function testPrimaryKeyAutoIncrement()
1417
    {
1418
        $table = new Table('test_pk_auto_increment');
1419
        $table->addColumn('id', 'integer', ['autoincrement' => true]);
1420
        $table->addColumn('text', 'string');
1421
        $table->setPrimaryKey(['id']);
1422
        $this->schemaManager->dropAndCreateTable($table);
1423
1424
        $this->connection->insert('test_pk_auto_increment', ['text' => '1']);
1425
1426
        $query = $this->connection->query('SELECT id FROM test_pk_auto_increment WHERE text = \'1\'');
1427
        $query->execute();
0 ignored issues
show
Bug introduced by
The method execute() does not exist on Doctrine\DBAL\Driver\ResultStatement. It seems like you code against a sub-type of Doctrine\DBAL\Driver\ResultStatement such as Doctrine\DBAL\Driver\Statement. ( Ignorable by Annotation )

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

1427
        $query->/** @scrutinizer ignore-call */ 
1428
                execute();
Loading history...
1428
        $lastUsedIdBeforeDelete = (int) $query->fetchColumn();
1429
1430
        $this->connection->query('DELETE FROM test_pk_auto_increment');
1431
1432
        $this->connection->insert('test_pk_auto_increment', ['text' => '2']);
1433
1434
        $query = $this->connection->query('SELECT id FROM test_pk_auto_increment WHERE text = \'2\'');
1435
        $query->execute();
1436
        $lastUsedIdAfterDelete = (int) $query->fetchColumn();
1437
1438
        $this->assertGreaterThan($lastUsedIdBeforeDelete, $lastUsedIdAfterDelete);
1439
    }
1440
1441
    public function testGenerateAnIndexWithPartialColumnLength() : void
1442
    {
1443
        if (! $this->schemaManager->getDatabasePlatform()->supportsColumnLengthIndexes()) {
1444
            self::markTestSkipped('This test is only supported on platforms that support indexes with column length definitions.');
1445
        }
1446
1447
        $table = new Table('test_partial_column_index');
1448
        $table->addColumn('long_column', 'string', ['length' => 40]);
1449
        $table->addColumn('standard_column', 'integer');
1450
        $table->addIndex(['long_column'], 'partial_long_column_idx', [], ['lengths' => [4]]);
1451
        $table->addIndex(['standard_column', 'long_column'], 'standard_and_partial_idx', [], ['lengths' => [null, 2]]);
1452
1453
        $expected = $table->getIndexes();
1454
1455
        $this->schemaManager->dropAndCreateTable($table);
1456
1457
        $onlineTable = $this->schemaManager->listTableDetails('test_partial_column_index');
1458
        self::assertEquals($expected, $onlineTable->getIndexes());
1459
    }
1460
}
1461