Failed Conditions
Pull Request — develop (#3348)
by Sergei
126:17 queued 61:12
created

AbstractPlatform   F

Complexity

Total Complexity 392

Size/Duplication

Total Lines 3099
Duplicated Lines 0 %

Test Coverage

Coverage 79.51%

Importance

Changes 0
Metric Value
wmc 392
eloc 664
dl 0
loc 3099
ccs 590
cts 742
cp 0.7951
rs 1.936
c 0
b 0
f 0

210 Methods

Rating   Name   Duplication   Size   Complexity  
A getSqlCommentStartString() 0 3 1
A getGuidTypeDeclarationSQL() 0 6 1
A getBinaryTypeDeclarationSQLSnippet() 0 3 1
A getAvgExpression() 0 3 1
A getDoctrineTypeComment() 0 3 1
A hasDoctrineTypeMappingFor() 0 9 2
A __construct() 0 2 1
A initializeAllDoctrineTypeMappings() 0 7 3
A getJsonTypeDeclarationSQL() 0 3 1
A getEventManager() 0 3 1
A getCharMaxLength() 0 3 1
A initializeCommentedDoctrineTypes() 0 12 3
A getVarcharMaxLength() 0 3 1
A getSqrtExpression() 0 3 1
A getBinaryDefaultLength() 0 3 1
A getSumExpression() 0 3 1
A registerDoctrineTypeMapping() 0 20 4
A markDoctrineTypeCommented() 0 9 3
A getSqlCommentEndString() 0 3 1
A getBinaryMaxLength() 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
A getMaxExpression() 0 3 1
A getLengthExpression() 0 3 1
A getVarcharDefaultLength() 0 3 1
A getIdentifierQuoteCharacter() 0 3 1
A isCommentedDoctrineType() 0 9 2
A getRegexpExpression() 0 3 1
A getDoctrineTypeMapping() 0 13 3
A getVarcharTypeDeclarationSQL() 0 17 4
A setEventManager() 0 3 1
A getRoundExpression() 0 3 1
A getBinaryTypeDeclarationSQL() 0 9 2
A getWildcards() 0 3 1
A getColumnComment() 0 9 2
A getLtrimExpression() 0 3 1
A getUpperExpression() 0 3 1
A getLowerExpression() 0 3 1
A getRtrimExpression() 0 3 1
B getTrimExpression() 0 40 7
A supportsAlterTable() 0 3 1
A getSequencePrefix() 0 10 4
A supportsForeignKeyOnUpdate() 0 3 1
A getCheckDeclarationSQL() 0 20 5
A getWriteLockSQL() 0 3 1
A getCreateSchemaSQL() 0 3 1
A getFloatDeclarationSQL() 0 3 1
A getDefaultSchemaName() 0 3 1
A supportsCreateDropDatabase() 0 3 1
A onSchemaAlterTableRenameColumn() 0 16 3
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 getCosExpression() 0 3 1
F getCreateTableSQL() 0 93 23
A supportsTransactions() 0 3 1
A getForUpdateSQL() 0 3 1
A getPreAlterTableIndexForeignKeySQL() 0 22 6
A hasNativeGuidType() 0 3 1
A getRenameIndexSQL() 0 5 1
A fixSchemaElementName() 0 3 1
A getDateTimeTypeDeclarationSQL() 0 3 1
A supportsCommentOnStatement() 0 3 1
A convertFromBoolean() 0 7 2
A getDateSubMonthExpression() 0 3 1
A _getAlterTableIndexForeignKeySQL() 0 3 1
A getTemporaryTableSQL() 0 3 1
A getDateAddYearsExpression() 0 3 1
A getIdentityColumnNullInsertSQL() 0 3 1
A getCreateIndexSQL() 0 20 4
A getDropSequenceSQL() 0 3 1
A getDefaultTransactionIsolationLevel() 0 3 1
A getBetweenExpression() 0 3 1
A getMaxIdentifierLength() 0 3 1
A getIsNullExpression() 0 3 1
A _getTransactionIsolationLevelSQL() 0 13 5
A getForeignKeyDeclarationSQL() 0 6 1
A prefersIdentityColumns() 0 3 1
A getDropForeignKeySQL() 0 14 3
A getListNamespacesSQL() 0 3 1
A appendLockHint() 0 3 1
A getUniqueConstraintDeclarationSQL() 0 23 4
A getDateDiffExpression() 0 3 1
A supportsViews() 0 3 1
A getCreatePrimaryKeySQL() 0 7 2
A canEmulateSchemas() 0 3 1
A getAlterTableSQL() 0 3 1
A getCreateViewSQL() 0 3 1
A getColumnCharsetDeclarationSQL() 0 3 1
A getDateFormatString() 0 3 1
A getListUsersSQL() 0 3 1
A getPiExpression() 0 3 1
A getListSequencesSQL() 0 3 1
A convertBooleans() 0 15 5
A createSavePoint() 0 3 1
A getCreateForeignKeySQL() 0 7 2
A convertBooleansToDatabaseValue() 0 3 1
A supportsIdentityColumns() 0 3 1
A getIdentitySequenceName() 0 3 1
A getReadLockSQL() 0 3 1
A getAcosExpression() 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
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 supportsSchemas() 0 3 1
A getSQLResultCasing() 0 3 1
A getDropViewSQL() 0 3 1
A getDateAddMonthExpression() 0 3 1
A getBitAndComparisonExpression() 0 3 1
A onSchemaAlterTableAddColumn() 0 16 3
B getDropTableSQL() 0 28 7
A getDateTimeFormatString() 0 3 1
A quoteSingleIdentifier() 0 5 1
A supportsSequences() 0 3 1
A getCurrentTimeSQL() 0 3 1
A getDateAddWeeksExpression() 0 3 1
A supportsReleaseSavepoints() 0 3 1
A getDummySelectSQL() 0 3 1
A getDropIndexSQL() 0 9 3
A supportsIndexes() 0 3 1
A getIndexFieldDeclarationListSQL() 0 21 5
A getDateSubMinutesExpression() 0 3 1
A getDateSubHourExpression() 0 3 1
A getDateTypeDeclarationSQL() 0 3 1
A getCreateIndexSQLFlags() 0 3 2
A getAdvancedForeignKeyOptionsSQL() 0 11 4
A supportsColumnCollation() 0 3 1
A getDecimalTypeDeclarationSQL() 0 8 5
A quoteStringLiteral() 0 5 1
A hasNativeJsonType() 0 3 1
A getTruncateTableSQL() 0 5 1
A getDateSubWeeksExpression() 0 3 1
A getLikeWildcardCharacters() 0 3 1
A usesSequenceEmulatedIdentityColumns() 0 3 1
A getSetTransactionIsolationSQL() 0 3 1
A getDateAddDaysExpression() 0 3 1
A getDateSubDaysExpression() 0 3 1
A getListTableForeignKeysSQL() 0 3 1
B getPostAlterTableIndexForeignKeySQL() 0 38 8
C _getCreateTableSQL() 0 37 12
A onSchemaAlterTable() 0 16 3
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 getBitOrComparisonExpression() 0 3 1
A getTimeTypeDeclarationSQL() 0 3 1
A supportsColumnLengthIndexes() 0 3 1
A getSinExpression() 0 3 1
A getTemporaryTableName() 0 3 1
A getForeignKeyBaseDeclarationSQL() 0 22 5
A getCreateTemporaryTableSnippetSQL() 0 3 1
A getNowExpression() 0 3 1
A getCreateSequenceSQL() 0 3 1
A getDateSubSecondsExpression() 0 3 1
A getDateTimeTzFormatString() 0 3 1
A supportsGettingAffectedRows() 0 3 1
A getCurrentTimestampSQL() 0 3 1
A releaseSavePoint() 0 3 1
A getIsNotNullExpression() 0 3 1
A getCreateConstraintSQL() 0 30 6
A rollbackSavePoint() 0 3 1
A getInlineColumnCommentSQL() 0 7 2
A getSubstringExpression() 0 7 2
A getListTableConstraintsSQL() 0 3 1
A getAlterSequenceSQL() 0 3 1
A getDateArithmeticIntervalExpression() 0 3 1
A getDateAddMinutesExpression() 0 3 1
A getIndexDeclarationSQL() 0 12 2
A getListTableIndexesSQL() 0 3 1
A convertNonNativeInterval() 0 7 2
A getConcatExpression() 0 3 1
A getLocateExpression() 0 3 1
A getListTablesSQL() 0 3 1
A getEmptyIdentityInsertSQL() 0 3 1
A supportsInlineColumnComments() 0 3 1
A quoteIdentifier() 0 9 2
A getDateAddQuartersExpression() 0 3 1
A getDropTemporaryTableSQL() 0 3 1
A doModifyLimitQuery() 0 11 3
A getPartialIndexSQL() 0 7 3
A getNotExpression() 0 3 1
A onSchemaAlterTableRemoveColumn() 0 16 3
A getDateSubYearsExpression() 0 3 1
A escapeStringForLike() 0 6 1
A getDropDatabaseSQL() 0 3 1
A getColumnDeclarationListSQL() 0 9 2
A onSchemaAlterTableChangeColumn() 0 16 3
A getDateAddSecondsExpression() 0 3 1
A supportsPrimaryConstraints() 0 3 1
A getDateAddHourExpression() 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
149
    public function __construct()
150 54007
    {
151
    }
152 54007
153
    /**
154
     * Sets the EventManager used by the Platform.
155
     */
156
    public function setEventManager(EventManager $eventManager) : void
157 51693
    {
158
        $this->_eventManager = $eventManager;
159 51693
    }
160 51693
161
    /**
162
     * Gets the EventManager used by the Platform.
163
     */
164
    public function getEventManager() : ?EventManager
165
    {
166
        return $this->_eventManager;
167 48959
    }
168
169 48959
    /**
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
     */
229
    public function getVarcharTypeDeclarationSQL(array $field) : string
230 48985
    {
231
        if (! isset($field['length'])) {
232 48985
            $field['length'] = $this->getVarcharDefaultLength();
233
        }
234 48985
235 48985
        $fixed = $field['fixed'] ?? false;
236 28427
237
        $maxLength = $fixed
238
            ? $this->getCharMaxLength()
239 48985
            : $this->getVarcharMaxLength();
240
241
        if ($field['length'] > $maxLength) {
242
            return $this->getClobTypeDeclarationSQL($field);
243
        }
244
245
        return $this->getVarcharTypeDeclarationSQLSnippet($field['length'], $fixed);
246
    }
247
248 50880
    /**
249
     * Returns the SQL snippet used to declare a BINARY/VARBINARY column type.
250 50880
     *
251 47537
     * @param mixed[] $field The column definition.
252
     */
253
    public function getBinaryTypeDeclarationSQL(array $field) : string
254 50880
    {
255
        if (! isset($field['length'])) {
256 50880
            $field['length'] = $this->getBinaryDefaultLength();
257 49087
        }
258 50880
259
        $fixed = $field['fixed'] ?? false;
260 50880
261
        return $this->getBinaryTypeDeclarationSQLSnippet($field['length'], $fixed);
262
    }
263
264 50880
    /**
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
    public function getGuidTypeDeclarationSQL(array $field) : string
273
    {
274 47724
        $field['length'] = 36;
275
        $field['fixed']  = true;
276 47724
277 47691
        return $this->getVarcharTypeDeclarationSQL($field);
278
    }
279
280 47724
    /**
281
     * Returns the SQL snippet to declare a JSON field.
282 47724
     *
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
    /**
294
     * @param int|null $length The length of the column.
295 47064
     * @param bool     $fixed  Whether the column length is fixed.
296
     *
297 47064
     * @throws DBALException If not supported on this platform.
298 47064
     */
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 47064
    {
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
     */
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 45261
    {
314
        throw DBALException::notSupported('BINARY/VARBINARY column types are not supported by this platform.');
315 45261
    }
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
            throw new DBALException('Unknown database type ' . $dbType . ' requested, ' . static::class . ' may not support it.');
378
        }
379 47037
380
        return $this->doctrineTypeMapping[$dbType];
381 47037
    }
