AbstractPostgreSqlPlatformTestCase.php 33.7 KB
Newer Older
1 2 3 4
<?php

namespace Doctrine\Tests\DBAL\Platforms;

Grégoire Paris's avatar
Grégoire Paris committed
5
use Doctrine\DBAL\Platforms\PostgreSqlPlatform;
6
use Doctrine\DBAL\Schema\Column;
Sergei Morozov's avatar
Sergei Morozov committed
7
use Doctrine\DBAL\Schema\ColumnDiff;
8
use Doctrine\DBAL\Schema\Comparator;
Sergei Morozov's avatar
Sergei Morozov committed
9 10
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
use Doctrine\DBAL\Schema\Sequence;
11 12
use Doctrine\DBAL\Schema\Table;
use Doctrine\DBAL\Schema\TableDiff;
13
use Doctrine\DBAL\TransactionIsolationLevel;
14
use Doctrine\DBAL\Types\Type;
15
use UnexpectedValueException;
Sergei Morozov's avatar
Sergei Morozov committed
16
use function sprintf;
17 18 19

abstract class AbstractPostgreSqlPlatformTestCase extends AbstractPlatformTestCase
{
Grégoire Paris's avatar
Grégoire Paris committed
20 21 22
    /** @var PostgreSqlPlatform */
    protected $platform;

23
    public function getGenerateTableSql() : string
24 25 26 27
    {
        return 'CREATE TABLE test (id SERIAL NOT NULL, test VARCHAR(255) DEFAULT NULL, PRIMARY KEY(id))';
    }

28 29 30 31
    /**
     * {@inheritDoc}
     */
    public function getGenerateTableWithMultiColumnUniqueIndexSql() : array
32
    {
Sergei Morozov's avatar
Sergei Morozov committed
33
        return [
34
            'CREATE TABLE test (foo VARCHAR(255) DEFAULT NULL, bar VARCHAR(255) DEFAULT NULL)',
Sergei Morozov's avatar
Sergei Morozov committed
35 36
            'CREATE UNIQUE INDEX UNIQ_D87F7E0C8C73652176FF8CAA ON test (foo, bar)',
        ];
37 38
    }

39 40 41 42
    /**
     * {@inheritDoc}
     */
    public function getGenerateAlterTableSql() : array
43
    {
Sergei Morozov's avatar
Sergei Morozov committed
44
        return [
45 46 47 48 49 50 51 52 53
            'ALTER TABLE mytable ADD quota INT DEFAULT NULL',
            'ALTER TABLE mytable DROP foo',
            'ALTER TABLE mytable ALTER bar TYPE VARCHAR(255)',
            "ALTER TABLE mytable ALTER bar SET DEFAULT 'def'",
            'ALTER TABLE mytable ALTER bar SET NOT NULL',
            'ALTER TABLE mytable ALTER bloo TYPE BOOLEAN',
            "ALTER TABLE mytable ALTER bloo SET DEFAULT 'false'",
            'ALTER TABLE mytable ALTER bloo SET NOT NULL',
            'ALTER TABLE mytable RENAME TO userlist',
Sergei Morozov's avatar
Sergei Morozov committed
54
        ];
55 56
    }

57
    public function getGenerateIndexSql() : string
58 59 60 61
    {
        return 'CREATE INDEX my_idx ON mytable (user_name, last_login)';
    }

62
    public function getGenerateForeignKeySql() : string
63 64 65 66
    {
        return 'ALTER TABLE test ADD FOREIGN KEY (fk_name_id) REFERENCES other_table (id) NOT DEFERRABLE INITIALLY IMMEDIATE';
    }

67
    public function testGeneratesForeignKeySqlForNonStandardOptions() : void
68
    {
Sergei Morozov's avatar
Sergei Morozov committed
69 70 71 72 73 74
        $foreignKey = new ForeignKeyConstraint(
            ['foreign_id'],
            'my_table',
            ['id'],
            'my_fk',
            ['onDelete' => 'CASCADE']
75
        );
76
        self::assertEquals(
Sergei Morozov's avatar
Sergei Morozov committed
77
            'CONSTRAINT my_fk FOREIGN KEY (foreign_id) REFERENCES my_table (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE',
Sergei Morozov's avatar
Sergei Morozov committed
78
            $this->platform->getForeignKeyDeclarationSQL($foreignKey)
79 80
        );

Sergei Morozov's avatar
Sergei Morozov committed
81 82 83 84 85 86
        $foreignKey = new ForeignKeyConstraint(
            ['foreign_id'],
            'my_table',
            ['id'],
            'my_fk',
            ['match' => 'full']
87
        );
88
        self::assertEquals(
Sergei Morozov's avatar
Sergei Morozov committed
89
            'CONSTRAINT my_fk FOREIGN KEY (foreign_id) REFERENCES my_table (id) MATCH full NOT DEFERRABLE INITIALLY IMMEDIATE',
Sergei Morozov's avatar
Sergei Morozov committed
90
            $this->platform->getForeignKeyDeclarationSQL($foreignKey)
91 92
        );

Sergei Morozov's avatar
Sergei Morozov committed
93 94 95 96 97 98
        $foreignKey = new ForeignKeyConstraint(
            ['foreign_id'],
            'my_table',
            ['id'],
            'my_fk',
            ['deferrable' => true]
99
        );
100
        self::assertEquals(
Sergei Morozov's avatar
Sergei Morozov committed
101
            'CONSTRAINT my_fk FOREIGN KEY (foreign_id) REFERENCES my_table (id) DEFERRABLE INITIALLY IMMEDIATE',
Sergei Morozov's avatar
Sergei Morozov committed
102
            $this->platform->getForeignKeyDeclarationSQL($foreignKey)
103 104
        );

Sergei Morozov's avatar
Sergei Morozov committed
105 106 107 108 109 110
        $foreignKey = new ForeignKeyConstraint(
            ['foreign_id'],
            'my_table',
            ['id'],
            'my_fk',
            ['deferred' => true]
111
        );
112
        self::assertEquals(
Sergei Morozov's avatar
Sergei Morozov committed
113
            'CONSTRAINT my_fk FOREIGN KEY (foreign_id) REFERENCES my_table (id) NOT DEFERRABLE INITIALLY DEFERRED',
Sergei Morozov's avatar
Sergei Morozov committed
114
            $this->platform->getForeignKeyDeclarationSQL($foreignKey)
115 116
        );

Sergei Morozov's avatar
Sergei Morozov committed
117 118 119 120 121 122
        $foreignKey = new ForeignKeyConstraint(
            ['foreign_id'],
            'my_table',
            ['id'],
            'my_fk',
            ['feferred' => true]
123
        );
124
        self::assertEquals(
Sergei Morozov's avatar
Sergei Morozov committed
125
            'CONSTRAINT my_fk FOREIGN KEY (foreign_id) REFERENCES my_table (id) NOT DEFERRABLE INITIALLY DEFERRED',
Sergei Morozov's avatar
Sergei Morozov committed
126
            $this->platform->getForeignKeyDeclarationSQL($foreignKey)
127 128
        );

Sergei Morozov's avatar
Sergei Morozov committed
129 130 131 132 133 134
        $foreignKey = new ForeignKeyConstraint(
            ['foreign_id'],
            'my_table',
            ['id'],
            'my_fk',
            ['deferrable' => true, 'deferred' => true, 'match' => 'full']
135
        );
136
        self::assertEquals(
Sergei Morozov's avatar
Sergei Morozov committed
137
            'CONSTRAINT my_fk FOREIGN KEY (foreign_id) REFERENCES my_table (id) MATCH full DEFERRABLE INITIALLY DEFERRED',
Sergei Morozov's avatar
Sergei Morozov committed
138
            $this->platform->getForeignKeyDeclarationSQL($foreignKey)
139 140 141
        );
    }

142
    public function testGeneratesSqlSnippets() : void
143
    {
Sergei Morozov's avatar
Sergei Morozov committed
144 145 146 147 148
        self::assertEquals('SIMILAR TO', $this->platform->getRegexpExpression(), 'Regular expression operator is not correct');
        self::assertEquals('"', $this->platform->getIdentifierQuoteCharacter(), 'Identifier quote character is not correct');
        self::assertEquals('column1 || column2 || column3', $this->platform->getConcatExpression('column1', 'column2', 'column3'), 'Concatenation expression is not correct');
        self::assertEquals('SUBSTRING(column FROM 5)', $this->platform->getSubstringExpression('column', 5), 'Substring expression without length is not correct');
        self::assertEquals('SUBSTRING(column FROM 1 FOR 5)', $this->platform->getSubstringExpression('column', 1, 5), 'Substring expression with length is not correct');
149 150
    }

151
    public function testGeneratesTransactionCommands() : void
152
    {
153
        self::assertEquals(
154
            'SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL READ UNCOMMITTED',
Sergei Morozov's avatar
Sergei Morozov committed
155
            $this->platform->getSetTransactionIsolationSQL(TransactionIsolationLevel::READ_UNCOMMITTED)
156
        );
157
        self::assertEquals(
158
            'SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL READ COMMITTED',
Sergei Morozov's avatar
Sergei Morozov committed
159
            $this->platform->getSetTransactionIsolationSQL(TransactionIsolationLevel::READ_COMMITTED)
160
        );
161
        self::assertEquals(
162
            'SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL REPEATABLE READ',
Sergei Morozov's avatar
Sergei Morozov committed
163
            $this->platform->getSetTransactionIsolationSQL(TransactionIsolationLevel::REPEATABLE_READ)
164
        );
165
        self::assertEquals(
166
            'SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE',
Sergei Morozov's avatar
Sergei Morozov committed
167
            $this->platform->getSetTransactionIsolationSQL(TransactionIsolationLevel::SERIALIZABLE)
168 169 170
        );
    }

171
    public function testGeneratesDDLSnippets() : void
172
    {
Sergei Morozov's avatar
Sergei Morozov committed
173 174 175
        self::assertEquals('CREATE DATABASE foobar', $this->platform->getCreateDatabaseSQL('foobar'));
        self::assertEquals('DROP DATABASE foobar', $this->platform->getDropDatabaseSQL('foobar'));
        self::assertEquals('DROP TABLE foobar', $this->platform->getDropTableSQL('foobar'));
176 177
    }

178
    public function testGenerateTableWithAutoincrement() : void
179
    {
Sergei Morozov's avatar
Sergei Morozov committed
180
        $table  = new Table('autoinc_table');
181 182 183
        $column = $table->addColumn('id', 'integer');
        $column->setAutoincrement(true);

Sergei Morozov's avatar
Sergei Morozov committed
184
        self::assertEquals(['CREATE TABLE autoinc_table (id SERIAL NOT NULL)'], $this->platform->getCreateTableSQL($table));
185 186
    }

Sergei Morozov's avatar
Sergei Morozov committed
187
    /**
188
     * @return mixed[][]
Sergei Morozov's avatar
Sergei Morozov committed
189
     */
190
    public static function serialTypes() : iterable
191 192 193 194 195 196 197 198 199 200 201 202 203
    {
        return [
            ['integer', 'SERIAL'],
            ['bigint', 'BIGSERIAL'],
        ];
    }

