Failed Conditions
Pull Request — develop (#3348)
by Sergei
63:22
created

AbstractPlatform   F

Complexity

Total Complexity 392

Size/Duplication

Total Lines 3078
Duplicated Lines 0 %

Test Coverage

Coverage 79.7%

Importance

Changes 0
Metric Value
wmc 392
eloc 654
dl 0
loc 3078
ccs 581
cts 729
cp 0.797
rs 1.946
c 0
b 0
f 0

210 Methods

Rating   Name   Duplication   Size   Complexity  
A getEventManager() 0 3 1
A getWriteLockSQL() 0 3 1
A getCharMaxLength() 0 3 1
A getCreateSchemaSQL() 0 3 1
A getLtrimExpression() 0 3 1
A onSchemaAlterTableRenameColumn() 0 16 3
A initializeCommentedDoctrineTypes() 0 12 3
A getCosExpression() 0 3 1
F getCreateTableSQL() 0 93 23
A getForUpdateSQL() 0 3 1
A getPreAlterTableIndexForeignKeySQL() 0 22 6
A getRenameIndexSQL() 0 5 1
A getVarcharMaxLength() 0 3 1
A getSqrtExpression() 0 3 1
A getDateSubMonthExpression() 0 3 1
A _getAlterTableIndexForeignKeySQL() 0 3 1
A getDateAddYearsExpression() 0 3 1
A getCreateIndexSQL() 0 20 4
A getBetweenExpression() 0 3 1
A getIsNullExpression() 0 3 1
A getSqlCommentStartString() 0 3 1
A getUpperExpression() 0 3 1
A getDropForeignKeySQL() 0 14 3
A getBinaryDefaultLength() 0 3 1
A getSumExpression() 0 3 1
A registerDoctrineTypeMapping() 0 20 4
A appendLockHint() 0 3 1
A markDoctrineTypeCommented() 0 9 3
A getSqlCommentEndString() 0 3 1
A getDateDiffExpression() 0 3 1
A getCreatePrimaryKeySQL() 0 7 2
A getAlterTableSQL() 0 3 1
A getGuidTypeDeclarationSQL() 0 6 1
A getPiExpression() 0 3 1
A getBinaryTypeDeclarationSQLSnippet() 0 3 1
A getAvgExpression() 0 3 1
A getCreateForeignKeySQL() 0 7 2
A getBinaryMaxLength() 0 3 1
A getReadLockSQL() 0 3 1
A getAcosExpression() 0 3 1
A getDoctrineTypeComment() 0 3 1
A getMd5Expression() 0 3 1
A getCountExpression() 0 3 1
A getMinExpression() 0 3 1
A getVarcharTypeDeclarationSQLSnippet() 0 3 1
A getModExpression() 0 3 1
F getColumnDeclarationSQL() 0 30 15
A getDropConstraintSQL() 0 14 3
A getCommentOnColumnSQL() 0 10 1
A getDateSubQuartersExpression() 0 3 1
C getDefaultValueDeclarationSQL() 0 35 12
A getDateAddMonthExpression() 0 3 1
A getMaxExpression() 0 3 1
A getBitAndComparisonExpression() 0 3 1
A onSchemaAlterTableAddColumn() 0 16 3
B getDropTableSQL() 0 28 7
A quoteSingleIdentifier() 0 5 1
A getDateAddWeeksExpression() 0 3 1
A getDropIndexSQL() 0 9 3
A hasDoctrineTypeMappingFor() 0 9 2
A getDateSubMinutesExpression() 0 3 1
A getDateSubHourExpression() 0 3 1
A getCreateIndexSQLFlags() 0 3 2
A getLengthExpression() 0 3 1
A getVarcharDefaultLength() 0 3 1
A getDecimalTypeDeclarationSQL() 0 8 5
A getDateSubWeeksExpression() 0 3 1
A getDateAddDaysExpression() 0 3 1
A getIdentifierQuoteCharacter() 0 3 1
A getDateSubDaysExpression() 0 3 1
A __construct() 0 2 1
B getPostAlterTableIndexForeignKeySQL() 0 38 8
C _getCreateTableSQL() 0 37 12
A onSchemaAlterTable() 0 16 3
A initializeAllDoctrineTypeMappings() 0 7 3
A isCommentedDoctrineType() 0 9 2
A getRegexpExpression() 0 3 1
A getDoctrineTypeMapping() 0 13 3
A getBitOrComparisonExpression() 0 3 1
A getSinExpression() 0 3 1
A getLowerExpression() 0 3 1
A getCreateTemporaryTableSnippetSQL() 0 3 1
A getNowExpression() 0 3 1
A getCreateSequenceSQL() 0 3 1
A getVarcharTypeDeclarationSQL() 0 17 4
A getDateSubSecondsExpression() 0 3 1
A setEventManager() 0 3 1
A getRoundExpression() 0 3 1
A getIsNotNullExpression() 0 3 1
A getRtrimExpression() 0 3 1
A getCreateConstraintSQL() 0 30 6
A getInlineColumnCommentSQL() 0 7 2
A getSubstringExpression() 0 7 2
A getAlterSequenceSQL() 0 3 1
A getDateArithmeticIntervalExpression() 0 3 1
A getDateAddMinutesExpression() 0 3 1
A getBinaryTypeDeclarationSQL() 0 9 2
A getJsonTypeDeclarationSQL() 0 3 1
A convertNonNativeInterval() 0 7 2
A getConcatExpression() 0 3 1
A getLocateExpression() 0 3 1
A getWildcards() 0 3 1
A quoteIdentifier() 0 9 2
B getTrimExpression() 0 27 7
A getDateAddQuartersExpression() 0 3 1
A getDropTemporaryTableSQL() 0 3 1
A getPartialIndexSQL() 0 7 3
A getNotExpression() 0 3 1
A onSchemaAlterTableRemoveColumn() 0 16 3
A getDateSubYearsExpression() 0 3 1
A getDropDatabaseSQL() 0 3 1
A getColumnDeclarationListSQL() 0 9 2
A onSchemaAlterTableChangeColumn() 0 16 3
A getColumnComment() 0 9 2
A getDateAddSecondsExpression() 0 3 1
A getDateAddHourExpression() 0 3 1
A supportsAlterTable() 0 3 1
A getSequencePrefix() 0 10 4
A supportsForeignKeyOnUpdate() 0 3 1
A getCheckDeclarationSQL() 0 20 5
A getFloatDeclarationSQL() 0 3 1
A getDefaultSchemaName() 0 3 1
A supportsCreateDropDatabase() 0 3 1
A getCreateDatabaseSQL() 0 3 1
A getListTableColumnsSQL() 0 3 1
A supportsLimitOffset() 0 3 1
A getListDatabasesSQL() 0 3 1
A supportsSavepoints() 0 3 1
A getDateTimeTzTypeDeclarationSQL() 0 3 1
A supportsTransactions() 0 3 1
A hasNativeGuidType() 0 3 1
A fixSchemaElementName() 0 3 1
A getDateTimeTypeDeclarationSQL() 0 3 1
A supportsCommentOnStatement() 0 3 1
A convertFromBoolean() 0 3 2
A getTemporaryTableSQL() 0 3 1
A getIdentityColumnNullInsertSQL() 0 3 1
A getDropSequenceSQL() 0 3 1
A getDefaultTransactionIsolationLevel() 0 3 1
A getMaxIdentifierLength() 0 3 1
A _getTransactionIsolationLevelSQL() 0 13 5
A getForeignKeyDeclarationSQL() 0 6 1
A prefersIdentityColumns() 0 3 1
A getListNamespacesSQL() 0 3 1
A getUniqueConstraintDeclarationSQL() 0 20 4
A supportsViews() 0 3 1
A canEmulateSchemas() 0 3 1
A getCreateViewSQL() 0 3 1
A getColumnCharsetDeclarationSQL() 0 3 1
A getDateFormatString() 0 3 1
A getListUsersSQL() 0 3 1
A getListSequencesSQL() 0 3 1
A convertBooleans() 0 15 5
A createSavePoint() 0 3 1
A convertBooleansToDatabaseValue() 0 3 1
A supportsIdentityColumns() 0 3 1
A getIdentitySequenceName() 0 3 1
A getForeignKeyReferentialActionSQL() 0 12 6
A getSequenceNextValSQL() 0 3 1
A getCurrentDateSQL() 0 3 1
A getCustomTypeDeclarationSQL() 0 3 1
A supportsPartialIndexes() 0 3 1
A prefersSequences() 0 3 1
A getReservedKeywordsClass() 0 3 1
A getReservedKeywordsList() 0 17 3
A getStringLiteralQuoteCharacter() 0 3 1
A getColumnCollationDeclarationSQL() 0 3 2
A supportsSchemas() 0 3 1
A getSQLResultCasing() 0 3 1
A getDropViewSQL() 0 3 1
A getDateTimeFormatString() 0 3 1
A supportsSequences() 0 3 1
A getCurrentTimeSQL() 0 3 1
A supportsReleaseSavepoints() 0 3 1
A getDummySelectSQL() 0 3 1
A supportsIndexes() 0 3 1
A getIndexFieldDeclarationListSQL() 0 21 5
A getDateTypeDeclarationSQL() 0 3 1
A getAdvancedForeignKeyOptionsSQL() 0 11 4
A supportsColumnCollation() 0 3 1
A quoteStringLiteral() 0 5 1
A hasNativeJsonType() 0 3 1
A getTruncateTableSQL() 0 5 1
A getLikeWildcardCharacters() 0 3 1
A usesSequenceEmulatedIdentityColumns() 0 3 1
A getSetTransactionIsolationSQL() 0 3 1
A getListTableForeignKeysSQL() 0 3 1
A getListViewsSQL() 0 3 1
A getUniqueFieldDeclarationSQL() 0 3 1
A supportsForeignKeyConstraints() 0 3 1
A modifyLimitQuery() 0 17 4
A getTimeFormatString() 0 3 1
A getTimeTypeDeclarationSQL() 0 3 1
A supportsColumnLengthIndexes() 0 3 1
A getTemporaryTableName() 0 3 1
A getForeignKeyBaseDeclarationSQL() 0 22 5
A getDateTimeTzFormatString() 0 3 1
A supportsGettingAffectedRows() 0 3 1
A getCurrentTimestampSQL() 0 3 1
A releaseSavePoint() 0 3 1
A rollbackSavePoint() 0 3 1
A getListTableConstraintsSQL() 0 3 1
A getIndexDeclarationSQL() 0 12 2
A getListTableIndexesSQL() 0 3 1
A getListTablesSQL() 0 3 1
A getEmptyIdentityInsertSQL() 0 3 1
A supportsInlineColumnComments() 0 3 1
A doModifyLimitQuery() 0 11 3
A escapeStringForLike() 0 6 1
A supportsPrimaryConstraints() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like AbstractPlatform 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 AbstractPlatform, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Doctrine\DBAL\Platforms;
4
5
use Doctrine\Common\EventManager;
6
use Doctrine\DBAL\DBALException;
7
use Doctrine\DBAL\Event\SchemaAlterTableAddColumnEventArgs;
8
use Doctrine\DBAL\Event\SchemaAlterTableChangeColumnEventArgs;
9
use Doctrine\DBAL\Event\SchemaAlterTableEventArgs;
10
use Doctrine\DBAL\Event\SchemaAlterTableRemoveColumnEventArgs;
11
use Doctrine\DBAL\Event\SchemaAlterTableRenameColumnEventArgs;
12
use Doctrine\DBAL\Event\SchemaCreateTableColumnEventArgs;
13
use Doctrine\DBAL\Event\SchemaCreateTableEventArgs;
14
use Doctrine\DBAL\Event\SchemaDropTableEventArgs;
15
use Doctrine\DBAL\Events;
16
use Doctrine\DBAL\Platforms\Keywords\KeywordList;
17
use Doctrine\DBAL\Schema\Column;
18
use Doctrine\DBAL\Schema\ColumnDiff;
19
use Doctrine\DBAL\Schema\Constraint;
20
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
21
use Doctrine\DBAL\Schema\Identifier;
22
use Doctrine\DBAL\Schema\Index;
23
use Doctrine\DBAL\Schema\Sequence;
24
use Doctrine\DBAL\Schema\Table;
25
use Doctrine\DBAL\Schema\TableDiff;
26
use Doctrine\DBAL\Schema\UniqueConstraint;
27
use Doctrine\DBAL\TransactionIsolationLevel;
28
use Doctrine\DBAL\Types;
29
use Doctrine\DBAL\Types\Type;
30
use InvalidArgumentException;
31
use UnexpectedValueException;
32
use function addcslashes;
33
use function array_map;
34
use function array_merge;
35
use function array_unique;
36
use function array_values;
37
use function assert;
38
use function count;
39
use function explode;
40
use function implode;
41
use function in_array;
42
use function is_array;
43
use function is_bool;
44
use function is_numeric;
45
use function is_string;
46
use function preg_quote;
47
use function preg_replace;
48
use function sprintf;
49
use function str_replace;
50
use function strlen;
51
use function strpos;
52
use function strtolower;
53
use function strtoupper;
54
55
/**
56
 * Base class for all DatabasePlatforms. The DatabasePlatforms are the central
57
 * point of abstraction of platform-specific behaviors, features and SQL dialects.
58
 * They are a passive source of information.
59
 *
60
 * @todo Remove any unnecessary methods.
61
 */
62
abstract class AbstractPlatform
63
{
64
    public const CREATE_INDEXES = 1;
65
66
    public const CREATE_FOREIGNKEYS = 2;
67
68
    /**
69
     * @deprecated Use DateIntervalUnit::INTERVAL_UNIT_SECOND.
70
     */
71
    public const DATE_INTERVAL_UNIT_SECOND = DateIntervalUnit::SECOND;
72
73
    /**
74
     * @deprecated Use DateIntervalUnit::MINUTE.
75
     */
76
    public const DATE_INTERVAL_UNIT_MINUTE = DateIntervalUnit::MINUTE;
77
78
    /**
79
     * @deprecated Use DateIntervalUnit::HOUR.
80
     */
81
    public const DATE_INTERVAL_UNIT_HOUR = DateIntervalUnit::HOUR;
82
83
    /**
84
     * @deprecated Use DateIntervalUnit::DAY.
85
     */
86
    public const DATE_INTERVAL_UNIT_DAY = DateIntervalUnit::DAY;
87
88
    /**
89
     * @deprecated Use DateIntervalUnit::WEEK.
90
     */
91
    public const DATE_INTERVAL_UNIT_WEEK = DateIntervalUnit::WEEK;
92
93
    /**
94
     * @deprecated Use DateIntervalUnit::MONTH.
95
     */
96
    public const DATE_INTERVAL_UNIT_MONTH = DateIntervalUnit::MONTH;
97
98
    /**
99
     * @deprecated Use DateIntervalUnit::QUARTER.
100
     */
101
    public const DATE_INTERVAL_UNIT_QUARTER = DateIntervalUnit::QUARTER;
102
103
    /**
104
     * @deprecated Use DateIntervalUnit::QUARTER.
105
     */
106
    public const DATE_INTERVAL_UNIT_YEAR = DateIntervalUnit::YEAR;
107
108
    /**
109
     * @deprecated Use TrimMode::UNSPECIFIED.
110
     */
111
    public const TRIM_UNSPECIFIED = TrimMode::UNSPECIFIED;
112
113
    /**
114
     * @deprecated Use TrimMode::LEADING.
115
     */
116
    public const TRIM_LEADING = TrimMode::LEADING;
117
118
    /**
119
     * @deprecated Use TrimMode::TRAILING.
120
     */
121
    public const TRIM_TRAILING = TrimMode::TRAILING;
122
123
    /**
124
     * @deprecated Use TrimMode::BOTH.
125
     */
126
    public const TRIM_BOTH = TrimMode::BOTH;
127
128
    /** @var string[]|null */
129
    protected $doctrineTypeMapping = null;
130
131
    /**
132
     * Contains a list of all columns that should generate parseable column comments for type-detection
133
     * in reverse engineering scenarios.
134
     *
135
     * @var string[]|null
136
     */
137
    protected $doctrineTypeComments = null;
138
139
    /** @var EventManager|null */
140
    protected $_eventManager;
141
142
    /**
143
     * Holds the KeywordList instance for the current platform.
144
     *
145
     * @var KeywordList|null
146
     */
147
    protected $_keywords;
148 55140
149
    public function __construct()
150 55140
    {
151
    }
152
153
    /**
154
     * Sets the EventManager used by the Platform.
155 1665
     */
156
    public function setEventManager(?EventManager $eventManager) : void
157 1665
    {
158 1665
        $this->_eventManager = $eventManager;
159
    }
160
161
    /**
162
     * Gets the EventManager used by the Platform.
163
     */
164
    public function getEventManager() : ?EventManager
165 1295
    {
166
        return $this->_eventManager;
167 1295
    }
168
169
    /**
170
     * Returns the SQL snippet that declares a boolean column.
171
     *
172
     * @param mixed[] $columnDef
173
     */
174
    abstract public function getBooleanTypeDeclarationSQL(array $columnDef) : string;
175
176
    /**
177
     * Returns the SQL snippet that declares a 4 byte integer column.
178
     *
179
     * @param mixed[] $columnDef
180
     */
181
    abstract public function getIntegerTypeDeclarationSQL(array $columnDef) : string;
182
183
    /**
184
     * Returns the SQL snippet that declares an 8 byte integer column.
185
     *
186
     * @param mixed[] $columnDef
187
     */
188
    abstract public function getBigIntTypeDeclarationSQL(array $columnDef) : string;
189
190
    /**
191
     * Returns the SQL snippet that declares a 2 byte integer column.
192
     *
193
     * @param mixed[] $columnDef
194
     */
195
    abstract public function getSmallIntTypeDeclarationSQL(array $columnDef) : string;
196
197
    /**
198
     * Returns the SQL snippet that declares common properties of an integer column.
199
     *
200
     * @param mixed[] $columnDef
201
     */
202
    abstract protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef) : string;
203
204
    /**
205
     * Lazy load Doctrine Type Mappings.
206
     */
207
    abstract protected function initializeDoctrineTypeMappings() : void;
208
209
    /**
210
     * Initializes Doctrine Type Mappings with the platform defaults
211
     * and with all additional type mappings.
212
     */
213
    private function initializeAllDoctrineTypeMappings() : void
214
    {
215
        $this->initializeDoctrineTypeMappings();
216
217
        foreach (Type::getTypesMap() as $typeName => $className) {
218
            foreach (Type::getType($typeName)->getMappedDatabaseTypes($this) as $dbType) {
219
                $this->doctrineTypeMapping[$dbType] = $typeName;
220
            }
221
        }
222
    }
223
224
    /**
225
     * Returns the SQL snippet used to declare a VARCHAR column type.
226
     *
227
     * @param mixed[] $field
228 1576
     */
229
    public function getVarcharTypeDeclarationSQL(array $field) : string
230 1576
    {
231
        if (! isset($field['length'])) {
232 1576
            $field['length'] = $this->getVarcharDefaultLength();
233 1576
        }
234 1576
235
        $fixed = $field['fixed'] ?? false;
236
237 1576
        $maxLength = $fixed
238
            ? $this->getCharMaxLength()
239
            : $this->getVarcharMaxLength();
240
241
        if ($field['length'] > $maxLength) {
242
            return $this->getClobTypeDeclarationSQL($field);
243
        }
244
245
        return $this->getVarcharTypeDeclarationSQLSnippet($field['length'], $fixed);
246 6058
    }
247
248 6058
    /**
249 1328
     * Returns the SQL snippet used to declare a BINARY/VARBINARY column type.
250
     *
251
     * @param mixed[] $field The column definition.
252 6058
     */
253
    public function getBinaryTypeDeclarationSQL(array $field) : string
254 6058
    {
255 909
        if (! isset($field['length'])) {
256 6058
            $field['length'] = $this->getBinaryDefaultLength();
257
        }
258 6058
259
        $fixed = $field['fixed'] ?? false;
260
261
        return $this->getBinaryTypeDeclarationSQLSnippet($field['length'], $fixed);
262 6058
    }
263
264
    /**
265
     * Returns the SQL snippet to declare a GUID/UUID field.
266
     *
267
     * By default this maps directly to a CHAR(36) and only maps to more
268
     * special datatypes when the underlying databases support this datatype.
269
     *
270
     * @param mixed[] $field
271
     */
272 321
    public function getGuidTypeDeclarationSQL(array $field) : string
273
    {
274 321
        $field['length'] = 36;
275 298
        $field['fixed']  = true;
276
277
        return $this->getVarcharTypeDeclarationSQL($field);
278 321
    }
279
280 321
    /**
281
     * Returns the SQL snippet to declare a JSON field.
282
     *
283
     * By default this maps directly to a CLOB and only maps to more
284
     * special datatypes when the underlying databases support this datatype.
285
     *
286
     * @param mixed[] $field
287
     */
288
    public function getJsonTypeDeclarationSQL(array $field) : string
289
    {
290
        return $this->getClobTypeDeclarationSQL($field);
291
    }
292
293 150
    /**
294
     * @param int|null $length The length of the column.
295 150
     * @param bool     $fixed  Whether the column length is fixed.
296 150
     *
297
     * @throws DBALException If not supported on this platform.
298 150
     */
299
    protected function getVarcharTypeDeclarationSQLSnippet(?int $length, bool $fixed) : string
0 ignored issues
show
Unused Code introduced by
The parameter $fixed 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

299
    protected function getVarcharTypeDeclarationSQLSnippet(?int $length, /** @scrutinizer ignore-unused */ bool $fixed) : string

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...
300
    {
301
        throw DBALException::notSupported('VARCHARs not supported by Platform.');
302
    }
303
304
    /**
305
     * Returns the SQL snippet used to declare a BINARY/VARBINARY column type.
306
     *
307
     * @param int|null $length The length of the column.
308
     * @param bool     $fixed  Whether the column length is fixed.
309
     *
310
     * @throws DBALException If not supported on this platform.
311 354
     */
312
    protected function getBinaryTypeDeclarationSQLSnippet(?int $length, bool $fixed) : string
0 ignored issues
show
Unused Code introduced by
The parameter $fixed 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

312
    protected function getBinaryTypeDeclarationSQLSnippet(?int $length, /** @scrutinizer ignore-unused */ bool $fixed) : string

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...
313 354
    {
314
        throw DBALException::notSupported('BINARY/VARBINARY column types are not supported by this platform.');
315
    }
316
317
    /**
318
     * Returns the SQL snippet used to declare a CLOB column type.
319
     *
320
     * @param mixed[] $field
321
     */
322
    abstract public function getClobTypeDeclarationSQL(array $field) : string;
323
324
    /**
325
     * Returns the SQL Snippet used to declare a BLOB column type.
326
     *
327
     * @param mixed[] $field
328
     */
329
    abstract public function getBlobTypeDeclarationSQL(array $field) : string;
330
331
    /**
332
     * Gets the name of the platform.
333
     */
334
    abstract public function getName() : string;
335
336
    /**
337
     * Registers a doctrine type to be used in conjunction with a column type of this platform.
338
     *
339
     * @throws DBALException If the type is not found.
340
     */
341
    public function registerDoctrineTypeMapping(string $dbType, string $doctrineType) : void
342
    {
343
        if ($this->doctrineTypeMapping === null) {
344
            $this->initializeAllDoctrineTypeMappings();
345
        }
346
347
        if (! Types\Type::hasType($doctrineType)) {
348
            throw DBALException::typeNotFound($doctrineType);
349
        }
350
351
        $dbType                             = strtolower($dbType);
352
        $this->doctrineTypeMapping[$dbType] = $doctrineType;
353
354
        $doctrineType = Type::getType($doctrineType);
355
356
        if (! $doctrineType->requiresSQLCommentHint($this)) {
357
            return;
358
        }
359
360
        $this->markDoctrineTypeCommented($doctrineType);
361
    }
362
363
    /**
364
     * Gets the Doctrine type that is mapped for the given database column type.
365
     *
366
     * @throws DBALException
367
     */
368
    public function getDoctrineTypeMapping(string $dbType) : string
369
    {
370
        if ($this->doctrineTypeMapping === null) {
371
            $this->initializeAllDoctrineTypeMappings();
372
        }
373
374
        $dbType = strtolower($dbType);
375
376
        if (! isset($this->doctrineTypeMapping[$dbType])) {
377 857
            throw new DBALException('Unknown database type ' . $dbType . ' requested, ' . static::class . ' may not support it.');
378
        }
379 857
380 851
        return $this->doctrineTypeMapping[$dbType];
381
    }
382
383 857
    /**
384 276
     * Checks if a database type is currently supported by this platform.
385
     */
386
    public function hasDoctrineTypeMappingFor(string $dbType) : bool
387 581
    {
388 581
        if ($this->doctrineTypeMapping === null) {
389
            $this->initializeAllDoctrineTypeMappings();
390 581
        }
391
392 581
        $dbType = strtolower($dbType);
393 305
394
        return isset($this->doctrineTypeMapping[$dbType]);
395
    }
396 276
397 276
    /**
398
     * Initializes the Doctrine Type comments instance variable for in_array() checks.
399
     */
400
    protected function initializeCommentedDoctrineTypes() : void
401
    {
402
        $this->doctrineTypeComments = [];
403
404
        foreach (Type::getTypesMap() as $typeName => $className) {
405
            $type = Type::getType($typeName);
406
407
            if (! $type->requiresSQLCommentHint($this)) {
408 2119
                continue;
409
            }
410 2119
411 357
            $this->doctrineTypeComments[] = $typeName;
412
        }
413
    }
414 2119
415
    /**
416 2119
     * Is it necessary for the platform to add a parsable type comment to allow reverse engineering the given type?
417 276
     */
418
    public function isCommentedDoctrineType(Type $doctrineType) : bool
419
    {
420 1843
        if ($this->doctrineTypeComments === null) {
421
            $this->initializeCommentedDoctrineTypes();
422
        }
423
424
        assert(is_array($this->doctrineTypeComments));
425
426
        return in_array($doctrineType->getName(), $this->doctrineTypeComments);
427
    }
428
429
    /**
430 415
     * Marks this type as to be commented in ALTER TABLE and CREATE TABLE statements.
431
     *
432 415
     * @param string|Type $doctrineType
433 368
     */
434
    public function markDoctrineTypeCommented($doctrineType) : void
435
    {
436 415
        if ($this->doctrineTypeComments === null) {
437
            $this->initializeCommentedDoctrineTypes();
438 415
        }
439
440
        assert(is_array($this->doctrineTypeComments));
441
442
        $this->doctrineTypeComments[] = $doctrineType instanceof Type ? $doctrineType->getName() : $doctrineType;
443
    }
444
445
    /**
446 14094
     * Gets the comment to append to a column comment that helps parsing this type in reverse engineering.
447
     */
448 14094
    public function getDoctrineTypeComment(Type $doctrineType) : string
449
    {
450 14094
        return '(DC2Type:' . $doctrineType->getName() . ')';
451 14094
    }
452
453 14094
    /**
454 14094
     * Gets the comment of a passed column modified by potential doctrine type comment hints.
455
     */
456
    protected function getColumnComment(Column $column) : ?string
457 14094
    {
458
        $comment = $column->getComment();
459 14094
460
        if ($this->isCommentedDoctrineType($column->getType())) {
461
            $comment .= $this->getDoctrineTypeComment($column->getType());
462
        }
463
464
        return $comment;
465
    }
466 17434
467
    /**
468 17434
     * Gets the character used for identifier quoting.
469 13818
     */
470
    public function getIdentifierQuoteCharacter() : string
471
    {
472 17434
        return '"';
473
    }
474
475
    /**
476
     * Gets the string portion that starts an SQL comment.
477
     */
478
    public function getSqlCommentStartString() : string
479
    {
480
        return '--';
481
    }
482 276
483
    /**
484 276
     * Gets the string portion that ends an SQL comment.
485 276
     */
486
    public function getSqlCommentEndString() : string
487
    {
488 276
        return "\n";
489 276
    }
490
491
    /**
492
     * Gets the maximum length of a char field.
493
     */
494
    public function getCharMaxLength() : int
495
    {
496 873
        return $this->getVarcharMaxLength();
497
    }
498 873
499
    /**
500
     * Gets the maximum length of a varchar field.
501
     */
502
    public function getVarcharMaxLength() : int
503
    {
504
        return 4000;
505
    }
506 10282
507
    /**
508 10282
     * Gets the default length of a varchar field.
509
     */
510 10282
    public function getVarcharDefaultLength() : int
511 873
    {
512
        return 255;
513
    }
514 10282
515
    /**
516
     * Gets the maximum length of a binary field.
517
     */
518
    public function getBinaryMaxLength() : int
519
    {
520
        return 4000;
521
    }
522 4636
523
    /**
524 4636
     * Gets the default length of a binary field.
525
     */
526
    public function getBinaryDefaultLength() : int
527
    {
528
        return 255;
529
    }
530
531
    /**
532
     * Gets all SQL wildcard characters of the platform.
533
     *
534
     * @return string[]
535
     */
536
    public function getWildcards() : array
537
    {
538
        return ['%', '_'];
539
    }
540
541
    /**
542
     * Returns the regular expression operator.
543
     *
544
     * @throws DBALException If not supported on this platform.
545
     */
546
    public function getRegexpExpression() : string
547
    {
548
        throw DBALException::notSupported(__METHOD__);
549
    }
550 810
551
    /**
552 810
     * Returns the SQL snippet to get the average value of a column.
553
     *
554
     * @param string $expression The column to use.
555
     *
556
     * @return string Generated SQL including an AVG aggregate function.
557
     */
558
    public function getAvgExpression(string $expression) : string
559
    {
560 2430
        return 'AVG(' . $expression . ')';
561
    }
562 2430
563
    /**
564
     * Returns the SQL snippet to get the number of rows (without a NULL value) of a column.
565
     *
566
     * If a '*' is used instead of a column the number of selected rows is returned.
567
     *
568
     * @param string $expression The expression to count.
569
     *
570 1259
     * @return string Generated SQL including a COUNT aggregate function.
571
     */
572 1259
    public function getCountExpression(string $expression) : string
573
    {
574
        return 'COUNT(' . $expression . ')';
575
    }
576
577
    /**
578
     * Returns the SQL snippet to get the highest value of a column.
579
     *
580
     * @param string $expression The column to use.
581
     *
582
     * @return string Generated SQL including a MAX aggregate function.
583
     */
584
    public function getMaxExpression(string $expression) : string
585
    {
586
        return 'MAX(' . $expression . ')';
587
    }
588
589
    /**
590 291
     * Returns the SQL snippet to get the lowest value of a column.
591
     *
592 291
     * @param string $expression The column to use.
593
     *
594
     * @return string Generated SQL including a MIN aggregate function.
595
     */
596
    public function getMinExpression(string $expression) : string
597
    {
598
        return 'MIN(' . $expression . ')';
599
    }
600
601
    /**
602
     * Returns the SQL snippet to get the total sum of a column.
603
     *
604
     * @param string $expression The column to use.
605
     *
606
     * @return string Generated SQL including a SUM aggregate function.
607
     */
608
    public function getSumExpression(string $expression) : string
609
    {
610
        return 'SUM(' . $expression . ')';
611
    }
612 69
613
    // scalar functions
614 69
615
    /**
616
     * Returns the SQL snippet to get the md5 sum of a field.
617
     *
618
     * Note: Not SQL92, but common functionality.
619
     */
620
    public function getMd5Expression(string $expression) : string
621
    {
622
        return 'MD5(' . $expression . ')';
623
    }
624
625
    /**
626
     * Returns the SQL snippet to get the length of a text field.
627
     */
628
    public function getLengthExpression(string $expression) : string
629
    {
630
        return 'LENGTH(' . $expression . ')';
631
    }
632
633
    /**
634
     * Returns the SQL snippet to get the squared value of a column.
635
     *
636
     * @param string $expression The column to use.
637
     *
638
     * @return string Generated SQL including an SQRT aggregate function.
639
     */
640
    public function getSqrtExpression(string $expression) : string
641
    {
642
        return 'SQRT(' . $expression . ')';
643
    }
644
645
    /**
646
     * Returns the SQL snippet to round a numeric field to the number of decimals specified.
647
     */
648
    public function getRoundExpression(string $expression, int $decimals = 0) : string
649
    {
650
        return 'ROUND(' . $expression . ', ' . $decimals . ')';
651
    }
652
653
    /**
654
     * Returns the SQL snippet to get the remainder of the division operation $expression1 / $expression2.
655
     */
656
    public function getModExpression(string $expression1, string $expression2) : string
657
    {
658
        return 'MOD(' . $expression1 . ', ' . $expression2 . ')';
659
    }
660
661
    /**
662
     * Returns the SQL snippet to trim a string.
663
     *
664
     * @param string      $str  The expression to apply the trim to.
665
     * @param int         $mode The position of the trim (leading/trailing/both).
666
     * @param string|null $char The char to trim, has to be quoted already. Defaults to space.
667
     */
668
    public function getTrimExpression(string $str, int $mode = TrimMode::UNSPECIFIED, ?string $char = null) : string
669
    {
670
        $expression = '';
671
672
        switch ($mode) {
673
            case TrimMode::LEADING:
674
                $expression = 'LEADING ';
675
                break;
676
677
            case TrimMode::TRAILING:
678
                $expression = 'TRAILING ';
679
                break;
680
681
            case TrimMode::BOTH:
682
                $expression = 'BOTH ';
683
                break;
684
        }
685
686
        if ($char !== null) {
687
            $expression .= $char . ' ';
688
        }
689
690
        if ($mode || $char !== null) {
691
            $expression .= 'FROM ';
692
        }
693
694
        return 'TRIM(' . $expression . $str . ')';
695
    }
696
697
    /**
698
     * Returns the SQL snippet to trim trailing space characters from the expression.
699
     *
700
     * @param string $expression Literal string or column name.
701
     */
702
    public function getRtrimExpression(string $expression) : string
703
    {
704
        return 'RTRIM(' . $expression . ')';
705
    }
706
707
    /**
708
     * Returns the SQL snippet to trim leading space characters from the expression.
709
     *
710
     * @param string $expression Literal string or column name.
711
     */
712
    public function getLtrimExpression(string $expression) : string
713
    {
714
        return 'LTRIM(' . $expression . ')';
715
    }
716
717
    /**
718
     * Returns the SQL snippet to change all characters from the expression to uppercase,
719
     * according to the current character set mapping.
720
     *
721
     * @param string $expression Literal string or column name.
722
     */
723
    public function getUpperExpression(string $expression) : string
724
    {
725
        return 'UPPER(' . $expression . ')';
726
    }
727
728
    /**
729
     * Returns the SQL snippet to change all characters from the expression to lowercase,
730
     * according to the current character set mapping.
731
     *
732
     * @param string $expression Literal string or column name.
733
     */
734
    public function getLowerExpression(string $expression) : string
735
    {
736
        return 'LOWER(' . $expression . ')';
737
    }
738
739
    /**
740
     * Returns the SQL snippet to get the position of the first occurrence of substring $substr in string $str.
741
     *
742
     * @param string $str      Literal string.
743
     * @param string $substr   Literal string to find.
744
     * @param int    $startPos Position to start at, beginning of string by default.
745
     */
746
    public function getLocateExpression(string $str, string $substr, int $startPos = 1) : string
0 ignored issues
show
Unused Code introduced by
The parameter $startPos 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

746
    public function getLocateExpression(string $str, string $substr, /** @scrutinizer ignore-unused */ int $startPos = 1) : string

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 $substr 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

746
    public function getLocateExpression(string $str, /** @scrutinizer ignore-unused */ string $substr, int $startPos = 1) : string

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...
747
    {
748
        throw DBALException::notSupported(__METHOD__);
749
    }
750
751
    /**
752
     * Returns the SQL snippet to get the current system date.
753
     */
754 720
    public function getNowExpression() : string
755
    {
756 720
        return 'NOW()';
757
    }
758 720
759
    /**
760 180
     * Returns a SQL snippet to get a substring inside an SQL statement.
761 180
     *
762
     * Note: Not SQL92, but common functionality.
763
     *
764 180
     * SQLite only supports the 2 parameter variant of this function.
765 180
     *
766
     * @param string   $value  An sql string literal or column name/alias.
767
     * @param int      $from   Where to start the substring portion.
768 180
     * @param int|null $length The substring portion length.
769 180
     */
770
    public function getSubstringExpression(string $value, int $from, ?int $length = null) : string
771
    {
772 720
        if ($length === null) {
773 560
            return 'SUBSTRING(' . $value . ' FROM ' . $from . ')';
774
        }
775
776 720
        return 'SUBSTRING(' . $value . ' FROM ' . $from . ' FOR ' . $length . ')';
777 680
    }
778
779
    /**
780 720
     * Returns a SQL snippet to concatenate the given expressions.
781
     *
782
     * @param string[] ...$expressions
783
     */
784
    public function getConcatExpression(string ...$expressions) : string
785
    {
786
        return implode(' || ', $expressions);
787
    }
788
789
    /**
790 23
     * Returns the SQL for a logical not.
791
     *
792 23
     * Example:
793
     * <code>
794
     * $q = new Doctrine_Query();
795
     * $e = $q->expr;
796
     * $q->select('*')->from('table')
797
     *   ->where($e->eq('id', $e->not('null'));
798
     * </code>
799
     *
800
     * @return string The logical expression.
801
     */
802 23
    public function getNotExpression(string $expression) : string
803
    {
804 23
        return 'NOT(' . $expression . ')';
805
    }
806
807
    /**
808
     * Returns the SQL that checks if an expression is null.
809
     *
810
     * @param string $expression The expression that should be compared to null.
811
     *
812
     * @return string The logical expression.
813
     */
814
    public function getIsNullExpression(string $expression) : string
815
    {
816
        return $expression . ' IS NULL';
817
    }
818
819
    /**
820
     * Returns the SQL that checks if an expression is not null.
821
     *
822
     * @param string $expression The expression that should be compared to null.
823
     *
824
     * @return string The logical expression.
825
     */
826
    public function getIsNotNullExpression(string $expression) : string
827
    {
828
        return $expression . ' IS NOT NULL';
829
    }
830
831
    /**
832
     * Returns the SQL that checks if an expression evaluates to a value between two values.
833
     *
834
     * The parameter $expression is checked if it is between $value1 and $value2.
835
     *
836
     * Note: There is a slight difference in the way BETWEEN works on some databases.
837
     * http://www.w3schools.com/sql/sql_between.asp. If you want complete database
838
     * independence you should avoid using between().
839
     *
840
     * @param string $expression The value to compare to.
841
     * @param string $value1     The lower value to compare with.
842
     * @param string $value2     The higher value to compare with.
843
     *
844
     * @return string The logical expression.
845
     */
846
    public function getBetweenExpression(string $expression, string $value1, string $value2) : string
847
    {
848
        return $expression . ' BETWEEN ' . $value1 . ' AND ' . $value2;
849
    }
850
851
    /**
852
     * Returns the SQL to get the arccosine of a value.
853
     */
854
    public function getAcosExpression(string $value) : string
855
    {
856
        return 'ACOS(' . $value . ')';
857
    }
858
859
    /**
860
     * Returns the SQL to get the sine of a value.
861
     */
862
    public function getSinExpression(string $value) : string
863
    {
864
        return 'SIN(' . $value . ')';
865
    }
866
867
    /**
868
     * Returns the SQL to get the PI value.
869
     */
870
    public function getPiExpression() : string
871
    {
872
        return 'PI()';
873
    }
874
875
    /**
876
     * Returns the SQL to get the cosine of a value.
877
     */
878
    public function getCosExpression(string $value) : string
879
    {
880
        return 'COS(' . $value . ')';
881
    }
882
883
    /**
884
     * Returns the SQL to calculate the difference in days between the two passed dates.
885
     *
886
     * Computes diff = date1 - date2.
887
     *
888 92
     * @throws DBALException If not supported on this platform.
889
     */
890 92
    public function getDateDiffExpression(string $date1, string $date2) : string
0 ignored issues
show
Unused Code introduced by
The parameter $date1 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

890
    public function getDateDiffExpression(/** @scrutinizer ignore-unused */ string $date1, string $date2) : string

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 $date2 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

890
    public function getDateDiffExpression(string $date1, /** @scrutinizer ignore-unused */ string $date2) : string

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...
891
    {
892
        throw DBALException::notSupported(__METHOD__);
893
    }
894
895
    /**
896
     * Returns the SQL to add the number of given seconds to a date.
897
     *
898
     * @throws DBALException If not supported on this platform.
899
     */
900
    public function getDateAddSecondsExpression(string $date, string $seconds) : string
901
    {
902
        return $this->getDateArithmeticIntervalExpression($date, '+', $seconds, DateIntervalUnit::SECOND);
903
    }
904
905
    /**
906
     * Returns the SQL to subtract the number of given seconds from a date.
907
     *
908
     * @throws DBALException If not supported on this platform.
909
     */
910
    public function getDateSubSecondsExpression(string $date, string $seconds) : string
911
    {
912
        return $this->getDateArithmeticIntervalExpression($date, '-', $seconds, DateIntervalUnit::SECOND);
913
    }
914
915
    /**
916
     * Returns the SQL to add the number of given minutes to a date.
917
     *
918
     * @throws DBALException If not supported on this platform.
919
     */
920 92
    public function getDateAddMinutesExpression(string $date, string $minutes) : string
921
    {
922 92
        return $this->getDateArithmeticIntervalExpression($date, '+', $minutes, DateIntervalUnit::MINUTE);
923
    }
924
925
    /**
926
     * Returns the SQL to subtract the number of given minutes from a date.
927
     *
928
     * @throws DBALException If not supported on this platform.
929
     */
930
    public function getDateSubMinutesExpression(string $date, string $minutes) : string
931
    {
932
        return $this->getDateArithmeticIntervalExpression($date, '-', $minutes, DateIntervalUnit::MINUTE);
933
    }
934
935
    /**
936
     * Returns the SQL to add the number of given hours to a date.
937
     *
938
     * @throws DBALException If not supported on this platform.
939
     */
940
    public function getDateAddHourExpression(string $date, string $hours) : string
941
    {
942
        return $this->getDateArithmeticIntervalExpression($date, '+', $hours, DateIntervalUnit::HOUR);
943
    }
944
945
    /**
946
     * Returns the SQL to subtract the number of given hours to a date.
947
     *
948
     * @throws DBALException If not supported on this platform.
949
     */
950
    public function getDateSubHourExpression(string $date, string $hours) : string
951
    {
952
        return $this->getDateArithmeticIntervalExpression($date, '-', $hours, DateIntervalUnit::HOUR);
953
    }
954
955
    /**
956
     * Returns the SQL to add the number of given days to a date.
957
     *
958
     * @throws DBALException If not supported on this platform.
959
     */
960
    public function getDateAddDaysExpression(string $date, string $days) : string
961
    {
962
        return $this->getDateArithmeticIntervalExpression($date, '+', $days, DateIntervalUnit::DAY);
963
    }
964
965
    /**
966
     * Returns the SQL to subtract the number of given days to a date.
967
     *
968
     * @throws DBALException If not supported on this platform.
969
     */
970
    public function getDateSubDaysExpression(string $date, string $days) : string
971
    {
972
        return $this->getDateArithmeticIntervalExpression($date, '-', $days, DateIntervalUnit::DAY);
973
    }
974
975
    /**
976
     * Returns the SQL to add the number of given weeks to a date.
977
     *
978
     * @throws DBALException If not supported on this platform.
979
     */
980
    public function getDateAddWeeksExpression(string $date, string $weeks) : string
981
    {
982
        return $this->getDateArithmeticIntervalExpression($date, '+', $weeks, DateIntervalUnit::WEEK);
983
    }
984
985
    /**
986
     * Returns the SQL to subtract the number of given weeks from a date.
987
     *
988
     * @throws DBALException If not supported on this platform.
989
     */
990
    public function getDateSubWeeksExpression(string $date, string $weeks) : string
991
    {
992
        return $this->getDateArithmeticIntervalExpression($date, '-', $weeks, DateIntervalUnit::WEEK);
993
    }
994
995
    /**
996
     * Returns the SQL to add the number of given months to a date.
997
     *
998
     * @throws DBALException If not supported on this platform.
999
     */
1000
    public function getDateAddMonthExpression(string $date, string $months) : string
1001
    {
1002
        return $this->getDateArithmeticIntervalExpression($date, '+', $months, DateIntervalUnit::MONTH);
1003
    }
1004
1005
    /**
1006
     * Returns the SQL to subtract the number of given months to a date.
1007
     *
1008
     * @throws DBALException If not supported on this platform.
1009
     */
1010
    public function getDateSubMonthExpression(string $date, string $months) : string
1011
    {
1012
        return $this->getDateArithmeticIntervalExpression($date, '-', $months, DateIntervalUnit::MONTH);
1013
    }
1014
1015
    /**
1016
     * Returns the SQL to add the number of given quarters to a date.
1017
     *
1018
     * @throws DBALException If not supported on this platform.
1019
     */
1020
    public function getDateAddQuartersExpression(string $date, string $quarters) : string
1021
    {
1022
        return $this->getDateArithmeticIntervalExpression($date, '+', $quarters, DateIntervalUnit::QUARTER);
1023
    }
1024
1025
    /**
1026
     * Returns the SQL to subtract the number of given quarters from a date.
1027
     *
1028
     * @throws DBALException If not supported on this platform.
1029
     */
1030 69
    public function getDateSubQuartersExpression(string $date, string $quarters) : string
1031
    {
1032 69
        return $this->getDateArithmeticIntervalExpression($date, '-', $quarters, DateIntervalUnit::QUARTER);
1033
    }
1034
1035
    /**
1036
     * Returns the SQL to add the number of given years to a date.
1037
     *
1038
     * @throws DBALException If not supported on this platform.
1039
     */
1040
    public function getDateAddYearsExpression(string $date, string $years) : string
1041
    {
1042
        return $this->getDateArithmeticIntervalExpression($date, '+', $years, DateIntervalUnit::YEAR);
1043
    }
1044
1045 69
    /**
1046
     * Returns the SQL to subtract the number of given years from a date.
1047 69
     *
1048
     * @throws DBALException If not supported on this platform.
1049
     */
1050
    public function getDateSubYearsExpression(string $date, string $years) : string
1051
    {
1052
        return $this->getDateArithmeticIntervalExpression($date, '-', $years, DateIntervalUnit::YEAR);
1053
    }
1054
1055
    /**
1056
     * Returns the SQL for a date arithmetic expression.
1057
     *
1058
     * @param string $date     The column or literal representing a date to perform the arithmetic operation on.
1059
     * @param string $operator The arithmetic operator (+ or -).
1060 69
     * @param string $interval The interval that shall be calculated into the date.
1061
     * @param string $unit     The unit of the interval that shall be calculated into the date.
1062 69
     *                                 One of the DATE_INTERVAL_UNIT_* constants.
1063
     *
1064
     * @throws DBALException If not supported on this platform.
1065
     */
1066
    protected function getDateArithmeticIntervalExpression(string $date, string $operator, string $interval, string $unit) : string
0 ignored issues
show
Unused Code introduced by
The parameter $operator 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

1066
    protected function getDateArithmeticIntervalExpression(string $date, /** @scrutinizer ignore-unused */ string $operator, string $interval, string $unit) : string

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...
1067
    {
1068
        throw DBALException::notSupported(__METHOD__);
1069
    }
1070
1071
    /**
1072
     * Converts the value of a natively unsupported date interval to a value of a supported one
1073
     *
1074
     * @param string $interval   SQL expression describing the value of the interval
1075 69
     * @param string $unit       Interval unit
1076
     * @param int    $multiplier Interval multiplier
1077 69
     *
1078
     * @throws DBALException
1079
     */
1080
    protected function convertNonNativeInterval(string $interval, string $unit, int $multiplier) : string
1081
    {
1082
        if (! is_numeric($interval)) {
1083
            throw new DBALException(sprintf('Non-numeric value of a %s interval is not supported', $unit));
1084
        }
1085
1086
        return (string) ((int) $interval * $multiplier);
1087
    }
1088
1089
    /**
1090 69
     * Returns the SQL bit AND comparison expression.
1091
     */
1092 69
    public function getBitAndComparisonExpression(string $value1, string $value2) : string
1093
    {
1094
        return '(' . $value1 . ' & ' . $value2 . ')';
1095
    }
1096
1097
    /**
1098
     * Returns the SQL bit OR comparison expression.
1099
     */
1100
    public function getBitOrComparisonExpression(string $value1, string $value2) : string
1101
    {
1102
        return '(' . $value1 . ' | ' . $value2 . ')';
1103
    }
1104
1105 69
    /**
1106
     * Returns the FOR UPDATE expression.
1107 69
     */
1108
    public function getForUpdateSQL() : string
1109
    {
1110
        return 'FOR UPDATE';
1111
    }
1112
1113
    /**
1114
     * Honors that some SQL vendors such as MsSql use table hints for locking instead of the ANSI SQL FOR UPDATE specification.
1115
     *
1116
     * @param string   $fromClause The FROM clause to append the hint for the given lock mode to.
1117
     * @param int|null $lockMode   One of the Doctrine\DBAL\LockMode::* constants. If null is given, nothing will
1118
     *                             be appended to the FROM clause.
1119
     */
1120 115
    public function appendLockHint(string $fromClause, ?int $lockMode) : string
0 ignored issues
show
Unused Code introduced by
The parameter $lockMode 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

1120
    public function appendLockHint(string $fromClause, /** @scrutinizer ignore-unused */ ?int $lockMode) : string

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...
1121
    {
1122 115
        return $fromClause;
1123
    }
1124
1125
    /**
1126
     * Returns the SQL snippet to append to any SELECT statement which locks rows in shared read lock.
1127
     *
1128
     * This defaults to the ANSI SQL "FOR UPDATE", which is an exclusive lock (Write). Some database
1129
     * vendors allow to lighten this constraint up to be a real read lock.
1130
     */
1131
    public function getReadLockSQL() : string
1132
    {
1133
        return $this->getForUpdateSQL();
1134
    }
1135 70
1136
    /**
1137 70
     * Returns the SQL snippet to append to any SELECT statement which obtains an exclusive lock on the rows.
1138
     *
1139
     * The semantics of this lock mode should equal the SELECT .. FOR UPDATE of the ANSI SQL standard.
1140
     */
1141
    public function getWriteLockSQL() : string
1142
    {
1143
        return $this->getForUpdateSQL();
1144
    }
1145
1146
    /**
1147
     * Returns the SQL snippet to drop an existing database.
1148
     *
1149
     * @param string $database The name of the database that should be dropped.
1150 69
     */
1151
    public function getDropDatabaseSQL(string $database) : string
1152 69
    {
1153
        return 'DROP DATABASE ' . $database;
1154
    }
1155
1156
    /**
1157
     * Returns the SQL snippet to drop an existing table.
1158
     *
1159
     * @param Table|string $table
1160
     *
1161
     * @throws InvalidArgumentException
1162
     */
1163
    public function getDropTableSQL($table) : string
1164
    {
1165 69
        $tableArg = $table;
1166
1167 69
        if ($table instanceof Table) {
1168
            $table = $table->getQuotedName($this);
1169
        }
1170
1171
        if (! is_string($table)) {
0 ignored issues
show
introduced by
The condition is_string($table) is always true.
Loading history...
1172
            throw new InvalidArgumentException('getDropTableSQL() expects $table parameter to be string or \Doctrine\DBAL\Schema\Table.');
1173
        }
1174
1175
        if ($this->_eventManager !== null && $this->_eventManager->hasListeners(Events::onSchemaDropTable)) {
1176
            $eventArgs = new SchemaDropTableEventArgs($tableArg, $this);
1177
            $this->_eventManager->dispatchEvent(Events::onSchemaDropTable, $eventArgs);
1178
1179
            if ($eventArgs->isDefaultPrevented()) {
1180 69
                $sql = $eventArgs->getSql();
1181
1182 69
                if ($sql === null) {
1183
                    throw new UnexpectedValueException('Default implementation of DROP TABLE was overridden with NULL');
1184
                }
1185
1186
                return $sql;
1187
            }
1188
        }
1189
1190
        return 'DROP TABLE ' . $table;
1191
    }
1192
1193
    /**
1194
     * Returns the SQL to safely drop a temporary table WITHOUT implicitly committing an open transaction.
1195 69
     *
1196
     * @param Table|string $table
1197 69
     */
1198
    public function getDropTemporaryTableSQL($table) : string
1199
    {
1200
        return $this->getDropTableSQL($table);
1201
    }
1202
1203
    /**
1204
     * Returns the SQL to drop an index from a table.
1205
     *
1206
     * @param Index|string $index
1207
     * @param Table|string $table
1208
     *
1209
     * @throws InvalidArgumentException
1210 69
     */
1211
    public function getDropIndexSQL($index, $table = null) : string
1212 69
    {
1213
        if ($index instanceof Index) {
1214
            $index = $index->getQuotedName($this);
1215
        } elseif (! is_string($index)) {
0 ignored issues
show
introduced by
The condition is_string($index) is always true.
Loading history...
1216
            throw new InvalidArgumentException('AbstractPlatform::getDropIndexSQL() expects $index parameter to be string or \Doctrine\DBAL\Schema\Index.');
1217
        }
1218
1219
        return 'DROP INDEX ' . $index;
1220
    }
1221
1222
    /**
1223
     * Returns the SQL to drop a constraint.
1224
     *
1225 69
     * @param Constraint|string $constraint
1226
     * @param Table|string      $table
1227 69
     */
1228
    public function getDropConstraintSQL($constraint, $table) : string
1229
    {
1230
        if (! $constraint instanceof Constraint) {
1231
            $constraint = new Identifier($constraint);
1232
        }
1233
1234
        if (! $table instanceof Table) {
1235
            $table = new Identifier($table);
1236
        }
1237
1238
        $constraint = $constraint->getQuotedName($this);
1239
        $table      = $table->getQuotedName($this);
1240 69
1241
        return 'ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $constraint;
1242 69
    }
1243
1244
    /**
1245
     * Returns the SQL to drop a foreign key.
1246
     *
1247
     * @param ForeignKeyConstraint|string $foreignKey
1248
     * @param Table|string                $table
1249
     */
1250
    public function getDropForeignKeySQL($foreignKey, $table) : string
1251
    {
1252
        if (! $foreignKey instanceof ForeignKeyConstraint) {
1253
            $foreignKey = new Identifier($foreignKey);
1254
        }
1255 69
1256
        if (! $table instanceof Table) {
1257 69
            $table = new Identifier($table);
1258
        }
1259
1260
        $foreignKey = $foreignKey->getQuotedName($this);
1261
        $table      = $table->getQuotedName($this);
1262
1263
        return 'ALTER TABLE ' . $table . ' DROP FOREIGN KEY ' . $foreignKey;
1264
    }
1265
1266
    /**
1267
     * Returns the SQL statement(s) to create a table with the specified name, columns and constraints
1268
     * on this platform.
1269
     *
1270
     * @return string[] The sequence of SQL statements.
1271
     *
1272
     * @throws DBALException
1273
     * @throws InvalidArgumentException
1274
     */
1275
    public function getCreateTableSQL(Table $table, int $createFlags = self::CREATE_INDEXES) : array
1276
    {
1277
        if (count($table->getColumns()) === 0) {
1278
            throw DBALException::noColumnsSpecifiedForTable($table->getName());
1279
        }
1280
1281
        $tableName                    = $table->getQuotedName($this);
1282
        $options                      = $table->getOptions();
1283
        $options['uniqueConstraints'] = [];
1284
        $options['indexes']           = [];
1285
        $options['primary']           = [];
1286 251
1287
        if (($createFlags & self::CREATE_INDEXES) > 0) {
1288 251
            foreach ($table->getIndexes() as $index) {
1289
                /** @var $index Index */
1290
                if (! $index->isPrimary()) {
1291
                    $options['indexes'][$index->getQuotedName($this)] = $index;
1292
1293
                    continue;
1294
                }
1295
1296
                $options['primary']       = $index->getQuotedColumns($this);
1297
                $options['primary_index'] = $index;
1298
            }
1299 251
1300
            foreach ($table->getUniqueConstraints() as $uniqueConstraint) {
1301 251
                /** @var UniqueConstraint $uniqueConstraint */
1302
                $options['uniqueConstraints'][$uniqueConstraint->getQuotedName($this)] = $uniqueConstraint;
1303
            }
1304
        }
1305
1306
        if (($createFlags & self::CREATE_FOREIGNKEYS) > 0) {
1307
            $options['foreignKeys'] = [];
1308
1309 38
            foreach ($table->getForeignKeys() as $fkConstraint) {
1310
                $options['foreignKeys'][] = $fkConstraint;
1311 38
            }
1312
        }
1313
1314
        $columnSql = [];
1315
        $columns   = [];
1316
1317
        foreach ($table->getColumns() as $column) {
1318
            if ($this->_eventManager !== null && $this->_eventManager->hasListeners(Events::onSchemaCreateTableColumn)) {
1319
                $eventArgs = new SchemaCreateTableColumnEventArgs($column, $table, $this);
1320
                $this->_eventManager->dispatchEvent(Events::onSchemaCreateTableColumn, $eventArgs);
1321
1322
                $columnSql = array_merge($columnSql, $eventArgs->getSql());
1323 40
1324
                if ($eventArgs->isDefaultPrevented()) {
1325 40
                    continue;
1326
                }
1327
            }
1328
1329
            $columnData            = $column->toArray();
1330
            $columnData['name']    = $column->getQuotedName($this);
1331
            $columnData['version'] = $column->hasPlatformOption('version') ? $column->getPlatformOption('version') : false;
1332
            $columnData['comment'] = $this->getColumnComment($column);
1333
1334
            if ($columnData['type'] instanceof Types\StringType && $columnData['length'] === null) {
1335
                $columnData['length'] = 255;
1336
            }
1337
1338
            if (in_array($column->getName(), $options['primary'])) {
1339
                $columnData['primary'] = true;
1340
            }
1341
1342
            $columns[$columnData['name']] = $columnData;
1343
        }
1344
1345
        if ($this->_eventManager !== null && $this->_eventManager->hasListeners(Events::onSchemaCreateTable)) {
1346
            $eventArgs = new SchemaCreateTableEventArgs($table, $columns, $options, $this);
1347
            $this->_eventManager->dispatchEvent(Events::onSchemaCreateTable, $eventArgs);
1348 44
1349
            if ($eventArgs->isDefaultPrevented()) {
1350 44
                return array_merge($eventArgs->getSql(), $columnSql);
1351
            }
1352
        }
1353
1354
        $sql = $this->_getCreateTableSQL($tableName, $columns, $options);
1355
        if ($this->supportsCommentOnStatement()) {
1356
            foreach ($table->getColumns() as $column) {
1357
                $comment = $this->getColumnComment($column);
1358
1359
                if ($comment === null || $comment === '') {
1360 81
                    continue;
1361
                }
1362 81
1363
                $sql[] = $this->getCommentOnColumnSQL($tableName, $column->getQuotedName($this), $comment);
1364
            }
1365
        }
1366
1367
        return array_merge($sql, $columnSql);
1368
    }
1369
1370
    public function getCommentOnColumnSQL(string $tableName, string $columnName, ?string $comment) : string
1371
    {
1372
        $tableName  = new Identifier($tableName);
1373
        $columnName = new Identifier($columnName);
1374 3453
1375
        return sprintf(
1376 3453
            'COMMENT ON COLUMN %s.%s IS %s',
1377
            $tableName->getQuotedName($this),
1378 3453
            $columnName->getQuotedName($this),
1379 335
            $this->quoteStringLiteral((string) $comment)
1380
        );
1381
    }
1382 3453
1383
    /**
1384
     * Returns the SQL to create inline comment on a column.
1385
     *
1386 3453
     * @throws DBALException If not supported on this platform.
1387 276
     */
1388 276
    public function getInlineColumnCommentSQL(?string $comment) : string
1389
    {
1390 276
        if (! $this->supportsInlineColumnComments()) {
1391
            throw DBALException::notSupported(__METHOD__);
1392
        }
1393
1394
        return 'COMMENT ' . $this->quoteStringLiteral((string) $comment);
1395 3453
    }
1396
1397
    /**
1398
     * Returns the SQL used to create a table.
1399
     *
1400
     * @param mixed[][] $columns
1401
     * @param mixed[]   $options
1402
     *
1403
     * @return string[]
1404
     */
1405 20
    protected function _getCreateTableSQL(string $tableName, array $columns, array $options = []) : array
1406
    {
1407 20
        $columnListSql = $this->getColumnDeclarationListSQL($columns);
1408
1409
        if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) {
1410
            foreach ($options['uniqueConstraints'] as $name => $definition) {
1411
                $columnListSql .= ', ' . $this->getUniqueConstraintDeclarationSQL($name, $definition);
1412
            }
1413
        }
1414
1415
        if (isset($options['primary']) && ! empty($options['primary'])) {
1416
            $columnListSql .= ', PRIMARY KEY(' . implode(', ', array_unique(array_values($options['primary']))) . ')';
1417
        }
1418
1419
        if (isset($options['indexes']) && ! empty($options['indexes'])) {
1420 164
            foreach ($options['indexes'] as $index => $definition) {
1421
                $columnListSql .= ', ' . $this->getIndexDeclarationSQL($index, $definition);
1422 164
            }
1423 155
        }
1424 9
1425
        $query = 'CREATE TABLE ' . $tableName . ' (' . $columnListSql;
1426
1427
        $check = $this->getCheckDeclarationSQL($columns);
1428 164
        if (! empty($check)) {
1429
            $query .= ', ' . $check;
1430
        }
1431
        $query .= ')';
1432
1433
        $sql[] = $query;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$sql was never initialized. Although not strictly required by PHP, it is generally a good practice to add $sql = array(); before regardless.
Loading history...
1434
1435
        if (isset($options['foreignKeys'])) {
1436
            foreach ((array) $options['foreignKeys'] as $definition) {
1437
                $sql[] = $this->getCreateForeignKeySQL($definition, $tableName);
1438
            }
1439 655
        }
1440
1441 655
        return $sql;
1442 511
    }
