Completed
Pull Request — master (#3626)
by Dallas
14:09
created

testGeneratesForeignKeyCreationSql()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

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

1010
            $this->platform->getAlterTableSQL(/** @scrutinizer ignore-type */ $comparator->diffTable($fromTable, $toTable))
Loading history...
1011
        );
1012
    }
1013
1014
    /**
1015
     * Returns SQL statements for {@link testQuotesAlterTableRenameColumn}.
1016
     *
1017
     * @return string[]
1018
     *
1019
     * @group DBAL-835
1020
     */
1021
    abstract protected function getQuotedAlterTableRenameColumnSQL() : array;
1022
1023
    /**
1024
     * @group DBAL-835
1025
     */
1026
    public function testQuotesAlterTableChangeColumnLength() : void
1027
    {
1028
        $fromTable = new Table('mytable');
1029
1030
        $fromTable->addColumn('unquoted1', 'string', ['comment' => 'Unquoted 1', 'length' => 10]);
1031
        $fromTable->addColumn('unquoted2', 'string', ['comment' => 'Unquoted 2', 'length' => 10]);
1032
        $fromTable->addColumn('unquoted3', 'string', ['comment' => 'Unquoted 3', 'length' => 10]);
1033
1034
        $fromTable->addColumn('create', 'string', ['comment' => 'Reserved keyword 1', 'length' => 10]);
1035
        $fromTable->addColumn('table', 'string', ['comment' => 'Reserved keyword 2', 'length' => 10]);
1036
        $fromTable->addColumn('select', 'string', ['comment' => 'Reserved keyword 3', 'length' => 10]);
1037
1038
        $toTable = new Table('mytable');
1039
1040
        $toTable->addColumn('unquoted1', 'string', ['comment' => 'Unquoted 1', 'length' => 255]);
1041
        $toTable->addColumn('unquoted2', 'string', ['comment' => 'Unquoted 2', 'length' => 255]);
1042
        $toTable->addColumn('unquoted3', 'string', ['comment' => 'Unquoted 3', 'length' => 255]);
1043
1044
        $toTable->addColumn('create', 'string', ['comment' => 'Reserved keyword 1', 'length' => 255]);
1045
        $toTable->addColumn('table', 'string', ['comment' => 'Reserved keyword 2', 'length' => 255]);
1046
        $toTable->addColumn('select', 'string', ['comment' => 'Reserved keyword 3', 'length' => 255]);
1047
1048
        $comparator = new Comparator();
1049
1050
        self::assertEquals(
1051
            $this->getQuotedAlterTableChangeColumnLengthSQL(),
1052
            $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

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