Failed Conditions
Pull Request — 2.10 (#3762)
by Benjamin
09:16
created

testGeneratesForeignKeySqlOnlyWhenSupportingForeignKeys()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 6
dl 0
loc 9
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 0

1 Method

Rating   Name   Duplication   Size   Complexity  
A AbstractPlatformTestCase::getBitAndComparisonExpressionSql() 0 3 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
        if (! $this->platform->supportsCreateDropForeignKeyConstraints()) {
258
            $this->markTestSkipped('Platform does not support creating or dropping foreign key constraints.');
259
        }
260
261
        $idx = new Index('constraint_name', ['test'], true, false);
262
        $sql = $this->platform->getCreateConstraintSQL($idx, 'test');
263
        self::assertEquals($this->getGenerateConstraintUniqueIndexSql(), $sql);
264
265
        $pk  = new Index('constraint_name', ['test'], true, true);
266
        $sql = $this->platform->getCreateConstraintSQL($pk, 'test');
267
        self::assertEquals($this->getGenerateConstraintPrimaryIndexSql(), $sql);
268
269
        $fk  = new ForeignKeyConstraint(['fk_name'], 'foreign', ['id'], 'constraint_fk');
270
        $sql = $this->platform->getCreateConstraintSQL($fk, 'test');
271
        self::assertEquals($this->getGenerateConstraintForeignKeySql($fk), $sql);
272
    }
273
274
    protected function getBitAndComparisonExpressionSql(string $value1, string $value2) : string
275
    {
276
        return '(' . $value1 . ' & ' . $value2 . ')';
277
    }
278
279
    /**
280
     * @group DDC-1213
281
     */
282
    public function testGeneratesBitAndComparisonExpressionSql() : void
283
    {
284
        $sql = $this->platform->getBitAndComparisonExpression(2, 4);
285
        self::assertEquals($this->getBitAndComparisonExpressionSql(2, 4), $sql);
286
    }
287
288
    protected function getBitOrComparisonExpressionSql(string $value1, string $value2) : string
289
    {
290
        return '(' . $value1 . ' | ' . $value2 . ')';
291
    }
292
293
    /**
294
     * @group DDC-1213
295
     */
296
    public function testGeneratesBitOrComparisonExpressionSql() : void
297
    {
298
        $sql = $this->platform->getBitOrComparisonExpression(2, 4);
299
        self::assertEquals($this->getBitOrComparisonExpressionSql(2, 4), $sql);
300
    }
301
302
    public function getGenerateConstraintUniqueIndexSql() : string
303
    {
304
        return 'ALTER TABLE test ADD CONSTRAINT constraint_name UNIQUE (test)';
305
    }
306
307
    public function getGenerateConstraintPrimaryIndexSql() : string
308
    {
309
        return 'ALTER TABLE test ADD CONSTRAINT constraint_name PRIMARY KEY (test)';
310
    }
311
312
    public function getGenerateConstraintForeignKeySql(ForeignKeyConstraint $fk) : string
313
    {
314
        $quotedForeignTable = $fk->getQuotedForeignTableName($this->platform);
315
316
        return sprintf(
317
            'ALTER TABLE test ADD CONSTRAINT constraint_fk FOREIGN KEY (fk_name) REFERENCES %s (id)',
318
            $quotedForeignTable
319
        );
320
    }
321
322
    /**
323
     * @return string[]
324
     */
325
    abstract public function getGenerateAlterTableSql() : array;
326
327
    public function testGeneratesTableAlterationSql() : void