    /**
     * @dataProvider serialTypes
     * @group 2906
     */
    public function testGenerateTableWithAutoincrementDoesNotSetDefault(string $type, string $definition) : void
    {
Sergei Morozov's avatar
Sergei Morozov committed
204
        $table  = new Table('autoinc_table_notnull');
205 206 207 208
        $column = $table->addColumn('id', $type);
        $column->setAutoIncrement(true);
        $column->setNotNull(false);

Sergei Morozov's avatar
Sergei Morozov committed
209
        $sql = $this->platform->getCreateTableSQL($table);
210

Sergei Morozov's avatar
Sergei Morozov committed
211
        self::assertEquals([sprintf('CREATE TABLE autoinc_table_notnull (id %s)', $definition)], $sql);
212 213 214 215 216 217 218 219
    }

    /**
     * @dataProvider serialTypes
     * @group 2906
     */
    public function testCreateTableWithAutoincrementAndNotNullAddsConstraint(string $type, string $definition) : void
    {
Sergei Morozov's avatar
Sergei Morozov committed
220
        $table  = new Table('autoinc_table_notnull_enabled');
221 222 223 224
        $column = $table->addColumn('id', $type);
        $column->setAutoIncrement(true);
        $column->setNotNull(true);

Sergei Morozov's avatar
Sergei Morozov committed
225
        $sql = $this->platform->getCreateTableSQL($table);
226

Sergei Morozov's avatar
Sergei Morozov committed
227
        self::assertEquals([sprintf('CREATE TABLE autoinc_table_notnull_enabled (id %s NOT NULL)', $definition)], $sql);
228 229 230 231 232 233 234 235
    }