1443
1444
    public function getCreateTemporaryTableSnippetSQL() : string
1445 655
    {
1446 655
        return 'CREATE TEMPORARY TABLE';
1447
    }
1448
1449 655
    /**
1450 655
     * Returns the SQL to create a sequence on this platform.
1451
     *
1452 655
     * @throws DBALException If not supported on this platform.
1453
     */
1454
    public function getCreateSequenceSQL(Sequence $sequence) : string
0 ignored issues
show
Unused Code introduced by
The parameter $sequence 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

1454
    public function getCreateSequenceSQL(/** @scrutinizer ignore-unused */ Sequence $sequence) : string

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...
1455
    {
1456
        throw DBALException::notSupported(__METHOD__);
1457
    }
1458
1459
    /**
1460
     * Returns the SQL to change a sequence on this platform.
1461
     *
1462
     * @throws DBALException If not supported on this platform.
1463 368
     */
1464
    public function getAlterSequenceSQL(Sequence $sequence) : string
0 ignored issues
show
Unused Code introduced by
The parameter $sequence 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

1464
    public function getAlterSequenceSQL(/** @scrutinizer ignore-unused */ Sequence $sequence) : string

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...
1465 368
    {
1466 115
        throw DBALException::notSupported(__METHOD__);
1467
    }
