Passed
Push — master ( fb0ce7...e4af10 )
by Sergei
03:36
created

testComparisonWithAutoDetectedSequenceDefinition()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 28
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

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

728
        $this->_sm->alterTable(/** @scrutinizer ignore-type */ $tableDiff);
Loading history...
729
730
        $table       = $this->_sm->listTableDetails('test_fk_rename');
731
        $foreignKeys = $table->getForeignKeys();
732
733
        self::assertTrue($table->hasColumn('rename_fk_id'));
734
        self::assertCount(1, $foreignKeys);
735
        self::assertSame(['rename_fk_id'], array_map('strtolower', current($foreignKeys)->getColumns()));
736
    }
737
738
    /**
739
     * @group DBAL-1062
740
     */
741
    public function testRenameIndexUsedInForeignKeyConstraint()
742
    {
743
        if (! $this->_sm->getDatabasePlatform()->supportsForeignKeyConstraints()) {
744
            $this->markTestSkipped('This test is only supported on platforms that have foreign keys.');
745
        }
746
747
        $primaryTable = new Table('test_rename_index_primary');
748
        $primaryTable->addColumn('id', 'integer');
749
        $primaryTable->setPrimaryKey(array('id'));
750
751
        $foreignTable = new Table('test_rename_index_foreign');
752
        $foreignTable->addColumn('fk', 'integer');
753
        $foreignTable->addIndex(array('fk'), 'rename_index_fk_idx');
754
        $foreignTable->addForeignKeyConstraint(
755
            'test_rename_index_primary',
756
            array('fk'),
757
            array('id'),
758
            array(),
759
            'fk_constraint'
760
        );
761
762
        $this->_sm->dropAndCreateTable($primaryTable);
763
        $this->_sm->dropAndCreateTable($foreignTable);
764
765
        $foreignTable2 = clone $foreignTable;
766
        $foreignTable2->renameIndex('rename_index_fk_idx', 'renamed_index_fk_idx');
767
768
        $comparator = new Comparator();
769
770
        $this->_sm->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

770
        $this->_sm->alterTable(/** @scrutinizer ignore-type */ $comparator->diffTable($foreignTable, $foreignTable2));
Loading history...
771
772
        $foreignTable = $this->_sm->listTableDetails('test_rename_index_foreign');
773
774
        self::assertFalse($foreignTable->hasIndex('rename_index_fk_idx'));
775
        self::assertTrue($foreignTable->hasIndex('renamed_index_fk_idx'));
776
        self::assertTrue($foreignTable->hasForeignKey('fk_constraint'));
777
    }
778
779
    /**
780
     * @group DBAL-42
781
     */
782
    public function testGetColumnComment()
783
    {
784
        if ( ! $this->_conn->getDatabasePlatform()->supportsInlineColumnComments() &&
785
             ! $this->_conn->getDatabasePlatform()->supportsCommentOnStatement() &&
786
            $this->_conn->getDatabasePlatform()->getName() != 'mssql') {
787
            $this->markTestSkipped('Database does not support column comments.');
788
        }
789
790
        $table = new Table('column_comment_test');
791
        $table->addColumn('id', 'integer', array('comment' => 'This is a comment'));
792
        $table->setPrimaryKey(array('id'));
793
794
        $this->_sm->createTable($table);
795
796
        $columns = $this->_sm->listTableColumns("column_comment_test");
797
        self::assertEquals(1, count($columns));
798
        self::assertEquals('This is a comment', $columns['id']->getComment());
799
800
        $tableDiff = new \Doctrine\DBAL\Schema\TableDiff('column_comment_test');
801
        $tableDiff->fromTable = $table;
802
        $tableDiff->changedColumns['id'] = new \Doctrine\DBAL\Schema\ColumnDiff(
803
            'id', new \Doctrine\DBAL\Schema\Column(
804
                'id', \Doctrine\DBAL\Types\Type::getType('integer')
805
            ),
806
            array('comment'),
807
            new \Doctrine\DBAL\Schema\Column(
808
                'id', \Doctrine\DBAL\Types\Type::getType('integer'), array('comment' => 'This is a comment')
809
            )
810
        );
811
812
        $this->_sm->alterTable($tableDiff);
813
814
        $columns = $this->_sm->listTableColumns("column_comment_test");
815
        self::assertEquals(1, count($columns));
816
        self::assertEmpty($columns['id']->getComment());
817
    }