328
    {
329
        $expectedSql = $this->getGenerateAlterTableSql();
330
331
        $table = new Table('mytable');
332
        $table->addColumn('id', 'integer', ['autoincrement' => true]);
333
        $table->addColumn('foo', 'integer');
334
        $table->addColumn('bar', 'string');
335
        $table->addColumn('bloo', 'boolean');
336
        $table->setPrimaryKey(['id']);
337
338
        $tableDiff                         = new TableDiff('mytable');
339
        $tableDiff->fromTable              = $table;
340
        $tableDiff->newName                = 'userlist';
341
        $tableDiff->addedColumns['quota']  = new Column('quota', Type::getType('integer'), ['notnull' => false]);
342
        $tableDiff->removedColumns['foo']  = new Column('foo', Type::getType('integer'));
343
        $tableDiff->changedColumns['bar']  = new ColumnDiff(
344
            'bar',
345
            new Column(
346
                'baz',
347
                Type::getType('string'),
348
                ['default' => 'def']
349
            ),
350
            ['type', 'notnull', 'default']
351
        );
352
        $tableDiff->changedColumns['bloo'] = new ColumnDiff(
353
            'bloo',
354
            new Column(
355
                'bloo',
356
                Type::getType('boolean'),
357
                ['default' => false]
358
            ),
359
            ['type', 'notnull', 'default']
360
        );
361
362
        $sql = $this->platform->getAlterTableSQL($tableDiff);
363
364
        self::assertEquals($expectedSql, $sql);
365
    }
366
367
    public function testGetCustomColumnDeclarationSql() : void
368
    {
369
        $field = ['columnDefinition' => 'MEDIUMINT(6) UNSIGNED'];
370
        self::assertEquals('foo MEDIUMINT(6) UNSIGNED', $this->platform->getColumnDeclarationSQL('foo', $field));
371
    }
372
373
    public function testGetCreateTableSqlDispatchEvent() : void
374
    {
375
        $listenerMock = $this->getMockBuilder($this->getMockClass('GetCreateTableSqlDispatchEvenListener'))
376
            ->addMethods(['onSchemaCreateTable', 'onSchemaCreateTableColumn'])
377
            ->getMock();
378
        $listenerMock
379
            ->expects($this->once())
380
            ->method('onSchemaCreateTable');
381
        $listenerMock
382
            ->expects($this->exactly(2))
383
            ->method('onSchemaCreateTableColumn');
384
385
        $eventManager = new EventManager();
386
        $eventManager->addEventListener([Events::onSchemaCreateTable, Events::onSchemaCreateTableColumn], $listenerMock);
387
388
        $this->platform->setEventManager($eventManager);
389
390
        $table = new Table('test');
391
        $table->addColumn('foo', 'string', ['notnull' => false, 'length' => 255]);
392
        $table->addColumn('bar', 'string', ['notnull' => false, 'length' => 255]);
393
394
        $this->platform->getCreateTableSQL($table);
395
    }
396
397
    public function testGetDropTableSqlDispatchEvent() : void
398
    {
399
        $listenerMock = $this->getMockBuilder($this->getMockClass('GetDropTableSqlDispatchEventListener'))
400
            ->addMethods(['onSchemaDropTable'])
401
            ->getMock();
402
        $listenerMock
403
            ->expects($this->once())
404
            ->method('onSchemaDropTable');
405
406
        $eventManager = new EventManager();
407
        $eventManager->addEventListener([Events::onSchemaDropTable], $listenerMock);
408
409
        $this->platform->setEventManager($eventManager);
410
411
        $this->platform->getDropTableSQL('TABLE');
412
    }
413
414
    public function testGetAlterTableSqlDispatchEvent() : void