1468
1469 368
    /**
1470 368
     * Returns the SQL to create a constraint on a table on this platform.
1471
     *
1472
     * @param Table|string $table
1473 368
     *
1474 368
     * @throws InvalidArgumentException
1475
     */
1476 368
    public function getCreateConstraintSQL(Constraint $constraint, $table) : string
1477
    {
1478
        if ($table instanceof Table) {
1479
            $table = $table->getQuotedName($this);
1480
        }
1481
1482
        $query = 'ALTER TABLE ' . $table . ' ADD CONSTRAINT ' . $constraint->getQuotedName($this);
1483
1484
        $columnList = '(' . implode(', ', $constraint->getQuotedColumns($this)) . ')';
1485
1486
        $referencesClause = '';
1487
        if ($constraint instanceof Index) {
1488
            if ($constraint->isPrimary()) {
1489
                $query .= ' PRIMARY KEY';
1490 8074
            } elseif ($constraint->isUnique()) {
1491
                $query .= ' UNIQUE';
1492 8074
            } else {
1493
                throw new InvalidArgumentException(
1494
                    'Can only create primary or unique constraints, no common indexes with getCreateConstraintSQL().'
1495
                );
1496 8074
            }
1497 276
        } elseif ($constraint instanceof ForeignKeyConstraint) {
1498
            $query .= ' FOREIGN KEY';
1499
1500 7798
            $referencesClause = ' REFERENCES ' . $constraint->getQuotedForeignTableName($this) .
1501 7798
                ' (' . implode(', ', $constraint->getQuotedForeignColumns($this)) . ')';
1502 7798
        }
1503 7798
        $query .= ' ' . $columnList . $referencesClause;
1504 7798
1505
        return $query;
1506 7798
    }