    /**
     * @dataProvider serialTypes
     * @group 2906
     */
    public function testGetDefaultValueDeclarationSQLIgnoresTheDefaultKeyWhenTheFieldIsSerial(string $type) : void
    {
Sergei Morozov's avatar
Sergei Morozov committed
236
        $sql = $this->platform->getDefaultValueDeclarationSQL(
237 238 239 240 241 242 243 244 245 246
            [
                'autoincrement' => true,
                'type'          => Type::getType($type),
                'default'       => 1,
            ]
        );

        self::assertSame('', $sql);
    }

247
    public function testGeneratesTypeDeclarationForIntegers() : void
248
    {
249
        self::assertEquals(
250
            'INT',
Sergei Morozov's avatar
Sergei Morozov committed
251
            $this->platform->getIntegerTypeDeclarationSQL([])
252
        );
253
        self::assertEquals(
254
            'SERIAL',
Sergei Morozov's avatar
Sergei Morozov committed
255
            $this->platform->getIntegerTypeDeclarationSQL(['autoincrement' => true])
Sergei Morozov's avatar
Sergei Morozov committed
256
        );
257
        self::assertEquals(
258
            'SERIAL',
Sergei Morozov's avatar
Sergei Morozov committed
259
            $this->platform->getIntegerTypeDeclarationSQL(
Sergei Morozov's avatar
Sergei Morozov committed
260 261 262
                ['autoincrement' => true, 'primary' => true]
            )
        );
263 264
    }

265
    public function testGeneratesTypeDeclarationForStrings() : void
266
    {
267
        self::assertEquals(
268
            'CHAR(10)',
Sergei Morozov's avatar
Sergei Morozov committed
269
            $this->platform->getVarcharTypeDeclarationSQL(
Sergei Morozov's avatar
Sergei Morozov committed
270 271
                ['length' => 10, 'fixed' => true]
            )
272
        );
273
        self::assertEquals(
274
            'VARCHAR(50)',
Sergei Morozov's avatar
Sergei Morozov committed
275
            $this->platform->getVarcharTypeDeclarationSQL(['length' => 50]),
276 277
            'Variable string declaration is not correct'
        );
278
        self::assertEquals(
279
            'VARCHAR(255)',
Sergei Morozov's avatar
Sergei Morozov committed
280
            $this->platform->getVarcharTypeDeclarationSQL([]),
281 282 283 284
            'Long string declaration is not correct'
        );
    }

285
    public function getGenerateUniqueIndexSql() : string
286 287 288 289
    {
        return 'CREATE UNIQUE INDEX index_name ON test (test, test2)';
    }

290
    public function testGeneratesSequenceSqlCommands() : void
291
    {
Sergei Morozov's avatar
Sergei Morozov committed
292
        $sequence = new Sequence('myseq', 20, 1);
293
        self::assertEquals(
294
            'CREATE SEQUENCE myseq INCREMENT BY 20 MINVALUE 1 START 1',
Sergei Morozov's avatar
Sergei Morozov committed
295
            $this->platform->getCreateSequenceSQL($sequence)
296
        );
297
        self::assertEquals(
298
            'DROP SEQUENCE myseq CASCADE',
Sergei Morozov's avatar
Sergei Morozov committed
299
            $this->platform->getDropSequenceSQL('myseq')
300
        );
301
        self::assertEquals(
302
            "SELECT NEXTVAL('myseq')",
Sergei Morozov's avatar
Sergei Morozov committed
303
            $this->platform->getSequenceNextValSQL('myseq')
304 305 306
        );
    }

307
    public function testDoesNotPreferIdentityColumns() : void
308
    {
Sergei Morozov's avatar
Sergei Morozov committed
309
        self::assertFalse($this->platform->prefersIdentityColumns());
310 311
    }

312
    public function testPrefersSequences() : void
313
    {
Sergei Morozov's avatar
Sergei Morozov committed
314
        self::assertTrue($this->platform->prefersSequences());
315 316
    }

317
    public function testSupportsIdentityColumns() : void
318
    {
Sergei Morozov's avatar
Sergei Morozov committed
319
        self::assertTrue($this->platform->supportsIdentityColumns());
320 321
    }

322
    public function testSupportsSavePoints() : void
323
    {
Sergei Morozov's avatar
Sergei Morozov committed
324
        self::assertTrue($this->platform->supportsSavepoints());
325 326
    }

327
    public function testSupportsSequences() : void
328
    {
Sergei Morozov's avatar
Sergei Morozov committed
329
        self::assertTrue($this->platform->supportsSequences());
330 331
    }

332
    protected function supportsCommentOnStatement() : bool
333 334 335 336
    {
        return true;
    }

337
    public function testModifyLimitQuery() : void
338
    {
Sergei Morozov's avatar
Sergei Morozov committed
339
        $sql = $this->platform->modifyLimitQuery('SELECT * FROM user', 10, 0);
340
        self::assertEquals('SELECT * FROM user LIMIT 10', $sql);
341 342
    }

343
    public function testModifyLimitQueryWithEmptyOffset() : void
344
    {
Sergei Morozov's avatar
Sergei Morozov committed
345
        $sql = $this->platform->modifyLimitQuery('SELECT * FROM user', 10);
346
        self::assertEquals('SELECT * FROM user LIMIT 10', $sql);
347 348
    }

349 350 351 352
    /**
     * {@inheritDoc}
     */
    public function getCreateTableColumnCommentsSQL() : array
353
    {
Sergei Morozov's avatar
Sergei Morozov committed
354 355
        return [
            'CREATE TABLE test (id INT NOT NULL, PRIMARY KEY(id))',
356
            "COMMENT ON COLUMN test.id IS 'This is a comment'",
Sergei Morozov's avatar
Sergei Morozov committed
357
        ];
358 359
    }

360 361 362 363
    /**
     * {@inheritDoc}
     */
    public function getAlterTableColumnCommentsSQL() : array
364
    {
Sergei Morozov's avatar
Sergei Morozov committed
365 366
        return [
            'ALTER TABLE mytable ADD quota INT NOT NULL',
367
            "COMMENT ON COLUMN mytable.quota IS 'A comment'",
Sergei Morozov's avatar
Sergei Morozov committed
368
            'COMMENT ON COLUMN mytable.foo IS NULL',
369
            "COMMENT ON COLUMN mytable.baz IS 'B comment'",
Sergei Morozov's avatar
Sergei Morozov committed
370
        ];
371 372
    }

373 374 375 376
    /**
     * {@inheritDoc}
     */
    public function getCreateTableColumnTypeCommentsSQL() : array
377
    {
Sergei Morozov's avatar
Sergei Morozov committed
378 379 380 381
        return [
            'CREATE TABLE test (id INT NOT NULL, data TEXT NOT NULL, PRIMARY KEY(id))',
            "COMMENT ON COLUMN test.data IS '(DC2Type:array)'",
        ];
382 383
    }

384 385 386 387
    /**
     * {@inheritDoc}
     */
    protected function getQuotedColumnInPrimaryKeySQL() : array
388
    {
Sergei Morozov's avatar
Sergei Morozov committed
389
        return ['CREATE TABLE "quoted" ("create" VARCHAR(255) NOT NULL, PRIMARY KEY("create"))'];
390 391
    }

392 393 394 395
    /**
     * {@inheritDoc}
     */
    protected function getQuotedColumnInIndexSQL() : array
396
    {
Sergei Morozov's avatar
Sergei Morozov committed
397
        return [
398 399
            'CREATE TABLE "quoted" ("create" VARCHAR(255) NOT NULL)',
            'CREATE INDEX IDX_22660D028FD6E0FB ON "quoted" ("create")',
Sergei Morozov's avatar
Sergei Morozov committed
400
        ];
401 402
    }

403 404 405 406
    /**
     * {@inheritDoc}
     */
    protected function getQuotedNameInIndexSQL() : array
Markus Fasselt's avatar
Markus Fasselt committed
407
    {
Sergei Morozov's avatar
Sergei Morozov committed
408
        return [
Markus Fasselt's avatar
Markus Fasselt committed
409 410
            'CREATE TABLE test (column1 VARCHAR(255) NOT NULL)',
            'CREATE INDEX "key" ON test (column1)',
Sergei Morozov's avatar
Sergei Morozov committed
411
        ];
Markus Fasselt's avatar
Markus Fasselt committed
412 413
    }

414 415 416 417
    /**
     * {@inheritDoc}
     */
    protected function getQuotedColumnInForeignKeySQL() : array
418
    {
Sergei Morozov's avatar
Sergei Morozov committed
419
        return [
420 421 422 423
            'CREATE TABLE "quoted" ("create" VARCHAR(255) NOT NULL, foo VARCHAR(255) NOT NULL, "bar" VARCHAR(255) NOT NULL)',
            'ALTER TABLE "quoted" ADD CONSTRAINT FK_WITH_RESERVED_KEYWORD FOREIGN KEY ("create", foo, "bar") REFERENCES "foreign" ("create", bar, "foo-bar") NOT DEFERRABLE INITIALLY IMMEDIATE',
            'ALTER TABLE "quoted" ADD CONSTRAINT FK_WITH_NON_RESERVED_KEYWORD FOREIGN KEY ("create", foo, "bar") REFERENCES foo ("create", bar, "foo-bar") NOT DEFERRABLE INITIALLY IMMEDIATE',
            'ALTER TABLE "quoted" ADD CONSTRAINT FK_WITH_INTENDED_QUOTATION FOREIGN KEY ("create", foo, "bar") REFERENCES "foo-bar" ("create", bar, "foo-bar") NOT DEFERRABLE INITIALLY IMMEDIATE',
Sergei Morozov's avatar
Sergei Morozov committed
424
        ];
425 426 427
    }