382 46455
383
    /**
384
     * Checks if a database type is currently supported by this platform.
385 47037
     */
386 44697
    public function hasDoctrineTypeMappingFor(string $dbType) : bool
387
    {
388
        if ($this->doctrineTypeMapping === null) {
389 47025
            $this->initializeAllDoctrineTypeMappings();
390 47025
        }
391
392 47025
        $dbType = strtolower($dbType);
393
394 47025
        return isset($this->doctrineTypeMapping[$dbType]);
395 47013
    }
396
397
    /**
398 44673
     * Initializes the Doctrine Type comments instance variable for in_array() checks.
399 44673
     */
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
                continue;
409
            }
410 48996
411
            $this->doctrineTypeComments[] = $typeName;
412 48996
        }
413 48932
    }
414
415
    /**
416 48996
     * Is it necessary for the platform to add a parsable type comment to allow reverse engineering the given type?
417
     */
418 48996
    public function isCommentedDoctrineType(Type $doctrineType) : bool
419 44745
    {
420
        if ($this->doctrineTypeComments === null) {
421
            $this->initializeCommentedDoctrineTypes();
422 48984
        }
423
424
        assert(is_array($this->doctrineTypeComments));
425
426
        return in_array($doctrineType->getName(), $this->doctrineTypeComments);
427
    }
