ComparatorTest.php 45.7 KB
Newer Older
1 2
<?php

Michael Moravec's avatar
Michael Moravec committed
3 4
declare(strict_types=1);

5 6
namespace Doctrine\Tests\DBAL\Schema;

jeroendedauw's avatar
jeroendedauw committed
7 8 9 10 11 12 13 14 15 16 17 18
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
19
use PHPUnit\Framework\TestCase;
20
use function array_keys;
21
use function get_class;
22

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

194
        $c = new Comparator();
Sergei Morozov's avatar
Sergei Morozov committed
195 196
        self::assertEquals(['type'], $c->diffColumn($column1, $column2));
        self::assertEquals([], $c->diffColumn($column1, $column1));
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 227 228
    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));
    }

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

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

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

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

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

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

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

Benjamin Morel's avatar
Benjamin Morel committed
257
        self::assertNotNull($tableDiff);
Sergei Morozov's avatar
Sergei Morozov committed
258
        self::assertCount(1, $tableDiff->renamedColumns, 'we should have one rename datefield1 => new_datefield1.');
259 260 261
        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
262 263
        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.');
264
    }
Lee Davis's avatar
Lee Davis committed
265

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

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

Sergei Morozov's avatar
Sergei Morozov committed
317
        self::assertEquals($expected, Comparator::compareSchemas($schema1, $schema2));
318 319
    }

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

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

Sergei Morozov's avatar
Sergei Morozov committed
369
        self::assertEquals($expected, Comparator::compareSchemas($schema1, $schema2));
370 371
    }

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

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

Sergei Morozov's avatar
Sergei Morozov committed
432
        self::assertEquals($expected, Comparator::compareSchemas($schema1, $schema2));
433
    }
434

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

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

Sergei Morozov's avatar
Sergei Morozov committed
480
        self::assertEquals($expected, Comparator::compareSchemas($schema1, $schema2));
481 482
    }

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

        $c = new Comparator();

491 492
        self::assertTrue($c->diffSequence($seq1, $seq2));
        self::assertTrue($c->diffSequence($seq1, $seq3));
493 494
    }

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

        $schema2 = new Schema();

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

604
    public function testTablesCaseInsensitive() : void
605 606 607 608 609 610 611 612 613 614 615 616 617
    {
        $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
618
        $c    = new Comparator();
619 620
        $diff = $c->compare($schemaA, $schemaB);

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

624
    public function testSequencesCaseInsensitive() : void
625
    {
626 627 628 629 630 631 632 633 634 635 636
        $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');
637

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

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

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

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

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

655
        self::assertNull($tableDiff);
656 657
    }

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

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

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

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

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

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

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

692
        self::assertNull($tableDiff);
693 694
    }

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

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

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

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

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

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

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

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

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

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

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

Benjamin Morel's avatar
Benjamin Morel committed
753
        self::assertNotNull($tableDiff);
Gabriel Caruso's avatar
Gabriel Caruso committed
754
        self::assertCount(1, $tableDiff->addedColumns, "'baz' should be added, not created through renaming!");
755
        self::assertArrayHasKey('baz', $tableDiff->addedColumns, "'baz' should be added, not created through renaming!");
Gabriel Caruso's avatar
Gabriel Caruso committed
756
        self::assertCount(2, $tableDiff->removedColumns, "'foo' and 'bar' should both be dropped, an ambiguity exists which one could be renamed to 'baz'.");
757 758
        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
759
        self::assertCount(0, $tableDiff->renamedColumns, 'no renamings should take place.');
760 761
    }

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

        $table2 = clone $table1;

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

Sergei Morozov's avatar
Sergei Morozov committed
774
        $table2->addIndex(['foo'], 'idx_bar');
775 776

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

Benjamin Morel's avatar
Benjamin Morel committed
779
        self::assertNotNull($tableDiff);
780 781 782 783
        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());
784 785 786 787 788 789 790 791 792
    }

    /**
     * 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
     */
793
    public function testDetectRenameIndexAmbiguous() : void