415
    {
416
        $events = [
417
            'onSchemaAlterTable',
418
            'onSchemaAlterTableAddColumn',
419
            'onSchemaAlterTableRemoveColumn',
420
            'onSchemaAlterTableChangeColumn',
421
            'onSchemaAlterTableRenameColumn',
422
        ];
423
424
        $listenerMock = $this->getMockBuilder($this->getMockClass('GetAlterTableSqlDispatchEvenListener'))
425
            ->addMethods($events)
426
            ->getMock();
427
        $listenerMock
428
            ->expects($this->once())
429
            ->method('onSchemaAlterTable');
430
        $listenerMock
431
            ->expects($this->once())
432
            ->method('onSchemaAlterTableAddColumn');
433
        $listenerMock
434
            ->expects($this->once())
435
            ->method('onSchemaAlterTableRemoveColumn');
436
        $listenerMock
437
            ->expects($this->once())
438
            ->method('onSchemaAlterTableChangeColumn');
439
        $listenerMock
440
            ->expects($this->once())
441
            ->method('onSchemaAlterTableRenameColumn');
442
443
        $eventManager = new EventManager();
444
        $events       = [
445
            Events::onSchemaAlterTable,
446
            Events::onSchemaAlterTableAddColumn,
447
            Events::onSchemaAlterTableRemoveColumn,
448
            Events::onSchemaAlterTableChangeColumn,
449
            Events::onSchemaAlterTableRenameColumn,
450
        ];
451
        $eventManager->addEventListener($events, $listenerMock);
452
453
        $this->platform->setEventManager($eventManager);
454
455
        $table = new Table('mytable');
456
        $table->addColumn('removed', 'integer');
457
        $table->addColumn('changed', 'integer');
458
        $table->addColumn('renamed', 'integer');
459
460
        $tableDiff                            = new TableDiff('mytable');
461
        $tableDiff->fromTable                 = $table;
462
        $tableDiff->addedColumns['added']     = new Column('added', Type::getType('integer'), []);
463
        $tableDiff->removedColumns['removed'] = new Column('removed', Type::getType('integer'), []);
464
        $tableDiff->changedColumns['changed'] = new ColumnDiff(
465
            'changed',
466
            new Column(
467
                'changed2',
468
                Type::getType('string'),
469
                []
470
            ),
471
            []
472
        );
473
        $tableDiff->renamedColumns['renamed'] = new Column('renamed2', Type::getType('integer'), []);
474
475
        $this->platform->getAlterTableSQL($tableDiff);
476
    }
477
478
    /**
479
     * @group DBAL-42
480
     */
481
    public function testCreateTableColumnComments() : void
482
    {
483
        $table = new Table('test');
484
        $table->addColumn('id', 'integer', ['comment' => 'This is a comment']);
485
        $table->setPrimaryKey(['id']);
486
487
        self::assertEquals($this->getCreateTableColumnCommentsSQL(), $this->platform->getCreateTableSQL($table));
488
    }
489
490
    /**
491
     * @group DBAL-42
492
     */
493
    public function testAlterTableColumnComments() : void
494
    {
495
        $tableDiff                        = new TableDiff('mytable');
496
        $tableDiff->addedColumns['quota'] = new Column('quota', Type::getType('integer'), ['comment' => 'A comment']);
497
        $tableDiff->changedColumns['foo'] = new ColumnDiff(
498
            'foo',
499
            new Column(
500
                'foo',
501
                Type::getType('string')
502
            ),
503
            ['comment']
504
        );
505
        $tableDiff->changedColumns['bar'] = new ColumnDiff(
506
            'bar',
507
            new Column(
508
                'baz',
509
                Type::getType('string'),
510
                ['comment' => 'B comment']
511
            ),
512
            ['comment']
513
        );
514
515
        self::assertEquals($this->getAlterTableColumnCommentsSQL(), $this->platform->getAlterTableSQL($tableDiff));
516
    }
517
518
    public function testCreateTableColumnTypeComments() : void
519
    {
520
        $table = new Table('test');
521
        $table->addColumn('id', 'integer');
522
        $table->addColumn('data', 'array');
523
        $table->setPrimaryKey(['id']);
524
525
        self::assertEquals($this->getCreateTableColumnTypeCommentsSQL(), $this->platform->getCreateTableSQL($table));
526
    }
527
528
    /**
529
     * @return string[]
530
     */
531
    public function getCreateTableColumnCommentsSQL() : array
532
    {
533
        $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...
534
    }
535
536
    /**
537
     * @return string[]
538
     */
