Completed
Push — develop ( 8dba78...8cb2fb )
by Marco
31s queued 13s
created

AbstractPlatform   F

Complexity

Total Complexity 392

Size/Duplication

Total Lines 3458
Duplicated Lines 0 %

Test Coverage

Coverage 79.39%

Importance

Changes 0
Metric Value
wmc 392
eloc 661
dl 0
loc 3458
ccs 647
cts 815
cp 0.7939
rs 1.939
c 0
b 0
f 0

210 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 2 1
A setEventManager() 0 3 1
A supportsAlterTable() 0 3 1
A getEventManager() 0 3 1
A getSequencePrefix() 0 10 4
A supportsForeignKeyOnUpdate() 0 3 1
A getCheckDeclarationSQL() 0 18 5
A getWriteLockSQL() 0 3 1
A getCharMaxLength() 0 3 1
A getCreateSchemaSQL() 0 3 1
A getLtrimExpression() 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 initializeCommentedDoctrineTypes() 0 12 3
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 97 24
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 getVarcharMaxLength() 0 3 1
A getDateTimeTypeDeclarationSQL() 0 3 1
A supportsCommentOnStatement() 0 3 1
A convertFromBoolean() 0 3 2
A getSqrtExpression() 0 3 1
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 getSqlCommentStartString() 0 3 1
A getUpperExpression() 0 3 1
A _getTransactionIsolationLevelSQL() 0 13 5
A getForeignKeyDeclarationSQL() 0 6 1
A prefersIdentityColumns() 0 3 1
A getDropForeignKeySQL() 0 14 3
A getBinaryDefaultLength() 0 3 1
A getListNamespacesSQL() 0 3 1
A getSumExpression() 0 3 1
A registerDoctrineTypeMapping() 0 20 4
A appendLockHint() 0 3 1
A markDoctrineTypeCommented() 0 9 3
A getSqlCommentEndString() 0 3 1
A getUniqueConstraintDeclarationSQL() 0 20 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 getGuidTypeDeclarationSQL() 0 6 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 getBinaryTypeDeclarationSQLSnippet() 0 3 1
A createSavePoint() 0 3 1
A getAvgExpression() 0 3 1
A getCreateForeignKeySQL() 0 7 2
A convertBooleansToDatabaseValue() 0 3 1
A supportsIdentityColumns() 0 3 1
A getIdentitySequenceName() 0 3 1
A getBinaryMaxLength() 0 3 1
A getReadLockSQL() 0 3 1
A getAcosExpression() 0 3 1
A getForeignKeyReferentialActionSQL() 0 12 6
A getSequenceNextValSQL() 0 3 1
A getDoctrineTypeComment() 0 3 1
A getMd5Expression() 0 3 1
A getCurrentDateSQL() 0 3 1
A getCustomTypeDeclarationSQL() 0 3 1
A supportsPartialIndexes() 0 3 1
A getCountExpression() 0 3 1
A prefersSequences() 0 3 1
A getMinExpression() 0 3 1
A getReservedKeywordsClass() 0 3 1
A getReservedKeywordsList() 0 17 3
A getVarcharTypeDeclarationSQLSnippet() 0 3 1
A getModExpression() 0 3 1
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 getMaxExpression() 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 hasDoctrineTypeMappingFor() 0 9 2
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 getLengthExpression() 0 3 1
A supportsColumnCollation() 0 3 1
A getVarcharDefaultLength() 0 3 1
A getDecimalTypeDeclarationSQL() 0 8 5
A quoteStringLiteral() 0 5 1
A hasNativeJsonType() 0 3 1
A multiplyInterval() 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 getIdentifierQuoteCharacter() 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 initializeAllDoctrineTypeMappings() 0 7 3
A isCommentedDoctrineType() 0 9 2
A getRegexpExpression() 0 3 1
A getDoctrineTypeMapping() 0 13 3
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 getLowerExpression() 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 getVarcharTypeDeclarationSQL() 0 17 4
A getDateSubSecondsExpression() 0 3 1
A getDateTimeTzFormatString() 0 3 1
A supportsGettingAffectedRows() 0 3 1
A getRoundExpression() 0 3 1
A getCurrentTimestampSQL() 0 3 1
A releaseSavePoint() 0 3 1
A getIsNotNullExpression() 0 3 1
A getRtrimExpression() 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 getBinaryTypeDeclarationSQL() 0 9 2
A getIndexDeclarationSQL() 0 12 2
A getJsonTypeDeclarationSQL() 0 3 1
A getListTableIndexesSQL() 0 3 1
A getConcatExpression() 0 3 1
A getLocateExpression() 0 3 1
A getWildcards() 0 3 1
A getListTablesSQL() 0 3 1
A getEmptyIdentityInsertSQL() 0 3 1
A supportsInlineColumnComments() 0 3 1
A quoteIdentifier() 0 9 2
B getTrimExpression() 0 40 7
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 getColumnComment() 0 9 2
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_int;
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 */
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)
157 51693
    {
158
        $this->_eventManager = $eventManager;
159 51693
    }
160 51693
161
    /**
162
     * Gets the EventManager used by the Platform.
163
     *
164
     * @return EventManager
165
     */
166
    public function getEventManager()
167 48959
    {
168
        return $this->_eventManager;
169 48959
    }
170
171
    /**
172
     * Returns the SQL snippet that declares a boolean column.
173
     *
174
     * @param mixed[] $columnDef
175
     *
176
     * @return string
177
     */
178
    abstract public function getBooleanTypeDeclarationSQL(array $columnDef);
179
180
    /**
181
     * Returns the SQL snippet that declares a 4 byte integer column.
182
     *
183
     * @param mixed[] $columnDef
184
     *
185
     * @return string
186
     */
187
    abstract public function getIntegerTypeDeclarationSQL(array $columnDef);
188
189
    /**
190
     * Returns the SQL snippet that declares an 8 byte integer column.
191
     *
192
     * @param mixed[] $columnDef
193
     *
194
     * @return string
195
     */
196
    abstract public function getBigIntTypeDeclarationSQL(array $columnDef);
197
198
    /**
199
     * Returns the SQL snippet that declares a 2 byte integer column.
200
     *
201
     * @param mixed[] $columnDef
202
     *
203
     * @return string
204
     */
205
    abstract public function getSmallIntTypeDeclarationSQL(array $columnDef);
206
207
    /**
208
     * Returns the SQL snippet that declares common properties of an integer column.
209
     *
210
     * @param mixed[] $columnDef
211
     *
212
     * @return string
213
     */
214
    abstract protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef);
215
216
    /**
217
     * Lazy load Doctrine Type Mappings.
218
     *
219
     * @return void
220
     */
221
    abstract protected function initializeDoctrineTypeMappings();
222
223
    /**
224
     * Initializes Doctrine Type Mappings with the platform defaults
225
     * and with all additional type mappings.
226
     *
227
     * @return void
228
     */
229
    private function initializeAllDoctrineTypeMappings()
230 48985
    {
231
        $this->initializeDoctrineTypeMappings();
232 48985
233
        foreach (Type::getTypesMap() as $typeName => $className) {
234 48985
            foreach (Type::getType($typeName)->getMappedDatabaseTypes($this) as $dbType) {
235 48985
                $this->doctrineTypeMapping[$dbType] = $typeName;
236 28427
            }
237
        }
238
    }
239 48985
240
    /**
241
     * Returns the SQL snippet used to declare a VARCHAR column type.
242
     *
243
     * @param mixed[] $field
244
     *
245
     * @return string
246
     */
247
    public function getVarcharTypeDeclarationSQL(array $field)
248 50880
    {
249
        if (! isset($field['length'])) {
250 50880
            $field['length'] = $this->getVarcharDefaultLength();
251 47537
        }
252
253
        $fixed = $field['fixed'] ?? false;
254 50880
255
        $maxLength = $fixed
256 50880
            ? $this->getCharMaxLength()
257 49087
            : $this->getVarcharMaxLength();
258 50880
259
        if ($field['length'] > $maxLength) {
260 50880
            return $this->getClobTypeDeclarationSQL($field);
261
        }
262
263
        return $this->getVarcharTypeDeclarationSQLSnippet($field['length'], $fixed);
264 50880
    }
265
266
    /**
267
     * Returns the SQL snippet used to declare a BINARY/VARBINARY column type.
268
     *
269
     * @param mixed[] $field The column definition.
270
     *
271
     * @return string
272
     */
273
    public function getBinaryTypeDeclarationSQL(array $field)
274 47724
    {
275
        if (! isset($field['length'])) {
276 47724
            $field['length'] = $this->getBinaryDefaultLength();
277 47691
        }
278
279
        $fixed = $field['fixed'] ?? false;
280 47724
281
        return $this->getBinaryTypeDeclarationSQLSnippet($field['length'], $fixed);
282 47724
    }
283
284
    /**
285
     * Returns the SQL snippet to declare a GUID/UUID field.
286
     *
287
     * By default this maps directly to a CHAR(36) and only maps to more
288
     * special datatypes when the underlying databases support this datatype.
289
     *
290
     * @param mixed[] $field
291
     *
292
     * @return string
293
     */
294
    public function getGuidTypeDeclarationSQL(array $field)
295 47064
    {
296
        $field['length'] = 36;
297 47064
        $field['fixed']  = true;
298 47064
299
        return $this->getVarcharTypeDeclarationSQL($field);
300 47064
    }
301
302
    /**
303
     * Returns the SQL snippet to declare a JSON field.
304
     *
305
     * By default this maps directly to a CLOB and only maps to more
306
     * special datatypes when the underlying databases support this datatype.
307
     *
308
     * @param mixed[] $field
309
     *
310
     * @return string
311
     */
312
    public function getJsonTypeDeclarationSQL(array $field)
313 45261
    {
314
        return $this->getClobTypeDeclarationSQL($field);
315 45261
    }
316
317
    /**
318
     * @param int  $length
319
     * @param bool $fixed
320
     *
321
     * @return string
322
     *
323
     * @throws DBALException If not supported on this platform.
324
     */
325
    protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed)
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

325
    protected function getVarcharTypeDeclarationSQLSnippet($length, /** @scrutinizer ignore-unused */ $fixed)

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...
326
    {
327
        throw DBALException::notSupported('VARCHARs not supported by Platform.');
328
    }
329
330
    /**
331
     * Returns the SQL snippet used to declare a BINARY/VARBINARY column type.
332
     *
333
     * @param int  $length The length of the column.
334
     * @param bool $fixed  Whether the column length is fixed.
335
     *
336
     * @return string
337
     *
338
     * @throws DBALException If not supported on this platform.
339
     */
340
    protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed)
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

340
    protected function getBinaryTypeDeclarationSQLSnippet($length, /** @scrutinizer ignore-unused */ $fixed)

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...
341
    {
342
        throw DBALException::notSupported('BINARY/VARBINARY column types are not supported by this platform.');
343
    }
344
345
    /**
346
     * Returns the SQL snippet used to declare a CLOB column type.
347
     *
348
     * @param mixed[] $field
349
     *
350
     * @return string
351
     */
352
    abstract public function getClobTypeDeclarationSQL(array $field);
353
354
    /**
355
     * Returns the SQL Snippet used to declare a BLOB column type.
356
     *
357
     * @param mixed[] $field
358
     *
359
     * @return string
360
     */
361
    abstract public function getBlobTypeDeclarationSQL(array $field);
362
363
    /**
364
     * Gets the name of the platform.
365
     *
366
     * @return string
367
     */
368
    abstract public function getName();
369
370
    /**
371
     * Registers a doctrine type to be used in conjunction with a column type of this platform.
372
     *
373
     * @param string $dbType
374
     * @param string $doctrineType
375
     *
376
     * @throws DBALException If the type is not found.
377
     */
378
    public function registerDoctrineTypeMapping($dbType, $doctrineType)
379 47037
    {
380
        if ($this->doctrineTypeMapping === null) {
381 47037
            $this->initializeAllDoctrineTypeMappings();
382 46455
        }
383
384
        if (! Types\Type::hasType($doctrineType)) {
385 47037
            throw DBALException::typeNotFound($doctrineType);
386 44697
        }
387
388
        $dbType                             = strtolower($dbType);
389 47025
        $this->doctrineTypeMapping[$dbType] = $doctrineType;
390 47025
391
        $doctrineType = Type::getType($doctrineType);
392 47025
393
        if (! $doctrineType->requiresSQLCommentHint($this)) {
394 47025
            return;
395 47013
        }
396
397
        $this->markDoctrineTypeCommented($doctrineType);
398 44673
    }
399 44673
400
    /**
401
     * Gets the Doctrine type that is mapped for the given database column type.
402
     *
403
     * @param string $dbType
404
     *
405
     * @return string
406
     *
407
     * @throws DBALException
408
     */
409
    public function getDoctrineTypeMapping($dbType)
410 48996
    {
411
        if ($this->doctrineTypeMapping === null) {
412 48996
            $this->initializeAllDoctrineTypeMappings();
413 48932
        }
414
415
        $dbType = strtolower($dbType);
416 48996
417
        if (! isset($this->doctrineTypeMapping[$dbType])) {
418 48996
            throw new DBALException('Unknown database type ' . $dbType . ' requested, ' . static::class . ' may not support it.');
419 44745
        }
420
421
        return $this->doctrineTypeMapping[$dbType];
422 48984
    }
423
424
    /**
425
     * Checks if a database type is currently supported by this platform.
426
     *
427
     * @param string $dbType
428
     *
429
     * @return bool
430
     */
431
    public function hasDoctrineTypeMappingFor($dbType)
432 46318
    {
433
        if ($this->doctrineTypeMapping === null) {
434 46318
            $this->initializeAllDoctrineTypeMappings();
435 42709
        }
436
437
        $dbType = strtolower($dbType);
438 46318
439
        return isset($this->doctrineTypeMapping[$dbType]);
440 46318
    }
441
442
    /**
443
     * Initializes the Doctrine Type comments instance variable for in_array() checks.
444
     *
445
     * @return void
446
     */
447
    protected function initializeCommentedDoctrineTypes()
448 51536
    {
449
        $this->doctrineTypeComments = [];
450 51536
451
        foreach (Type::getTypesMap() as $typeName => $className) {
452 51536
            $type = Type::getType($typeName);
453 51536
454
            if (! $type->requiresSQLCommentHint($this)) {
455 51536
                continue;
456 51536
            }
457
458
            $this->doctrineTypeComments[] = $typeName;
459 51536
        }
460
    }
461 51536
462
    /**
463
     * Is it necessary for the platform to add a parsable type comment to allow reverse engineering the given type?
464
     *
465
     * @return bool
466
     */
467
    public function isCommentedDoctrineType(Type $doctrineType)
468 51666
    {
469
        if ($this->doctrineTypeComments === null) {
470 51666
            $this->initializeCommentedDoctrineTypes();
471 51524
        }
472
473
        assert(is_array($this->doctrineTypeComments));
474 51666
475
        return in_array($doctrineType->getName(), $this->doctrineTypeComments);
476 51666
    }