794 795 796 797 798 799
    {
        $table1 = new Table('foo');
        $table1->addColumn('foo', 'integer');

        $table2 = clone $table1;

Sergei Morozov's avatar
Sergei Morozov committed
800 801
        $table1->addIndex(['foo'], 'idx_foo');
        $table1->addIndex(['foo'], 'idx_bar');
802

Sergei Morozov's avatar
Sergei Morozov committed
803
        $table2->addIndex(['foo'], 'idx_baz');
804 805

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

Benjamin Morel's avatar
Benjamin Morel committed
808
        self::assertNotNull($tableDiff);
809 810 811 812 813 814
        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);
815 816
    }

817
    public function testDetectChangeIdentifierType() : void
beberlei's avatar
beberlei committed
818
    {
819 820
        $this->markTestSkipped('DBAL-2 was reopened, this test cannot work anymore.');

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

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

Sergei Morozov's avatar
Sergei Morozov committed
827
        $c         = new Comparator();
beberlei's avatar
beberlei committed
828 829
        $tableDiff = $c->diffTable($tableA, $tableB);

Sergei Morozov's avatar
Sergei Morozov committed
830
        self::assertInstanceOf(TableDiff::class, $tableDiff);
831
        self::assertArrayHasKey('id', $tableDiff->changedColumns);
beberlei's avatar
beberlei committed
832 833
    }

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

Sergei Morozov's avatar
Sergei Morozov committed
845 846
        $newtable = new Table('twitter_users');
        $newtable->addColumn('id', 'integer', ['autoincrement' => true]);
847 848 849
        $newtable->addColumn('twitter_id', 'integer');
        $newtable->addColumn('display_name', 'string');
        $newtable->addColumn('logged_in_at', 'datetime');
Sergei Morozov's avatar
Sergei Morozov committed
850
        $newtable->setPrimaryKey(['id']);
851

Sergei Morozov's avatar
Sergei Morozov committed
852
        $c         = new Comparator();
853 854
        $tableDiff = $c->diffTable($table, $newtable);

Sergei Morozov's avatar
Sergei Morozov committed
855
        self::assertInstanceOf(TableDiff::class, $tableDiff);
Sergei Morozov's avatar
Sergei Morozov committed
856 857
        self::assertEquals(['twitterid', 'displayname'], array_keys($tableDiff->renamedColumns));
        self::assertEquals(['logged_in_at'], array_keys($tableDiff->addedColumns));
Gabriel Caruso's avatar
Gabriel Caruso committed
858
        self::assertCount(0, $tableDiff->removedColumns);
859
    }
860

861 862 863
    /**
     * @group DBAL-112
     */
864
    public function testChangedSequence() : void