428
429
    /**
430
     * Marks this type as to be commented in ALTER TABLE and CREATE TABLE statements.
431
     *
432 46318
     * @param string|Type $doctrineType
433
     */
434 46318
    public function markDoctrineTypeCommented($doctrineType) : void
435 42709
    {
436
        if ($this->doctrineTypeComments === null) {
437
            $this->initializeCommentedDoctrineTypes();
438 46318
        }
439
440 46318
        assert(is_array($this->doctrineTypeComments));
441
442
        $this->doctrineTypeComments[] = $doctrineType instanceof Type ? $doctrineType->getName() : $doctrineType;
443
    }
444
445
    /**
446
     * Gets the comment to append to a column comment that helps parsing this type in reverse engineering.
447
     */
448 51536
    public function getDoctrineTypeComment(Type $doctrineType) : string
449
    {
450 51536
        return '(DC2Type:' . $doctrineType->getName() . ')';
451
    }
452 51536
453 51536
    /**
454
     * Gets the comment of a passed column modified by potential doctrine type comment hints.
455 51536
     */
456 51536
    protected function getColumnComment(Column $column) : ?string
457
    {
458
        $comment = $column->getComment();
459 51536
460
        if ($this->isCommentedDoctrineType($column->getType())) {
461 51536
            $comment .= $this->getDoctrineTypeComment($column->getType());
462
        }
463
464
        return $comment;
465
    }