1507 7476
1508
    /**
1509 5003
     * Returns the SQL to create an index on a table on this platform.
1510 1567
     *
1511
     * @param Table|string $table The name of the table on which the index is to be created.
1512 1567
     *
1513
     * @throws InvalidArgumentException
1514
     */
1515 3870
    public function getCreateIndexSQL(Index $index, $table) : string
1516 3870
    {
1517
        if ($table instanceof Table) {
1518
            $table = $table->getQuotedName($this);
1519 7476
        }
1520
        $name    = $index->getQuotedName($this);
1521
        $columns = $index->getColumns();
1522
1523
        if (count($columns) === 0) {
1524
            throw new InvalidArgumentException("Incomplete definition. 'columns' required.");
1525 7798
        }
1526 4345
1527
        if ($index->isPrimary()) {
1528 4345
            return $this->getCreatePrimaryKeySQL($index, $table);
1529 675
        }
1530
1531
        $query  = 'CREATE ' . $this->getCreateIndexSQLFlags($index) . 'INDEX ' . $name . ' ON ' . $table;
1532
        $query .= ' (' . $this->getIndexFieldDeclarationListSQL($index) . ')' . $this->getPartialIndexSQL($index);
1533 7798
1534 7798
        return $query;
1535
    }
1536 7798
1537
    /**
1538 7798
     * Adds condition for partial index.
1539 276
     */
