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

19
use function array_keys;
20
use function get_class;
21

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

198
    public function testCompareColumnsMultipleTypeInstances(): void
199 200 201 202 203 204 205 206 207 208 209 210
    {
        $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));
    }

211
    public function testCompareColumnsOverriddenType(): void
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
    {
        $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));
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        $c = new Comparator();

490 491
        self::assertTrue($c->diffSequence($seq1, $seq2));
        self::assertTrue($c->diffSequence($seq1, $seq3));
492
        self::assertFalse($c->diffSequence($seq1, $seq4));
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::assertFalse($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');
Sergei Morozov's avatar
Sergei Morozov committed
683
        $tableA->addNamedForeignKeyConstraint('foo_constraint', 'bar', ['id'], ['id']);
684

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

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

692
        self::assertFalse($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);

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

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

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

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

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

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

        $table2 = clone $table1;

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

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

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

777 778 779 780
        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());
781 782 783 784 785 786 787 788 789
    }

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

        $table2 = clone $table1;

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

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

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

805 806 807 808 809 810
        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);
811 812
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

906
        self::assertEquals($expected, Comparator::compareSchemas($oldSchema, $newSchema));
907 908
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

972
        self::assertEquals($expected, Comparator::compareSchemas($oldSchema, $newSchema));
973 974
    }

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

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

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

994
        self::assertCount(0, $diff->removedSequences);
995 996
    }

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

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

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

1018
        self::assertCount(0, $diff->newSequences);
1019
    }
1020

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

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

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

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

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

        $newSchema = new Schema();

1047 1048 1049 1050 1051 1052 1053 1054
        $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);
1055

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

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

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

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

Sergei Morozov's avatar
Sergei Morozov committed
1071 1072 1073 1074 1075 1076 1077
        $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'];
1078

1079
        self::assertEquals($expected, Comparator::compareSchemas($oldSchema, $newSchema));
1080
    }
1081

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

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

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

Sergei Morozov's avatar
Sergei Morozov committed
1093 1094 1095 1096 1097 1098 1099
        $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
1100

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

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

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

1115
        self::assertFalse($diff);
1116 1117
    }

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

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

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

        $comparator = new Comparator();

Sergei Morozov's avatar
Sergei Morozov committed
1145 1146 1147 1148 1149 1150
        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));
1151
    }
1152

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

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

        $comparator = new Comparator();

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

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

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

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

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

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

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

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

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

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

        $comparator = new Comparator();

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

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

1249
        self::assertSame($expectedDiff, $actualDiff);
1250 1251 1252

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

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

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

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