AbstractPostgreSQLPlatformTestCase   F
last analyzed

Complexity

Total Complexity 80

Size/Duplication

Total Lines 1005
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 389
dl 0
loc 1005
rs 2
c 0
b 0
f 0
wmc 80

80 Methods

Rating   Name   Duplication   Size   Complexity  
A getQuotesDropForeignKeySQL() 0 3 1
A testSupportsIdentityColumns() 0 3 1
A testConvertBooleanAsLiteralStrings() 0 7 1
A getQuotesReservedKeywordInTruncateTableSQL() 0 3 1
A getCreateTableColumnCommentsSQL() 0 5 1
A getQuotedAlterTableRenameIndexSQL() 0 5 1
A getExpectedVariableLengthBinaryTypeDeclarationSQLNoLength() 0 3 1
A getAlterTableColumnCommentsSQL() 0 7 1
A testQuotesTableNameInListTableIndexesSQL() 0 5 1
A testGeneratesTransactionCommands() 0 17 1
A testDoesNotPropagateUnnecessaryTableAlterationOnBinaryType() 0 50 1
A testConvertBooleanAsDatabaseValueStrings() 0 7 1
A testInitializesTsvectorTypeMapping() 0 4 1
A testGetCreateSchemaSQL() 0 5 1
A testQuotesTableNameInListTableForeignKeysSQL() 0 5 1
A getGenerateUniqueIndexSql() 0 3 1
A testGenerateTableWithAutoincrement() 0 7 1
A getGenerateTableSql() 0 3 1
A testConvertBooleanAsLiteralIntegers() 0 9 1
A getAlterTableRenameColumnSQL() 0 3 1
A getGenerateTableWithMultiColumnUniqueIndexSql() 0 5 1
A getQuotesReservedKeywordInIndexDeclarationSQL() 0 3 1
A testReturnsIdentitySequenceName() 0 3 1
A testCreateSequenceWithCache() 0 4 1
A testPrefersSequences() 0 3 1
A testAltersTableColumnCommentWithExplicitlyQuotedIdentifiers() 0 13 1
A getQuotedColumnInForeignKeySQL() 0 7 1
A testSupportsSequences() 0 3 1
A testConvertFromBoolean() 0 3 1
A testQuotesDatabaseNameInCloseActiveDatabaseConnectionsSQL() 0 5 1
A getQuotedNameInIndexSQL() 0 5 1
A testGetDefaultValueDeclarationSQLIgnoresTheDefaultKeyWhenTheFieldIsSerial() 0 11 1
A getQuotedColumnInIndexSQL() 0 5 1
A getCreateTableColumnTypeCommentsSQL() 0 5 1
A testModifyLimitQueryWithEmptyOffset() 0 4 1
A testQuotesSchemaNameInListTableForeignKeysSQL() 0 5 1
A getAlterTableRenameIndexInSchemaSQL() 0 3 1
A dataCreateSequenceWithCache() 0 4 1
A getAlterTableRenameIndexSQL() 0 3 1
A testUsesSequenceEmulatedIdentityColumns() 0 3 1
A testAltersTableColumnCommentIfRequiredByType() 0 17 1
A getCommentOnColumnSQL() 0 6 1
A getQuotedAlterTableRenameColumnSQL() 0 12 1
A getExpectedVariableLengthBinaryTypeDeclarationSQLWithLength() 0 3 1
A testGeneratesForeignKeySqlForNonStandardOptions() 0 72 1
A testReturnsCloseActiveDatabaseConnectionsSQL() 0 5 1
A testReturnsGuidTypeDeclarationSQL() 0 3 1
A testQuotesTableNameInListTableConstraintsSQL() 0 5 1
A testDoesNotPreferIdentityColumns() 0 3 1
A testQuotesSchemaNameInListTableIndexesSQL() 0 5 1
A getExpectedFixedLengthBinaryTypeDeclarationSQLWithLength() 0 3 1
A getGeneratesAlterTableRenameIndexUsedByForeignKeySQL() 0 3 1
A getGenerateIndexSql() 0 3 1
A testReturnsDisallowDatabaseConnectionsSQL() 0 5 1
A testDroppingConstraintsBeforeColumns() 0 24 1
A getExpectedFixedLengthBinaryTypeDeclarationSQLNoLength() 0 3 1
A getQuotedAlterTableRenameIndexInSchemaSQL() 0 5 1
A supportsCommentOnStatement() 0 3 1
A getQuotesReservedKeywordInUniqueConstraintDeclarationSQL() 0 3 1
A testQuotesTableNameInListTableColumnsSQL() 0 5 1
A getGenerateForeignKeySql() 0 3 1
A testAlterDecimalPrecisionScale() 0 57 1
A testSupportsSavePoints() 0 3 1
A getQuotedAlterTableChangeColumnLengthSQL() 0 9 1
A testModifyLimitQuery() 0 4 1
A getAlterStringToFixedStringSQL() 0 3 1
A testCreateTableWithAutoincrementAndNotNullAddsConstraint() 0 10 1
A getQuotesTableIdentifiersInAlterTableSQL() 0 14 1
A testConvertBooleanAsDatabaseValueIntegers() 0 6 1
A testGeneratesDDLSnippets() 0 5 1
A testGenerateTableWithAutoincrementDoesNotSetDefault() 0 10 1
A testGeneratesTypeDeclarationForIntegers() 0 14 1
A getGenerateAlterTableSql() 0 12 1
A testGeneratesSqlSnippets() 0 7 1
A serialTypes() 0 5 1
A testQuotesSchemaNameInListTableColumnsSQL() 0 5 1
A pgBooleanProvider() 0 17 1
A testThrowsExceptionWithInvalidBooleanLiteral() 0 6 1
A testGeneratesSequenceSqlCommands() 0 14 1
A getQuotedColumnInPrimaryKeySQL() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like AbstractPostgreSQLPlatformTestCase often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use AbstractPostgreSQLPlatformTestCase, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\DBAL\Tests\Platforms;
6
7
use Doctrine\DBAL\Platforms\PostgreSQL94Platform;
8
use Doctrine\DBAL\Schema\Column;
9
use Doctrine\DBAL\Schema\ColumnDiff;
10
use Doctrine\DBAL\Schema\Comparator;
11
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
12
use Doctrine\DBAL\Schema\Sequence;
13
use Doctrine\DBAL\Schema\Table;
14
use Doctrine\DBAL\Schema\TableDiff;
15
use Doctrine\DBAL\TransactionIsolationLevel;
16
use Doctrine\DBAL\Types\Type;
17
use UnexpectedValueException;
18
use function sprintf;
19
20
abstract class AbstractPostgreSQLPlatformTestCase extends AbstractPlatformTestCase
21
{
22
    /** @var PostgreSQL94Platform */
23
    protected $platform;
24
25
    public function getGenerateTableSql() : string
26
    {
27
        return 'CREATE TABLE test (id SERIAL NOT NULL, test VARCHAR(255) DEFAULT NULL, PRIMARY KEY(id))';
28
    }
29
30
    /**
31
     * {@inheritDoc}
32
     */
33
    public function getGenerateTableWithMultiColumnUniqueIndexSql() : array
34
    {
35
        return [
36
            'CREATE TABLE test (foo VARCHAR(255) DEFAULT NULL, bar VARCHAR(255) DEFAULT NULL)',
37
            'CREATE UNIQUE INDEX UNIQ_D87F7E0C8C73652176FF8CAA ON test (foo, bar)',
38
        ];
39
    }
40
41
    /**
42
     * {@inheritDoc}
43
     */
44
    public function getGenerateAlterTableSql() : array
45
    {
46
        return [
47
            'ALTER TABLE mytable ADD quota INT DEFAULT NULL',
48
            'ALTER TABLE mytable DROP foo',
49
            'ALTER TABLE mytable ALTER bar TYPE VARCHAR(255)',
50
            "ALTER TABLE mytable ALTER bar SET DEFAULT 'def'",
51
            'ALTER TABLE mytable ALTER bar SET NOT NULL',
52
            'ALTER TABLE mytable ALTER bloo TYPE BOOLEAN',
53
            "ALTER TABLE mytable ALTER bloo SET DEFAULT 'false'",
54
            'ALTER TABLE mytable ALTER bloo SET NOT NULL',
55
            'ALTER TABLE mytable RENAME TO userlist',
56
        ];
57
    }
58
59
    public function getGenerateIndexSql() : string
60
    {
61
        return 'CREATE INDEX my_idx ON mytable (user_name, last_login)';
62
    }
63
64
    public function getGenerateForeignKeySql() : string
65
    {
66
        return 'ALTER TABLE test ADD FOREIGN KEY (fk_name_id) REFERENCES other_table (id) NOT DEFERRABLE INITIALLY IMMEDIATE';
67
    }
68
69
    public function testGeneratesForeignKeySqlForNonStandardOptions() : void
70
    {
71
        $foreignKey = new ForeignKeyConstraint(
72
            ['foreign_id'],
73
            'my_table',
74
            ['id'],
75
            'my_fk',
76
            ['onDelete' => 'CASCADE']
77
        );
78
        self::assertEquals(
79
            'CONSTRAINT my_fk FOREIGN KEY (foreign_id) REFERENCES my_table (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE',
80
            $this->platform->getForeignKeyDeclarationSQL($foreignKey)
81
        );
82
83
        $foreignKey = new ForeignKeyConstraint(
84
            ['foreign_id'],
85
            'my_table',
86
            ['id'],
87
            'my_fk',
88
            ['match' => 'full']
89
        );
90
        self::assertEquals(
91
            'CONSTRAINT my_fk FOREIGN KEY (foreign_id) REFERENCES my_table (id) MATCH full NOT DEFERRABLE INITIALLY IMMEDIATE',
92
            $this->platform->getForeignKeyDeclarationSQL($foreignKey)
93
        );
94
95
        $foreignKey = new ForeignKeyConstraint(
96
            ['foreign_id'],
97
            'my_table',
98
            ['id'],
99
            'my_fk',
100
            ['deferrable' => true]
101
        );
102
        self::assertEquals(
103
            'CONSTRAINT my_fk FOREIGN KEY (foreign_id) REFERENCES my_table (id) DEFERRABLE INITIALLY IMMEDIATE',
104
            $this->platform->getForeignKeyDeclarationSQL($foreignKey)
105
        );
106
107
        $foreignKey = new ForeignKeyConstraint(
108
            ['foreign_id'],
109
            'my_table',
110
            ['id'],
111
            'my_fk',
112
            ['deferred' => true]
113
        );
114
        self::assertEquals(
115
            'CONSTRAINT my_fk FOREIGN KEY (foreign_id) REFERENCES my_table (id) NOT DEFERRABLE INITIALLY DEFERRED',
116
            $this->platform->getForeignKeyDeclarationSQL($foreignKey)
117
        );
118
119
        $foreignKey = new ForeignKeyConstraint(
120
            ['foreign_id'],
121
            'my_table',
122
            ['id'],
123
            'my_fk',
124
            ['deferred' => true]
125
        );
126
        self::assertEquals(
127
            'CONSTRAINT my_fk FOREIGN KEY (foreign_id) REFERENCES my_table (id) NOT DEFERRABLE INITIALLY DEFERRED',
128
            $this->platform->getForeignKeyDeclarationSQL($foreignKey)
129
        );
130
131
        $foreignKey = new ForeignKeyConstraint(
132
            ['foreign_id'],
133
            'my_table',
134
            ['id'],
135
            'my_fk',
136
            ['deferrable' => true, 'deferred' => true, 'match' => 'full']
137
        );
138
        self::assertEquals(
139
            'CONSTRAINT my_fk FOREIGN KEY (foreign_id) REFERENCES my_table (id) MATCH full DEFERRABLE INITIALLY DEFERRED',
140
            $this->platform->getForeignKeyDeclarationSQL($foreignKey)
141
        );
142
    }
143
144
    public function testGeneratesSqlSnippets() : void
145
    {
146
        self::assertEquals('SIMILAR TO', $this->platform->getRegexpExpression(), 'Regular expression operator is not correct');
147
        self::assertEquals('"', $this->platform->getIdentifierQuoteCharacter(), 'Identifier quote character is not correct');
148
        self::assertEquals('column1 || column2 || column3', $this->platform->getConcatExpression('column1', 'column2', 'column3'), 'Concatenation expression is not correct');
149
        self::assertEquals('SUBSTRING(column FROM 5)', $this->platform->getSubstringExpression('column', '5'), 'Substring expression without length is not correct');
150
        self::assertEquals('SUBSTRING(column FROM 1 FOR 5)', $this->platform->getSubstringExpression('column', '1', '5'), 'Substring expression with length is not correct');
151
    }
152
153
    public function testGeneratesTransactionCommands() : void
154
    {
155
        self::assertEquals(
156
            'SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL READ UNCOMMITTED',
157
            $this->platform->getSetTransactionIsolationSQL(TransactionIsolationLevel::READ_UNCOMMITTED)
158
        );
159
        self::assertEquals(
160
            'SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL READ COMMITTED',
161
            $this->platform->getSetTransactionIsolationSQL(TransactionIsolationLevel::READ_COMMITTED)
162
        );
163
        self::assertEquals(
164
            'SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL REPEATABLE READ',
165
            $this->platform->getSetTransactionIsolationSQL(TransactionIsolationLevel::REPEATABLE_READ)
166
        );
167
        self::assertEquals(
168
            'SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE',
169
            $this->platform->getSetTransactionIsolationSQL(TransactionIsolationLevel::SERIALIZABLE)
170
        );
171
    }
172
173
    public function testGeneratesDDLSnippets() : void
174
    {
175
        self::assertEquals('CREATE DATABASE foobar', $this->platform->getCreateDatabaseSQL('foobar'));
176
        self::assertEquals('DROP DATABASE foobar', $this->platform->getDropDatabaseSQL('foobar'));
177
        self::assertEquals('DROP TABLE foobar', $this->platform->getDropTableSQL('foobar'));
178
    }
179
180
    public function testGenerateTableWithAutoincrement() : void
181
    {
182
        $table  = new Table('autoinc_table');
183
        $column = $table->addColumn('id', 'integer');
184
        $column->setAutoincrement(true);
185
186
        self::assertEquals(['CREATE TABLE autoinc_table (id SERIAL NOT NULL)'], $this->platform->getCreateTableSQL($table));
187
    }
188
189
    /**
190
     * @return mixed[][]
191
     */
192
    public static function serialTypes() : iterable
193
    {
194
        return [
195
            ['integer', 'SERIAL'],
196
            ['bigint', 'BIGSERIAL'],
197
        ];
198
    }
199
200
    /**
201
     * @dataProvider serialTypes
202
     * @group 2906
203
     */
204
    public function testGenerateTableWithAutoincrementDoesNotSetDefault(string $type, string $definition) : void
205
    {
206
        $table  = new Table('autoinc_table_notnull');
207
        $column = $table->addColumn('id', $type);
208
        $column->setAutoincrement(true);
209
        $column->setNotnull(false);
210
211
        $sql = $this->platform->getCreateTableSQL($table);
212
213
        self::assertEquals([sprintf('CREATE TABLE autoinc_table_notnull (id %s)', $definition)], $sql);
214
    }
215
216
    /**
217
     * @dataProvider serialTypes
218
     * @group 2906
219
     */
220
    public function testCreateTableWithAutoincrementAndNotNullAddsConstraint(string $type, string $definition) : void
221
    {
222
        $table  = new Table('autoinc_table_notnull_enabled');
223
        $column = $table->addColumn('id', $type);
224
        $column->setAutoincrement(true);
225
        $column->setNotnull(true);
226
227
        $sql = $this->platform->getCreateTableSQL($table);
228
229
        self::assertEquals([sprintf('CREATE TABLE autoinc_table_notnull_enabled (id %s NOT NULL)', $definition)], $sql);
230
    }
231
232
    /**
233
     * @dataProvider serialTypes
234
     * @group 2906
235
     */
236
    public function testGetDefaultValueDeclarationSQLIgnoresTheDefaultKeyWhenTheFieldIsSerial(string $type) : void
237
    {
238
        $sql = $this->platform->getDefaultValueDeclarationSQL(
239
            [
240
                'autoincrement' => true,
241
                'type'          => Type::getType($type),
242
                'default'       => 1,
243
            ]
244
        );
245
246
        self::assertSame('', $sql);
247
    }
248
249
    public function testGeneratesTypeDeclarationForIntegers() : void
250
    {
251
        self::assertEquals(
252
            'INT',
253
            $this->platform->getIntegerTypeDeclarationSQL([])
254
        );
255
        self::assertEquals(
256
            'SERIAL',
257
            $this->platform->getIntegerTypeDeclarationSQL(['autoincrement' => true])
258
        );
259
        self::assertEquals(
260
            'SERIAL',
261
            $this->platform->getIntegerTypeDeclarationSQL(
262
                ['autoincrement' => true, 'primary' => true]
263
            )
264
        );
265
    }
266
267
    public function getGenerateUniqueIndexSql() : string
268
    {
269
        return 'CREATE UNIQUE INDEX index_name ON test (test, test2)';
270
    }
271
272
    public function testGeneratesSequenceSqlCommands() : void
273
    {
274
        $sequence = new Sequence('myseq', 20, 1);
275
        self::assertEquals(
276
            'CREATE SEQUENCE myseq INCREMENT BY 20 MINVALUE 1 START 1',
277
            $this->platform->getCreateSequenceSQL($sequence)
278
        );
279
        self::assertEquals(
280
            'DROP SEQUENCE myseq CASCADE',
281
            $this->platform->getDropSequenceSQL('myseq')
282
        );
283
        self::assertEquals(
284
            "SELECT NEXTVAL('myseq')",
285
            $this->platform->getSequenceNextValSQL('myseq')
286
        );
287
    }
288
289
    public function testDoesNotPreferIdentityColumns() : void
290
    {
291
        self::assertFalse($this->platform->prefersIdentityColumns());
292
    }
293
294
    public function testPrefersSequences() : void
295
    {
296
        self::assertTrue($this->platform->prefersSequences());
297
    }
298
299
    public function testSupportsIdentityColumns() : void
300
    {
301
        self::assertTrue($this->platform->supportsIdentityColumns());
302
    }
303
304
    public function testSupportsSavePoints() : void
305
    {
306
        self::assertTrue($this->platform->supportsSavepoints());
307
    }
308
309
    public function testSupportsSequences() : void
310
    {
311
        self::assertTrue($this->platform->supportsSequences());
312
    }
313
314
    protected function supportsCommentOnStatement() : bool
315
    {
316
        return true;
317
    }
318
319
    public function testModifyLimitQuery() : void
320
    {
321
        $sql = $this->platform->modifyLimitQuery('SELECT * FROM user', 10, 0);
322
        self::assertEquals('SELECT * FROM user LIMIT 10', $sql);
323
    }
324
325
    public function testModifyLimitQueryWithEmptyOffset() : void
326
    {
327
        $sql = $this->platform->modifyLimitQuery('SELECT * FROM user', 10);
328
        self::assertEquals('SELECT * FROM user LIMIT 10', $sql);
329
    }
330
331
    /**
332
     * {@inheritDoc}
333
     */
334
    public function getCreateTableColumnCommentsSQL() : array
335
    {
336
        return [
337
            'CREATE TABLE test (id INT NOT NULL, PRIMARY KEY(id))',
338
            "COMMENT ON COLUMN test.id IS 'This is a comment'",
339
        ];
340
    }
341
342
    /**
343
     * {@inheritDoc}
344
     */
345
    public function getAlterTableColumnCommentsSQL() : array
346
    {
347
        return [
348
            'ALTER TABLE mytable ADD quota INT NOT NULL',
349
            "COMMENT ON COLUMN mytable.quota IS 'A comment'",
350
            "COMMENT ON COLUMN mytable.foo IS ''",
351
            "COMMENT ON COLUMN mytable.baz IS 'B comment'",
352
        ];
353
    }
354
355
    /**
356
     * {@inheritDoc}
357
     */
358
    public function getCreateTableColumnTypeCommentsSQL() : array
359
    {
360
        return [
361
            'CREATE TABLE test (id INT NOT NULL, data TEXT NOT NULL, PRIMARY KEY(id))',
362
            "COMMENT ON COLUMN test.data IS '(DC2Type:array)'",
363
        ];
364
    }
365
366
    /**
367
     * {@inheritDoc}
368
     */
369
    protected function getQuotedColumnInPrimaryKeySQL() : array
370
    {
371
        return ['CREATE TABLE "quoted" ("create" VARCHAR(255) NOT NULL, PRIMARY KEY("create"))'];
372
    }
373
374
    /**
375
     * {@inheritDoc}
376
     */
377
    protected function getQuotedColumnInIndexSQL() : array
378
    {
379
        return [
380
            'CREATE TABLE "quoted" ("create" VARCHAR(255) NOT NULL)',
381
            'CREATE INDEX IDX_22660D028FD6E0FB ON "quoted" ("create")',
382
        ];
383
    }
384
385
    /**
386
     * {@inheritDoc}
387
     */
388
    protected function getQuotedNameInIndexSQL() : array
389
    {
390
        return [
391
            'CREATE TABLE test (column1 VARCHAR(255) NOT NULL)',
392
            'CREATE INDEX "key" ON test (column1)',
393
        ];
394
    }
395
396
    /**
397
     * {@inheritDoc}
398
     */
399
    protected function getQuotedColumnInForeignKeySQL() : array
400
    {
401
        return [
402
            'CREATE TABLE "quoted" ("create" VARCHAR(255) NOT NULL, foo VARCHAR(255) NOT NULL, "bar" VARCHAR(255) NOT NULL)',
403
            'ALTER TABLE "quoted" ADD CONSTRAINT FK_WITH_RESERVED_KEYWORD FOREIGN KEY ("create", foo, "bar") REFERENCES "foreign" ("create", bar, "foo-bar") NOT DEFERRABLE INITIALLY IMMEDIATE',
404
            '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',
405
            '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',
406
        ];
407
    }
408
409
    /**
410
     * @group DBAL-457
411
     * @dataProvider pgBooleanProvider
412
     */
413
    public function testConvertBooleanAsLiteralStrings(
414
        string $databaseValue,
415
        string $preparedStatementValue,
416
        int $integerValue,
0 ignored issues
show
Unused Code introduced by
The parameter $integerValue is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

416
        /** @scrutinizer ignore-unused */ int $integerValue,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
417
        bool $booleanValue
0 ignored issues
show
Unused Code introduced by
The parameter $booleanValue is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

417
        /** @scrutinizer ignore-unused */ bool $booleanValue

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
418
    ) : void {
419
        self::assertEquals($preparedStatementValue, $this->platform->convertBooleans($databaseValue));
420
    }
421
422
    /**
423
     * @group DBAL-457
424
     */
425
    public function testConvertBooleanAsLiteralIntegers() : void
426
    {
427
        $this->platform->setUseBooleanTrueFalseStrings(false);
428
429
        self::assertEquals(1, $this->platform->convertBooleans(true));
430
        self::assertEquals(1, $this->platform->convertBooleans('1'));
431
432
        self::assertEquals(0, $this->platform->convertBooleans(false));
433
        self::assertEquals(0, $this->platform->convertBooleans('0'));
434
    }
435
436
    /**
437
     * @group DBAL-630
438
     * @dataProvider pgBooleanProvider
439
     */
440
    public function testConvertBooleanAsDatabaseValueStrings(
441
        string $databaseValue,
0 ignored issues
show
Unused Code introduced by
The parameter $databaseValue is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

441
        /** @scrutinizer ignore-unused */ string $databaseValue,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
442
        string $preparedStatementValue,
0 ignored issues
show
Unused Code introduced by
The parameter $preparedStatementValue is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

442
        /** @scrutinizer ignore-unused */ string $preparedStatementValue,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
443
        int $integerValue,
444
        bool $booleanValue
445
    ) : void {
446
        self::assertSame($integerValue, $this->platform->convertBooleansToDatabaseValue($booleanValue));
447
    }
448
449
    /**
450
     * @group DBAL-630
451
     */
452
    public function testConvertBooleanAsDatabaseValueIntegers() : void
453
    {
454
        $this->platform->setUseBooleanTrueFalseStrings(false);
455
456
        self::assertSame(1, $this->platform->convertBooleansToDatabaseValue(true));
457
        self::assertSame(0, $this->platform->convertBooleansToDatabaseValue(false));
458
    }
459
460
    /**
461
     * @dataProvider pgBooleanProvider
462
     */
463
    public function testConvertFromBoolean(string $databaseValue, string $prepareStatementValue, int $integerValue, bool $booleanValue) : void
0 ignored issues
show
Unused Code introduced by
The parameter $prepareStatementValue is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

463
    public function testConvertFromBoolean(string $databaseValue, /** @scrutinizer ignore-unused */ string $prepareStatementValue, int $integerValue, bool $booleanValue) : void

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $integerValue is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

463
    public function testConvertFromBoolean(string $databaseValue, string $prepareStatementValue, /** @scrutinizer ignore-unused */ int $integerValue, bool $booleanValue) : void

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
464
    {
465
        self::assertSame($booleanValue, $this->platform->convertFromBoolean($databaseValue));
466
    }
467
468
    public function testThrowsExceptionWithInvalidBooleanLiteral() : void
469
    {
470
        $this->expectException(UnexpectedValueException::class);
471
        $this->expectExceptionMessage('Unrecognized boolean literal, my-bool given.');
472
473
        $this->platform->convertBooleansToDatabaseValue('my-bool');
474
    }
475
476
    public function testGetCreateSchemaSQL() : void
477
    {
478
        $schemaName = 'schema';
479
        $sql        = $this->platform->getCreateSchemaSQL($schemaName);
480
        self::assertEquals('CREATE SCHEMA ' . $schemaName, $sql);
481
    }
482
483
    public function testAlterDecimalPrecisionScale() : void
484
    {
485
        $table = new Table('mytable');
486
        $table->addColumn('dfoo1', 'decimal');
487
        $table->addColumn('dfoo2', 'decimal', ['precision' => 10, 'scale' => 6]);
488
        $table->addColumn('dfoo3', 'decimal', ['precision' => 10, 'scale' => 6]);
489
        $table->addColumn('dfoo4', 'decimal', ['precision' => 10, 'scale' => 6]);
490
491
        $tableDiff            = new TableDiff('mytable');
492
        $tableDiff->fromTable = $table;
493
494
        $tableDiff->changedColumns['dloo1'] = new ColumnDiff(
495
            'dloo1',
496
            new Column(
497
                'dloo1',
498
                Type::getType('decimal'),
499
                ['precision' => 16, 'scale' => 6]
500
            ),
501
            ['precision']
502
        );
503
        $tableDiff->changedColumns['dloo2'] = new ColumnDiff(
504
            'dloo2',
505
            new Column(
506
                'dloo2',
507
                Type::getType('decimal'),
508
                ['precision' => 10, 'scale' => 4]
509
            ),
510
            ['scale']
511
        );
512
        $tableDiff->changedColumns['dloo3'] = new ColumnDiff(
513
            'dloo3',
514
            new Column(
515
                'dloo3',
516
                Type::getType('decimal'),
517
                ['precision' => 10, 'scale' => 6]
518
            ),
519
            []
520
        );
521
        $tableDiff->changedColumns['dloo4'] = new ColumnDiff(
522
            'dloo4',
523
            new Column(
524
                'dloo4',
525
                Type::getType('decimal'),
526
                ['precision' => 16, 'scale' => 8]
527
            ),
528
            ['precision', 'scale']
529
        );
530
531
        $sql = $this->platform->getAlterTableSQL($tableDiff);
532
533
        $expectedSql = [
534
            'ALTER TABLE mytable ALTER dloo1 TYPE NUMERIC(16, 6)',
535
            'ALTER TABLE mytable ALTER dloo2 TYPE NUMERIC(10, 4)',
536
            'ALTER TABLE mytable ALTER dloo4 TYPE NUMERIC(16, 8)',
537
        ];
538
539
        self::assertEquals($expectedSql, $sql);
540
    }
541
542
    /**
543
     * @group DBAL-365
544
     */
545
    public function testDroppingConstraintsBeforeColumns() : void
546
    {
547
        $newTable = new Table('mytable');
548
        $newTable->addColumn('id', 'integer');
549
        $newTable->setPrimaryKey(['id']);
550
551
        $oldTable = clone $newTable;
552
        $oldTable->addColumn('parent_id', 'integer');
553
        $oldTable->addForeignKeyConstraint('mytable', ['parent_id'], ['id']);
554
555
        $comparator = new Comparator();
556
        $tableDiff  = $comparator->diffTable($oldTable, $newTable);
557
558
        self::assertNotNull($tableDiff);
559
560
        $sql = $this->platform->getAlterTableSQL($tableDiff);
561
562
        $expectedSql = [
563
            'ALTER TABLE mytable DROP CONSTRAINT FK_6B2BD609727ACA70',
564
            'DROP INDEX IDX_6B2BD609727ACA70',
565
            'ALTER TABLE mytable DROP parent_id',
566
        ];
567
568
        self::assertEquals($expectedSql, $sql);
569
    }
570
571
    /**
572
     * @group DBAL-563
573
     */
574
    public function testUsesSequenceEmulatedIdentityColumns() : void
575
    {
576
        self::assertTrue($this->platform->usesSequenceEmulatedIdentityColumns());
577
    }
578
579
    /**
580
     * @group DBAL-563
581
     */
582
    public function testReturnsIdentitySequenceName() : void
583
    {
584
        self::assertSame('mytable_mycolumn_seq', $this->platform->getIdentitySequenceName('mytable', 'mycolumn'));
585
    }
586
587
    /**
588
     * @dataProvider dataCreateSequenceWithCache
589
     * @group DBAL-139
590
     */
591
    public function testCreateSequenceWithCache(int $cacheSize, string $expectedSql) : void
592
    {
593
        $sequence = new Sequence('foo', 1, 1, $cacheSize);
594
        self::assertStringContainsString($expectedSql, $this->platform->getCreateSequenceSQL($sequence));
595
    }
596
597
    /**
598
     * @return mixed[][]
599
     */
600
    public static function dataCreateSequenceWithCache() : iterable
601
    {
602
        return [
603
            [3, 'CACHE 3'],
604
        ];
605
    }
606
607
    public function getExpectedFixedLengthBinaryTypeDeclarationSQLNoLength() : string
608
    {
609
        return 'BYTEA';
610
    }
611
612
    public function getExpectedFixedLengthBinaryTypeDeclarationSQLWithLength() : string
613
    {
614
        return 'BYTEA';
615
    }
616
617
    public function getExpectedVariableLengthBinaryTypeDeclarationSQLNoLength() : string
618
    {
619
        return 'BYTEA';
620
    }
621
622
    public function getExpectedVariableLengthBinaryTypeDeclarationSQLWithLength() : string
623
    {
624
        return 'BYTEA';
625
    }
626
627
    public function testDoesNotPropagateUnnecessaryTableAlterationOnBinaryType() : void
628
    {
629
        $table1 = new Table('mytable');
630
        $table1->addColumn('column_varbinary', 'binary');
631
        $table1->addColumn('column_binary', 'binary', ['fixed' => true]);
632
        $table1->addColumn('column_blob', 'blob');
633
634
        $table2 = new Table('mytable');
635
        $table2->addColumn('column_varbinary', 'binary', ['fixed' => true]);
636
        $table2->addColumn('column_binary', 'binary');
637
        $table2->addColumn('column_blob', 'binary');
638
639
        $comparator = new Comparator();
640
641
        $diff = $comparator->diffTable($table1, $table2);
642
643
        self::assertNotNull($diff);
644
645
        // VARBINARY -> BINARY
646
        // BINARY    -> VARBINARY
647
        // BLOB      -> VARBINARY
648
        self::assertEmpty($this->platform->getAlterTableSQL($diff));
649
650
        $table2 = new Table('mytable');
651
        $table2->addColumn('column_varbinary', 'binary', ['length' => 42]);
652
        $table2->addColumn('column_binary', 'blob');
653
        $table2->addColumn('column_blob', 'binary', ['length' => 11, 'fixed' => true]);
654
655
        $diff = $comparator->diffTable($table1, $table2);
656
657
        self::assertNotNull($diff);
658
659
        // VARBINARY -> VARBINARY with changed length
660
        // BINARY    -> BLOB
661
        // BLOB      -> BINARY
662
        self::assertEmpty($this->platform->getAlterTableSQL($diff));
663
664
        $table2 = new Table('mytable');
665
        $table2->addColumn('column_varbinary', 'blob');
666
        $table2->addColumn('column_binary', 'binary', ['length' => 42, 'fixed' => true]);
667
        $table2->addColumn('column_blob', 'blob');
668
669
        $diff = $comparator->diffTable($table1, $table2);
670
671
        self::assertNotNull($diff);
672
673
        // VARBINARY -> BLOB
674
        // BINARY    -> BINARY with changed length
675
        // BLOB      -> BLOB
676
        self::assertEmpty($this->platform->getAlterTableSQL($diff));
677
    }
678
679
    /**
680
     * {@inheritDoc}
681
     *
682
     * @group DBAL-234
683
     */
684
    protected function getAlterTableRenameIndexSQL() : array
685
    {
686
        return ['ALTER INDEX idx_foo RENAME TO idx_bar'];
687
    }
688
689
    /**
690
     * {@inheritDoc}
691
     *
692
     * @group DBAL-234
693
     */
694
    protected function getQuotedAlterTableRenameIndexSQL() : array
695
    {
696
        return [
697
            'ALTER INDEX "create" RENAME TO "select"',
698
            'ALTER INDEX "foo" RENAME TO "bar"',
699
        ];
700
    }
701
702
    /**
703
     * PostgreSQL boolean strings provider
704
     *
705
     * @return mixed[][]
706
     */
707
    public static function pgBooleanProvider() : iterable
708
    {
709
        return [
710
            // Database value, prepared statement value, boolean integer value, boolean value.
711
            ['t', 'true', 1, true],
712
            ['true', 'true', 1, true],
713
            ['y', 'true', 1, true],
714
            ['yes', 'true', 1, true],
715
            ['on', 'true', 1, true],
716
            ['1', 'true', 1, true],
717
718
            ['f', 'false', 0, false],
719
            ['false', 'false', 0, false],
720
            [ 'n', 'false', 0, false],
721
            ['no', 'false', 0, false],
722
            ['off', 'false', 0, false],
723
            ['0', 'false', 0, false],
724
        ];
725
    }
726
727
    /**
728
     * {@inheritdoc}
729
     */
730
    protected function getQuotedAlterTableRenameColumnSQL() : array
731
    {
732
        return [
733
            'ALTER TABLE mytable RENAME COLUMN unquoted1 TO unquoted',
734
            'ALTER TABLE mytable RENAME COLUMN unquoted2 TO "where"',
735
            'ALTER TABLE mytable RENAME COLUMN unquoted3 TO "foo"',
736
            'ALTER TABLE mytable RENAME COLUMN "create" TO reserved_keyword',
737
            'ALTER TABLE mytable RENAME COLUMN "table" TO "from"',
738
            'ALTER TABLE mytable RENAME COLUMN "select" TO "bar"',
739
            'ALTER TABLE mytable RENAME COLUMN quoted1 TO quoted',
740
            'ALTER TABLE mytable RENAME COLUMN quoted2 TO "and"',
741
            'ALTER TABLE mytable RENAME COLUMN quoted3 TO "baz"',
742
        ];
743
    }
744
745
    /**
746
     * {@inheritdoc}
747
     */
748
    protected function getQuotedAlterTableChangeColumnLengthSQL() : array
749
    {
750
        return [
751
            'ALTER TABLE mytable ALTER unquoted1 TYPE VARCHAR(255)',
752
            'ALTER TABLE mytable ALTER unquoted2 TYPE VARCHAR(255)',
753
            'ALTER TABLE mytable ALTER unquoted3 TYPE VARCHAR(255)',
754
            'ALTER TABLE mytable ALTER "create" TYPE VARCHAR(255)',
755
            'ALTER TABLE mytable ALTER "table" TYPE VARCHAR(255)',
756
            'ALTER TABLE mytable ALTER "select" TYPE VARCHAR(255)',
757
        ];
758
    }
759
760
    /**
761
     * {@inheritDoc}
762
     *
763
     * @group DBAL-807
764
     */
765
    protected function getAlterTableRenameIndexInSchemaSQL() : array
766
    {
767
        return ['ALTER INDEX myschema.idx_foo RENAME TO idx_bar'];
768
    }
769
770
    /**
771
     * {@inheritDoc}
772
     *
773
     * @group DBAL-807
774
     */
775
    protected function getQuotedAlterTableRenameIndexInSchemaSQL() : array
776
    {
777
        return [
778
            'ALTER INDEX "schema"."create" RENAME TO "select"',
779
            'ALTER INDEX "schema"."foo" RENAME TO "bar"',
780
        ];
781
    }
782
783
    protected function getQuotesDropForeignKeySQL() : string
784
    {
785
        return 'ALTER TABLE "table" DROP CONSTRAINT "select"';
786
    }
787
788
    /**
789
     * @group DBAL-423
790
     */
791
    public function testReturnsGuidTypeDeclarationSQL() : void
792
    {
793
        self::assertSame('UUID', $this->platform->getGuidTypeDeclarationSQL([]));
794
    }
795
796
    /**
797
     * {@inheritdoc}
798
     */
799
    public function getAlterTableRenameColumnSQL() : array
800
    {
801
        return ['ALTER TABLE foo RENAME COLUMN bar TO baz'];
802
    }
803
804
    /**
805
     * {@inheritdoc}
806
     */
807
    protected function getQuotesTableIdentifiersInAlterTableSQL() : array
808
    {
809
        return [
810
            'ALTER TABLE "foo" DROP CONSTRAINT fk1',
811
            'ALTER TABLE "foo" DROP CONSTRAINT fk2',
812
            'ALTER TABLE "foo" ADD bloo INT NOT NULL',
813
            'ALTER TABLE "foo" DROP baz',
814
            'ALTER TABLE "foo" ALTER bar DROP NOT NULL',
815
            'ALTER TABLE "foo" RENAME COLUMN id TO war',
816
            'ALTER TABLE "foo" RENAME TO "table"',
817
            'ALTER TABLE "table" ADD CONSTRAINT fk_add FOREIGN KEY (fk3) REFERENCES fk_table (id) NOT DEFERRABLE ' .
818
            'INITIALLY IMMEDIATE',
819
            'ALTER TABLE "table" ADD CONSTRAINT fk2 FOREIGN KEY (fk2) REFERENCES fk_table2 (id) NOT DEFERRABLE ' .
820
            'INITIALLY IMMEDIATE',
821
        ];
822
    }
823
824
    /**
825
     * {@inheritdoc}
826
     */
827
    protected function getCommentOnColumnSQL() : array
828
    {
829
        return [
830
            'COMMENT ON COLUMN foo.bar IS \'comment\'',
831
            'COMMENT ON COLUMN "Foo"."BAR" IS \'comment\'',
832
            'COMMENT ON COLUMN "select"."from" IS \'comment\'',
833
        ];
834
    }
835
836
    /**
837
     * @group DBAL-1004
838
     */
839
    public function testAltersTableColumnCommentWithExplicitlyQuotedIdentifiers() : void
840
    {
841
        $table1 = new Table('"foo"', [new Column('"bar"', Type::getType('integer'))]);
842
        $table2 = new Table('"foo"', [new Column('"bar"', Type::getType('integer'), ['comment' => 'baz'])]);
843
844
        $comparator = new Comparator();
845
846
        $tableDiff = $comparator->diffTable($table1, $table2);
847
848
        self::assertInstanceOf(TableDiff::class, $tableDiff);
849
        self::assertSame(
850
            ['COMMENT ON COLUMN "foo"."bar" IS \'baz\''],
851
            $this->platform->getAlterTableSQL($tableDiff)
852
        );
853
    }
854
855
    /**
856
     * @group 3158
857
     */
858
    public function testAltersTableColumnCommentIfRequiredByType() : void
859
    {
860
        $table1 = new Table('"foo"', [new Column('"bar"', Type::getType('datetime'))]);
861
        $table2 = new Table('"foo"', [new Column('"bar"', Type::getType('datetime_immutable'))]);
862
863
        $comparator = new Comparator();
864
865
        $tableDiff = $comparator->diffTable($table1, $table2);
866
867
        self::assertInstanceOf('Doctrine\DBAL\Schema\TableDiff', $tableDiff);
868
        self::assertSame(
869
            [
870
                'ALTER TABLE "foo" ALTER "bar" TYPE TIMESTAMP(0) WITHOUT TIME ZONE',
871
                'ALTER TABLE "foo" ALTER "bar" DROP DEFAULT',
872
                'COMMENT ON COLUMN "foo"."bar" IS \'(DC2Type:datetime_immutable)\'',
873
            ],
874
            $this->platform->getAlterTableSQL($tableDiff)
875
        );
876
    }
877
878
    protected function getQuotesReservedKeywordInUniqueConstraintDeclarationSQL() : string
879
    {
880
        return 'CONSTRAINT "select" UNIQUE (foo)';
881
    }
882
883
    protected function getQuotesReservedKeywordInIndexDeclarationSQL() : string
884
    {
885
        return 'INDEX "select" (foo)';
886
    }
887
888
    protected function getQuotesReservedKeywordInTruncateTableSQL() : string
889
    {
890
        return 'TRUNCATE "select"';
891
    }
892
893
    /**
894
     * {@inheritdoc}
895
     */
896
    protected function getAlterStringToFixedStringSQL() : array
897
    {
898
        return ['ALTER TABLE mytable ALTER name TYPE CHAR(2)'];
899
    }
900
901
    /**
902
     * {@inheritdoc}
903
     */
904
    protected function getGeneratesAlterTableRenameIndexUsedByForeignKeySQL() : array
905
    {
906
        return ['ALTER INDEX idx_foo RENAME TO idx_foo_renamed'];
907
    }
908
909
    /**
910
     * @group DBAL-1142
911
     */
912
    public function testInitializesTsvectorTypeMapping() : void
913
    {
914
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('tsvector'));
915
        self::assertEquals('text', $this->platform->getDoctrineTypeMapping('tsvector'));
916
    }