477
478
    /**
479
     * Marks this type as to be commented in ALTER TABLE and CREATE TABLE statements.
480
     *
481
     * @param string|Type $doctrineType
482
     *
483
     * @return void
484
     */
485
    public function markDoctrineTypeCommented($doctrineType)
486 44673
    {
487
        if ($this->doctrineTypeComments === null) {
488 44673
            $this->initializeCommentedDoctrineTypes();
489 44673
        }
490
491
        assert(is_array($this->doctrineTypeComments));
492 44673
493
        $this->doctrineTypeComments[] = $doctrineType instanceof Type ? $doctrineType->getName() : $doctrineType;
494 44673
    }
495 44673
496
    /**
497
     * Gets the comment to append to a column comment that helps parsing this type in reverse engineering.
498
     *
499
     * @return string
500
     */
501
    public function getDoctrineTypeComment(Type $doctrineType)
502 48317
    {
503
        return '(DC2Type:' . $doctrineType->getName() . ')';
504 48317
    }
505
506
    /**
507
     * Gets the comment of a passed column modified by potential doctrine type comment hints.
508
     *
509
     * @return string|null
510
     */
511
    protected function getColumnComment(Column $column)
512 51355
    {
513
        $comment = $column->getComment();
514 51355
515
        if ($this->isCommentedDoctrineType($column->getType())) {
516 51355
            $comment .= $this->getDoctrineTypeComment($column->getType());
517 48317
        }
518
519
        return $comment;
520 51355
    }
521
522
    /**
523
     * Gets the character used for identifier quoting.
524
     *
525
     * @return string
526
     */
527
    public function getIdentifierQuoteCharacter()
528 47010
    {
529
        return '"';
530 47010
    }
531
532
    /**
533
     * Gets the string portion that starts an SQL comment.
534
     *
535
     * @return string
536
     */
537
    public function getSqlCommentStartString()
538
    {
539
        return '--';
540
    }
541
542
    /**
543
     * Gets the string portion that ends an SQL comment.
544
     *
545
     * @return string
546
     */
547
    public function getSqlCommentEndString()
548
    {
549
        return "\n";
550
    }
551
552
    /**
553
     * Gets the maximum length of a char field.
554
     */
555
    public function getCharMaxLength() : int
556 48837
    {
557
        return $this->getVarcharMaxLength();
558 48837
    }
559
560
    /**
561
     * Gets the maximum length of a varchar field.
562
     *
563
     * @return int
564
     */
565
    public function getVarcharMaxLength()
566 46371
    {
567
        return 4000;
568 46371
    }
569
570
    /**
571
     * Gets the default length of a varchar field.
572
     *
573
     * @return int
574
     */
575
    public function getVarcharDefaultLength()
576 47534
    {
577
        return 255;
578 47534
    }
579
580
    /**
581
     * Gets the maximum length of a binary field.
582
     *
583
     * @return int
584
     */
585
    public function getBinaryMaxLength()
586
    {
587
        return 4000;
588
    }
589
590
    /**
591
     * Gets the default length of a binary field.
592
     *
593
     * @return int
594
     */
595
    public function getBinaryDefaultLength()
596 45694
    {
597
        return 255;
598 45694
    }
599
600
    /**
601
     * Gets all SQL wildcard characters of the platform.
602
     *
603
     * @return string[]
604
     */
605
    public function getWildcards()
606
    {
607
        return ['%', '_'];
608
    }
609
610
    /**
611
     * Returns the regular expression operator.
612
     *
613
     * @throws DBALException If not supported on this platform.
614
     */
615
    public function getRegexpExpression() : string
616
    {
617
        throw DBALException::notSupported(__METHOD__);
618 32634
    }
619
620 32634
    /**
621
     * Returns the SQL snippet to get the average value of a column.
622
     *
623
     * @param string $value SQL expression producing the value.
624
     */
625
    public function getAvgExpression(string $value) : string
626
    {
627
        return 'AVG(' . $value . ')';
628
    }
629
630
    /**
631
     * Returns the SQL snippet to get the number of rows (without a NULL value) of a column.
632
     *
633
     * If a '*' is used instead of a column the number of selected rows is returned.
634
     *
635
     * @param string $expression The expression to count.
636
     */
637
    public function getCountExpression(string $expression) : string
638
    {
639
        return 'COUNT(' . $expression . ')';
640
    }
641
642
    /**
643
     * Returns the SQL snippet to get the maximum value in a set of values.
644
     *
645
     * @param string $value SQL expression producing the value.
646
     */
647
    public function getMaxExpression(string $value) : string
648
    {
649
        return 'MAX(' . $value . ')';
650
    }
651
652
    /**
653
     * Returns the SQL snippet to get the minimum value in a set of values.
654
     *
655
     * @param string $value SQL expression producing the value.
656
     */
657
    public function getMinExpression(string $value) : string
658
    {
659
        return 'MIN(' . $value . ')';
660
    }
661
662
    /**
663
     * Returns the SQL snippet to get the total sum of the values in a set.
664
     *
665
     * @param string $value SQL expression producing the value.
666
     */
667
    public function getSumExpression(string $value) : string
668
    {
669
        return 'SUM(' . $value . ')';
670
    }
671
672
    // scalar functions
673
674
    /**
675
     * Returns the SQL snippet to get the md5 sum of the value.
676
     *
677
     * Note: Not SQL92, but common functionality.
678
     *
679
     * @param string $string SQL expression producing the string.
680
     */
681
    public function getMd5Expression(string $string) : string
682
    {
683
        return 'MD5(' . $string . ')';
684
    }
685
686
    /**
687
     * Returns the SQL snippet to get the length of a text field.
688
     *
689
     * @param string $string SQL expression producing the string.
690
     */
691
    public function getLengthExpression(string $string) : string
692
    {
693
        return 'LENGTH(' . $string . ')';
694
    }
695
696
    /**
697
     * Returns the SQL snippet to get the square root of the value.
698
     *
699
     * @param string $number SQL expression producing the number.
700
     */
701
    public function getSqrtExpression(string $number) : string
702
    {
703
        return 'SQRT(' . $number . ')';
704
    }
705
706
    /**
707
     * Returns the SQL snippet to round a number to the number of decimals specified.
708
     *
709
     * @param string $number   SQL expression producing the number to round.
710
     * @param string $decimals SQL expression producing the number of decimals.
711
     */
712
    public function getRoundExpression(string $number, string $decimals = '0') : string
713
    {
714
        return 'ROUND(' . $number . ', ' . $decimals . ')';
715
    }
716
717
    /**
718
     * Returns the SQL snippet to get the remainder of the operation of division of dividend by divisor.
719
     *
720
     * @param string $dividend SQL expression producing the dividend.
721
     * @param string $divisor  SQL expression producing the divisor.
722
     */
723
    public function getModExpression(string $dividend, string $divisor) : string
724
    {
725
        return 'MOD(' . $dividend . ', ' . $divisor . ')';
726
    }
727
728
    /**
729
     * Returns the SQL snippet to trim a string.
730
     *
731
     * @param string      $str  The expression to apply the trim to.
732
     * @param int         $mode The position of the trim (leading/trailing/both).
733
     * @param string|null $char The char to trim, has to be quoted already. Defaults to space.
734
     *
735
     * @throws InvalidArgumentException
736
     */
737
    public function getTrimExpression(string $str, int $mode = TrimMode::UNSPECIFIED, ?string $char = null) : string
738
    {
739
        $tokens = [];
740
741
        switch ($mode) {
742
            case TrimMode::UNSPECIFIED:
743
                break;
744
745
            case TrimMode::LEADING:
746
                $tokens[] = 'LEADING';
747
                break;
748
749
            case TrimMode::TRAILING:
750
                $tokens[] = 'TRAILING';
751
                break;
752
753
            case TrimMode::BOTH:
754
                $tokens[] = 'BOTH';
755
                break;
756
757
            default:
758
                throw new InvalidArgumentException(
759
                    sprintf(
760 44305
                        'The value of $mode is expected to be one of the TrimMode constants, %d given',
761
                        $mode
762 44305
                    )
763
                );
764 44305
        }
765
766 44257
        if ($char !== null) {
767 44257
            $tokens[] = $char;
768
        }
769
770 44236
        if (count($tokens) > 0) {
771 44236
            $tokens[] = 'FROM';
772
        }
773
774 44215
        $tokens[] = $str;
775 44215
776
        return sprintf('TRIM(%s)', implode(' ', $tokens));
777
    }
778 44305
779 44213
    /**
780
     * Returns the SQL snippet to trim trailing space characters from the string.
781
     *
782 44305
     * @param string $string SQL expression producing the string.
783 44282
     */
784
    public function getRtrimExpression(string $string) : string
785
    {
786 44305
        return 'RTRIM(' . $string . ')';
787
    }
788
789
    /**
790
     * Returns the SQL snippet to trim leading space characters from the string.
791
     *
792
     * @param string $string SQL expression producing the string.
793
     */
794
    public function getLtrimExpression(string $string) : string
795
    {
796 16399
        return 'LTRIM(' . $string . ')';
797
    }
798 16399
799
    /**
800
     * Returns the SQL snippet to change all characters from the string to uppercase,
801
     * according to the current character set mapping.
802
     *
803
     * @param string $string SQL expression producing the string.
804
     */
805
    public function getUpperExpression(string $string) : string
806
    {
807
        return 'UPPER(' . $string . ')';
808 16399
    }
809
810 16399
    /**
811
     * Returns the SQL snippet to change all characters from the string to lowercase,
812
     * according to the current character set mapping.
813
     *
814
     * @param string $string SQL expression producing the string.
815
     */
816
    public function getLowerExpression(string $string) : string
817
    {
818
        return 'LOWER(' . $string . ')';
819
    }
820
821
    /**
822
     * Returns the SQL snippet to get the position of the first occurrence of the substring in the string.
823
     *
824
     * @param string      $string    SQL expression producing the string to locate the substring in.
825
     * @param string      $substring SQL expression producing the substring to locate.
826
     * @param string|null $start     SQL expression producing the position to start at.
827
     *                               Defaults to the beginning of the string.
828
     *
829
     * @throws DBALException If not supported on this platform.
830
     */
831
    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

831
    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...
832
    {
833
        throw DBALException::notSupported(__METHOD__);
834
    }
835
836
    /**
837
     * Returns the SQL snippet to get the current system date.
838
     */
839
    public function getNowExpression() : string
840
    {
841
        return 'NOW()';
842
    }
843
844
    /**
845
     * Returns an SQL snippet to get a substring inside the string.
846
     *
847
     * Note: Not SQL92, but common functionality.
848
     *
849
     * @param string      $string SQL expression producing the string from which a substring should be extracted.
850
     * @param string      $start  SQL expression producing the position to start at,
851
     * @param string|null $length SQL expression producing the length of the substring portion to be returned.
852
     *                            By default, the entire substring is returned.
853
     */
854
    public function getSubstringExpression(string $string, string $start, ?string $length = null) : string
855
    {
856
        if ($length === null) {
857
            return sprintf('SUBSTRING(%s FROM %s)', $string, $start);
858
        }
859
860
        return sprintf('SUBSTRING(%s FROM %s FOR %s)', $string, $start, $length);
861
    }
862
863
    /**
864
     * Returns a SQL snippet to concatenate the given strings.
865
     *
866
     * @param string[] ...$string
867
     */
868
    public function getConcatExpression(string ...$string) : string
869
    {
870
        return implode(' || ', $string);
871
    }
872
873
    /**
874
     * Returns the SQL for a logical not.
875
     *
876
     * @param string $value SQL expression producing the value to negate.
877
     */
878
    public function getNotExpression(string $value) : string
879
    {
880
        return 'NOT(' . $value . ')';
881
    }
882
883
    /**
884
     * Returns the SQL that checks if an expression is null.
885
     *
886
     * @param string $value SQL expression producing the to be compared to null.
887
     */
888
    public function getIsNullExpression(string $value) : string
889
    {
890
        return $value . ' IS NULL';
891
    }
892
893
    /**
894 32611
     * Returns the SQL that checks if an expression is not null.
895
     *
896 32611
     * @param string $value SQL expression producing the to be compared to null.
897
     */
898
    public function getIsNotNullExpression(string $value) : string
899
    {
900
        return $value . ' IS NOT NULL';
901
    }
902
903
    /**
904
     * Returns the SQL that checks if an expression evaluates to a value between two values.
905
     *
906
     * The parameter $value is checked if it is between $min and $max.
907
     *
908
     * Note: There is a slight difference in the way BETWEEN works on some databases.
909
     * http://www.w3schools.com/sql/sql_between.asp. If you want complete database
910
     * independence you should avoid using between().
911
     *
912
     * @param string $value SQL expression producing the value to compare.
913
     * @param string $min   SQL expression producing the lower value to compare with.
914
     * @param string $max   SQL expression producing the higher value to compare with.
915
     */
916
    public function getBetweenExpression(string $value, string $min, string $max) : string
917
    {
918
        return $value . ' BETWEEN ' . $min . ' AND ' . $max;
919
    }
920
921
    /**
922
     * Returns the SQL to get the arccosine of a value.
923
     *
924
     * @param string $number SQL expression producing the number.
925
     */
926 51682
    public function getAcosExpression(string $number) : string
927
    {
928 51682
        return 'ACOS(' . $number . ')';
929
    }
930
931
    /**
932
     * Returns the SQL to get the sine of a value.
933
     *
934
     * @param string $number SQL expression producing the number.
935
     */
936
    public function getSinExpression(string $number) : string
937
    {
938
        return 'SIN(' . $number . ')';
939
    }
940
941
    /**
942
     * Returns the SQL to get the PI value.
943
     */
944
    public function getPiExpression() : string
945
    {
946
        return 'PI()';
947
    }
948
949
    /**
950
     * Returns the SQL to get the cosine of a value.
951
     *
952
     * @param string $number SQL expression producing the number.
953
     */
954
    public function getCosExpression(string $number) : string
955
    {
956
        return 'COS(' . $number . ')';
957
    }
958
959
    /**
960
     * Returns the SQL to calculate the difference in days between the two passed dates.
961
     *
962
     * Computes diff = date1 - date2.
963
     *
964
     * @throws DBALException If not supported on this platform.
965
     */
966
    public function getDateDiffExpression(string $date1, string $date2) : string
0 ignored issues
show
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

966
    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...
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

966
    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...
967
    {
968
        throw DBALException::notSupported(__METHOD__);
969
    }
970
971
    /**
972
     * Returns the SQL to add the number of given seconds to a date.
973
     *
974
     * @param string $date    SQL expression producing the date.
975
     * @param string $seconds SQL expression producing the number of seconds.
976
     *
977
     * @throws DBALException If not supported on this platform.
978
     */
979
    public function getDateAddSecondsExpression(string $date, string $seconds) : string
980
    {
981
        return $this->getDateArithmeticIntervalExpression($date, '+', $seconds, DateIntervalUnit::SECOND);
982
    }
983
984
    /**
985
     * Returns the SQL to subtract the number of given seconds from a date.
986
     *
987
     * @param string $date    SQL expression producing the date.
988
     * @param string $seconds SQL expression producing the number of seconds.
989
     *
990
     * @throws DBALException If not supported on this platform.
991
     */
