ComparatorTest.php 45.5 KB
Newer Older
1 2 3 4
<?php

namespace Doctrine\Tests\DBAL\Schema;

jeroendedauw's avatar
jeroendedauw committed
5 6 7 8 9 10 11 12 13 14 15 16
use Doctrine\DBAL\Schema\Column;
use Doctrine\DBAL\Schema\ColumnDiff;
use Doctrine\DBAL\Schema\Comparator;
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
use Doctrine\DBAL\Schema\Index;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Schema\SchemaConfig;
use Doctrine\DBAL\Schema\SchemaDiff;
use Doctrine\DBAL\Schema\Sequence;
use Doctrine\DBAL\Schema\Table;
use Doctrine\DBAL\Schema\TableDiff;
use Doctrine\DBAL\Types\Type;
Sergei Morozov's avatar
Sergei Morozov committed
17
use PHPUnit\Framework\TestCase;
18
use function array_keys;
19
use function get_class;
20

Sergei Morozov's avatar
Sergei Morozov committed
21
class ComparatorTest extends TestCase
22
{
23
    public function testCompareSame1() : void
24
    {
Sergei Morozov's avatar
Sergei Morozov committed
25 26 27 28 29 30
        $schema1 = new Schema([
            'bugdb' => new Table(
                'bugdb',
                [
                    'integerfield1' => new Column('integerfield1', Type::getType('integer')),
                ]
31
            ),
Sergei Morozov's avatar
Sergei Morozov committed
32 33 34 35 36 37 38
        ]);
        $schema2 = new Schema([
            'bugdb' => new Table(
                'bugdb',
                [
                    'integerfield1' => new Column('integerfield1', Type::getType('integer')),
                ]
39
            ),
Sergei Morozov's avatar
Sergei Morozov committed
40
        ]);
41

Sergei Morozov's avatar
Sergei Morozov committed
42
        $expected             = new SchemaDiff();
43
        $expected->fromSchema = $schema1;
Sergei Morozov's avatar
Sergei Morozov committed
44
        self::assertEquals($expected, Comparator::compareSchemas($schema1, $schema2));
45 46
    }

47
    public function testCompareSame2() : void
48
    {
Sergei Morozov's avatar
Sergei Morozov committed
49 50 51 52
        $schema1 = new Schema([
            'bugdb' => new Table(
                'bugdb',
                [
53 54
                    'integerfield1' => new Column('integerfield1', Type::getType('integer')),
                    'integerfield2' => new Column('integerfield2', Type::getType('integer')),
Sergei Morozov's avatar
Sergei Morozov committed
55
                ]
56
            ),
Sergei Morozov's avatar
Sergei Morozov committed
57 58 59 60 61
        ]);
        $schema2 = new Schema([
            'bugdb' => new Table(
                'bugdb',
                [
62 63
                    'integerfield2' => new Column('integerfield2', Type::getType('integer')),
                    'integerfield1' => new Column('integerfield1', Type::getType('integer')),
Sergei Morozov's avatar
Sergei Morozov committed
64
                ]
65
            ),
Sergei Morozov's avatar
Sergei Morozov committed
66
        ]);
67

Sergei Morozov's avatar
Sergei Morozov committed
68
        $expected             = new SchemaDiff();
69
        $expected->fromSchema = $schema1;
Sergei Morozov's avatar
Sergei Morozov committed
70
        self::assertEquals($expected, Comparator::compareSchemas($schema1, $schema2));
71 72
    }

73
    public function testCompareMissingTable() : void
74
    {
Sergei Morozov's avatar
Sergei Morozov committed
75 76
        $schemaConfig = new SchemaConfig();
        $table        = new Table('bugdb', ['integerfield1' => new Column('integerfield1', Type::getType('integer'))]);
77
        $table->setSchemaConfig($schemaConfig);
78

Sergei Morozov's avatar
Sergei Morozov committed
79 80
        $schema1 = new Schema([$table], [], $schemaConfig);
        $schema2 = new Schema([], [], $schemaConfig);
81

Sergei Morozov's avatar
Sergei Morozov committed
82
        $expected = new SchemaDiff([], [], ['bugdb' => $table], $schema1);
83

Sergei Morozov's avatar
Sergei Morozov committed
84
        self::assertEquals($expected, Comparator::compareSchemas($schema1, $schema2));
85 86
    }

87
    public function testCompareNewTable() : void
88
    {
Sergei Morozov's avatar
Sergei Morozov committed
89 90
        $schemaConfig = new SchemaConfig();
        $table        = new Table('bugdb', ['integerfield1' => new Column('integerfield1', Type::getType('integer'))]);
91
        $table->setSchemaConfig($schemaConfig);
92

Sergei Morozov's avatar
Sergei Morozov committed
93 94
        $schema1 = new Schema([], [], $schemaConfig);
        $schema2 = new Schema([$table], [], $schemaConfig);
95

Sergei Morozov's avatar
Sergei Morozov committed
96
        $expected = new SchemaDiff(['bugdb' => $table], [], [], $schema1);
97

Sergei Morozov's avatar
Sergei Morozov committed
98
        self::assertEquals($expected, Comparator::compareSchemas($schema1, $schema2));
99 100
    }

101
    public function testCompareOnlyAutoincrementChanged() : void
102
    {
Sergei Morozov's avatar
Sergei Morozov committed
103 104
        $column1 = new Column('foo', Type::getType('integer'), ['autoincrement' => true]);
        $column2 = new Column('foo', Type::getType('integer'), ['autoincrement' => false]);
105

Sergei Morozov's avatar
Sergei Morozov committed
106
        $comparator        = new Comparator();
107 108
        $changedProperties = $comparator->diffColumn($column1, $column2);

Sergei Morozov's avatar
Sergei Morozov committed
109
        self::assertEquals(['autoincrement'], $changedProperties);
110 111
    }

112
    public function testCompareMissingField() : void
113
    {
114
        $missingColumn = new Column('integerfield1', Type::getType('integer'));
Sergei Morozov's avatar
Sergei Morozov committed
115 116 117 118
        $schema1       = new Schema([
            'bugdb' => new Table(
                'bugdb',
                [
119
                    'integerfield1' => $missingColumn,
120
                    'integerfield2' => new Column('integerfield2', Type::getType('integer')),
Sergei Morozov's avatar
Sergei Morozov committed
121
                ]
122
            ),
Sergei Morozov's avatar
Sergei Morozov committed
123 124 125 126 127
        ]);
        $schema2       = new Schema([
            'bugdb' => new Table(
                'bugdb',
                [
128
                    'integerfield2' => new Column('integerfield2', Type::getType('integer')),
Sergei Morozov's avatar
Sergei Morozov committed
129
                ]
130
            ),
Sergei Morozov's avatar
Sergei Morozov committed
131 132 133 134 135 136 137 138 139 140 141 142
        ]);

        $expected                                    = new SchemaDiff(
            [],
            [
                'bugdb' => new TableDiff(
                    'bugdb',
                    [],
                    [],
                    ['integerfield1' => $missingColumn]
                ),
            ]
143
        );
Sergei Morozov's avatar
Sergei Morozov committed
144
        $expected->fromSchema                        = $schema1;
145 146
        $expected->changedTables['bugdb']->fromTable = $schema1->getTable('bugdb');

Sergei Morozov's avatar
Sergei Morozov committed
147
        self::assertEquals($expected, Comparator::compareSchemas($schema1, $schema2));
148 149
    }

150
    public function testCompareNewField() : void
151
    {
Sergei Morozov's avatar
Sergei Morozov committed
152 153 154 155
        $schema1 = new Schema([
            'bugdb' => new Table(
                'bugdb',
                [
156
                    'integerfield1' => new Column('integerfield1', Type::getType('integer')),
Sergei Morozov's avatar
Sergei Morozov committed
157
                ]
158
            ),
Sergei Morozov's avatar
Sergei Morozov committed
159 160 161 162 163
        ]);
        $schema2 = new Schema([
            'bugdb' => new Table(
                'bugdb',
                [
164 165
                    'integerfield1' => new Column('integerfield1', Type::getType('integer')),
                    'integerfield2' => new Column('integerfield2', Type::getType('integer')),
Sergei Morozov's avatar
Sergei Morozov committed
166
                ]
167
            ),
Sergei Morozov's avatar
Sergei Morozov committed
168 169 170 171 172 173 174 175
        ]);

        $expected                                    = new SchemaDiff(
            [],
            [
                'bugdb' => new TableDiff(
                    'bugdb',
                    [
176
                        'integerfield2' => new Column('integerfield2', Type::getType('integer')),
Sergei Morozov's avatar
Sergei Morozov committed
177
                    ]
178
                ),
Sergei Morozov's avatar
Sergei Morozov committed
179
            ]
180
        );
Sergei Morozov's avatar
Sergei Morozov committed
181
        $expected->fromSchema                        = $schema1;
182 183
        $expected->changedTables['bugdb']->fromTable = $schema1->getTable('bugdb');

Sergei Morozov's avatar
Sergei Morozov committed
184
        self::assertEquals($expected, Comparator::compareSchemas($schema1, $schema2));
185 186
    }

187
    public function testCompareChangedColumnsChangeType() : void
188
    {
189 190
        $column1 = new Column('charfield1', Type::getType('string'));
        $column2 = new Column('charfield1', Type::getType('integer'));
191

192
        $c = new Comparator();
Sergei Morozov's avatar
Sergei Morozov committed
193 194
        self::assertEquals(['type'], $c->diffColumn($column1, $column2));
        self::assertEquals([], $c->diffColumn($column1, $column1));
195 196
    }

197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
    public function testCompareColumnsMultipleTypeInstances() : void
    {
        $integerType1 = Type::getType('integer');
        Type::overrideType('integer', get_class($integerType1));
        $integerType2 = Type::getType('integer');

        $column1 = new Column('integerfield1', $integerType1);
        $column2 = new Column('integerfield1', $integerType2);

        $c = new Comparator();
        self::assertEquals([], $c->diffColumn($column1, $column2));
    }

    public function testCompareColumnsOverriddenType() : void
    {
        $oldStringInstance = Type::getType('string');
        $integerType       = Type::getType('integer');

        Type::overrideType('string', get_class($integerType));
        $overriddenStringType = Type::getType('string');

        Type::overrideType('string', get_class($oldStringInstance));

        $column1 = new Column('integerfield1', $integerType);
        $column2 = new Column('integerfield1', $overriddenStringType);

        $c = new Comparator();
        self::assertEquals([], $c->diffColumn($column1, $column2));
    }

227
    public function testCompareChangedColumnsChangeCustomSchemaOption() : void
228 229 230 231 232 233
    {
        $column1 = new Column('charfield1', Type::getType('string'));
        $column2 = new Column('charfield1', Type::getType('string'));

        $column1->setCustomSchemaOption('foo', 'bar');
        $column2->setCustomSchemaOption('foo', 'bar');
234

235 236 237 238
        $column1->setCustomSchemaOption('foo1', 'bar1');
        $column2->setCustomSchemaOption('foo2', 'bar2');

        $c = new Comparator();
Sergei Morozov's avatar
Sergei Morozov committed
239 240
        self::assertEquals(['foo1', 'foo2'], $c->diffColumn($column1, $column2));
        self::assertEquals([], $c->diffColumn($column1, $column1));
241 242
    }

243
    public function testCompareChangeColumnsMultipleNewColumnsRename() : void
244
    {
Sergei Morozov's avatar
Sergei Morozov committed
245
        $tableA = new Table('foo');
246 247
        $tableA->addColumn('datefield1', 'datetime');

Sergei Morozov's avatar
Sergei Morozov committed
248
        $tableB = new Table('foo');
249 250 251
        $tableB->addColumn('new_datefield1', 'datetime');
        $tableB->addColumn('new_datefield2', 'datetime');

Sergei Morozov's avatar
Sergei Morozov committed
252
        $c         = new Comparator();
253 254
        $tableDiff = $c->diffTable($tableA, $tableB);

Sergei Morozov's avatar
Sergei Morozov committed
255
        self::assertCount(1, $tableDiff->renamedColumns, 'we should have one rename datefield1 => new_datefield1.');
256 257 258
        self::assertArrayHasKey('datefield1', $tableDiff->renamedColumns, "'datefield1' should be set to be renamed to new_datefield1");
        self::assertCount(1, $tableDiff->addedColumns, "'new_datefield2' should be added");
        self::assertArrayHasKey('new_datefield2', $tableDiff->addedColumns, "'new_datefield2' should be added, not created through renaming!");
Sergei Morozov's avatar
Sergei Morozov committed
259 260
        self::assertCount(0, $tableDiff->removedColumns, 'Nothing should be removed.');
        self::assertCount(0, $tableDiff->changedColumns, 'Nothing should be changed as all fields old & new have diff names.');
261
    }
Lee Davis's avatar
Lee Davis committed
262

263
    public function testCompareRemovedIndex() : void
264
    {
Sergei Morozov's avatar
Sergei Morozov committed
265 266 267 268
        $schema1 = new Schema([
            'bugdb' => new Table(
                'bugdb',
                [
269 270
                    'integerfield1' => new Column('integerfield1', Type::getType('integer')),
                    'integerfield2' => new Column('integerfield2', Type::getType('integer')),
Sergei Morozov's avatar
Sergei Morozov committed
271 272 273 274 275
                ],
                [
                    'primary' => new Index(
                        'primary',
                        ['integerfield1'],
276
                        true
Sergei Morozov's avatar
Sergei Morozov committed
277 278
                    ),
                ]
279
            ),
Sergei Morozov's avatar
Sergei Morozov committed
280 281 282 283 284
        ]);
        $schema2 = new Schema([
            'bugdb' => new Table(
                'bugdb',
                [
285 286
                    'integerfield1' => new Column('integerfield1', Type::getType('integer')),
                    'integerfield2' => new Column('integerfield2', Type::getType('integer')),
Sergei Morozov's avatar
Sergei Morozov committed
287
                ]
288
            ),
Sergei Morozov's avatar
Sergei Morozov committed
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305
        ]);

        $expected                                    = new SchemaDiff(
            [],
            [
                'bugdb' => new TableDiff(
                    'bugdb',
                    [],
                    [],
                    [],
                    [],
                    [],
                    [
                        'primary' => new Index(
                            'primary',
                            ['integerfield1'],
                            true
306
                        ),
Sergei Morozov's avatar
Sergei Morozov committed
307
                    ]
308
                ),
Sergei Morozov's avatar
Sergei Morozov committed
309
            ]
310
        );
Sergei Morozov's avatar
Sergei Morozov committed
311
        $expected->fromSchema                        = $schema1;
312 313
        $expected->changedTables['bugdb']->fromTable = $schema1->getTable('bugdb');

Sergei Morozov's avatar
Sergei Morozov committed
314
        self::assertEquals($expected, Comparator::compareSchemas($schema1, $schema2));
315 316
    }

317
    public function testCompareNewIndex() : void
318
    {
Sergei Morozov's avatar
Sergei Morozov committed
319 320 321 322
        $schema1 = new Schema([
            'bugdb' => new Table(
                'bugdb',
                [
323 324
                    'integerfield1' => new Column('integerfield1', Type::getType('integer')),
                    'integerfield2' => new Column('integerfield2', Type::getType('integer')),
Sergei Morozov's avatar
Sergei Morozov committed
325
                ]
326
            ),
Sergei Morozov's avatar
Sergei Morozov committed
327 328 329 330 331
        ]);
        $schema2 = new Schema([
            'bugdb' => new Table(
                'bugdb',
                [
332 333
                    'integerfield1' => new Column('integerfield1', Type::getType('integer')),
                    'integerfield2' => new Column('integerfield2', Type::getType('integer')),
Sergei Morozov's avatar
Sergei Morozov committed
334 335 336 337 338
                ],
                [
                    'primary' => new Index(
                        'primary',
                        ['integerfield1'],
339
                        true
Sergei Morozov's avatar
Sergei Morozov committed
340 341
                    ),
                ]
342
            ),
Sergei Morozov's avatar
Sergei Morozov committed
343 344 345 346 347 348 349 350 351 352 353 354 355 356
        ]);

        $expected                                    = new SchemaDiff(
            [],
            [
                'bugdb' => new TableDiff(
                    'bugdb',
                    [],
                    [],
                    [],
                    [
                        'primary' => new Index(
                            'primary',
                            ['integerfield1'],
357
                            true
Sergei Morozov's avatar
Sergei Morozov committed
358 359
                        ),
                    ]
360
                ),
Sergei Morozov's avatar
Sergei Morozov committed
361
            ]
362
        );
Sergei Morozov's avatar
Sergei Morozov committed
363
        $expected->fromSchema                        = $schema1;
364 365
        $expected->changedTables['bugdb']->fromTable = $schema1->getTable('bugdb');

Sergei Morozov's avatar
Sergei Morozov committed
366
        self::assertEquals($expected, Comparator::compareSchemas($schema1, $schema2));
367 368
    }

369
    public function testCompareChangedIndex() : void
370
    {
Sergei Morozov's avatar
Sergei Morozov committed
371 372 373 374
        $schema1 = new Schema([
            'bugdb' => new Table(
                'bugdb',
                [
375 376
                    'integerfield1' => new Column('integerfield1', Type::getType('integer')),
                    'integerfield2' => new Column('integerfield2', Type::getType('integer')),
Sergei Morozov's avatar
Sergei Morozov committed
377 378 379 380 381
                ],
                [
                    'primary' => new Index(
                        'primary',
                        ['integerfield1'],
382
                        true
Sergei Morozov's avatar
Sergei Morozov committed
383 384
                    ),
                ]
385
            ),
Sergei Morozov's avatar
Sergei Morozov committed
386 387 388 389 390
        ]);
        $schema2 = new Schema([
            'bugdb' => new Table(
                'bugdb',
                [
391 392
                    'integerfield1' => new Column('integerfield1', Type::getType('integer')),
                    'integerfield2' => new Column('integerfield2', Type::getType('integer')),
Sergei Morozov's avatar
Sergei Morozov committed
393 394 395 396 397
                ],
                [
                    'primary' => new Index(
                        'primary',
                        ['integerfield1', 'integerfield2'],
398
                        true
Sergei Morozov's avatar
Sergei Morozov committed
399 400
                    ),
                ]
401
            ),
Sergei Morozov's avatar
Sergei Morozov committed
402 403 404 405 406 407 408 409 410 411 412 413 414 415 416
        ]);

        $expected                                    = new SchemaDiff(
            [],
            [
                'bugdb' => new TableDiff(
                    'bugdb',
                    [],
                    [],
                    [],
                    [],
                    [
                        'primary' => new Index(
                            'primary',
                            [
417
                                'integerfield1',
Sergei Morozov's avatar
Sergei Morozov committed
418 419
                                'integerfield2',
                            ],
420
                            true
Sergei Morozov's avatar
Sergei Morozov committed
421 422
                        ),
                    ]
423
                ),
Sergei Morozov's avatar
Sergei Morozov committed
424
            ]
425
        );
Sergei Morozov's avatar
Sergei Morozov committed
426
        $expected->fromSchema                        = $schema1;
427 428
        $expected->changedTables['bugdb']->fromTable = $schema1->getTable('bugdb');

Sergei Morozov's avatar
Sergei Morozov committed
429
        self::assertEquals($expected, Comparator::compareSchemas($schema1, $schema2));
430
    }
431

432
    public function testCompareChangedIndexFieldPositions() : void
433
    {
Sergei Morozov's avatar
Sergei Morozov committed
434 435 436 437
        $schema1 = new Schema([
            'bugdb' => new Table(
                'bugdb',
                [
438 439
                    'integerfield1' => new Column('integerfield1', Type::getType('integer')),
                    'integerfield2' => new Column('integerfield2', Type::getType('integer')),
Sergei Morozov's avatar
Sergei Morozov committed
440 441 442 443
                ],
                [
                    'primary' => new Index('primary', ['integerfield1', 'integerfield2'], true),
                ]
444
            ),
Sergei Morozov's avatar
Sergei Morozov committed
445 446 447 448 449
        ]);
        $schema2 = new Schema([
            'bugdb' => new Table(
                'bugdb',
                [
450 451
                    'integerfield1' => new Column('integerfield1', Type::getType('integer')),
                    'integerfield2' => new Column('integerfield2', Type::getType('integer')),
Sergei Morozov's avatar
Sergei Morozov committed
452 453 454 455
                ],
                [
                    'primary' => new Index('primary', ['integerfield2', 'integerfield1'], true),
                ]
456
            ),
Sergei Morozov's avatar
Sergei Morozov committed
457 458 459 460 461 462 463 464 465 466 467 468 469 470
        ]);

        $expected                                    = new SchemaDiff(
            [],
            [
                'bugdb' => new TableDiff(
                    'bugdb',
                    [],
                    [],
                    [],
                    [],
                    [
                        'primary' => new Index('primary', ['integerfield2', 'integerfield1'], true),
                    ]
471
                ),
Sergei Morozov's avatar
Sergei Morozov committed
472
            ]
473
        );
Sergei Morozov's avatar
Sergei Morozov committed
474
        $expected->fromSchema                        = $schema1;
475 476
        $expected->changedTables['bugdb']->fromTable = $schema1->getTable('bugdb');

Sergei Morozov's avatar
Sergei Morozov committed
477
        self::assertEquals($expected, Comparator::compareSchemas($schema1, $schema2));
478 479
    }

480
    public function testCompareSequences() : void
481 482 483 484
    {
        $seq1 = new Sequence('foo', 1, 1);
        $seq2 = new Sequence('foo', 1, 2);
        $seq3 = new Sequence('foo', 2, 1);
485
        $seq4 = new Sequence('foo', '1', '1');
486 487 488

        $c = new Comparator();

489 490
        self::assertTrue($c->diffSequence($seq1, $seq2));
        self::assertTrue($c->diffSequence($seq1, $seq3));
491
        self::assertFalse($c->diffSequence($seq1, $seq4));
492 493
    }

494
    public function testRemovedSequence() : void
495 496
    {
        $schema1 = new Schema();
Sergei Morozov's avatar
Sergei Morozov committed
497
        $seq     = $schema1->createSequence('foo');
498 499 500

        $schema2 = new Schema();

Sergei Morozov's avatar
Sergei Morozov committed
501
        $c          = new Comparator();
502 503
        $diffSchema = $c->compare($schema1, $schema2);

Gabriel Caruso's avatar
Gabriel Caruso committed
504
        self::assertCount(1, $diffSchema->removedSequences);
505
        self::assertSame($seq, $diffSchema->removedSequences[0]);
506 507
    }

508
    public function testAddedSequence() : void
509 510 511 512
    {
        $schema1 = new Schema();

        $schema2 = new Schema();
Sergei Morozov's avatar
Sergei Morozov committed
513
        $seq     = $schema2->createSequence('foo');
514

Sergei Morozov's avatar
Sergei Morozov committed
515
        $c          = new Comparator();
516 517
        $diffSchema = $c->compare($schema1, $schema2);

Gabriel Caruso's avatar
Gabriel Caruso committed
518
        self::assertCount(1, $diffSchema->newSequences);
519
        self::assertSame($seq, $diffSchema->newSequences[0]);
520 521
    }

522
    public function testTableAddForeignKey() : void
523
    {
Sergei Morozov's avatar
Sergei Morozov committed
524
        $tableForeign = new Table('bar');
525
        $tableForeign->addColumn('id', 'integer');
526

Sergei Morozov's avatar
Sergei Morozov committed
527
        $table1 = new Table('foo');
528
        $table1->addColumn('fk', 'integer');
529

Sergei Morozov's avatar
Sergei Morozov committed
530
        $table2 = new Table('foo');
531
        $table2->addColumn('fk', 'integer');
Sergei Morozov's avatar
Sergei Morozov committed
532
        $table2->addForeignKeyConstraint($tableForeign, ['fk'], ['id']);
533

Sergei Morozov's avatar
Sergei Morozov committed
534
        $c         = new Comparator();
535 536
        $tableDiff = $c->diffTable($table1, $table2);

Sergei Morozov's avatar
Sergei Morozov committed
537
        self::assertInstanceOf(TableDiff::class, $tableDiff);
Gabriel Caruso's avatar
Gabriel Caruso committed
538
        self::assertCount(1, $tableDiff->addedForeignKeys);
539 540
    }

541
    public function testTableRemoveForeignKey() : void
542
    {
Sergei Morozov's avatar
Sergei Morozov committed
543
        $tableForeign = new Table('bar');
544
        $tableForeign->addColumn('id', 'integer');
545

Sergei Morozov's avatar
Sergei Morozov committed
546
        $table1 = new Table('foo');
547
        $table1->addColumn('fk', 'integer');
548

Sergei Morozov's avatar
Sergei Morozov committed
549
        $table2 = new Table('foo');
550
        $table2->addColumn('fk', 'integer');
Sergei Morozov's avatar
Sergei Morozov committed
551
        $table2->addForeignKeyConstraint($tableForeign, ['fk'], ['id']);
552

Sergei Morozov's avatar
Sergei Morozov committed
553
        $c         = new Comparator();
554 555
        $tableDiff = $c->diffTable($table2, $table1);

Sergei Morozov's avatar
Sergei Morozov committed
556
        self::assertInstanceOf(TableDiff::class, $tableDiff);
Gabriel Caruso's avatar
Gabriel Caruso committed
557
        self::assertCount(1, $tableDiff->removedForeignKeys);
558 559
    }

560
    public function testTableUpdateForeignKey() : void
561
    {
Sergei Morozov's avatar
Sergei Morozov committed
562
        $tableForeign = new Table('bar');
563
        $tableForeign->addColumn('id', 'integer');
564

Sergei Morozov's avatar
Sergei Morozov committed
565
        $table1 = new Table('foo');
566
        $table1->addColumn('fk', 'integer');
Sergei Morozov's avatar
Sergei Morozov committed
567
        $table1->addForeignKeyConstraint($tableForeign, ['fk'], ['id']);
568

Sergei Morozov's avatar
Sergei Morozov committed
569
        $table2 = new Table('foo');
570
        $table2->addColumn('fk', 'integer');
Sergei Morozov's avatar
Sergei Morozov committed
571
        $table2->addForeignKeyConstraint($tableForeign, ['fk'], ['id'], ['onUpdate' => 'CASCADE']);
572

Sergei Morozov's avatar
Sergei Morozov committed
573
        $c         = new Comparator();
574 575
        $tableDiff = $c->diffTable($table1, $table2);

Sergei Morozov's avatar
Sergei Morozov committed
576
        self::assertInstanceOf(TableDiff::class, $tableDiff);
Gabriel Caruso's avatar
Gabriel Caruso committed
577
        self::assertCount(1, $tableDiff->changedForeignKeys);
578 579
    }

580
    public function testMovedForeignKeyForeignTable() : void
581
    {
Sergei Morozov's avatar
Sergei Morozov committed
582
        $tableForeign = new Table('bar');
583 584
        $tableForeign->addColumn('id', 'integer');

Sergei Morozov's avatar
Sergei Morozov committed
585
        $tableForeign2 = new Table('bar2');
586 587
        $tableForeign2->addColumn('id', 'integer');

Sergei Morozov's avatar
Sergei Morozov committed
588
        $table1 = new Table('foo');
589
        $table1->addColumn('fk', 'integer');
Sergei Morozov's avatar
Sergei Morozov committed
590
        $table1->addForeignKeyConstraint($tableForeign, ['fk'], ['id']);
591

Sergei Morozov's avatar
Sergei Morozov committed
592
        $table2 = new Table('foo');
593
        $table2->addColumn('fk', 'integer');
Sergei Morozov's avatar
Sergei Morozov committed
594
        $table2->addForeignKeyConstraint($tableForeign2, ['fk'], ['id']);
595

Sergei Morozov's avatar
Sergei Morozov committed
596
        $c         = new Comparator();
597 598
        $tableDiff = $c->diffTable($table1, $table2);

Sergei Morozov's avatar
Sergei Morozov committed
599
        self::assertInstanceOf(TableDiff::class, $tableDiff);
Gabriel Caruso's avatar
Gabriel Caruso committed
600
        self::assertCount(1, $tableDiff->changedForeignKeys);
601
    }
602

603
    public function testTablesCaseInsensitive() : void
604 605 606 607 608 609 610 611 612 613 614 615 616
    {
        $schemaA = new Schema();
        $schemaA->createTable('foo');
        $schemaA->createTable('bAr');
        $schemaA->createTable('BAZ');
        $schemaA->createTable('new');

        $schemaB = new Schema();
        $schemaB->createTable('FOO');
        $schemaB->createTable('bar');
        $schemaB->createTable('Baz');
        $schemaB->createTable('old');

Sergei Morozov's avatar
Sergei Morozov committed
617
        $c    = new Comparator();
618 619
        $diff = $c->compare($schemaA, $schemaB);

620
        $this->assertSchemaTableChangeCount($diff, 1, 0, 1);
621 622
    }

623
    public function testSequencesCaseInsensitive() : void
624
    {
625 626 627 628 629 630 631 632 633 634 635
        $schemaA = new Schema();
        $schemaA->createSequence('foo');
        $schemaA->createSequence('BAR');
        $schemaA->createSequence('Baz');
        $schemaA->createSequence('new');

        $schemaB = new Schema();
        $schemaB->createSequence('FOO');
        $schemaB->createSequence('Bar');
        $schemaB->createSequence('baz');
        $schemaB->createSequence('old');
636

Sergei Morozov's avatar
Sergei Morozov committed
637
        $c    = new Comparator();
638 639
        $diff = $c->compare($schemaA, $schemaB);

640
        $this->assertSchemaSequenceChangeCount($diff, 1, 0, 1);
641 642
    }

643
    public function testCompareColumnCompareCaseInsensitive() : void
644
    {
Sergei Morozov's avatar
Sergei Morozov committed
645
        $tableA = new Table('foo');
646
        $tableA->addColumn('id', 'integer');
647

Sergei Morozov's avatar
Sergei Morozov committed
648
        $tableB = new Table('foo');
649
        $tableB->addColumn('ID', 'integer');
650

Sergei Morozov's avatar
Sergei Morozov committed
651
        $c         = new Comparator();
652 653
        $tableDiff = $c->diffTable($tableA, $tableB);

654
        self::assertFalse($tableDiff);
655 656
    }

657
    public function testCompareIndexBasedOnPropertiesNotName() : void
658
    {
Sergei Morozov's avatar
Sergei Morozov committed
659
        $tableA = new Table('foo');
660
        $tableA->addColumn('id', 'integer');
Sergei Morozov's avatar
Sergei Morozov committed
661
        $tableA->addIndex(['id'], 'foo_bar_idx');
662

Sergei Morozov's avatar
Sergei Morozov committed
663
        $tableB = new Table('foo');
664
        $tableB->addColumn('ID', 'integer');
Sergei Morozov's avatar
Sergei Morozov committed
665
        $tableB->addIndex(['id'], 'bar_foo_idx');
666

Sergei Morozov's avatar
Sergei Morozov committed
667 668 669 670
        $c                                        = new Comparator();
        $tableDiff                                = new TableDiff('foo');
        $tableDiff->fromTable                     = $tableA;
        $tableDiff->renamedIndexes['foo_bar_idx'] = new Index('bar_foo_idx', ['id']);
671

672
        self::assertEquals(
673 674 675
            $tableDiff,
            $c->diffTable($tableA, $tableB)
        );
676 677
    }

678
    public function testCompareForeignKeyBasedOnPropertiesNotName() : void
679
    {
Sergei Morozov's avatar
Sergei Morozov committed
680
        $tableA = new Table('foo');
681
        $tableA->addColumn('id', 'integer');
Sergei Morozov's avatar
Sergei Morozov committed
682
        $tableA->addNamedForeignKeyConstraint('foo_constraint', 'bar', ['id'], ['id']);
683

Sergei Morozov's avatar
Sergei Morozov committed
684
        $tableB = new Table('foo');
685
        $tableB->addColumn('ID', 'integer');
Sergei Morozov's avatar
Sergei Morozov committed
686
        $tableB->addNamedForeignKeyConstraint('bar_constraint', 'bar', ['id'], ['id']);
687

Sergei Morozov's avatar
Sergei Morozov committed
688
        $c         = new Comparator();
689 690
        $tableDiff = $c->diffTable($tableA, $tableB);

691
        self::assertFalse($tableDiff);
692 693
    }

694
    public function testCompareForeignKeyRestrictNoActionAreTheSame() : void
695
    {
Sergei Morozov's avatar
Sergei Morozov committed
696 697
        $fk1 = new ForeignKeyConstraint(['foo'], 'bar', ['baz'], 'fk1', ['onDelete' => 'NO ACTION']);
        $fk2 = new ForeignKeyConstraint(['foo'], 'bar', ['baz'], 'fk1', ['onDelete' => 'RESTRICT']);
698 699

        $c = new Comparator();
700
        self::assertFalse($c->diffForeignKey($fk1, $fk2));
701 702
    }

703 704 705
    /**
     * @group DBAL-492
     */
706
    public function testCompareForeignKeyNamesUnqualifiedAsNoSchemaInformationIsAvailable() : void
707
    {
Sergei Morozov's avatar
Sergei Morozov committed
708 709
        $fk1 = new ForeignKeyConstraint(['foo'], 'foo.bar', ['baz'], 'fk1');
        $fk2 = new ForeignKeyConstraint(['foo'], 'baz.bar', ['baz'], 'fk1');
710 711

        $c = new Comparator();
712
        self::assertFalse($c->diffForeignKey($fk1, $fk2));
713 714
    }

715
    public function testDetectRenameColumn() : void
716
    {
Sergei Morozov's avatar
Sergei Morozov committed
717
        $tableA = new Table('foo');
718
        $tableA->addColumn('foo', 'integer');
719

Sergei Morozov's avatar
Sergei Morozov committed
720
        $tableB = new Table('foo');
721
        $tableB->addColumn('bar', 'integer');
722

Sergei Morozov's avatar
Sergei Morozov committed
723
        $c         = new Comparator();
724 725
        $tableDiff = $c->diffTable($tableA, $tableB);

Gabriel Caruso's avatar
Gabriel Caruso committed
726 727
        self::assertCount(0, $tableDiff->addedColumns);
        self::assertCount(0, $tableDiff->removedColumns);
728 729
        self::assertArrayHasKey('foo', $tableDiff->renamedColumns);
        self::assertEquals('bar', $tableDiff->renamedColumns['foo']->getName());
730 731
    }

732
    /**
Possum's avatar
Possum committed
733
     * You can easily have ambiguities in the column renaming. If these
734 735 736 737 738
     * are detected no renaming should take place, instead adding and dropping
     * should be used exclusively.
     *
     * @group DBAL-24
     */
739
    public function testDetectRenameColumnAmbiguous() : void
740
    {
Sergei Morozov's avatar
Sergei Morozov committed
741
        $tableA = new Table('foo');
742 743 744
        $tableA->addColumn('foo', 'integer');
        $tableA->addColumn('bar', 'integer');

Sergei Morozov's avatar
Sergei Morozov committed
745
        $tableB = new Table('foo');
746
        $tableB->addColumn('baz', 'integer');
747

Sergei Morozov's avatar
Sergei Morozov committed
748
        $c         = new Comparator();
749 750
        $tableDiff = $c->diffTable($tableA, $tableB);

Gabriel Caruso's avatar
Gabriel Caruso committed
751
        self::assertCount(1, $tableDiff->addedColumns, "'baz' should be added, not created through renaming!");
752
        self::assertArrayHasKey('baz', $tableDiff->addedColumns, "'baz' should be added, not created through renaming!");
Gabriel Caruso's avatar
Gabriel Caruso committed
753
        self::assertCount(2, $tableDiff->removedColumns, "'foo' and 'bar' should both be dropped, an ambiguity exists which one could be renamed to 'baz'.");
754 755
        self::assertArrayHasKey('foo', $tableDiff->removedColumns, "'foo' should be removed.");
        self::assertArrayHasKey('bar', $tableDiff->removedColumns, "'bar' should be removed.");
Sergei Morozov's avatar
Sergei Morozov committed
756
        self::assertCount(0, $tableDiff->renamedColumns, 'no renamings should take place.');
757 758
    }

759 760 761
    /**
     * @group DBAL-1063
     */
762
    public function testDetectRenameIndex() : void
763 764 765 766 767 768
    {
        $table1 = new Table('foo');
        $table1->addColumn('foo', 'integer');

        $table2 = clone $table1;

Sergei Morozov's avatar
Sergei Morozov committed
769
        $table1->addIndex(['foo'], 'idx_foo');
770

Sergei Morozov's avatar
Sergei Morozov committed
771
        $table2->addIndex(['foo'], 'idx_bar');
772 773

        $comparator = new Comparator();
Sergei Morozov's avatar
Sergei Morozov committed
774
        $tableDiff  = $comparator->diffTable($table1, $table2);
775

776 777 778 779
        self::assertCount(0, $tableDiff->addedIndexes);
        self::assertCount(0, $tableDiff->removedIndexes);
        self::assertArrayHasKey('idx_foo', $tableDiff->renamedIndexes);
        self::assertEquals('idx_bar', $tableDiff->renamedIndexes['idx_foo']->getName());
780 781 782 783 784 785 786 787 788
    }

    /**
     * You can easily have ambiguities in the index renaming. If these
     * are detected no renaming should take place, instead adding and dropping
     * should be used exclusively.
     *
     * @group DBAL-1063
     */
789
    public function testDetectRenameIndexAmbiguous() : void
790 791 792 793 794 795
    {
        $table1 = new Table('foo');
        $table1->addColumn('foo', 'integer');

        $table2 = clone $table1;

Sergei Morozov's avatar
Sergei Morozov committed
796 797
        $table1->addIndex(['foo'], 'idx_foo');
        $table1->addIndex(['foo'], 'idx_bar');
798

Sergei Morozov's avatar
Sergei Morozov committed
799
        $table2->addIndex(['foo'], 'idx_baz');
800 801

        $comparator = new Comparator();
Sergei Morozov's avatar
Sergei Morozov committed
802
        $tableDiff  = $comparator->diffTable($table1, $table2);
803

804 805 806 807 808 809
        self::assertCount(1, $tableDiff->addedIndexes);
        self::assertArrayHasKey('idx_baz', $tableDiff->addedIndexes);
        self::assertCount(2, $tableDiff->removedIndexes);
        self::assertArrayHasKey('idx_foo', $tableDiff->removedIndexes);
        self::assertArrayHasKey('idx_bar', $tableDiff->removedIndexes);
        self::assertCount(0, $tableDiff->renamedIndexes);
810 811
    }

812
    public function testDetectChangeIdentifierType() : void
beberlei's avatar
beberlei committed
813
    {
814 815
        $this->markTestSkipped('DBAL-2 was reopened, this test cannot work anymore.');

Sergei Morozov's avatar
Sergei Morozov committed
816 817
        $tableA = new Table('foo');
        $tableA->addColumn('id', 'integer', ['autoincrement' => false]);
beberlei's avatar
beberlei committed
818

Sergei Morozov's avatar
Sergei Morozov committed
819 820
        $tableB = new Table('foo');
        $tableB->addColumn('id', 'integer', ['autoincrement' => true]);
beberlei's avatar
beberlei committed
821

Sergei Morozov's avatar
Sergei Morozov committed
822
        $c         = new Comparator();
beberlei's avatar
beberlei committed
823 824
        $tableDiff = $c->diffTable($tableA, $tableB);

Sergei Morozov's avatar
Sergei Morozov committed
825
        self::assertInstanceOf(TableDiff::class, $tableDiff);
826
        self::assertArrayHasKey('id', $tableDiff->changedColumns);
beberlei's avatar
beberlei committed
827 828
    }

829 830 831
    /**
     * @group DBAL-105
     */
832
    public function testDiff() : void
833
    {
Sergei Morozov's avatar
Sergei Morozov committed
834 835
        $table = new Table('twitter_users');
        $table->addColumn('id', 'integer', ['autoincrement' => true]);
836 837
        $table->addColumn('twitterId', 'integer');
        $table->addColumn('displayName', 'string');
Sergei Morozov's avatar
Sergei Morozov committed
838
        $table->setPrimaryKey(['id']);
839

Sergei Morozov's avatar
Sergei Morozov committed
840 841
        $newtable = new Table('twitter_users');
        $newtable->addColumn('id', 'integer', ['autoincrement' => true]);
842 843 844
        $newtable->addColumn('twitter_id', 'integer');
        $newtable->addColumn('display_name', 'string');
        $newtable->addColumn('logged_in_at', 'datetime');
Sergei Morozov's avatar
Sergei Morozov committed
845
        $newtable->setPrimaryKey(['id']);
846

Sergei Morozov's avatar
Sergei Morozov committed
847
        $c         = new Comparator();
848 849
        $tableDiff = $c->diffTable($table, $newtable);

Sergei Morozov's avatar
Sergei Morozov committed
850
        self::assertInstanceOf(TableDiff::class, $tableDiff);
Sergei Morozov's avatar
Sergei Morozov committed
851 852
        self::assertEquals(['twitterid', 'displayname'], array_keys($tableDiff->renamedColumns));
        self::assertEquals(['logged_in_at'], array_keys($tableDiff->addedColumns));
Gabriel Caruso's avatar
Gabriel Caruso committed
853
        self::assertCount(0, $tableDiff->removedColumns);
854
    }
855

856 857 858
    /**
     * @group DBAL-112
     */
859
    public function testChangedSequence() : void
860
    {
Sergei Morozov's avatar
Sergei Morozov committed
861
        $schema   = new Schema();
862
        $sequence = $schema->createSequence('baz');
863

864 865
        $schemaNew = clone $schema;
        $schemaNew->getSequence('baz')->setAllocationSize(20);
866

Sergei Morozov's avatar
Sergei Morozov committed
867
        $c    = new Comparator();
868
        $diff = $c->compare($schema, $schemaNew);
869

Sergei Morozov's avatar
Sergei Morozov committed
870
        self::assertSame($diff->changedSequences[0], $schemaNew->getSequence('baz'));
871
    }
872

873 874 875
    /**
     * @group DBAL-106
     */
876
    public function testDiffDecimalWithNullPrecision() : void
877 878 879 880 881 882 883
    {
        $column = new Column('foo', Type::getType('decimal'));
        $column->setPrecision(null);

        $column2 = new Column('foo', Type::getType('decimal'));

        $c = new Comparator();
Sergei Morozov's avatar
Sergei Morozov committed
884
        self::assertEquals([], $c->diffColumn($column, $column2));
885 886
    }

887 888 889
    /**
     * @group DBAL-204
     */
890
    public function testFqnSchemaComparison() : void
891 892
    {
        $config = new SchemaConfig();
Sergei Morozov's avatar
Sergei Morozov committed
893
        $config->setName('foo');
894

Sergei Morozov's avatar
Sergei Morozov committed
895
        $oldSchema = new Schema([], [], $config);
896 897
        $oldSchema->createTable('bar');

Sergei Morozov's avatar
Sergei Morozov committed
898
        $newSchema = new Schema([], [], $config);
899 900
        $newSchema->createTable('foo.bar');

Sergei Morozov's avatar
Sergei Morozov committed
901
        $expected             = new SchemaDiff();
902 903
        $expected->fromSchema = $oldSchema;

904
        self::assertEquals($expected, Comparator::compareSchemas($oldSchema, $newSchema));
905 906
    }

Marco Pivetta's avatar
Marco Pivetta committed
907 908 909
    /**
     * @group DBAL-669
     */
910
    public function testNamespacesComparison() : void
Marco Pivetta's avatar
Marco Pivetta committed
911 912
    {
        $config = new SchemaConfig();
Sergei Morozov's avatar
Sergei Morozov committed
913
        $config->setName('schemaName');
Marco Pivetta's avatar
Marco Pivetta committed
914

Sergei Morozov's avatar
Sergei Morozov committed
915
        $oldSchema = new Schema([], [], $config);
Marco Pivetta's avatar
Marco Pivetta committed
916 917 918
        $oldSchema->createTable('taz');
        $oldSchema->createTable('war.tab');

Sergei Morozov's avatar
Sergei Morozov committed
919
        $newSchema = new Schema([], [], $config);
Marco Pivetta's avatar
Marco Pivetta committed
920 921 922 923
        $newSchema->createTable('bar.tab');
        $newSchema->createTable('baz.tab');
        $newSchema->createTable('war.tab');

Sergei Morozov's avatar
Sergei Morozov committed
924 925 926
        $expected                = new SchemaDiff();
        $expected->fromSchema    = $oldSchema;
        $expected->newNamespaces = ['bar' => 'bar', 'baz' => 'baz'];
Marco Pivetta's avatar
Marco Pivetta committed
927 928 929

        $diff = Comparator::compareSchemas($oldSchema, $newSchema);

Sergei Morozov's avatar
Sergei Morozov committed
930
        self::assertEquals(['bar' => 'bar', 'baz' => 'baz'], $diff->newNamespaces);
931
        self::assertCount(2, $diff->newTables);
Marco Pivetta's avatar
Marco Pivetta committed
932 933
    }

934 935 936
    /**
     * @group DBAL-204
     */
937
    public function testFqnSchemaComparisonDifferentSchemaNameButSameTableNoDiff() : void
938 939
    {
        $config = new SchemaConfig();
Sergei Morozov's avatar
Sergei Morozov committed
940
        $config->setName('foo');
941

Sergei Morozov's avatar
Sergei Morozov committed
942
        $oldSchema = new Schema([], [], $config);
943
        $oldSchema->createTable('foo.bar');
944 945

        $newSchema = new Schema();
946
        $newSchema->createTable('bar');
947

Sergei Morozov's avatar
Sergei Morozov committed
948
        $expected             = new SchemaDiff();
949 950
        $expected->fromSchema = $oldSchema;

951
        self::assertEquals($expected, Comparator::compareSchemas($oldSchema, $newSchema));
952 953 954 955 956
    }

    /**
     * @group DBAL-204
     */
957
    public function testFqnSchemaComparisonNoSchemaSame() : void
958 959
    {
        $config = new SchemaConfig();
Sergei Morozov's avatar
Sergei Morozov committed
960 961
        $config->setName('foo');
        $oldSchema = new Schema([], [], $config);
962 963 964 965 966
        $oldSchema->createTable('bar');

        $newSchema = new Schema();
        $newSchema->createTable('bar');

Sergei Morozov's avatar
Sergei Morozov committed
967
        $expected             = new SchemaDiff();
968
        $expected->fromSchema = $oldSchema;
969

970
        self::assertEquals($expected, Comparator::compareSchemas($oldSchema, $newSchema));
971 972
    }

973 974 975
    /**
     * @group DDC-1657
     */
976
    public function testAutoIncrementSequences() : void
977 978
    {
        $oldSchema = new Schema();
Sergei Morozov's avatar
Sergei Morozov committed
979 980 981 982
        $table     = $oldSchema->createTable('foo');
        $table->addColumn('id', 'integer', ['autoincrement' => true]);
        $table->setPrimaryKey(['id']);
        $oldSchema->createSequence('foo_id_seq');
983 984

        $newSchema = new Schema();
Sergei Morozov's avatar
Sergei Morozov committed
985 986 987
        $table     = $newSchema->createTable('foo');
        $table->addColumn('id', 'integer', ['autoincrement' => true]);
        $table->setPrimaryKey(['id']);
988

Sergei Morozov's avatar
Sergei Morozov committed
989
        $c    = new Comparator();
990 991
        $diff = $c->compare($oldSchema, $newSchema);

992
        self::assertCount(0, $diff->removedSequences);
993 994
    }

995 996
    /**
     * Check that added autoincrement sequence is not populated in newSequences
Sergei Morozov's avatar
Sergei Morozov committed
997
     *
998 999
     * @group DBAL-562
     */
1000
    public function testAutoIncrementNoSequences() : void
1001 1002
    {
        $oldSchema = new Schema();
Sergei Morozov's avatar
Sergei Morozov committed
1003 1004 1005
        $table     = $oldSchema->createTable('foo');
        $table->addColumn('id', 'integer', ['autoincrement' => true]);
        $table->setPrimaryKey(['id']);
1006 1007

        $newSchema = new Schema();
Sergei Morozov's avatar
Sergei Morozov committed
1008 1009 1010 1011
        $table     = $newSchema->createTable('foo');
        $table->addColumn('id', 'integer', ['autoincrement' => true]);
        $table->setPrimaryKey(['id']);
        $newSchema->createSequence('foo_id_seq');
1012

Sergei Morozov's avatar
Sergei Morozov committed
1013
        $c    = new Comparator();
1014 1015
        $diff = $c->compare($oldSchema, $newSchema);

1016
        self::assertCount(0, $diff->newSequences);
1017
    }
1018

1019
    /**
1020 1021 1022 1023
     * You can get multiple drops for a FK when a table referenced by a foreign
     * key is deleted, as this FK is referenced twice, once on the orphanedForeignKeys
     * array because of the dropped table, and once on changedTables array. We
     * now check that the key is present once.
1024
     */
1025
    public function testAvoidMultipleDropForeignKey() : void
1026 1027 1028
    {
        $oldSchema = new Schema();

1029 1030 1031 1032 1033
        $tableA = $oldSchema->createTable('table_a');
        $tableA->addColumn('id', 'integer');

        $tableB = $oldSchema->createTable('table_b');
        $tableB->addColumn('id', 'integer');
1034

1035 1036 1037 1038
        $tableC = $oldSchema->createTable('table_c');
        $tableC->addColumn('id', 'integer');
        $tableC->addColumn('table_a_id', 'integer');
        $tableC->addColumn('table_b_id', 'integer');
1039

Sergei Morozov's avatar
Sergei Morozov committed
1040 1041
        $tableC->addForeignKeyConstraint($tableA, ['table_a_id'], ['id']);
        $tableC->addForeignKeyConstraint($tableB, ['table_b_id'], ['id']);
1042 1043 1044

        $newSchema = new Schema();

1045 1046 1047 1048 1049 1050 1051 1052
        $tableB = $newSchema->createTable('table_b');
        $tableB->addColumn('id', 'integer');

        $tableC = $newSchema->createTable('table_c');
        $tableC->addColumn('id', 'integer');

        $comparator = new Comparator();
        $schemaDiff = $comparator->compare($oldSchema, $newSchema);
1053

1054 1055
        self::assertCount(1, $schemaDiff->changedTables['table_c']->removedForeignKeys);
        self::assertCount(1, $schemaDiff->orphanedForeignKeys);
1056 1057
    }

1058
    public function testCompareChangedColumn() : void
1059 1060 1061 1062 1063 1064 1065
    {
        $oldSchema = new Schema();

        $tableFoo = $oldSchema->createTable('foo');
        $tableFoo->addColumn('id', 'integer');

        $newSchema = new Schema();
Sergei Morozov's avatar
Sergei Morozov committed
1066
        $table     = $newSchema->createTable('foo');
1067 1068
        $table->addColumn('id', 'string');

Sergei Morozov's avatar
Sergei Morozov committed
1069 1070 1071 1072 1073 1074 1075
        $expected                      = new SchemaDiff();
        $expected->fromSchema          = $oldSchema;
        $tableDiff                     = $expected->changedTables['foo'] = new TableDiff('foo');
        $tableDiff->fromTable          = $tableFoo;
        $columnDiff                    = $tableDiff->changedColumns['id'] = new ColumnDiff('id', $table->getColumn('id'));
        $columnDiff->fromColumn        = $tableFoo->getColumn('id');
        $columnDiff->changedProperties = ['type'];
1076

1077
        self::assertEquals($expected, Comparator::compareSchemas($oldSchema, $newSchema));
1078
    }
1079

1080
    public function testCompareChangedBinaryColumn() : void
Steve Müller's avatar
Steve Müller committed
1081 1082 1083 1084 1085 1086 1087
    {
        $oldSchema = new Schema();

        $tableFoo = $oldSchema->createTable('foo');
        $tableFoo->addColumn('id', 'binary');

        $newSchema = new Schema();
Sergei Morozov's avatar
Sergei Morozov committed
1088 1089
        $table     = $newSchema->createTable('foo');
        $table->addColumn('id', 'binary', ['length' => 42, 'fixed' => true]);
Steve Müller's avatar
Steve Müller committed
1090

Sergei Morozov's avatar
Sergei Morozov committed
1091 1092 1093 1094 1095 1096 1097
        $expected                      = new SchemaDiff();
        $expected->fromSchema          = $oldSchema;
        $tableDiff                     = $expected->changedTables['foo'] = new TableDiff('foo');
        $tableDiff->fromTable          = $tableFoo;
        $columnDiff                    = $tableDiff->changedColumns['id'] = new ColumnDiff('id', $table->getColumn('id'));
        $columnDiff->fromColumn        = $tableFoo->getColumn('id');
        $columnDiff->changedProperties = ['length', 'fixed'];
Steve Müller's avatar
Steve Müller committed
1098

1099
        self::assertEquals($expected, Comparator::compareSchemas($oldSchema, $newSchema));
Steve Müller's avatar
Steve Müller committed
1100 1101
    }

1102 1103 1104
    /**
     * @group DBAL-617
     */
1105
    public function testCompareQuotedAndUnquotedForeignKeyColumns() : void
1106
    {
Sergei Morozov's avatar
Sergei Morozov committed
1107 1108
        $fk1 = new ForeignKeyConstraint(['foo'], 'bar', ['baz'], 'fk1', ['onDelete' => 'NO ACTION']);
        $fk2 = new ForeignKeyConstraint(['`foo`'], 'bar', ['`baz`'], 'fk1', ['onDelete' => 'NO ACTION']);
1109 1110

        $comparator = new Comparator();
Sergei Morozov's avatar
Sergei Morozov committed
1111
        $diff       = $comparator->diffForeignKey($fk1, $fk2);
1112

1113
        self::assertFalse($diff);
1114 1115
    }

1116
    public function assertSchemaTableChangeCount(SchemaDiff $diff, int $newTableCount = 0, int $changeTableCount = 0, int $removeTableCount = 0) : void
1117
    {
Gabriel Caruso's avatar
Gabriel Caruso committed
1118 1119 1120
        self::assertCount($newTableCount, $diff->newTables);
        self::assertCount($changeTableCount, $diff->changedTables);
        self::assertCount($removeTableCount, $diff->removedTables);
1121
    }
1122

1123 1124 1125 1126 1127 1128
    public function assertSchemaSequenceChangeCount(
        SchemaDiff $diff,
        int $newSequenceCount = 0,
        int $changeSequenceCount = 0,
        int $removeSequenceCount = 0
    ) : void {
Sergei Morozov's avatar
Sergei Morozov committed
1129 1130 1131
        self::assertCount($newSequenceCount, $diff->newSequences, 'Expected number of new sequences is wrong.');
        self::assertCount($changeSequenceCount, $diff->changedSequences, 'Expected number of changed sequences is wrong.');
        self::assertCount($removeSequenceCount, $diff->removedSequences, 'Expected number of removed sequences is wrong.');
1132
    }
1133

1134
    public function testDiffColumnPlatformOptions() : void
1135
    {
Sergei Morozov's avatar
Sergei Morozov committed
1136 1137 1138
        $column1 = new Column('foo', Type::getType('string'), ['platformOptions' => ['foo' => 'foo', 'bar' => 'bar']]);
        $column2 = new Column('foo', Type::getType('string'), ['platformOptions' => ['foo' => 'foo', 'foobar' => 'foobar']]);
        $column3 = new Column('foo', Type::getType('string'), ['platformOptions' => ['foo' => 'foo', 'bar' => 'rab']]);
1139 1140 1141 1142
        $column4 = new Column('foo', Type::getType('string'));

        $comparator = new Comparator();

Sergei Morozov's avatar
Sergei Morozov committed
1143 1144 1145 1146 1147 1148
        self::assertEquals([], $comparator->diffColumn($column1, $column2));
        self::assertEquals([], $comparator->diffColumn($column2, $column1));
        self::assertEquals(['bar'], $comparator->diffColumn($column1, $column3));
        self::assertEquals(['bar'], $comparator->diffColumn($column3, $column1));
        self::assertEquals([], $comparator->diffColumn($column1, $column4));
        self::assertEquals([], $comparator->diffColumn($column4, $column1));
1149
    }
1150

1151
    public function testComplexDiffColumn() : void
1152
    {
Sergei Morozov's avatar
Sergei Morozov committed
1153 1154 1155 1156
        $column1 = new Column('foo', Type::getType('string'), [
            'platformOptions' => ['foo' => 'foo'],
            'customSchemaOptions' => ['foo' => 'bar'],
        ]);
1157

Sergei Morozov's avatar
Sergei Morozov committed
1158 1159 1160
        $column2 = new Column('foo', Type::getType('string'), [
            'platformOptions' => ['foo' => 'bar'],
        ]);
1161 1162 1163

        $comparator = new Comparator();

Sergei Morozov's avatar
Sergei Morozov committed
1164 1165
        self::assertEquals([], $comparator->diffColumn($column1, $column2));
        self::assertEquals([], $comparator->diffColumn($column2, $column1));
1166
    }
1167 1168 1169 1170

    /**
     * @group DBAL-669
     */
1171
    public function testComparesNamespaces() : void
1172 1173
    {
        $comparator = new Comparator();
Sergei Morozov's avatar
Sergei Morozov committed
1174
        $fromSchema = $this->getMockBuilder(Schema::class)
1175
            ->onlyMethods(['getNamespaces', 'hasNamespace'])
1176
            ->getMock();
Sergei Morozov's avatar
Sergei Morozov committed
1177
        $toSchema   = $this->getMockBuilder(Schema::class)
1178
            ->onlyMethods(['getNamespaces', 'hasNamespace'])
1179
            ->getMock();
1180 1181 1182

        $fromSchema->expects($this->once())
            ->method('getNamespaces')
Sergei Morozov's avatar
Sergei Morozov committed
1183
            ->will($this->returnValue(['foo', 'bar']));
1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196

        $fromSchema->expects($this->at(0))
            ->method('hasNamespace')
            ->with('bar')
            ->will($this->returnValue(true));

        $fromSchema->expects($this->at(1))
            ->method('hasNamespace')
            ->with('baz')
            ->will($this->returnValue(false));

        $toSchema->expects($this->once())
            ->method('getNamespaces')
Sergei Morozov's avatar
Sergei Morozov committed
1197
            ->will($this->returnValue(['bar', 'baz']));
1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208

        $toSchema->expects($this->at(1))
            ->method('hasNamespace')
            ->with('foo')
            ->will($this->returnValue(false));

        $toSchema->expects($this->at(2))
            ->method('hasNamespace')
            ->with('bar')
            ->will($this->returnValue(true));

Sergei Morozov's avatar
Sergei Morozov committed
1209 1210 1211 1212
        $expected                    = new SchemaDiff();
        $expected->fromSchema        = $fromSchema;
        $expected->newNamespaces     = ['baz' => 'baz'];
        $expected->removedNamespaces = ['foo' => 'foo'];
1213

1214
        self::assertEquals($expected, $comparator->compare($fromSchema, $toSchema));
1215
    }
1216

1217
    public function testCompareGuidColumns() : void
1218 1219 1220
    {
        $comparator = new Comparator();

Sergei Morozov's avatar
Sergei Morozov committed
1221
        $column1 = new Column('foo', Type::getType('guid'), ['comment' => 'GUID 1']);
1222 1223 1224
        $column2 = new Column(
            'foo',
            Type::getType('guid'),
Sergei Morozov's avatar
Sergei Morozov committed
1225
            ['notnull' => false, 'length' => '36', 'fixed' => true, 'default' => 'NEWID()', 'comment' => 'GUID 2.']
1226 1227
        );

Sergei Morozov's avatar
Sergei Morozov committed
1228 1229
        self::assertEquals(['notnull', 'default', 'comment'], $comparator->diffColumn($column1, $column2));
        self::assertEquals(['notnull', 'default', 'comment'], $comparator->diffColumn($column2, $column1));
1230
    }
1231 1232 1233 1234 1235

    /**
     * @group DBAL-1009
     * @dataProvider getCompareColumnComments
     */
1236
    public function testCompareColumnComments(?string $comment1, ?string $comment2, bool $equals) : void
1237
    {
Sergei Morozov's avatar
Sergei Morozov committed
1238 1239
        $column1 = new Column('foo', Type::getType('integer'), ['comment' => $comment1]);
        $column2 = new Column('foo', Type::getType('integer'), ['comment' => $comment2]);
1240 1241 1242

        $comparator = new Comparator();

Sergei Morozov's avatar
Sergei Morozov committed
1243
        $expectedDiff = $equals ? [] : ['comment'];
1244 1245 1246

        $actualDiff = $comparator->diffColumn($column1, $column2);

1247
        self::assertSame($expectedDiff, $actualDiff);
1248 1249 1250

        $actualDiff = $comparator->diffColumn($column2, $column1);

1251
        self::assertSame($expectedDiff, $actualDiff);
1252 1253
    }

1254 1255 1256 1257
    /**
     * @return mixed[][]
     */
    public static function getCompareColumnComments() : iterable
1258
    {
Sergei Morozov's avatar
Sergei Morozov committed
1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279
        return [
            [null, null, true],
            ['', '', true],
            [' ', ' ', true],
            ['0', '0', true],
            ['foo', 'foo', true],

            [null, '', true],
            [null, ' ', false],
            [null, '0', false],
            [null, 'foo', false],

            ['', ' ', false],
            ['', '0', false],
            ['', 'foo', false],

            [' ', '0', false],
            [' ', 'foo', false],

            ['0', 'foo', false],
        ];
1280
    }
1281

1282
    public function testForeignKeyRemovalWithRenamedLocalColumn() : void
1283
    {
Sergei Morozov's avatar
Sergei Morozov committed
1284 1285 1286 1287
        $fromSchema = new Schema([
            'table1' => new Table(
                'table1',
                [
1288
                    'id' => new Column('id', Type::getType('integer')),
Sergei Morozov's avatar
Sergei Morozov committed
1289 1290 1291 1292 1293
                ]
            ),
            'table2' => new Table(
                'table2',
                [
1294
                    'id' => new Column('id', Type::getType('integer')),
Sergei Morozov's avatar
Sergei Morozov committed
1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306
                    'id_table1' => new Column('id_table1', Type::getType('integer')),
                ],
                [],
                [
                    new ForeignKeyConstraint(['id_table1'], 'table1', ['id'], 'fk_table2_table1'),
                ]
            ),
        ]);
        $toSchema   = new Schema([
            'table2' => new Table(
                'table2',
                [
1307
                    'id' => new Column('id', Type::getType('integer')),
Sergei Morozov's avatar
Sergei Morozov committed
1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323
                    'id_table3' => new Column('id_table3', Type::getType('integer')),
                ],
                [],
                [
                    new ForeignKeyConstraint(['id_table3'], 'table3', ['id'], 'fk_table2_table3'),
                ]
            ),
            'table3' => new Table(
                'table3',
                [
                    'id' => new Column('id', Type::getType('integer')),
                ]
            ),
        ]);
        $actual     = Comparator::compareSchemas($fromSchema, $toSchema);
        self::assertArrayHasKey('table2', $actual->changedTables);
1324
        self::assertCount(1, $actual->orphanedForeignKeys);
Sergei Morozov's avatar
Sergei Morozov committed
1325 1326 1327
        self::assertEquals('fk_table2_table1', $actual->orphanedForeignKeys[0]->getName());
        self::assertCount(1, $actual->changedTables['table2']->addedForeignKeys, 'FK to table3 should be added.');
        self::assertEquals('table3', $actual->changedTables['table2']->addedForeignKeys[0]->getForeignTableName());
1328
    }
1329
}