Passed
Pull Request — master (#3120)
by Sergei
12:25
created

SchemaManagerFunctionalTestCase   F

Complexity

Total Complexity 113

Size/Duplication

Total Lines 1422
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 113
dl 0
loc 1422
rs 0.6314
c 0
b 0
f 0

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