992
    public function getDateSubSecondsExpression(string $date, string $seconds) : string
993
    {
994
        return $this->getDateArithmeticIntervalExpression($date, '-', $seconds, DateIntervalUnit::SECOND);
995
    }
996
997
    /**
998
     * Returns the SQL to add the number of given minutes to a date.
999
     *
1000
     * @param string $date    SQL expression producing the date.
1001
     * @param string $minutes SQL expression producing the number of minutes.
1002
     *
1003
     * @throws DBALException If not supported on this platform.
1004
     */
1005
    public function getDateAddMinutesExpression(string $date, string $minutes) : string
1006
    {
1007
        return $this->getDateArithmeticIntervalExpression($date, '+', $minutes, DateIntervalUnit::MINUTE);
1008
    }
1009
1010
    /**
1011
     * Returns the SQL to subtract the number of given minutes from a date.
1012
     *
1013
     * @param string $date    SQL expression producing the date.
1014
     * @param string $minutes SQL expression producing the number of minutes.
1015
     *
1016
     * @throws DBALException If not supported on this platform.
1017
     */
1018
    public function getDateSubMinutesExpression(string $date, string $minutes) : string
1019
    {
1020
        return $this->getDateArithmeticIntervalExpression($date, '-', $minutes, DateIntervalUnit::MINUTE);
1021
    }
1022
1023
    /**
1024
     * Returns the SQL to add the number of given hours to a date.
1025
     *
1026
     * @param string $date  SQL expression producing the date.
1027
     * @param string $hours SQL expression producing the number of hours.
1028
     *
1029
     * @throws DBALException If not supported on this platform.
1030
     */
1031
    public function getDateAddHourExpression(string $date, string $hours) : string
1032
    {
1033
        return $this->getDateArithmeticIntervalExpression($date, '+', $hours, DateIntervalUnit::HOUR);
1034
    }
1035
1036 49830
    /**
1037
     * Returns the SQL to subtract the number of given hours to a date.
1038 49830
     *
1039
     * @param string $date  SQL expression producing the date.
1040
     * @param string $hours SQL expression producing the number of hours.
1041
     *
1042
     * @throws DBALException If not supported on this platform.
1043
     */
1044
    public function getDateSubHourExpression(string $date, string $hours) : string
1045
    {
1046
        return $this->getDateArithmeticIntervalExpression($date, '-', $hours, DateIntervalUnit::HOUR);
1047
    }
1048
1049
    /**
1050
     * Returns the SQL to add the number of given days to a date.
1051 49830
     *
1052
     * @param string $date SQL expression producing the date.
1053 49830
     * @param string $days SQL expression producing the number of days.
1054
     *
1055
     * @throws DBALException If not supported on this platform.
1056
     */
1057
    public function getDateAddDaysExpression(string $date, string $days) : string
1058
    {
1059
        return $this->getDateArithmeticIntervalExpression($date, '+', $days, DateIntervalUnit::DAY);
1060
    }
1061
1062
    /**
1063
     * Returns the SQL to subtract the number of given days to a date.
1064
     *
1065
     * @param string $date SQL expression producing the date.
1066 49830
     * @param string $days SQL expression producing the number of days.
1067
     *
1068 49830
     * @throws DBALException If not supported on this platform.
1069
     */
1070
    public function getDateSubDaysExpression(string $date, string $days) : string
1071
    {
1072
        return $this->getDateArithmeticIntervalExpression($date, '-', $days, DateIntervalUnit::DAY);
1073
    }
1074
1075
    /**
1076
     * Returns the SQL to add the number of given weeks to a date.
1077
     *
1078
     * @param string $date  SQL expression producing the date.
1079
     * @param string $weeks SQL expression producing the number of weeks.
1080
     *
1081 49830
     * @throws DBALException If not supported on this platform.
1082
     */
1083 49830
    public function getDateAddWeeksExpression(string $date, string $weeks) : string
1084
    {
1085
        return $this->getDateArithmeticIntervalExpression($date, '+', $weeks, DateIntervalUnit::WEEK);
1086
    }
1087
1088
    /**
1089
     * Returns the SQL to subtract the number of given weeks from a date.
1090
     *
1091
     * @param string $date  SQL expression producing the date.
1092
     * @param string $weeks SQL expression producing the number of weeks.
1093
     *
1094
     * @throws DBALException If not supported on this platform.
1095
     */
1096 49830
    public function getDateSubWeeksExpression(string $date, string $weeks) : string
1097
    {
1098 49830
        return $this->getDateArithmeticIntervalExpression($date, '-', $weeks, DateIntervalUnit::WEEK);
1099
    }
1100
1101
    /**
1102
     * Returns the SQL to add the number of given months to a date.
1103
     *
1104
     * @param string $date   SQL expression producing the date.
1105
     * @param string $months SQL expression producing the number of months.
1106
     *
1107
     * @throws DBALException If not supported on this platform.
1108
     */
1109
    public function getDateAddMonthExpression(string $date, string $months) : string
1110
    {
1111 49830
        return $this->getDateArithmeticIntervalExpression($date, '+', $months, DateIntervalUnit::MONTH);
1112
    }
1113 49830
1114
    /**
1115
     * Returns the SQL to subtract the number of given months to a date.
1116
     *
1117
     * @param string $date   SQL expression producing the date.
1118
     * @param string $months SQL expression producing the number of months.
1119
     *
1120
     * @throws DBALException If not supported on this platform.
1121
     */
1122
    public function getDateSubMonthExpression(string $date, string $months) : string
1123
    {
1124
        return $this->getDateArithmeticIntervalExpression($date, '-', $months, DateIntervalUnit::MONTH);
1125
    }
1126 49832
1127
    /**
1128 49832
     * Returns the SQL to add the number of given quarters to a date.
1129
     *
1130
     * @param string $date     SQL expression producing the date.
1131
     * @param string $quarters SQL expression producing the number of quarters.
1132
     *
1133
     * @throws DBALException If not supported on this platform.
1134
     */
1135
    public function getDateAddQuartersExpression(string $date, string $quarters) : string
1136
    {
1137
        return $this->getDateArithmeticIntervalExpression($date, '+', $quarters, DateIntervalUnit::QUARTER);
1138
    }
1139
1140
    /**
1141 49830
     * Returns the SQL to subtract the number of given quarters from a date.
1142
     *
1143 49830
     * @param string $date     SQL expression producing the date.
1144
     * @param string $quarters SQL expression producing the number of quarters.
1145
     *
1146
     * @throws DBALException If not supported on this platform.
1147
     */
1148
    public function getDateSubQuartersExpression(string $date, string $quarters) : string
1149
    {
1150
        return $this->getDateArithmeticIntervalExpression($date, '-', $quarters, DateIntervalUnit::QUARTER);
1151
    }
1152
1153
    /**
1154
     * Returns the SQL to add the number of given years to a date.
1155
     *
1156 49830
     * @param string $date  SQL expression producing the date.
1157
     * @param string $years SQL expression producing the number of years.
1158 49830
     *
1159
     * @throws DBALException If not supported on this platform.
1160
     */
1161
    public function getDateAddYearsExpression(string $date, string $years) : string
1162
    {
1163
        return $this->getDateArithmeticIntervalExpression($date, '+', $years, DateIntervalUnit::YEAR);
1164
    }
1165
1166
    /**
1167
     * Returns the SQL to subtract the number of given years from a date.
1168
     *
1169
     * @param string $date  SQL expression producing the date.
1170
     * @param string $years SQL expression producing the number of years.
1171 49830
     *
1172
     * @throws DBALException If not supported on this platform.
1173 49830
     */
1174
    public function getDateSubYearsExpression(string $date, string $years) : string
1175
    {
1176
        return $this->getDateArithmeticIntervalExpression($date, '-', $years, DateIntervalUnit::YEAR);
1177
    }
1178
1179
    /**
1180
     * Returns the SQL for a date arithmetic expression.
1181
     *
1182
     * @param string $date     SQL expression representing a date to perform the arithmetic operation on.
1183
     * @param string $operator The arithmetic operator (+ or -).
1184
     * @param string $interval SQL expression representing the value of the interval that shall be calculated
1185
     *                         into the date.
1186 49830
     * @param string $unit     The unit of the interval that shall be calculated into the date.
1187
     *                         One of the DATE_INTERVAL_UNIT_* constants.
1188 49830
     *
1189
     * @throws DBALException If not supported on this platform.
1190
     */
1191
    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

1191
    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...
Unused Code introduced by
The parameter $unit 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