539
    public function getAlterTableColumnCommentsSQL() : 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 getCreateTableColumnTypeCommentsSQL() : 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
    public function testGetDefaultValueDeclarationSQL() : void
553
    {
554
        // non-timestamp value will get single quotes
555
        $field = [
556
            'type' => Type::getType('string'),
557
            'default' => 'non_timestamp',
558
        ];
559
560
        self::assertEquals(" DEFAULT 'non_timestamp'", $this->platform->getDefaultValueDeclarationSQL($field));
561
    }
562
563
    /**
564
     * @group 2859
565
     */
566
    public function testGetDefaultValueDeclarationSQLDateTime() : void
567
    {
568
        // timestamps on datetime types should not be quoted
569
        foreach (['datetime', 'datetimetz', 'datetime_immutable', 'datetimetz_immutable'] as $type) {
570
            $field = [
571
                'type'    => Type::getType($type),
572
                'default' => $this->platform->getCurrentTimestampSQL(),
573
            ];
574
575
            self::assertSame(
576
                ' DEFAULT ' . $this->platform->getCurrentTimestampSQL(),
577
                $this->platform->getDefaultValueDeclarationSQL($field)
578
            );
579
        }
580
    }
581
582
    public function testGetDefaultValueDeclarationSQLForIntegerTypes() : void
583
    {
584
        foreach (['bigint', 'integer', 'smallint'] as $type) {
585
            $field = [
586
                'type'    => Type::getType($type),
587
                'default' => 1,
588
            ];
589
590
            self::assertEquals(
591
                ' DEFAULT 1',
592
                $this->platform->getDefaultValueDeclarationSQL($field)
593
            );
594
        }
595
    }
596
597
    /**
598
     * @group 2859
599
     */
600
    public function testGetDefaultValueDeclarationSQLForDateType() : void
601
    {
602
        $currentDateSql = $this->platform->getCurrentDateSQL();
603
        foreach (['date', 'date_immutable'] as $type) {
604
            $field = [
605
                'type'    => Type::getType($type),
606
                'default' => $currentDateSql,
607
            ];
608
609
            self::assertSame(
610
                ' DEFAULT ' . $currentDateSql,
611
                $this->platform->getDefaultValueDeclarationSQL($field)
612
            );
613
        }
614
    }
615
616
    /**
617
     * @group DBAL-45
618
     */
619
    public function testKeywordList() : void
620
    {
621
        $keywordList = $this->platform->getReservedKeywordsList();
622
        self::assertInstanceOf(KeywordList::class, $keywordList);
623
624
        self::assertTrue($keywordList->isKeyword('table'));
625
    }
626
627
    /**
628
     * @group DBAL-374
629
     */
630
    public function testQuotedColumnInPrimaryKeyPropagation() : void
631
    {
632
        $table = new Table('`quoted`');
633
        $table->addColumn('create', 'string');
634
        $table->setPrimaryKey(['create']);
635
636
        $sql = $this->platform->getCreateTableSQL($table);
637
        self::assertEquals($this->getQuotedColumnInPrimaryKeySQL(), $sql);
638
    }
639
640
    /**
641
     * @return string[]
642
     */
643
    abstract protected function getQuotedColumnInPrimaryKeySQL() : array;
644
645
    /**
646
     * @return string[]
647
     */
648
    abstract protected function getQuotedColumnInIndexSQL() : array;
649
650
    /**
651
     * @return string[]
652
     */
653
    abstract protected function getQuotedNameInIndexSQL() : array;
654
655
    /**
656
     * @return string[]
657
     */
658
    abstract protected function getQuotedColumnInForeignKeySQL() : array;
659
660
    /**
661
     * @group DBAL-374
662
     */
663
    public function testQuotedColumnInIndexPropagation() : void
664
    {
665
        $table = new Table('`quoted`');
666
        $table->addColumn('create', 'string');
667
        $table->addIndex(['create']);
668
669
        $sql = $this->platform->getCreateTableSQL($table);
670
        self::assertEquals($this->getQuotedColumnInIndexSQL(), $sql);
671
    }