    /**
428
     * @param string|bool $databaseValue
Sergei Morozov's avatar
Sergei Morozov committed
429 430 431
     *
     * @group DBAL-457
     * @dataProvider pgBooleanProvider
432
     */
433 434
    public function testConvertBooleanAsLiteralStrings(
        $databaseValue,
435 436 437 438
        string $preparedStatementValue,
        ?int $integerValue,
        ?bool $booleanValue
    ) : void {
439 440
        $platform = $this->createPlatform();

441
        self::assertEquals($preparedStatementValue, $platform->convertBooleans($databaseValue));
442 443 444 445 446
    }

    /**
     * @group DBAL-457
     */
447
    public function testConvertBooleanAsLiteralIntegers() : void
448 449 450 451
    {
        $platform = $this->createPlatform();
        $platform->setUseBooleanTrueFalseStrings(false);

452 453
        self::assertEquals(1, $platform->convertBooleans(true));
        self::assertEquals(1, $platform->convertBooleans('1'));
454

455 456
        self::assertEquals(0, $platform->convertBooleans(false));
        self::assertEquals(0, $platform->convertBooleans('0'));
457 458 459
    }

    /**
460
     * @param string|bool $databaseValue
Sergei Morozov's avatar
Sergei Morozov committed
461 462 463
     *
     * @group DBAL-630
     * @dataProvider pgBooleanProvider
464
     */
465 466
    public function testConvertBooleanAsDatabaseValueStrings(
        $databaseValue,
467 468 469 470
        string $preparedStatementValue,
        ?int $integerValue,
        ?bool $booleanValue
    ) : void {
471 472
        $platform = $this->createPlatform();

473
        self::assertSame($integerValue, $platform->convertBooleansToDatabaseValue($booleanValue));
474 475 476 477 478
    }

    /**
     * @group DBAL-630
     */
479
    public function testConvertBooleanAsDatabaseValueIntegers() : void
480 481 482 483
    {
        $platform = $this->createPlatform();
        $platform->setUseBooleanTrueFalseStrings(false);

484 485
        self::assertSame(1, $platform->convertBooleansToDatabaseValue(true));
        self::assertSame(0, $platform->convertBooleansToDatabaseValue(false));
486
    }
487 488