1191
    protected function getDateArithmeticIntervalExpression(string $date, string $operator, string $interval, /** @scrutinizer ignore-unused */ 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...
1192
    {
1193
        throw DBALException::notSupported(__METHOD__);
1194
    }
1195
1196
    /**
1197
     * Generates the SQL expression which represents the given date interval multiplied by a number
1198
     *
1199
     * @param string $interval   SQL expression describing the interval value
1200
     * @param int    $multiplier Interval multiplier
1201 49830
     *
1202
     * @throws DBALException
1203 49830
     */
1204
    protected function multiplyInterval(string $interval, int $multiplier) : string
1205
    {
1206
        return sprintf('(%s * %d)', $interval, $multiplier);
1207
    }
1208
1209
    /**
1210
     * Returns the SQL bit AND comparison expression.
1211
     *
1212
     * @param string $value1 SQL expression producing the first value.
1213
     * @param string $value2 SQL expression producing the second value.
1214
     */
1215
    public function getBitAndComparisonExpression(string $value1, string $value2) : string
1216 49830
    {
1217
        return '(' . $value1 . ' & ' . $value2 . ')';
1218 49830
    }
1219
1220
    /**
1221
     * Returns the SQL bit OR comparison expression.
1222
     *
1223
     * @param string $value1 SQL expression producing the first value.
1224
     * @param string $value2 SQL expression producing the second value.
1225
     */
1226
    public function getBitOrComparisonExpression(string $value1, string $value2) : string
1227
    {
1228
        return '(' . $value1 . ' | ' . $value2 . ')';
1229
    }
1230
1231 49830
    /**
1232
     * Returns the FOR UPDATE expression.
1233 49830
     *
1234
     * @return string
1235
     */
1236
    public function getForUpdateSQL()
1237
    {
1238
        return 'FOR UPDATE';
1239
    }
1240
1241
    /**
1242
     * Honors that some SQL vendors such as MsSql use table hints for locking instead of the ANSI SQL FOR UPDATE specification.
1243
     *
1244
     * @param string   $fromClause The FROM clause to append the hint for the given lock mode to.
1245
     * @param int|null $lockMode   One of the Doctrine\DBAL\LockMode::* constants. If null is given, nothing will
1246 49830
     *                             be appended to the FROM clause.
1247
     *
1248 49830
     * @return string
1249
     */
1250
    public function appendLockHint($fromClause, $lockMode)
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

1250
    public function appendLockHint($fromClause, /** @scrutinizer ignore-unused */ $lockMode)

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...
1251
    {
1252
        return $fromClause;
1253
    }
1254
1255
    /**
1256
     * Returns the SQL snippet to append to any SELECT statement which locks rows in shared read lock.
1257
     *
1258
     * This defaults to the ANSI SQL "FOR UPDATE", which is an exclusive lock (Write). Some database
1259
     * vendors allow to lighten this constraint up to be a real read lock.
1260
     *
1261 49830
     * @return string
1262
     */
1263 49830
    public function getReadLockSQL()
1264
    {
1265
        return $this->getForUpdateSQL();
1266
    }
1267
1268
    /**
1269
     * Returns the SQL snippet to append to any SELECT statement which obtains an exclusive lock on the rows.
1270
     *
1271
     * The semantics of this lock mode should equal the SELECT .. FOR UPDATE of the ANSI SQL standard.
1272
     *
1273
     * @return string
1274
     */
1275
    public function getWriteLockSQL()
1276
    {
1277
        return $this->getForUpdateSQL();
1278
    }
1279
1280
    /**
1281
     * Returns the SQL snippet to drop an existing database.
1282
     *
1283
     * @param string $database The name of the database that should be dropped.
1284
     *
1285
     * @return string
1286
     */
1287
    public function getDropDatabaseSQL($database)
1288
    {
1289
        return 'DROP DATABASE ' . $database;
1290
    }
1291
1292 49481
    /**
1293
     * Returns the SQL snippet to drop an existing table.
1294 49481
     *
1295
     * @param Table|string $table
1296
     *
1297
     * @return string
1298
     *
1299
     * @throws InvalidArgumentException
1300
     */
1301
    public function getDropTableSQL($table)
1302
    {
1303
        $tableArg = $table;
1304
1305 49480
        if ($table instanceof Table) {
1306
            $table = $table->getQuotedName($this);
1307 49480
        }
1308
1309
        if (! is_string($table)) {
0 ignored issues
show
introduced by
The condition is_string($table) is always true.
Loading history...
1310
            throw new InvalidArgumentException('getDropTableSQL() expects $table parameter to be string or \Doctrine\DBAL\Schema\Table.');
1311
        }
1312
1313
        if ($this->_eventManager !== null && $this->_eventManager->hasListeners(Events::onSchemaDropTable)) {
1314
            $eventArgs = new SchemaDropTableEventArgs($tableArg, $this);
1315 38749
            $this->_eventManager->dispatchEvent(Events::onSchemaDropTable, $eventArgs);
1316
1317 38749
            if ($eventArgs->isDefaultPrevented()) {
1318
                $sql = $eventArgs->getSql();
1319
1320
                if ($sql === null) {
1321
                    throw new UnexpectedValueException('Default implementation of DROP TABLE was overridden with NULL');
1322
                }
1323
1324
                return $sql;
1325
            }
1326
        }
1327
1328
        return 'DROP TABLE ' . $table;
1329 40708
    }
1330
1331 40708
    /**
1332
     * Returns the SQL to safely drop a temporary table WITHOUT implicitly committing an open transaction.
1333
     *
1334
     * @param Table|string $table
1335
     *
1336
     * @return string
1337
     */
1338
    public function getDropTemporaryTableSQL($table)
1339
    {
1340
        return $this->getDropTableSQL($table);
1341
    }
1342
1343
    /**
1344
     * Returns the SQL to drop an index from a table.
1345
     *
1346
     * @param Index|string $index
1347
     * @param Table|string $table
1348
     *
1349
     * @return string
1350
     *
1351
     * @throws InvalidArgumentException
1352
     */
1353
    public function getDropIndexSQL($index, $table = null)
1354 44584
    {
1355
        if ($index instanceof Index) {
1356 44584
            $index = $index->getQuotedName($this);
1357
        } elseif (! is_string($index)) {
0 ignored issues
show
introduced by
The condition is_string($index) is always true.
Loading history...
1358
            throw new InvalidArgumentException('AbstractPlatform::getDropIndexSQL() expects $index parameter to be string or \Doctrine\DBAL\Schema\Index.');
1359
        }
1360
1361
        return 'DROP INDEX ' . $index;
1362
    }
1363
1364
    /**
1365
     * Returns the SQL to drop a constraint.
1366 34803
     *
1367
     * @param Constraint|string $constraint
1368 34803
     * @param Table|string      $table
1369
     *
1370
     * @return string
1371
     */
1372
    public function getDropConstraintSQL($constraint, $table)
1373
    {
1374
        if (! $constraint instanceof Constraint) {
1375
            $constraint = new Identifier($constraint);
1376
        }
1377
1378
        if (! $table instanceof Table) {
1379
            $table = new Identifier($table);
1380 51070
        }
1381
1382 51070
        $constraint = $constraint->getQuotedName($this);
1383
        $table      = $table->getQuotedName($this);
1384 51070
1385 7011
        return 'ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $constraint;
1386
    }
1387
1388 51070
    /**
1389
     * Returns the SQL to drop a foreign key.
1390
     *
1391
     * @param ForeignKeyConstraint|string $foreignKey
1392 51070
     * @param Table|string                $table
1393 43953
     *
1394 43953
     * @return string
1395
     */
1396 43953
    public function getDropForeignKeySQL($foreignKey, $table)
1397
    {
1398
        if (! $foreignKey instanceof ForeignKeyConstraint) {
1399
            $foreignKey = new Identifier($foreignKey);
1400
        }
1401
1402
        if (! $table instanceof Table) {
1403
            $table = new Identifier($table);
1404
        }
1405
1406
        $foreignKey = $foreignKey->getQuotedName($this);
1407 51070
        $table      = $table->getQuotedName($this);
1408
1409
        return 'ALTER TABLE ' . $table . ' DROP FOREIGN KEY ' . $foreignKey;
1410
    }
1411
1412
    /**
1413
     * Returns the SQL statement(s) to create a table with the specified name, columns and constraints
1414
     * on this platform.
1415
     *
1416
     * @param int $createFlags
1417 19387
     *
1418
     * @return string[] The sequence of SQL statements.
1419 19387
     *
1420
     * @throws DBALException
1421
     * @throws InvalidArgumentException
1422
     */
1423
    public function getCreateTableSQL(Table $table, $createFlags = self::CREATE_INDEXES)
1424
    {
1425
        if (! is_int($createFlags)) {
0 ignored issues
show
introduced by
The condition is_int($createFlags) is always true.
Loading history...
1426
            throw new InvalidArgumentException('Second argument of AbstractPlatform::getCreateTableSQL() has to be integer.');
1427
        }
1428
1429
        if (count($table->getColumns()) === 0) {
1430
            throw DBALException::noColumnsSpecifiedForTable($table->getName());
1431
        }
1432 34622
1433
        $tableName                    = $table->getQuotedName($this);
1434 34622
        $options                      = $table->getOptions();
1435 34597
        $options['uniqueConstraints'] = [];
1436 15974
        $options['indexes']           = [];
1437
        $options['primary']           = [];
1438
1439
        if (($createFlags & self::CREATE_INDEXES) > 0) {
1440 34622
            foreach ($table->getIndexes() as $index) {
1441
                /** @var $index Index */
1442
                if (! $index->isPrimary()) {
1443
                    $options['indexes'][$index->getQuotedName($this)] = $index;
1444
1445
                    continue;
1446
                }
1447
1448
                $options['primary']       = $index->getQuotedColumns($this);
1449
                $options['primary_index'] = $index;
1450
            }
1451 44490
1452
            foreach ($table->getUniqueConstraints() as $uniqueConstraint) {
1453 44490
                /** @var UniqueConstraint $uniqueConstraint */
1454 43354
                $options['uniqueConstraints'][$uniqueConstraint->getQuotedName($this)] = $uniqueConstraint;
1455
            }
1456
        }
1457 44490
1458 44490
        if (($createFlags & self::CREATE_FOREIGNKEYS) > 0) {
1459
            $options['foreignKeys'] = [];
1460
1461 44490
            foreach ($table->getForeignKeys() as $fkConstraint) {
1462 44490
                $options['foreignKeys'][] = $fkConstraint;
1463
            }
1464 44490
        }
1465
1466
        $columnSql = [];
1467
        $columns   = [];
1468
1469
        foreach ($table->getColumns() as $column) {
1470
            if ($this->_eventManager !== null && $this->_eventManager->hasListeners(Events::onSchemaCreateTableColumn)) {
1471
                $eventArgs = new SchemaCreateTableColumnEventArgs($column, $table, $this);
1472
                $this->_eventManager->dispatchEvent(Events::onSchemaCreateTableColumn, $eventArgs);
1473
1474
                $columnSql = array_merge($columnSql, $eventArgs->getSql());
1475 46041
1476
                if ($eventArgs->isDefaultPrevented()) {
1477 46041
                    continue;
1478 43250
                }
1479
            }
1480
1481 46041
            $columnData            = $column->toArray();
1482 46041
            $columnData['name']    = $column->getQuotedName($this);
1483
            $columnData['version'] = $column->hasPlatformOption('version') ? $column->getPlatformOption('version') : false;
1484
            $columnData['comment'] = $this->getColumnComment($column);
1485 46041
1486 46041
            if ($columnData['type'] instanceof Types\StringType && $columnData['length'] === null) {
1487
                $columnData['length'] = 255;
1488 46041
            }
1489
1490
            if (in_array($column->getName(), $options['primary'])) {
1491
                $columnData['primary'] = true;
1492
            }
1493
1494
            $columns[$columnData['name']] = $columnData;
1495
        }
1496
1497
        if ($this->_eventManager !== null && $this->_eventManager->hasListeners(Events::onSchemaCreateTable)) {
1498
            $eventArgs = new SchemaCreateTableEventArgs($table, $columns, $options, $this);
1499
            $this->_eventManager->dispatchEvent(Events::onSchemaCreateTable, $eventArgs);
1500
1501
            if ($eventArgs->isDefaultPrevented()) {
1502 51259
                return array_merge($eventArgs->getSql(), $columnSql);
1503
            }
1504 51259
        }
1505
1506
        $sql = $this->_getCreateTableSQL($tableName, $columns, $options);
1507
        if ($this->supportsCommentOnStatement()) {
1508 51259
            foreach ($table->getColumns() as $column) {
1509 44241
                $comment = $this->getColumnComment($column);
1510
1511
                if ($comment === null || $comment === '') {
1512 51247
                    continue;
1513 51247
                }
1514 51247
1515 51247
                $sql[] = $this->getCommentOnColumnSQL($tableName, $column->getQuotedName($this), $comment);
1516 51247
            }
1517
        }
1518 51247
1519 51233
        return array_merge($sql, $columnSql);
1520
    }
1521 51129
1522 49668
    /**
1523
     * @param string      $tableName
1524 49668
     * @param string      $columnName
1525
     * @param string|null $comment
1526
     *
1527 51081
     * @return string
1528 51081
     */
1529
    public function getCommentOnColumnSQL($tableName, $columnName, $comment)
1530
    {
1531 51233
        $tableName  = new Identifier($tableName);
1532
        $columnName = new Identifier($columnName);
1533
1534
        return sprintf(
1535
            'COMMENT ON COLUMN %s.%s IS %s',
1536
            $tableName->getQuotedName($this),
1537 51247
            $columnName->getQuotedName($this),
1538 51094
            $this->quoteStringLiteral((string) $comment)
1539
        );
1540 51094
    }
1541 49557
1542
    /**
1543
     * Returns the SQL to create inline comment on a column.
1544
     *
1545 51247
     * @param string $comment
1546 51247
     *
1547
     * @return string
1548 51247
     *
1549 51247
     * @throws DBALException If not supported on this platform.
1550 43977
     */
1551 43977
    public function getInlineColumnCommentSQL($comment)
1552
    {
1553 43977
        if (! $this->supportsInlineColumnComments()) {
1554
            throw DBALException::notSupported(__METHOD__);
1555 43977
        }
1556
1557
        return 'COMMENT ' . $this->quoteStringLiteral($comment);
1558
    }
1559
1560 51247
    /**
1561 51247
     * Returns the SQL used to create a table.
1562 51247
     *
1563 51247
     * @param string    $tableName
1564
     * @param mixed[][] $columns
1565 51247
     * @param mixed[]   $options
1566 50743
     *
1567
     * @return string[]
1568
     */
1569 51247
    protected function _getCreateTableSQL($tableName, array $columns, array $options = [])
1570 51067
    {
1571
        $columnListSql = $this->getColumnDeclarationListSQL($columns);
1572
1573 51247
        if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) {
1574
            foreach ($options['uniqueConstraints'] as $name => $definition) {
1575
                $columnListSql .= ', ' . $this->getUniqueConstraintDeclarationSQL($name, $definition);
1576 51247
            }
1577 43977
        }
1578 43977
1579
        if (isset($options['primary']) && ! empty($options['primary'])) {
1580 43977
            $columnListSql .= ', PRIMARY KEY(' . implode(', ', array_unique(array_values($options['primary']))) . ')';
1581
        }
1582
1583
        if (isset($options['indexes']) && ! empty($options['indexes'])) {
1584
            foreach ($options['indexes'] as $index => $definition) {
1585 51247
                $columnListSql .= ', ' . $this->getIndexDeclarationSQL($index, $definition);
1586 51247
            }
1587 47364
        }
1588 47364
1589
        $query = 'CREATE TABLE ' . $tableName . ' (' . $columnListSql;
1590 47364
1591 47352
        $check = $this->getCheckDeclarationSQL($columns);
1592
        if (! empty($check)) {
1593
            $query .= ', ' . $check;
1594 45209
        }
1595
        $query .= ')';
1596
1597
        $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...
1598 51247
1599
        if (isset($options['foreignKeys'])) {
1600
            foreach ((array) $options['foreignKeys'] as $definition) {
1601
                $sql[] = $this->getCreateForeignKeySQL($definition, $tableName);
1602
            }
1603
        }
1604
1605
        return $sql;
1606
    }
1607
1608 44128
    /**
1609
     * @return string
1610 44128
     */
1611 44128
    public function getCreateTemporaryTableSnippetSQL()
1612
    {
1613 44128
        return 'CREATE TEMPORARY TABLE';
1614 63
    }
1615 44128
1616 44128
    /**
1617 44128
     * Returns the SQL to create a sequence on this platform.
1618
     *
1619
     * @return string
1620
     *
1621
     * @throws DBALException If not supported on this platform.
1622
     */
1623
    public function getCreateSequenceSQL(Sequence $sequence)
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

1623
    public function getCreateSequenceSQL(/** @scrutinizer ignore-unused */ Sequence $sequence)

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...
1624
    {
1625
        throw DBALException::notSupported(__METHOD__);
1626
    }
1627
1628
    /**
1629
     * Returns the SQL to change a sequence on this platform.
1630 46211
     *
1631
     * @return string
1632 46211
     *
1633 43133
     * @throws DBALException If not supported on this platform.
1634
     */
1635
    public function getAlterSequenceSQL(Sequence $sequence)
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

1635
    public function getAlterSequenceSQL(/** @scrutinizer ignore-unused */ Sequence $sequence)

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...
1636 45181
    {
1637
        throw DBALException::notSupported(__METHOD__);
1638
    }
1639
1640
    /**
1641
     * Returns the SQL to create a constraint on a table on this platform.
1642
     *
1643
     * @param Table|string $table
1644
     *
1645
     * @return string
1646
     *
1647
     * @throws InvalidArgumentException
1648 45918
     */
1649
    public function getCreateConstraintSQL(Constraint $constraint, $table)
1650 45918
    {
1651
        if ($table instanceof Table) {
1652 45918
            $table = $table->getQuotedName($this);
1653
        }
1654
1655
        $query = 'ALTER TABLE ' . $table . ' ADD CONSTRAINT ' . $constraint->getQuotedName($this);
1656
1657
        $columnList = '(' . implode(', ', $constraint->getQuotedColumns($this)) . ')';
1658 45918
1659 45855
        $referencesClause = '';
1660
        if ($constraint instanceof Index) {
1661
            if ($constraint->isPrimary()) {
1662 45918
                $query .= ' PRIMARY KEY';
1663
            } elseif ($constraint->isUnique()) {
1664
                $query .= ' UNIQUE';
1665
            } else {
1666
                throw new InvalidArgumentException(
1667
                    'Can only create primary or unique constraints, no common indexes with getCreateConstraintSQL().'
1668 45918
                );
1669
            }
1670 45918
        } elseif ($constraint instanceof ForeignKeyConstraint) {
1671 45918
            $query .= ' FOREIGN KEY';
1672 45528
1673
            $referencesClause = ' REFERENCES ' . $constraint->getQuotedForeignTableName($this) .
1674 45918
                ' (' . implode(', ', $constraint->getQuotedForeignColumns($this)) . ')';
1675
        }
1676 45918
        $query .= ' ' . $columnList . $referencesClause;
1677
1678 45918
        return $query;
1679 45866
    }
1680 45654
1681
    /**
1682
     * Returns the SQL to create an index on a table on this platform.
1683
     *
1684 45918
     * @param Table|string $table The name of the table on which the index is to be created.
1685
     *
1686
     * @return string
1687
     *
1688
     * @throws InvalidArgumentException
1689
     */
1690 40643
    public function getCreateIndexSQL(Index $index, $table)
1691
    {
1692 40643
        if ($table instanceof Table) {
1693
            $table = $table->getQuotedName($this);
1694
        }
1695
        $name    = $index->getQuotedName($this);
1696
        $columns = $index->getColumns();
1697
1698
        if (count($columns) === 0) {
1699
            throw new InvalidArgumentException("Incomplete definition. 'columns' required.");
1700
        }
1701
1702
        if ($index->isPrimary()) {
1703
            return $this->getCreatePrimaryKeySQL($index, $table);
1704
        }
1705
1706
        $query  = 'CREATE ' . $this->getCreateIndexSQLFlags($index) . 'INDEX ' . $name . ' ON ' . $table;
1707
        $query .= ' (' . $this->getIndexFieldDeclarationListSQL($index) . ')' . $this->getPartialIndexSQL($index);
1708
1709
        return $query;
1710
    }
1711
1712
    /**
1713
     * Adds condition for partial index.
1714
     *
1715
     * @return string
1716
     */
1717
    protected function getPartialIndexSQL(Index $index)
1718
    {
1719
        if ($this->supportsPartialIndexes() && $index->hasOption('where')) {
1720
            return ' WHERE ' . $index->getOption('where');
1721
        }
1722
1723
        return '';
1724
    }
1725
1726
    /**
1727
     * Adds additional flags for index generation.
1728 44122
     *
1729
     * @return string
1730 44122
     */
1731
    protected function getCreateIndexSQLFlags(Index $index)
1732
    {
1733
        return $index->isUnique() ? 'UNIQUE ' : '';
1734 44122
    }
1735
1736 44122
    /**
1737
     * Returns the SQL to create an unnamed primary key constraint.
1738 44122
     *
1739 44122
     * @param Table|string $table
1740 44122
     *
1741 44122
     * @return string
1742 44095
     */
1743 44095
    public function getCreatePrimaryKeySQL(Index $index, $table)
1744
    {
1745
        if ($table instanceof Table) {
1746 44122
            $table = $table->getQuotedName($this);
1747
        }
1748
1749 44095
        return 'ALTER TABLE ' . $table . ' ADD PRIMARY KEY (' . $this->getIndexFieldDeclarationListSQL($index) . ')';
1750 44095
    }