672
673
    public function testQuotedNameInIndexSQL() : void
674
    {
675
        $table = new Table('test');
676
        $table->addColumn('column1', 'string');
677
        $table->addIndex(['column1'], '`key`');
678
679
        $sql = $this->platform->getCreateTableSQL($table);
680
        self::assertEquals($this->getQuotedNameInIndexSQL(), $sql);
681
    }
682
683
    /**
684
     * @group DBAL-374
685
     */
686
    public function testQuotedColumnInForeignKeyPropagation() : void
687
    {
688
        $table = new Table('`quoted`');
689
        $table->addColumn('create', 'string');
690
        $table->addColumn('foo', 'string');
691
        $table->addColumn('`bar`', 'string');
692
693
        // Foreign table with reserved keyword as name (needs quotation).
694
        $foreignTable = new Table('foreign');
695
        $foreignTable->addColumn('create', 'string');    // Foreign column with reserved keyword as name (needs quotation).
696
        $foreignTable->addColumn('bar', 'string');       // Foreign column with non-reserved keyword as name (does not need quotation).
697
        $foreignTable->addColumn('`foo-bar`', 'string'); // Foreign table with special character in name (needs quotation on some platforms, e.g. Sqlite).
698
699
        $table->addForeignKeyConstraint($foreignTable, ['create', 'foo', '`bar`'], ['create', 'bar', '`foo-bar`'], [], 'FK_WITH_RESERVED_KEYWORD');
700
701
        // Foreign table with non-reserved keyword as name (does not need quotation).
702
        $foreignTable = new Table('foo');
703
        $foreignTable->addColumn('create', 'string');    // Foreign column with reserved keyword as name (needs quotation).
704
        $foreignTable->addColumn('bar', 'string');       // Foreign column with non-reserved keyword as name (does not need quotation).
705
        $foreignTable->addColumn('`foo-bar`', 'string'); // Foreign table with special character in name (needs quotation on some platforms, e.g. Sqlite).
706
707
        $table->addForeignKeyConstraint($foreignTable, ['create', 'foo', '`bar`'], ['create', 'bar', '`foo-bar`'], [], 'FK_WITH_NON_RESERVED_KEYWORD');
708
709
        // Foreign table with special character in name (needs quotation on some platforms, e.g. Sqlite).
710
        $foreignTable = new Table('`foo-bar`');
711
        $foreignTable->addColumn('create', 'string');    // Foreign column with reserved keyword as name (needs quotation).
712
        $foreignTable->addColumn('bar', 'string');       // Foreign column with non-reserved keyword as name (does not need quotation).
713
        $foreignTable->addColumn('`foo-bar`', 'string'); // Foreign table with special character in name (needs quotation on some platforms, e.g. Sqlite).
714
715
        $table->addForeignKeyConstraint($foreignTable, ['create', 'foo', '`bar`'], ['create', 'bar', '`foo-bar`'], [], 'FK_WITH_INTENDED_QUOTATION');
716
717
        $sql = $this->platform->getCreateTableSQL($table, AbstractPlatform::CREATE_FOREIGNKEYS);
718
        self::assertEquals($this->getQuotedColumnInForeignKeySQL(), $sql);
719
    }
720
721
    /**
722
     * @group DBAL-1051
723
     */
724
    public function testQuotesReservedKeywordInUniqueConstraintDeclarationSQL() : void
725
    {
726
        $index = new Index('select', ['foo'], true);
727
728
        self::assertSame(
729
            $this->getQuotesReservedKeywordInUniqueConstraintDeclarationSQL(),
730
            $this->platform->getUniqueConstraintDeclarationSQL('select', $index)
731
        );
732
    }
733
734
    abstract protected function getQuotesReservedKeywordInUniqueConstraintDeclarationSQL() : string;
735
736
    /**
737
     * @group DBAL-2270
738
     */