466
467
    /**
468 51666
     * Gets the character used for identifier quoting.
469
     */
470 51666
    public function getIdentifierQuoteCharacter() : string
471 51524
    {
472
        return '"';
473
    }
474 51666
475
    /**
476 51666
     * Gets the string portion that starts an SQL comment.
477
     */
478
    public function getSqlCommentStartString() : string
479
    {
480
        return '--';
481
    }
482
483
    /**
484
     * Gets the string portion that ends an SQL comment.
485
     */
486 44673
    public function getSqlCommentEndString() : string
487
    {
488 44673
        return "\n";
489 44673
    }
490
491
    /**
492 44673
     * Gets the maximum length of a char field.
493
     */
494 44673
    public function getCharMaxLength() : int
495 44673
    {
496
        return $this->getVarcharMaxLength();
497
    }
498
499
    /**
500
     * Gets the maximum length of a varchar field.
501
     */
502 48317
    public function getVarcharMaxLength() : int
503
    {
504 48317
        return 4000;
505
    }
506
507
    /**
508
     * Gets the default length of a varchar field.
509
     */
510
    public function getVarcharDefaultLength() : int
511
    {
512 51355
        return 255;
513
    }
514 51355
515
    /**
516 51355
     * Gets the maximum length of a binary field.
517 48317
     */
518
    public function getBinaryMaxLength() : int
519
    {
520 51355
        return 4000;
521
    }
522
523
    /**
524
     * Gets the default length of a binary field.
525
     */
526
    public function getBinaryDefaultLength() : int
527
    {
528 47010
        return 255;
529
    }
530 47010
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
551
    /**
552
     * Returns the SQL snippet to get the average value of a column.
553
     *
554
     * @param string $expression The column to use.
555
     *
556 48837
     * @return string Generated SQL including an AVG aggregate function.
557
     */
558 48837
    public function getAvgExpression(string $expression) : string