917
918
    /**
919
     * @group DBAL-1220
920
     */
921
    public function testReturnsDisallowDatabaseConnectionsSQL() : void
922
    {
923
        self::assertSame(
924
            "UPDATE pg_database SET datallowconn = 'false' WHERE datname = 'foo'",
925
            $this->platform->getDisallowDatabaseConnectionsSQL('foo')
926
        );
927
    }
928
929
    /**
930
     * @group DBAL-1220
931
     */
932
    public function testReturnsCloseActiveDatabaseConnectionsSQL() : void
933
    {
934
        self::assertSame(
935
            "SELECT pg_terminate_backend(procpid) FROM pg_stat_activity WHERE datname = 'foo'",
936
            $this->platform->getCloseActiveDatabaseConnectionsSQL('foo')
937
        );
938
    }
939
940
    /**
941
     * @group DBAL-2436
942
     */
943
    public function testQuotesTableNameInListTableForeignKeysSQL() : void
944
    {
945
        self::assertStringContainsStringIgnoringCase(
946
            "'Foo''Bar\\'",
947
            $this->platform->getListTableForeignKeysSQL("Foo'Bar\\")
948
        );
949
    }
950
951
    /**
952
     * @group DBAL-2436
953
     */
954
    public function testQuotesSchemaNameInListTableForeignKeysSQL() : void