739
    public function testQuotesReservedKeywordInTruncateTableSQL() : void
740
    {
741
        self::assertSame(
742
            $this->getQuotesReservedKeywordInTruncateTableSQL(),
743
            $this->platform->getTruncateTableSQL('select')
744
        );
745
    }
746
747
    abstract protected function getQuotesReservedKeywordInTruncateTableSQL() : string;
748
749
    /**
750
     * @group DBAL-1051
751
     */
752
    public function testQuotesReservedKeywordInIndexDeclarationSQL() : void
753
    {
754
        $index = new Index('select', ['foo']);
755
756
        if (! $this->supportsInlineIndexDeclaration()) {
757
            $this->expectException(DBALException::class);
758
        }
759
760
        self::assertSame(
761
            $this->getQuotesReservedKeywordInIndexDeclarationSQL(),
762
            $this->platform->getIndexDeclarationSQL('select', $index)
763
        );
764
    }
765
766
    abstract protected function getQuotesReservedKeywordInIndexDeclarationSQL() : string;
767
768
    protected function supportsInlineIndexDeclaration() : bool
769
    {
770
        return true;
771
    }
772
773
    public function testSupportsCommentOnStatement() : void
774
    {
775
        self::assertSame($this->supportsCommentOnStatement(), $this->platform->supportsCommentOnStatement());
776
    }
777
778
    protected function supportsCommentOnStatement() : bool
779
    {
780
        return false;
781
    }
782
783
    public function testGetCreateSchemaSQL() : void
784
    {
785
        $this->expectException(DBALException::class);
786
787
        $this->platform->getCreateSchemaSQL('schema');
788
    }
789
790
    /**
791
     * @group DBAL-585
792
     */
793
    public function testAlterTableChangeQuotedColumn() : void
794
    {
795
        $tableDiff                        = new TableDiff('mytable');
796
        $tableDiff->fromTable             = new Table('mytable');
797
        $tableDiff->changedColumns['foo'] = new ColumnDiff(
798
            'select',
799
            new Column(
800
                'select',
801
                Type::getType('string')
802
            ),
803
            ['type']
804
        );
805
806
        self::assertStringContainsString(
807
            $this->platform->quoteIdentifier('select'),
808
            implode(';', $this->platform->getAlterTableSQL($tableDiff))
809
        );
810
    }
811
812
    /**
813
     * @group DBAL-563
814
     */
815
    public function testUsesSequenceEmulatedIdentityColumns() : void
816
    {
817
        self::assertFalse($this->platform->usesSequenceEmulatedIdentityColumns());
818
    }
819
820
    /**
821
     * @group DBAL-563
822
     */
823
    public function testReturnsIdentitySequenceName() : void
824
    {
825
        $this->expectException(DBALException::class);
826
827
        $this->platform->getIdentitySequenceName('mytable', 'mycolumn');
828
    }
829
830
    public function testReturnsBinaryDefaultLength() : void
831
    {
832
        self::assertSame($this->getBinaryDefaultLength(), $this->platform->getBinaryDefaultLength());
833
    }
834
835
    protected function getBinaryDefaultLength() : int
836
    {
837
        return 255;
838
    }
839
840
    public function testReturnsBinaryMaxLength() : void
841
    {
842
        self::assertSame($this->getBinaryMaxLength(), $this->platform->getBinaryMaxLength());
843
    }
844
845
    protected function getBinaryMaxLength() : int
846
    {
847
        return 4000;
848
    }
849
850
    public function testReturnsBinaryTypeDeclarationSQL() : void
851
    {
852
        $this->expectException(DBALException::class);
853
854
        $this->platform->getBinaryTypeDeclarationSQL([]);
855
    }
856
857
    public function testReturnsBinaryTypeLongerThanMaxDeclarationSQL() : void
858
    {
859
        $this->markTestSkipped('Not applicable to the platform');
860
    }
861
862
    /**
863
     * @group DBAL-553
864
     */
865
    public function hasNativeJsonType() : void