865
    {
866 867
        $schema = new Schema();
        $schema->createSequence('baz');
868

869 870
        $schemaNew = clone $schema;
        $schemaNew->getSequence('baz')->setAllocationSize(20);
871

Sergei Morozov's avatar
Sergei Morozov committed
872
        $c    = new Comparator();
873
        $diff = $c->compare($schema, $schemaNew);
874

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

878 879 880
    /**
     * @group DBAL-106
     */
881
    public function testDiffDecimalWithNullPrecision() : void
882 883 884 885 886 887 888
    {
        $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
889
        self::assertEquals([], $c->diffColumn($column, $column2));
890 891
    }

892 893 894
    /**
     * @group DBAL-204
     */
895
    public function testFqnSchemaComparison() : void
896 897
    {
        $config = new SchemaConfig();
Sergei Morozov's avatar
Sergei Morozov committed
898
        $config->setName('foo');
899

Sergei Morozov's avatar
Sergei Morozov committed
900
        $oldSchema = new Schema([], [], $config);
901 902
        $oldSchema->createTable('bar');

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

Sergei Morozov's avatar
Sergei Morozov committed
906
        $expected             = new SchemaDiff();
907 908
        $expected->fromSchema = $oldSchema;

909
        self::assertEquals($expected, Comparator::compareSchemas($oldSchema, $newSchema));
910 911
    }

Marco Pivetta's avatar
Marco Pivetta committed
912 913 914
    /**
     * @group DBAL-669
     */
915
    public function testNamespacesComparison() : void
Marco Pivetta's avatar
Marco Pivetta committed
916 917
    {
        $config = new SchemaConfig();
Sergei Morozov's avatar
Sergei Morozov committed
918
        $config->setName('schemaName');
Marco Pivetta's avatar
Marco Pivetta committed
919

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

Sergei Morozov's avatar
Sergei Morozov committed
924
        $newSchema = new Schema([], [], $config);
Marco Pivetta's avatar
Marco Pivetta committed
925 926 927 928
        $newSchema->createTable('bar.tab');
        $newSchema->createTable('baz.tab');
        $newSchema->createTable('war.tab');

Sergei Morozov's avatar
Sergei Morozov committed
929 930 931
        $expected                = new SchemaDiff();
        $expected->fromSchema    = $oldSchema;
        $expected->newNamespaces = ['bar' => 'bar', 'baz' => 'baz'];
Marco Pivetta's avatar
Marco Pivetta committed
932 933 934

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

Sergei Morozov's avatar
Sergei Morozov committed
935
        self::assertEquals(['bar' => 'bar', 'baz' => 'baz'], $diff->newNamespaces);
936
        self::assertCount(2, $diff->newTables);
Marco Pivetta's avatar
Marco Pivetta committed
937 938
    }

939 940 941
    /**
     * @group DBAL-204
     */
942
    public function testFqnSchemaComparisonDifferentSchemaNameButSameTableNoDiff() : void
943 944
    {
        $config = new SchemaConfig();
Sergei Morozov's avatar
Sergei Morozov committed
945
        $config->setName('foo');
946

Sergei Morozov's avatar
Sergei Morozov committed
947
        $oldSchema = new Schema([], [], $config);
948
        $oldSchema->createTable('foo.bar');
949 950

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

Sergei Morozov's avatar
Sergei Morozov committed
953
        $expected             = new SchemaDiff();
954 955
        $expected->fromSchema = $oldSchema;

956
        self::assertEquals($expected, Comparator::compareSchemas($oldSchema, $newSchema));
957 958 959 960 961
    }

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

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

Sergei Morozov's avatar
Sergei Morozov committed
972
        $expected             = new SchemaDiff();
973
        $expected->fromSchema = $oldSchema;
974

975
        self::assertEquals($expected, Comparator::compareSchemas($oldSchema, $newSchema));
976 977
    }

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

        $newSchema = new Schema();
Sergei Morozov's avatar
Sergei Morozov committed
990 991 992
        $table     = $newSchema->createTable('foo');
        $table->addColumn('id', 'integer', ['autoincrement' => true]);
        $table->setPrimaryKey(['id']);
993

Sergei Morozov's avatar
Sergei Morozov committed
994
        $c    = new Comparator();
995 996
        $diff = $c->compare($oldSchema, $newSchema);

997
        self::assertCount(0, $diff->removedSequences);
998 999
    }

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

        $newSchema = new Schema();
Sergei Morozov's avatar
Sergei Morozov committed
1013 1014 1015 1016
        $table     = $newSchema->createTable('foo');
        $table->addColumn('id', 'integer', ['autoincrement' => true]);
        $table->setPrimaryKey(['id']);
        $newSchema->createSequence('foo_id_seq');
1017

Sergei Morozov's avatar
Sergei Morozov committed
1018
        $c    = new Comparator();
1019 1020
        $diff = $c->compare($oldSchema, $newSchema);

1021
        self::assertCount(0, $diff->newSequences);
1022
    }
1023

1024
    /**
1025 1026 1027 1028
     * 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.
1029
     */
1030
    public function testAvoidMultipleDropForeignKey() : void
