Failed Conditions
Push — 3.0.x ( 655b6b...430dce )
by Grégoire
16:57 queued 12:22
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
cc 2
eloc 6
nc 2
nop 0
dl 0
loc 9
rs 10
c 0
b 0
f 0

1 Method

Rating   Name   Duplication   Size   Complexity  
A AbstractPlatformTestCase::getBitAndComparisonExpressionSql() 0 3 1
1
<?php
2
3
namespace Doctrine\DBAL\Tests\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 PHPUnit\Framework\TestCase;
19
use function get_class;
20
use function implode;
21
use function sprintf;
22
use function str_repeat;
23
24
abstract class AbstractPlatformTestCase extends TestCase
25
{
26
    /** @var AbstractPlatform */
27
    protected $platform;
28
29
    abstract public function createPlatform() : AbstractPlatform;
30
31
    protected function setUp() : void
32
    {
33
        $this->platform = $this->createPlatform();
34
    }
35
36
    /**
37
     * @group DDC-1360
38
     */
39
    public function testQuoteIdentifier() : void
40
    {
41
        if ($this->platform->getName() === 'mssql') {
42
            self::markTestSkipped('Not working this way on mssql.');
43
        }
44
45
        $c = $this->platform->getIdentifierQuoteCharacter();
46
        self::assertEquals($c . 'test' . $c, $this->platform->quoteIdentifier('test'));
47
        self::assertEquals($c . 'test' . $c . '.' . $c . 'test' . $c, $this->platform->quoteIdentifier('test.test'));
48
        self::assertEquals(str_repeat($c, 4), $this->platform->quoteIdentifier($c));
49
    }
50
51
    /**
52
     * @group DDC-1360
53
     */
54
    public function testQuoteSingleIdentifier() : void
55
    {
56
        if ($this->platform->getName() === 'mssql') {
57
            self::markTestSkipped('Not working this way on mssql.');
58
        }
59
60
        $c = $this->platform->getIdentifierQuoteCharacter();
61
        self::assertEquals($c . 'test' . $c, $this->platform->quoteSingleIdentifier('test'));
62
        self::assertEquals($c . 'test.test' . $c, $this->platform->quoteSingleIdentifier('test.test'));
63
        self::assertEquals(str_repeat($c, 4), $this->platform->quoteSingleIdentifier($c));
64
    }
65
66
    /**
67
     * @group DBAL-1029
68
     * @dataProvider getReturnsForeignKeyReferentialActionSQL
69
     */
70
    public function testReturnsForeignKeyReferentialActionSQL(string $action, string $expectedSQL) : void
71
    {
72
        self::assertSame($expectedSQL, $this->platform->getForeignKeyReferentialActionSQL($action));
73
    }
74
75
    /**
76
     * @return mixed[][]
77
     */
78
    public static function getReturnsForeignKeyReferentialActionSQL() : iterable
79
    {
80
        return [
81
            ['CASCADE', 'CASCADE'],
82
            ['SET NULL', 'SET NULL'],
83
            ['NO ACTION', 'NO ACTION'],
84
            ['RESTRICT', 'RESTRICT'],
85
            ['SET DEFAULT', 'SET DEFAULT'],
86
            ['CaScAdE', 'CASCADE'],
87
        ];
88
    }
89
90
    public function testGetInvalidForeignKeyReferentialActionSQL() : void
91
    {
92
        $this->expectException('InvalidArgumentException');
93
        $this->platform->getForeignKeyReferentialActionSQL('unknown');
94
    }
95
96
    public function testGetUnknownDoctrineMappingType() : void
97
    {
98
        $this->expectException(DBALException::class);
99
        $this->platform->getDoctrineTypeMapping('foobar');
100
    }
101
102
    public function testRegisterDoctrineMappingType() : void
103
    {
104
        $this->platform->registerDoctrineTypeMapping('foo', 'integer');
105
        self::assertEquals('integer', $this->platform->getDoctrineTypeMapping('foo'));
106
    }
107
108
    public function testRegisterUnknownDoctrineMappingType() : void
109
    {
110
        $this->expectException(DBALException::class);
111
        $this->platform->registerDoctrineTypeMapping('foo', 'bar');
112
    }
113
114
    /**
115
     * @group DBAL-2594
116
     */
117
    public function testRegistersCommentedDoctrineMappingTypeImplicitly() : void
118
    {
119
        $type = Type::getType('array');
120
        $this->platform->registerDoctrineTypeMapping('foo', 'array');
121
122
        self::assertTrue($this->platform->isCommentedDoctrineType($type));
123
    }
124
125
    /**
126
     * @group DBAL-939
127
     * @dataProvider getIsCommentedDoctrineType
128
     */
129
    public function testIsCommentedDoctrineType(Type $type, bool $commented) : void
130
    {
131
        self::assertSame($commented, $this->platform->isCommentedDoctrineType($type));
132
    }
133
134
    /**
135
     * @return mixed[]
136
     */
137
    public function getIsCommentedDoctrineType() : iterable
138
    {
139
        $this->setUp();
140
141
        $data = [];
142
143
        foreach (Type::getTypesMap() as $typeName => $className) {
144
            $type = Type::getType($typeName);
145
146
            $data[$typeName] = [
147
                $type,
148
                $type->requiresSQLCommentHint($this->platform),
149
            ];
150
        }
151
152
        return $data;
153
    }
154
155
    public function testCreateWithNoColumns() : void
156
    {
157
        $table = new Table('test');
158
159
        $this->expectException(DBALException::class);
160
        $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...
161
    }
162
163
    public function testGeneratesTableCreationSql() : void
164
    {
165
        $table = new Table('test');
166
        $table->addColumn('id', 'integer', ['notnull' => true, 'autoincrement' => true]);
167
        $table->addColumn('test', 'string', ['notnull' => false, 'length' => 255]);
168
        $table->setPrimaryKey(['id']);
169
170
        $sql = $this->platform->getCreateTableSQL($table);
171
        self::assertEquals($this->getGenerateTableSql(), $sql[0]);
172
    }
173
174
    abstract public function getGenerateTableSql() : string;
175
176
    public function testGenerateTableWithMultiColumnUniqueIndex() : void
177
    {
178
        $table = new Table('test');
179
        $table->addColumn('foo', 'string', ['notnull' => false, 'length' => 255]);
180
        $table->addColumn('bar', 'string', ['notnull' => false, 'length' => 255]);
181
        $table->addUniqueIndex(['foo', 'bar']);
182
183
        $sql = $this->platform->getCreateTableSQL($table);
184
        self::assertEquals($this->getGenerateTableWithMultiColumnUniqueIndexSql(), $sql);
185
    }
186
187
    /**
188
     * @return string[]
189
     */
190
    abstract public function getGenerateTableWithMultiColumnUniqueIndexSql() : array;
191
192
    public function testGeneratesIndexCreationSql() : void
193
    {
194
        $indexDef = new Index('my_idx', ['user_name', 'last_login']);
195
196
        self::assertEquals(
197
            $this->getGenerateIndexSql(),
198
            $this->platform->getCreateIndexSQL($indexDef, 'mytable')
199
        );
200
    }
201
202
    abstract public function getGenerateIndexSql() : string;
203
204
    public function testGeneratesUniqueIndexCreationSql() : void
205
    {
206
        $indexDef = new Index('index_name', ['test', 'test2'], true);
207
208
        $sql = $this->platform->getCreateIndexSQL($indexDef, 'test');
209
        self::assertEquals($this->getGenerateUniqueIndexSql(), $sql);
210
    }
211
212
    abstract public function getGenerateUniqueIndexSql() : string;
213
214
    public function testGeneratesPartialIndexesSqlOnlyWhenSupportingPartialIndexes() : void
215
    {
216
        $where       = 'test IS NULL AND test2 IS NOT NULL';
217
        $indexDef    = new Index('name', ['test', 'test2'], false, false, [], ['where' => $where]);
218
        $uniqueIndex = new Index('name', ['test', 'test2'], true, false, [], ['where' => $where]);
219
220
        $expected = ' WHERE ' . $where;
221
222
        $actuals = [];
223
224
        if ($this->supportsInlineIndexDeclaration()) {
225
            $actuals[] = $this->platform->getIndexDeclarationSQL('name', $indexDef);
226
        }
227
228
        $actuals[] = $this->platform->getUniqueConstraintDeclarationSQL('name', $uniqueIndex);
229
        $actuals[] = $this->platform->getCreateIndexSQL($indexDef, 'table');
230
231
        foreach ($actuals as $actual) {
232
            if ($this->platform->supportsPartialIndexes()) {
233
                self::assertStringEndsWith($expected, $actual, 'WHERE clause should be present');
234
            } else {
235
                self::assertStringEndsNotWith($expected, $actual, 'WHERE clause should NOT be present');
236
            }
237
        }
238
    }
239
240
    public function testGeneratesForeignKeyCreationSql() : void
241
    {
242
        $fk = new ForeignKeyConstraint(['fk_name_id'], 'other_table', ['id'], '');
243
244
        $sql = $this->platform->getCreateForeignKeySQL($fk, 'test');
245
        self::assertEquals($sql, $this->getGenerateForeignKeySql());
246
    }
247
248
    abstract public function getGenerateForeignKeySql() : string;
249
250
    public function testGeneratesConstraintCreationSql() : void
251
    {
252
        if (! $this->platform->supportsCreateDropForeignKeyConstraints()) {
253
            $this->markTestSkipped('Platform does not support creating or dropping foreign key constraints.');
254
        }
255
256
        $idx = new Index('constraint_name', ['test'], true, false);
257
        $sql = $this->platform->getCreateConstraintSQL($idx, 'test');
258
        self::assertEquals($this->getGenerateConstraintUniqueIndexSql(), $sql);
259
260
        $pk  = new Index('constraint_name', ['test'], true, true);
261
        $sql = $this->platform->getCreateConstraintSQL($pk, 'test');
262
        self::assertEquals($this->getGenerateConstraintPrimaryIndexSql(), $sql);
263
264
        $fk  = new ForeignKeyConstraint(['fk_name'], 'foreign', ['id'], 'constraint_fk');
265
        $sql = $this->platform->getCreateConstraintSQL($fk, 'test');
266
        self::assertEquals($this->getGenerateConstraintForeignKeySql($fk), $sql);
267
    }
268
269
    protected function getBitAndComparisonExpressionSql(string $value1, string $value2) : string
270
    {
271
        return '(' . $value1 . ' & ' . $value2 . ')';
272
    }
273
274
    /**
275
     * @group DDC-1213
276
     */
277
    public function testGeneratesBitAndComparisonExpressionSql() : void
278
    {
279
        $sql = $this->platform->getBitAndComparisonExpression(2, 4);
280
        self::assertEquals($this->getBitAndComparisonExpressionSql(2, 4), $sql);
281
    }
282
283
    protected function getBitOrComparisonExpressionSql(string $value1, string $value2) : string
284
    {
285
        return '(' . $value1 . ' | ' . $value2 . ')';
286
    }
287
288
    /**
289
     * @group DDC-1213
290
     */
291
    public function testGeneratesBitOrComparisonExpressionSql() : void
292
    {
293
        $sql = $this->platform->getBitOrComparisonExpression(2, 4);
294
        self::assertEquals($this->getBitOrComparisonExpressionSql(2, 4), $sql);
295
    }
296
297
    public function getGenerateConstraintUniqueIndexSql() : string
298
    {
299
        return 'ALTER TABLE test ADD CONSTRAINT constraint_name UNIQUE (test)';
300
    }
301
302
    public function getGenerateConstraintPrimaryIndexSql() : string
303
    {
304
        return 'ALTER TABLE test ADD CONSTRAINT constraint_name PRIMARY KEY (test)';
305
    }
306
307
    public function getGenerateConstraintForeignKeySql(ForeignKeyConstraint $fk) : string
308
    {
309
        $quotedForeignTable = $fk->getQuotedForeignTableName($this->platform);
310
311
        return sprintf(
312
            'ALTER TABLE test ADD CONSTRAINT constraint_fk FOREIGN KEY (fk_name) REFERENCES %s (id)',
313
            $quotedForeignTable
314
        );
315
    }
316
317
    /**
318
     * @return string[]
319
     */
320
    abstract public function getGenerateAlterTableSql() : array;
321
322
    public function testGeneratesTableAlterationSql() : void
323
    {
324
        $expectedSql = $this->getGenerateAlterTableSql();
325
326
        $table = new Table('mytable');
327
        $table->addColumn('id', 'integer', ['autoincrement' => true]);
328
        $table->addColumn('foo', 'integer');
329
        $table->addColumn('bar', 'string');
330
        $table->addColumn('bloo', 'boolean');
331
        $table->setPrimaryKey(['id']);
332
333
        $tableDiff                         = new TableDiff('mytable');
334
        $tableDiff->fromTable              = $table;
335
        $tableDiff->newName                = 'userlist';
336
        $tableDiff->addedColumns['quota']  = new Column('quota', Type::getType('integer'), ['notnull' => false]);
337
        $tableDiff->removedColumns['foo']  = new Column('foo', Type::getType('integer'));
338
        $tableDiff->changedColumns['bar']  = new ColumnDiff(
339
            'bar',
340
            new Column(
341
                'baz',
342
                Type::getType('string'),
343
                ['default' => 'def']
344
            ),
345
            ['type', 'notnull', 'default']
346
        );
347
        $tableDiff->changedColumns['bloo'] = new ColumnDiff(
348
            'bloo',
349
            new Column(
350
                'bloo',
351
                Type::getType('boolean'),
352
                ['default' => false]
353
            ),
354
            ['type', 'notnull', 'default']
355
        );
356
357
        $sql = $this->platform->getAlterTableSQL($tableDiff);
358
359
        self::assertEquals($expectedSql, $sql);
360
    }
361
362
    public function testGetCustomColumnDeclarationSql() : void
363
    {
364
        $field = ['columnDefinition' => 'MEDIUMINT(6) UNSIGNED'];
365
        self::assertEquals('foo MEDIUMINT(6) UNSIGNED', $this->platform->getColumnDeclarationSQL('foo', $field));
366
    }
367
368
    public function testGetCreateTableSqlDispatchEvent() : void
369
    {
370
        $listenerMock = $this->getMockBuilder($this->getMockClass('GetCreateTableSqlDispatchEvenListener'))
371
            ->addMethods(['onSchemaCreateTable', 'onSchemaCreateTableColumn'])
372
            ->getMock();
373
        $listenerMock
374
            ->expects(self::once())
375
            ->method('onSchemaCreateTable');
376
        $listenerMock
377
            ->expects(self::exactly(2))
378
            ->method('onSchemaCreateTableColumn');
379
380
        $eventManager = new EventManager();
381
        $eventManager->addEventListener([Events::onSchemaCreateTable, Events::onSchemaCreateTableColumn], $listenerMock);
382
383
        $this->platform->setEventManager($eventManager);
384
385
        $table = new Table('test');
386
        $table->addColumn('foo', 'string', ['notnull' => false, 'length' => 255]);
387
        $table->addColumn('bar', 'string', ['notnull' => false, 'length' => 255]);
388
389
        $this->platform->getCreateTableSQL($table);
390
    }
391
392
    public function testGetDropTableSqlDispatchEvent() : void
393
    {
394
        $listenerMock = $this->getMockBuilder($this->getMockClass('GetDropTableSqlDispatchEventListener'))
395
            ->addMethods(['onSchemaDropTable'])
396
            ->getMock();
397
        $listenerMock
398
            ->expects(self::once())
399
            ->method('onSchemaDropTable');
400
401
        $eventManager = new EventManager();
402
        $eventManager->addEventListener([Events::onSchemaDropTable], $listenerMock);
403
404
        $this->platform->setEventManager($eventManager);
405
406
        $this->platform->getDropTableSQL('TABLE');
407
    }
408
409
    public function testGetAlterTableSqlDispatchEvent() : void
410
    {
411
        $events = [
412
            'onSchemaAlterTable',
413
            'onSchemaAlterTableAddColumn',
414
            'onSchemaAlterTableRemoveColumn',
415
            'onSchemaAlterTableChangeColumn',
416
            'onSchemaAlterTableRenameColumn',
417
        ];
418
419
        $listenerMock = $this->getMockBuilder($this->getMockClass('GetAlterTableSqlDispatchEvenListener'))
420
            ->addMethods($events)
421
            ->getMock();
422
        $listenerMock
423
            ->expects(self::once())
424
            ->method('onSchemaAlterTable');
425
        $listenerMock
426
            ->expects(self::once())
427
            ->method('onSchemaAlterTableAddColumn');
428
        $listenerMock
429
            ->expects(self::once())
430
            ->method('onSchemaAlterTableRemoveColumn');
431
        $listenerMock
432
            ->expects(self::once())
433
            ->method('onSchemaAlterTableChangeColumn');
434
        $listenerMock
435
            ->expects(self::once())
436
            ->method('onSchemaAlterTableRenameColumn');
437
438
        $eventManager = new EventManager();
439
        $events       = [
440
            Events::onSchemaAlterTable,
441
            Events::onSchemaAlterTableAddColumn,
442
            Events::onSchemaAlterTableRemoveColumn,
443
            Events::onSchemaAlterTableChangeColumn,
444
            Events::onSchemaAlterTableRenameColumn,
445
        ];
446
        $eventManager->addEventListener($events, $listenerMock);
447
448
        $this->platform->setEventManager($eventManager);
449
450
        $table = new Table('mytable');
451
        $table->addColumn('removed', 'integer');
452
        $table->addColumn('changed', 'integer');
453
        $table->addColumn('renamed', 'integer');
454
455
        $tableDiff                            = new TableDiff('mytable');
456
        $tableDiff->fromTable                 = $table;
457
        $tableDiff->addedColumns['added']     = new Column('added', Type::getType('integer'), []);
458
        $tableDiff->removedColumns['removed'] = new Column('removed', Type::getType('integer'), []);
459
        $tableDiff->changedColumns['changed'] = new ColumnDiff(
460
            'changed',
461
            new Column(
462
                'changed2',
463
                Type::getType('string'),
464
                []
465
            ),
466
            []
467
        );
468
        $tableDiff->renamedColumns['renamed'] = new Column('renamed2', Type::getType('integer'), []);
469
470
        $this->platform->getAlterTableSQL($tableDiff);
471
    }
472
473
    /**
474
     * @group DBAL-42
475
     */
476
    public function testCreateTableColumnComments() : void
477
    {
478
        $table = new Table('test');
479
        $table->addColumn('id', 'integer', ['comment' => 'This is a comment']);
480
        $table->setPrimaryKey(['id']);
481
482
        self::assertEquals($this->getCreateTableColumnCommentsSQL(), $this->platform->getCreateTableSQL($table));
483
    }
484
485
    /**
486
     * @group DBAL-42
487
     */
488
    public function testAlterTableColumnComments() : void
489
    {
490
        $tableDiff                        = new TableDiff('mytable');
491
        $tableDiff->addedColumns['quota'] = new Column('quota', Type::getType('integer'), ['comment' => 'A comment']);
492
        $tableDiff->changedColumns['foo'] = new ColumnDiff(
493
            'foo',
494
            new Column(
495
                'foo',
496
                Type::getType('string')
497
            ),
498
            ['comment']
499
        );
500
        $tableDiff->changedColumns['bar'] = new ColumnDiff(
501
            'bar',
502
            new Column(
503
                'baz',
504
                Type::getType('string'),
505
                ['comment' => 'B comment']
506
            ),
507
            ['comment']
508
        );
509
510
        self::assertEquals($this->getAlterTableColumnCommentsSQL(), $this->platform->getAlterTableSQL($tableDiff));
511
    }
512
513
    public function testCreateTableColumnTypeComments() : void
514
    {
515
        $table = new Table('test');
516
        $table->addColumn('id', 'integer');
517
        $table->addColumn('data', 'array');
518
        $table->setPrimaryKey(['id']);
519
520
        self::assertEquals($this->getCreateTableColumnTypeCommentsSQL(), $this->platform->getCreateTableSQL($table));
521
    }
522
523
    /**
524
     * @return string[]
525
     */
526
    public function getCreateTableColumnCommentsSQL() : array
527
    {
528
        self::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...
529
    }
530
531
    /**
532
     * @return string[]
533
     */
534
    public function getAlterTableColumnCommentsSQL() : array
535
    {
536
        self::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...
537
    }
538
539
    /**
540
     * @return string[]
541
     */
542
    public function getCreateTableColumnTypeCommentsSQL() : array
543
    {
544
        self::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...
545
    }
546
547
    public function testGetDefaultValueDeclarationSQL() : void
548
    {
549
        // non-timestamp value will get single quotes
550
        $field = [
551
            'type' => Type::getType('string'),
552
            'default' => 'non_timestamp',
553
        ];
554
555
        self::assertEquals(" DEFAULT 'non_timestamp'", $this->platform->getDefaultValueDeclarationSQL($field));
556
    }
557
558
    /**
559
     * @group 2859
560
     */
561
    public function testGetDefaultValueDeclarationSQLDateTime() : void
562
    {
563
        // timestamps on datetime types should not be quoted
564
        foreach (['datetime', 'datetimetz', 'datetime_immutable', 'datetimetz_immutable'] as $type) {
565
            $field = [
566
                'type'    => Type::getType($type),
567
                'default' => $this->platform->getCurrentTimestampSQL(),
568
            ];
569
570
            self::assertSame(
571
                ' DEFAULT ' . $this->platform->getCurrentTimestampSQL(),
572
                $this->platform->getDefaultValueDeclarationSQL($field)
573
            );
574
        }
575
    }
576
577
    public function testGetDefaultValueDeclarationSQLForIntegerTypes() : void
578
    {
579
        foreach (['bigint', 'integer', 'smallint'] as $type) {
580
            $field = [
581
                'type'    => Type::getType($type),
582
                'default' => 1,
583
            ];
584
585
            self::assertEquals(
586
                ' DEFAULT 1',
587
                $this->platform->getDefaultValueDeclarationSQL($field)
588
            );
589
        }
590
    }
591
592
    /**
593
     * @group 2859
594
     */
595
    public function testGetDefaultValueDeclarationSQLForDateType() : void
596
    {
597
        $currentDateSql = $this->platform->getCurrentDateSQL();
598
        foreach (['date', 'date_immutable'] as $type) {
599
            $field = [
600
                'type'    => Type::getType($type),
601
                'default' => $currentDateSql,
602
            ];
603
604
            self::assertSame(
605
                ' DEFAULT ' . $currentDateSql,
606
                $this->platform->getDefaultValueDeclarationSQL($field)
607
            );
608
        }
609
    }
610
611
    /**
612
     * @group DBAL-45
613
     */
614
    public function testKeywordList() : void
615
    {
616
        $keywordList = $this->platform->getReservedKeywordsList();
617
        self::assertInstanceOf(KeywordList::class, $keywordList);
618
619
        self::assertTrue($keywordList->isKeyword('table'));
620
    }
621
622
    /**
623
     * @group DBAL-374
624
     */
625
    public function testQuotedColumnInPrimaryKeyPropagation() : void
626
    {
627
        $table = new Table('`quoted`');
628
        $table->addColumn('create', 'string');
629
        $table->setPrimaryKey(['create']);
630
631
        $sql = $this->platform->getCreateTableSQL($table);
632
        self::assertEquals($this->getQuotedColumnInPrimaryKeySQL(), $sql);
633
    }
634
635
    /**
636
     * @return string[]
637
     */
638
    abstract protected function getQuotedColumnInPrimaryKeySQL() : array;
639
640
    /**
641
     * @return string[]
642
     */
643
    abstract protected function getQuotedColumnInIndexSQL() : array;
644
645
    /**
646
     * @return string[]
647
     */
648
    abstract protected function getQuotedNameInIndexSQL() : array;
649
650
    /**
651
     * @return string[]
652
     */
653
    abstract protected function getQuotedColumnInForeignKeySQL() : array;
654
655
    /**
656
     * @group DBAL-374
657
     */
658
    public function testQuotedColumnInIndexPropagation() : void
659
    {
660
        $table = new Table('`quoted`');
661
        $table->addColumn('create', 'string');
662
        $table->addIndex(['create']);
663
664
        $sql = $this->platform->getCreateTableSQL($table);
665
        self::assertEquals($this->getQuotedColumnInIndexSQL(), $sql);
666
    }
667
668
    public function testQuotedNameInIndexSQL() : void
669
    {
670
        $table = new Table('test');
671
        $table->addColumn('column1', 'string');
672
        $table->addIndex(['column1'], '`key`');
673
674
        $sql = $this->platform->getCreateTableSQL($table);
675
        self::assertEquals($this->getQuotedNameInIndexSQL(), $sql);
676
    }
677
678
    /**
679
     * @group DBAL-374
680
     */
681
    public function testQuotedColumnInForeignKeyPropagation() : void
682
    {
683
        $table = new Table('`quoted`');
684
        $table->addColumn('create', 'string');
685
        $table->addColumn('foo', 'string');
686
        $table->addColumn('`bar`', 'string');
687
688
        // Foreign table with reserved keyword as name (needs quotation).
689
        $foreignTable = new Table('foreign');
690
        $foreignTable->addColumn('create', 'string');    // Foreign column with reserved keyword as name (needs quotation).
691
        $foreignTable->addColumn('bar', 'string');       // Foreign column with non-reserved keyword as name (does not need quotation).
692
        $foreignTable->addColumn('`foo-bar`', 'string'); // Foreign table with special character in name (needs quotation on some platforms, e.g. Sqlite).
693
694
        $table->addForeignKeyConstraint($foreignTable, ['create', 'foo', '`bar`'], ['create', 'bar', '`foo-bar`'], [], 'FK_WITH_RESERVED_KEYWORD');
695
696
        // Foreign table with non-reserved keyword as name (does not need quotation).
697
        $foreignTable = new Table('foo');
698
        $foreignTable->addColumn('create', 'string');    // Foreign column with reserved keyword as name (needs quotation).
699
        $foreignTable->addColumn('bar', 'string');       // Foreign column with non-reserved keyword as name (does not need quotation).
700
        $foreignTable->addColumn('`foo-bar`', 'string'); // Foreign table with special character in name (needs quotation on some platforms, e.g. Sqlite).
701
702
        $table->addForeignKeyConstraint($foreignTable, ['create', 'foo', '`bar`'], ['create', 'bar', '`foo-bar`'], [], 'FK_WITH_NON_RESERVED_KEYWORD');
703
704
        // Foreign table with special character in name (needs quotation on some platforms, e.g. Sqlite).
705
        $foreignTable = new Table('`foo-bar`');
706
        $foreignTable->addColumn('create', 'string');    // Foreign column with reserved keyword as name (needs quotation).
707
        $foreignTable->addColumn('bar', 'string');       // Foreign column with non-reserved keyword as name (does not need quotation).
708
        $foreignTable->addColumn('`foo-bar`', 'string'); // Foreign table with special character in name (needs quotation on some platforms, e.g. Sqlite).
709
710
        $table->addForeignKeyConstraint($foreignTable, ['create', 'foo', '`bar`'], ['create', 'bar', '`foo-bar`'], [], 'FK_WITH_INTENDED_QUOTATION');
711
712
        $sql = $this->platform->getCreateTableSQL($table, AbstractPlatform::CREATE_FOREIGNKEYS);
713
        self::assertEquals($this->getQuotedColumnInForeignKeySQL(), $sql);
714
    }
715
716
    /**
717
     * @group DBAL-1051
718
     */
719
    public function testQuotesReservedKeywordInUniqueConstraintDeclarationSQL() : void
720
    {
721
        $index = new Index('select', ['foo'], true);
722
723
        self::assertSame(
724
            $this->getQuotesReservedKeywordInUniqueConstraintDeclarationSQL(),
725
            $this->platform->getUniqueConstraintDeclarationSQL('select', $index)
726
        );
727
    }
728
729
    abstract protected function getQuotesReservedKeywordInUniqueConstraintDeclarationSQL() : string;
730
731
    /**
732
     * @group DBAL-2270
733
     */
734
    public function testQuotesReservedKeywordInTruncateTableSQL() : void
735
    {
736
        self::assertSame(
737
            $this->getQuotesReservedKeywordInTruncateTableSQL(),
738
            $this->platform->getTruncateTableSQL('select')
739
        );
740
    }
741
742
    abstract protected function getQuotesReservedKeywordInTruncateTableSQL() : string;
743
744
    /**
745
     * @group DBAL-1051
746
     */
747
    public function testQuotesReservedKeywordInIndexDeclarationSQL() : void
748
    {
749
        $index = new Index('select', ['foo']);
750
751
        if (! $this->supportsInlineIndexDeclaration()) {
752
            $this->expectException(DBALException::class);
753
        }
754
755
        self::assertSame(
756
            $this->getQuotesReservedKeywordInIndexDeclarationSQL(),
757
            $this->platform->getIndexDeclarationSQL('select', $index)
758
        );
759
    }
760
761
    abstract protected function getQuotesReservedKeywordInIndexDeclarationSQL() : string;
762
763
    protected function supportsInlineIndexDeclaration() : bool
764
    {
765
        return true;
766
    }
767
768
    public function testSupportsCommentOnStatement() : void
769
    {
770
        self::assertSame($this->supportsCommentOnStatement(), $this->platform->supportsCommentOnStatement());
771
    }
772
773
    protected function supportsCommentOnStatement() : bool
774
    {
775
        return false;
776
    }
777
778
    public function testGetCreateSchemaSQL() : void
779
    {
780
        $this->expectException(DBALException::class);
781
782
        $this->platform->getCreateSchemaSQL('schema');
783
    }
784
785
    /**
786
     * @group DBAL-585
787
     */
788
    public function testAlterTableChangeQuotedColumn() : void
789
    {
790
        $tableDiff                        = new TableDiff('mytable');
791
        $tableDiff->fromTable             = new Table('mytable');
792
        $tableDiff->changedColumns['foo'] = new ColumnDiff(
793
            'select',
794
            new Column(
795
                'select',
796
                Type::getType('string')
797
            ),
798
            ['type']
799
        );
800
801
        self::assertStringContainsString(
802
            $this->platform->quoteIdentifier('select'),
803
            implode(';', $this->platform->getAlterTableSQL($tableDiff))
804
        );
805
    }
806
807
    /**
808
     * @group DBAL-563
809
     */
810
    public function testUsesSequenceEmulatedIdentityColumns() : void
811
    {
812
        self::assertFalse($this->platform->usesSequenceEmulatedIdentityColumns());
813
    }
814
815
    /**
816
     * @group DBAL-563
817
     */
818
    public function testReturnsIdentitySequenceName() : void
819
    {
820
        $this->expectException(DBALException::class);
821
822
        $this->platform->getIdentitySequenceName('mytable', 'mycolumn');
823
    }
824
825
    public function testReturnsBinaryDefaultLength() : void
826
    {
827
        self::assertSame($this->getBinaryDefaultLength(), $this->platform->getBinaryDefaultLength());
828
    }
829
830
    protected function getBinaryDefaultLength() : int
831
    {
832
        return 255;
833
    }
834
835
    public function testReturnsBinaryMaxLength() : void
836
    {
837
        self::assertSame($this->getBinaryMaxLength(), $this->platform->getBinaryMaxLength());
838
    }
839
840
    protected function getBinaryMaxLength() : int
841
    {
842
        return 4000;
843
    }
844
845
    public function testReturnsBinaryTypeDeclarationSQL() : void
846
    {
847
        $this->expectException(DBALException::class);
848
849
        $this->platform->getBinaryTypeDeclarationSQL([]);
850
    }
851
852
    public function testReturnsBinaryTypeLongerThanMaxDeclarationSQL() : void
853
    {
854
        $this->markTestSkipped('Not applicable to the platform');
855
    }
856
857
    /**
858
     * @group DBAL-553
859
     */
860
    public function hasNativeJsonType() : void
861
    {
862
        self::assertFalse($this->platform->hasNativeJsonType());
863
    }
864
865
    /**
866
     * @group DBAL-553
867
     */
868
    public function testReturnsJsonTypeDeclarationSQL() : void
869
    {
870
        $column = [
871
            'length'  => 666,
872
            'notnull' => true,
873
            'type'    => Type::getType('json_array'),
874
        ];
875
876
        self::assertSame(
877
            $this->platform->getClobTypeDeclarationSQL($column),
878
            $this->platform->getJsonTypeDeclarationSQL($column)
879
        );
880
    }
881
882
    /**
883
     * @group DBAL-234
884
     */
885
    public function testAlterTableRenameIndex() : void
886
    {
887
        $tableDiff            = new TableDiff('mytable');
888
        $tableDiff->fromTable = new Table('mytable');
889
        $tableDiff->fromTable->addColumn('id', 'integer');
890
        $tableDiff->fromTable->setPrimaryKey(['id']);
891
        $tableDiff->renamedIndexes = [
892
            'idx_foo' => new Index('idx_bar', ['id']),
893
        ];
894
895
        self::assertSame(
896
            $this->getAlterTableRenameIndexSQL(),
897
            $this->platform->getAlterTableSQL($tableDiff)
898
        );
899
    }
900
901
    /**
902
     * @return string[]
903
     *
904
     * @group DBAL-234
905
     */
906
    protected function getAlterTableRenameIndexSQL() : array
907
    {
908
        return [
909
            'DROP INDEX idx_foo',
910
            'CREATE INDEX idx_bar ON mytable (id)',
911
        ];
912
    }
913
914
    /**
915
     * @group DBAL-234
916
     */
917
    public function testQuotesAlterTableRenameIndex() : void
918
    {
919
        $tableDiff            = new TableDiff('table');
920
        $tableDiff->fromTable = new Table('table');
921
        $tableDiff->fromTable->addColumn('id', 'integer');
922
        $tableDiff->fromTable->setPrimaryKey(['id']);
923
        $tableDiff->renamedIndexes = [
924
            'create' => new Index('select', ['id']),
925
            '`foo`'  => new Index('`bar`', ['id']),
926
        ];
927
928
        self::assertSame(
929
            $this->getQuotedAlterTableRenameIndexSQL(),
930
            $this->platform->getAlterTableSQL($tableDiff)
931
        );
932
    }
933
934
    /**
935
     * @return string[]
936
     *
937
     * @group DBAL-234
938
     */
939
    protected function getQuotedAlterTableRenameIndexSQL() : array
940
    {
941
        return [
942
            'DROP INDEX "create"',
943
            'CREATE INDEX "select" ON "table" (id)',
944
            'DROP INDEX "foo"',
945
            'CREATE INDEX "bar" ON "table" (id)',
946
        ];
947
    }
948
949
    /**
950
     * @group DBAL-835
951
     */
952
    public function testQuotesAlterTableRenameColumn() : void
953
    {
954
        $fromTable = new Table('mytable');
955
956
        $fromTable->addColumn('unquoted1', 'integer', ['comment' => 'Unquoted 1']);
957
        $fromTable->addColumn('unquoted2', 'integer', ['comment' => 'Unquoted 2']);
958
        $fromTable->addColumn('unquoted3', 'integer', ['comment' => 'Unquoted 3']);
959
960
        $fromTable->addColumn('create', 'integer', ['comment' => 'Reserved keyword 1']);
961
        $fromTable->addColumn('table', 'integer', ['comment' => 'Reserved keyword 2']);
962
        $fromTable->addColumn('select', 'integer', ['comment' => 'Reserved keyword 3']);
963
964
        $fromTable->addColumn('`quoted1`', 'integer', ['comment' => 'Quoted 1']);
965
        $fromTable->addColumn('`quoted2`', 'integer', ['comment' => 'Quoted 2']);
966
        $fromTable->addColumn('`quoted3`', 'integer', ['comment' => 'Quoted 3']);
967
968
        $toTable = new Table('mytable');
969
970
        $toTable->addColumn('unquoted', 'integer', ['comment' => 'Unquoted 1']); // unquoted -> unquoted
971
        $toTable->addColumn('where', 'integer', ['comment' => 'Unquoted 2']); // unquoted -> reserved keyword
972
        $toTable->addColumn('`foo`', 'integer', ['comment' => 'Unquoted 3']); // unquoted -> quoted
973
974
        $toTable->addColumn('reserved_keyword', 'integer', ['comment' => 'Reserved keyword 1']); // reserved keyword -> unquoted
975
        $toTable->addColumn('from', 'integer', ['comment' => 'Reserved keyword 2']); // reserved keyword -> reserved keyword
976
        $toTable->addColumn('`bar`', 'integer', ['comment' => 'Reserved keyword 3']); // reserved keyword -> quoted
977
978
        $toTable->addColumn('quoted', 'integer', ['comment' => 'Quoted 1']); // quoted -> unquoted
979
        $toTable->addColumn('and', 'integer', ['comment' => 'Quoted 2']); // quoted -> reserved keyword
980
        $toTable->addColumn('`baz`', 'integer', ['comment' => 'Quoted 3']); // quoted -> quoted
981
982
        $comparator = new Comparator();
983
984
        self::assertEquals(
985
            $this->getQuotedAlterTableRenameColumnSQL(),
986
            $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

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

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