866
    {
867
        self::assertFalse($this->platform->hasNativeJsonType());
868
    }
869
870
    /**
871
     * @group DBAL-553
872
     */
873
    public function testReturnsJsonTypeDeclarationSQL() : void
874
    {
875
        $column = [
876
            'length'  => 666,
877
            'notnull' => true,
878
            'type'    => Type::getType('json_array'),
879
        ];
880
881
        self::assertSame(
882
            $this->platform->getClobTypeDeclarationSQL($column),
883
            $this->platform->getJsonTypeDeclarationSQL($column)
884
        );
885
    }
886
887
    /**
888
     * @group DBAL-234
889
     */
890
    public function testAlterTableRenameIndex() : void
891
    {
892
        $tableDiff            = new TableDiff('mytable');
893
        $tableDiff->fromTable = new Table('mytable');
894
        $tableDiff->fromTable->addColumn('id', 'integer');
895
        $tableDiff->fromTable->setPrimaryKey(['id']);
896
        $tableDiff->renamedIndexes = [
897
            'idx_foo' => new Index('idx_bar', ['id']),
898
        ];
899
900
        self::assertSame(
901
            $this->getAlterTableRenameIndexSQL(),
902
            $this->platform->getAlterTableSQL($tableDiff)
903
        );
904
    }
905
906
    /**
907
     * @return string[]
908
     *
909
     * @group DBAL-234
910
     */
911
    protected function getAlterTableRenameIndexSQL() : array
912
    {
913
        return [
914
            'DROP INDEX idx_foo',
915
            'CREATE INDEX idx_bar ON mytable (id)',
916
        ];
917
    }
918
919
    /**
920
     * @group DBAL-234
921
     */
922
    public function testQuotesAlterTableRenameIndex() : void
923
    {
924
        $tableDiff            = new TableDiff('table');
925
        $tableDiff->fromTable = new Table('table');
926
        $tableDiff->fromTable->addColumn('id', 'integer');
927
        $tableDiff->fromTable->setPrimaryKey(['id']);
928
        $tableDiff->renamedIndexes = [
929
            'create' => new Index('select', ['id']),
930
            '`foo`'  => new Index('`bar`', ['id']),
931
        ];
932
933
        self::assertSame(
934
            $this->getQuotedAlterTableRenameIndexSQL(),
935
            $this->platform->getAlterTableSQL($tableDiff)
936
        );
937
    }
938
939
    /**
940
     * @return string[]
941
     *
942
     * @group DBAL-234
943
     */
944
    protected function getQuotedAlterTableRenameIndexSQL() : array
945
    {
946
        return [
947
            'DROP INDEX "create"',
948
            'CREATE INDEX "select" ON "table" (id)',
949
            'DROP INDEX "foo"',
950
            'CREATE INDEX "bar" ON "table" (id)',
951
        ];
952
    }
953
954
    /**
955
     * @group DBAL-835
956
     */
957
    public function testQuotesAlterTableRenameColumn() : void