1031 1032 1033
    {
        $oldSchema = new Schema();

1034 1035 1036 1037 1038
        $tableA = $oldSchema->createTable('table_a');
        $tableA->addColumn('id', 'integer');

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

1040 1041 1042 1043
        $tableC = $oldSchema->createTable('table_c');
        $tableC->addColumn('id', 'integer');
        $tableC->addColumn('table_a_id', 'integer');
        $tableC->addColumn('table_b_id', 'integer');
1044

Sergei Morozov's avatar
Sergei Morozov committed
1045 1046
        $tableC->addForeignKeyConstraint($tableA, ['table_a_id'], ['id']);
        $tableC->addForeignKeyConstraint($tableB, ['table_b_id'], ['id']);
1047 1048 1049

        $newSchema = new Schema();

1050 1051 1052 1053 1054 1055 1056 1057
        $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);
1058

1059 1060
        self::assertCount(1, $schemaDiff->changedTables['table_c']->removedForeignKeys);
        self::assertCount(1, $schemaDiff->orphanedForeignKeys);
1061 1062
    }

1063
    public function testCompareChangedColumn() : void
1064 1065 1066 1067 1068 1069 1070
    {
        $oldSchema = new Schema();

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

        $newSchema = new Schema();
Sergei Morozov's avatar
Sergei Morozov committed
1071
        $table     = $newSchema->createTable('foo');
1072 1073
        $table->addColumn('id', 'string');

Sergei Morozov's avatar
Sergei Morozov committed
1074 1075 1076 1077 1078 1079 1080
        $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'];
1081

1082
        self::assertEquals($expected, Comparator::compareSchemas($oldSchema, $newSchema));
1083
    }
1084

1085
    public function testCompareChangedBinaryColumn() : void
Steve Müller's avatar
Steve Müller committed
1086 1087 1088 1089 1090 1091 1092
    {
        $oldSchema = new Schema();

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

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

Sergei Morozov's avatar
Sergei Morozov committed
1096 1097 1098 1099 1100 1101 1102
        $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
1103

1104
        self::assertEquals($expected, Comparator::compareSchemas($oldSchema, $newSchema));
Steve Müller's avatar
Steve Müller committed
1105 1106
    }

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

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

1118
        self::assertFalse($diff);
1119 1120
    }

1121
    public function assertSchemaTableChangeCount(SchemaDiff $diff, int $newTableCount = 0, int $changeTableCount = 0, int $removeTableCount = 0) : void
1122
    {
Gabriel Caruso's avatar
Gabriel Caruso committed
1123 1124 1125
        self::assertCount($newTableCount, $diff->newTables);
        self::assertCount($changeTableCount, $diff->changedTables);
        self::assertCount($removeTableCount, $diff->removedTables);
1126
    }
1127

1128 1129 1130 1131 1132 1133
    public function assertSchemaSequenceChangeCount(
        SchemaDiff $diff,
        int $newSequenceCount = 0,
        int $changeSequenceCount = 0,
        int $removeSequenceCount = 0
    ) : void {
Sergei Morozov's avatar
Sergei Morozov committed
1134 1135 1136
        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.');
1137
    }
1138

1139
    public function testDiffColumnPlatformOptions() : void
1140
    {
Sergei Morozov's avatar
Sergei Morozov committed
1141 1142 1143
        $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']]);
1144 1145 1146 1147
        $column4 = new Column('foo', Type::getType('string'));

        $comparator = new Comparator();

Sergei Morozov's avatar
Sergei Morozov committed
1148 1149 1150 1151 1152 1153
        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));
1154
    }
1155

1156
    public function testComplexDiffColumn() : void
1157
    {
Sergei Morozov's avatar
Sergei Morozov committed
1158 1159 1160 1161
        $column1 = new Column('foo', Type::getType('string'), [
            'platformOptions' => ['foo' => 'foo'],
            'customSchemaOptions' => ['foo' => 'bar'],
        ]);
1162

Sergei Morozov's avatar
Sergei Morozov committed
1163 1164 1165
        $column2 = new Column('foo', Type::getType('string'), [
            'platformOptions' => ['foo' => 'bar'],
        ]);
1166 1167 1168

        $comparator = new Comparator();

Sergei Morozov's avatar
Sergei Morozov committed
1169 1170
        self::assertEquals([], $comparator->diffColumn($column1, $column2));
        self::assertEquals([], $comparator->diffColumn($column2, $column1));
1171
    }