    /**
489
     * @param string|bool $databaseValue
Sergei Morozov's avatar
Sergei Morozov committed
490 491
     *
     * @dataProvider pgBooleanProvider
492
     */
493
    public function testConvertFromBoolean($databaseValue, string $prepareStatementValue, ?int $integerValue, ?bool $booleanValue) : void
494 495
    {
        $platform = $this->createPlatform();
496

497
        self::assertSame($booleanValue, $platform->convertFromBoolean($databaseValue));
498
    }
499

500
    public function testThrowsExceptionWithInvalidBooleanLiteral() : void
501
    {
502 503 504 505 506 507
        $platform = $this->createPlatform();

        $this->expectException(UnexpectedValueException::class);
        $this->expectExceptionMessage("Unrecognized boolean literal 'my-bool'");

        $platform->convertBooleansToDatabaseValue('my-bool');
508 509
    }

510
    public function testGetCreateSchemaSQL() : void
511 512
    {
        $schemaName = 'schema';
Sergei Morozov's avatar
Sergei Morozov committed
513
        $sql        = $this->platform->getCreateSchemaSQL($schemaName);
514
        self::assertEquals('CREATE SCHEMA ' . $schemaName, $sql);
515 516
    }

517
    public function testAlterDecimalPrecisionScale() : void
518 519 520
    {
        $table = new Table('mytable');
        $table->addColumn('dfoo1', 'decimal');
Sergei Morozov's avatar
Sergei Morozov committed
521 522 523
        $table->addColumn('dfoo2', 'decimal', ['precision' => 10, 'scale' => 6]);
        $table->addColumn('dfoo3', 'decimal', ['precision' => 10, 'scale' => 6]);
        $table->addColumn('dfoo4', 'decimal', ['precision' => 10, 'scale' => 6]);
524

Sergei Morozov's avatar
Sergei Morozov committed
525
        $tableDiff            = new TableDiff('mytable');
526 527
        $tableDiff->fromTable = $table;

Sergei Morozov's avatar
Sergei Morozov committed
528 529 530 531 532 533
        $tableDiff->changedColumns['dloo1'] = new ColumnDiff(
            'dloo1',
            new Column(
                'dloo1',
                Type::getType('decimal'),
                ['precision' => 16, 'scale' => 6]
534
            ),
Sergei Morozov's avatar
Sergei Morozov committed
535 536 537 538 539 540 541 542
            ['precision']
        );
        $tableDiff->changedColumns['dloo2'] = new ColumnDiff(
            'dloo2',
            new Column(
                'dloo2',
                Type::getType('decimal'),
                ['precision' => 10, 'scale' => 4]
543
            ),
Sergei Morozov's avatar
Sergei Morozov committed
544 545 546 547 548 549 550 551
            ['scale']
        );
        $tableDiff->changedColumns['dloo3'] = new ColumnDiff(
            'dloo3',
            new Column(
                'dloo3',
                Type::getType('decimal'),
                ['precision' => 10, 'scale' => 6]
552
            ),
Sergei Morozov's avatar
Sergei Morozov committed
553 554 555 556 557 558 559 560
            []
        );
        $tableDiff->changedColumns['dloo4'] = new ColumnDiff(
            'dloo4',
            new Column(
                'dloo4',
                Type::getType('decimal'),
                ['precision' => 16, 'scale' => 8]
561
            ),
Sergei Morozov's avatar
Sergei Morozov committed
562
            ['precision', 'scale']
563 564
        );

Sergei Morozov's avatar
Sergei Morozov committed
565
        $sql = $this->platform->getAlterTableSQL($tableDiff);
566

Sergei Morozov's avatar
Sergei Morozov committed
567
        $expectedSql = [
568 569 570
            'ALTER TABLE mytable ALTER dloo1 TYPE NUMERIC(16, 6)',
            'ALTER TABLE mytable ALTER dloo2 TYPE NUMERIC(10, 4)',
            'ALTER TABLE mytable ALTER dloo4 TYPE NUMERIC(16, 8)',
Sergei Morozov's avatar
Sergei Morozov committed
571
        ];
572

573
        self::assertEquals($expectedSql, $sql);
574 575 576 577 578
    }

    /**
     * @group DBAL-365
     */
579
    public function testDroppingConstraintsBeforeColumns() : void
580 581 582
    {
        $newTable = new Table('mytable');
        $newTable->addColumn('id', 'integer');
Sergei Morozov's avatar
Sergei Morozov committed
583
        $newTable->setPrimaryKey(['id']);
584 585 586

        $oldTable = clone $newTable;
        $oldTable->addColumn('parent_id', 'integer');
Sergei Morozov's avatar
Sergei Morozov committed
587
        $oldTable->addUnnamedForeignKeyConstraint('mytable', ['parent_id'], ['id']);
588

Sergei Morozov's avatar
Sergei Morozov committed
589 590
        $comparator = new Comparator();
        $tableDiff  = $comparator->diffTable($oldTable, $newTable);
591

Sergei Morozov's avatar
Sergei Morozov committed
592
        $sql = $this->platform->getAlterTableSQL($tableDiff);
593

Sergei Morozov's avatar
Sergei Morozov committed
594
        $expectedSql = [
595 596 597
            'ALTER TABLE mytable DROP CONSTRAINT FK_6B2BD609727ACA70',
            'DROP INDEX IDX_6B2BD609727ACA70',
            'ALTER TABLE mytable DROP parent_id',
Sergei Morozov's avatar
Sergei Morozov committed
598
        ];
599

600
        self::assertEquals($expectedSql, $sql);
601 602 603 604 605
    }

    /**
     * @group DBAL-563
     */
606
    public function testUsesSequenceEmulatedIdentityColumns() : void
607
    {
Sergei Morozov's avatar
Sergei Morozov committed
608
        self::assertTrue($this->platform->usesSequenceEmulatedIdentityColumns());
609 610 611 612 613
    }

    /**
     * @group DBAL-563
     */
614
    public function testReturnsIdentitySequenceName() : void
615
    {
Sergei Morozov's avatar
Sergei Morozov committed
616
        self::assertSame('mytable_mycolumn_seq', $this->platform->getIdentitySequenceName('mytable', 'mycolumn'));
617 618 619 620 621 622
    }