1540 276
    protected function getPartialIndexSQL(Index $index) : string
1541
    {
1542 276
        if ($this->supportsPartialIndexes() && $index->hasOption('where')) {
1543
            return ' WHERE ' . $index->getOption('where');
1544 276
        }
1545
1546
        return '';
1547
    }
1548
1549 7798
    /**
1550 7798
     * Adds additional flags for index generation.
1551 7798
     */
1552 7798
    protected function getCreateIndexSQLFlags(Index $index) : string
1553
    {
1554 7798
        return $index->isUnique() ? 'UNIQUE ' : '';
1555 2881
    }
1556
1557
    /**
1558 7798
     * Returns the SQL to create an unnamed primary key constraint.
1559 3548
     *
1560
     * @param Table|string $table
1561
     */
1562 7798
    public function getCreatePrimaryKeySQL(Index $index, $table) : string
1563
    {
1564
        if ($table instanceof Table) {
1565 7798
            $table = $table->getQuotedName($this);
1566 276
        }
1567 276
1568
        return 'ALTER TABLE ' . $table . ' ADD PRIMARY KEY (' . $this->getIndexFieldDeclarationListSQL($index) . ')';
1569 276
    }
1570
1571
    /**
1572
     * Returns the SQL to create a named schema.
1573
     *
1574 7798
     * @throws DBALException If not supported on this platform.
1575 7798
     */
1576 2993
    public function getCreateSchemaSQL(string $schemaName) : string
1577 2993
    {
1578
        throw DBALException::notSupported(__METHOD__);
1579 2993
    }
1580 2807
1581
    /**
1582
     * Quotes a string so that it can be safely used as a table or column name,
1583 525
     * even if it is a reserved word of the platform. This also detects identifier
1584
     * chains separated by dot and quotes them independently.
1585
     *
1586
     * NOTE: Just because you CAN use quoted identifiers doesn't mean
1587 7798
     * you SHOULD use them. In general, they end up causing way more
1588
     * problems than they solve.
1589
     *
1590
     * @param string $str The identifier name to be quoted.
1591
     *
1592
     * @return string The quoted identifier string.
1593
     */
1594
    public function quoteIdentifier(string $str) : string
1595
    {
1596
        if (strpos($str, '.') !== false) {
1597 777
            $parts = array_map([$this, 'quoteSingleIdentifier'], explode('.', $str));
1598
1599 777
            return implode('.', $parts);
1600 777
        }
1601 777
1602
        return $this->quoteSingleIdentifier($str);
1603 777
    }
1604 777
1605 777
    /**
1606 777
     * Quotes a single identifier (no dot chain separation).
1607 777
     *
1608
     * @param string $str The identifier name to be quoted.
1609
     *
1610
     * @return string The quoted identifier string.
1611
     */
1612
    public function quoteSingleIdentifier(string $str) : string
1613
    {
1614
        $c = $this->getIdentifierQuoteCharacter();
1615
1616
        return $c . str_replace($c, $c . $c, $str) . $c;
1617
    }
1618
1619
    /**
1620 1209
     * Returns the SQL to create a new foreign key.
1621
     *
1622 1209
     * @param ForeignKeyConstraint $foreignKey The foreign key constraint.
1623 184
     * @param Table|string         $table      The name of the table on which the foreign key is to be created.
1624
     */
1625
    public function getCreateForeignKeySQL(ForeignKeyConstraint $foreignKey, $table) : string
1626 1025
    {
1627
        if ($table instanceof Table) {
1628
            $table = $table->getQuotedName($this);
1629
        }
1630
1631
        return 'ALTER TABLE ' . $table . ' ADD ' . $this->getForeignKeyDeclarationSQL($foreignKey);
1632
    }
1633
1634
    /**
1635
     * Gets the SQL statements for altering an existing table.
1636
     *
1637
     * This method returns an array of SQL statements, since some platforms need several statements.
1638 793
     *
1639
     * @return string[]
1640 793
     *
1641
     * @throws DBALException If not supported on this platform.
1642 793
     */
1643
    public function getAlterTableSQL(TableDiff $diff)
1644
    {
1645
        throw DBALException::notSupported(__METHOD__);
1646
    }
1647
1648 793
    /**
1649 405
     * @param mixed[] $columnSql
1650
     */
1651
    protected function onSchemaAlterTableAddColumn(Column $column, TableDiff $diff, array &$columnSql) : bool
1652 793
    {
1653
        if ($this->_eventManager === null) {
1654
            return false;
1655
        }
1656
1657
        if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTableAddColumn)) {
1658 793
            return false;
1659
        }
1660 793
1661 793
        $eventArgs = new SchemaAlterTableAddColumnEventArgs($column, $diff, $this);
1662 23
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableAddColumn, $eventArgs);
1663
1664 793
        $columnSql = array_merge($columnSql, $eventArgs->getSql());
1665
1666 793
        return $eventArgs->isDefaultPrevented();
1667
    }
1668 793
1669 322
    /**
1670 82
     * @param string[] $columnSql
1671
     */
1672
    protected function onSchemaAlterTableRemoveColumn(Column $column, TableDiff $diff, array &$columnSql) : bool
1673
    {
1674 793
        if ($this->_eventManager === null) {
1675
            return false;
1676
        }
1677
1678
        if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTableRemoveColumn)) {
1679
            return false;
1680 38
        }
1681
1682 38
        $eventArgs = new SchemaAlterTableRemoveColumnEventArgs($column, $diff, $this);
1683
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableRemoveColumn, $eventArgs);
1684
1685
        $columnSql = array_merge($columnSql, $eventArgs->getSql());
1686
1687
        return $eventArgs->isDefaultPrevented();
1688
    }
1689
1690
    /**
1691
     * @param string[] $columnSql
1692
     */
1693
    protected function onSchemaAlterTableChangeColumn(ColumnDiff $columnDiff, TableDiff $diff, array &$columnSql) : bool
1694
    {
1695
        if ($this->_eventManager === null) {
1696
            return false;
1697
        }
1698
1699
        if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTableChangeColumn)) {
1700
            return false;
1701
        }
1702
1703
        $eventArgs = new SchemaAlterTableChangeColumnEventArgs($columnDiff, $diff, $this);
1704
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableChangeColumn, $eventArgs);
1705
1706
        $columnSql = array_merge($columnSql, $eventArgs->getSql());
1707
1708
        return $eventArgs->isDefaultPrevented();
1709
    }
1710
1711
    /**
1712
     * @param string[] $columnSql
1713
     */
1714
    protected function onSchemaAlterTableRenameColumn(string $oldColumnName, Column $column, TableDiff $diff, array &$columnSql) : bool
1715
    {
1716
        if ($this->_eventManager === null) {
1717
            return false;
1718 323
        }
1719
1720 323
        if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTableRenameColumn)) {
1721
            return false;
1722
        }
1723
1724 323
        $eventArgs = new SchemaAlterTableRenameColumnEventArgs($oldColumnName, $column, $diff, $this);
1725
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableRenameColumn, $eventArgs);
1726 323
1727
        $columnSql = array_merge($columnSql, $eventArgs->getSql());
1728 323
1729 323
        return $eventArgs->isDefaultPrevented();
1730 323
    }
1731 323
1732 230
    /**
1733 230
     * @param string[] $sql
1734
     */
1735
    protected function onSchemaAlterTable(TableDiff $diff, array &$sql) : bool
1736 323
    {
1737
        if ($this->_eventManager === null) {
1738
            return false;
1739 230
        }
1740 230
1741
        if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTable)) {
1742 230
            return false;
1743 230
        }
1744
1745 323
        $eventArgs = new SchemaAlterTableEventArgs($diff, $this);
1746
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTable, $eventArgs);
1747 323
1748
        $sql = array_merge($sql, $eventArgs->getSql());
1749
1750
        return $eventArgs->isDefaultPrevented();
1751
    }
1752
1753
    /**
1754
     * @return string[]
1755
     */
1756
    protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) : array
1757
    {
1758
        $tableName = $diff->getName($this)->getQuotedName($this);
1759 2638
1760
        $sql = [];
1761 2638
        if ($this->supportsForeignKeyConstraints()) {
1762 23
            foreach ($diff->removedForeignKeys as $foreignKey) {
1763
                $sql[] = $this->getDropForeignKeySQL($foreignKey, $tableName);
1764 2638
            }
1765 2638
            foreach ($diff->changedForeignKeys as $foreignKey) {
1766
                $sql[] = $this->getDropForeignKeySQL($foreignKey, $tableName);
1767 2638
            }
1768
        }
1769
1770
        foreach ($diff->removedIndexes as $index) {
1771 2638
            $sql[] = $this->getDropIndexSQL($index, $tableName);
1772 437
        }
1773
        foreach ($diff->changedIndexes as $index) {
1774
            $sql[] = $this->getDropIndexSQL($index, $tableName);
1775 2224
        }
1776 2224
1777
        return $sql;
1778 2224
    }
1779
1780
    /**
1781
     * @return string[]
1782
     */
1783
    protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff) : array
1784
    {
1785
        $sql     = [];
1786 3084
        $newName = $diff->getNewName();
1787
1788 3084
        if ($newName !== false) {
1789 75
            $tableName = $newName->getQuotedName($this);
1790
        } else {
1791
            $tableName = $diff->getName($this)->getQuotedName($this);
1792 3009
        }
1793
1794
        if ($this->supportsForeignKeyConstraints()) {
1795
            foreach ($diff->addedForeignKeys as $foreignKey) {
1796
                $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableName);
1797
            }
1798
1799
            foreach ($diff->changedForeignKeys as $foreignKey) {
1800 1240
                $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableName);
1801
            }
1802 1240
        }
1803
1804
        foreach ($diff->addedIndexes as $index) {
1805
            $sql[] = $this->getCreateIndexSQL($index, $tableName);
1806
        }
1807
1808
        foreach ($diff->changedIndexes as $index) {
1809
            $sql[] = $this->getCreateIndexSQL($index, $tableName);
1810
        }
1811
1812 391
        foreach ($diff->renamedIndexes as $oldIndexName => $index) {
1813
            $oldIndexName = new Identifier($oldIndexName);
1814 391
            $sql          = array_merge(
1815
                $sql,
1816
                $this->getRenameIndexSQL($oldIndexName->getQuotedName($this), $index, $tableName)
1817
            );
1818
        }
1819
1820
        return $sql;
1821
    }
1822
1823
    /**
1824
     * Returns the SQL for renaming an index on a table.
1825
     *
1826 161
     * @param string $oldIndexName The name of the index to rename from.
1827
     * @param Index  $index        The definition of the index to rename to.
1828 161
     * @param string $tableName    The table to rename the given index on.
1829
     *
1830
     * @return string[] The sequence of SQL statements for renaming the given index.
1831
     */
1832
    protected function getRenameIndexSQL(string $oldIndexName, Index $index, string $tableName) : array
1833
    {
1834
        return [
1835
            $this->getDropIndexSQL($oldIndexName, $tableName),
1836
            $this->getCreateIndexSQL($index, $tableName),
1837
        ];
1838
    }
1839
1840
    /**
1841
     * Common code for alter table statement generation that updates the changed Index and Foreign Key definitions.
1842
     *
1843
     * @return string[]
1844 6377
     */
1845
    protected function _getAlterTableIndexForeignKeySQL(TableDiff $diff) : array