955
    {
956
        self::assertStringContainsStringIgnoringCase(
957
            "'Foo''Bar\\'",
958
            $this->platform->getListTableForeignKeysSQL("Foo'Bar\\.baz_table")
959
        );
960
    }
961
962
    /**
963
     * @group DBAL-2436
964
     */
965
    public function testQuotesTableNameInListTableConstraintsSQL() : void
966
    {
967
        self::assertStringContainsStringIgnoringCase(
968
            "'Foo''Bar\\'",
969
            $this->platform->getListTableConstraintsSQL("Foo'Bar\\")
970
        );
971
    }
972
973
    /**
974
     * @group DBAL-2436
975
     */
976
    public function testQuotesTableNameInListTableIndexesSQL() : void
977
    {
978
        self::assertStringContainsStringIgnoringCase(
979
            "'Foo''Bar\\'",
980
            $this->platform->getListTableIndexesSQL("Foo'Bar\\")
981
        );
982
    }
983
984
    /**
985
     * @group DBAL-2436
986
     */
987
    public function testQuotesSchemaNameInListTableIndexesSQL() : void
988
    {
989
        self::assertStringContainsStringIgnoringCase(
990
            "'Foo''Bar\\'",
991
            $this->platform->getListTableIndexesSQL("Foo'Bar\\.baz_table")
992
        );
993
    }
994
995
    /**
996
     * @group DBAL-2436
997
     */
998
    public function testQuotesTableNameInListTableColumnsSQL() : void
999
    {
1000
        self::assertStringContainsStringIgnoringCase(
1001
            "'Foo''Bar\\'",
1002
            $this->platform->getListTableColumnsSQL("Foo'Bar\\")
1003
        );
1004
    }
1005
1006
    /**
1007
     * @group DBAL-2436
1008
     */
1009
    public function testQuotesSchemaNameInListTableColumnsSQL() : void
1010
    {
1011
        self::assertStringContainsStringIgnoringCase(
1012
            "'Foo''Bar\\'",
1013
            $this->platform->getListTableColumnsSQL("Foo'Bar\\.baz_table")
1014
        );
1015
    }
1016
1017
    /**
1018
     * @group DBAL-2436
1019
     */
1020
    public function testQuotesDatabaseNameInCloseActiveDatabaseConnectionsSQL() : void
1021
    {
1022
        self::assertStringContainsStringIgnoringCase(
1023
            "'Foo''Bar\\'",
1024
            $this->platform->getCloseActiveDatabaseConnectionsSQL("Foo'Bar\\")
1025
        );
1026
    }
1027
}
1028