Passed
Pull Request — 2.11.x (#3876)
by Claudio
64:16
created

testAutoQuotedColumnInPrimaryKeyPropagation()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 5
c 1
b 0
f 0
dl 0
loc 8
rs 10
cc 1
nc 1
nop 1
1
<?php
2
3
namespace Doctrine\Tests\DBAL\Platforms;
4
5
use Doctrine\Common\EventManager;
6
use Doctrine\DBAL\DBALException;
7
use Doctrine\DBAL\Events;
8
use Doctrine\DBAL\Platforms\AbstractPlatform;
9
use Doctrine\DBAL\Platforms\Keywords\KeywordList;
10
use Doctrine\DBAL\Schema\Column;
11
use Doctrine\DBAL\Schema\ColumnDiff;
12
use Doctrine\DBAL\Schema\Comparator;
13
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
14
use Doctrine\DBAL\Schema\Index;
15
use Doctrine\DBAL\Schema\Table;
16
use Doctrine\DBAL\Schema\TableDiff;
17
use Doctrine\DBAL\Types\Type;
18
use Doctrine\Tests\DbalTestCase;
19
use Doctrine\Tests\Types\CommentedType;
20
use function get_class;
21
use function implode;
22
use function sprintf;
23
use function str_repeat;
24
25
abstract class AbstractPlatformTestCase extends DbalTestCase
26
{
27
    /** @var AbstractPlatform */
28
    protected $platform;
29
30
    abstract public function createPlatform() : AbstractPlatform;
31
32
    protected function setUp() : void
33
    {
34
        $this->platform = $this->createPlatform();
35
    }
36
37
    /**
38
     * @group DDC-1360
39
     */
40
    public function testQuoteIdentifier() : void
41
    {
42
        if ($this->platform->getName() === 'mssql') {
43
            $this->markTestSkipped('Not working this way on mssql.');
44
        }
45
46
        $c = $this->platform->getIdentifierQuoteCharacter();
47
        self::assertEquals($c . 'test' . $c, $this->platform->quoteIdentifier('test'));
48
        self::assertEquals($c . 'test' . $c . '.' . $c . 'test' . $c, $this->platform->quoteIdentifier('test.test'));
49
        self::assertEquals(str_repeat($c, 4), $this->platform->quoteIdentifier($c));
50
    }
51
52
    /**
53
     * @group DDC-1360
54
     */
55
    public function testQuoteSingleIdentifier() : void
56
    {
57
        if ($this->platform->getName() === 'mssql') {
58
            $this->markTestSkipped('Not working this way on mssql.');
59
        }
60
61
        $c = $this->platform->getIdentifierQuoteCharacter();
62
        self::assertEquals($c . 'test' . $c, $this->platform->quoteSingleIdentifier('test'));
63
        self::assertEquals($c . 'test.test' . $c, $this->platform->quoteSingleIdentifier('test.test'));
64
        self::assertEquals(str_repeat($c, 4), $this->platform->quoteSingleIdentifier($c));
65
    }
66
67
    /**
68
     * @group DBAL-1029
69
     * @dataProvider getReturnsForeignKeyReferentialActionSQL
70
     */
71
    public function testReturnsForeignKeyReferentialActionSQL(string $action, string $expectedSQL) : void
72
    {
73
        self::assertSame($expectedSQL, $this->platform->getForeignKeyReferentialActionSQL($action));
74
    }
75
76
    /**
77
     * @return mixed[][]
78
     */
79
    public static function getReturnsForeignKeyReferentialActionSQL() : iterable
80
    {
81
        return [
82
            ['CASCADE', 'CASCADE'],
83
            ['SET NULL', 'SET NULL'],
84
            ['NO ACTION', 'NO ACTION'],
85
            ['RESTRICT', 'RESTRICT'],
86
            ['SET DEFAULT', 'SET DEFAULT'],
87
            ['CaScAdE', 'CASCADE'],
88
        ];
89
    }
90
91
    public function testGetInvalidForeignKeyReferentialActionSQL() : void
92
    {
93
        $this->expectException('InvalidArgumentException');
94
        $this->platform->getForeignKeyReferentialActionSQL('unknown');
95
    }
96
97
    public function testGetUnknownDoctrineMappingType() : void
98
    {
99
        $this->expectException(DBALException::class);
100
        $this->platform->getDoctrineTypeMapping('foobar');
101
    }
102
103
    public function testRegisterDoctrineMappingType() : void
104
    {
105
        $this->platform->registerDoctrineTypeMapping('foo', 'integer');
106
        self::assertEquals('integer', $this->platform->getDoctrineTypeMapping('foo'));
107
    }
108
109
    public function testRegisterUnknownDoctrineMappingType() : void
110
    {
111
        $this->expectException(DBALException::class);
112
        $this->platform->registerDoctrineTypeMapping('foo', 'bar');
113
    }
114
115
    /**
116
     * @group DBAL-2594
117
     */
118
    public function testRegistersCommentedDoctrineMappingTypeImplicitly() : void
119
    {
120
        if (! Type::hasType('my_commented')) {
121
            Type::addType('my_commented', CommentedType::class);
122
        }
123
124
        $type = Type::getType('my_commented');
125
        $this->platform->registerDoctrineTypeMapping('foo', 'my_commented');
126
127
        self::assertTrue($this->platform->isCommentedDoctrineType($type));
128
    }
129
130
    /**
131
     * @group DBAL-939
132
     * @dataProvider getIsCommentedDoctrineType
133
     */
134
    public function testIsCommentedDoctrineType(Type $type, bool $commented) : void
135
    {
136
        self::assertSame($commented, $this->platform->isCommentedDoctrineType($type));
137
    }
138
139
    /**
140
     * @return mixed[]
141
     */
142
    public function getIsCommentedDoctrineType() : iterable
143
    {
144
        $this->setUp();
145
146
        $data = [];
147
148
        foreach (Type::getTypesMap() as $typeName => $className) {
149
            $type = Type::getType($typeName);
150
151
            $data[$typeName] = [
152
                $type,
153
                $type->requiresSQLCommentHint($this->platform),
154
            ];
155
        }
156
157
        return $data;
158
    }
159
160
    public function testCreateWithNoColumns() : void
161
    {
162
        $table = new Table('test');
163
164
        $this->expectException(DBALException::class);
165
        $sql = $this->platform->getCreateTableSQL($table);
0 ignored issues
show
Unused Code introduced by
The assignment to $sql is dead and can be removed.
Loading history...
166
    }
167
168
    public function testGeneratesTableCreationSql() : void
169
    {
170
        $table = new Table('test');
171
        $table->addColumn('id', 'integer', ['notnull' => true, 'autoincrement' => true]);
172
        $table->addColumn('test', 'string', ['notnull' => false, 'length' => 255]);
173
        $table->setPrimaryKey(['id']);
174
175
        $sql = $this->platform->getCreateTableSQL($table);
176
        self::assertEquals($this->getGenerateTableSql(), $sql[0]);
177
    }
178
179
    abstract public function getGenerateTableSql() : string;
180
181
    public function testGenerateTableWithMultiColumnUniqueIndex() : void
182
    {
183
        $table = new Table('test');
184
        $table->addColumn('foo', 'string', ['notnull' => false, 'length' => 255]);
185
        $table->addColumn('bar', 'string', ['notnull' => false, 'length' => 255]);
186
        $table->addUniqueIndex(['foo', 'bar']);
187
188
        $sql = $this->platform->getCreateTableSQL($table);
189
        self::assertEquals($this->getGenerateTableWithMultiColumnUniqueIndexSql(), $sql);
190
    }
191
192
    /**
193
     * @return string[]
194
     */
195
    abstract public function getGenerateTableWithMultiColumnUniqueIndexSql() : array;
196
197
    public function testGeneratesIndexCreationSql() : void
198
    {
199
        $indexDef = new Index('my_idx', ['user_name', 'last_login']);
200
201
        self::assertEquals(
202
            $this->getGenerateIndexSql(),
203
            $this->platform->getCreateIndexSQL($indexDef, 'mytable')
204
        );
205
    }
206
207
    abstract public function getGenerateIndexSql() : string;
208
209
    public function testGeneratesUniqueIndexCreationSql() : void
210
    {
211
        $indexDef = new Index('index_name', ['test', 'test2'], true);
212
213
        $sql = $this->platform->getCreateIndexSQL($indexDef, 'test');
214
        self::assertEquals($this->getGenerateUniqueIndexSql(), $sql);
215
    }
216
217
    abstract public function getGenerateUniqueIndexSql() : string;
218
219
    public function testGeneratesPartialIndexesSqlOnlyWhenSupportingPartialIndexes() : void
220
    {
221
        $where       = 'test IS NULL AND test2 IS NOT NULL';
222
        $indexDef    = new Index('name', ['test', 'test2'], false, false, [], ['where' => $where]);
223
        $uniqueIndex = new Index('name', ['test', 'test2'], true, false, [], ['where' => $where]);
224
225
        $expected = ' WHERE ' . $where;
226
227
        $actuals = [];
228
229
        if ($this->supportsInlineIndexDeclaration()) {
230
            $actuals[] = $this->platform->getIndexDeclarationSQL('name', $indexDef);
231
        }
232
233
        $actuals[] = $this->platform->getUniqueConstraintDeclarationSQL('name', $uniqueIndex);
234
        $actuals[] = $this->platform->getCreateIndexSQL($indexDef, 'table');
235
236
        foreach ($actuals as $actual) {
237
            if ($this->platform->supportsPartialIndexes()) {
238
                self::assertStringEndsWith($expected, $actual, 'WHERE clause should be present');
239
            } else {
240
                self::assertStringEndsNotWith($expected, $actual, 'WHERE clause should NOT be present');
241
            }
242
        }
243
    }
244
245
    public function testGeneratesForeignKeyCreationSql() : void
246
    {
247
        $fk = new ForeignKeyConstraint(['fk_name_id'], 'other_table', ['id'], '');
248
249
        $sql = $this->platform->getCreateForeignKeySQL($fk, 'test');
250
        self::assertEquals($sql, $this->getGenerateForeignKeySql());
251
    }
252
253
    abstract public function getGenerateForeignKeySql() : string;
254
255
    public function testGeneratesConstraintCreationSql() : void
256
    {
257
        $idx = new Index('constraint_name', ['test'], true, false);
258
        $sql = $this->platform->getCreateConstraintSQL($idx, 'test');
259
        self::assertEquals($this->getGenerateConstraintUniqueIndexSql(), $sql);
260
261
        $pk  = new Index('constraint_name', ['test'], true, true);
262
        $sql = $this->platform->getCreateConstraintSQL($pk, 'test');
263
        self::assertEquals($this->getGenerateConstraintPrimaryIndexSql(), $sql);
264
265
        $fk  = new ForeignKeyConstraint(['fk_name'], 'foreign', ['id'], 'constraint_fk');
266
        $sql = $this->platform->getCreateConstraintSQL($fk, 'test');
267
        self::assertEquals($this->getGenerateConstraintForeignKeySql($fk), $sql);
268
    }
269
270
    public function testGeneratesForeignKeySqlOnlyWhenSupportingForeignKeys() : void
271
    {
272
        $fk = new ForeignKeyConstraint(['fk_name'], 'foreign', ['id'], 'constraint_fk');
273
274
        if ($this->platform->supportsForeignKeyConstraints()) {
275
            self::assertIsString($this->platform->getCreateForeignKeySQL($fk, 'test'));
276
        } else {
277
            $this->expectException(DBALException::class);
278
            $this->platform->getCreateForeignKeySQL($fk, 'test');
279
        }
280
    }
281
282
    protected function getBitAndComparisonExpressionSql(string $value1, string $value2) : string
283
    {
284
        return '(' . $value1 . ' & ' . $value2 . ')';
285
    }
286
287
    /**
288
     * @group DDC-1213
289
     */
290
    public function testGeneratesBitAndComparisonExpressionSql() : void
291
    {
292
        $sql = $this->platform->getBitAndComparisonExpression(2, 4);
293
        self::assertEquals($this->getBitAndComparisonExpressionSql(2, 4), $sql);
294
    }
295
296
    protected function getBitOrComparisonExpressionSql(string $value1, string $value2) : string
297
    {
298
        return '(' . $value1 . ' | ' . $value2 . ')';
299
    }
300
301
    /**
302
     * @group DDC-1213
303
     */
304
    public function testGeneratesBitOrComparisonExpressionSql() : void
305
    {
306
        $sql = $this->platform->getBitOrComparisonExpression(2, 4);
307
        self::assertEquals($this->getBitOrComparisonExpressionSql(2, 4), $sql);
308
    }
309
310
    public function getGenerateConstraintUniqueIndexSql() : string
311
    {
312
        return 'ALTER TABLE test ADD CONSTRAINT constraint_name UNIQUE (test)';
313
    }
314
315
    public function getGenerateConstraintPrimaryIndexSql() : string
316
    {
317
        return 'ALTER TABLE test ADD CONSTRAINT constraint_name PRIMARY KEY (test)';
318
    }
319
320
    public function getGenerateConstraintForeignKeySql(ForeignKeyConstraint $fk) : string
321
    {
322
        $quotedForeignTable = $fk->getQuotedForeignTableName($this->platform);
323
324
        return sprintf(
325
            'ALTER TABLE test ADD CONSTRAINT constraint_fk FOREIGN KEY (fk_name) REFERENCES %s (id)',
326
            $quotedForeignTable
327
        );
328
    }
329
330
    /**
331
     * @return string[]
332
     */
333
    abstract public function getGenerateAlterTableSql() : array;
334
335
    public function testGeneratesTableAlterationSql() : void
336
    {
337
        $expectedSql = $this->getGenerateAlterTableSql();
338
339
        $table = new Table('mytable');
340
        $table->addColumn('id', 'integer', ['autoincrement' => true]);
341
        $table->addColumn('foo', 'integer');
342
        $table->addColumn('bar', 'string');
343
        $table->addColumn('bloo', 'boolean');
344
        $table->setPrimaryKey(['id']);
345
346
        $tableDiff                         = new TableDiff('mytable');
347
        $tableDiff->fromTable              = $table;
348
        $tableDiff->newName                = 'userlist';
349
        $tableDiff->addedColumns['quota']  = new Column('quota', Type::getType('integer'), ['notnull' => false]);
350
        $tableDiff->removedColumns['foo']  = new Column('foo', Type::getType('integer'));
351
        $tableDiff->changedColumns['bar']  = new ColumnDiff(
352
            'bar',
353
            new Column(
354
                'baz',
355
                Type::getType('string'),
356
                ['default' => 'def']
357
            ),
358
            ['type', 'notnull', 'default']
359
        );
360
        $tableDiff->changedColumns['bloo'] = new ColumnDiff(
361
            'bloo',
362
            new Column(
363
                'bloo',
364
                Type::getType('boolean'),
365
                ['default' => false]
366
            ),
367
            ['type', 'notnull', 'default']
368
        );
369
370
        $sql = $this->platform->getAlterTableSQL($tableDiff);
371
372
        self::assertEquals($expectedSql, $sql);
373
    }
374
375
    public function testGetCustomColumnDeclarationSql() : void
376
    {
377
        $field = ['columnDefinition' => 'MEDIUMINT(6) UNSIGNED'];
378
        self::assertEquals('foo MEDIUMINT(6) UNSIGNED', $this->platform->getColumnDeclarationSQL('foo', $field));
379
    }
380
381
    public function testGetCreateTableSqlDispatchEvent() : void
382
    {
383
        $listenerMock = $this->getMockBuilder($this->getMockClass('GetCreateTableSqlDispatchEvenListener'))
384
            ->addMethods(['onSchemaCreateTable', 'onSchemaCreateTableColumn'])
385
            ->getMock();
386
        $listenerMock
387
            ->expects($this->once())
388
            ->method('onSchemaCreateTable');
389
        $listenerMock
390
            ->expects($this->exactly(2))
391
            ->method('onSchemaCreateTableColumn');
392
393
        $eventManager = new EventManager();
394
        $eventManager->addEventListener([Events::onSchemaCreateTable, Events::onSchemaCreateTableColumn], $listenerMock);
395
396
        $this->platform->setEventManager($eventManager);
397
398
        $table = new Table('test');
399
        $table->addColumn('foo', 'string', ['notnull' => false, 'length' => 255]);
400
        $table->addColumn('bar', 'string', ['notnull' => false, 'length' => 255]);
401
402
        $this->platform->getCreateTableSQL($table);
403
    }
404
405
    public function testGetDropTableSqlDispatchEvent() : void
406
    {
407
        $listenerMock = $this->getMockBuilder($this->getMockClass('GetDropTableSqlDispatchEventListener'))
408
            ->addMethods(['onSchemaDropTable'])
409
            ->getMock();
410
        $listenerMock
411
            ->expects($this->once())
412
            ->method('onSchemaDropTable');
413
414
        $eventManager = new EventManager();
415
        $eventManager->addEventListener([Events::onSchemaDropTable], $listenerMock);
416
417
        $this->platform->setEventManager($eventManager);
418
419
        $this->platform->getDropTableSQL('TABLE');
420
    }
421
422
    public function testGetAlterTableSqlDispatchEvent() : void
423
    {
424
        $events = [
425
            'onSchemaAlterTable',
426
            'onSchemaAlterTableAddColumn',
427
            'onSchemaAlterTableRemoveColumn',
428
            'onSchemaAlterTableChangeColumn',
429
            'onSchemaAlterTableRenameColumn',
430
        ];
431
432
        $listenerMock = $this->getMockBuilder($this->getMockClass('GetAlterTableSqlDispatchEvenListener'))
433
            ->addMethods($events)
434
            ->getMock();
435
        $listenerMock
436
            ->expects($this->once())
437
            ->method('onSchemaAlterTable');
438
        $listenerMock
439
            ->expects($this->once())
440
            ->method('onSchemaAlterTableAddColumn');
441
        $listenerMock
442
            ->expects($this->once())
443
            ->method('onSchemaAlterTableRemoveColumn');
444
        $listenerMock
445
            ->expects($this->once())
446
            ->method('onSchemaAlterTableChangeColumn');
447
        $listenerMock
448
            ->expects($this->once())
449
            ->method('onSchemaAlterTableRenameColumn');
450
451
        $eventManager = new EventManager();
452
        $events       = [
453
            Events::onSchemaAlterTable,
454
            Events::onSchemaAlterTableAddColumn,
455
            Events::onSchemaAlterTableRemoveColumn,
456
            Events::onSchemaAlterTableChangeColumn,
457
            Events::onSchemaAlterTableRenameColumn,
458
        ];
459
        $eventManager->addEventListener($events, $listenerMock);
460
461
        $this->platform->setEventManager($eventManager);
462
463
        $table = new Table('mytable');
464
        $table->addColumn('removed', 'integer');
465
        $table->addColumn('changed', 'integer');
466
        $table->addColumn('renamed', 'integer');
467
468
        $tableDiff                            = new TableDiff('mytable');
469
        $tableDiff->fromTable                 = $table;
470
        $tableDiff->addedColumns['added']     = new Column('added', Type::getType('integer'), []);
471
        $tableDiff->removedColumns['removed'] = new Column('removed', Type::getType('integer'), []);
472
        $tableDiff->changedColumns['changed'] = new ColumnDiff(
473
            'changed',
474
            new Column(
475
                'changed2',
476
                Type::getType('string'),
477
                []
478
            ),
479
            []
480
        );
481
        $tableDiff->renamedColumns['renamed'] = new Column('renamed2', Type::getType('integer'), []);
482
483
        $this->platform->getAlterTableSQL($tableDiff);
484
    }
485
486
    /**
487
     * @group DBAL-42
488
     */
489
    public function testCreateTableColumnComments() : void
490
    {
491
        $table = new Table('test');
492
        $table->addColumn('id', 'integer', ['comment' => 'This is a comment']);
493
        $table->setPrimaryKey(['id']);
494
495
        self::assertEquals($this->getCreateTableColumnCommentsSQL(), $this->platform->getCreateTableSQL($table));
496
    }
497
498
    /**
499
     * @group DBAL-42
500
     */
501
    public function testAlterTableColumnComments() : void
502
    {
503
        $tableDiff                        = new TableDiff('mytable');
504
        $tableDiff->addedColumns['quota'] = new Column('quota', Type::getType('integer'), ['comment' => 'A comment']);
505
        $tableDiff->changedColumns['foo'] = new ColumnDiff(
506
            'foo',
507
            new Column(
508
                'foo',
509
                Type::getType('string')
510
            ),
511
            ['comment']
512
        );
513
        $tableDiff->changedColumns['bar'] = new ColumnDiff(
514
            'bar',
515
            new Column(
516
                'baz',
517
                Type::getType('string'),
518
                ['comment' => 'B comment']
519
            ),
520
            ['comment']
521
        );
522
523
        self::assertEquals($this->getAlterTableColumnCommentsSQL(), $this->platform->getAlterTableSQL($tableDiff));
524
    }
525
526
    public function testCreateTableColumnTypeComments() : void
527
    {
528
        $table = new Table('test');
529
        $table->addColumn('id', 'integer');
530
        $table->addColumn('data', 'array');
531
        $table->setPrimaryKey(['id']);
532
533
        self::assertEquals($this->getCreateTableColumnTypeCommentsSQL(), $this->platform->getCreateTableSQL($table));
534
    }
535
536
    /**
537
     * @return string[]
538
     */
539
    public function getCreateTableColumnCommentsSQL() : array
540
    {
541
        $this->markTestSkipped('Platform does not support Column comments.');
0 ignored issues
show
Bug Best Practice introduced by
In this branch, the function will implicitly return null which is incompatible with the type-hinted return array. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
542
    }
543
544
    /**
545
     * @return string[]
546
     */
547
    public function getAlterTableColumnCommentsSQL() : array
548
    {
549
        $this->markTestSkipped('Platform does not support Column comments.');
0 ignored issues
show
Bug Best Practice introduced by
In this branch, the function will implicitly return null which is incompatible with the type-hinted return array. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
550
    }
551
552
    /**
553
     * @return string[]
554
     */
555
    public function getCreateTableColumnTypeCommentsSQL() : array
556
    {
557
        $this->markTestSkipped('Platform does not support Column comments.');
0 ignored issues
show
Bug Best Practice introduced by
In this branch, the function will implicitly return null which is incompatible with the type-hinted return array. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
558
    }
559
560
    public function testGetDefaultValueDeclarationSQL() : void
561
    {
562
        // non-timestamp value will get single quotes
563
        $field = [
564
            'type' => Type::getType('string'),
565
            'default' => 'non_timestamp',
566
        ];
567
568
        self::assertEquals(" DEFAULT 'non_timestamp'", $this->platform->getDefaultValueDeclarationSQL($field));
569
    }
570
571
    /**
572
     * @group 2859
573
     */
574
    public function testGetDefaultValueDeclarationSQLDateTime() : void
575
    {
576
        // timestamps on datetime types should not be quoted
577
        foreach (['datetime', 'datetimetz', 'datetime_immutable', 'datetimetz_immutable'] as $type) {
578
            $field = [
579
                'type'    => Type::getType($type),
580
                'default' => $this->platform->getCurrentTimestampSQL(),
581
            ];
582
583
            self::assertSame(
584
                ' DEFAULT ' . $this->platform->getCurrentTimestampSQL(),
585
                $this->platform->getDefaultValueDeclarationSQL($field)
586
            );
587
        }
588
    }
589
590
    public function testGetDefaultValueDeclarationSQLForIntegerTypes() : void
591
    {
592
        foreach (['bigint', 'integer', 'smallint'] as $type) {
593
            $field = [
594
                'type'    => Type::getType($type),
595
                'default' => 1,
596
            ];
597
598
            self::assertEquals(
599
                ' DEFAULT 1',
600
                $this->platform->getDefaultValueDeclarationSQL($field)
601
            );
602
        }
603
    }
604
605
    /**
606
     * @group 2859
607
     */
608
    public function testGetDefaultValueDeclarationSQLForDateType() : void
609
    {
610
        $currentDateSql = $this->platform->getCurrentDateSQL();
611
        foreach (['date', 'date_immutable'] as $type) {
612
            $field = [
613
                'type'    => Type::getType($type),
614
                'default' => $currentDateSql,
615
            ];
616
617
            self::assertSame(
618
                ' DEFAULT ' . $currentDateSql,
619
                $this->platform->getDefaultValueDeclarationSQL($field)
620
            );
621
        }
622
    }
623
624
    /**
625
     * @group DBAL-45
626
     */
627
    public function testKeywordList() : void
628
    {
629
        $keywordList = $this->platform->getReservedKeywordsList();
630
        self::assertInstanceOf(KeywordList::class, $keywordList);
631
632
        self::assertTrue($keywordList->isKeyword('table'));
633
    }
634
635
    /**
636
     * @return string[][]
637
     */
638
    public function getAutoQuotedIdentifiers() : array
639
    {
640
        return [
641
            ['create'],
642
            ['select'],
643
            ['a name with spaces'],
644
            ['a name with/slash'],
645
            ['name-with-dash'],
646
        ];
647
    }
648
649
    /**
650
     * @group DBAL-374
651
     * @group DBAL-2979
652
     * @dataProvider getAutoQuotedIdentifiers
653
     */
654
    public function testAutoQuotedColumnInPrimaryKeyPropagation(string $columnName) : void
655
    {
656
        $table = new Table('`quoted`');
657
        $table->addColumn($columnName, 'string');
658
        $table->setPrimaryKey([$columnName]);
659
660
        $sql = $this->platform->getCreateTableSQL($table);
661
        self::assertEquals($this->getQuotedColumnInPrimaryKeySQL($columnName), $sql);
662
    }
663
664
    /**
665
     * @return string[]
666
     */
667
    abstract protected function getQuotedColumnInPrimaryKeySQL(string $columnName = 'create') : array;
668
669
    /**
670
     * @return string[]
671
     */
672
    abstract protected function getQuotedColumnInIndexSQL() : array;
673
674
    /**
675
     * @return string[]
676
     */
677
    abstract protected function getQuotedNameInIndexSQL() : array;
678
679
    /**
680
     * @return string[]
681
     */
682
    abstract protected function getQuotedColumnInForeignKeySQL(string $columnName = 'create') : array;
683
684
    /**
685
     * @group DBAL-374
686
     */
687
    public function testQuotedColumnInIndexPropagation() : void
688
    {
689
        $table = new Table('`quoted`');
690
        $table->addColumn('create', 'string');
691
        $table->addIndex(['create']);
692
693
        $sql = $this->platform->getCreateTableSQL($table);
694
        self::assertEquals($this->getQuotedColumnInIndexSQL(), $sql);
695
    }
696
697
    public function testQuotedNameInIndexSQL() : void
698
    {
699
        $table = new Table('test');
700
        $table->addColumn('column1', 'string');
701
        $table->addIndex(['column1'], '`key`');
702
703
        $sql = $this->platform->getCreateTableSQL($table);
704
        self::assertEquals($this->getQuotedNameInIndexSQL(), $sql);
705
    }
706
707
    /**
708
     * @group DBAL-374
709
     * @group DBAL-2979
710
     * @dataProvider getAutoQuotedIdentifiers
711
     * @throws DBALException
712
     */
713
    public function testQuotedColumnInForeignKeyPropagation(string $columnName) : void
714
    {
715
        $table = new Table('`quoted`');
716
        $table->addColumn($columnName, 'string');
717
        $table->addColumn('foo', 'string');
718
        $table->addColumn('`bar`', 'string');
719
720
        // Foreign table with reserved keyword as name (needs quotation).
721
        $foreignTable = new Table('foreign');
722
        $foreignTable->addColumn($columnName, 'string');    // Foreign column with reserved keyword as name (needs quotation).
723
        $foreignTable->addColumn('bar', 'string');       // Foreign column with non-reserved keyword as name (does not need quotation).
724
        $foreignTable->addColumn('`foo-bar`', 'string'); // Foreign table with special character in name (needs quotation on some platforms, e.g. Sqlite).
725
726
        $table->addForeignKeyConstraint($foreignTable, [$columnName, 'foo', '`bar`'], [$columnName, 'bar', '`foo-bar`'], [], 'FK_WITH_RESERVED_KEYWORD');
727
728
        // Foreign table with non-reserved keyword as name (does not need quotation).
729
        $foreignTable = new Table('foo');
730
        $foreignTable->addColumn($columnName, 'string');    // Foreign column with reserved keyword as name (needs quotation).
731
        $foreignTable->addColumn('bar', 'string');       // Foreign column with non-reserved keyword as name (does not need quotation).
732
        $foreignTable->addColumn('`foo-bar`', 'string'); // Foreign table with special character in name (needs quotation on some platforms, e.g. Sqlite).
733
734
        $table->addForeignKeyConstraint($foreignTable, [$columnName, 'foo', '`bar`'], [$columnName, 'bar', '`foo-bar`'], [], 'FK_WITH_NON_RESERVED_KEYWORD');
735
736
        // Foreign table with special character in name (needs quotation on some platforms, e.g. Sqlite).
737
        $foreignTable = new Table('`foo-bar`');
738
        $foreignTable->addColumn($columnName, 'string');    // Foreign column with reserved keyword as name (needs quotation).
739
        $foreignTable->addColumn('bar', 'string');       // Foreign column with non-reserved keyword as name (does not need quotation).
740
        $foreignTable->addColumn('`foo-bar`', 'string'); // Foreign table with special character in name (needs quotation on some platforms, e.g. Sqlite).
741
742
        $table->addForeignKeyConstraint($foreignTable, [$columnName, 'foo', '`bar`'], [$columnName, 'bar', '`foo-bar`'], [], 'FK_WITH_INTENDED_QUOTATION');
743
744
        $sql = $this->platform->getCreateTableSQL($table, AbstractPlatform::CREATE_FOREIGNKEYS);
745
        self::assertEquals($this->getQuotedColumnInForeignKeySQL($columnName), $sql);
746
    }
747
748
    /**
749
     * @group DBAL-1051
750
     * @group DBAL-2979
751
     * @dataProvider getAutoQuotedIdentifiers
752
     */
753
    public function testQuotesReservedKeywordInUniqueConstraintDeclarationSQL(string $constraintName) : void
754
    {
755
        $index = new Index($constraintName, ['foo'], true);
756
757
        self::assertSame(
758
            $this->getQuotesReservedKeywordInUniqueConstraintDeclarationSQL($constraintName),
759
            $this->platform->getUniqueConstraintDeclarationSQL($constraintName, $index)
760
        );
761
    }
762
763
    abstract protected function getQuotesReservedKeywordInUniqueConstraintDeclarationSQL(string $constraintName = 'select') : string;
764
765
    /**
766
     * @group DBAL-2270
767
     * @group DBAL-2979
768
     * @dataProvider getAutoQuotedIdentifiers
769
     */
770
    public function testQuotesReservedKeywordInTruncateTableSQL(string $tableName) : void
771
    {
772
        self::assertSame(
773
            $this->getQuotesReservedKeywordInTruncateTableSQL($tableName),
774
            $this->platform->getTruncateTableSQL($tableName)
775
        );
776
    }
777
778
    abstract protected function getQuotesReservedKeywordInTruncateTableSQL(string $tableName = 'select') : string;
779
780
    /**
781
     * @group DBAL-1051
782
     * @group DBAL-2979
783
     * @dataProvider getAutoQuotedIdentifiers
784
     */
785
    public function testQuotesReservedKeywordInIndexDeclarationSQL(string $indexName) : void
786
    {
787
        $index = new Index($indexName, ['foo']);
788
789
        if (! $this->supportsInlineIndexDeclaration()) {
790
            $this->expectException(DBALException::class);
791
        }
792
793
        self::assertSame(
794
            $this->getQuotesReservedKeywordInIndexDeclarationSQL($indexName),
795
            $this->platform->getIndexDeclarationSQL($indexName, $index)
796
        );
797
    }
798
799
    abstract protected function getQuotesReservedKeywordInIndexDeclarationSQL(string $indexName = 'select') : string;
800
801
    protected function supportsInlineIndexDeclaration() : bool
802
    {
803
        return true;
804
    }
805
806
    public function testSupportsCommentOnStatement() : void
807
    {
808
        self::assertSame($this->supportsCommentOnStatement(), $this->platform->supportsCommentOnStatement());
809
    }
810
811
    protected function supportsCommentOnStatement() : bool
812
    {
813
        return false;
814
    }
815
816
    public function testGetCreateSchemaSQL() : void
817
    {
818
        $this->expectException(DBALException::class);
819
820
        $this->platform->getCreateSchemaSQL('schema');
821
    }
822
823
    /**
824
     * @group DBAL-585
825
     */
826
    public function testAlterTableChangeQuotedColumn() : void
827
    {
828
        $tableDiff                        = new TableDiff('mytable');
829
        $tableDiff->fromTable             = new Table('mytable');
830
        $tableDiff->changedColumns['foo'] = new ColumnDiff(
831
            'select',
832
            new Column(
833
                'select',
834
                Type::getType('string')
835
            ),
836
            ['type']
837
        );
838
839
        self::assertStringContainsString(
840
            $this->platform->quoteIdentifier('select'),
841
            implode(';', $this->platform->getAlterTableSQL($tableDiff))
842
        );
843
    }
844
845
    /**
846
     * @group DBAL-563
847
     */
848
    public function testUsesSequenceEmulatedIdentityColumns() : void
849
    {
850
        self::assertFalse($this->platform->usesSequenceEmulatedIdentityColumns());
851
    }
852
853
    /**
854
     * @group DBAL-563
855
     */
856
    public function testReturnsIdentitySequenceName() : void
857
    {
858
        $this->expectException(DBALException::class);
859
860
        $this->platform->getIdentitySequenceName('mytable', 'mycolumn');
861
    }
862
863
    public function testReturnsBinaryDefaultLength() : void
864
    {
865
        self::assertSame($this->getBinaryDefaultLength(), $this->platform->getBinaryDefaultLength());
866
    }
867
868
    protected function getBinaryDefaultLength() : int
869
    {
870
        return 255;
871
    }
872
873
    public function testReturnsBinaryMaxLength() : void
874
    {
875
        self::assertSame($this->getBinaryMaxLength(), $this->platform->getBinaryMaxLength());
876
    }
877
878
    protected function getBinaryMaxLength() : int
879
    {
880
        return 4000;
881
    }
882
883
    public function testReturnsBinaryTypeDeclarationSQL() : void
884
    {
885
        $this->expectException(DBALException::class);
886
887
        $this->platform->getBinaryTypeDeclarationSQL([]);
888
    }
889
890
    public function testReturnsBinaryTypeLongerThanMaxDeclarationSQL() : void
891
    {
892
        $this->markTestSkipped('Not applicable to the platform');
893
    }
894
895
    /**
896
     * @group DBAL-553
897
     */
898
    public function hasNativeJsonType() : void
899
    {
900
        self::assertFalse($this->platform->hasNativeJsonType());
901
    }
902
903
    /**
904
     * @group DBAL-553
905
     */
906
    public function testReturnsJsonTypeDeclarationSQL() : void
907
    {
908
        $column = [
909
            'length'  => 666,
910
            'notnull' => true,
911
            'type'    => Type::getType('json_array'),
912
        ];
913
914
        self::assertSame(
915
            $this->platform->getClobTypeDeclarationSQL($column),
916
            $this->platform->getJsonTypeDeclarationSQL($column)
917
        );
918
    }
919
920
    /**
921
     * @group DBAL-234
922
     */
923
    public function testAlterTableRenameIndex() : void
924
    {
925
        $tableDiff            = new TableDiff('mytable');
926
        $tableDiff->fromTable = new Table('mytable');
927
        $tableDiff->fromTable->addColumn('id', 'integer');
928
        $tableDiff->fromTable->setPrimaryKey(['id']);
929
        $tableDiff->renamedIndexes = [
930
            'idx_foo' => new Index('idx_bar', ['id']),
931
        ];
932
933
        self::assertSame(
934
            $this->getAlterTableRenameIndexSQL(),
935
            $this->platform->getAlterTableSQL($tableDiff)
936
        );
937
    }
938
939
    /**
940
     * @return string[]
941
     *
942
     * @group DBAL-234
943
     */
944
    protected function getAlterTableRenameIndexSQL() : array
945
    {
946
        return [
947
            'DROP INDEX idx_foo',
948
            'CREATE INDEX idx_bar ON mytable (id)',
949
        ];
950
    }
951
952
    /**
953
     * @group DBAL-234
954
     */
955
    public function testQuotesAlterTableRenameIndex() : void
956
    {
957
        $tableDiff            = new TableDiff('table');
958
        $tableDiff->fromTable = new Table('table');
959
        $tableDiff->fromTable->addColumn('id', 'integer');
960
        $tableDiff->fromTable->setPrimaryKey(['id']);
961
        $tableDiff->renamedIndexes = [
962
            'create' => new Index('select', ['id']),
963
            '`foo`'  => new Index('`bar`', ['id']),
964
        ];
965
966
        self::assertSame(
967
            $this->getQuotedAlterTableRenameIndexSQL(),
968
            $this->platform->getAlterTableSQL($tableDiff)
969
        );
970
    }
971
972
    /**
973
     * @return string[]
974
     *
975
     * @group DBAL-234
976
     */
977
    protected function getQuotedAlterTableRenameIndexSQL() : array
978
    {
979
        return [
980
            'DROP INDEX "create"',
981
            'CREATE INDEX "select" ON "table" (id)',
982
            'DROP INDEX "foo"',
983
            'CREATE INDEX "bar" ON "table" (id)',
984
        ];
985
    }
986
987
    /**
988
     * @group DBAL-835
989
     */
990
    public function testQuotesAlterTableRenameColumn() : void
991
    {
992
        $fromTable = new Table('mytable');
993
994
        $fromTable->addColumn('unquoted1', 'integer', ['comment' => 'Unquoted 1']);
995
        $fromTable->addColumn('unquoted2', 'integer', ['comment' => 'Unquoted 2']);
996
        $fromTable->addColumn('unquoted3', 'integer', ['comment' => 'Unquoted 3']);
997
998
        $fromTable->addColumn('create', 'integer', ['comment' => 'Reserved keyword 1']);
999
        $fromTable->addColumn('table', 'integer', ['comment' => 'Reserved keyword 2']);
1000
        $fromTable->addColumn('select', 'integer', ['comment' => 'Reserved keyword 3']);
1001
1002
        $fromTable->addColumn('`quoted1`', 'integer', ['comment' => 'Quoted 1']);
1003
        $fromTable->addColumn('`quoted2`', 'integer', ['comment' => 'Quoted 2']);
1004
        $fromTable->addColumn('`quoted3`', 'integer', ['comment' => 'Quoted 3']);
1005
1006
        $toTable = new Table('mytable');
1007
1008
        $toTable->addColumn('unquoted', 'integer', ['comment' => 'Unquoted 1']); // unquoted -> unquoted
1009
        $toTable->addColumn('where', 'integer', ['comment' => 'Unquoted 2']); // unquoted -> reserved keyword
1010
        $toTable->addColumn('`foo`', 'integer', ['comment' => 'Unquoted 3']); // unquoted -> quoted
1011
1012
        $toTable->addColumn('reserved_keyword', 'integer', ['comment' => 'Reserved keyword 1']); // reserved keyword -> unquoted
1013
        $toTable->addColumn('from', 'integer', ['comment' => 'Reserved keyword 2']); // reserved keyword -> reserved keyword
1014
        $toTable->addColumn('`bar`', 'integer', ['comment' => 'Reserved keyword 3']); // reserved keyword -> quoted
1015
1016
        $toTable->addColumn('quoted', 'integer', ['comment' => 'Quoted 1']); // quoted -> unquoted
1017
        $toTable->addColumn('and', 'integer', ['comment' => 'Quoted 2']); // quoted -> reserved keyword
1018
        $toTable->addColumn('`baz`', 'integer', ['comment' => 'Quoted 3']); // quoted -> quoted
1019
1020
        $comparator = new Comparator();
1021
1022
        self::assertEquals(
1023
            $this->getQuotedAlterTableRenameColumnSQL(),
1024
            $this->platform->getAlterTableSQL($comparator->diffTable($fromTable, $toTable))
0 ignored issues
show
Bug introduced by
It seems like $comparator->diffTable($fromTable, $toTable) can also be of type false; however, parameter $diff of Doctrine\DBAL\Platforms\...orm::getAlterTableSQL() does only seem to accept Doctrine\DBAL\Schema\TableDiff, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1024
            $this->platform->getAlterTableSQL(/** @scrutinizer ignore-type */ $comparator->diffTable($fromTable, $toTable))
Loading history...
1025
        );
1026
    }
1027
1028
    /**
1029
     * Returns SQL statements for {@link testQuotesAlterTableRenameColumn}.
1030
     *
1031
     * @return string[]
1032
     *
1033
     * @group DBAL-835
1034
     */
1035
    abstract protected function getQuotedAlterTableRenameColumnSQL() : array;
1036
1037
    /**
1038
     * @group DBAL-835
1039
     */
1040
    public function testQuotesAlterTableChangeColumnLength() : void
1041
    {
1042
        $fromTable = new Table('mytable');
1043
1044
        $fromTable->addColumn('unquoted1', 'string', ['comment' => 'Unquoted 1', 'length' => 10]);
1045
        $fromTable->addColumn('unquoted2', 'string', ['comment' => 'Unquoted 2', 'length' => 10]);
1046
        $fromTable->addColumn('unquoted3', 'string', ['comment' => 'Unquoted 3', 'length' => 10]);
1047
1048
        $fromTable->addColumn('create', 'string', ['comment' => 'Reserved keyword 1', 'length' => 10]);
1049
        $fromTable->addColumn('table', 'string', ['comment' => 'Reserved keyword 2', 'length' => 10]);
1050
        $fromTable->addColumn('select', 'string', ['comment' => 'Reserved keyword 3', 'length' => 10]);
1051
1052
        $toTable = new Table('mytable');
1053
1054
        $toTable->addColumn('unquoted1', 'string', ['comment' => 'Unquoted 1', 'length' => 255]);
1055
        $toTable->addColumn('unquoted2', 'string', ['comment' => 'Unquoted 2', 'length' => 255]);
1056
        $toTable->addColumn('unquoted3', 'string', ['comment' => 'Unquoted 3', 'length' => 255]);
1057
1058
        $toTable->addColumn('create', 'string', ['comment' => 'Reserved keyword 1', 'length' => 255]);
1059
        $toTable->addColumn('table', 'string', ['comment' => 'Reserved keyword 2', 'length' => 255]);
1060
        $toTable->addColumn('select', 'string', ['comment' => 'Reserved keyword 3', 'length' => 255]);
1061
1062
        $comparator = new Comparator();
1063
1064
        self::assertEquals(
1065
            $this->getQuotedAlterTableChangeColumnLengthSQL(),
1066
            $this->platform->getAlterTableSQL($comparator->diffTable($fromTable, $toTable))
0 ignored issues
show
Bug introduced by
It seems like $comparator->diffTable($fromTable, $toTable) can also be of type false; however, parameter $diff of Doctrine\DBAL\Platforms\...orm::getAlterTableSQL() does only seem to accept Doctrine\DBAL\Schema\TableDiff, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1066
            $this->platform->getAlterTableSQL(/** @scrutinizer ignore-type */ $comparator->diffTable($fromTable, $toTable))
Loading history...
1067
        );
1068
    }
1069
1070
    /**
1071
     * Returns SQL statements for {@link testQuotesAlterTableChangeColumnLength}.
1072
     *
1073
     * @return string[]
1074
     *
1075
     * @group DBAL-835
1076
     */
1077
    abstract protected function getQuotedAlterTableChangeColumnLengthSQL() : array;
1078
1079
    /**
1080
     * @group DBAL-807
1081
     */
1082
    public function testAlterTableRenameIndexInSchema() : void
1083
    {
1084
        $tableDiff            = new TableDiff('myschema.mytable');
1085
        $tableDiff->fromTable = new Table('myschema.mytable');
1086
        $tableDiff->fromTable->addColumn('id', 'integer');
1087
        $tableDiff->fromTable->setPrimaryKey(['id']);
1088
        $tableDiff->renamedIndexes = [
1089
            'idx_foo' => new Index('idx_bar', ['id']),
1090
        ];
1091
1092
        self::assertSame(
1093
            $this->getAlterTableRenameIndexInSchemaSQL(),
1094
            $this->platform->getAlterTableSQL($tableDiff)
1095
        );
1096
    }
1097
1098
    /**
1099
     * @return string[]
1100
     *
1101
     * @group DBAL-807
1102
     */
1103
    protected function getAlterTableRenameIndexInSchemaSQL() : array
1104
    {
1105
        return [
1106
            'DROP INDEX idx_foo',
1107
            'CREATE INDEX idx_bar ON myschema.mytable (id)',
1108
        ];
1109
    }
1110
1111
    /**
1112
     * @group DBAL-807
1113
     */
1114
    public function testQuotesAlterTableRenameIndexInSchema() : void
1115
    {
1116
        $tableDiff            = new TableDiff('`schema`.table');
1117
        $tableDiff->fromTable = new Table('`schema`.table');
1118
        $tableDiff->fromTable->addColumn('id', 'integer');
1119
        $tableDiff->fromTable->setPrimaryKey(['id']);
1120
        $tableDiff->renamedIndexes = [
1121
            'create' => new Index('select', ['id']),
1122
            '`foo`'  => new Index('`bar`', ['id']),
1123
        ];
1124
1125
        self::assertSame(
1126
            $this->getQuotedAlterTableRenameIndexInSchemaSQL(),
1127
            $this->platform->getAlterTableSQL($tableDiff)
1128
        );
1129
    }
1130
1131
    /**
1132
     * @return string[]
1133
     *
1134
     * @group DBAL-234
1135
     */
1136
    protected function getQuotedAlterTableRenameIndexInSchemaSQL() : array
1137
    {
1138
        return [
1139
            'DROP INDEX "schema"."create"',
1140
            'CREATE INDEX "select" ON "schema"."table" (id)',
1141
            'DROP INDEX "schema"."foo"',
1142
            'CREATE INDEX "bar" ON "schema"."table" (id)',
1143
        ];
1144
    }
1145
1146
    /**
1147
     * @group DBAL-1237
1148
     */
1149
    public function testQuotesDropForeignKeySQL() : void
1150
    {
1151
        if (! $this->platform->supportsForeignKeyConstraints()) {
1152
            $this->markTestSkipped(
1153
                sprintf('%s does not support foreign key constraints.', get_class($this->platform))
1154
            );
1155
        }
1156
1157
        $tableName      = 'table';
1158
        $table          = new Table($tableName);
1159
        $foreignKeyName = 'select';
1160
        $foreignKey     = new ForeignKeyConstraint([], 'foo', [], 'select');
1161
        $expectedSql    = $this->getQuotesDropForeignKeySQL();
1162
1163
        self::assertSame($expectedSql, $this->platform->getDropForeignKeySQL($foreignKeyName, $tableName));
1164
        self::assertSame($expectedSql, $this->platform->getDropForeignKeySQL($foreignKey, $table));
1165
    }
1166
1167
    protected function getQuotesDropForeignKeySQL() : string
1168
    {
1169
        return 'ALTER TABLE "table" DROP FOREIGN KEY "select"';
1170
    }
1171
1172
    /**
1173
     * @group DBAL-1237
1174
     */
1175
    public function testQuotesDropConstraintSQL() : void
1176
    {
1177
        $tableName      = 'table';
1178
        $table          = new Table($tableName);
1179
        $constraintName = 'select';
1180
        $constraint     = new ForeignKeyConstraint([], 'foo', [], 'select');
1181
        $expectedSql    = $this->getQuotesDropConstraintSQL();
1182
1183
        self::assertSame($expectedSql, $this->platform->getDropConstraintSQL($constraintName, $tableName));
1184
        self::assertSame($expectedSql, $this->platform->getDropConstraintSQL($constraint, $table));
1185
    }
1186
1187
    protected function getQuotesDropConstraintSQL() : string
1188
    {
1189
        return 'ALTER TABLE "table" DROP CONSTRAINT "select"';
1190
    }
1191
1192
    protected function getStringLiteralQuoteCharacter() : string
1193
    {
1194
        return "'";
1195
    }
1196
1197
    public function testGetStringLiteralQuoteCharacter() : void
1198
    {
1199
        self::assertSame($this->getStringLiteralQuoteCharacter(), $this->platform->getStringLiteralQuoteCharacter());
1200
    }
1201
1202
    protected function getQuotedCommentOnColumnSQLWithoutQuoteCharacter() : string
1203
    {
1204
        return "COMMENT ON COLUMN mytable.id IS 'This is a comment'";
1205
    }
1206
1207
    public function testGetCommentOnColumnSQLWithoutQuoteCharacter() : void
1208
    {
1209
        self::assertEquals(
1210
            $this->getQuotedCommentOnColumnSQLWithoutQuoteCharacter(),
1211
            $this->platform->getCommentOnColumnSQL('mytable', 'id', 'This is a comment')
1212
        );
1213
    }
1214
1215
    protected function getQuotedCommentOnColumnSQLWithQuoteCharacter() : string
1216
    {
1217
        return "COMMENT ON COLUMN mytable.id IS 'It''s a quote !'";
1218
    }
1219
1220
    public function testGetCommentOnColumnSQLWithQuoteCharacter() : void
1221
    {
1222
        $c = $this->getStringLiteralQuoteCharacter();
1223
1224
        self::assertEquals(
1225
            $this->getQuotedCommentOnColumnSQLWithQuoteCharacter(),
1226
            $this->platform->getCommentOnColumnSQL('mytable', 'id', 'It' . $c . 's a quote !')
1227
        );
1228
    }
1229
1230
    /**
1231
     * @see testGetCommentOnColumnSQL
1232
     *
1233
     * @return string[]
1234
     */
1235
    abstract protected function getCommentOnColumnSQL() : array;
1236
1237
    /**
1238
     * @group DBAL-1004
1239
     */
1240
    public function testGetCommentOnColumnSQL() : void
1241
    {
1242
        self::assertSame(
1243
            $this->getCommentOnColumnSQL(),
1244
            [
1245
                $this->platform->getCommentOnColumnSQL('foo', 'bar', 'comment'), // regular identifiers
1246
                $this->platform->getCommentOnColumnSQL('`Foo`', '`BAR`', 'comment'), // explicitly quoted identifiers
1247
                $this->platform->getCommentOnColumnSQL('select', 'from', 'comment'), // reserved keyword identifiers
1248
            ]
1249
        );
1250
    }
1251
1252
    /**
1253
     * @group DBAL-1176
1254
     * @dataProvider getGeneratesInlineColumnCommentSQL
1255
     */
1256
    public function testGeneratesInlineColumnCommentSQL(?string $comment, string $expectedSql) : void
1257
    {
1258
        if (! $this->platform->supportsInlineColumnComments()) {
1259
            $this->markTestSkipped(sprintf('%s does not support inline column comments.', get_class($this->platform)));
1260
        }
1261
1262
        self::assertSame($expectedSql, $this->platform->getInlineColumnCommentSQL($comment));
1263
    }
1264
1265
    /**
1266
     * @return mixed[][]
1267
     */
1268
    public static function getGeneratesInlineColumnCommentSQL() : iterable
1269
    {
1270
        return [
1271
            'regular comment' => ['Regular comment', static::getInlineColumnRegularCommentSQL()],
1272
            'comment requiring escaping' => [
1273
                sprintf(
1274
                    'Using inline comment delimiter %s works',
1275
                    static::getInlineColumnCommentDelimiter()
1276
                ),
1277
                static::getInlineColumnCommentRequiringEscapingSQL(),
1278
            ],
1279
            'empty comment' => ['', static::getInlineColumnEmptyCommentSQL()],
1280
        ];
1281
    }
1282
1283
    protected static function getInlineColumnCommentDelimiter() : string
1284
    {
1285
        return "'";
1286
    }
1287
1288
    protected static function getInlineColumnRegularCommentSQL() : string
1289
    {
1290
        return "COMMENT 'Regular comment'";
1291
    }
1292
1293
    protected static function getInlineColumnCommentRequiringEscapingSQL() : string
1294
    {
1295
        return "COMMENT 'Using inline comment delimiter '' works'";
1296
    }
1297
1298
    protected static function getInlineColumnEmptyCommentSQL() : string
1299
    {
1300
        return "COMMENT ''";
1301
    }
1302
1303
    protected function getQuotedStringLiteralWithoutQuoteCharacter() : string
1304
    {
1305
        return "'No quote'";
1306
    }
1307
1308
    protected function getQuotedStringLiteralWithQuoteCharacter() : string
1309
    {
1310
        return "'It''s a quote'";
1311
    }
1312
1313
    protected function getQuotedStringLiteralQuoteCharacter() : string
1314
    {
1315
        return "''''";
1316
    }
1317
1318
    /**
1319
     * @group DBAL-1176
1320
     */
1321
    public function testThrowsExceptionOnGeneratingInlineColumnCommentSQLIfUnsupported() : void
1322
    {
1323
        if ($this->platform->supportsInlineColumnComments()) {
1324
            $this->markTestSkipped(sprintf('%s supports inline column comments.', get_class($this->platform)));
1325
        }
1326
1327
        $this->expectException(DBALException::class);
1328
        $this->expectExceptionMessage("Operation 'Doctrine\\DBAL\\Platforms\\AbstractPlatform::getInlineColumnCommentSQL' is not supported by platform.");
1329
        $this->expectExceptionCode(0);
1330
1331
        $this->platform->getInlineColumnCommentSQL('unsupported');
1332
    }
1333
1334
    public function testQuoteStringLiteral() : void
1335
    {
1336
        $c = $this->getStringLiteralQuoteCharacter();
1337
1338
        self::assertEquals(
1339
            $this->getQuotedStringLiteralWithoutQuoteCharacter(),
1340
            $this->platform->quoteStringLiteral('No quote')
1341
        );
1342
        self::assertEquals(
1343
            $this->getQuotedStringLiteralWithQuoteCharacter(),
1344
            $this->platform->quoteStringLiteral('It' . $c . 's a quote')
1345
        );
1346
        self::assertEquals(
1347
            $this->getQuotedStringLiteralQuoteCharacter(),
1348
            $this->platform->quoteStringLiteral($c)
1349
        );
1350
    }
1351
1352
    /**
1353
     * @group DBAL-423
1354
     */
1355
    public function testReturnsGuidTypeDeclarationSQL() : void
1356
    {
1357
        $this->expectException(DBALException::class);
1358
1359
        $this->platform->getGuidTypeDeclarationSQL([]);
1360
    }
1361
1362
    /**
1363
     * @group DBAL-1010
1364
     */
1365
    public function testGeneratesAlterTableRenameColumnSQL() : void
1366
    {
1367
        $table = new Table('foo');
1368
        $table->addColumn(
1369
            'bar',
1370
            'integer',
1371
            ['notnull' => true, 'default' => 666, 'comment' => 'rename test']
1372
        );
1373
1374
        $tableDiff                        = new TableDiff('foo');
1375
        $tableDiff->fromTable             = $table;
1376
        $tableDiff->renamedColumns['bar'] = new Column(
1377
            'baz',
1378
            Type::getType('integer'),
1379
            ['notnull' => true, 'default' => 666, 'comment' => 'rename test']
1380
        );
1381
1382
        self::assertSame($this->getAlterTableRenameColumnSQL(), $this->platform->getAlterTableSQL($tableDiff));
1383
    }
1384
1385
    /**
1386
     * @return string[]
1387
     */
1388
    abstract public function getAlterTableRenameColumnSQL() : array;
1389
1390
    /**
1391
     * @group DBAL-1016
1392
     */
1393
    public function testQuotesTableIdentifiersInAlterTableSQL() : void
1394
    {
1395
        $table = new Table('"foo"');
1396
        $table->addColumn('id', 'integer');
1397
        $table->addColumn('fk', 'integer');
1398
        $table->addColumn('fk2', 'integer');
1399
        $table->addColumn('fk3', 'integer');
1400
        $table->addColumn('bar', 'integer');
1401
        $table->addColumn('baz', 'integer');
1402
        $table->addForeignKeyConstraint('fk_table', ['fk'], ['id'], [], 'fk1');
1403
        $table->addForeignKeyConstraint('fk_table', ['fk2'], ['id'], [], 'fk2');
1404
1405
        $tableDiff                        = new TableDiff('"foo"');
1406
        $tableDiff->fromTable             = $table;
1407
        $tableDiff->newName               = 'table';
1408
        $tableDiff->addedColumns['bloo']  = new Column('bloo', Type::getType('integer'));
1409
        $tableDiff->changedColumns['bar'] = new ColumnDiff(
1410
            'bar',
1411
            new Column('bar', Type::getType('integer'), ['notnull' => false]),
1412
            ['notnull'],
1413
            $table->getColumn('bar')
1414
        );
1415
        $tableDiff->renamedColumns['id']  = new Column('war', Type::getType('integer'));
1416
        $tableDiff->removedColumns['baz'] = new Column('baz', Type::getType('integer'));
1417
        $tableDiff->addedForeignKeys[]    = new ForeignKeyConstraint(['fk3'], 'fk_table', ['id'], 'fk_add');
1418
        $tableDiff->changedForeignKeys[]  = new ForeignKeyConstraint(['fk2'], 'fk_table2', ['id'], 'fk2');
1419
        $tableDiff->removedForeignKeys[]  = new ForeignKeyConstraint(['fk'], 'fk_table', ['id'], 'fk1');
1420
1421
        self::assertSame(
1422
            $this->getQuotesTableIdentifiersInAlterTableSQL(),
1423
            $this->platform->getAlterTableSQL($tableDiff)
1424
        );
1425
    }
1426
1427
    /**
1428
     * @return string[]
1429
     */
1430
    abstract protected function getQuotesTableIdentifiersInAlterTableSQL() : array;
1431
1432
    /**
1433
     * @group DBAL-1090
1434
     */
1435
    public function testAlterStringToFixedString() : void
1436
    {
1437
        $table = new Table('mytable');
1438
        $table->addColumn('name', 'string', ['length' => 2]);
1439
1440
        $tableDiff            = new TableDiff('mytable');
1441
        $tableDiff->fromTable = $table;
1442
1443
        $tableDiff->changedColumns['name'] = new ColumnDiff(
1444
            'name',
1445
            new Column(
1446
                'name',
1447
                Type::getType('string'),
1448
                ['fixed' => true, 'length' => 2]
1449
            ),
1450
            ['fixed']
1451
        );
1452
1453
        $sql = $this->platform->getAlterTableSQL($tableDiff);
1454
1455
        $expectedSql = $this->getAlterStringToFixedStringSQL();
1456
1457
        self::assertEquals($expectedSql, $sql);
1458
    }
1459
1460
    /**
1461
     * @return string[]
1462
     */
1463
    abstract protected function getAlterStringToFixedStringSQL() : array;
1464
1465
    /**
1466
     * @group DBAL-1062
1467
     */
1468
    public function testGeneratesAlterTableRenameIndexUsedByForeignKeySQL() : void
1469
    {
1470
        $foreignTable = new Table('foreign_table');
1471
        $foreignTable->addColumn('id', 'integer');
1472
        $foreignTable->setPrimaryKey(['id']);
1473
1474
        $primaryTable = new Table('mytable');
1475
        $primaryTable->addColumn('foo', 'integer');
1476
        $primaryTable->addColumn('bar', 'integer');
1477
        $primaryTable->addColumn('baz', 'integer');
1478
        $primaryTable->addIndex(['foo'], 'idx_foo');
1479
        $primaryTable->addIndex(['bar'], 'idx_bar');
1480
        $primaryTable->addForeignKeyConstraint($foreignTable, ['foo'], ['id'], [], 'fk_foo');
1481
        $primaryTable->addForeignKeyConstraint($foreignTable, ['bar'], ['id'], [], 'fk_bar');
1482
1483
        $tableDiff                            = new TableDiff('mytable');
1484
        $tableDiff->fromTable                 = $primaryTable;
1485
        $tableDiff->renamedIndexes['idx_foo'] = new Index('idx_foo_renamed', ['foo']);
1486
1487
        self::assertSame(
1488
            $this->getGeneratesAlterTableRenameIndexUsedByForeignKeySQL(),
1489
            $this->platform->getAlterTableSQL($tableDiff)
1490
        );
1491
    }
1492
1493
    /**
1494
     * @return string[]
1495
     */
1496
    abstract protected function getGeneratesAlterTableRenameIndexUsedByForeignKeySQL() : array;
1497
1498
    /**
1499
     * @param mixed[] $column
1500
     *
1501
     * @group DBAL-1082
1502
     * @dataProvider getGeneratesDecimalTypeDeclarationSQL
1503
     */
1504
    public function testGeneratesDecimalTypeDeclarationSQL(array $column, string $expectedSql) : void
1505
    {
1506
        self::assertSame($expectedSql, $this->platform->getDecimalTypeDeclarationSQL($column));
1507
    }
1508
1509
    /**
1510
     * @return mixed[][]
1511
     */
1512
    public static function getGeneratesDecimalTypeDeclarationSQL() : iterable
1513
    {
1514
        return [
1515
            [[], 'NUMERIC(10, 0)'],
1516
            [['unsigned' => true], 'NUMERIC(10, 0)'],
1517
            [['unsigned' => false], 'NUMERIC(10, 0)'],
1518
            [['precision' => 5], 'NUMERIC(5, 0)'],
1519
            [['scale' => 5], 'NUMERIC(10, 5)'],
1520
            [['precision' => 8, 'scale' => 2], 'NUMERIC(8, 2)'],
1521
        ];
1522
    }
1523
1524
    /**
1525
     * @param mixed[] $column
1526
     *
1527
     * @group DBAL-1082
1528
     * @dataProvider getGeneratesFloatDeclarationSQL
1529
     */
1530
    public function testGeneratesFloatDeclarationSQL(array $column, string $expectedSql) : void
1531
    {
1532
        self::assertSame($expectedSql, $this->platform->getFloatDeclarationSQL($column));
1533
    }
1534
1535
    /**
1536
     * @return mixed[][]
1537
     */
1538
    public static function getGeneratesFloatDeclarationSQL() : iterable
1539
    {
1540
        return [
1541
            [[], 'DOUBLE PRECISION'],
1542
            [['unsigned' => true], 'DOUBLE PRECISION'],
1543
            [['unsigned' => false], 'DOUBLE PRECISION'],
1544
            [['precision' => 5], 'DOUBLE PRECISION'],
1545
            [['scale' => 5], 'DOUBLE PRECISION'],
1546
            [['precision' => 8, 'scale' => 2], 'DOUBLE PRECISION'],
1547
        ];
1548
    }
1549
1550
    public function testItEscapesStringsForLike() : void
1551
    {
1552
        self::assertSame(
1553
            '\_25\% off\_ your next purchase \\\\o/',
1554
            $this->platform->escapeStringForLike('_25% off_ your next purchase \o/', '\\')
1555
        );
1556
    }
1557
1558
    public function testZeroOffsetWithoutLimitIsIgnored() : void
1559
    {
1560
        $query = 'SELECT * FROM user';
1561
1562
        self::assertSame(
1563
            $query,
1564
            $this->platform->modifyLimitQuery($query, null, 0)
1565
        );
1566
    }
1567
}
1568