1751
1752 44095
    /**
1753 44095
     * Returns the SQL to create a named schema.
1754
     *
1755 44122
     * @param string $schemaName
1756
     *
1757 44122
     * @return string
1758
     *
1759
     * @throws DBALException If not supported on this platform.
1760
     */
1761
    public function getCreateSchemaSQL($schemaName)
1762
    {
1763
        throw DBALException::notSupported(__METHOD__);
1764
    }
1765
1766
    /**
1767
     * Quotes a string so that it can be safely used as a table or column name,
1768
     * even if it is a reserved word of the platform. This also detects identifier
1769 48553
     * chains separated by dot and quotes them independently.
1770
     *
1771 48553
     * NOTE: Just because you CAN use quoted identifiers doesn't mean
1772 47892
     * you SHOULD use them. In general, they end up causing way more
1773
     * problems than they solve.
1774 48553
     *
1775 48553
     * @param string $str The identifier name to be quoted.
1776
     *
1777 48553
     * @return string The quoted identifier string.
1778
     */
1779
    public function quoteIdentifier($str)
1780
    {
1781 48553
        if (strpos($str, '.') !== false) {
1782 42280
            $parts = array_map([$this, 'quoteSingleIdentifier'], explode('.', $str));
1783
1784
            return implode('.', $parts);
1785 48535
        }
1786 48535
1787
        return $this->quoteSingleIdentifier($str);
1788 48535
    }
1789
1790
    /**
1791
     * Quotes a single identifier (no dot chain separation).
1792
     *
1793
     * @param string $str The identifier name to be quoted.
1794
     *
1795
     * @return string The quoted identifier string.
1796 49734
     */
1797
    public function quoteSingleIdentifier($str)
1798 49734
    {
1799 32155
        $c = $this->getIdentifierQuoteCharacter();
1800
1801
        return $c . str_replace($c, $c . $c, $str) . $c;
1802 49731
    }
1803
1804
    /**
1805
     * Returns the SQL to create a new foreign key.
1806
     *
1807
     * @param ForeignKeyConstraint $foreignKey The foreign key constraint.
1808
     * @param Table|string         $table      The name of the table on which the foreign key is to be created.
1809
     *
1810 46939
     * @return string
1811
     */
1812 46939
    public function getCreateForeignKeySQL(ForeignKeyConstraint $foreignKey, $table)
1813
    {
1814
        if ($table instanceof Table) {
1815
            $table = $table->getQuotedName($this);
1816
        }
1817
1818
        return 'ALTER TABLE ' . $table . ' ADD ' . $this->getForeignKeyDeclarationSQL($foreignKey);
1819
    }
1820
1821
    /**
1822 45448
     * Gets the SQL statements for altering an existing table.
1823
     *
1824 45448
     * This method returns an array of SQL statements, since some platforms need several statements.
1825
     *
1826
     * @return string[]
1827
     *
1828 45448
     * @throws DBALException If not supported on this platform.
1829
     */
1830
    public function getAlterTableSQL(TableDiff $diff)
1831
    {
1832
        throw DBALException::notSupported(__METHOD__);
1833
    }
1834
1835
    /**
1836
     * @param mixed[] $columnSql
1837
     *
1838
     * @return bool
1839
     */
1840 43516
    protected function onSchemaAlterTableAddColumn(Column $column, TableDiff $diff, &$columnSql)
1841
    {
1842 43516
        if ($this->_eventManager === null) {
1843
            return false;
1844
        }
1845
1846
        if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTableAddColumn)) {
1847
            return false;
1848
        }
1849
1850
        $eventArgs = new SchemaAlterTableAddColumnEventArgs($column, $diff, $this);
1851
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableAddColumn, $eventArgs);
1852
1853
        $columnSql = array_merge($columnSql, $eventArgs->getSql());
1854
1855
        return $eventArgs->isDefaultPrevented();
1856
    }
1857
1858 50917
    /**
1859
     * @param string[] $columnSql
1860 50917
     *
1861 45464
     * @return bool
1862
     */
1863 45464
    protected function onSchemaAlterTableRemoveColumn(Column $column, TableDiff $diff, &$columnSql)
1864
    {
1865
        if ($this->_eventManager === null) {
1866 50917
            return false;
1867
        }
1868
1869
        if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTableRemoveColumn)) {
1870
            return false;
1871
        }
1872
1873
        $eventArgs = new SchemaAlterTableRemoveColumnEventArgs($column, $diff, $this);
1874
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableRemoveColumn, $eventArgs);
1875
1876 50555
        $columnSql = array_merge($columnSql, $eventArgs->getSql());
1877
1878 50555
        return $eventArgs->isDefaultPrevented();
1879
    }
1880 50555
1881
    /**
1882
     * @param string[] $columnSql
1883
     *
1884
     * @return bool
1885
     */
1886
    protected function onSchemaAlterTableChangeColumn(ColumnDiff $columnDiff, TableDiff $diff, &$columnSql)
1887
    {
1888
        if ($this->_eventManager === null) {
1889
            return false;
1890
        }
1891 49509
1892
        if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTableChangeColumn)) {
1893 49509
            return false;
1894 2857
        }
1895
1896
        $eventArgs = new SchemaAlterTableChangeColumnEventArgs($columnDiff, $diff, $this);
1897 49509
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableChangeColumn, $eventArgs);
1898
1899
        $columnSql = array_merge($columnSql, $eventArgs->getSql());
1900
1901
        return $eventArgs->isDefaultPrevented();
1902
    }
1903
1904
    /**
1905
     * @param string   $oldColumnName
1906
     * @param string[] $columnSql
1907
     *
1908
     * @return bool
1909
     */
1910
    protected function onSchemaAlterTableRenameColumn($oldColumnName, Column $column, TableDiff $diff, &$columnSql)
1911
    {
1912
        if ($this->_eventManager === null) {
1913
            return false;
1914
        }
1915
1916
        if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTableRenameColumn)) {
1917
            return false;
1918
        }
1919 48687
1920
        $eventArgs = new SchemaAlterTableRenameColumnEventArgs($oldColumnName, $column, $diff, $this);
1921 48687
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableRenameColumn, $eventArgs);
1922 44069
1923
        $columnSql = array_merge($columnSql, $eventArgs->getSql());
1924
1925 48631
        return $eventArgs->isDefaultPrevented();
1926 48619
    }
1927
1928
    /**
1929 43929
     * @param string[] $sql
1930 43929
     *
1931
     * @return bool
1932 43929
     */
1933
    protected function onSchemaAlterTable(TableDiff $diff, &$sql)
1934 43929
    {
1935
        if ($this->_eventManager === null) {
1936
            return false;
1937
        }
1938
1939
        if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTable)) {
1940
            return false;
1941
        }
1942 47896
1943
        $eventArgs = new SchemaAlterTableEventArgs($diff, $this);
1944 47896
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTable, $eventArgs);
1945 44051
1946
        $sql = array_merge($sql, $eventArgs->getSql());
1947
1948 47858
        return $eventArgs->isDefaultPrevented();
1949 47846
    }
1950
1951
    /**
1952 43929
     * @return string[]
1953 43929
     */
1954
    protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff)
1955 43929
    {
1956
        $tableName = $diff->getName($this)->getQuotedName($this);
1957 43929
1958
        $sql = [];
1959
        if ($this->supportsForeignKeyConstraints()) {
1960
            foreach ($diff->removedForeignKeys as $foreignKey) {
1961
                $sql[] = $this->getDropForeignKeySQL($foreignKey, $tableName);
1962
            }
1963
            foreach ($diff->changedForeignKeys as $foreignKey) {
1964
                $sql[] = $this->getDropForeignKeySQL($foreignKey, $tableName);
1965 48584
            }
1966
        }
1967 48584
1968 45327
        foreach ($diff->removedIndexes as $index) {
1969
            $sql[] = $this->getDropIndexSQL($index, $tableName);
1970
        }
1971 48472
        foreach ($diff->changedIndexes as $index) {
1972 48460
            $sql[] = $this->getDropIndexSQL($index, $tableName);
1973
        }
1974
1975 43929
        return $sql;
1976 43929
    }
1977
1978 43929
    /**
1979
     * @return string[]
1980 43929
     */
1981
    protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff)
1982
    {
1983
        $sql     = [];
1984
        $newName = $diff->getNewName();
1985
1986
        if ($newName !== false) {
1987
            $tableName = $newName->getQuotedName($this);
1988
        } else {
1989 47651
            $tableName = $diff->getName($this)->getQuotedName($this);
1990
        }
1991 47651
1992 43356
        if ($this->supportsForeignKeyConstraints()) {
1993
            foreach ($diff->addedForeignKeys as $foreignKey) {
1994
                $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableName);
1995 47612
            }
1996 45771
1997
            foreach ($diff->changedForeignKeys as $foreignKey) {
1998
                $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableName);
1999 43929
            }
2000 43929
        }
2001
2002 43929
        foreach ($diff->addedIndexes as $index) {
2003
            $sql[] = $this->getCreateIndexSQL($index, $tableName);
2004 43929
        }
2005
2006
        foreach ($diff->changedIndexes as $index) {
2007
            $sql[] = $this->getCreateIndexSQL($index, $tableName);
2008
        }
2009
2010
        foreach ($diff->renamedIndexes as $oldIndexName => $index) {
2011
            $oldIndexName = new Identifier($oldIndexName);
2012 49123
            $sql          = array_merge(
2013
                $sql,
2014 49123
                $this->getRenameIndexSQL($oldIndexName->getQuotedName($this), $index, $tableName)
2015 45451
            );
2016
        }
2017
2018 48887
        return $sql;
2019 48875
    }
2020
2021
    /**
2022 43929
     * Returns the SQL for renaming an index on a table.
2023 43929
     *
2024
     * @param string $oldIndexName The name of the index to rename from.
2025 43929
     * @param Index  $index        The definition of the index to rename to.
2026
     * @param string $tableName    The table to rename the given index on.
2027 43929
     *
2028
     * @return string[] The sequence of SQL statements for renaming the given index.
2029
     */
2030
    protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName)
2031
    {
2032
        return [
2033 48990
            $this->getDropIndexSQL($oldIndexName, $tableName),
2034
            $this->getCreateIndexSQL($index, $tableName),
2035 48990
        ];
2036
    }
2037 48990
2038 48990
    /**
2039 48990
     * Common code for alter table statement generation that updates the changed Index and Foreign Key definitions.
2040 47533
     *
2041
     * @return string[]
2042 48990
     */
2043 43067
    protected function _getAlterTableIndexForeignKeySQL(TableDiff $diff)
2044
    {
2045
        return array_merge($this->getPreAlterTableIndexForeignKeySQL($diff), $this->getPostAlterTableIndexForeignKeySQL($diff));
2046
    }
2047 48990
2048 48027
    /**
2049
     * Gets declaration of a number of fields in bulk.
2050 48990
     *
2051 47600
     * @param mixed[][] $fields A multidimensional associative array.
2052
     *                          The first dimension determines the field name, while the second
2053
     *                          dimension is keyed with the name of the properties
2054 48990
     *                          of the field being declared as array indexes. Currently, the types
2055
     *                          of supported field properties are as follows:
2056
     *
2057
     *      length
2058
     *          Integer value that determines the maximum length of the text
2059
     *          field. If this argument is missing the field should be
2060 48990
     *          declared to have the longest length allowed by the DBMS.
2061
     *
2062 48990
     *      default
2063 48990
     *          Text value to be used as default for this field.
2064
     *
2065 48990
     *      notnull
2066 44393
     *          Boolean flag that indicates whether this field is constrained
2067
     *          to not be set to null.
2068 48967
     *      charset
2069
     *          Text value with the default CHARACTER SET for this field.
2070
     *      collation
2071 48990
     *          Text value with the default COLLATION for this field.
2072 48990
     *      unique
2073 47631
     *          unique constraint
2074
     *
2075
     * @return string
2076 48990
     */
2077 43067
    public function getColumnDeclarationListSQL(array $fields)
2078
    {
2079
        $queryFields = [];
2080
2081 48990
        foreach ($fields as $fieldName => $field) {
2082 47594
            $queryFields[] = $this->getColumnDeclarationSQL($fieldName, $field);
2083
        }
2084
2085 48990
        return implode(', ', $queryFields);
2086 47600
    }
2087
2088
    /**
2089 48990
     * Obtains DBMS specific SQL code portion needed to declare a generic type
2090 47685
     * field to be used in statements like CREATE TABLE.
2091 47685
     *
2092 47685
     * @param string  $name  The name the field to be declared.
2093 47685
     * @param mixed[] $field An associative array with the name of the properties
2094
     *                       of the field being declared as array indexes. Currently, the types
2095
     *                       of supported field properties are as follows:
2096
     *
2097 48990
     *      length
2098
     *          Integer value that determines the maximum length of the text
2099
     *          field. If this argument is missing the field should be
2100
     *          declared to have the longest length allowed by the DBMS.
2101
     *
2102
     *      default
2103
     *          Text value to be used as default for this field.
2104
     *
2105
     *      notnull
2106
     *          Boolean flag that indicates whether this field is constrained
2107
     *          to not be set to null.
2108
     *      charset
2109 43349
     *          Text value with the default CHARACTER SET for this field.
2110
     *      collation
2111
     *          Text value with the default COLLATION for this field.
2112 43349
     *      unique
2113 43349
     *          unique constraint
2114
     *      check
2115
     *          column check constraint
2116
     *      columnDefinition
2117
     *          a string that defines the complete column
2118
     *
2119
     * @return string DBMS specific SQL code portion that should be used to declare the column.
2120
     */
2121
    public function getColumnDeclarationSQL($name, array $field)
2122
    {
2123
        if (isset($field['columnDefinition'])) {
2124
            $columnDef = $this->getCustomTypeDeclarationSQL($field);
2125
        } else {
2126
            $default = $this->getDefaultValueDeclarationSQL($field);
2127
2128
            $charset = isset($field['charset']) && $field['charset'] ?
2129
                ' ' . $this->getColumnCharsetDeclarationSQL($field['charset']) : '';
2130
2131
            $collation = isset($field['collation']) && $field['collation'] ?
2132
                ' ' . $this->getColumnCollationDeclarationSQL($field['collation']) : '';
2133
2134
            $notnull = isset($field['notnull']) && $field['notnull'] ? ' NOT NULL' : '';
2135
2136
            $unique = isset($field['unique']) && $field['unique'] ?
2137
                ' ' . $this->getUniqueFieldDeclarationSQL() : '';
2138
2139
            $check = isset($field['check']) && $field['check'] ?
2140
                ' ' . $field['check'] : '';
2141
2142
            $typeDecl  = $field['type']->getSQLDeclaration($field, $this);
2143
            $columnDef = $typeDecl . $charset . $default . $notnull . $unique . $check . $collation;
2144
2145
            if ($this->supportsInlineColumnComments() && isset($field['comment']) && $field['comment'] !== '') {
2146
                $columnDef .= ' ' . $this->getInlineColumnCommentSQL($field['comment']);
2147
            }
2148
        }
2149
2150
        return $name . ' ' . $columnDef;
2151
    }