    /**
     * @dataProvider dataCreateSequenceWithCache
     * @group DBAL-139
     */
623
    public function testCreateSequenceWithCache(int $cacheSize, string $expectedSql) : void
624
    {
Sergei Morozov's avatar
Sergei Morozov committed
625
        $sequence = new Sequence('foo', 1, 1, $cacheSize);
626
        self::assertStringContainsString($expectedSql, $this->platform->getCreateSequenceSQL($sequence));
627 628
    }

629 630 631 632
    /**
     * @return mixed[][]
     */
    public static function dataCreateSequenceWithCache() : iterable
633
    {
Sergei Morozov's avatar
Sergei Morozov committed
634 635 636
        return [
            [3, 'CACHE 3'],
        ];
637 638
    }

639
    protected function getBinaryDefaultLength() : int
640 641 642 643
    {
        return 0;
    }

644
    protected function getBinaryMaxLength() : int
645 646 647 648
    {
        return 0;
    }

649
    public function testReturnsBinaryTypeDeclarationSQL() : void
650
    {
Sergei Morozov's avatar
Sergei Morozov committed
651 652 653
        self::assertSame('BYTEA', $this->platform->getBinaryTypeDeclarationSQL([]));
        self::assertSame('BYTEA', $this->platform->getBinaryTypeDeclarationSQL(['length' => 0]));
        self::assertSame('BYTEA', $this->platform->getBinaryTypeDeclarationSQL(['length' => 9999999]));
654

Sergei Morozov's avatar
Sergei Morozov committed
655 656 657
        self::assertSame('BYTEA', $this->platform->getBinaryTypeDeclarationSQL(['fixed' => true]));
        self::assertSame('BYTEA', $this->platform->getBinaryTypeDeclarationSQL(['fixed' => true, 'length' => 0]));
        self::assertSame('BYTEA', $this->platform->getBinaryTypeDeclarationSQL(['fixed' => true, 'length' => 9999999]));
658 659
    }

660
    public function testDoesNotPropagateUnnecessaryTableAlterationOnBinaryType() : void
661 662 663
    {
        $table1 = new Table('mytable');
        $table1->addColumn('column_varbinary', 'binary');
Sergei Morozov's avatar
Sergei Morozov committed
664
        $table1->addColumn('column_binary', 'binary', ['fixed' => true]);
665 666 667
        $table1->addColumn('column_blob', 'blob');

        $table2 = new Table('mytable');
Sergei Morozov's avatar
Sergei Morozov committed
668
        $table2->addColumn('column_varbinary', 'binary', ['fixed' => true]);
669 670 671 672 673 674 675 676
        $table2->addColumn('column_binary', 'binary');
        $table2->addColumn('column_blob', 'binary');

        $comparator = new Comparator();

        // VARBINARY -> BINARY
        // BINARY    -> VARBINARY
        // BLOB      -> VARBINARY
Sergei Morozov's avatar
Sergei Morozov committed
677
        self::assertEmpty($this->platform->getAlterTableSQL($comparator->diffTable($table1, $table2)));
678 679

        $table2 = new Table('mytable');
Sergei Morozov's avatar
Sergei Morozov committed
680
        $table2->addColumn('column_varbinary', 'binary', ['length' => 42]);
681
        $table2->addColumn('column_binary', 'blob');
Sergei Morozov's avatar
Sergei Morozov committed
682
        $table2->addColumn('column_blob', 'binary', ['length' => 11, 'fixed' => true]);
683 684 685 686

        // VARBINARY -> VARBINARY with changed length
        // BINARY    -> BLOB
        // BLOB      -> BINARY
Sergei Morozov's avatar
Sergei Morozov committed
687
        self::assertEmpty($this->platform->getAlterTableSQL($comparator->diffTable($table1, $table2)));
688 689 690

        $table2 = new Table('mytable');
        $table2->addColumn('column_varbinary', 'blob');
Sergei Morozov's avatar
Sergei Morozov committed
691
        $table2->addColumn('column_binary', 'binary', ['length' => 42, 'fixed' => true]);
692 693 694 695 696
        $table2->addColumn('column_blob', 'blob');

        // VARBINARY -> BLOB
        // BINARY    -> BINARY with changed length
        // BLOB      -> BLOB
Sergei Morozov's avatar
Sergei Morozov committed
697
        self::assertEmpty($this->platform->getAlterTableSQL($comparator->diffTable($table1, $table2)));
698 699 700
    }

    /**
701 702
     * {@inheritDoc}
     *
703 704
     * @group DBAL-234
     */
705
    protected function getAlterTableRenameIndexSQL() : array
706
    {
Sergei Morozov's avatar
Sergei Morozov committed
707
        return ['ALTER INDEX idx_foo RENAME TO idx_bar'];
708 709 710
    }

    /**
711 712
     * {@inheritDoc}
     *
713 714
     * @group DBAL-234
     */
715
    protected function getQuotedAlterTableRenameIndexSQL() : array
716
    {
Sergei Morozov's avatar
Sergei Morozov committed
717
        return [
718 719
            'ALTER INDEX "create" RENAME TO "select"',
            'ALTER INDEX "foo" RENAME TO "bar"',
Sergei Morozov's avatar
Sergei Morozov committed
720
        ];
721
    }
722

723 724
    /**
     * PostgreSQL boolean strings provider
Sergei Morozov's avatar
Sergei Morozov committed
725
     *
726
     * @return mixed[][]
727
     */
728
    public static function pgBooleanProvider() : iterable
729
    {
Sergei Morozov's avatar
Sergei Morozov committed
730
        return [
731
            // Database value, prepared statement value, boolean integer value, boolean value.
Sergei Morozov's avatar
Sergei Morozov committed
732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749
            [true, 'true', 1, true],
            ['t', 'true', 1, true],
            ['true', 'true', 1, true],
            ['y', 'true', 1, true],
            ['yes', 'true', 1, true],
            ['on', 'true', 1, true],
            ['1', 'true', 1, true],

            [false, 'false', 0, false],
            ['f', 'false', 0, false],
            ['false', 'false', 0, false],
            [ 'n', 'false', 0, false],
            ['no', 'false', 0, false],
            ['off', 'false', 0, false],
            ['0', 'false', 0, false],

            [null, 'NULL', null, null],
        ];
750 751
    }

752 753 754
    /**
     * {@inheritdoc}
     */
755
    protected function getQuotedAlterTableRenameColumnSQL() : array
756
    {
Sergei Morozov's avatar
Sergei Morozov committed
757
        return [
758 759 760 761 762 763 764 765 766
            'ALTER TABLE mytable RENAME COLUMN unquoted1 TO unquoted',
            'ALTER TABLE mytable RENAME COLUMN unquoted2 TO "where"',
            'ALTER TABLE mytable RENAME COLUMN unquoted3 TO "foo"',
            'ALTER TABLE mytable RENAME COLUMN "create" TO reserved_keyword',
            'ALTER TABLE mytable RENAME COLUMN "table" TO "from"',
            'ALTER TABLE mytable RENAME COLUMN "select" TO "bar"',
            'ALTER TABLE mytable RENAME COLUMN quoted1 TO quoted',
            'ALTER TABLE mytable RENAME COLUMN quoted2 TO "and"',
            'ALTER TABLE mytable RENAME COLUMN quoted3 TO "baz"',
Sergei Morozov's avatar
Sergei Morozov committed
767
        ];
768
    }
769

770 771 772
    /**
     * {@inheritdoc}
     */
773
    protected function getQuotedAlterTableChangeColumnLengthSQL() : array
774
    {
Sergei Morozov's avatar
Sergei Morozov committed
775
        return [
776 777 778 779 780 781
            'ALTER TABLE mytable ALTER unquoted1 TYPE VARCHAR(255)',
            'ALTER TABLE mytable ALTER unquoted2 TYPE VARCHAR(255)',
            'ALTER TABLE mytable ALTER unquoted3 TYPE VARCHAR(255)',
            'ALTER TABLE mytable ALTER "create" TYPE VARCHAR(255)',
            'ALTER TABLE mytable ALTER "table" TYPE VARCHAR(255)',
            'ALTER TABLE mytable ALTER "select" TYPE VARCHAR(255)',
Sergei Morozov's avatar
Sergei Morozov committed
782
        ];
783 784
    }

785
    /**
786 787
     * {@inheritDoc}
     *
788 789
     * @group DBAL-807
     */
790
    protected function getAlterTableRenameIndexInSchemaSQL() : array
791
    {
Sergei Morozov's avatar
Sergei Morozov committed
792
        return ['ALTER INDEX myschema.idx_foo RENAME TO idx_bar'];
793 794 795
    }

