summaryrefslogtreecommitdiff
path: root/vendor/doctrine/dbal/src/Platforms/AbstractMySQLPlatform.php
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/doctrine/dbal/src/Platforms/AbstractMySQLPlatform.php')
-rw-r--r--vendor/doctrine/dbal/src/Platforms/AbstractMySQLPlatform.php842
1 files changed, 842 insertions, 0 deletions
diff --git a/vendor/doctrine/dbal/src/Platforms/AbstractMySQLPlatform.php b/vendor/doctrine/dbal/src/Platforms/AbstractMySQLPlatform.php
new file mode 100644
index 0000000..770d863
--- /dev/null
+++ b/vendor/doctrine/dbal/src/Platforms/AbstractMySQLPlatform.php
@@ -0,0 +1,842 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Doctrine\DBAL\Platforms;
6
7use Doctrine\DBAL\Connection;
8use Doctrine\DBAL\Exception;
9use Doctrine\DBAL\Platforms\Keywords\KeywordList;
10use Doctrine\DBAL\Platforms\Keywords\MySQLKeywords;
11use Doctrine\DBAL\Schema\AbstractAsset;
12use Doctrine\DBAL\Schema\ForeignKeyConstraint;
13use Doctrine\DBAL\Schema\Identifier;
14use Doctrine\DBAL\Schema\Index;
15use Doctrine\DBAL\Schema\MySQLSchemaManager;
16use Doctrine\DBAL\Schema\TableDiff;
17use Doctrine\DBAL\SQL\Builder\DefaultSelectSQLBuilder;
18use Doctrine\DBAL\SQL\Builder\SelectSQLBuilder;
19use Doctrine\DBAL\TransactionIsolationLevel;
20use Doctrine\DBAL\Types\Types;
21
22use function array_merge;
23use function array_unique;
24use function array_values;
25use function count;
26use function implode;
27use function in_array;
28use function is_numeric;
29use function sprintf;
30use function str_replace;
31use function strtolower;
32
33/**
34 * Provides the base implementation for the lowest versions of supported MySQL-like database platforms.
35 */
36abstract class AbstractMySQLPlatform extends AbstractPlatform
37{
38 final public const LENGTH_LIMIT_TINYTEXT = 255;
39 final public const LENGTH_LIMIT_TEXT = 65535;
40 final public const LENGTH_LIMIT_MEDIUMTEXT = 16777215;
41
42 final public const LENGTH_LIMIT_TINYBLOB = 255;
43 final public const LENGTH_LIMIT_BLOB = 65535;
44 final public const LENGTH_LIMIT_MEDIUMBLOB = 16777215;
45
46 protected function doModifyLimitQuery(string $query, ?int $limit, int $offset): string
47 {
48 if ($limit !== null) {
49 $query .= sprintf(' LIMIT %d', $limit);
50
51 if ($offset > 0) {
52 $query .= sprintf(' OFFSET %d', $offset);
53 }
54 } elseif ($offset > 0) {
55 // 2^64-1 is the maximum of unsigned BIGINT, the biggest limit possible
56 $query .= sprintf(' LIMIT 18446744073709551615 OFFSET %d', $offset);
57 }
58
59 return $query;
60 }
61
62 public function quoteSingleIdentifier(string $str): string
63 {
64 return '`' . str_replace('`', '``', $str) . '`';
65 }
66
67 public function getRegexpExpression(): string
68 {
69 return 'RLIKE';
70 }
71
72 public function getLocateExpression(string $string, string $substring, ?string $start = null): string
73 {
74 if ($start === null) {
75 return sprintf('LOCATE(%s, %s)', $substring, $string);
76 }
77
78 return sprintf('LOCATE(%s, %s, %s)', $substring, $string, $start);
79 }
80
81 public function getConcatExpression(string ...$string): string
82 {
83 return sprintf('CONCAT(%s)', implode(', ', $string));
84 }
85
86 protected function getDateArithmeticIntervalExpression(
87 string $date,
88 string $operator,
89 string $interval,
90 DateIntervalUnit $unit,
91 ): string {
92 $function = $operator === '+' ? 'DATE_ADD' : 'DATE_SUB';
93
94 return $function . '(' . $date . ', INTERVAL ' . $interval . ' ' . $unit->value . ')';
95 }
96
97 public function getDateDiffExpression(string $date1, string $date2): string
98 {
99 return 'DATEDIFF(' . $date1 . ', ' . $date2 . ')';
100 }
101
102 public function getCurrentDatabaseExpression(): string
103 {
104 return 'DATABASE()';
105 }
106
107 public function getLengthExpression(string $string): string
108 {
109 return 'CHAR_LENGTH(' . $string . ')';
110 }
111
112 /** @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */
113 public function getListDatabasesSQL(): string
114 {
115 return 'SHOW DATABASES';
116 }
117
118 /** @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */
119 public function getListViewsSQL(string $database): string
120 {
121 return 'SELECT * FROM information_schema.VIEWS WHERE TABLE_SCHEMA = ' . $this->quoteStringLiteral($database);
122 }
123
124 /**
125 * {@inheritDoc}
126 */
127 public function getJsonTypeDeclarationSQL(array $column): string
128 {
129 return 'JSON';
130 }
131
132 /**
133 * Gets the SQL snippet used to declare a CLOB column type.
134 * TINYTEXT : 2 ^ 8 - 1 = 255
135 * TEXT : 2 ^ 16 - 1 = 65535
136 * MEDIUMTEXT : 2 ^ 24 - 1 = 16777215
137 * LONGTEXT : 2 ^ 32 - 1 = 4294967295
138 *
139 * {@inheritDoc}
140 */
141 public function getClobTypeDeclarationSQL(array $column): string
142 {
143 if (! empty($column['length']) && is_numeric($column['length'])) {
144 $length = $column['length'];
145
146 if ($length <= static::LENGTH_LIMIT_TINYTEXT) {
147 return 'TINYTEXT';
148 }
149
150 if ($length <= static::LENGTH_LIMIT_TEXT) {
151 return 'TEXT';
152 }
153
154 if ($length <= static::LENGTH_LIMIT_MEDIUMTEXT) {
155 return 'MEDIUMTEXT';
156 }
157 }
158
159 return 'LONGTEXT';
160 }
161
162 /**
163 * {@inheritDoc}
164 */
165 public function getDateTimeTypeDeclarationSQL(array $column): string
166 {
167 if (isset($column['version']) && $column['version'] === true) {
168 return 'TIMESTAMP';
169 }
170
171 return 'DATETIME';
172 }
173
174 /**
175 * {@inheritDoc}
176 */
177 public function getDateTypeDeclarationSQL(array $column): string
178 {
179 return 'DATE';
180 }
181
182 /**
183 * {@inheritDoc}
184 */
185 public function getTimeTypeDeclarationSQL(array $column): string
186 {
187 return 'TIME';
188 }
189
190 /**
191 * {@inheritDoc}
192 */
193 public function getBooleanTypeDeclarationSQL(array $column): string
194 {
195 return 'TINYINT(1)';
196 }
197
198 /**
199 * {@inheritDoc}
200 *
201 * MySQL supports this through AUTO_INCREMENT columns.
202 */
203 public function supportsIdentityColumns(): bool
204 {
205 return true;
206 }
207
208 /** @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */
209 public function supportsInlineColumnComments(): bool
210 {
211 return true;
212 }
213
214 /** @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */
215 public function supportsColumnCollation(): bool
216 {
217 return true;
218 }
219
220 /**
221 * The SQL snippet required to elucidate a column type
222 *
223 * Returns a column type SELECT snippet string
224 */
225 public function getColumnTypeSQLSnippet(string $tableAlias, string $databaseName): string
226 {
227 return $tableAlias . '.COLUMN_TYPE';
228 }
229
230 /**
231 * {@inheritDoc}
232 */
233 protected function _getCreateTableSQL(string $name, array $columns, array $options = []): array
234 {
235 $queryFields = $this->getColumnDeclarationListSQL($columns);
236
237 if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) {
238 foreach ($options['uniqueConstraints'] as $definition) {
239 $queryFields .= ', ' . $this->getUniqueConstraintDeclarationSQL($definition);
240 }
241 }
242
243 // add all indexes
244 if (isset($options['indexes']) && ! empty($options['indexes'])) {
245 foreach ($options['indexes'] as $definition) {
246 $queryFields .= ', ' . $this->getIndexDeclarationSQL($definition);
247 }
248 }
249
250 // attach all primary keys
251 if (isset($options['primary']) && ! empty($options['primary'])) {
252 $keyColumns = array_unique(array_values($options['primary']));
253 $queryFields .= ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')';
254 }
255
256 $sql = ['CREATE'];
257
258 if (! empty($options['temporary'])) {
259 $sql[] = 'TEMPORARY';
260 }
261
262 $sql[] = 'TABLE ' . $name . ' (' . $queryFields . ')';
263
264 $tableOptions = $this->buildTableOptions($options);
265
266 if ($tableOptions !== '') {
267 $sql[] = $tableOptions;
268 }
269
270 if (isset($options['partition_options'])) {
271 $sql[] = $options['partition_options'];
272 }
273
274 $sql = [implode(' ', $sql)];
275
276 if (isset($options['foreignKeys'])) {
277 foreach ($options['foreignKeys'] as $definition) {
278 $sql[] = $this->getCreateForeignKeySQL($definition, $name);
279 }
280 }
281
282 return $sql;
283 }
284
285 public function createSelectSQLBuilder(): SelectSQLBuilder
286 {
287 return new DefaultSelectSQLBuilder($this, 'FOR UPDATE', null);
288 }
289
290 /**
291 * Build SQL for table options
292 *
293 * @param mixed[] $options
294 */
295 private function buildTableOptions(array $options): string
296 {
297 if (isset($options['table_options'])) {
298 return $options['table_options'];
299 }
300
301 $tableOptions = [];
302
303 if (isset($options['charset'])) {
304 $tableOptions[] = sprintf('DEFAULT CHARACTER SET %s', $options['charset']);
305 }
306
307 if (isset($options['collation'])) {
308 $tableOptions[] = $this->getColumnCollationDeclarationSQL($options['collation']);
309 }
310
311 if (isset($options['engine'])) {
312 $tableOptions[] = sprintf('ENGINE = %s', $options['engine']);
313 }
314
315 // Auto increment
316 if (isset($options['auto_increment'])) {
317 $tableOptions[] = sprintf('AUTO_INCREMENT = %s', $options['auto_increment']);
318 }
319
320 // Comment
321 if (isset($options['comment'])) {
322 $tableOptions[] = sprintf('COMMENT = %s ', $this->quoteStringLiteral($options['comment']));
323 }
324
325 // Row format
326 if (isset($options['row_format'])) {
327 $tableOptions[] = sprintf('ROW_FORMAT = %s', $options['row_format']);
328 }
329
330 return implode(' ', $tableOptions);
331 }
332
333 /**
334 * {@inheritDoc}
335 */
336 public function getAlterTableSQL(TableDiff $diff): array
337 {
338 $columnSql = [];
339 $queryParts = [];
340
341 foreach ($diff->getAddedColumns() as $column) {
342 $columnProperties = array_merge($column->toArray(), [
343 'comment' => $column->getComment(),
344 ]);
345
346 $queryParts[] = 'ADD ' . $this->getColumnDeclarationSQL(
347 $column->getQuotedName($this),
348 $columnProperties,
349 );
350 }
351
352 foreach ($diff->getDroppedColumns() as $column) {
353 $queryParts[] = 'DROP ' . $column->getQuotedName($this);
354 }
355
356 foreach ($diff->getModifiedColumns() as $columnDiff) {
357 $newColumn = $columnDiff->getNewColumn();
358
359 $newColumnProperties = array_merge($newColumn->toArray(), [
360 'comment' => $newColumn->getComment(),
361 ]);
362
363 $oldColumn = $columnDiff->getOldColumn();
364
365 $queryParts[] = 'CHANGE ' . $oldColumn->getQuotedName($this) . ' '
366 . $this->getColumnDeclarationSQL($newColumn->getQuotedName($this), $newColumnProperties);
367 }
368
369 foreach ($diff->getRenamedColumns() as $oldColumnName => $column) {
370 $oldColumnName = new Identifier($oldColumnName);
371
372 $columnProperties = array_merge($column->toArray(), [
373 'comment' => $column->getComment(),
374 ]);
375
376 $queryParts[] = 'CHANGE ' . $oldColumnName->getQuotedName($this) . ' '
377 . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnProperties);
378 }
379
380 $addedIndexes = $this->indexAssetsByLowerCaseName($diff->getAddedIndexes());
381 $modifiedIndexes = $this->indexAssetsByLowerCaseName($diff->getModifiedIndexes());
382 $diffModified = false;
383
384 if (isset($addedIndexes['primary'])) {
385 $keyColumns = array_values(array_unique($addedIndexes['primary']->getColumns()));
386 $queryParts[] = 'ADD PRIMARY KEY (' . implode(', ', $keyColumns) . ')';
387 unset($addedIndexes['primary']);
388 $diffModified = true;
389 } elseif (isset($modifiedIndexes['primary'])) {
390 $addedColumns = $this->indexAssetsByLowerCaseName($diff->getAddedColumns());
391
392 // Necessary in case the new primary key includes a new auto_increment column
393 foreach ($modifiedIndexes['primary']->getColumns() as $columnName) {
394 if (isset($addedColumns[$columnName]) && $addedColumns[$columnName]->getAutoincrement()) {
395 $keyColumns = array_values(array_unique($modifiedIndexes['primary']->getColumns()));
396 $queryParts[] = 'DROP PRIMARY KEY';
397 $queryParts[] = 'ADD PRIMARY KEY (' . implode(', ', $keyColumns) . ')';
398 unset($modifiedIndexes['primary']);
399 $diffModified = true;
400 break;
401 }
402 }
403 }
404
405 if ($diffModified) {
406 $diff = new TableDiff(
407 $diff->getOldTable(),
408 $diff->getAddedColumns(),
409 $diff->getModifiedColumns(),
410 $diff->getDroppedColumns(),
411 $diff->getRenamedColumns(),
412 array_values($addedIndexes),
413 array_values($modifiedIndexes),
414 $diff->getDroppedIndexes(),
415 $diff->getRenamedIndexes(),
416 $diff->getAddedForeignKeys(),
417 $diff->getModifiedForeignKeys(),
418 $diff->getDroppedForeignKeys(),
419 );
420 }
421
422 $sql = [];
423 $tableSql = [];
424
425 if (count($queryParts) > 0) {
426 $sql[] = 'ALTER TABLE ' . $diff->getOldTable()->getQuotedName($this) . ' '
427 . implode(', ', $queryParts);
428 }
429
430 $sql = array_merge(
431 $this->getPreAlterTableIndexForeignKeySQL($diff),
432 $sql,
433 $this->getPostAlterTableIndexForeignKeySQL($diff),
434 );
435
436 return array_merge($sql, $tableSql, $columnSql);
437 }
438
439 /**
440 * {@inheritDoc}
441 */
442 protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff): array
443 {
444 $sql = [];
445
446 $tableNameSQL = $diff->getOldTable()->getQuotedName($this);
447
448 foreach ($diff->getModifiedIndexes() as $changedIndex) {
449 $sql = array_merge($sql, $this->getPreAlterTableAlterPrimaryKeySQL($diff, $changedIndex));
450 }
451
452 foreach ($diff->getDroppedIndexes() as $droppedIndex) {
453 $sql = array_merge($sql, $this->getPreAlterTableAlterPrimaryKeySQL($diff, $droppedIndex));
454
455 foreach ($diff->getAddedIndexes() as $addedIndex) {
456 if ($droppedIndex->getColumns() !== $addedIndex->getColumns()) {
457 continue;
458 }
459
460 $indexClause = 'INDEX ' . $addedIndex->getName();
461
462 if ($addedIndex->isPrimary()) {
463 $indexClause = 'PRIMARY KEY';
464 } elseif ($addedIndex->isUnique()) {
465 $indexClause = 'UNIQUE INDEX ' . $addedIndex->getName();
466 }
467
468 $query = 'ALTER TABLE ' . $tableNameSQL . ' DROP INDEX ' . $droppedIndex->getName() . ', ';
469 $query .= 'ADD ' . $indexClause;
470 $query .= ' (' . implode(', ', $addedIndex->getQuotedColumns($this)) . ')';
471
472 $sql[] = $query;
473
474 $diff->unsetAddedIndex($addedIndex);
475 $diff->unsetDroppedIndex($droppedIndex);
476
477 break;
478 }
479 }
480
481 return array_merge(
482 $sql,
483 $this->getPreAlterTableAlterIndexForeignKeySQL($diff),
484 parent::getPreAlterTableIndexForeignKeySQL($diff),
485 $this->getPreAlterTableRenameIndexForeignKeySQL($diff),
486 );
487 }
488
489 /**
490 * @return list<string>
491 *
492 * @throws Exception
493 */
494 private function getPreAlterTableAlterPrimaryKeySQL(TableDiff $diff, Index $index): array
495 {
496 if (! $index->isPrimary()) {
497 return [];
498 }
499
500 $table = $diff->getOldTable();
501
502 $sql = [];
503
504 $tableNameSQL = $table->getQuotedName($this);
505
506 // Dropping primary keys requires to unset autoincrement attribute on the particular column first.
507 foreach ($index->getColumns() as $columnName) {
508 if (! $table->hasColumn($columnName)) {
509 continue;
510 }
511
512 $column = $table->getColumn($columnName);
513
514 if (! $column->getAutoincrement()) {
515 continue;
516 }
517
518 $column->setAutoincrement(false);
519
520 $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' MODIFY ' .
521 $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray());
522
523 // original autoincrement information might be needed later on by other parts of the table alteration
524 $column->setAutoincrement(true);
525 }
526
527 return $sql;
528 }
529
530 /**
531 * @param TableDiff $diff The table diff to gather the SQL for.
532 *
533 * @return list<string>
534 *
535 * @throws Exception
536 */
537 private function getPreAlterTableAlterIndexForeignKeySQL(TableDiff $diff): array
538 {
539 $table = $diff->getOldTable();
540
541 $primaryKey = $table->getPrimaryKey();
542
543 if ($primaryKey === null) {
544 return [];
545 }
546
547 $primaryKeyColumns = [];
548
549 foreach ($primaryKey->getColumns() as $columnName) {
550 if (! $table->hasColumn($columnName)) {
551 continue;
552 }
553
554 $primaryKeyColumns[] = $table->getColumn($columnName);
555 }
556
557 if (count($primaryKeyColumns) === 0) {
558 return [];
559 }
560
561 $sql = [];
562
563 $tableNameSQL = $table->getQuotedName($this);
564
565 foreach ($diff->getModifiedIndexes() as $changedIndex) {
566 // Changed primary key
567 if (! $changedIndex->isPrimary()) {
568 continue;
569 }
570
571 foreach ($primaryKeyColumns as $column) {
572 // Check if an autoincrement column was dropped from the primary key.
573 if (! $column->getAutoincrement() || in_array($column->getName(), $changedIndex->getColumns(), true)) {
574 continue;
575 }
576
577 // The autoincrement attribute needs to be removed from the dropped column
578 // before we can drop and recreate the primary key.
579 $column->setAutoincrement(false);
580
581 $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' MODIFY ' .
582 $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray());
583
584 // Restore the autoincrement attribute as it might be needed later on
585 // by other parts of the table alteration.
586 $column->setAutoincrement(true);
587 }
588 }
589
590 return $sql;
591 }
592
593 /**
594 * @param TableDiff $diff The table diff to gather the SQL for.
595 *
596 * @return list<string>
597 */
598 protected function getPreAlterTableRenameIndexForeignKeySQL(TableDiff $diff): array
599 {
600 return [];
601 }
602
603 protected function getCreateIndexSQLFlags(Index $index): string
604 {
605 $type = '';
606 if ($index->isUnique()) {
607 $type .= 'UNIQUE ';
608 } elseif ($index->hasFlag('fulltext')) {
609 $type .= 'FULLTEXT ';
610 } elseif ($index->hasFlag('spatial')) {
611 $type .= 'SPATIAL ';
612 }
613
614 return $type;
615 }
616
617 /**
618 * {@inheritDoc}
619 */
620 public function getIntegerTypeDeclarationSQL(array $column): string
621 {
622 return 'INT' . $this->_getCommonIntegerTypeDeclarationSQL($column);
623 }
624
625 /**
626 * {@inheritDoc}
627 */
628 public function getBigIntTypeDeclarationSQL(array $column): string
629 {
630 return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($column);
631 }
632
633 /**
634 * {@inheritDoc}
635 */
636 public function getSmallIntTypeDeclarationSQL(array $column): string
637 {
638 return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($column);
639 }
640
641 /**
642 * {@inheritDoc}
643 */
644 public function getFloatDeclarationSQL(array $column): string
645 {
646 return 'DOUBLE PRECISION' . $this->getUnsignedDeclaration($column);
647 }
648
649 /**
650 * {@inheritDoc}
651 */
652 public function getDecimalTypeDeclarationSQL(array $column): string
653 {
654 return parent::getDecimalTypeDeclarationSQL($column) . $this->getUnsignedDeclaration($column);
655 }
656
657 /**
658 * Get unsigned declaration for a column.
659 *
660 * @param mixed[] $columnDef
661 */
662 private function getUnsignedDeclaration(array $columnDef): string
663 {
664 return ! empty($columnDef['unsigned']) ? ' UNSIGNED' : '';
665 }
666
667 /**
668 * {@inheritDoc}
669 */
670 protected function _getCommonIntegerTypeDeclarationSQL(array $column): string
671 {
672 $autoinc = '';
673 if (! empty($column['autoincrement'])) {
674 $autoinc = ' AUTO_INCREMENT';
675 }
676
677 return $this->getUnsignedDeclaration($column) . $autoinc;
678 }
679
680 /** @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */
681 public function getColumnCharsetDeclarationSQL(string $charset): string
682 {
683 return 'CHARACTER SET ' . $charset;
684 }
685
686 /** @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */
687 public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey): string
688 {
689 $query = '';
690 if ($foreignKey->hasOption('match')) {
691 $query .= ' MATCH ' . $foreignKey->getOption('match');
692 }
693
694 $query .= parent::getAdvancedForeignKeyOptionsSQL($foreignKey);
695
696 return $query;
697 }
698
699 public function getDropIndexSQL(string $name, string $table): string
700 {
701 return 'DROP INDEX ' . $name . ' ON ' . $table;
702 }
703
704 /**
705 * The `ALTER TABLE ... DROP CONSTRAINT` syntax is only available as of MySQL 8.0.19.
706 *
707 * @link https://dev.mysql.com/doc/refman/8.0/en/alter-table.html
708 */
709 public function getDropUniqueConstraintSQL(string $name, string $tableName): string
710 {
711 return $this->getDropIndexSQL($name, $tableName);
712 }
713
714 public function getSetTransactionIsolationSQL(TransactionIsolationLevel $level): string
715 {
716 return 'SET SESSION TRANSACTION ISOLATION LEVEL ' . $this->_getTransactionIsolationLevelSQL($level);
717 }
718
719 protected function initializeDoctrineTypeMappings(): void
720 {
721 $this->doctrineTypeMapping = [
722 'bigint' => Types::BIGINT,
723 'binary' => Types::BINARY,
724 'blob' => Types::BLOB,
725 'char' => Types::STRING,
726 'date' => Types::DATE_MUTABLE,
727 'datetime' => Types::DATETIME_MUTABLE,
728 'decimal' => Types::DECIMAL,
729 'double' => Types::FLOAT,
730 'float' => Types::FLOAT,
731 'int' => Types::INTEGER,
732 'integer' => Types::INTEGER,
733 'json' => Types::JSON,
734 'longblob' => Types::BLOB,
735 'longtext' => Types::TEXT,
736 'mediumblob' => Types::BLOB,
737 'mediumint' => Types::INTEGER,
738 'mediumtext' => Types::TEXT,
739 'numeric' => Types::DECIMAL,
740 'real' => Types::FLOAT,
741 'set' => Types::SIMPLE_ARRAY,
742 'smallint' => Types::SMALLINT,
743 'string' => Types::STRING,
744 'text' => Types::TEXT,
745 'time' => Types::TIME_MUTABLE,
746 'timestamp' => Types::DATETIME_MUTABLE,
747 'tinyblob' => Types::BLOB,
748 'tinyint' => Types::BOOLEAN,
749 'tinytext' => Types::TEXT,
750 'varbinary' => Types::BINARY,
751 'varchar' => Types::STRING,
752 'year' => Types::DATE_MUTABLE,
753 ];
754 }
755
756 protected function createReservedKeywordsList(): KeywordList
757 {
758 return new MySQLKeywords();
759 }
760
761 /**
762 * {@inheritDoc}
763 *
764 * MySQL commits a transaction implicitly when DROP TABLE is executed, however not
765 * if DROP TEMPORARY TABLE is executed.
766 */
767 public function getDropTemporaryTableSQL(string $table): string
768 {
769 return 'DROP TEMPORARY TABLE ' . $table;
770 }
771
772 /**
773 * Gets the SQL Snippet used to declare a BLOB column type.
774 * TINYBLOB : 2 ^ 8 - 1 = 255
775 * BLOB : 2 ^ 16 - 1 = 65535
776 * MEDIUMBLOB : 2 ^ 24 - 1 = 16777215
777 * LONGBLOB : 2 ^ 32 - 1 = 4294967295
778 *
779 * {@inheritDoc}
780 */
781 public function getBlobTypeDeclarationSQL(array $column): string
782 {
783 if (! empty($column['length']) && is_numeric($column['length'])) {
784 $length = $column['length'];
785
786 if ($length <= static::LENGTH_LIMIT_TINYBLOB) {
787 return 'TINYBLOB';
788 }
789
790 if ($length <= static::LENGTH_LIMIT_BLOB) {
791 return 'BLOB';
792 }
793
794 if ($length <= static::LENGTH_LIMIT_MEDIUMBLOB) {
795 return 'MEDIUMBLOB';
796 }
797 }
798
799 return 'LONGBLOB';
800 }
801
802 public function quoteStringLiteral(string $str): string
803 {
804 // MySQL requires backslashes to be escaped as well.
805 $str = str_replace('\\', '\\\\', $str);
806
807 return parent::quoteStringLiteral($str);
808 }
809
810 public function getDefaultTransactionIsolationLevel(): TransactionIsolationLevel
811 {
812 return TransactionIsolationLevel::REPEATABLE_READ;
813 }
814
815 public function supportsColumnLengthIndexes(): bool
816 {
817 return true;
818 }
819
820 public function createSchemaManager(Connection $connection): MySQLSchemaManager
821 {
822 return new MySQLSchemaManager($connection, $this);
823 }
824
825 /**
826 * @param array<T> $assets
827 *
828 * @return array<string,T>
829 *
830 * @template T of AbstractAsset
831 */
832 private function indexAssetsByLowerCaseName(array $assets): array
833 {
834 $result = [];
835
836 foreach ($assets as $asset) {
837 $result[strtolower($asset->getName())] = $asset;
838 }
839
840 return $result;
841 }
842}