2152
2153
    /**
2154
     * Returns the SQL snippet that declares a floating point column of arbitrary precision.
2155
     *
2156 51247
     * @param mixed[] $columnDef
2157
     *
2158 51247
     * @return string
2159
     */
2160 51247
    public function getDecimalTypeDeclarationSQL(array $columnDef)
2161 51247
    {
2162
        $columnDef['precision'] = ! isset($columnDef['precision']) || empty($columnDef['precision'])
2163
            ? 10 : $columnDef['precision'];
2164 51247
        $columnDef['scale']     = ! isset($columnDef['scale']) || empty($columnDef['scale'])
2165
            ? 0 : $columnDef['scale'];
2166
2167
        return 'NUMERIC(' . $columnDef['precision'] . ', ' . $columnDef['scale'] . ')';
2168
    }
2169
2170
    /**
2171
     * Obtains DBMS specific SQL code portion needed to set a default value
2172
     * declaration to be used in statements like CREATE TABLE.
2173
     *
2174
     * @param mixed[] $field The field definition array.
2175
     *
2176
     * @return string DBMS specific SQL code portion needed to set a default value.
2177
     */
2178
    public function getDefaultValueDeclarationSQL($field)
2179
    {
2180
        if (! isset($field['default'])) {
2181
            return empty($field['notnull']) ? ' DEFAULT NULL' : '';
2182
        }
2183
2184
        $default = $field['default'];
2185
2186
        if (! isset($field['type'])) {
2187
            return " DEFAULT '" . $default . "'";
2188
        }
2189
2190
        $type = $field['type'];
2191
2192
        if ($type instanceof Types\PhpIntegerMappingType) {
2193
            return ' DEFAULT ' . $default;
2194
        }
2195
2196
        if ($type instanceof Types\PhpDateTimeMappingType && $default === $this->getCurrentTimestampSQL()) {
2197
            return ' DEFAULT ' . $this->getCurrentTimestampSQL();
2198
        }
2199
2200 50807
        if ($type instanceof Types\TimeType && $default === $this->getCurrentTimeSQL()) {
2201
            return ' DEFAULT ' . $this->getCurrentTimeSQL();
2202 50807
        }
2203 46147
2204
        if ($type instanceof Types\DateType && $default === $this->getCurrentDateSQL()) {
2205 50798
            return ' DEFAULT ' . $this->getCurrentDateSQL();
2206
        }
2207 50798
2208 50798
        if ($type instanceof Types\BooleanType) {
2209
            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

2209
            return " DEFAULT '" . /** @scrutinizer ignore-type */ $this->convertBooleans($default) . "'";
Loading history...
2210 50798
        }
2211 50798
2212
        return " DEFAULT '" . $default . "'";
2213 50798
    }
2214
2215 50798
    /**
2216 50798
     * Obtains DBMS specific SQL code portion needed to set a CHECK constraint
2217
     * declaration to be used in statements like CREATE TABLE.
2218 50798
     *
2219 50798
     * @param string[]|mixed[][] $definition The check definition.
2220
     *
2221 50798
     * @return string DBMS specific SQL code portion needed to set a CHECK constraint.
2222 50798
     */
2223
    public function getCheckDeclarationSQL(array $definition)
2224 50798
    {
2225 45458
        $constraints = [];
2226
        foreach ($definition as $field => $def) {
2227
            if (is_string($def)) {
2228
                $constraints[] = 'CHECK (' . $def . ')';
2229 50807
            } else {
2230
                if (isset($def['min'])) {
2231
                    $constraints[] = 'CHECK (' . $field . ' >= ' . $def['min'] . ')';
2232
                }
2233
2234
                if (isset($def['max'])) {
2235
                    $constraints[] = 'CHECK (' . $field . ' <= ' . $def['max'] . ')';
2236
                }
2237
            }
2238
        }
2239 48402
2240
        return implode(', ', $constraints);
2241 48402
    }
2242 48402
2243 48402
    /**
2244 48402
     * Obtains DBMS specific SQL code portion needed to set a unique
2245
     * constraint declaration to be used in statements like CREATE TABLE.
2246 48402
     *
2247
     * @param string           $name       The name of the unique constraint.
2248
     * @param UniqueConstraint $constraint The unique constraint definition.
2249
     *
2250
     * @return string DBMS specific SQL code portion needed to set a constraint.
2251
     *
2252
     * @throws InvalidArgumentException
2253
     */
2254
    public function getUniqueConstraintDeclarationSQL($name, UniqueConstraint $constraint)
2255
    {
2256
        $columns = $constraint->getColumns();
2257 50984
        $name    = new Identifier($name);
2258
2259 50984
        if (count($columns) === 0) {
2260 50936
            throw new InvalidArgumentException("Incomplete definition. 'columns' required.");
2261
        }
2262
2263 48457
        $flags = ['UNIQUE'];
2264
2265 48457
        if ($constraint->hasFlag('clustered')) {
2266
            $flags[] = 'CLUSTERED';
2267
        }
2268
2269 48457
        $constraintName  = $name->getQuotedName($this);
2270
        $constraintName  = ! empty($constraintName) ? $constraintName . ' ' : '';
2271 48457
        $columnListNames = $this->getIndexFieldDeclarationListSQL($columns);
2272 48307
2273
        return sprintf('CONSTRAINT %s%s (%s)', $constraintName, implode(' ', $flags), $columnListNames);
2274
    }
2275 48417
2276 46410
    /**
2277
     * Obtains DBMS specific SQL code portion needed to set an index
2278
     * declaration to be used in statements like CREATE TABLE.
2279 48407
     *
2280 8029
     * @param string $name  The name of the index.
2281
     * @param Index  $index The index definition.
2282
     *
2283 48407
     * @return string DBMS specific SQL code portion needed to set an index.
2284 44500
     *
2285
     * @throws InvalidArgumentException
2286
     */
2287 48397
    public function getIndexDeclarationSQL($name, Index $index)
2288 48296
    {
2289
        $columns = $index->getColumns();
2290
        $name    = new Identifier($name);
2291 48047
2292
        if (count($columns) === 0) {
2293
            throw new InvalidArgumentException("Incomplete definition. 'columns' required.");
2294
        }
2295
2296
        return $this->getCreateIndexSQLFlags($index) . 'INDEX ' . $name->getQuotedName($this) . ' ('
2297
            . $this->getIndexFieldDeclarationListSQL($index)
2298
            . ')' . $this->getPartialIndexSQL($index);
2299
    }
2300
2301
    /**
2302 46294
     * Obtains SQL code portion needed to create a custom column,
2303
     * e.g. when a field has the "columnDefinition" keyword.
2304 46294
     * Only "AUTOINCREMENT" and "PRIMARY KEY" are added if appropriate.
2305 46294
     *
2306 46294
     * @param mixed[] $columnDef
2307
     *
2308
     * @return string
2309 46294
     */
2310 45529
    public function getCustomTypeDeclarationSQL(array $columnDef)
2311
    {
2312
        return $columnDef['columnDefinition'];
2313 46294
    }
2314 45739
2315
    /**
2316
     * Obtains DBMS specific SQL code portion needed to set an index
2317
     * declaration to be used in statements like CREATE TABLE.
2318
     *
2319 46294
     * @param mixed[]|Index $columnsOrIndex array declaration is deprecated, prefer passing Index to this method
2320
     */
2321
    public function getIndexFieldDeclarationListSQL($columnsOrIndex) : string
2322
    {
2323
        if ($columnsOrIndex instanceof Index) {
2324
            return implode(', ', $columnsOrIndex->getQuotedColumns($this));
2325
        }
2326
2327
        if (! is_array($columnsOrIndex)) {
0 ignored issues
show
introduced by
The condition is_array($columnsOrIndex) is always true.
Loading history...
2328
            throw new InvalidArgumentException('Fields argument should be an Index or array.');
2329
        }
2330
2331
        $ret = [];
2332
2333 44159
        foreach ($columnsOrIndex as $column => $definition) {
2334
            if (is_array($definition)) {
2335 44159
                $ret[] = $column;
2336 44159
            } else {
2337
                $ret[] = $definition;
2338 44159
            }
2339 16759
        }
2340
2341
        return implode(', ', $ret);
2342 44158
    }
2343
2344 44158
    /**
2345 16783
     * Returns the required SQL string that fits between CREATE ... TABLE
2346
     * to create the table as a temporary table.
2347
     *
2348 44158
     * Should be overridden in driver classes to return the correct string for the
2349 44158
     * specific database type.
2350 44158
     *
2351
     * The default is to return the string "TEMPORARY" - this will result in a
2352 44158
     * SQL error for any database that does not support temporary tables, or that
2353
     * requires a different SQL command from "CREATE TEMPORARY TABLE".
2354
     *
2355
     * @return string The string required to be placed between "CREATE" and "TABLE"
2356
     *                to generate a temporary table, if possible.
2357
     */
2358
    public function getTemporaryTableSQL()
2359
    {
2360
        return 'TEMPORARY';
2361
    }
2362
2363
    /**
2364
     * Some vendors require temporary table names to be qualified specially.
2365
     *
2366 46795
     * @param string $tableName
2367
     *
2368 46795
     * @return string
2369 46795
     */
2370
    public function getTemporaryTableName($tableName)
2371 46795
    {
2372
        return $tableName;
2373
    }
2374
2375 46795
    /**
2376 46795
     * Obtain DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
2377 46795
     * of a field declaration to be used in statements like CREATE TABLE.
2378
     *
2379
     * @return string DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
2380
     *                of a field declaration.
2381
     */
2382
    public function getForeignKeyDeclarationSQL(ForeignKeyConstraint $foreignKey)
2383
    {
2384
        $sql  = $this->getForeignKeyBaseDeclarationSQL($foreignKey);
2385
        $sql .= $this->getAdvancedForeignKeyOptionsSQL($foreignKey);
2386
2387
        return $sql;
2388
    }
2389 46150
2390
    /**
2391 46150
     * Returns the FOREIGN KEY query section dealing with non-standard options
2392
     * as MATCH, INITIALLY DEFERRED, ON UPDATE, ...
2393
     *
2394
     * @param ForeignKeyConstraint $foreignKey The foreign key definition.
2395
     *
2396
     * @return string
2397
     */
2398
    public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey)
2399
    {
2400 49780
        $query = '';
2401
        if ($this->supportsForeignKeyOnUpdate() && $foreignKey->hasOption('onUpdate')) {
2402 49780
            $query .= ' ON UPDATE ' . $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onUpdate'));
2403 49758
        }
2404
        if ($foreignKey->hasOption('onDelete')) {
2405
            $query .= ' ON DELETE ' . $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onDelete'));
2406 44168
        }
2407
2408
        return $query;
2409
    }
2410 44168
2411
    /**
2412 44168
     * Returns the given referential action in uppercase if valid, otherwise throws an exception.
2413 44168
     *
2414
     * @param string $action The foreign key referential action.
2415
     *
2416 44168
     * @return string
2417
     *
2418
     * @throws InvalidArgumentException If unknown referential action given.
2419
     */
2420 44168
    public function getForeignKeyReferentialActionSQL($action)
2421
    {
2422
        $upper = strtoupper($action);
2423
        switch ($upper) {
2424
            case 'CASCADE':
2425
            case 'SET NULL':
2426
            case 'NO ACTION':
2427
            case 'RESTRICT':
2428
            case 'SET DEFAULT':
2429
                return $upper;
2430
            default:
2431
                throw new InvalidArgumentException('Invalid foreign key action: ' . $upper);
2432
        }
2433
    }
2434
2435
    /**
2436
     * Obtains DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
2437
     * of a field declaration to be used in statements like CREATE TABLE.
2438
     *
2439
     * @return string
2440
     *
2441
     * @throws InvalidArgumentException
2442
     */
2443
    public function getForeignKeyBaseDeclarationSQL(ForeignKeyConstraint $foreignKey)
2444
    {
2445
        $sql = '';
2446
        if (strlen($foreignKey->getName())) {
2447
            $sql .= 'CONSTRAINT ' . $foreignKey->getQuotedName($this) . ' ';
2448
        }
2449 38707
        $sql .= 'FOREIGN KEY (';
2450
2451 38707
        if (count($foreignKey->getLocalColumns()) === 0) {
2452
            throw new InvalidArgumentException("Incomplete definition. 'local' required.");
2453
        }
2454
        if (count($foreignKey->getForeignColumns()) === 0) {
2455
            throw new InvalidArgumentException("Incomplete definition. 'foreign' required.");
2456
        }
2457
        if (strlen($foreignKey->getForeignTableName()) === 0) {
2458
            throw new InvalidArgumentException("Incomplete definition. 'foreignTable' required.");
2459
        }
2460
2461 49607
        return $sql . implode(', ', $foreignKey->getQuotedLocalColumns($this))
2462
            . ') REFERENCES '
2463 49607
            . $foreignKey->getQuotedForeignTableName($this) . ' ('
2464 49604
            . implode(', ', $foreignKey->getQuotedForeignColumns($this)) . ')';
2465
    }
2466 49604
2467
    /**
2468
     * Obtains DBMS specific SQL code portion needed to set the UNIQUE constraint
2469
     * of a field declaration to be used in statements like CREATE TABLE.
2470
     *
2471
     * @return string DBMS specific SQL code portion needed to set the UNIQUE constraint
2472
     *                of a field declaration.
2473
     */
2474
    public function getUniqueFieldDeclarationSQL()
2475
    {
2476
        return 'UNIQUE';
2477 49588
    }
2478
2479 49588
    /**
2480 49588
     * Obtains DBMS specific SQL code portion needed to set the CHARACTER SET
2481 16735
     * of a field declaration to be used in statements like CREATE TABLE.
2482
     *
2483 49588
     * @param string $charset The name of the charset.
2484 47265
     *
2485
     * @return string DBMS specific SQL code portion needed to set the CHARACTER SET
2486
     *                of a field declaration.
2487 49588
     */
2488
    public function getColumnCharsetDeclarationSQL($charset)
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

2488
    public function getColumnCharsetDeclarationSQL(/** @scrutinizer ignore-unused */ $charset)

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...
2489
    {
2490
        return '';
2491
    }
2492
2493
    /**
2494
     * Obtains DBMS specific SQL code portion needed to set the COLLATION
2495
     * of a field declaration to be used in statements like CREATE TABLE.
2496
     *
2497
     * @param string $collation The name of the collation.
2498
     *
2499 47986
     * @return string DBMS specific SQL code portion needed to set the COLLATION
2500
     *                of a field declaration.
2501 47986
     */
2502 45976
    public function getColumnCollationDeclarationSQL($collation)
2503 2088
    {
2504 1943
        return $this->supportsColumnCollation() ? 'COLLATE ' . $collation : '';
2505 1930
    }
2506 1919
2507 1909
    /**
2508 47975
     * Whether the platform prefers sequences for ID generation.
2509
     * Subclasses should override this method to return TRUE if they prefer sequences.
2510 44768
     *
2511
     * @return bool
2512
     */