    /**
796 797
     * {@inheritDoc}
     *
798 799
     * @group DBAL-807
     */
800
    protected function getQuotedAlterTableRenameIndexInSchemaSQL() : array
801
    {
Sergei Morozov's avatar
Sergei Morozov committed
802
        return [
803 804
            'ALTER INDEX "schema"."create" RENAME TO "select"',
            'ALTER INDEX "schema"."foo" RENAME TO "bar"',
Sergei Morozov's avatar
Sergei Morozov committed
805
        ];
806
    }
807

808
    protected function getQuotesDropForeignKeySQL() : string
809 810 811 812
    {
        return 'ALTER TABLE "table" DROP CONSTRAINT "select"';
    }

813
    public function testGetNullCommentOnColumnSQL() : void
814
    {
815
        self::assertEquals(
Sergei Morozov's avatar
Sergei Morozov committed
816
            'COMMENT ON COLUMN mytable.id IS NULL',
Sergei Morozov's avatar
Sergei Morozov committed
817
            $this->platform->getCommentOnColumnSQL('mytable', 'id', null)
818 819
        );
    }
820 821 822 823

    /**
     * @group DBAL-423
     */
824
    public function testReturnsGuidTypeDeclarationSQL() : void
825
    {
Sergei Morozov's avatar
Sergei Morozov committed
826
        self::assertSame('UUID', $this->platform->getGuidTypeDeclarationSQL([]));
827
    }
828 829 830 831

    /**
     * {@inheritdoc}
     */
832
    public function getAlterTableRenameColumnSQL() : array
833
    {
Sergei Morozov's avatar
Sergei Morozov committed
834
        return ['ALTER TABLE foo RENAME COLUMN bar TO baz'];
835
    }
836 837 838 839

    /**
     * {@inheritdoc}
     */
840
    protected function getQuotesTableIdentifiersInAlterTableSQL() : array
841
    {
Sergei Morozov's avatar
Sergei Morozov committed
842
        return [
843 844 845 846 847 848 849 850 851 852 853
            'ALTER TABLE "foo" DROP CONSTRAINT fk1',
            'ALTER TABLE "foo" DROP CONSTRAINT fk2',
            'ALTER TABLE "foo" ADD bloo INT NOT NULL',
            'ALTER TABLE "foo" DROP baz',
            'ALTER TABLE "foo" ALTER bar DROP NOT NULL',
            'ALTER TABLE "foo" RENAME COLUMN id TO war',
            'ALTER TABLE "foo" RENAME TO "table"',
            'ALTER TABLE "table" ADD CONSTRAINT fk_add FOREIGN KEY (fk3) REFERENCES fk_table (id) NOT DEFERRABLE ' .
            'INITIALLY IMMEDIATE',
            'ALTER TABLE "table" ADD CONSTRAINT fk2 FOREIGN KEY (fk2) REFERENCES fk_table2 (id) NOT DEFERRABLE ' .
            'INITIALLY IMMEDIATE',
Sergei Morozov's avatar
Sergei Morozov committed
854
        ];
855
    }
856 857 858 859

    /**
     * {@inheritdoc}
     */
860
    protected function getCommentOnColumnSQL() : array
861
    {
Sergei Morozov's avatar
Sergei Morozov committed
862
        return [
863 864 865
            'COMMENT ON COLUMN foo.bar IS \'comment\'',
            'COMMENT ON COLUMN "Foo"."BAR" IS \'comment\'',
            'COMMENT ON COLUMN "select"."from" IS \'comment\'',
Sergei Morozov's avatar
Sergei Morozov committed
866
        ];
867 868 869 870 871
    }