1846 6377
    {
1847 300
        return array_merge($this->getPreAlterTableIndexForeignKeySQL($diff), $this->getPostAlterTableIndexForeignKeySQL($diff));
1848
    }
1849 300
1850
    /**
1851
     * Gets declaration of a number of fields in bulk.
1852 6377
     *
1853
     * @param mixed[][] $fields A multidimensional associative array.
1854
     *                          The first dimension determines the field name, while the second
1855
     *                          dimension is keyed with the name of the properties
1856
     *                          of the field being declared as array indexes. Currently, the types
1857
     *                          of supported field properties are as follows:
1858
     *
1859
     *      length
1860
     *          Integer value that determines the maximum length of the text
1861
     *          field. If this argument is missing the field should be
1862 6293
     *          declared to have the longest length allowed by the DBMS.
1863
     *
1864 6293
     *      default
1865
     *          Text value to be used as default for this field.
1866 6293
     *
1867
     *      notnull
1868
     *          Boolean flag that indicates whether this field is constrained
1869
     *          to not be set to null.
1870
     *      charset
1871
     *          Text value with the default CHARACTER SET for this field.
1872
     *      collation
1873
     *          Text value with the default COLLATION for this field.
1874
     *      unique
1875
     *          unique constraint
1876
     */
1877 1499
    public function getColumnDeclarationListSQL(array $fields) : string
1878
    {
1879 1499
        $queryFields = [];
1880 23
1881
        foreach ($fields as $fieldName => $field) {
1882
            $queryFields[] = $this->getColumnDeclarationSQL($fieldName, $field);
1883 1499
        }
1884
1885
        return implode(', ', $queryFields);
1886
    }
1887
1888
    /**
1889
     * Obtains DBMS specific SQL code portion needed to declare a generic type
1890
     * field to be used in statements like CREATE TABLE.
1891
     *
1892
     * @param string  $name  The name the field to be declared.
1893
     * @param mixed[] $field An associative array with the name of the properties
1894
     *                       of the field being declared as array indexes. Currently, the types
1895
     *                       of supported field properties are as follows:
1896
     *
1897
     *      length
1898
     *          Integer value that determines the maximum length of the text
1899
     *          field. If this argument is missing the field should be
1900
     *          declared to have the longest length allowed by the DBMS.
1901
     *
1902
     *      default
1903
     *          Text value to be used as default for this field.
1904
     *
1905 1591
     *      notnull
1906
     *          Boolean flag that indicates whether this field is constrained
1907 1591
     *          to not be set to null.
1908 1288
     *      charset
1909
     *          Text value with the default CHARACTER SET for this field.
1910
     *      collation
1911 303
     *          Text value with the default COLLATION for this field.
1912 27
     *      unique
1913
     *          unique constraint
1914
     *      check
1915 276
     *          column check constraint
1916 276
     *      columnDefinition
1917
     *          a string that defines the complete column
1918 276
     *
1919
     * @return string DBMS specific SQL code portion that should be used to declare the column.
1920 276
     */
1921
    public function getColumnDeclarationSQL(string $name, array $field) : string
1922
    {
1923
        if (isset($field['columnDefinition'])) {
1924
            $columnDef = $this->getCustomTypeDeclarationSQL($field);
1925
        } else {
1926
            $default = $this->getDefaultValueDeclarationSQL($field);
1927
1928 1179
            $charset = isset($field['charset']) && $field['charset'] ?
1929
                ' ' . $this->getColumnCharsetDeclarationSQL($field['charset']) : '';
1930 1179
1931 874
            $collation = isset($field['collation']) && $field['collation'] ?
1932
                ' ' . $this->getColumnCollationDeclarationSQL($field['collation']) : '';
1933
1934 305
            $notnull = isset($field['notnull']) && $field['notnull'] ? ' NOT NULL' : '';
1935 29
1936
            $unique = isset($field['unique']) && $field['unique'] ?
1937
                ' ' . $this->getUniqueFieldDeclarationSQL() : '';
1938 276
1939 276
            $check = isset($field['check']) && $field['check'] ?
1940
                ' ' . $field['check'] : '';
1941 276
1942
            $typeDecl  = $field['type']->getSQLDeclaration($field, $this);
1943 276
            $columnDef = $typeDecl . $charset . $default . $notnull . $unique . $check . $collation;
1944
1945
            if ($this->supportsInlineColumnComments() && isset($field['comment']) && $field['comment'] !== '') {
1946
                $columnDef .= ' ' . $this->getInlineColumnCommentSQL($field['comment']);
1947
            }
1948
        }
1949
1950
        return $name . ' ' . $columnDef;
1951 3165
    }
1952
1953 3165
    /**
1954 2576
     * Returns the SQL snippet that declares a floating point column of arbitrary precision.
1955
     *
1956
     * @param mixed[] $columnDef
1957 589
     */
1958 313
    public function getDecimalTypeDeclarationSQL(array $columnDef) : string
1959
    {
1960
        $columnDef['precision'] = ! isset($columnDef['precision']) || empty($columnDef['precision'])
1961 276
            ? 10 : $columnDef['precision'];
1962 276
        $columnDef['scale']     = ! isset($columnDef['scale']) || empty($columnDef['scale'])
1963
            ? 0 : $columnDef['scale'];
1964 276
1965
        return 'NUMERIC(' . $columnDef['precision'] . ', ' . $columnDef['scale'] . ')';
1966 276
    }
1967
1968
    /**
1969
     * Obtains DBMS specific SQL code portion needed to set a default value
1970
     * declaration to be used in statements like CREATE TABLE.
1971
     *
1972
     * @param mixed[] $field The field definition array.
1973
     *
1974
     * @return string DBMS specific SQL code portion needed to set a default value.
1975 1199
     */
1976
    public function getDefaultValueDeclarationSQL(array $field) : string
1977 1199
    {
1978 897
        if (! isset($field['default'])) {
1979
            return empty($field['notnull']) ? ' DEFAULT NULL' : '';
1980
        }
1981 302
1982 26
        $default = $field['default'];
1983
1984
        if (! isset($field['type'])) {
1985 276
            return " DEFAULT '" . $default . "'";
1986 276
        }
1987
1988 276
        $type = $field['type'];
1989
1990 276
        if ($type instanceof Types\PhpIntegerMappingType) {
1991
            return ' DEFAULT ' . $default;
1992
        }
1993
1994
        if ($type instanceof Types\PhpDateTimeMappingType && $default === $this->getCurrentTimestampSQL()) {
1995
            return ' DEFAULT ' . $this->getCurrentTimestampSQL();
1996
        }
1997
1998 6129
        if ($type instanceof Types\TimeType && $default === $this->getCurrentTimeSQL()) {
1999
            return ' DEFAULT ' . $this->getCurrentTimeSQL();
2000 6129
        }
2001 5428
2002
        if ($type instanceof Types\DateType && $default === $this->getCurrentDateSQL()) {
2003
            return ' DEFAULT ' . $this->getCurrentDateSQL();
2004 701
        }
2005 425
2006
        if ($type instanceof Types\BooleanType) {
2007
            return " DEFAULT '" . $this->convertBooleans($default) . "'";
0 ignored issues
show
Bug introduced by
Are you sure $this->convertBooleans($default) of type array|integer|mixed can be used in concatenation? ( Ignorable by Annotation )

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

2007
            return " DEFAULT '" . /** @scrutinizer ignore-type */ $this->convertBooleans($default) . "'";
Loading history...
2008 276
        }
2009 276
2010
        return " DEFAULT '" . $default . "'";
2011 276
    }
2012
2013 276
    /**
2014
     * Obtains DBMS specific SQL code portion needed to set a CHECK constraint
2015
     * declaration to be used in statements like CREATE TABLE.
2016
     *
2017
     * @param string[]|mixed[][] $definition The check definition.
2018
     *
2019 5811
     * @return string DBMS specific SQL code portion needed to set a CHECK constraint.
2020
     */
2021 5811
    public function getCheckDeclarationSQL(array $definition) : string
2022
    {
2023 5811
        $constraints = [];
2024 5811
        foreach ($definition as $field => $def) {
2025 5811
            if (is_string($def)) {
2026 413
                $constraints[] = 'CHECK (' . $def . ')';
2027
            } else {
2028 5811
                if (isset($def['min'])) {
2029 322
                    $constraints[] = 'CHECK (' . $field . ' >= ' . $def['min'] . ')';
2030
                }
2031
2032
                if (! isset($def['max'])) {
2033 5811
                    continue;
2034 253
                }
2035
2036 5811
                $constraints[] = 'CHECK (' . $field . ' <= ' . $def['max'] . ')';
2037 412
            }
2038
        }
2039
2040 5811
        return implode(', ', $constraints);
2041
    }
2042
2043
    /**
2044
     * Obtains DBMS specific SQL code portion needed to set a unique
2045
     * constraint declaration to be used in statements like CREATE TABLE.
2046 5811
     *
2047
     * @param string|null      $name       The name of the unique constraint.
2048 5811
     * @param UniqueConstraint $constraint The unique constraint definition.
2049 509
     *
2050 5811
     * @return string DBMS specific SQL code portion needed to set a constraint.
2051
     *
2052 5811
     * @throws InvalidArgumentException
2053
     */
2054 5811
    public function getUniqueConstraintDeclarationSQL(?string $name, UniqueConstraint $constraint) : string
2055 5811
    {
2056 366
        $columns = $constraint->getColumns();
2057
        $name    = new Identifier($name);
2058
2059 5811
        if (count($columns) === 0) {
2060 322
            throw new InvalidArgumentException("Incomplete definition. 'columns' required.");
2061
        }
2062
2063
        $flags = ['UNIQUE'];
2064 5811
2065 91
        if ($constraint->hasFlag('clustered')) {
2066
            $flags[] = 'CLUSTERED';
2067
        }
2068 5811
2069 412
        $constraintName  = $name->getQuotedName($this);
2070
        $constraintName  = ! empty($constraintName) ? $constraintName . ' ' : '';
2071
        $columnListNames = $this->getIndexFieldDeclarationListSQL($columns);
2072 5811
2073 1309
        return sprintf('CONSTRAINT %s%s (%s)', $constraintName, implode(' ', $flags), $columnListNames);
2074 1309
    }
2075 1309
2076 1309
    /**
2077
     * Obtains DBMS specific SQL code portion needed to set an index
2078
     * declaration to be used in statements like CREATE TABLE.
2079
     *
2080 5811
     * @param string $name  The name of the index.
2081
     * @param Index  $index The index definition.
2082
     *
2083
     * @return string DBMS specific SQL code portion needed to set an index.
2084
     *
2085
     * @throws InvalidArgumentException
2086
     */
2087
    public function getIndexDeclarationSQL(string $name, Index $index) : string
2088
    {
2089
        $columns = $index->getColumns();
2090
        $name    = new Identifier($name);
2091
2092 250
        if (count($columns) === 0) {
2093
            throw new InvalidArgumentException("Incomplete definition. 'columns' required.");
2094
        }
2095 250
2096 250
        return $this->getCreateIndexSQLFlags($index) . 'INDEX ' . $name->getQuotedName($this) . ' ('
2097
            . $this->getIndexFieldDeclarationListSQL($index)
2098
            . ')' . $this->getPartialIndexSQL($index);
2099
    }
2100
2101
    /**
2102
     * Obtains SQL code portion needed to create a custom column,
2103
     * e.g. when a field has the "columnDefinition" keyword.
2104
     * Only "AUTOINCREMENT" and "PRIMARY KEY" are added if appropriate.
2105
     *
2106
     * @param mixed[] $columnDef
2107
     */
2108
    public function getCustomTypeDeclarationSQL(array $columnDef) : string
2109
    {
2110
        return $columnDef['columnDefinition'];
2111
    }
2112
2113
    /**
2114
     * Obtains DBMS specific SQL code portion needed to set an index
2115
     * declaration to be used in statements like CREATE TABLE.
2116
     *
2117
     * @param mixed[]|Index $columnsOrIndex array declaration is deprecated, prefer passing Index to this method
2118
     */
2119
    public function getIndexFieldDeclarationListSQL($columnsOrIndex) : string
2120
    {
2121
        if ($columnsOrIndex instanceof Index) {
2122
            return implode(', ', $columnsOrIndex->getQuotedColumns($this));
2123
        }
2124
2125
        if (! is_array($columnsOrIndex)) {
0 ignored issues
show
introduced by
The condition is_array($columnsOrIndex) is always true.
Loading history...
2126
            throw new InvalidArgumentException('Fields argument should be an Index or array.');
2127
        }
2128
2129
        $ret = [];
2130
2131
        foreach ($columnsOrIndex as $column => $definition) {
2132
            if (is_array($definition)) {
2133
                $ret[] = $column;
2134
            } else {
2135
                $ret[] = $definition;
2136
            }
2137
        }
2138
2139 7798
        return implode(', ', $ret);
2140
    }
2141 7798
2142
    /**
2143 7798
     * Returns the required SQL string that fits between CREATE ... TABLE
2144 7798
     * to create the table as a temporary table.
2145
     *
2146
     * Should be overridden in driver classes to return the correct string for the
2147 7798
     * specific database type.
2148
     *
2149
     * The default is to return the string "TEMPORARY" - this will result in a
2150
     * SQL error for any database that does not support temporary tables, or that
2151
     * requires a different SQL command from "CREATE TEMPORARY TABLE".
2152
     *
2153
     * @return string The string required to be placed between "CREATE" and "TABLE"
2154
     *                to generate a temporary table, if possible.
2155
     */
2156
    public function getTemporaryTableSQL() : string
2157
    {
2158
        return 'TEMPORARY';
2159
    }
2160
2161
    /**
2162
     * Some vendors require temporary table names to be qualified specially.
2163
     */
2164
    public function getTemporaryTableName(string $tableName) : string
2165
    {
2166
        return $tableName;
2167
    }
2168
2169
    /**
2170
     * Obtain DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
2171
     * of a field declaration to be used in statements like CREATE TABLE.
2172
     *
2173
     * @return string DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
2174
     *                of a field declaration.
2175
     */
2176
    public function getForeignKeyDeclarationSQL(ForeignKeyConstraint $foreignKey) : string
2177
    {
2178
        $sql  = $this->getForeignKeyBaseDeclarationSQL($foreignKey);
2179
        $sql .= $this->getAdvancedForeignKeyOptionsSQL($foreignKey);
2180
2181
        return $sql;
2182
    }
2183 8106
2184
    /**
2185 8106
     * Returns the FOREIGN KEY query section dealing with non-standard options
2186 220
     * as MATCH, INITIALLY DEFERRED, ON UPDATE, ...
2187
     *
2188 7899
     * @param ForeignKeyConstraint $foreignKey The foreign key definition.
2189
     */
2190 7899
    public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey) : string
2191 7899
    {
2192
        $query = '';
2193 7899
        if ($this->supportsForeignKeyOnUpdate() && $foreignKey->hasOption('onUpdate')) {
2194 7899
            $query .= ' ON UPDATE ' . $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onUpdate'));
2195
        }
2196 7899
        if ($foreignKey->hasOption('onDelete')) {
2197
            $query .= ' ON DELETE ' . $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onDelete'));
2198 7899
        }
2199 7899
2200
        return $query;
2201 7899
    }
2202 7899
2203
    /**
2204 7899
     * Returns the given referential action in uppercase if valid, otherwise throws an exception.
2205 7899
     *
2206
     * @param string $action The foreign key referential action.
2207 7899
     *
2208 919
     * @throws InvalidArgumentException If unknown referential action given.
2209
     */
2210
    public function getForeignKeyReferentialActionSQL(string $action) : string