2513
    public function prefersSequences()
2514
    {
2515
        return false;
2516
    }
2517
2518
    /**
2519
     * Whether the platform prefers identity columns (eg. autoincrement) for ID generation.
2520
     * Subclasses should override this method to return TRUE if they prefer identity columns.
2521
     *
2522 49597
     * @return bool
2523
     */
2524 49597
    public function prefersIdentityColumns()
2525 49597
    {
2526 49502
        return false;
2527
    }
2528 49597
2529
    /**
2530 49597
     * Some platforms need the boolean values to be converted.
2531
     *
2532
     * The default conversion in this implementation converts to integers (false => 0, true => 1).
2533 49597
     *
2534
     * Note: if the input is not a boolean the original input might be returned.
2535
     *
2536 49597
     * There are two contexts when converting booleans: Literals and Prepared Statements.
2537
     * This method should handle the literal case
2538
     *
2539
     * @param mixed $item A boolean or an array of them.
2540 49597
     *
2541 49597
     * @return mixed A boolean database value or an array of them.
2542 49597
     */
2543 49597
    public function convertBooleans($item)
2544
    {
2545
        if (is_array($item)) {
2546
            foreach ($item as $k => $value) {
2547
                if (! is_bool($value)) {
2548
                    continue;
2549
                }
2550
2551
                $item[$k] = (int) $value;
2552
            }
2553
        } elseif (is_bool($item)) {
2554
            $item = (int) $item;
2555
        }
2556
2557
        return $item;
2558
    }
2559
2560
    /**
2561
     * Some platforms have boolean literals that needs to be correctly converted
2562
     *
2563
     * The default conversion tries to convert value into bool "(bool)$item"
2564
     *
2565
     * @param mixed $item
2566
     *
2567
     * @return bool|null
2568
     */
2569
    public function convertFromBoolean($item)
2570
    {
2571
        return $item === null ? null: (bool) $item;
2572
    }
2573
2574
    /**
2575
     * This method should handle the prepared statements case. When there is no
2576
     * distinction, it's OK to use the same method.
2577
     *
2578
     * Note: if the input is not a boolean the original input might be returned.
2579
     *
2580
     * @param mixed $item A boolean or an array of them.
2581 46455
     *
2582
     * @return mixed A boolean database value or an array of them.
2583 46455
     */
2584
    public function convertBooleansToDatabaseValue($item)
2585
    {
2586
        return $this->convertBooleans($item);
2587
    }
2588
2589
    /**
2590
     * Returns the SQL specific for the platform to get the current date.
2591
     *
2592 16112
     * @return string
2593
     */
2594 16112
    public function getCurrentDateSQL()
2595
    {
2596
        return 'CURRENT_DATE';
2597
    }
2598
2599
    /**
2600
     * Returns the SQL specific for the platform to get the current time.
2601
     *
2602
     * @return string
2603 35773
     */
2604
    public function getCurrentTimeSQL()
2605 35773
    {
2606
        return 'CURRENT_TIME';
2607
    }
2608
2609
    /**
2610
     * Returns the SQL specific for the platform to get the current timestamp
2611
     *
2612
     * @return string
2613
     */
2614
    public function getCurrentTimestampSQL()
2615
    {
2616
        return 'CURRENT_TIMESTAMP';
2617
    }
2618
2619
    /**
2620
     * Returns the SQL for a given transaction isolation level Connection constant.
2621
     *
2622 47096
     * @param int $level
2623
     *
2624 47096
     * @return string
2625
     *
2626
     * @throws InvalidArgumentException
2627
     */
2628
    protected function _getTransactionIsolationLevelSQL($level)
2629
    {
2630
        switch ($level) {
2631
            case TransactionIsolationLevel::READ_UNCOMMITTED:
2632 47096
                return 'READ UNCOMMITTED';
2633 47095
            case TransactionIsolationLevel::READ_COMMITTED:
2634
                return 'READ COMMITTED';
2635
            case TransactionIsolationLevel::REPEATABLE_READ:
2636 47096
                return 'REPEATABLE READ';
2637
            case TransactionIsolationLevel::SERIALIZABLE:
2638
                return 'SERIALIZABLE';
2639
            default:
2640
                throw new InvalidArgumentException('Invalid isolation level:' . $level);
2641
        }
2642
    }
2643
2644
    /**
2645
     * @return string
2646
     *
2647
     * @throws DBALException If not supported on this platform.
2648 46418
     */
2649
    public function getListDatabasesSQL()
2650 46418
    {
2651
        throw DBALException::notSupported(__METHOD__);
2652
    }
2653
2654
    /**
2655
     * Returns the SQL statement for retrieving the namespaces defined in the database.
2656
     *
2657
     * @return string
2658
     *
2659
     * @throws DBALException If not supported on this platform.
2660
     */
2661
    public function getListNamespacesSQL()
2662
    {
2663 41875
        throw DBALException::notSupported(__METHOD__);
2664
    }
2665 41875
2666
    /**
2667
     * @param string $database
2668
     *
2669
     * @return string
2670
     *
2671
     * @throws DBALException If not supported on this platform.
2672
     */
2673 45033
    public function getListSequencesSQL($database)
2674
    {
2675 45033
        throw DBALException::notSupported(__METHOD__);
2676
    }
2677
2678
    /**
2679
     * @param string $table
2680
     *
2681
     * @return string
2682
     *
2683 8029
     * @throws DBALException If not supported on this platform.
2684
     */
2685 8029
    public function getListTableConstraintsSQL($table)
2686
    {
2687
        throw DBALException::notSupported(__METHOD__);
2688
    }
2689
2690
    /**
2691
     * @param string      $table
2692
     * @param string|null $database
2693 45713
     *
2694
     * @return string
2695 45713
     *
2696
     * @throws DBALException If not supported on this platform.
2697
     */
2698
    public function getListTableColumnsSQL($table, $database = null)
2699
    {
2700
        throw DBALException::notSupported(__METHOD__);
2701
    }
2702
2703
    /**
2704
     * @return string
2705
     *
2706
     * @throws DBALException If not supported on this platform.
2707 42605
     */
2708
    public function getListTablesSQL()
2709 8
    {
2710 42597
        throw DBALException::notSupported(__METHOD__);
2711 42605
    }
2712 42597
2713 42605
    /**
2714 42597
     * @return string
2715 42605
     *
2716 42597
     * @throws DBALException If not supported on this platform.
2717 42605
     */
2718
    public function getListUsersSQL()
2719
    {
2720
        throw DBALException::notSupported(__METHOD__);
2721
    }
2722
2723
    /**
2724
     * Returns the SQL to list all views of a database or user.
2725
     *
2726
     * @param string $database
2727
     *
2728 2006
     * @return string
2729
     *
2730 2006
     * @throws DBALException If not supported on this platform.
2731
     */
2732
    public function getListViewsSQL($database)
2733
    {
2734
        throw DBALException::notSupported(__METHOD__);
2735
    }
2736
2737
    /**
2738
     * Returns the list of indexes for the current database.
2739
     *
2740
     * The current database parameter is optional but will always be passed
2741
     * when using the SchemaManager API and is the database the given table is in.
2742
     *
2743
     * Attention: Some platforms only support currentDatabase when they
2744
     * are connected with that database. Cross-database information schema
2745
     * requests may be impossible.
2746
     *
2747
     * @param string $table
2748
     * @param string $currentDatabase
2749
     *
2750
     * @return string
2751
     *
2752
     * @throws DBALException If not supported on this platform.
2753
     */
2754
    public function getListTableIndexesSQL($table, $currentDatabase = null)
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

2754
    public function getListTableIndexesSQL($table, /** @scrutinizer ignore-unused */ $currentDatabase = null)

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...
2755
    {
2756
        throw DBALException::notSupported(__METHOD__);
2757
    }
2758
2759
    /**
2760
     * @param string $table
2761
     *
2762
     * @return string
2763
     *
2764
     * @throws DBALException If not supported on this platform.
2765
     */
2766
    public function getListTableForeignKeysSQL($table)
2767
    {
2768
        throw DBALException::notSupported(__METHOD__);
2769
    }
2770
2771
    /**
2772
     * @param string $name
2773
     * @param string $sql
2774
     *
2775
     * @return string
2776
     *
2777
     * @throws DBALException If not supported on this platform.
2778
     */
2779
    public function getCreateViewSQL($name, $sql)
2780
    {
2781
        throw DBALException::notSupported(__METHOD__);
2782
    }
2783
2784
    /**
2785
     * @param string $name
2786
     *
2787
     * @return string
2788
     *
2789
     * @throws DBALException If not supported on this platform.
2790
     */
2791
    public function getDropViewSQL($name)
2792
    {
2793
        throw DBALException::notSupported(__METHOD__);
2794
    }
2795
2796
    /**
2797
     * Returns the SQL snippet to drop an existing sequence.
2798
     *
2799
     * @param Sequence|string $sequence
2800
     *
2801
     * @return string
2802
     *
2803
     * @throws DBALException If not supported on this platform.
2804
     */
2805
    public function getDropSequenceSQL($sequence)
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

2805
    public function getDropSequenceSQL(/** @scrutinizer ignore-unused */ $sequence)

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...
2806
    {
2807
        throw DBALException::notSupported(__METHOD__);
2808
    }
2809
2810
    /**
2811
     * @param string $sequenceName
2812
     *
2813
     * @return string
2814
     *
2815
     * @throws DBALException If not supported on this platform.
2816
     */
2817
    public function getSequenceNextValSQL($sequenceName)
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

2817
    public function getSequenceNextValSQL(/** @scrutinizer ignore-unused */ $sequenceName)

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...
2818
    {
2819
        throw DBALException::notSupported(__METHOD__);
2820
    }
2821
2822
    /**
2823
     * Returns the SQL to create a new database.
2824
     *
2825
     * @param string $database The name of the database that should be created.
2826
     *
2827
     * @return string
2828
     *
2829
     * @throws DBALException If not supported on this platform.
2830
     */
2831
    public function getCreateDatabaseSQL($database)
2832
    {
2833
        throw DBALException::notSupported(__METHOD__);
2834
    }
2835
2836
    /**
2837
     * Returns the SQL to set the transaction isolation level.
2838
     *
2839
     * @param int $level
2840
     *
2841
     * @return string
2842
     *
2843
     * @throws DBALException If not supported on this platform.
2844
     */
2845
    public function getSetTransactionIsolationSQL($level)
2846
    {
2847
        throw DBALException::notSupported(__METHOD__);
2848
    }
2849
2850
    /**
2851
     * Obtains DBMS specific SQL to be used to create datetime fields in
2852
     * statements like CREATE TABLE.
2853
     *
2854
     * @param mixed[] $fieldDeclaration
2855
     *
2856
     * @return string
2857
     *
2858
     * @throws DBALException If not supported on this platform.
2859
     */
2860
    public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration)
2861
    {
2862
        throw DBALException::notSupported(__METHOD__);
2863
    }
2864
2865
    /**
2866
     * Obtains DBMS specific SQL to be used to create datetime with timezone offset fields.
2867
     *
2868
     * @param mixed[] $fieldDeclaration
2869
     *
2870
     * @return string
2871
     */
2872
    public function getDateTimeTzTypeDeclarationSQL(array $fieldDeclaration)
2873
    {
2874
        return $this->getDateTimeTypeDeclarationSQL($fieldDeclaration);
2875
    }
2876
2877
    /**
2878
     * Obtains DBMS specific SQL to be used to create date fields in statements
2879
     * like CREATE TABLE.
2880
     *
2881
     * @param mixed[] $fieldDeclaration
2882
     *
2883
     * @return string
2884
     *
2885
     * @throws DBALException If not supported on this platform.
2886
     */
2887
    public function getDateTypeDeclarationSQL(array $fieldDeclaration)
2888
    {
2889
        throw DBALException::notSupported(__METHOD__);
2890
    }
2891
2892
    /**
2893
     * Obtains DBMS specific SQL to be used to create time fields in statements
2894
     * like CREATE TABLE.
2895
     *
2896
     * @param mixed[] $fieldDeclaration
2897
     *
2898
     * @return string
2899
     *
2900
     * @throws DBALException If not supported on this platform.
2901
     */
2902
    public function getTimeTypeDeclarationSQL(array $fieldDeclaration)
2903
    {
2904
        throw DBALException::notSupported(__METHOD__);
2905
    }
2906
2907
    /**
2908
     * @param mixed[] $fieldDeclaration
2909
     *
2910 32560
     * @return string
2911
     */
2912 32560
    public function getFloatDeclarationSQL(array $fieldDeclaration)
2913
    {
2914
        return 'DOUBLE PRECISION';
2915
    }
2916
2917
    /**
2918
     * Gets the default transaction isolation level of the platform.
2919
     *
2920
     * @see TransactionIsolationLevel
2921
     *
2922
     * @return int The default isolation level.
2923
     */
2924
    public function getDefaultTransactionIsolationLevel()
2925
    {
2926
        return TransactionIsolationLevel::READ_COMMITTED;
2927
    }
2928
2929
    /* supports*() methods */
2930
2931
    /**
2932
     * Whether the platform supports sequences.
2933
     *
2934
     * @return bool
2935
     */
2936
    public function supportsSequences()
2937
    {
2938
        return false;
2939
    }
2940
2941
    /**
2942
     * Whether the platform supports identity columns.
2943
     *
2944
     * Identity columns are columns that receive an auto-generated value from the
2945
     * database on insert of a row.
2946
     *
2947
     * @return bool
2948
     */
2949
    public function supportsIdentityColumns()
2950
    {
2951 30929
        return false;
2952
    }
2953 30929
2954
    /**
2955
     * Whether the platform emulates identity columns through sequences.
2956
     *
2957
     * Some platforms that do not support identity columns natively
2958
     * but support sequences can emulate identity columns by using
2959
     * sequences.
2960
     *
2961
     * @return bool
2962
     */
2963
    public function usesSequenceEmulatedIdentityColumns()
2964
    {
2965
        return false;
2966
    }
2967
2968
    /**
2969
     * Gets the sequence name prefix based on table information.
2970
     *
2971
     * @param string      $tableName
2972
     * @param string|null $schemaName
2973
     *
2974
     * @return string
2975
     */
2976
    public function getSequencePrefix($tableName, $schemaName = null)
2977
    {
2978
        if (! $schemaName) {
2979
            return $tableName;
2980
        }
2981
2982
        // Prepend the schema name to the table name if there is one
2983
        return ! $this->supportsSchemas() && $this->canEmulateSchemas()
2984
            ? $schemaName . '__' . $tableName
2985
            : $schemaName . '.' . $tableName;
2986
    }
2987
2988
    /**
2989
     * Returns the name of the sequence for a particular identity column in a particular table.
2990
     *
2991
     * @see    usesSequenceEmulatedIdentityColumns
2992 44917
     *
2993
     * @param string $tableName  The name of the table to return the sequence name for.
2994 44917
     * @param string $columnName The name of the identity column in the table to return the sequence name for.
2995
     *
2996
     * @return string
2997
     *
2998
     * @throws DBALException If not supported on this platform.
2999
     */