559
    {
560
        return 'AVG(' . $expression . ')';
561
    }
562
563
    /**
564
     * Returns the SQL snippet to get the number of rows (without a NULL value) of a column.
565
     *
566 46371
     * If a '*' is used instead of a column the number of selected rows is returned.
567
     *
568 46371
     * @param string $expression The expression to count.
569
     *
570
     * @return string Generated SQL including a COUNT aggregate function.
571
     */
572
    public function getCountExpression(string $expression) : string
573
    {
574
        return 'COUNT(' . $expression . ')';
575
    }
576 47534
577
    /**
578 47534
     * 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
     * Returns the SQL snippet to get the lowest value of a column.
591
     *
592
     * @param string $expression The column to use.
593
     *
594
     * @return string Generated SQL including a MIN aggregate function.
595
     */
596 45694
    public function getMinExpression(string $expression) : string
597
    {
598 45694
        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
613
    // scalar functions
614
615
    /**
616
     * Returns the SQL snippet to get the md5 sum of a field.
617
     *
618 32634
     * Note: Not SQL92, but common functionality.
619
     */
620 32634
    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
     * @throws InvalidArgumentException
669
     */
670
    public function getTrimExpression(string $str, int $mode = TrimMode::UNSPECIFIED, ?string $char = null) : string
671
    {
672
        $tokens = [];
673
674
        switch ($mode) {
675
            case TrimMode::UNSPECIFIED:
676
                break;
677
678
            case TrimMode::LEADING:
679
                $tokens[] = 'LEADING';
680
                break;
681
682
            case TrimMode::TRAILING:
683
                $tokens[] = 'TRAILING';
684
                break;
685
686
            case TrimMode::BOTH:
687
                $tokens[] = 'BOTH';
688
                break;
689
690
            default:
691
                throw new InvalidArgumentException(
692
                    sprintf(
693
                        'The value of $mode is expected to be one of the TrimMode constants, %d given',
694
                        $mode
695
                    )
696
                );
697
        }
698
699
        if ($char !== null) {
700
            $tokens[] = $char;
701
        }
702
703
        if (count($tokens) > 0) {
704
            $tokens[] = 'FROM';
705
        }
706
707
        $tokens[] = $str;
708
709
        return sprintf('TRIM(%s)', implode(' ', $tokens));
710
    }
711
712
    /**
713
     * Returns the SQL snippet to trim trailing space characters from the expression.
714
     *
715
     * @param string $expression Literal string or column name.
716
     */
717
    public function getRtrimExpression(string $expression) : string
718
    {
719
        return 'RTRIM(' . $expression . ')';
720
    }
721
722
    /**
723
     * Returns the SQL snippet to trim leading space characters from the expression.
724
     *
725
     * @param string $expression Literal string or column name.
726
     */
727
    public function getLtrimExpression(string $expression) : string
728
    {
729
        return 'LTRIM(' . $expression . ')';
730
    }
731
732
    /**
733
     * Returns the SQL snippet to change all characters from the expression to uppercase,
734
     * according to the current character set mapping.
735
     *
736
     * @param string $expression Literal string or column name.
737
     */
738
    public function getUpperExpression(string $expression) : string
739
    {
740
        return 'UPPER(' . $expression . ')';
741
    }
742
743
    /**
744
     * Returns the SQL snippet to change all characters from the expression to lowercase,
745
     * according to the current character set mapping.
746
     *
747
     * @param string $expression Literal string or column name.
748
     */
749
    public function getLowerExpression(string $expression) : string
750
    {
751
        return 'LOWER(' . $expression . ')';
752
    }
753
754
    /**
755
     * Returns the SQL snippet to get the position of the first occurrence of the substring in the string.
756
     *
757
     * @param string      $string    SQL expression producing the string to locate the substring in.
758
     * @param string      $substring SQL expression producing the substring to locate.
759
     * @param string|null $start     SQL expression producing the position to start at.
760 44305
     *                               Defaults to the beginning of the string.
761
     *
762 44305
     * @throws DBALException If not supported on this platform.
763
     */
764 44305
    public function getLocateExpression(string $string, string $substring, ?string $start = null) : string
0 ignored issues
show
Unused Code introduced by
The parameter $substring 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

764
    public function getLocateExpression(string $string, /** @scrutinizer ignore-unused */ string $substring, ?string $start = 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...
765
    {
766 44257
        throw DBALException::notSupported(__METHOD__);
767 44257
    }
768
769
    /**
770 44236
     * Returns the SQL snippet to get the current system date.
771 44236
     */
772
    public function getNowExpression() : string
773
    {
774 44215
        return 'NOW()';
775 44215
    }
776
777
    /**
778 44305
     * Returns an SQL snippet to get a substring inside the string.
779 44213
     *
780
     * Note: Not SQL92, but common functionality.
781
     *
782 44305
     * @param string      $string SQL expression producing the string from which a substring should be extracted.
783 44282
     * @param string      $start  SQL expression producing the position to start at,
784
     * @param string|null $length SQL expression producing the length of the substring portion to be returned.
785
     *                            By default, the entire substring is returned.
786 44305
     */
787
    public function getSubstringExpression(string $string, string $start, ?string $length = null) : string
788
    {
789
        if ($length === null) {
790
            return sprintf('SUBSTRING(%s FROM %s)', $string, $start);
791
        }
792
793
        return sprintf('SUBSTRING(%s FROM %s FOR %s)', $string, $start, $length);
794
    }
795
796 16399
    /**
797
     * Returns a SQL snippet to concatenate the given expressions.
798 16399
     *
799
     * @param string[] ...$expressions
800
     */
801
    public function getConcatExpression(string ...$expressions) : string
802
    {
803
        return implode(' || ', $expressions);
804
    }
805
806
    /**
807
     * Returns the SQL for a logical not.
808 16399
     *
809
     * Example:
810 16399
     * <code>
811
     * $q = new Doctrine_Query();
812
     * $e = $q->expr;
813
     * $q->select('*')->from('table')
814
     *   ->where($e->eq('id', $e->not('null'));
815
     * </code>
816
     *
817
     * @return string The logical expression.
818
     */
819
    public function getNotExpression(string $expression) : string
820
    {
821
        return 'NOT(' . $expression . ')';
822
    }
823
824
    /**
825
     * Returns the SQL that checks if an expression is null.
826
     *
827
     * @param string $expression The expression that should be compared to null.
828
     *
829
     * @return string The logical expression.
830
     */
831
    public function getIsNullExpression(string $expression) : string
832
    {
833
        return $expression . ' IS NULL';
834
    }
835
836
    /**
837
     * Returns the SQL that checks if an expression is not null.
838
     *
839
     * @param string $expression The expression that should be compared to null.
840
     *
841
     * @return string The logical expression.
842
     */
843
    public function getIsNotNullExpression(string $expression) : string
844
    {
845
        return $expression . ' IS NOT NULL';
846
    }
847
848
    /**
849
     * Returns the SQL that checks if an expression evaluates to a value between two values.
850
     *
851
     * The parameter $expression is checked if it is between $value1 and $value2.
852
     *
853
     * Note: There is a slight difference in the way BETWEEN works on some databases.
854
     * http://www.w3schools.com/sql/sql_between.asp. If you want complete database
855
     * independence you should avoid using between().
856
     *
857
     * @param string $expression The value to compare to.
858
     * @param string $value1     The lower value to compare with.
859
     * @param string $value2     The higher value to compare with.
860
     *
861
     * @return string The logical expression.
862
     */
863
    public function getBetweenExpression(string $expression, string $value1, string $value2) : string
864
    {
865
        return $expression . ' BETWEEN ' . $value1 . ' AND ' . $value2;
866
    }
867
868
    /**
869
     * Returns the SQL to get the arccosine of a value.
870
     */
871
    public function getAcosExpression(string $value) : string
872
    {
873
        return 'ACOS(' . $value . ')';
874
    }
875
876
    /**
877
     * Returns the SQL to get the sine of a value.
878
     */
879
    public function getSinExpression(string $value) : string
880
    {
881
        return 'SIN(' . $value . ')';
882
    }
883
884
    /**
885
     * Returns the SQL to get the PI value.
886
     */
887
    public function getPiExpression() : string
888
    {
889
        return 'PI()';
890
    }
891
892
    /**
893
     * Returns the SQL to get the cosine of a value.
894 32611
     */
895
    public function getCosExpression(string $value) : string
896 32611
    {
897
        return 'COS(' . $value . ')';
898
    }
899
900
    /**
901
     * Returns the SQL to calculate the difference in days between the two passed dates.
902
     *
903
     * Computes diff = date1 - date2.
904
     *
905
     * @throws DBALException If not supported on this platform.
906
     */
907
    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

907
    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

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

1083
    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...
1084
    {
1085
        throw DBALException::notSupported(__METHOD__);
1086
    }
1087
1088
    /**
1089
     * Converts the value of a natively unsupported date interval to a value of a supported one
1090
     *
1091
     * @param string $interval   SQL expression describing the value of the interval
1092
     * @param string $unit       Interval unit
1093
     * @param int    $multiplier Interval multiplier
1094
     *
1095
     * @throws DBALException
1096 49830
     */
1097
    protected function convertNonNativeInterval(string $interval, string $unit, int $multiplier) : string
1098 49830
    {
1099
        if (! is_numeric($interval)) {
1100
            throw new DBALException(sprintf('Non-numeric value of a %s interval is not supported', $unit));
1101
        }
1102
1103
        return (string) ((int) $interval * $multiplier);
1104
    }
1105
1106
    /**
1107
     * Returns the SQL bit AND comparison expression.
1108
     */
1109
    public function getBitAndComparisonExpression(string $value1, string $value2) : string
1110
    {
1111 49830
        return '(' . $value1 . ' & ' . $value2 . ')';
1112
    }
1113 49830
1114
    /**
1115
     * Returns the SQL bit OR comparison expression.
1116
     */
1117
    public function getBitOrComparisonExpression(string $value1, string $value2) : string
1118
    {
1119
        return '(' . $value1 . ' | ' . $value2 . ')';
1120
    }
1121
1122
    /**
1123
     * Returns the FOR UPDATE expression.
1124
     */
1125
    public function getForUpdateSQL() : string
1126 49832
    {
1127
        return 'FOR UPDATE';
1128 49832
    }
1129
1130
    /**
1131
     * Honors that some SQL vendors such as MsSql use table hints for locking instead of the ANSI SQL FOR UPDATE specification.
1132
     *
1133
     * @param string   $fromClause The FROM clause to append the hint for the given lock mode to.
1134
     * @param int|null $lockMode   One of the Doctrine\DBAL\LockMode::* constants. If null is given, nothing will
1135
     *                             be appended to the FROM clause.
1136
     */
1137
    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

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

1470
    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...
1471
    {
1472
        throw DBALException::notSupported(__METHOD__);
1473
    }
1474
1475 46041
    /**
1476
     * Returns the SQL to change a sequence on this platform.
1477 46041
     *
1478 43250
     * @throws DBALException If not supported on this platform.
1479
     */
1480
    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

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

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

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

2519
    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...
2520
    {
2521
        throw DBALException::notSupported(__METHOD__);
2522 49597
    }
2523
2524 49597
    /**
2525 49597
     * @throws DBALException If not supported on this platform.
2526 49502
     */
2527
    public function getListTableForeignKeysSQL(string $table, ?string $database = null) : string
2528 49597
    {
2529
        throw DBALException::notSupported(__METHOD__);
2530 49597
    }
2531
2532
    /**
2533 49597
     * @throws DBALException If not supported on this platform.
2534
     */
2535
    public function getCreateViewSQL(string $name, string $sql) : string
2536 49597
    {
2537
        throw DBALException::notSupported(__METHOD__);
2538
    }
2539
2540 49597
    /**
2541 49597
     * @throws DBALException If not supported on this platform.
2542 49597
     */
2543 49597
    public function getDropViewSQL(string $name) : string
2544
    {
2545
        throw DBALException::notSupported(__METHOD__);
2546
    }
2547
2548
    /**
2549
     * Returns the SQL snippet to drop an existing sequence.
2550
     *
2551
     * @param Sequence|string $sequence
2552
     *
2553
     * @throws DBALException If not supported on this platform.
2554
     */
2555
    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

2555
    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...
2556
    {
2557
        throw DBALException::notSupported(__METHOD__);
2558
    }
2559
2560
    /**
2561
     * @throws DBALException If not supported on this platform.
2562
     */
2563
    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

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

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