2211
    {
2212 8106
        $upper = strtoupper($action);
2213
        switch ($upper) {
2214
            case 'CASCADE':
2215
            case 'SET NULL':
2216
            case 'NO ACTION':
2217
            case 'RESTRICT':
2218
            case 'SET DEFAULT':
2219
                return $upper;
2220
            default:
2221
                throw new InvalidArgumentException('Invalid foreign key action: ' . $upper);
2222 2217
        }
2223
    }
2224 2217
2225 2217
    /**
2226 2217
     * Obtains DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
2227 2217
     * of a field declaration to be used in statements like CREATE TABLE.
2228
     *
2229 2217
     * @throws InvalidArgumentException
2230
     */
2231
    public function getForeignKeyBaseDeclarationSQL(ForeignKeyConstraint $foreignKey) : string
2232
    {
2233
        $sql = '';
2234
        if ($foreignKey->getName() !== null) {
0 ignored issues
show
introduced by
The condition $foreignKey->getName() !== null is always true.
Loading history...
2235
            $sql .= 'CONSTRAINT ' . $foreignKey->getQuotedName($this) . ' ';
2236
        }
2237
        $sql .= 'FOREIGN KEY (';
2238
2239
        if (count($foreignKey->getLocalColumns()) === 0) {
2240 8990
            throw new InvalidArgumentException("Incomplete definition. 'local' required.");
2241
        }
2242 8990
        if (count($foreignKey->getForeignColumns()) === 0) {
2243 7878
            throw new InvalidArgumentException("Incomplete definition. 'foreign' required.");
2244
        }
2245
        if (strlen($foreignKey->getForeignTableName()) === 0) {
2246 1536
            throw new InvalidArgumentException("Incomplete definition. 'foreignTable' required.");
2247
        }
2248 1536
2249
        return $sql . implode(', ', $foreignKey->getQuotedLocalColumns($this))
2250
            . ') REFERENCES '
2251
            . $foreignKey->getQuotedForeignTableName($this) . ' ('
2252 1536
            . implode(', ', $foreignKey->getQuotedForeignColumns($this)) . ')';
2253
    }
2254 1536
2255 479
    /**
2256
     * Obtains DBMS specific SQL code portion needed to set the UNIQUE constraint
2257
     * of a field declaration to be used in statements like CREATE TABLE.
2258 1117
     *
2259 258
     * @return string DBMS specific SQL code portion needed to set the UNIQUE constraint
2260
     *                of a field declaration.
2261
     */
2262 875
    public function getUniqueFieldDeclarationSQL() : string
2263 4
    {
2264
        return 'UNIQUE';
2265
    }
2266 875
2267 234
    /**
2268
     * Obtains DBMS specific SQL code portion needed to set the CHARACTER SET
2269
     * of a field declaration to be used in statements like CREATE TABLE.
2270 641
     *
2271 236
     * @param string $charset The name of the charset.
2272
     *
2273
     * @return string DBMS specific SQL code portion needed to set the CHARACTER SET
2274 635
     *                of a field declaration.
2275
     */
2276
    public function getColumnCharsetDeclarationSQL(string $charset) : string
0 ignored issues
show
Unused Code introduced by
The parameter $charset 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

2276
    public function getColumnCharsetDeclarationSQL(/** @scrutinizer ignore-unused */ string $charset) : string

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...
2277
    {
2278
        return '';
2279
    }
2280
2281
    /**
2282
     * Obtains DBMS specific SQL code portion needed to set the COLLATION
2283
     * of a field declaration to be used in statements like CREATE TABLE.
2284
     *
2285 2139
     * @param string $collation The name of the collation.
2286
     *
2287 2139
     * @return string DBMS specific SQL code portion needed to set the COLLATION
2288 2139
     *                of a field declaration.
2289 2139
     */
2290
    public function getColumnCollationDeclarationSQL(string $collation) : string
2291
    {
2292 2139
        return $this->supportsColumnCollation() ? 'COLLATE ' . $collation : '';
2293 46
    }
2294
2295
    /**
2296 2139
     * Whether the platform prefers sequences for ID generation.
2297 2139
     * Subclasses should override this method to return TRUE if they prefer sequences.
2298
     */
2299
    public function prefersSequences() : bool
2300
    {
2301
        return false;
2302 2139
    }
2303
2304
    /**
2305
     * Whether the platform prefers identity columns (eg. autoincrement) for ID generation.
2306
     * Subclasses should override this method to return TRUE if they prefer identity columns.
2307
     */
2308
    public function prefersIdentityColumns() : bool
2309
    {
2310
        return false;
2311
    }
2312
2313
    /**
2314
     * Some platforms need the boolean values to be converted.
2315
     *
2316 598
     * The default conversion in this implementation converts to integers (false => 0, true => 1).
2317
     *
2318 598
     * Note: if the input is not a boolean the original input might be returned.
2319 598
     *
2320
     * There are two contexts when converting booleans: Literals and Prepared Statements.
2321 598
     * This method should handle the literal case
2322 23
     *
2323
     * @param mixed $item A boolean or an array of them.
2324
     *
2325 575
     * @return mixed A boolean database value or an array of them.
2326
     */
2327 575
    public function convertBooleans($item)
2328 23
    {
2329
        if (is_array($item)) {
2330
            foreach ($item as $k => $value) {
2331 575
                if (! is_bool($value)) {
2332 575
                    continue;
2333 575
                }
2334
2335 575
                $item[$k] = (int) $value;
2336
            }
2337
        } elseif (is_bool($item)) {
2338
            $item = (int) $item;
2339
        }
2340
2341
        return $item;
2342
    }
2343
2344
    /**
2345
     * Some platforms have boolean literals that needs to be correctly converted
2346
     *
2347
     * The default conversion tries to convert value into bool "(bool)$item"
2348
     *
2349 1124
     * @param mixed $item
2350
     */
2351 1124
    public function convertFromBoolean($item) : ?bool
2352 1124
    {
2353
        return $item === null ? null: (bool) $item;
2354 1124
    }
2355
2356
    /**
2357
     * This method should handle the prepared statements case. When there is no
2358 1124
     * distinction, it's OK to use the same method.
2359 1124
     *
2360 1124
     * Note: if the input is not a boolean the original input might be returned.
2361
     *
2362
     * @param mixed $item A boolean or an array of them.
2363
     *
2364
     * @return mixed A boolean database value or an array of them.
2365
     */
2366
    public function convertBooleansToDatabaseValue($item)
2367
    {
2368
        return $this->convertBooleans($item);
2369
    }
2370
2371
    /**
2372 289
     * Returns the SQL specific for the platform to get the current date.
2373
     */
2374 289
    public function getCurrentDateSQL() : string
2375
    {
2376
        return 'CURRENT_DATE';
2377
    }
2378
2379
    /**
2380
     * Returns the SQL specific for the platform to get the current time.
2381
     */
2382
    public function getCurrentTimeSQL() : string
2383 4142
    {
2384
        return 'CURRENT_TIME';
2385 4142
    }
2386 3636
2387
    /**
2388
     * Returns the SQL specific for the platform to get the current timestamp
2389 805
     */
2390
    public function getCurrentTimestampSQL() : string
2391
    {
2392
        return 'CURRENT_TIMESTAMP';
2393 805
    }
2394
2395 805
    /**
2396 805
     * Returns the SQL for a given transaction isolation level Connection constant.
2397
     *
2398
     * @throws InvalidArgumentException
2399 805
     */
2400
    protected function _getTransactionIsolationLevelSQL(int $level) : string
2401
    {
2402
        switch ($level) {
2403 805
            case TransactionIsolationLevel::READ_UNCOMMITTED:
2404
                return 'READ UNCOMMITTED';
2405
            case TransactionIsolationLevel::READ_COMMITTED:
2406
                return 'READ COMMITTED';
2407
            case TransactionIsolationLevel::REPEATABLE_READ:
2408
                return 'REPEATABLE READ';
2409
            case TransactionIsolationLevel::SERIALIZABLE:
2410
                return 'SERIALIZABLE';
2411
            default:
2412
                throw new InvalidArgumentException('Invalid isolation level:' . $level);
2413
        }
2414
    }
2415
2416
    /**
2417
     * @throws DBALException If not supported on this platform.
2418
     */
2419
    public function getListDatabasesSQL() : string
2420
    {
2421
        throw DBALException::notSupported(__METHOD__);
2422
    }
2423
2424
    /**
2425
     * Returns the SQL statement for retrieving the namespaces defined in the database.
2426
     *
2427
     * @throws DBALException If not supported on this platform.
2428
     */
2429
    public function getListNamespacesSQL() : string
2430
    {
2431
        throw DBALException::notSupported(__METHOD__);
2432 36
    }
2433
2434 36
    /**
2435
     * @throws DBALException If not supported on this platform.
2436
     */
2437
    public function getListSequencesSQL(?string $database) : string
2438
    {
2439
        throw DBALException::notSupported(__METHOD__);
2440
    }
2441
2442
    /**
2443
     * @throws DBALException If not supported on this platform.
2444 1822
     */
2445
    public function getListTableConstraintsSQL(string $table) : string
2446 1822
    {
2447 1753
        throw DBALException::notSupported(__METHOD__);
2448
    }
2449 1753
2450
    /**
2451
     * @throws DBALException If not supported on this platform.
2452
     */
2453
    public function getListTableColumnsSQL(string $table, ?string $database = null) : string
2454
    {
2455
        throw DBALException::notSupported(__METHOD__);
2456
    }
2457
2458
    /**
2459
     * @throws DBALException If not supported on this platform.
2460 1649
     */
2461
    public function getListTablesSQL() : string
2462 1649
    {
2463 1649
        throw DBALException::notSupported(__METHOD__);
2464 23
    }
2465
2466 1649
    /**
2467 113
     * @throws DBALException If not supported on this platform.
2468
     */
2469
    public function getListUsersSQL() : string
2470 1649
    {
2471
        throw DBALException::notSupported(__METHOD__);
2472
    }
2473
2474
    /**
2475
     * Returns the SQL to list all views of a database or user.
2476
     *
2477
     * @throws DBALException If not supported on this platform.
2478
     */
2479
    public function getListViewsSQL(?string $database) : string
2480
    {
2481
        throw DBALException::notSupported(__METHOD__);
2482 1815
    }
2483
2484 1815
    /**
2485 1815
     * Returns the list of indexes for the current database.
2486 1815
     *
2487 1225
     * The current database parameter is optional but will always be passed
2488 949
     * when using the SchemaManager API and is the database the given table is in.
2489 719
     *
2490 512
     * Attention: Some platforms only support currentDatabase when they
2491 1562
     * are connected with that database. Cross-database information schema
2492
     * requests may be impossible.
2493 253
     *
2494
     * @throws DBALException If not supported on this platform.
2495
     */
2496
    public function getListTableIndexesSQL(string $table, ?string $currentDatabase = null) : string
0 ignored issues
show
Unused Code introduced by
The parameter $currentDatabase 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

2496
    public function getListTableIndexesSQL(string $table, /** @scrutinizer ignore-unused */ ?string $currentDatabase = null) : string

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...
2497
    {
2498
        throw DBALException::notSupported(__METHOD__);
2499
    }
2500
2501
    /**
2502
     * @throws DBALException If not supported on this platform.
2503
     */
2504
    public function getListTableForeignKeysSQL(string $table, ?string $database = null) : string
2505 1592
    {
2506
        throw DBALException::notSupported(__METHOD__);
2507 1592
    }
2508 1592
2509 1339
    /**
2510
     * @throws DBALException If not supported on this platform.
2511 1592
     */
2512
    public function getCreateViewSQL(string $name, string $sql) : string
2513 1592
    {
2514
        throw DBALException::notSupported(__METHOD__);
2515
    }
2516 1592
2517
    /**
2518
     * @throws DBALException If not supported on this platform.
2519 1592
     */
2520
    public function getDropViewSQL(string $name) : string
2521
    {
2522
        throw DBALException::notSupported(__METHOD__);
2523 1592
    }
2524 1592
2525 1592
    /**
2526 1592
     * Returns the SQL snippet to drop an existing sequence.
2527
     *
2528
     * @param Sequence|string $sequence
2529
     *
2530
     * @throws DBALException If not supported on this platform.
2531
     */
2532
    public function getDropSequenceSQL($sequence) : string
0 ignored issues
show
Unused Code introduced by
The parameter $sequence 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

2532
    public function getDropSequenceSQL(/** @scrutinizer ignore-unused */ $sequence) : string

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...
2533
    {
2534
        throw DBALException::notSupported(__METHOD__);
2535
    }
2536
2537
    /**
2538
     * @throws DBALException If not supported on this platform.
2539
     */
2540
    public function getSequenceNextValSQL(string $sequenceName) : string
0 ignored issues
show
Unused Code introduced by
The parameter $sequenceName 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

2540
    public function getSequenceNextValSQL(/** @scrutinizer ignore-unused */ string $sequenceName) : string

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...
2541
    {
2542
        throw DBALException::notSupported(__METHOD__);
2543
    }
2544
2545
    /**
2546
     * Returns the SQL to create a new database.
2547
     *
2548
     * @param string $database The name of the database that should be created.
2549
     *
2550
     * @throws DBALException If not supported on this platform.
2551
     */
2552
    public function getCreateDatabaseSQL(string $database) : string
2553
    {
2554
        throw DBALException::notSupported(__METHOD__);
2555
    }
2556
2557
    /**
2558
     * Returns the SQL to set the transaction isolation level.
2559
     *
2560
     * @throws DBALException If not supported on this platform.
2561
     */
2562
    public function getSetTransactionIsolationSQL(int $level) : string
2563
    {
2564 291
        throw DBALException::notSupported(__METHOD__);
2565
    }
2566 291
2567
    /**
2568
     * Obtains DBMS specific SQL to be used to create datetime fields in
2569
     * statements like CREATE TABLE.
2570
     *
2571
     * @param mixed[] $fieldDeclaration
2572
     *
2573
     * @throws DBALException If not supported on this platform.
2574
     */
2575 46
    public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) : string
2576
    {
2577 46
        throw DBALException::notSupported(__METHOD__);
2578
    }
2579
2580
    /**
2581
     * Obtains DBMS specific SQL to be used to create datetime with timezone offset fields.
2582
     *
2583
     * @param mixed[] $fieldDeclaration
2584
     */
2585
    public function getDateTimeTzTypeDeclarationSQL(array $fieldDeclaration) : string
2586 92
    {
2587
        return $this->getDateTimeTypeDeclarationSQL($fieldDeclaration);
2588 92
    }
2589
2590
    /**
2591
     * Obtains DBMS specific SQL to be used to create date fields in statements
2592
     * like CREATE TABLE.
2593
     *
2594
     * @param mixed[] $fieldDeclaration
2595
     *
2596
     * @throws DBALException If not supported on this platform.
2597
     */
2598
    public function getDateTypeDeclarationSQL(array $fieldDeclaration) : string
2599
    {
2600
        throw DBALException::notSupported(__METHOD__);
2601
    }
2602
2603
    /**
2604
     * Obtains DBMS specific SQL to be used to create time fields in statements
2605 352
     * like CREATE TABLE.
2606
     *
2607 352
     * @param mixed[] $fieldDeclaration
2608
     *
2609
     * @throws DBALException If not supported on this platform.
2610
     */
2611
    public function getTimeTypeDeclarationSQL(array $fieldDeclaration) : string
2612
    {
2613
        throw DBALException::notSupported(__METHOD__);
2614
    }
2615 352
2616 329
    /**
2617
     * @param mixed[] $fieldDeclaration
2618
     */
2619 352
    public function getFloatDeclarationSQL(array $fieldDeclaration) : string
2620
    {
2621
        return 'DOUBLE PRECISION';
2622
    }