    /**
     * @group DBAL-1004
     */
872
    public function testAltersTableColumnCommentWithExplicitlyQuotedIdentifiers() : void
873
    {
Sergei Morozov's avatar
Sergei Morozov committed
874 875
        $table1 = new Table('"foo"', [new Column('"bar"', Type::getType('integer'))]);
        $table2 = new Table('"foo"', [new Column('"bar"', Type::getType('integer'), ['comment' => 'baz'])]);
876 877 878 879 880

        $comparator = new Comparator();

        $tableDiff = $comparator->diffTable($table1, $table2);

Sergei Morozov's avatar
Sergei Morozov committed
881
        self::assertInstanceOf(TableDiff::class, $tableDiff);
882
        self::assertSame(
Sergei Morozov's avatar
Sergei Morozov committed
883
            ['COMMENT ON COLUMN "foo"."bar" IS \'baz\''],
Sergei Morozov's avatar
Sergei Morozov committed
884
            $this->platform->getAlterTableSQL($tableDiff)
885 886
        );
    }
887

888 889 890
    /**
     * @group 3158
     */
891
    public function testAltersTableColumnCommentIfRequiredByType() : void
892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910
    {
        $table1 = new Table('"foo"', [new Column('"bar"', Type::getType('datetime'))]);
        $table2 = new Table('"foo"', [new Column('"bar"', Type::getType('datetime_immutable'))]);

        $comparator = new Comparator();

        $tableDiff = $comparator->diffTable($table1, $table2);

        $this->assertInstanceOf('Doctrine\DBAL\Schema\TableDiff', $tableDiff);
        $this->assertSame(
            [
                'ALTER TABLE "foo" ALTER "bar" TYPE TIMESTAMP(0) WITHOUT TIME ZONE',
                'ALTER TABLE "foo" ALTER "bar" DROP DEFAULT',
                'COMMENT ON COLUMN "foo"."bar" IS \'(DC2Type:datetime_immutable)\'',
            ],
            $this->platform->getAlterTableSQL($tableDiff)
        );
    }

911
    protected function getQuotesReservedKeywordInUniqueConstraintDeclarationSQL() : string
912 913 914 915
    {
        return 'CONSTRAINT "select" UNIQUE (foo)';
    }

916
    protected function getQuotesReservedKeywordInIndexDeclarationSQL() : string
917 918 919
    {
        return 'INDEX "select" (foo)';
    }
920

921
    protected function getQuotesReservedKeywordInTruncateTableSQL() : string
922 923 924 925
    {
        return 'TRUNCATE "select"';
    }

926
    /**
927
     * {@inheritdoc}
928
     */
929
    protected function getAlterStringToFixedStringSQL() : array
930
    {
Sergei Morozov's avatar
Sergei Morozov committed
931
        return ['ALTER TABLE mytable ALTER name TYPE CHAR(2)'];
932
    }
933 934 935 936

    /**
     * {@inheritdoc}
     */
937
    protected function getGeneratesAlterTableRenameIndexUsedByForeignKeySQL() : array
938
    {
Sergei Morozov's avatar
Sergei Morozov committed
939
        return ['ALTER INDEX idx_foo RENAME TO idx_foo_renamed'];
940
    }
Steve Müller's avatar
Steve Müller committed
941 942 943 944

    /**
     * @group DBAL-1142
     */
945
    public function testInitializesTsvectorTypeMapping() : void
Steve Müller's avatar
Steve Müller committed
946
    {
Sergei Morozov's avatar
Sergei Morozov committed
947 948
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('tsvector'));
        self::assertEquals('text', $this->platform->getDoctrineTypeMapping('tsvector'));
Steve Müller's avatar
Steve Müller committed
949
    }
950 951 952 953

    /**
     * @group DBAL-1220
     */
954
    public function testReturnsDisallowDatabaseConnectionsSQL() : void
955
    {
956
        self::assertSame(
957
            "UPDATE pg_database SET datallowconn = 'false' WHERE datname = 'foo'",
Sergei Morozov's avatar
Sergei Morozov committed
958
            $this->platform->getDisallowDatabaseConnectionsSQL('foo')
959 960 961 962 963 964
        );
    }

    /**
     * @group DBAL-1220
     */
965
    public function testReturnsCloseActiveDatabaseConnectionsSQL() : void
966
    {
967
        self::assertSame(
968
            "SELECT pg_terminate_backend(procpid) FROM pg_stat_activity WHERE datname = 'foo'",
Sergei Morozov's avatar
Sergei Morozov committed
969
            $this->platform->getCloseActiveDatabaseConnectionsSQL('foo')
970 971
        );
    }
972 973 974 975

    /**
     * @group DBAL-2436
     */
976
    public function testQuotesTableNameInListTableForeignKeysSQL() : void
977
    {
978 979 980 981
        self::assertStringContainsStringIgnoringCase(
            "'Foo''Bar\\'",
            $this->platform->getListTableForeignKeysSQL("Foo'Bar\\")
        );
982 983 984 985 986
    }

    /**
     * @group DBAL-2436
     */
987
    public function testQuotesSchemaNameInListTableForeignKeysSQL() : void
988
    {
989
        self::assertStringContainsStringIgnoringCase(
990
            "'Foo''Bar\\'",
991
            $this->platform->getListTableForeignKeysSQL("Foo'Bar\\.baz_table")
992 993 994 995 996 997
        );
    }

    /**
     * @group DBAL-2436
     */
998
    public function testQuotesTableNameInListTableConstraintsSQL() : void
999
    {
1000 1001 1002 1003
        self::assertStringContainsStringIgnoringCase(
            "'Foo''Bar\\'",
            $this->platform->getListTableConstraintsSQL("Foo'Bar\\")
        );
1004 1005 1006 1007 1008
    }

    /**
     * @group DBAL-2436
     */
1009
    public function testQuotesTableNameInListTableIndexesSQL() : void
1010
    {
1011 1012 1013 1014
        self::assertStringContainsStringIgnoringCase(
            "'Foo''Bar\\'",
            $this->platform->getListTableIndexesSQL("Foo'Bar\\")
        );
1015 1016 1017 1018 1019
    }

    /**
     * @group DBAL-2436
     */
1020
    public function testQuotesSchemaNameInListTableIndexesSQL() : void
1021
    {
1022
        self::assertStringContainsStringIgnoringCase(
1023
            "'Foo''Bar\\'",
1024
            $this->platform->getListTableIndexesSQL("Foo'Bar\\.baz_table")
1025 1026 1027 1028 1029 1030
        );
    }

    /**
     * @group DBAL-2436
     */
1031
    public function testQuotesTableNameInListTableColumnsSQL() : void
1032
    {
1033 1034 1035 1036
        self::assertStringContainsStringIgnoringCase(
            "'Foo''Bar\\'",
            $this->platform->getListTableColumnsSQL("Foo'Bar\\")
        );
1037 1038 1039 1040 1041
    }

    /**
     * @group DBAL-2436
     */
1042
    public function testQuotesSchemaNameInListTableColumnsSQL() : void
1043
    {
1044
        self::assertStringContainsStringIgnoringCase(
1045
            "'Foo''Bar\\'",
1046
            $this->platform->getListTableColumnsSQL("Foo'Bar\\.baz_table")
1047 1048 1049 1050 1051 1052
        );
    }

    /**
     * @group DBAL-2436
     */
1053
    public function testQuotesDatabaseNameInCloseActiveDatabaseConnectionsSQL() : void
1054
    {
1055
        self::assertStringContainsStringIgnoringCase(
1056
            "'Foo''Bar\\'",
1057
            $this->platform->getCloseActiveDatabaseConnectionsSQL("Foo'Bar\\")
1058 1059
        );
    }
1060
}