818
819
    /**
820
     * @group DBAL-42
821
     */
822
    public function testAutomaticallyAppendCommentOnMarkedColumns()
823
    {
824
        if ( ! $this->_conn->getDatabasePlatform()->supportsInlineColumnComments() &&
825
             ! $this->_conn->getDatabasePlatform()->supportsCommentOnStatement() &&
826
            $this->_conn->getDatabasePlatform()->getName() != 'mssql') {
827
            $this->markTestSkipped('Database does not support column comments.');
828
        }
829
830
        $table = new Table('column_comment_test2');
831
        $table->addColumn('id', 'integer', array('comment' => 'This is a comment'));
832
        $table->addColumn('obj', 'object', array('comment' => 'This is a comment'));
833
        $table->addColumn('arr', 'array', array('comment' => 'This is a comment'));
834
        $table->setPrimaryKey(array('id'));
835
836
        $this->_sm->createTable($table);
837
838
        $columns = $this->_sm->listTableColumns("column_comment_test2");
839
        self::assertEquals(3, count($columns));
840
        self::assertEquals('This is a comment', $columns['id']->getComment());
841
        self::assertEquals('This is a comment', $columns['obj']->getComment(), "The Doctrine2 Typehint should be stripped from comment.");
842
        self::assertInstanceOf('Doctrine\DBAL\Types\ObjectType', $columns['obj']->getType(), "The Doctrine2 should be detected from comment hint.");
843
        self::assertEquals('This is a comment', $columns['arr']->getComment(), "The Doctrine2 Typehint should be stripped from comment.");
844
        self::assertInstanceOf('Doctrine\DBAL\Types\ArrayType', $columns['arr']->getType(), "The Doctrine2 should be detected from comment hint.");
845
    }
846
847
    /**
848
     * @group DBAL-1228
849
     */
850
    public function testCommentHintOnDateIntervalTypeColumn()
851
    {
852
        if ( ! $this->_conn->getDatabasePlatform()->supportsInlineColumnComments() &&
853
            ! $this->_conn->getDatabasePlatform()->supportsCommentOnStatement() &&
854
            $this->_conn->getDatabasePlatform()->getName() != 'mssql') {
855
            $this->markTestSkipped('Database does not support column comments.');
856
        }
857
858
        $table = new Table('column_dateinterval_comment');
859
        $table->addColumn('id', 'integer', array('comment' => 'This is a comment'));
860
        $table->addColumn('date_interval', 'dateinterval', array('comment' => 'This is a comment'));
861
        $table->setPrimaryKey(array('id'));
862
863
        $this->_sm->createTable($table);
864
865
        $columns = $this->_sm->listTableColumns("column_dateinterval_comment");
866
        self::assertEquals(2, count($columns));
867
        self::assertEquals('This is a comment', $columns['id']->getComment());
868
        self::assertEquals('This is a comment', $columns['date_interval']->getComment(), "The Doctrine2 Typehint should be stripped from comment.");
869
        self::assertInstanceOf('Doctrine\DBAL\Types\DateIntervalType', $columns['date_interval']->getType(), "The Doctrine2 should be detected from comment hint.");
870
    }
871
872
    /**
873
     * @group DBAL-825
874
     */
875
    public function testChangeColumnsTypeWithDefaultValue()