1172 1173 1174 1175

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

        $fromSchema->expects($this->once())
            ->method('getNamespaces')
Sergei Morozov's avatar
Sergei Morozov committed
1188
            ->will($this->returnValue(['foo', 'bar']));
1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201

        $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
1202
            ->will($this->returnValue(['bar', 'baz']));
1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213

        $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
1214 1215 1216 1217
        $expected                    = new SchemaDiff();
        $expected->fromSchema        = $fromSchema;
        $expected->newNamespaces     = ['baz' => 'baz'];
        $expected->removedNamespaces = ['foo' => 'foo'];
1218

1219
        self::assertEquals($expected, $comparator->compare($fromSchema, $toSchema));
1220
    }
1221

1222
    public function testCompareGuidColumns() : void
1223 1224 1225
    {
        $comparator = new Comparator();

Sergei Morozov's avatar
Sergei Morozov committed
1226
        $column1 = new Column('foo', Type::getType('guid'), ['comment' => 'GUID 1']);
1227 1228 1229
        $column2 = new Column(
            'foo',
            Type::getType('guid'),
1230
            ['notnull' => false, 'length' => 36, 'fixed' => true, 'default' => 'NEWID()', 'comment' => 'GUID 2.']
1231 1232
        );

Sergei Morozov's avatar
Sergei Morozov committed
1233 1234
        self::assertEquals(['notnull', 'default', 'comment'], $comparator->diffColumn($column1, $column2));
        self::assertEquals(['notnull', 'default', 'comment'], $comparator->diffColumn($column2, $column1));
1235
    }
1236 1237 1238 1239 1240

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

        $comparator = new Comparator();

Sergei Morozov's avatar
Sergei Morozov committed
1248
        $expectedDiff = $equals ? [] : ['comment'];
1249 1250 1251

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

1252
        self::assertSame($expectedDiff, $actualDiff);
1253 1254 1255

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

1256
        self::assertSame($expectedDiff, $actualDiff);
1257 1258
    }

1259 1260 1261 1262
    /**
     * @return mixed[][]
     */
    public static function getCompareColumnComments() : iterable
1263
    {
Sergei Morozov's avatar
Sergei Morozov committed
1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284
        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],
        ];
1285
    }
1286

1287
    public function testForeignKeyRemovalWithRenamedLocalColumn() : void
1288
    {
Sergei Morozov's avatar
Sergei Morozov committed
1289 1290 1291 1292
        $fromSchema = new Schema([
            'table1' => new Table(
                'table1',
                [
1293
                    'id' => new Column('id', Type::getType('integer')),
Sergei Morozov's avatar
Sergei Morozov committed
1294 1295 1296 1297 1298
                ]
            ),
            'table2' => new Table(
                'table2',
                [
1299
                    'id' => new Column('id', Type::getType('integer')),
Sergei Morozov's avatar
Sergei Morozov committed
1300 1301 1302
                    'id_table1' => new Column('id_table1', Type::getType('integer')),
                ],
                [],
1303
                [],
Sergei Morozov's avatar
Sergei Morozov committed
1304 1305 1306 1307 1308 1309 1310 1311 1312
                [
                    new ForeignKeyConstraint(['id_table1'], 'table1', ['id'], 'fk_table2_table1'),
                ]
            ),
        ]);
        $toSchema   = new Schema([
            'table2' => new Table(
                'table2',
                [
1313
                    'id' => new Column('id', Type::getType('integer')),
Sergei Morozov's avatar
Sergei Morozov committed
1314 1315 1316
                    'id_table3' => new Column('id_table3', Type::getType('integer')),
                ],
                [],
1317
                [],
Sergei Morozov's avatar
Sergei Morozov committed
1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330
                [
                    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);
1331
        self::assertCount(1, $actual->orphanedForeignKeys);
Sergei Morozov's avatar
Sergei Morozov committed
1332 1333 1334
        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());
1335
    }
1336
}