Failed Conditions
Push — master ( 30b923...92920e )
by Marco
19s queued 13s
created

Tests/DBAL/Platforms/AbstractPlatformTestCase.php (1 issue)

1
<?php
2
3
namespace Doctrine\Tests\DBAL\Platforms;
4
5
use Doctrine\Common\EventManager;
6
use Doctrine\DBAL\DBALException;
7
use Doctrine\DBAL\Events;
8
use Doctrine\DBAL\Platforms\AbstractPlatform;
9
use Doctrine\DBAL\Platforms\Keywords\KeywordList;
10
use Doctrine\DBAL\Schema\Column;
11
use Doctrine\DBAL\Schema\ColumnDiff;
12
use Doctrine\DBAL\Schema\Comparator;
13
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
14
use Doctrine\DBAL\Schema\Index;
15
use Doctrine\DBAL\Schema\Table;
16
use Doctrine\DBAL\Schema\TableDiff;
17
use Doctrine\DBAL\Types\Type;
18
use Doctrine\Tests\DbalTestCase;
19
use Doctrine\Tests\Types\CommentedType;
20
use function get_class;
21
use function implode;
22
use function sprintf;
23
use function str_repeat;
24
25
abstract class AbstractPlatformTestCase extends DbalTestCase
26
{
27
    /** @var AbstractPlatform */
28
    protected $platform;
29
30
    abstract public function createPlatform();
31
32
    protected function setUp() : void
33
    {
34
        $this->platform = $this->createPlatform();
35
    }
36
37
    /**
38
     * @group DDC-1360
39
     */
40
    public function testQuoteIdentifier()
41
    {
42
        if ($this->platform->getName() === 'mssql') {
43
            $this->markTestSkipped('Not working this way on mssql.');
44
        }
45
46
        $c = $this->platform->getIdentifierQuoteCharacter();
47
        self::assertEquals($c . 'test' . $c, $this->platform->quoteIdentifier('test'));
48
        self::assertEquals($c . 'test' . $c . '.' . $c . 'test' . $c, $this->platform->quoteIdentifier('test.test'));
49
        self::assertEquals(str_repeat($c, 4), $this->platform->quoteIdentifier($c));
50
    }
51
52
    /**
53
     * @group DDC-1360
54
     */
55
    public function testQuoteSingleIdentifier()
56
    {
57
        if ($this->platform->getName() === 'mssql') {
58
            $this->markTestSkipped('Not working this way on mssql.');
59
        }
60
61
        $c = $this->platform->getIdentifierQuoteCharacter();
62
        self::assertEquals($c . 'test' . $c, $this->platform->quoteSingleIdentifier('test'));
63
        self::assertEquals($c . 'test.test' . $c, $this->platform->quoteSingleIdentifier('test.test'));
64
        self::assertEquals(str_repeat($c, 4), $this->platform->quoteSingleIdentifier($c));
65
    }
66
67
    /**
68
     * @group DBAL-1029
69
     * @dataProvider getReturnsForeignKeyReferentialActionSQL
70
     */
71
    public function testReturnsForeignKeyReferentialActionSQL($action, $expectedSQL)
72
    {
73
        self::assertSame($expectedSQL, $this->platform->getForeignKeyReferentialActionSQL($action));
74
    }
75
76
    /**
77
     * @return string[][]
78
     */
79
    public function getReturnsForeignKeyReferentialActionSQL()
80
    {
81
        return [
82
            ['CASCADE', 'CASCADE'],
83
            ['SET NULL', 'SET NULL'],
84
            ['NO ACTION', 'NO ACTION'],
85
            ['RESTRICT', 'RESTRICT'],
86
            ['SET DEFAULT', 'SET DEFAULT'],
87
            ['CaScAdE', 'CASCADE'],
88
        ];
89
    }
90
91
    public function testGetInvalidForeignKeyReferentialActionSQL()
92
    {
93
        $this->expectException('InvalidArgumentException');
94
        $this->platform->getForeignKeyReferentialActionSQL('unknown');
95
    }
96
97
    public function testGetUnknownDoctrineMappingType()
98
    {
99
        $this->expectException(DBALException::class);
100
        $this->platform->getDoctrineTypeMapping('foobar');
101
    }
102
103
    public function testRegisterDoctrineMappingType()
104
    {
105
        $this->platform->registerDoctrineTypeMapping('foo', 'integer');
106
        self::assertEquals('integer', $this->platform->getDoctrineTypeMapping('foo'));
107
    }
108
109
    public function testRegisterUnknownDoctrineMappingType()
110
    {
111
        $this->expectException(DBALException::class);
112
        $this->platform->registerDoctrineTypeMapping('foo', 'bar');
113
    }
114
115
    /**
116
     * @group DBAL-2594
117
     */
118
    public function testRegistersCommentedDoctrineMappingTypeImplicitly()
119
    {
120
        if (! Type::hasType('my_commented')) {
121
            Type::addType('my_commented', CommentedType::class);
122
        }
123
124
        $type = Type::getType('my_commented');
125
        $this->platform->registerDoctrineTypeMapping('foo', 'my_commented');
126
127
        self::assertTrue($this->platform->isCommentedDoctrineType($type));
128
    }
129
130
    /**
131
     * @group DBAL-939
132
     * @dataProvider getIsCommentedDoctrineType
133
     */
134
    public function testIsCommentedDoctrineType(Type $type, $commented)
135
    {
136
        self::assertSame($commented, $this->platform->isCommentedDoctrineType($type));
137
    }
138
139
    public function getIsCommentedDoctrineType()
140
    {
141
        $this->setUp();
142
143
        $data = [];
144
145
        foreach (Type::getTypesMap() as $typeName => $className) {
146
            $type = Type::getType($typeName);
147
148
            $data[$typeName] = [
149
                $type,
150
                $type->requiresSQLCommentHint($this->platform),
151
            ];
152
        }
153
154
        return $data;
155
    }
156
157
    public function testCreateWithNoColumns()
158
    {
159
        $table = new Table('test');
160
161
        $this->expectException(DBALException::class);
162
        $sql = $this->platform->getCreateTableSQL($table);
0 ignored issues
show
The assignment to $sql is dead and can be removed.
Loading history...
163
    }
164
165
    public function testGeneratesTableCreationSql()
166
    {
167
        $table = new Table('test');
168
        $table->addColumn('id', 'integer', ['notnull' => true, 'autoincrement' => true]);
169
        $table->addColumn('test', 'string', ['notnull' => false, 'length' => 255]);
170
        $table->setPrimaryKey(['id']);
171
172
        $sql = $this->platform->getCreateTableSQL($table);
173
        self::assertEquals($this->getGenerateTableSql(), $sql[0]);
174
    }
175
176
    abstract public function getGenerateTableSql();
177
178
    public function testGenerateTableWithMultiColumnUniqueIndex()
179
    {
180
        $table = new Table('test');
181
        $table->addColumn('foo', 'string', ['notnull' => false, 'length' => 255]);
182
        $table->addColumn('bar', 'string', ['notnull' => false, 'length' => 255]);
183
        $table->addUniqueIndex(['foo', 'bar']);
184
185
        $sql = $this->platform->getCreateTableSQL($table);
186
        self::assertEquals($this->getGenerateTableWithMultiColumnUniqueIndexSql(), $sql);
187
    }
188
189
    abstract public function getGenerateTableWithMultiColumnUniqueIndexSql();
190
191
    public function testGeneratesIndexCreationSql()
192
    {
193
        $indexDef = new Index('my_idx', ['user_name', 'last_login']);
194
195
        self::assertEquals(
196
            $this->getGenerateIndexSql(),
197
            $this->platform->getCreateIndexSQL($indexDef, 'mytable')
198
        );
199
    }
200
201
    abstract public function getGenerateIndexSql();
202
203
    public function testGeneratesUniqueIndexCreationSql()
204
    {
205
        $indexDef = new Index('index_name', ['test', 'test2'], true);
206
207
        $sql = $this->platform->getCreateIndexSQL($indexDef, 'test');
208
        self::assertEquals($this->getGenerateUniqueIndexSql(), $sql);
209
    }
210
211
    abstract public function getGenerateUniqueIndexSql();
212
213
    public function testGeneratesPartialIndexesSqlOnlyWhenSupportingPartialIndexes()
214
    {
215
        $where       = 'test IS NULL AND test2 IS NOT NULL';
216
        $indexDef    = new Index('name', ['test', 'test2'], false, false, [], ['where' => $where]);
217
        $uniqueIndex = new Index('name', ['test', 'test2'], true, false, [], ['where' => $where]);
218
219
        $expected = ' WHERE ' . $where;
220
221
        $actuals = [];
222
223
        if ($this->supportsInlineIndexDeclaration()) {
224
            $actuals[] = $this->platform->getIndexDeclarationSQL('name', $indexDef);
225
        }
226
227
        $actuals[] = $this->platform->getUniqueConstraintDeclarationSQL('name', $uniqueIndex);
228
        $actuals[] = $this->platform->getCreateIndexSQL($indexDef, 'table');
229
230
        foreach ($actuals as $actual) {
231
            if ($this->platform->supportsPartialIndexes()) {
232
                self::assertStringEndsWith($expected, $actual, 'WHERE clause should be present');
233
            } else {
234
                self::assertStringEndsNotWith($expected, $actual, 'WHERE clause should NOT be present');
235
            }
236
        }
237
    }
238
239
    public function testGeneratesForeignKeyCreationSql()
240
    {
241
        $fk = new ForeignKeyConstraint(['fk_name_id'], 'other_table', ['id'], '');
242
243
        $sql = $this->platform->getCreateForeignKeySQL($fk, 'test');
244
        self::assertEquals($sql, $this->getGenerateForeignKeySql());
245
    }
246
247
    abstract public function getGenerateForeignKeySql();
248
249
    public function testGeneratesConstraintCreationSql()
250
    {
251
        $idx = new Index('constraint_name', ['test'], true, false);
252
        $sql = $this->platform->getCreateConstraintSQL($idx, 'test');
253
        self::assertEquals($this->getGenerateConstraintUniqueIndexSql(), $sql);
254
255
        $pk  = new Index('constraint_name', ['test'], true, true);
256
        $sql = $this->platform->getCreateConstraintSQL($pk, 'test');
257
        self::assertEquals($this->getGenerateConstraintPrimaryIndexSql(), $sql);
258
259
        $fk  = new ForeignKeyConstraint(['fk_name'], 'foreign', ['id'], 'constraint_fk');
260
        $sql = $this->platform->getCreateConstraintSQL($fk, 'test');
261
        self::assertEquals($this->getGenerateConstraintForeignKeySql($fk), $sql);
262
    }
263
264
    public function testGeneratesForeignKeySqlOnlyWhenSupportingForeignKeys()
265
    {
266
        $fk = new ForeignKeyConstraint(['fk_name'], 'foreign', ['id'], 'constraint_fk');
267
268
        if ($this->platform->supportsForeignKeyConstraints()) {
269
            self::assertIsString($this->platform->getCreateForeignKeySQL($fk, 'test'));
270
        } else {
271
            $this->expectException(DBALException::class);
272
            $this->platform->getCreateForeignKeySQL($fk, 'test');
273
        }
274
    }
275
276
    protected function getBitAndComparisonExpressionSql($value1, $value2)
277
    {
278
        return '(' . $value1 . ' & ' . $value2 . ')';
279
    }
280
281
    /**
282
     * @group DDC-1213
283
     */
284
    public function testGeneratesBitAndComparisonExpressionSql()
285
    {
286
        $sql = $this->platform->getBitAndComparisonExpression(2, 4);
287
        self::assertEquals($this->getBitAndComparisonExpressionSql(2, 4), $sql);
288
    }
289
290
    protected function getBitOrComparisonExpressionSql($value1, $value2)
291
    {
292
        return '(' . $value1 . ' | ' . $value2 . ')';
293
    }
294
295
    /**
296
     * @group DDC-1213
297
     */
298
    public function testGeneratesBitOrComparisonExpressionSql()
299
    {
300
        $sql = $this->platform->getBitOrComparisonExpression(2, 4);
301
        self::assertEquals($this->getBitOrComparisonExpressionSql(2, 4), $sql);
302
    }
303
304
    public function getGenerateConstraintUniqueIndexSql()
305
    {
306
        return 'ALTER TABLE test ADD CONSTRAINT constraint_name UNIQUE (test)';
307
    }
308
309
    public function getGenerateConstraintPrimaryIndexSql()
310
    {
311
        return 'ALTER TABLE test ADD CONSTRAINT constraint_name PRIMARY KEY (test)';
312
    }
313
314
    public function getGenerateConstraintForeignKeySql(ForeignKeyConstraint $fk)
315
    {
316
        $quotedForeignTable = $fk->getQuotedForeignTableName($this->platform);
317
318
        return sprintf(
319
            'ALTER TABLE test ADD CONSTRAINT constraint_fk FOREIGN KEY (fk_name) REFERENCES %s (id)',
320
            $quotedForeignTable
321
        );
322
    }
323
324
    abstract public function getGenerateAlterTableSql();
325
326
    public function testGeneratesTableAlterationSql()
327
    {
328
        $expectedSql = $this->getGenerateAlterTableSql();
329
330
        $table = new Table('mytable');
331
        $table->addColumn('id', 'integer', ['autoincrement' => true]);
332
        $table->addColumn('foo', 'integer');
333
        $table->addColumn('bar', 'string');
334
        $table->addColumn('bloo', 'boolean');
335
        $table->setPrimaryKey(['id']);
336
337
        $tableDiff                         = new TableDiff('mytable');
338
        $tableDiff->fromTable              = $table;
339
        $tableDiff->newName                = 'userlist';
340
        $tableDiff->addedColumns['quota']  = new Column('quota', Type::getType('integer'), ['notnull' => false]);
341
        $tableDiff->removedColumns['foo']  = new Column('foo', Type::getType('integer'));
342
        $tableDiff->changedColumns['bar']  = new ColumnDiff(
343
            'bar',
344
            new Column(
345
                'baz',
346
                Type::getType('string'),
347
                ['default' => 'def']
348
            ),
349
            ['type', 'notnull', 'default']
350
        );
351
        $tableDiff->changedColumns['bloo'] = new ColumnDiff(
352
            'bloo',
353
            new Column(
354
                'bloo',
355
                Type::getType('boolean'),
356
                ['default' => false]
357
            ),
358
            ['type', 'notnull', 'default']
359
        );
360
361
        $sql = $this->platform->getAlterTableSQL($tableDiff);
362
363
        self::assertEquals($expectedSql, $sql);
364
    }
365
366
    public function testGetCustomColumnDeclarationSql()
367
    {
368
        $field = ['columnDefinition' => 'MEDIUMINT(6) UNSIGNED'];
369
        self::assertEquals('foo MEDIUMINT(6) UNSIGNED', $this->platform->getColumnDeclarationSQL('foo', $field));
370
    }
371
372
    public function testGetCreateTableSqlDispatchEvent()
373
    {
374
        $listenerMock = $this->getMockBuilder('GetCreateTableSqlDispatchEvenListener')
375
            ->setMethods(['onSchemaCreateTable', 'onSchemaCreateTableColumn'])
376
            ->getMock();
377
        $listenerMock
378
            ->expects($this->once())
379
            ->method('onSchemaCreateTable');
380
        $listenerMock
381
            ->expects($this->exactly(2))
382
            ->method('onSchemaCreateTableColumn');
383
384
        $eventManager = new EventManager();
385
        $eventManager->addEventListener([Events::onSchemaCreateTable, Events::onSchemaCreateTableColumn], $listenerMock);
386
387
        $this->platform->setEventManager($eventManager);
388
389
        $table = new Table('test');
390
        $table->addColumn('foo', 'string', ['notnull' => false, 'length' => 255]);
391
        $table->addColumn('bar', 'string', ['notnull' => false, 'length' => 255]);
392
393
        $this->platform->getCreateTableSQL($table);
394
    }
395
396
    public function testGetDropTableSqlDispatchEvent()
397
    {
398
        $listenerMock = $this->getMockBuilder('GetDropTableSqlDispatchEventListener')
399
            ->setMethods(['onSchemaDropTable'])
400
            ->getMock();
401
        $listenerMock
402
            ->expects($this->once())
403
            ->method('onSchemaDropTable');
404
405
        $eventManager = new EventManager();
406
        $eventManager->addEventListener([Events::onSchemaDropTable], $listenerMock);
407
408
        $this->platform->setEventManager($eventManager);
409
410
        $this->platform->getDropTableSQL('TABLE');
411
    }
412
413
    public function testGetAlterTableSqlDispatchEvent()
414
    {
415
        $events = [
416
            'onSchemaAlterTable',
417
            'onSchemaAlterTableAddColumn',
418
            'onSchemaAlterTableRemoveColumn',
419
            'onSchemaAlterTableChangeColumn',
420
            'onSchemaAlterTableRenameColumn',
421
        ];
422
423
        $listenerMock = $this->getMockBuilder('GetAlterTableSqlDispatchEvenListener')
424
            ->setMethods($events)
425
            ->getMock();
426
        $listenerMock
427
            ->expects($this->once())
428
            ->method('onSchemaAlterTable');
429
        $listenerMock
430
            ->expects($this->once())
431
            ->method('onSchemaAlterTableAddColumn');
432
        $listenerMock
433
            ->expects($this->once())
434
            ->method('onSchemaAlterTableRemoveColumn');
435
        $listenerMock
436
            ->expects($this->once())
437
            ->method('onSchemaAlterTableChangeColumn');
438
        $listenerMock
439
            ->expects($this->once())
440
            ->method('onSchemaAlterTableRenameColumn');
441
442
        $eventManager = new EventManager();
443
        $events       = [
444
            Events::onSchemaAlterTable,
445
            Events::onSchemaAlterTableAddColumn,
446
            Events::onSchemaAlterTableRemoveColumn,
447
            Events::onSchemaAlterTableChangeColumn,
448
            Events::onSchemaAlterTableRenameColumn,
449
        ];
450
        $eventManager->addEventListener($events, $listenerMock);
451
452
        $this->platform->setEventManager($eventManager);
453
454
        $table = new Table('mytable');
455
        $table->addColumn('removed', 'integer');
456
        $table->addColumn('changed', 'integer');
457
        $table->addColumn('renamed', 'integer');
458
459
        $tableDiff                            = new TableDiff('mytable');
460
        $tableDiff->fromTable                 = $table;
461
        $tableDiff->addedColumns['added']     = new Column('added', Type::getType('integer'), []);
462
        $tableDiff->removedColumns['removed'] = new Column('removed', Type::getType('integer'), []);
463
        $tableDiff->changedColumns['changed'] = new ColumnDiff(
464
            'changed',
465
            new Column(
466
                'changed2',
467
                Type::getType('string'),
468
                []
469
            ),
470
            []
471
        );
472
        $tableDiff->renamedColumns['renamed'] = new Column('renamed2', Type::getType('integer'), []);
473
474
        $this->platform->getAlterTableSQL($tableDiff);
475
    }
476
477
    /**
478
     * @group DBAL-42
479
     */
480
    public function testCreateTableColumnComments()
481
    {
482
        $table = new Table('test');
483
        $table->addColumn('id', 'integer', ['comment' => 'This is a comment']);
484
        $table->setPrimaryKey(['id']);
485
486
        self::assertEquals($this->getCreateTableColumnCommentsSQL(), $this->platform->getCreateTableSQL($table));
487
    }
488
489
    /**
490
     * @group DBAL-42
491
     */
492
    public function testAlterTableColumnComments()
493
    {
494
        $tableDiff                        = new TableDiff('mytable');
495
        $tableDiff->addedColumns['quota'] = new Column('quota', Type::getType('integer'), ['comment' => 'A comment']);
496
        $tableDiff->changedColumns['foo'] = new ColumnDiff(
497
            'foo',
498
            new Column(
499
                'foo',
500
                Type::getType('string')
501
            ),
502
            ['comment']
503
        );
504
        $tableDiff->changedColumns['bar'] = new ColumnDiff(
505
            'bar',
506
            new Column(
507
                'baz',
508
                Type::getType('string'),
509
                ['comment' => 'B comment']
510
            ),
511
            ['comment']
512
        );
513
514
        self::assertEquals($this->getAlterTableColumnCommentsSQL(), $this->platform->getAlterTableSQL($tableDiff));
515
    }
516
517
    public function testCreateTableColumnTypeComments()
518
    {
519
        $table = new Table('test');
520
        $table->addColumn('id', 'integer');
521
        $table->addColumn('data', 'array');
522
        $table->setPrimaryKey(['id']);
523
524
        self::assertEquals($this->getCreateTableColumnTypeCommentsSQL(), $this->platform->getCreateTableSQL($table));
525
    }
526
527
    public function getCreateTableColumnCommentsSQL()
528
    {
529
        $this->markTestSkipped('Platform does not support Column comments.');
530
    }
531
532
    public function getAlterTableColumnCommentsSQL()
533
    {
534
        $this->markTestSkipped('Platform does not support Column comments.');
535
    }
536
537
    public function getCreateTableColumnTypeCommentsSQL()
538
    {
539
        $this->markTestSkipped('Platform does not support Column comments.');
540
    }
541
542
    public function testGetDefaultValueDeclarationSQL()
543
    {
544
        // non-timestamp value will get single quotes
545
        $field = [
546
            'type' => Type::getType('string'),
547
            'default' => 'non_timestamp',
548
        ];
549
550
        self::assertEquals(" DEFAULT 'non_timestamp'", $this->platform->getDefaultValueDeclarationSQL($field));
551
    }
552
553
    /**
554
     * @group 2859
555
     */
556
    public function testGetDefaultValueDeclarationSQLDateTime() : void
557
    {
558
        // timestamps on datetime types should not be quoted
559
        foreach (['datetime', 'datetimetz', 'datetime_immutable', 'datetimetz_immutable'] as $type) {
560
            $field = [
561
                'type'    => Type::getType($type),
562
                'default' => $this->platform->getCurrentTimestampSQL(),
563
            ];
564
565
            self::assertSame(
566
                ' DEFAULT ' . $this->platform->getCurrentTimestampSQL(),
567
                $this->platform->getDefaultValueDeclarationSQL($field)
568
            );
569
        }
570
    }
571
572
    public function testGetDefaultValueDeclarationSQLForIntegerTypes()
573
    {
574
        foreach (['bigint', 'integer', 'smallint'] as $type) {
575
            $field = [
576
                'type'    => Type::getType($type),
577
                'default' => 1,
578
            ];
579
580
            self::assertEquals(
581
                ' DEFAULT 1',
582
                $this->platform->getDefaultValueDeclarationSQL($field)
583
            );
584
        }
585
    }
586
587
    /**
588
     * @group 2859
589
     */
590
    public function testGetDefaultValueDeclarationSQLForDateType() : void
591
    {
592
        $currentDateSql = $this->platform->getCurrentDateSQL();
593
        foreach (['date', 'date_immutable'] as $type) {
594
            $field = [
595
                'type'    => Type::getType($type),
596
                'default' => $currentDateSql,
597
            ];
598
599
            self::assertSame(
600
                ' DEFAULT ' . $currentDateSql,
601
                $this->platform->getDefaultValueDeclarationSQL($field)
602
            );
603
        }
604
    }
605
606
    /**
607
     * @group DBAL-45
608
     */
609
    public function testKeywordList()
610
    {
611
        $keywordList = $this->platform->getReservedKeywordsList();
612
        self::assertInstanceOf(KeywordList::class, $keywordList);
613
614
        self::assertTrue($keywordList->isKeyword('table'));
615
    }
616
617
    /**
618
     * @group DBAL-374
619
     */
620
    public function testQuotedColumnInPrimaryKeyPropagation()
621
    {
622
        $table = new Table('`quoted`');
623
        $table->addColumn('create', 'string');
624
        $table->setPrimaryKey(['create']);
625
626
        $sql = $this->platform->getCreateTableSQL($table);
627
        self::assertEquals($this->getQuotedColumnInPrimaryKeySQL(), $sql);
628
    }
629
630
    abstract protected function getQuotedColumnInPrimaryKeySQL();
631
    abstract protected function getQuotedColumnInIndexSQL();
632
    abstract protected function getQuotedNameInIndexSQL();
633
    abstract protected function getQuotedColumnInForeignKeySQL();
634
635
    /**
636
     * @group DBAL-374
637
     */
638
    public function testQuotedColumnInIndexPropagation()
639
    {
640
        $table = new Table('`quoted`');
641
        $table->addColumn('create', 'string');
642
        $table->addIndex(['create']);
643
644
        $sql = $this->platform->getCreateTableSQL($table);
645
        self::assertEquals($this->getQuotedColumnInIndexSQL(), $sql);
646
    }
647
648
    public function testQuotedNameInIndexSQL()
649
    {
650
        $table = new Table('test');
651
        $table->addColumn('column1', 'string');
652
        $table->addIndex(['column1'], '`key`');
653
654
        $sql = $this->platform->getCreateTableSQL($table);
655
        self::assertEquals($this->getQuotedNameInIndexSQL(), $sql);
656
    }
657
658
    /**
659
     * @group DBAL-374
660
     */
661
    public function testQuotedColumnInForeignKeyPropagation()
662
    {
663
        $table = new Table('`quoted`');
664
        $table->addColumn('create', 'string');
665
        $table->addColumn('foo', 'string');
666
        $table->addColumn('`bar`', 'string');
667
668
        // Foreign table with reserved keyword as name (needs quotation).
669
        $foreignTable = new Table('foreign');
670
        $foreignTable->addColumn('create', 'string');    // Foreign column with reserved keyword as name (needs quotation).
671
        $foreignTable->addColumn('bar', 'string');       // Foreign column with non-reserved keyword as name (does not need quotation).
672
        $foreignTable->addColumn('`foo-bar`', 'string'); // Foreign table with special character in name (needs quotation on some platforms, e.g. Sqlite).
673
674
        $table->addForeignKeyConstraint($foreignTable, ['create', 'foo', '`bar`'], ['create', 'bar', '`foo-bar`'], [], 'FK_WITH_RESERVED_KEYWORD');
675
676
        // Foreign table with non-reserved keyword as name (does not need quotation).
677
        $foreignTable = new Table('foo');
678
        $foreignTable->addColumn('create', 'string');    // Foreign column with reserved keyword as name (needs quotation).
679
        $foreignTable->addColumn('bar', 'string');       // Foreign column with non-reserved keyword as name (does not need quotation).
680
        $foreignTable->addColumn('`foo-bar`', 'string'); // Foreign table with special character in name (needs quotation on some platforms, e.g. Sqlite).
681
682
        $table->addForeignKeyConstraint($foreignTable, ['create', 'foo', '`bar`'], ['create', 'bar', '`foo-bar`'], [], 'FK_WITH_NON_RESERVED_KEYWORD');
683
684
        // Foreign table with special character in name (needs quotation on some platforms, e.g. Sqlite).
685
        $foreignTable = new Table('`foo-bar`');
686
        $foreignTable->addColumn('create', 'string');    // Foreign column with reserved keyword as name (needs quotation).
687
        $foreignTable->addColumn('bar', 'string');       // Foreign column with non-reserved keyword as name (does not need quotation).
688
        $foreignTable->addColumn('`foo-bar`', 'string'); // Foreign table with special character in name (needs quotation on some platforms, e.g. Sqlite).
689
690
        $table->addForeignKeyConstraint($foreignTable, ['create', 'foo', '`bar`'], ['create', 'bar', '`foo-bar`'], [], 'FK_WITH_INTENDED_QUOTATION');
691
692
        $sql = $this->platform->getCreateTableSQL($table, AbstractPlatform::CREATE_FOREIGNKEYS);
693
        self::assertEquals($this->getQuotedColumnInForeignKeySQL(), $sql);
694
    }
695
696
    /**
697
     * @group DBAL-1051
698
     */
699
    public function testQuotesReservedKeywordInUniqueConstraintDeclarationSQL()
700
    {
701
        $index = new Index('select', ['foo'], true);
702
703
        self::assertSame(
704
            $this->getQuotesReservedKeywordInUniqueConstraintDeclarationSQL(),
705
            $this->platform->getUniqueConstraintDeclarationSQL('select', $index)
706
        );
707
    }
708
709
    /**
710
     * @return string
711
     */
712
    abstract protected function getQuotesReservedKeywordInUniqueConstraintDeclarationSQL();
713
714
    /**
715
     * @group DBAL-2270
716
     */
717
    public function testQuotesReservedKeywordInTruncateTableSQL()
718
    {
719
        self::assertSame(
720
            $this->getQuotesReservedKeywordInTruncateTableSQL(),
721
            $this->platform->getTruncateTableSQL('select')
722
        );
723
    }
724
725
    /**
726
     * @return string
727
     */
728
    abstract protected function getQuotesReservedKeywordInTruncateTableSQL();
729
730
    /**
731
     * @group DBAL-1051
732
     */
733
    public function testQuotesReservedKeywordInIndexDeclarationSQL()
734
    {
735
        $index = new Index('select', ['foo']);
736
737
        if (! $this->supportsInlineIndexDeclaration()) {
738
            $this->expectException(DBALException::class);
739
        }
740
741
        self::assertSame(
742
            $this->getQuotesReservedKeywordInIndexDeclarationSQL(),
743
            $this->platform->getIndexDeclarationSQL('select', $index)
744
        );
745
    }
746
747
    /**
748
     * @return string
749
     */
750
    abstract protected function getQuotesReservedKeywordInIndexDeclarationSQL();
751
752
    /**
753
     * @return bool
754
     */
755
    protected function supportsInlineIndexDeclaration()
756
    {
757
        return true;
758
    }
759
760
    public function testSupportsCommentOnStatement()
761
    {
762
        self::assertSame($this->supportsCommentOnStatement(), $this->platform->supportsCommentOnStatement());
763
    }
764
765
    /**
766
     * @return bool
767
     */
768
    protected function supportsCommentOnStatement()
769
    {
770
        return false;
771
    }
772
773
    public function testGetCreateSchemaSQL()
774
    {
775
        $this->expectException(DBALException::class);
776
777
        $this->platform->getCreateSchemaSQL('schema');
778
    }
779
780
    /**
781
     * @group DBAL-585
782
     */
783
    public function testAlterTableChangeQuotedColumn()
784
    {
785
        $tableDiff                        = new TableDiff('mytable');
786
        $tableDiff->fromTable             = new Table('mytable');
787
        $tableDiff->changedColumns['foo'] = new ColumnDiff(
788
            'select',
789
            new Column(
790
                'select',
791
                Type::getType('string')
792
            ),
793
            ['type']
794
        );
795
796
        self::assertStringContainsString(
797
            $this->platform->quoteIdentifier('select'),
798
            implode(';', $this->platform->getAlterTableSQL($tableDiff))
799
        );
800
    }
801
802
    /**
803
     * @group DBAL-563
804
     */
805
    public function testUsesSequenceEmulatedIdentityColumns()
806
    {
807
        self::assertFalse($this->platform->usesSequenceEmulatedIdentityColumns());
808
    }
809
810
    /**
811
     * @group DBAL-563
812
     */
813
    public function testReturnsIdentitySequenceName()
814
    {
815
        $this->expectException(DBALException::class);
816
817
        $this->platform->getIdentitySequenceName('mytable', 'mycolumn');
818
    }
819
820
    public function testReturnsBinaryDefaultLength()
821
    {
822
        self::assertSame($this->getBinaryDefaultLength(), $this->platform->getBinaryDefaultLength());
823
    }
824
825
    protected function getBinaryDefaultLength()
826
    {
827
        return 255;
828
    }
829
830
    public function testReturnsBinaryMaxLength()
831
    {
832
        self::assertSame($this->getBinaryMaxLength(), $this->platform->getBinaryMaxLength());
833
    }
834
835
    protected function getBinaryMaxLength()
836
    {
837
        return 4000;
838
    }
839
840
    public function testReturnsBinaryTypeDeclarationSQL()
841
    {
842
        $this->expectException(DBALException::class);
843
844
        $this->platform->getBinaryTypeDeclarationSQL([]);
845
    }
846
847
    public function testReturnsBinaryTypeLongerThanMaxDeclarationSQL()
848
    {
849
        $this->markTestSkipped('Not applicable to the platform');
850
    }
851
852
    /**
853
     * @group DBAL-553
854
     */
855
    public function hasNativeJsonType()
856
    {
857
        self::assertFalse($this->platform->hasNativeJsonType());
858
    }
859
860
    /**
861
     * @group DBAL-553
862
     */
863
    public function testReturnsJsonTypeDeclarationSQL()
864
    {
865
        $column = [
866
            'length'  => 666,
867
            'notnull' => true,
868
            'type'    => Type::getType('json_array'),
869
        ];
870
871
        self::assertSame(
872
            $this->platform->getClobTypeDeclarationSQL($column),
873
            $this->platform->getJsonTypeDeclarationSQL($column)
874
        );
875
    }
876
877
    /**
878
     * @group DBAL-234
879
     */
880
    public function testAlterTableRenameIndex()
881
    {
882
        $tableDiff            = new TableDiff('mytable');
883
        $tableDiff->fromTable = new Table('mytable');
884
        $tableDiff->fromTable->addColumn('id', 'integer');
885
        $tableDiff->fromTable->setPrimaryKey(['id']);
886
        $tableDiff->renamedIndexes = [
887
            'idx_foo' => new Index('idx_bar', ['id']),
888
        ];
889
890
        self::assertSame(
891
            $this->getAlterTableRenameIndexSQL(),
892
            $this->platform->getAlterTableSQL($tableDiff)
893
        );
894
    }
895
896
    /**
897
     * @group DBAL-234
898
     */
899
    protected function getAlterTableRenameIndexSQL()
900
    {
901
        return [
902
            'DROP INDEX idx_foo',
903
            'CREATE INDEX idx_bar ON mytable (id)',
904
        ];
905
    }
906
907
    /**
908
     * @group DBAL-234
909
     */
910
    public function testQuotesAlterTableRenameIndex()
911
    {
912
        $tableDiff            = new TableDiff('table');
913
        $tableDiff->fromTable = new Table('table');
914
        $tableDiff->fromTable->addColumn('id', 'integer');
915
        $tableDiff->fromTable->setPrimaryKey(['id']);
916
        $tableDiff->renamedIndexes = [
917
            'create' => new Index('select', ['id']),
918
            '`foo`'  => new Index('`bar`', ['id']),
919
        ];
920
921
        self::assertSame(
922
            $this->getQuotedAlterTableRenameIndexSQL(),
923
            $this->platform->getAlterTableSQL($tableDiff)
924
        );
925
    }
926
927
    /**
928
     * @group DBAL-234
929
     */
930
    protected function getQuotedAlterTableRenameIndexSQL()
931
    {
932
        return [
933
            'DROP INDEX "create"',
934
            'CREATE INDEX "select" ON "table" (id)',
935
            'DROP INDEX "foo"',
936
            'CREATE INDEX "bar" ON "table" (id)',
937
        ];
938
    }
939
940
    /**
941
     * @group DBAL-835
942
     */
943
    public function testQuotesAlterTableRenameColumn()
944
    {
945
        $fromTable = new Table('mytable');
946
947
        $fromTable->addColumn('unquoted1', 'integer', ['comment' => 'Unquoted 1']);
948
        $fromTable->addColumn('unquoted2', 'integer', ['comment' => 'Unquoted 2']);
949
        $fromTable->addColumn('unquoted3', 'integer', ['comment' => 'Unquoted 3']);
950
951
        $fromTable->addColumn('create', 'integer', ['comment' => 'Reserved keyword 1']);
952
        $fromTable->addColumn('table', 'integer', ['comment' => 'Reserved keyword 2']);
953
        $fromTable->addColumn('select', 'integer', ['comment' => 'Reserved keyword 3']);
954
955
        $fromTable->addColumn('`quoted1`', 'integer', ['comment' => 'Quoted 1']);
956
        $fromTable->addColumn('`quoted2`', 'integer', ['comment' => 'Quoted 2']);
957
        $fromTable->addColumn('`quoted3`', 'integer', ['comment' => 'Quoted 3']);
958
959
        $toTable = new Table('mytable');
960
961
        $toTable->addColumn('unquoted', 'integer', ['comment' => 'Unquoted 1']); // unquoted -> unquoted
962
        $toTable->addColumn('where', 'integer', ['comment' => 'Unquoted 2']); // unquoted -> reserved keyword
963
        $toTable->addColumn('`foo`', 'integer', ['comment' => 'Unquoted 3']); // unquoted -> quoted
964
965
        $toTable->addColumn('reserved_keyword', 'integer', ['comment' => 'Reserved keyword 1']); // reserved keyword -> unquoted
966
        $toTable->addColumn('from', 'integer', ['comment' => 'Reserved keyword 2']); // reserved keyword -> reserved keyword
967
        $toTable->addColumn('`bar`', 'integer', ['comment' => 'Reserved keyword 3']); // reserved keyword -> quoted
968
969
        $toTable->addColumn('quoted', 'integer', ['comment' => 'Quoted 1']); // quoted -> unquoted
970
        $toTable->addColumn('and', 'integer', ['comment' => 'Quoted 2']); // quoted -> reserved keyword
971
        $toTable->addColumn('`baz`', 'integer', ['comment' => 'Quoted 3']); // quoted -> quoted
972
973
        $comparator = new Comparator();
974
975
        self::assertEquals(
976
            $this->getQuotedAlterTableRenameColumnSQL(),
977
            $this->platform->getAlterTableSQL($comparator->diffTable($fromTable, $toTable))
978
        );
979
    }
980
981
    /**
982
     * Returns SQL statements for {@link testQuotesAlterTableRenameColumn}.
983
     *
984
     * @return string[]
985
     *
986
     * @group DBAL-835
987
     */
988
    abstract protected function getQuotedAlterTableRenameColumnSQL();
989
990
    /**
991
     * @group DBAL-835
992
     */
993
    public function testQuotesAlterTableChangeColumnLength()
994
    {
995
        $fromTable = new Table('mytable');
996
997
        $fromTable->addColumn('unquoted1', 'string', ['comment' => 'Unquoted 1', 'length' => 10]);
998
        $fromTable->addColumn('unquoted2', 'string', ['comment' => 'Unquoted 2', 'length' => 10]);
999
        $fromTable->addColumn('unquoted3', 'string', ['comment' => 'Unquoted 3', 'length' => 10]);
1000
1001
        $fromTable->addColumn('create', 'string', ['comment' => 'Reserved keyword 1', 'length' => 10]);
1002
        $fromTable->addColumn('table', 'string', ['comment' => 'Reserved keyword 2', 'length' => 10]);
1003
        $fromTable->addColumn('select', 'string', ['comment' => 'Reserved keyword 3', 'length' => 10]);
1004
1005
        $toTable = new Table('mytable');
1006
1007
        $toTable->addColumn('unquoted1', 'string', ['comment' => 'Unquoted 1', 'length' => 255]);
1008
        $toTable->addColumn('unquoted2', 'string', ['comment' => 'Unquoted 2', 'length' => 255]);
1009
        $toTable->addColumn('unquoted3', 'string', ['comment' => 'Unquoted 3', 'length' => 255]);
1010
1011
        $toTable->addColumn('create', 'string', ['comment' => 'Reserved keyword 1', 'length' => 255]);
1012
        $toTable->addColumn('table', 'string', ['comment' => 'Reserved keyword 2', 'length' => 255]);
1013
        $toTable->addColumn('select', 'string', ['comment' => 'Reserved keyword 3', 'length' => 255]);
1014
1015
        $comparator = new Comparator();
1016
1017
        self::assertEquals(
1018
            $this->getQuotedAlterTableChangeColumnLengthSQL(),
1019
            $this->platform->getAlterTableSQL($comparator->diffTable($fromTable, $toTable))
1020
        );
1021
    }
1022
1023
    /**
1024
     * Returns SQL statements for {@link testQuotesAlterTableChangeColumnLength}.
1025
     *
1026
     * @return string[]
1027
     *
1028
     * @group DBAL-835
1029
     */
1030
    abstract protected function getQuotedAlterTableChangeColumnLengthSQL();
1031
1032
    /**
1033
     * @group DBAL-807
1034
     */
1035
    public function testAlterTableRenameIndexInSchema()
1036
    {
1037
        $tableDiff            = new TableDiff('myschema.mytable');
1038
        $tableDiff->fromTable = new Table('myschema.mytable');
1039
        $tableDiff->fromTable->addColumn('id', 'integer');
1040
        $tableDiff->fromTable->setPrimaryKey(['id']);
1041
        $tableDiff->renamedIndexes = [
1042
            'idx_foo' => new Index('idx_bar', ['id']),
1043
        ];
1044
1045
        self::assertSame(
1046
            $this->getAlterTableRenameIndexInSchemaSQL(),
1047
            $this->platform->getAlterTableSQL($tableDiff)
1048
        );
1049
    }
1050
1051
    /**
1052
     * @group DBAL-807
1053
     */
1054
    protected function getAlterTableRenameIndexInSchemaSQL()
1055
    {
1056
        return [
1057
            'DROP INDEX idx_foo',
1058
            'CREATE INDEX idx_bar ON myschema.mytable (id)',
1059
        ];
1060
    }
1061
1062
    /**
1063
     * @group DBAL-807
1064
     */
1065
    public function testQuotesAlterTableRenameIndexInSchema()
1066
    {
1067
        $tableDiff            = new TableDiff('`schema`.table');
1068
        $tableDiff->fromTable = new Table('`schema`.table');
1069
        $tableDiff->fromTable->addColumn('id', 'integer');
1070
        $tableDiff->fromTable->setPrimaryKey(['id']);
1071
        $tableDiff->renamedIndexes = [
1072
            'create' => new Index('select', ['id']),
1073
            '`foo`'  => new Index('`bar`', ['id']),
1074
        ];
1075
1076
        self::assertSame(
1077
            $this->getQuotedAlterTableRenameIndexInSchemaSQL(),
1078
            $this->platform->getAlterTableSQL($tableDiff)
1079
        );
1080
    }
1081
1082
    /**
1083
     * @group DBAL-234
1084
     */
1085
    protected function getQuotedAlterTableRenameIndexInSchemaSQL()
1086
    {
1087
        return [
1088
            'DROP INDEX "schema"."create"',
1089
            'CREATE INDEX "select" ON "schema"."table" (id)',
1090
            'DROP INDEX "schema"."foo"',
1091
            'CREATE INDEX "bar" ON "schema"."table" (id)',
1092
        ];
1093
    }
1094
1095
    /**
1096
     * @group DBAL-1237
1097
     */
1098
    public function testQuotesDropForeignKeySQL()
1099
    {
1100
        if (! $this->platform->supportsForeignKeyConstraints()) {
1101
            $this->markTestSkipped(
1102
                sprintf('%s does not support foreign key constraints.', get_class($this->platform))
1103
            );
1104
        }
1105
1106
        $tableName      = 'table';
1107
        $table          = new Table($tableName);
1108
        $foreignKeyName = 'select';
1109
        $foreignKey     = new ForeignKeyConstraint([], 'foo', [], 'select');
1110
        $expectedSql    = $this->getQuotesDropForeignKeySQL();
1111
1112
        self::assertSame($expectedSql, $this->platform->getDropForeignKeySQL($foreignKeyName, $tableName));
1113
        self::assertSame($expectedSql, $this->platform->getDropForeignKeySQL($foreignKey, $table));
1114
    }
1115
1116
    protected function getQuotesDropForeignKeySQL()
1117
    {
1118
        return 'ALTER TABLE "table" DROP FOREIGN KEY "select"';
1119
    }
1120
1121
    /**
1122
     * @group DBAL-1237
1123
     */
1124
    public function testQuotesDropConstraintSQL()
1125
    {
1126
        $tableName      = 'table';
1127
        $table          = new Table($tableName);
1128
        $constraintName = 'select';
1129
        $constraint     = new ForeignKeyConstraint([], 'foo', [], 'select');
1130
        $expectedSql    = $this->getQuotesDropConstraintSQL();
1131
1132
        self::assertSame($expectedSql, $this->platform->getDropConstraintSQL($constraintName, $tableName));
1133
        self::assertSame($expectedSql, $this->platform->getDropConstraintSQL($constraint, $table));
1134
    }
1135
1136
    protected function getQuotesDropConstraintSQL()
1137
    {
1138
        return 'ALTER TABLE "table" DROP CONSTRAINT "select"';
1139
    }
1140
1141
    protected function getStringLiteralQuoteCharacter()
1142
    {
1143
        return "'";
1144
    }
1145
1146
    public function testGetStringLiteralQuoteCharacter()
1147
    {
1148
        self::assertSame($this->getStringLiteralQuoteCharacter(), $this->platform->getStringLiteralQuoteCharacter());
1149
    }
1150
1151
    protected function getQuotedCommentOnColumnSQLWithoutQuoteCharacter()
1152
    {
1153
        return "COMMENT ON COLUMN mytable.id IS 'This is a comment'";
1154
    }
1155
1156
    public function testGetCommentOnColumnSQLWithoutQuoteCharacter()
1157
    {
1158
        self::assertEquals(
1159
            $this->getQuotedCommentOnColumnSQLWithoutQuoteCharacter(),
1160
            $this->platform->getCommentOnColumnSQL('mytable', 'id', 'This is a comment')
1161
        );
1162
    }
1163
1164
    protected function getQuotedCommentOnColumnSQLWithQuoteCharacter()
1165
    {
1166
        return "COMMENT ON COLUMN mytable.id IS 'It''s a quote !'";
1167
    }
1168
1169
    public function testGetCommentOnColumnSQLWithQuoteCharacter()
1170
    {
1171
        $c = $this->getStringLiteralQuoteCharacter();
1172
1173
        self::assertEquals(
1174
            $this->getQuotedCommentOnColumnSQLWithQuoteCharacter(),
1175
            $this->platform->getCommentOnColumnSQL('mytable', 'id', 'It' . $c . 's a quote !')
1176
        );
1177
    }
1178
1179
    /**
1180
     * @see testGetCommentOnColumnSQL
1181
     *
1182
     * @return string[]
1183
     */
1184
    abstract protected function getCommentOnColumnSQL();
1185
1186
    /**
1187
     * @group DBAL-1004
1188
     */
1189
    public function testGetCommentOnColumnSQL()
1190
    {
1191
        self::assertSame(
1192
            $this->getCommentOnColumnSQL(),
1193
            [
1194
                $this->platform->getCommentOnColumnSQL('foo', 'bar', 'comment'), // regular identifiers
1195
                $this->platform->getCommentOnColumnSQL('`Foo`', '`BAR`', 'comment'), // explicitly quoted identifiers
1196
                $this->platform->getCommentOnColumnSQL('select', 'from', 'comment'), // reserved keyword identifiers
1197
            ]
1198
        );
1199
    }
1200
1201
    /**
1202
     * @group DBAL-1176
1203
     * @dataProvider getGeneratesInlineColumnCommentSQL
1204
     */
1205
    public function testGeneratesInlineColumnCommentSQL($comment, $expectedSql)
1206
    {
1207
        if (! $this->platform->supportsInlineColumnComments()) {
1208
            $this->markTestSkipped(sprintf('%s does not support inline column comments.', get_class($this->platform)));
1209
        }
1210
1211
        self::assertSame($expectedSql, $this->platform->getInlineColumnCommentSQL($comment));
1212
    }
1213
1214
    public function getGeneratesInlineColumnCommentSQL()
1215
    {
1216
        return [
1217
            'regular comment' => ['Regular comment', $this->getInlineColumnRegularCommentSQL()],
1218
            'comment requiring escaping' => [
1219
                sprintf(
1220
                    'Using inline comment delimiter %s works',
1221
                    $this->getInlineColumnCommentDelimiter()
1222
                ),
1223
                $this->getInlineColumnCommentRequiringEscapingSQL(),
1224
            ],
1225
            'empty comment' => ['', $this->getInlineColumnEmptyCommentSQL()],
1226
        ];
1227
    }
1228
1229
    protected function getInlineColumnCommentDelimiter()
1230
    {
1231
        return "'";
1232
    }
1233
1234
    protected function getInlineColumnRegularCommentSQL()
1235
    {
1236
        return "COMMENT 'Regular comment'";
1237
    }
1238
1239
    protected function getInlineColumnCommentRequiringEscapingSQL()
1240
    {
1241
        return "COMMENT 'Using inline comment delimiter '' works'";
1242
    }
1243
1244
    protected function getInlineColumnEmptyCommentSQL()
1245
    {
1246
        return "COMMENT ''";
1247
    }
1248
1249
    protected function getQuotedStringLiteralWithoutQuoteCharacter()
1250
    {
1251
        return "'No quote'";
1252
    }
1253
1254
    protected function getQuotedStringLiteralWithQuoteCharacter()
1255
    {
1256
        return "'It''s a quote'";
1257
    }
1258
1259
    protected function getQuotedStringLiteralQuoteCharacter()
1260
    {
1261
        return "''''";
1262
    }
1263
1264
    /**
1265
     * @group DBAL-1176
1266
     */
1267
    public function testThrowsExceptionOnGeneratingInlineColumnCommentSQLIfUnsupported()
1268
    {
1269
        if ($this->platform->supportsInlineColumnComments()) {
1270
            $this->markTestSkipped(sprintf('%s supports inline column comments.', get_class($this->platform)));
1271
        }
1272
1273
        $this->expectException(DBALException::class);
1274
        $this->expectExceptionMessage("Operation 'Doctrine\\DBAL\\Platforms\\AbstractPlatform::getInlineColumnCommentSQL' is not supported by platform.");
1275
        $this->expectExceptionCode(0);
1276
1277
        $this->platform->getInlineColumnCommentSQL('unsupported');
1278
    }
1279
1280
    public function testQuoteStringLiteral()
1281
    {
1282
        $c = $this->getStringLiteralQuoteCharacter();
1283
1284
        self::assertEquals(
1285
            $this->getQuotedStringLiteralWithoutQuoteCharacter(),
1286
            $this->platform->quoteStringLiteral('No quote')
1287
        );
1288
        self::assertEquals(
1289
            $this->getQuotedStringLiteralWithQuoteCharacter(),
1290
            $this->platform->quoteStringLiteral('It' . $c . 's a quote')
1291
        );
1292
        self::assertEquals(
1293
            $this->getQuotedStringLiteralQuoteCharacter(),
1294
            $this->platform->quoteStringLiteral($c)
1295
        );
1296
    }
1297
1298
    /**
1299
     * @group DBAL-423
1300
     */
1301
    public function testReturnsGuidTypeDeclarationSQL()
1302
    {
1303
        $this->expectException(DBALException::class);
1304
1305
        $this->platform->getGuidTypeDeclarationSQL([]);
1306
    }
1307
1308
    /**
1309
     * @group DBAL-1010
1310
     */
1311
    public function testGeneratesAlterTableRenameColumnSQL()
1312
    {
1313
        $table = new Table('foo');
1314
        $table->addColumn(
1315
            'bar',
1316
            'integer',
1317
            ['notnull' => true, 'default' => 666, 'comment' => 'rename test']
1318
        );
1319
1320
        $tableDiff                        = new TableDiff('foo');
1321
        $tableDiff->fromTable             = $table;
1322
        $tableDiff->renamedColumns['bar'] = new Column(
1323
            'baz',
1324
            Type::getType('integer'),
1325
            ['notnull' => true, 'default' => 666, 'comment' => 'rename test']
1326
        );
1327
1328
        self::assertSame($this->getAlterTableRenameColumnSQL(), $this->platform->getAlterTableSQL($tableDiff));
1329
    }
1330
1331
    /**
1332
     * @return string[]
1333
     */
1334
    abstract public function getAlterTableRenameColumnSQL();
1335
1336
    /**
1337
     * @group DBAL-1016
1338
     */
1339
    public function testQuotesTableIdentifiersInAlterTableSQL()
1340
    {
1341
        $table = new Table('"foo"');
1342
        $table->addColumn('id', 'integer');
1343
        $table->addColumn('fk', 'integer');
1344
        $table->addColumn('fk2', 'integer');
1345
        $table->addColumn('fk3', 'integer');
1346
        $table->addColumn('bar', 'integer');
1347
        $table->addColumn('baz', 'integer');
1348
        $table->addForeignKeyConstraint('fk_table', ['fk'], ['id'], [], 'fk1');
1349
        $table->addForeignKeyConstraint('fk_table', ['fk2'], ['id'], [], 'fk2');
1350
1351
        $tableDiff                        = new TableDiff('"foo"');
1352
        $tableDiff->fromTable             = $table;
1353
        $tableDiff->newName               = 'table';
1354
        $tableDiff->addedColumns['bloo']  = new Column('bloo', Type::getType('integer'));
1355
        $tableDiff->changedColumns['bar'] = new ColumnDiff(
1356
            'bar',
1357
            new Column('bar', Type::getType('integer'), ['notnull' => false]),
1358
            ['notnull'],
1359
            $table->getColumn('bar')
1360
        );
1361
        $tableDiff->renamedColumns['id']  = new Column('war', Type::getType('integer'));
1362
        $tableDiff->removedColumns['baz'] = new Column('baz', Type::getType('integer'));
1363
        $tableDiff->addedForeignKeys[]    = new ForeignKeyConstraint(['fk3'], 'fk_table', ['id'], 'fk_add');
1364
        $tableDiff->changedForeignKeys[]  = new ForeignKeyConstraint(['fk2'], 'fk_table2', ['id'], 'fk2');
1365
        $tableDiff->removedForeignKeys[]  = new ForeignKeyConstraint(['fk'], 'fk_table', ['id'], 'fk1');
1366
1367
        self::assertSame(
1368
            $this->getQuotesTableIdentifiersInAlterTableSQL(),
1369
            $this->platform->getAlterTableSQL($tableDiff)
1370
        );
1371
    }
1372
1373
    /**
1374
     * @return string[]
1375
     */
1376
    abstract protected function getQuotesTableIdentifiersInAlterTableSQL();
1377
1378
    /**
1379
     * @group DBAL-1090
1380
     */
1381
    public function testAlterStringToFixedString()
1382
    {
1383
        $table = new Table('mytable');
1384
        $table->addColumn('name', 'string', ['length' => 2]);
1385
1386
        $tableDiff            = new TableDiff('mytable');
1387
        $tableDiff->fromTable = $table;
1388
1389
        $tableDiff->changedColumns['name'] = new ColumnDiff(
1390
            'name',
1391
            new Column(
1392
                'name',
1393
                Type::getType('string'),
1394
                ['fixed' => true, 'length' => 2]
1395
            ),
1396
            ['fixed']
1397
        );
1398
1399
        $sql = $this->platform->getAlterTableSQL($tableDiff);
1400
1401
        $expectedSql = $this->getAlterStringToFixedStringSQL();
1402
1403
        self::assertEquals($expectedSql, $sql);
1404
    }
1405
1406
    /**
1407
     * @return string[]
1408
     */
1409
    abstract protected function getAlterStringToFixedStringSQL();
1410
1411
    /**
1412
     * @group DBAL-1062
1413
     */
1414
    public function testGeneratesAlterTableRenameIndexUsedByForeignKeySQL()
1415
    {
1416
        $foreignTable = new Table('foreign_table');
1417
        $foreignTable->addColumn('id', 'integer');
1418
        $foreignTable->setPrimaryKey(['id']);
1419
1420
        $primaryTable = new Table('mytable');
1421
        $primaryTable->addColumn('foo', 'integer');
1422
        $primaryTable->addColumn('bar', 'integer');
1423
        $primaryTable->addColumn('baz', 'integer');
1424
        $primaryTable->addIndex(['foo'], 'idx_foo');
1425
        $primaryTable->addIndex(['bar'], 'idx_bar');
1426
        $primaryTable->addForeignKeyConstraint($foreignTable, ['foo'], ['id'], [], 'fk_foo');
1427
        $primaryTable->addForeignKeyConstraint($foreignTable, ['bar'], ['id'], [], 'fk_bar');
1428
1429
        $tableDiff                            = new TableDiff('mytable');
1430
        $tableDiff->fromTable                 = $primaryTable;
1431
        $tableDiff->renamedIndexes['idx_foo'] = new Index('idx_foo_renamed', ['foo']);
1432
1433
        self::assertSame(
1434
            $this->getGeneratesAlterTableRenameIndexUsedByForeignKeySQL(),
1435
            $this->platform->getAlterTableSQL($tableDiff)
1436
        );
1437
    }
1438
1439
    /**
1440
     * @return string[]
1441
     */
1442
    abstract protected function getGeneratesAlterTableRenameIndexUsedByForeignKeySQL();
1443
1444
    /**
1445
     * @param mixed[] $column
1446
     *
1447
     * @group DBAL-1082
1448
     * @dataProvider getGeneratesDecimalTypeDeclarationSQL
1449
     */
1450
    public function testGeneratesDecimalTypeDeclarationSQL(array $column, $expectedSql)
1451
    {
1452
        self::assertSame($expectedSql, $this->platform->getDecimalTypeDeclarationSQL($column));
1453
    }
1454
1455
    /**
1456
     * @return mixed[]
1457
     */
1458
    public function getGeneratesDecimalTypeDeclarationSQL()
1459
    {
1460
        return [
1461
            [[], 'NUMERIC(10, 0)'],
1462
            [['unsigned' => true], 'NUMERIC(10, 0)'],
1463
            [['unsigned' => false], 'NUMERIC(10, 0)'],
1464
            [['precision' => 5], 'NUMERIC(5, 0)'],
1465
            [['scale' => 5], 'NUMERIC(10, 5)'],
1466
            [['precision' => 8, 'scale' => 2], 'NUMERIC(8, 2)'],
1467
        ];
1468
    }
1469
1470
    /**
1471
     * @param mixed[] $column
1472
     *
1473
     * @group DBAL-1082
1474
     * @dataProvider getGeneratesFloatDeclarationSQL
1475
     */
1476
    public function testGeneratesFloatDeclarationSQL(array $column, $expectedSql)
1477
    {
1478
        self::assertSame($expectedSql, $this->platform->getFloatDeclarationSQL($column));
1479
    }
1480
1481
    /**
1482
     * @return mixed[]
1483
     */
1484
    public function getGeneratesFloatDeclarationSQL()
1485
    {
1486
        return [
1487
            [[], 'DOUBLE PRECISION'],
1488
            [['unsigned' => true], 'DOUBLE PRECISION'],
1489
            [['unsigned' => false], 'DOUBLE PRECISION'],
1490
            [['precision' => 5], 'DOUBLE PRECISION'],
1491
            [['scale' => 5], 'DOUBLE PRECISION'],
1492
            [['precision' => 8, 'scale' => 2], 'DOUBLE PRECISION'],
1493
        ];
1494
    }
1495
1496
    public function testItEscapesStringsForLike() : void
1497
    {
1498
        self::assertSame(
1499
            '\_25\% off\_ your next purchase \\\\o/',
1500
            $this->platform->escapeStringForLike('_25% off_ your next purchase \o/', '\\')
1501
        );
1502
    }
1503
1504
    public function testZeroOffsetWithoutLimitIsIgnored() : void
1505
    {
1506
        $query = 'SELECT * FROM user';
1507
1508
        self::assertSame(
1509
            $query,
1510
            $this->platform->modifyLimitQuery($query, null, 0)
1511
        );
1512
    }
1513
}
1514