2623
2624
    /**
2625
     * Gets the default transaction isolation level of the platform.
2626
     *
2627
     * @see TransactionIsolationLevel
2628
     *
2629
     * @return int The default isolation level.
2630
     */
2631 713
    public function getDefaultTransactionIsolationLevel() : int
2632
    {
2633 713
        return TransactionIsolationLevel::READ_COMMITTED;
2634
    }
2635
2636
    /* supports*() methods */
2637
2638
    /**
2639
     * Whether the platform supports sequences.
2640
     */
2641
    public function supportsSequences() : bool
2642
    {
2643
        return false;
2644
    }
2645
2646 126
    /**
2647
     * Whether the platform supports identity columns.
2648 126
     *
2649
     * Identity columns are columns that receive an auto-generated value from the
2650
     * database on insert of a row.
2651
     */
2652
    public function supportsIdentityColumns() : bool
2653
    {
2654
        return false;
2655
    }
2656 231
2657
    /**
2658 231
     * Whether the platform emulates identity columns through sequences.
2659
     *
2660
     * Some platforms that do not support identity columns natively
2661
     * but support sequences can emulate identity columns by using
2662
     * sequences.
2663
     */
2664
    public function usesSequenceEmulatedIdentityColumns() : bool
2665
    {
2666 12
        return false;
2667
    }
2668 12
2669
    /**
2670
     * Gets the sequence name prefix based on table information.
2671
     */
2672
    public function getSequencePrefix(string $tableName, ?string $schemaName = null) : string
2673
    {
2674
        if (! $schemaName) {
2675
            return $tableName;
2676 314
        }
2677
2678 314
        // Prepend the schema name to the table name if there is one
2679
        return ! $this->supportsSchemas() && $this->canEmulateSchemas()
2680
            ? $schemaName . '__' . $tableName
2681
            : $schemaName . '.' . $tableName;
2682
    }
2683
2684
    /**
2685
     * Returns the name of the sequence for a particular identity column in a particular table.
2686
     *
2687
     * @see    usesSequenceEmulatedIdentityColumns
2688
     *
2689
     * @param string $tableName  The name of the table to return the sequence name for.
2690 184
     * @param string $columnName The name of the identity column in the table to return the sequence name for.
2691
     *
2692 184
     * @throws DBALException If not supported on this platform.
2693
     */
2694 184
    public function getIdentitySequenceName(string $tableName, string $columnName) : string
2695
    {
2696 184
        throw DBALException::notSupported(__METHOD__);
2697
    }
2698 184
2699
    /**
2700 184
     * Whether the platform supports indexes.
2701
     */
2702
    public function supportsIndexes() : bool
2703
    {
2704
        return true;
2705
    }
2706
2707
    /**
2708
     * Whether the platform supports partial indexes.
2709
     */
2710
    public function supportsPartialIndexes() : bool
2711 1
    {
2712
        return false;
2713 1
    }
2714
2715
    /**
2716
     * Whether the platform supports indexes with column length definitions.
2717
     */
2718
    public function supportsColumnLengthIndexes() : bool
2719
    {
2720
        return false;
2721
    }
2722
2723
    /**
2724
     * Whether the platform supports altering tables.
2725
     */
2726
    public function supportsAlterTable() : bool
2727
    {
2728
        return true;
2729
    }
2730
2731
    /**
2732
     * Whether the platform supports transactions.
2733
     */
2734
    public function supportsTransactions() : bool
2735
    {
2736
        return true;
2737
    }
2738
2739
    /**
2740
     * Whether the platform supports savepoints.
2741
     */
2742
    public function supportsSavepoints() : bool
2743
    {
2744
        return true;
2745
    }
2746
2747
    /**
2748
     * Whether the platform supports releasing savepoints.
2749
     */
2750
    public function supportsReleaseSavepoints() : bool
2751
    {
2752
        return $this->supportsSavepoints();
2753
    }
2754
2755
    /**
2756
     * Whether the platform supports primary key constraints.
2757
     */
2758
    public function supportsPrimaryConstraints() : bool
2759
    {
2760
        return true;
2761
    }
2762
2763
    /**
2764
     * Whether the platform supports foreign key constraints.
2765
     */
2766
    public function supportsForeignKeyConstraints() : bool
2767
    {
2768
        return true;
2769
    }
2770
2771
    /**
2772
     * Whether this platform supports onUpdate in foreign key constraints.
2773
     */
2774
    public function supportsForeignKeyOnUpdate() : bool
2775
    {
2776
        return $this->supportsForeignKeyConstraints();
2777
    }
2778
2779
    /**
2780
     * Whether the platform supports database schemas.
2781
     */
2782
    public function supportsSchemas() : bool
2783
    {
2784
        return false;
2785
    }
2786
2787
    /**
2788
     * Whether this platform can emulate schemas.
2789
     *
2790
     * Platforms that either support or emulate schemas don't automatically
2791
     * filter a schema for the namespaced elements in {@link
2792
     * AbstractManager#createSchema}.
2793
     */
2794
    public function canEmulateSchemas() : bool
2795
    {
2796
        return false;
2797
    }
2798
2799
    /**
2800
     * Returns the default schema name.
2801
     *
2802
     * @throws DBALException If not supported on this platform.
2803
     */
2804
    public function getDefaultSchemaName() : string
2805
    {
2806
        throw DBALException::notSupported(__METHOD__);
2807
    }
2808
2809
    /**
2810
     * Whether this platform supports create database.
2811
     *
2812
     * Some databases don't allow to create and drop databases at all or only with certain tools.
2813
     */
2814
    public function supportsCreateDropDatabase() : bool
2815
    {
2816
        return true;
2817
    }
2818
2819
    /**
2820
     * Whether the platform supports getting the affected rows of a recent update/delete type query.
2821
     */
2822
    public function supportsGettingAffectedRows() : bool
2823
    {
2824
        return true;
2825
    }
2826
2827
    /**
2828
     * Whether this platform support to add inline column comments as postfix.
2829
     */
2830
    public function supportsInlineColumnComments() : bool
2831
    {
2832
        return false;
2833
    }
2834
2835
    /**
2836
     * Whether this platform support the proprietary syntax "COMMENT ON asset".
2837
     */
2838
    public function supportsCommentOnStatement() : bool
2839
    {
2840
        return false;
2841
    }
2842
2843
    /**
2844
     * Does this platform have native guid type.
2845
     */
2846
    public function hasNativeGuidType() : bool
2847
    {
2848
        return false;
2849
    }
2850
2851
    /**
2852
     * Does this platform have native JSON type.
2853
     */
2854
    public function hasNativeJsonType() : bool
2855
    {
2856
        return false;
2857
    }
2858
2859
    /**
2860
     * @deprecated
2861
     *
2862
     * @todo Remove in 3.0
2863
     */
2864
    public function getIdentityColumnNullInsertSQL() : string
2865
    {
2866
        return '';
2867
    }
2868
2869
    /**
2870
     * Whether this platform supports views.
2871
     */
2872
    public function supportsViews() : bool
2873
    {
2874
        return true;
2875
    }
2876
2877
    /**
2878
     * Does this platform support column collation?
2879
     */
2880
    public function supportsColumnCollation() : bool
2881
    {
2882
        return false;
2883
    }
2884
2885
    /**
2886
     * Gets the format string, as accepted by the date() function, that describes
2887
     * the format of a stored datetime value of this platform.
2888
     *
2889
     * @return string The format string.
2890
     */
2891
    public function getDateTimeFormatString() : string
2892
    {
2893 23
        return 'Y-m-d H:i:s';
2894
    }
2895 23
2896
    /**
2897
     * Gets the format string, as accepted by the date() function, that describes
2898
     * the format of a stored datetime with timezone value of this platform.
2899
     *
2900
     * @return string The format string.
2901
     */
2902
    public function getDateTimeTzFormatString() : string
2903
    {
2904
        return 'Y-m-d H:i:s';
2905
    }
2906
2907
    /**
2908
     * Gets the format string, as accepted by the date() function, that describes
2909
     * the format of a stored date value of this platform.
2910
     *
2911
     * @return string The format string.
2912
     */
2913
    public function getDateFormatString() : string
2914
    {
2915
        return 'Y-m-d';
2916
    }
2917
2918
    /**
2919
     * Gets the format string, as accepted by the date() function, that describes
2920
     * the format of a stored time value of this platform.
2921
     *
2922
     * @return string The format string.
2923
     */
2924
    public function getTimeFormatString() : string
2925
    {
2926
        return 'H:i:s';
2927
    }
2928
2929
    /**
2930
     * Adds an driver-specific LIMIT clause to the query.
2931
     *
2932
     * @throws DBALException
2933
     */
2934 210
    final public function modifyLimitQuery(string $query, ?int $limit, int $offset = 0) : string
2935
    {
2936 210
        if ($offset < 0) {
2937
            throw new DBALException(sprintf(
2938
                'Offset must be a positive integer or zero, %d given',
2939
                $offset
2940
            ));
2941
        }
2942
2943
        if ($offset > 0 && ! $this->supportsLimitOffset()) {
2944
            throw new DBALException(sprintf(
2945
                'Platform %s does not support offset values in limit queries.',
2946
                $this->getName()
2947
            ));
2948
        }
2949
2950
        return $this->doModifyLimitQuery($query, $limit, $offset);
2951
    }
2952
2953
    /**
2954
     * Adds an platform-specific LIMIT clause to the query.
2955
     */
2956
    protected function doModifyLimitQuery(string $query, ?int $limit, int $offset) : string
2957
    {
2958
        if ($limit !== null) {
2959
            $query .= sprintf(' LIMIT %d', $limit);
2960
        }
2961
2962
        if ($offset > 0) {
2963
            $query .= sprintf(' OFFSET %d', $offset);
2964
        }
2965
2966
        return $query;
2967
    }
2968
2969
    /**
2970
     * Whether the database platform support offsets in modify limit clauses.
2971
     */
2972
    public function supportsLimitOffset() : bool
2973
    {
2974
        return true;
2975 1413
    }
2976
2977 1413
    /**
2978
     * Gets the character casing of a column in an SQL result set of this platform.
2979
     *
2980
     * @param string $column The column name for which to get the correct character casing.
2981
     *
2982
     * @return string The column name in the character casing used in SQL result sets.
2983
     */
2984
    public function getSQLResultCasing(string $column) : string
2985
    {
2986
        return $column;
2987
    }
2988
2989
    /**
2990
     * Makes any fixes to a name of a schema element (table, sequence, ...) that are required
2991
     * by restrictions of the platform, like a maximum length.
2992
     */
2993
    public function fixSchemaElementName(string $schemaElementName) : string
2994
    {
2995
        return $schemaElementName;
2996
    }
2997
2998
    /**
2999 83
     * Maximum length of any given database identifier, like tables or column names.
3000
     */
3001 83
    public function getMaxIdentifierLength() : int
3002
    {
3003
        return 63;
3004
    }
3005
3006
    /**
3007
     * Returns the insert SQL for an empty insert statement.
3008
     */
3009
    public function getEmptyIdentityInsertSQL(string $tableName, string $identifierColumnName) : string
3010
    {
3011
        return 'INSERT INTO ' . $tableName . ' (' . $identifierColumnName . ') VALUES (null)';
3012 25
    }
3013
3014 25
    /**
3015
     * Generates a Truncate Table SQL statement for a given table.
3016
     *
3017
     * Cascade is not supported on many platforms but would optionally cascade the truncate by
3018
     * following the foreign keys.
3019
     */
3020
    public function getTruncateTableSQL(string $tableName, bool $cascade = false) : string
0 ignored issues
show
Unused Code introduced by
The parameter $cascade 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

3020
    public function getTruncateTableSQL(string $tableName, /** @scrutinizer ignore-unused */ bool $cascade = false) : string

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...
3021
    {
3022
        $tableIdentifier = new Identifier($tableName);
3023
3024
        return 'TRUNCATE ' . $tableIdentifier->getQuotedName($this);
3025
    }
3026 200
3027
    /**
3028 200
     * This is for test reasons, many vendors have special requirements for dummy statements.
3029
     */
3030
    public function getDummySelectSQL(string $expression = '1') : string
3031
    {
3032
        return sprintf('SELECT %s', $expression);
3033
    }
3034
3035
    /**
3036
     * Returns the SQL to create a new savepoint.
3037
     */
3038
    public function createSavePoint(string $savepoint) : string
3039
    {
3040
        return 'SAVEPOINT ' . $savepoint;
3041
    }
3042
3043
    /**
3044
     * Returns the SQL to release a savepoint.
3045
     */
3046
    public function releaseSavePoint(string $savepoint) : string
3047
    {
3048
        return 'RELEASE SAVEPOINT ' . $savepoint;
3049
    }
3050
3051
    /**
3052
     * Returns the SQL to rollback a savepoint.
3053
     */
3054
    public function rollbackSavePoint(string $savepoint) : string
3055
    {
3056
        return 'ROLLBACK TO SAVEPOINT ' . $savepoint;
3057
    }
3058
3059
    /**
3060
     * Returns the keyword list instance of this platform.
3061
     *
3062
     * @throws DBALException If no keyword list is specified.
3063 184
     */
3064
    final public function getReservedKeywordsList() : KeywordList
3065 184
    {
3066
        // Check for an existing instantiation of the keywords class.
3067
        if ($this->_keywords) {
3068
            return $this->_keywords;
3069
        }
3070
3071
        $class    = $this->getReservedKeywordsClass();
3072
        $keywords = new $class();
3073 23
        if (! $keywords instanceof KeywordList) {
3074
            throw DBALException::notSupported(__METHOD__);
3075 23
        }
3076
3077
        // Store the instance so it doesn't need to be generated on every request.
3078
        $this->_keywords = $keywords;
3079
3080
        return $keywords;
3081
    }
3082
3083 2493
    /**
3084
     * Returns the class name of the reserved keywords list.
3085 2493
     *
3086
     * @return string
3087
     *
3088
     * @throws DBALException If not supported on this platform.
3089
     */
3090
    protected function getReservedKeywordsClass()
3091 4272
    {
3092
        throw DBALException::notSupported(__METHOD__);
3093 4272
    }
3094
3095
    /**
3096
     * Quotes a literal string.
3097
     * This method is NOT meant to fix SQL injections!
3098
     * It is only meant to escape this platform's string literal
3099
     * quote character inside the given literal string.
3100
     *
3101 46
     * @param string $str The literal string to be quoted.
3102
     *
3103 46
     * @return string The quoted literal string.
3104
     */
3105
    public function quoteStringLiteral(string $str) : string
3106
    {
3107
        $c = $this->getStringLiteralQuoteCharacter();
3108
3109
        return $c . str_replace($c, $c . $c, $str) . $c;
3110
    }
3111 23
3112
    /**
3113 23
     * Gets the character used for string literal quoting.
3114
     */
3115
    public function getStringLiteralQuoteCharacter() : string
3116
    {
3117
        return "'";
3118
    }
3119
3120
    /**
3121 297
     * Escapes metacharacters in a string intended to be used with a LIKE
3122
     * operator.
3123 297
     *
3124
     * @param string $inputString a literal, unquoted string
3125
     * @param string $escapeChar  should be reused by the caller in the LIKE
3126
     *                            expression.
3127
     */
3128
    final public function escapeStringForLike(string $inputString, string $escapeChar) : string
3129
    {
3130
        return preg_replace(
3131 42
            '~([' . preg_quote($this->getLikeWildcardCharacters() . $escapeChar, '~') . '])~u',
3132
            addcslashes($escapeChar, '\\') . '$1',
3133 42
            $inputString
3134
        );
3135
    }
3136
3137
    protected function getLikeWildcardCharacters() : string
3138
    {
3139
        return '%_';
3140
    }
3141
}
3142