3000
    public function getIdentitySequenceName($tableName, $columnName)
3001
    {
3002
        throw DBALException::notSupported(__METHOD__);
3003
    }
3004
3005
    /**
3006
     * Whether the platform supports indexes.
3007
     *
3008
     * @return bool
3009
     */
3010
    public function supportsIndexes()
3011
    {
3012
        return true;
3013
    }
3014
3015
    /**
3016 33416
     * Whether the platform supports partial indexes.
3017
     *
3018 33416
     * @return bool
3019
     */
3020
    public function supportsPartialIndexes()
3021
    {
3022
        return false;
3023
    }
3024
3025
    /**
3026
     * Whether the platform supports indexes with column length definitions.
3027
     */
3028
    public function supportsColumnLengthIndexes() : bool
3029 32418
    {
3030
        return false;
3031 32418
    }
3032
3033
    /**
3034
     * Whether the platform supports altering tables.
3035
     *
3036
     * @return bool
3037
     */
3038
    public function supportsAlterTable()
3039
    {
3040
        return true;
3041
    }
3042
3043 45127
    /**
3044
     * Whether the platform supports transactions.
3045 45127
     *
3046
     * @return bool
3047
     */
3048
    public function supportsTransactions()
3049
    {
3050
        return true;
3051
    }
3052
3053
    /**
3054
     * Whether the platform supports savepoints.
3055
     *
3056
     * @return bool
3057
     */
3058
    public function supportsSavepoints()
3059
    {
3060
        return true;
3061
    }
3062
3063
    /**
3064
     * Whether the platform supports releasing savepoints.
3065
     *
3066
     * @return bool
3067
     */
3068
    public function supportsReleaseSavepoints()
3069
    {
3070
        return $this->supportsSavepoints();
3071
    }
3072
3073
    /**
3074
     * Whether the platform supports primary key constraints.
3075
     *
3076
     * @return bool
3077
     */
3078
    public function supportsPrimaryConstraints()
3079
    {
3080 43445
        return true;
3081
    }
3082 43445
3083
    /**
3084
     * Whether the platform supports foreign key constraints.
3085
     *
3086
     * @return bool
3087
     */
3088
    public function supportsForeignKeyConstraints()
3089
    {
3090 15991
        return true;
3091
    }
3092 15991
3093
    /**
3094
     * Whether this platform supports onUpdate in foreign key constraints.
3095
     *
3096
     * @return bool
3097
     */
3098
    public function supportsForeignKeyOnUpdate()
3099
    {
3100 48651
        return $this->supportsForeignKeyConstraints();
3101
    }
3102 48651
3103
    /**
3104
     * Whether the platform supports database schemas.
3105
     *
3106
     * @return bool
3107
     */
3108 47883
    public function supportsSchemas()
3109
    {
3110 47883
        return false;
3111
    }
3112
3113
    /**
3114
     * Whether this platform can emulate schemas.
3115
     *
3116
     * Platforms that either support or emulate schemas don't automatically
3117
     * filter a schema for the namespaced elements in {@link
3118 47805
     * AbstractManager#createSchema}.
3119
     *
3120 47805
     * @return bool
3121
     */
3122
    public function canEmulateSchemas()
3123
    {
3124
        return false;
3125
    }
3126
3127
    /**
3128 16015
     * Returns the default schema name.
3129
     *
3130 16015
     * @return string
3131
     *
3132
     * @throws DBALException If not supported on this platform.
3133
     */
3134
    public function getDefaultSchemaName()
3135
    {
3136
        throw DBALException::notSupported(__METHOD__);
3137
    }
3138 50486
3139
    /**
3140 50486
     * Whether this platform supports create database.
3141
     *
3142
     * Some databases don't allow to create and drop databases at all or only with certain tools.
3143
     *
3144
     * @return bool
3145
     */
3146
    public function supportsCreateDropDatabase()
3147
    {
3148 46569
        return true;
3149
    }
3150 46569
3151
    /**
3152
     * Whether the platform supports getting the affected rows of a recent update/delete type query.
3153
     *
3154
     * @return bool
3155
     */
3156
    public function supportsGettingAffectedRows()
3157
    {
3158 16087
        return true;
3159
    }
3160 16087
3161
    /**
3162
     * Whether this platform support to add inline column comments as postfix.
3163
     *
3164
     * @return bool
3165
     */
3166
    public function supportsInlineColumnComments()
3167
    {
3168 49819
        return false;
3169
    }
3170 49819
3171
    /**
3172
     * Whether this platform support the proprietary syntax "COMMENT ON asset".
3173
     *
3174
     * @return bool
3175
     */
3176
    public function supportsCommentOnStatement()
3177
    {
3178 49589
        return false;
3179
    }
3180 49589
3181
    /**
3182
     * Does this platform have native guid type.
3183
     *
3184
     * @return bool
3185
     */
3186
    public function hasNativeGuidType()
3187
    {
3188 38092
        return false;
3189
    }
3190 38092
3191
    /**
3192
     * Does this platform have native JSON type.
3193
     *
3194
     * @return bool
3195
     */
3196
    public function hasNativeJsonType()
3197
    {
3198
        return false;
3199
    }
3200
3201
    /**
3202 15823
     * @deprecated
3203
     *
3204 15823
     * @todo Remove in 3.0
3205
     */
3206
    public function getIdentityColumnNullInsertSQL()
3207
    {
3208
        return '';
3209
    }
3210
3211
    /**
3212
     * Whether this platform supports views.
3213
     *
3214
     * @return bool
3215
     */
3216
    public function supportsViews()
3217
    {
3218
        return true;
3219
    }
3220
3221
    /**
3222
     * Does this platform support column collation?
3223
     *
3224
     * @return bool
3225
     */
3226 45577
    public function supportsColumnCollation()
3227
    {
3228 45577
        return false;
3229
    }
3230
3231
    /**
3232
     * Gets the format string, as accepted by the date() function, that describes
3233
     * the format of a stored datetime value of this platform.
3234
     *
3235
     * @return string The format string.
3236 15895
     */
3237
    public function getDateTimeFormatString()
3238 15895
    {
3239
        return 'Y-m-d H:i:s';
3240
    }
3241
3242
    /**
3243
     * Gets the format string, as accepted by the date() function, that describes
3244
     * the format of a stored datetime with timezone value of this platform.
3245
     *
3246 47430
     * @return string The format string.
3247
     */
3248 47430
    public function getDateTimeTzFormatString()
3249
    {
3250
        return 'Y-m-d H:i:s';
3251
    }
3252
3253
    /**
3254
     * Gets the format string, as accepted by the date() function, that describes
3255
     * the format of a stored date value of this platform.
3256 48608
     *
3257
     * @return string The format string.
3258 48608
     */
3259
    public function getDateFormatString()
3260
    {
3261
        return 'Y-m-d';
3262
    }
3263
3264
    /**
3265
     * Gets the format string, as accepted by the date() function, that describes
3266 49504
     * the format of a stored time value of this platform.
3267
     *
3268 49504
     * @return string The format string.
3269
     */
3270
    public function getTimeFormatString()
3271
    {
3272
        return 'H:i:s';
3273
    }
3274
3275
    /**
3276 49000
     * Adds an driver-specific LIMIT clause to the query.
3277
     *
3278 49000
     * @throws DBALException
3279
     */
3280
    final public function modifyLimitQuery(string $query, ?int $limit, int $offset = 0) : string
3281
    {
3282
        if ($offset < 0) {
3283
            throw new DBALException(sprintf(
3284
                'Offset must be a positive integer or zero, %d given',
3285
                $offset
3286
            ));
3287
        }
3288
3289
        if ($offset > 0 && ! $this->supportsLimitOffset()) {
3290
            throw new DBALException(sprintf(
3291
                'Platform %s does not support offset values in limit queries.',
3292
                $this->getName()
3293
            ));
3294
        }
3295
3296 47772
        return $this->doModifyLimitQuery($query, $limit, $offset);
3297
    }
3298 47772
3299
    /**
3300
     * Adds an platform-specific LIMIT clause to the query.
3301
     */
3302
    protected function doModifyLimitQuery(string $query, ?int $limit, int $offset) : string
3303
    {
3304
        if ($limit !== null) {
3305
            $query .= sprintf(' LIMIT %d', $limit);
3306
        }
3307
3308
        if ($offset > 0) {
3309
            $query .= sprintf(' OFFSET %d', $offset);
3310
        }
3311
3312
        return $query;
3313
    }
3314
3315
    /**
3316
     * Whether the database platform support offsets in modify limit clauses.
3317 46587
     *
3318
     * @return bool
3319 46587
     */
3320
    public function supportsLimitOffset()
3321
    {
3322
        return true;
3323
    }
3324
3325
    /**
3326
     * Gets the character casing of a column in an SQL result set of this platform.
3327
     *
3328 31128
     * @param string $column The column name for which to get the correct character casing.
3329
     *
3330 31128
     * @return string The column name in the character casing used in SQL result sets.
3331
     */
3332
    public function getSQLResultCasing($column)
3333
    {
3334
        return $column;
3335
    }
3336
3337
    /**
3338
     * Makes any fixes to a name of a schema element (table, sequence, ...) that are required
3339 42370
     * by restrictions of the platform, like a maximum length.
3340
     *
3341 42370
     * @param string $schemaElementName
3342
     *
3343
     * @return string
3344
     */
3345
    public function fixSchemaElementName($schemaElementName)
3346
    {
3347
        return $schemaElementName;
3348
    }
3349
3350 42249
    /**
3351
     * Maximum length of any given database identifier, like tables or column names.
3352 42249
     *
3353
     * @return int
3354
     */
3355
    public function getMaxIdentifierLength()
3356
    {
3357
        return 63;
3358
    }
3359
3360 49446
    /**
3361
     * Returns the insert SQL for an empty insert statement.
3362 49446
     *
3363
     * @param string $tableName
3364
     * @param string $identifierColumnName
3365
     *
3366
     * @return string
3367
     */
3368
    public function getEmptyIdentityInsertSQL($tableName, $identifierColumnName)
3369 49446
    {
3370
        return 'INSERT INTO ' . $tableName . ' (' . $identifierColumnName . ') VALUES (null)';
3371
    }
3372
3373
    /**
3374
     * Generates a Truncate Table SQL statement for a given table.
3375
     *
3376 49446
     * Cascade is not supported on many platforms but would optionally cascade the truncate by
3377
     * following the foreign keys.
3378
     *
3379
     * @param string $tableName
3380
     * @param bool   $cascade
3381
     *
3382 34907
     * @return string
3383
     */
3384 34907
    public function getTruncateTableSQL($tableName, $cascade = false)
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

3384
    public function getTruncateTableSQL($tableName, /** @scrutinizer ignore-unused */ $cascade = false)

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...
3385 34903
    {
3386
        $tableIdentifier = new Identifier($tableName);
3387
3388 34907
        return 'TRUNCATE ' . $tableIdentifier->getQuotedName($this);
3389 18225
    }
3390
3391
    /**
3392 34907
     * This is for test reasons, many vendors have special requirements for dummy statements.
3393
     */
3394
    public function getDummySelectSQL(string $expression = '1') : string
3395
    {
3396
        return sprintf('SELECT %s', $expression);
3397
    }
3398
3399
    /**
3400 49059
     * Returns the SQL to create a new savepoint.
3401
     *
3402 49059
     * @param string $savepoint
3403
     *
3404
     * @return string
3405
     */
3406
    public function createSavePoint($savepoint)
3407
    {
3408
        return 'SAVEPOINT ' . $savepoint;
3409
    }
3410
3411
    /**
3412
     * Returns the SQL to release a savepoint.
3413
     *
3414
     * @param string $savepoint
3415
     *
3416
     * @return string
3417
     */
3418
    public function releaseSavePoint($savepoint)
3419
    {
3420
        return 'RELEASE SAVEPOINT ' . $savepoint;
3421
    }
3422
3423
    /**
3424
     * Returns the SQL to rollback a savepoint.
3425
     *
3426
     * @param string $savepoint
3427
     *
3428
     * @return string
3429
     */
3430
    public function rollbackSavePoint($savepoint)
3431
    {
3432
        return 'ROLLBACK TO SAVEPOINT ' . $savepoint;
3433
    }
3434
3435 48917
    /**
3436
     * Returns the keyword list instance of this platform.
3437 48917
     *
3438
     * @return KeywordList
3439
     *
3440
     * @throws DBALException If no keyword list is specified.
3441
     */
3442
    final public function getReservedKeywordsList()
3443
    {
3444
        // Check for an existing instantiation of the keywords class.
3445
        if ($this->_keywords) {
3446
            return $this->_keywords;
3447
        }
3448 28531
3449
        $class    = $this->getReservedKeywordsClass();
3450 28531
        $keywords = new $class();
3451
        if (! $keywords instanceof KeywordList) {
3452
            throw DBALException::notSupported(__METHOD__);
3453
        }
3454
3455
        // Store the instance so it doesn't need to be generated on every request.
3456
        $this->_keywords = $keywords;
3457
3458
        return $keywords;
3459
    }
3460
3461
    /**
3462
     * Returns the class name of the reserved keywords list.
3463
     *
3464 45884
     * @return string
3465
     *
3466 45884
     * @throws DBALException If not supported on this platform.
3467
     */
3468 45884
    protected function getReservedKeywordsClass()
3469
    {
3470
        throw DBALException::notSupported(__METHOD__);
3471
    }
3472
3473
    /**
3474 48637
     * Quotes a literal string.
3475
     * This method is NOT meant to fix SQL injections!
3476 48637
     * It is only meant to escape this platform's string literal
3477
     * quote character inside the given literal string.
3478
     *
3479
     * @param string $str The literal string to be quoted.
3480
     *
3481
     * @return string The quoted literal string.
3482
     */
3483
    public function quoteStringLiteral($str)
3484
    {
3485
        $c = $this->getStringLiteralQuoteCharacter();
3486 44571
3487
        return $c . str_replace($c, $c . $c, $str) . $c;
3488 44571
    }
3489
3490
    /**
3491
     * Gets the character used for string literal quoting.
3492
     *
3493
     * @return string
3494
     */
3495
    public function getStringLiteralQuoteCharacter()
3496
    {
3497
        return "'";
3498 44570
    }
3499
3500 44570
    /**
3501
     * Escapes metacharacters in a string intended to be used with a LIKE
3502
     * operator.
3503
     *
3504
     * @param string $inputString a literal, unquoted string
3505
     * @param string $escapeChar  should be reused by the caller in the LIKE
3506
     *                            expression.
3507
     */
3508
    final public function escapeStringForLike(string $inputString, string $escapeChar) : string
3509
    {
3510 44571
        return preg_replace(
3511
            '~([' . preg_quote($this->getLikeWildcardCharacters() . $escapeChar, '~') . '])~u',
3512 44571
            addcslashes($escapeChar, '\\') . '$1',
3513
            $inputString
3514
        );
3515
    }
3516
3517
    protected function getLikeWildcardCharacters() : string
3518
    {
3519
        return '%_';
3520
    }
3521
}
3522