876
    {
877
        $tableName = 'column_def_change_type';
878
        $table     = new Table($tableName);
879
880
        $table->addColumn('col_int', 'smallint', array('default' => 666));
881
        $table->addColumn('col_string', 'string', array('default' => 'foo'));
882
883
        $this->_sm->dropAndCreateTable($table);
884
885
        $tableDiff = new TableDiff($tableName);
886
        $tableDiff->fromTable = $table;
887
        $tableDiff->changedColumns['col_int'] = new ColumnDiff(
888
            'col_int',
889
            new Column('col_int', Type::getType('integer'), array('default' => 666)),
890
            array('type'),
891
            new Column('col_int', Type::getType('smallint'), array('default' => 666))
892
        );
893
894
        $tableDiff->changedColumns['col_string'] = new ColumnDiff(
895
            'col_string',
896
            new Column('col_string', Type::getType('string'), array('default' => 'foo', 'fixed' => true)),
897
            array('fixed'),
898
            new Column('col_string', Type::getType('string'), array('default' => 'foo'))
899
        );
900
901
        $this->_sm->alterTable($tableDiff);
902
903
        $columns = $this->_sm->listTableColumns($tableName);
904
905
        self::assertInstanceOf('Doctrine\DBAL\Types\IntegerType', $columns['col_int']->getType());
906
        self::assertEquals(666, $columns['col_int']->getDefault());
907
908
        self::assertInstanceOf('Doctrine\DBAL\Types\StringType', $columns['col_string']->getType());
909
        self::assertEquals('foo', $columns['col_string']->getDefault());
910
    }
911
912
    /**
913
     * @group DBAL-197
914
     */
915
    public function testListTableWithBlob()
916
    {
917
        $table = new Table('test_blob_table');
918
        $table->addColumn('id', 'integer', ['comment' => 'This is a comment']);
919
        $table->addColumn('binarydata', 'blob', []);
920
        $table->setPrimaryKey(['id']);
921
922
        $this->_sm->createTable($table);
923
924
        $created = $this->_sm->listTableDetails('test_blob_table');
925
926
        self::assertTrue($created->hasColumn('id'));
927
        self::assertTrue($created->hasColumn('binarydata'));
928
        self::assertTrue($created->hasPrimaryKey());
929
    }
930
931
    /**
932
     * @param string $name
933
     * @param array  $data
934
     * @return Table
935
     */
936
    protected function createTestTable($name = 'test_table', $data = array())
937
    {
938
        $options = $data['options'] ?? [];
939
940
        $table = $this->getTestTable($name, $options);
941
942
        $this->_sm->dropAndCreateTable($table);
943
944
        return $table;
945
    }
946
947
    protected function getTestTable($name, $options=array())
948
    {
949
        $table = new Table($name, array(), array(), array(), 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

949
        $table = new Table($name, array(), array(), array(), /** @scrutinizer ignore-type */ false, $options);
Loading history...
950
        $table->setSchemaConfig($this->_sm->createSchemaConfig());
951
        $table->addColumn('id', 'integer', array('notnull' => true));
952
        $table->setPrimaryKey(array('id'));
953
        $table->addColumn('test', 'string', array('length' => 255));
954
        $table->addColumn('foreign_key_test', 'integer');
955
        return $table;
956
    }
957
958
    protected function getTestCompositeTable($name)
959
    {
960
        $table = new Table($name, array(), array(), array(), false, array());
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

960
        $table = new Table($name, array(), array(), array(), /** @scrutinizer ignore-type */ false, array());
Loading history...
961
        $table->setSchemaConfig($this->_sm->createSchemaConfig());
962
        $table->addColumn('id', 'integer', array('notnull' => true));
963
        $table->addColumn('other_id', 'integer', array('notnull' => true));
964
        $table->setPrimaryKey(array('id', 'other_id'));
965
        $table->addColumn('test', 'string', array('length' => 255));
966
        return $table;
967
    }
968
969
    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

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