958
    {
959
        $fromTable = new Table('mytable');
960
961
        $fromTable->addColumn('unquoted1', 'integer', ['comment' => 'Unquoted 1']);
962
        $fromTable->addColumn('unquoted2', 'integer', ['comment' => 'Unquoted 2']);
963
        $fromTable->addColumn('unquoted3', 'integer', ['comment' => 'Unquoted 3']);
964
965
        $fromTable->addColumn('create', 'integer', ['comment' => 'Reserved keyword 1']);
966
        $fromTable->addColumn('table', 'integer', ['comment' => 'Reserved keyword 2']);
967
        $fromTable->addColumn('select', 'integer', ['comment' => 'Reserved keyword 3']);
968
969
        $fromTable->addColumn('`quoted1`', 'integer', ['comment' => 'Quoted 1']);
970
        $fromTable->addColumn('`quoted2`', 'integer', ['comment' => 'Quoted 2']);
971
        $fromTable->addColumn('`quoted3`', 'integer', ['comment' => 'Quoted 3']);
972
973
        $toTable = new Table('mytable');
974
975
        $toTable->addColumn('unquoted', 'integer', ['comment' => 'Unquoted 1']); // unquoted -> unquoted
976
        $toTable->addColumn('where', 'integer', ['comment' => 'Unquoted 2']); // unquoted -> reserved keyword
977
        $toTable->addColumn('`foo`', 'integer', ['comment' => 'Unquoted 3']); // unquoted -> quoted
978
979
        $toTable->addColumn('reserved_keyword', 'integer', ['comment' => 'Reserved keyword 1']); // reserved keyword -> unquoted
980
        $toTable->addColumn('from', 'integer', ['comment' => 'Reserved keyword 2']); // reserved keyword -> reserved keyword
981
        $toTable->addColumn('`bar`', 'integer', ['comment' => 'Reserved keyword 3']); // reserved keyword -> quoted
982
983
        $toTable->addColumn('quoted', 'integer', ['comment' => 'Quoted 1']); // quoted -> unquoted
984
        $toTable->addColumn('and', 'integer', ['comment' => 'Quoted 2']); // quoted -> reserved keyword
985
        $toTable->addColumn('`baz`', 'integer', ['comment' => 'Quoted 3']); // quoted -> quoted
986
987
        $comparator = new Comparator();
988
989
        self::assertEquals(
990
            $this->getQuotedAlterTableRenameColumnSQL(),
991
            $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

991
            $this->platform->getAlterTableSQL(/** @scrutinizer ignore-type */ $comparator->diffTable($fromTable, $toTable))
Loading history...
992
        );
993
    }
994
995
    /**
996
     * Returns SQL statements for {@link testQuotesAlterTableRenameColumn}.
997
     *
998
     * @return string[]
999
     *
1000
     * @group DBAL-835
1001
     */
1002
    abstract protected function getQuotedAlterTableRenameColumnSQL() : array;
1003
1004
    /**
1005
     * @group DBAL-835
1006
     */
1007
    public function testQuotesAlterTableChangeColumnLength() : void
1008
    {
1009
        $fromTable = new Table('mytable');
1010
1011
        $fromTable->addColumn('unquoted1', 'string', ['comment' => 'Unquoted 1', 'length' => 10]);
1012
        $fromTable->addColumn('unquoted2', 'string', ['comment' => 'Unquoted 2', 'length' => 10]);
1013
        $fromTable->addColumn('unquoted3', 'string', ['comment' => 'Unquoted 3', 'length' => 10]);
1014
1015
        $fromTable->addColumn('create', 'string', ['comment' => 'Reserved keyword 1', 'length' => 10]);
1016
        $fromTable->addColumn('table', 'string', ['comment' => 'Reserved keyword 2', 'length' => 10]);
1017
        $fromTable->addColumn('select', 'string', ['comment' => 'Reserved keyword 3', 'length' => 10]);
1018
1019
        $toTable = new Table('mytable');
1020
1021
        $toTable->addColumn('unquoted1', 'string', ['comment' => 'Unquoted 1', 'length' => 255]);
1022
        $toTable->addColumn('unquoted2', 'string', ['comment' => 'Unquoted 2', 'length' => 255]);
1023
        $toTable->addColumn('unquoted3', 'string', ['comment' => 'Unquoted 3', 'length' => 255]);
1024
1025
        $toTable->addColumn('create', 'string', ['comment' => 'Reserved keyword 1', 'length' => 255]);
1026
        $toTable->addColumn('table', 'string', ['comment' => 'Reserved keyword 2', 'length' => 255]);
1027
        $toTable->addColumn('select', 'string', ['comment' => 'Reserved keyword 3', 'length' => 255]);
1028
1029
        $comparator = new Comparator();
1030
1031
        self::assertEquals(
1032
            $this->getQuotedAlterTableChangeColumnLengthSQL(),
1033
            $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

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