Passed
Pull Request — master (#2920)
by Luís
09:30
created

MySqlPlatform::getDropIndexSQL()   C

Complexity

Conditions 7
Paths 8

Size

Total Lines 23
Code Lines 13

Duplication

Lines 23
Ratio 100 %

Code Coverage

Tests 10
CRAP Score 7.6024

Importance

Changes 0
Metric Value
dl 23
loc 23
ccs 10
cts 13
cp 0.7692
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 13
nc 8
nop 2
crap 7.6024
1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\DBAL\Platforms;
21
22
use Doctrine\DBAL\Connection;
23
use Doctrine\DBAL\Schema\Identifier;
24
use Doctrine\DBAL\Schema\Index;
25
use Doctrine\DBAL\Schema\Table;
26
use Doctrine\DBAL\Schema\TableDiff;
27
use Doctrine\DBAL\Types\BlobType;
28
use Doctrine\DBAL\Types\TextType;
29
30
/**
31
 * The MySqlPlatform provides the behavior, features and SQL dialect of the
32
 * MySQL database platform. This platform represents a MySQL 5.0 or greater platform that
33
 * uses the InnoDB storage engine.
34
 *
35
 * @since  2.0
36
 * @author Roman Borschel <[email protected]>
37
 * @author Benjamin Eberlei <[email protected]>
38
 * @todo   Rename: MySQLPlatform
39
 */
40
class MySqlPlatform extends AbstractPlatform
41
{
42
    const LENGTH_LIMIT_TINYTEXT   = 255;
43
    const LENGTH_LIMIT_TEXT       = 65535;
44
    const LENGTH_LIMIT_MEDIUMTEXT = 16777215;
45
46
    const LENGTH_LIMIT_TINYBLOB   = 255;
47
    const LENGTH_LIMIT_BLOB       = 65535;
48
    const LENGTH_LIMIT_MEDIUMBLOB = 16777215;
49
50
    /**
51
     * Adds MySQL-specific LIMIT clause to the query
52
     * 18446744073709551615 is 2^64-1 maximum of unsigned BIGINT the biggest limit possible
53
     *
54
     * @param string  $query
55
     * @param integer $limit
56
     * @param integer $offset
57
     *
58
     * @return string
59
     */
60 6
    protected function doModifyLimitQuery($query, $limit, $offset)
61
    {
62 6
        if ($limit !== null) {
63 4
            $query .= ' LIMIT ' . $limit;
64 4
            if ($offset !== null) {
65 4
                $query .= ' OFFSET ' . $offset;
66
            }
67 2
        } elseif ($offset !== null) {
68 2
            $query .= ' LIMIT 18446744073709551615 OFFSET ' . $offset;
69
        }
70
71 6
        return $query;
72
    }
73
74
    /**
75
     * {@inheritDoc}
76
     */
77 56
    public function getIdentifierQuoteCharacter()
78
    {
79 56
        return '`';
80
    }
81
82
    /**
83
     * {@inheritDoc}
84
     */
85 2
    public function getRegexpExpression()
86
    {
87 2
        return 'RLIKE';
88
    }
89
90
    /**
91
     * {@inheritDoc}
92
     */
93
    public function getGuidExpression()
94
    {
95
        return 'UUID()';
96
    }
97
98
    /**
99
     * {@inheritDoc}
100
     */
101 View Code Duplication
    public function getLocateExpression($str, $substr, $startPos = false)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
102
    {
103
        if ($startPos == false) {
104
            return 'LOCATE(' . $substr . ', ' . $str . ')';
105
        }
106
107
        return 'LOCATE(' . $substr . ', ' . $str . ', '.$startPos.')';
108
    }
109
110
    /**
111
     * {@inheritDoc}
112
     */
113 2
    public function getConcatExpression()
114
    {
115 2
        $args = func_get_args();
116
117 2
        return 'CONCAT(' . join(', ', (array) $args) . ')';
118
    }
119
120
    /**
121
     * {@inheritdoc}
122
     */
123
    protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit)
124
    {
125
        $function = '+' === $operator ? 'DATE_ADD' : 'DATE_SUB';
126
127
        return $function . '(' . $date . ', INTERVAL ' . $interval . ' ' . $unit . ')';
128
    }
129
130
    /**
131
     * {@inheritDoc}
132
     */
133
    public function getDateDiffExpression($date1, $date2)
134
    {
135
        return 'DATEDIFF(' . $date1 . ', ' . $date2 . ')';
136
    }
137
138
    /**
139
     * {@inheritDoc}
140
     */
141 2
    public function getListDatabasesSQL()
142
    {
143 2
        return 'SHOW DATABASES';
144
    }
145
146
    /**
147
     * {@inheritDoc}
148
     */
149
    public function getListTableConstraintsSQL($table)
150
    {
151
        return 'SHOW INDEX FROM ' . $table;
152
    }
153
154
    /**
155
     * {@inheritDoc}
156
     *
157
     * Two approaches to listing the table indexes. The information_schema is
158
     * preferred, because it doesn't cause problems with SQL keywords such as "order" or "table".
159
     */
160 4 View Code Duplication
    public function getListTableIndexesSQL($table, $currentDatabase = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
161
    {
162 4
        if ($currentDatabase) {
163 4
            $currentDatabase = $this->quoteStringLiteral($currentDatabase);
164 4
            $table = $this->quoteStringLiteral($table);
165
166
            return "SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, ".
167
                   "SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, ".
168
                   "CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, " .
169
                   "NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment " .
170 4
                   "FROM information_schema.STATISTICS WHERE TABLE_NAME = " . $table . " AND TABLE_SCHEMA = " . $currentDatabase;
171
        }
172
173
        return 'SHOW INDEX FROM ' . $table;
174
    }
175
176
    /**
177
     * {@inheritDoc}
178
     */
179 2
    public function getListViewsSQL($database)
180
    {
181 2
        $database = $this->quoteStringLiteral($database);
182
183 2
        return "SELECT * FROM information_schema.VIEWS WHERE TABLE_SCHEMA = " . $database;
184
    }
185
186
    /**
187
     * {@inheritDoc}
188
     */
189 6
    public function getListTableForeignKeysSQL($table, $database = null)
190
    {
191 6
        $table = $this->quoteStringLiteral($table);
192
193 6
        if (null !== $database) {
194 4
            $database = $this->quoteStringLiteral($database);
195
        }
196
197
        $sql = "SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, ".
198
               "k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ ".
199
               "FROM information_schema.key_column_usage k /*!50116 ".
200
               "INNER JOIN information_schema.referential_constraints c ON ".
201
               "  c.constraint_name = k.constraint_name AND ".
202 6
               "  c.table_name = $table */ WHERE k.table_name = $table";
203
204 6
        $databaseNameSql = null === $database ? 'DATABASE()' : $database;
205
206 6
        $sql .= " AND k.table_schema = $databaseNameSql /*!50116 AND c.constraint_schema = $databaseNameSql */";
207 6
        $sql .= " AND k.`REFERENCED_COLUMN_NAME` is not NULL";
208
209 6
        return $sql;
210
    }
211
212
    /**
213
     * {@inheritDoc}
214
     */
215
    public function getCreateViewSQL($name, $sql)
216
    {
217
        return 'CREATE VIEW ' . $name . ' AS ' . $sql;
218
    }
219
220
    /**
221
     * {@inheritDoc}
222
     */
223
    public function getDropViewSQL($name)
224
    {
225
        return 'DROP VIEW '. $name;
226
    }
227
228
    /**
229
     * {@inheritDoc}
230
     */
231 30
    protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed)
232
    {
233 30
        return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(255)')
234 30
                : ($length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)');
235
    }
236
237
    /**
238
     * {@inheritdoc}
239
     */
240 2
    protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed)
241
    {
242 2
        return $fixed ? 'BINARY(' . ($length ?: 255) . ')' : 'VARBINARY(' . ($length ?: 255) . ')';
243
    }
244
245
    /**
246
     * Gets the SQL snippet used to declare a CLOB column type.
247
     *     TINYTEXT   : 2 ^  8 - 1 = 255
248
     *     TEXT       : 2 ^ 16 - 1 = 65535
249
     *     MEDIUMTEXT : 2 ^ 24 - 1 = 16777215
250
     *     LONGTEXT   : 2 ^ 32 - 1 = 4294967295
251
     *
252
     * @param array $field
253
     *
254
     * @return string
255
     */
256 11 View Code Duplication
    public function getClobTypeDeclarationSQL(array $field)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
257
    {
258 11
        if ( ! empty($field['length']) && is_numeric($field['length'])) {
259 3
            $length = $field['length'];
260
261 3
            if ($length <= static::LENGTH_LIMIT_TINYTEXT) {
262 2
                return 'TINYTEXT';
263
            }
264
265 3
            if ($length <= static::LENGTH_LIMIT_TEXT) {
266 3
                return 'TEXT';
267
            }
268
269 2
            if ($length <= static::LENGTH_LIMIT_MEDIUMTEXT) {
270 2
                return 'MEDIUMTEXT';
271
            }
272
        }
273
274 10
        return 'LONGTEXT';
275
    }
276
277
    /**
278
     * {@inheritDoc}
279
     */
280 2 View Code Duplication
    public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
281
    {
282 2
        if (isset($fieldDeclaration['version']) && $fieldDeclaration['version'] == true) {
283 2
            return 'TIMESTAMP';
284
        }
285
286 2
        return 'DATETIME';
287
    }
288
289
    /**
290
     * {@inheritDoc}
291
     */
292
    public function getDateTypeDeclarationSQL(array $fieldDeclaration)
293
    {
294
        return 'DATE';
295
    }
296
297
    /**
298
     * {@inheritDoc}
299
     */
300
    public function getTimeTypeDeclarationSQL(array $fieldDeclaration)
301
    {
302
        return 'TIME';
303
    }
304
305
    /**
306
     * {@inheritDoc}
307
     */
308 2
    public function getBooleanTypeDeclarationSQL(array $field)
309
    {
310 2
        return 'TINYINT(1)';
311
    }
312
313
    /**
314
     * Obtain DBMS specific SQL code portion needed to set the COLLATION
315
     * of a field declaration to be used in statements like CREATE TABLE.
316
     *
317
     * @deprecated Deprecated since version 2.5, Use {@link self::getColumnCollationDeclarationSQL()} instead.
318
     *
319
     * @param string $collation name of the collation
320
     *
321
     * @return string  DBMS specific SQL code portion needed to set the COLLATION
322
     *                 of a field declaration.
323
     */
324
    public function getCollationFieldDeclaration($collation)
325
    {
326
        return $this->getColumnCollationDeclarationSQL($collation);
327
    }
328
329
    /**
330
     * {@inheritDoc}
331
     *
332
     * MySql prefers "autoincrement" identity columns since sequences can only
333
     * be emulated with a table.
334
     */
335 2
    public function prefersIdentityColumns()
336
    {
337 2
        return true;
338
    }
339
340
    /**
341
     * {@inheritDoc}
342
     *
343
     * MySql supports this through AUTO_INCREMENT columns.
344
     */
345 2
    public function supportsIdentityColumns()
346
    {
347 2
        return true;
348
    }
349
350
    /**
351
     * {@inheritDoc}
352
     */
353 66
    public function supportsInlineColumnComments()
354
    {
355 66
        return true;
356
    }
357
358
    /**
359
     * {@inheritDoc}
360
     */
361
    public function supportsColumnCollation()
362
    {
363
        return true;
364
    }
365
366
    /**
367
     * {@inheritDoc}
368
     */
369
    public function getListTablesSQL()
370
    {
371
        return "SHOW FULL TABLES WHERE Table_type = 'BASE TABLE'";
372
    }
373
374
    /**
375
     * {@inheritDoc}
376
     */
377 4 View Code Duplication
    public function getListTableColumnsSQL($table, $database = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
378
    {
379 4
        $table = $this->quoteStringLiteral($table);
380
381 4
        if ($database) {
382 2
            $database = $this->quoteStringLiteral($database);
383
        } else {
384 2
            $database = 'DATABASE()';
385
        }
386
387
        return "SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, ".
388
               "COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, " .
389
               "CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation ".
390 4
               "FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = " . $database . " AND TABLE_NAME = " . $table;
391
    }
392
393
    /**
394
     * {@inheritDoc}
395
     */
396 2
    public function getCreateDatabaseSQL($name)
397
    {
398 2
        return 'CREATE DATABASE ' . $name;
399
    }
400
401
    /**
402
     * {@inheritDoc}
403
     */
404 2
    public function getDropDatabaseSQL($name)
405
    {
406 2
        return 'DROP DATABASE ' . $name;
407
    }
408
409
    /**
410
     * {@inheritDoc}
411
     */
412 30
    protected function _getCreateTableSQL($tableName, array $columns, array $options = [])
413
    {
414 30
        $queryFields = $this->getColumnDeclarationListSQL($columns);
415
416 30
        if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) {
417
            foreach ($options['uniqueConstraints'] as $index => $definition) {
418
                $queryFields .= ', ' . $this->getUniqueConstraintDeclarationSQL($index, $definition);
419
            }
420
        }
421
422
        // add all indexes
423 30
        if (isset($options['indexes']) && ! empty($options['indexes'])) {
424 14
            foreach ($options['indexes'] as $index => $definition) {
425 14
                $queryFields .= ', ' . $this->getIndexDeclarationSQL($index, $definition);
426
            }
427
        }
428
429
        // attach all primary keys
430 30 View Code Duplication
        if (isset($options['primary']) && ! empty($options['primary'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
431 10
            $keyColumns = array_unique(array_values($options['primary']));
432 10
            $queryFields .= ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')';
433
        }
434
435 30
        $query = 'CREATE ';
436
437 30
        if (!empty($options['temporary'])) {
438
            $query .= 'TEMPORARY ';
439
        }
440
441 30
        $query .= 'TABLE ' . $tableName . ' (' . $queryFields . ') ';
442 30
        $query .= $this->buildTableOptions($options);
443 30
        $query .= $this->buildPartitionOptions($options);
444
445 30
        $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...
446 30
        $engine = 'INNODB';
447
448 30
        if (isset($options['engine'])) {
449 6
            $engine = strtoupper(trim($options['engine']));
450
        }
451
452
        // Propagate foreign key constraints only for InnoDB.
453 30 View Code Duplication
        if (isset($options['foreignKeys']) && $engine === 'INNODB') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
454 4
            foreach ((array) $options['foreignKeys'] as $definition) {
455 4
                $sql[] = $this->getCreateForeignKeySQL($definition, $tableName);
456
            }
457
        }
458
459 30
        return $sql;
460
    }
461
462
    /**
463
     * {@inheritdoc}
464
     */
465 68
    public function getDefaultValueDeclarationSQL($field)
466
    {
467
        // Unset the default value if the given field definition does not allow default values.
468 68
        if ($field['type'] instanceof TextType || $field['type'] instanceof BlobType) {
469 6
            $field['default'] = null;
470
        }
471
472 68
        return parent::getDefaultValueDeclarationSQL($field);
473
    }
474
475
    /**
476
     * Build SQL for table options
477
     *
478
     * @param array $options
479
     *
480
     * @return string
481
     */
482 30
    private function buildTableOptions(array $options)
483
    {
484 30
        if (isset($options['table_options'])) {
485
            return $options['table_options'];
486
        }
487
488 30
        $tableOptions = [];
489
490
        // Charset
491 30
        if ( ! isset($options['charset'])) {
492 30
            $options['charset'] = 'utf8';
493
        }
494
495 30
        $tableOptions[] = sprintf('DEFAULT CHARACTER SET %s', $options['charset']);
496
497
        // Collate
498 30
        if ( ! isset($options['collate'])) {
499 30
            $options['collate'] = 'utf8_unicode_ci';
500
        }
501
502 30
        $tableOptions[] = sprintf('COLLATE %s', $options['collate']);
503
504
        // Engine
505 30
        if ( ! isset($options['engine'])) {
506 24
            $options['engine'] = 'InnoDB';
507
        }
508
509 30
        $tableOptions[] = sprintf('ENGINE = %s', $options['engine']);
510
511
        // Auto increment
512 30
        if (isset($options['auto_increment'])) {
513
            $tableOptions[] = sprintf('AUTO_INCREMENT = %s', $options['auto_increment']);
514
        }
515
516
        // Comment
517 30 View Code Duplication
        if (isset($options['comment'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
518
            $comment = trim($options['comment'], " '");
519
520
            $tableOptions[] = sprintf("COMMENT = %s ", $this->quoteStringLiteral($comment));
521
        }
522
523
        // Row format
524 30
        if (isset($options['row_format'])) {
525
            $tableOptions[] = sprintf('ROW_FORMAT = %s', $options['row_format']);
526
        }
527
528 30
        return implode(' ', $tableOptions);
529
    }
530
531
    /**
532
     * Build SQL for partition options.
533
     *
534
     * @param array $options
535
     *
536
     * @return string
537
     */
538 30
    private function buildPartitionOptions(array $options)
539
    {
540 30
        return (isset($options['partition_options']))
541
            ? ' ' . $options['partition_options']
542 30
            : '';
543
    }
544
545
    /**
546
     * {@inheritDoc}
547
     */
548 54
    public function getAlterTableSQL(TableDiff $diff)
549
    {
550 54
        $columnSql = [];
551 54
        $queryParts = [];
552 54
        if ($diff->newName !== false) {
553 4
            $queryParts[] = 'RENAME TO ' . $diff->getNewName()->getQuotedName($this);
554
        }
555
556 54 View Code Duplication
        foreach ($diff->addedColumns as $column) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
557 12
            if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) {
558
                continue;
559
            }
560
561 12
            $columnArray = $column->toArray();
562 12
            $columnArray['comment'] = $this->getColumnComment($column);
563 12
            $queryParts[] = 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray);
564
        }
565
566 54 View Code Duplication
        foreach ($diff->removedColumns as $column) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
567 6
            if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) {
568
                continue;
569
            }
570
571 6
            $queryParts[] =  'DROP ' . $column->getQuotedName($this);
572
        }
573
574 54 View Code Duplication
        foreach ($diff->changedColumns as $columnDiff) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
575 16
            if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) {
576
                continue;
577
            }
578
579
            /* @var $columnDiff \Doctrine\DBAL\Schema\ColumnDiff */
580 16
            $column = $columnDiff->column;
581 16
            $columnArray = $column->toArray();
582
583
            // Don't propagate default value changes for unsupported column types.
584 16
            if ($columnDiff->hasChanged('default') &&
585 16
                count($columnDiff->changedProperties) === 1 &&
586 16
                ($columnArray['type'] instanceof TextType || $columnArray['type'] instanceof BlobType)
587
            ) {
588 2
                continue;
589
            }
590
591 14
            $columnArray['comment'] = $this->getColumnComment($column);
592 14
            $queryParts[] =  'CHANGE ' . ($columnDiff->getOldColumnName()->getQuotedName($this)) . ' '
593 14
                    . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray);
594
        }
595
596 54 View Code Duplication
        foreach ($diff->renamedColumns as $oldColumnName => $column) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
597 8
            if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) {
598
                continue;
599
            }
600
601 8
            $oldColumnName = new Identifier($oldColumnName);
602 8
            $columnArray = $column->toArray();
603 8
            $columnArray['comment'] = $this->getColumnComment($column);
604 8
            $queryParts[] =  'CHANGE ' . $oldColumnName->getQuotedName($this) . ' '
605 8
                    . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray);
606
        }
607
608 54
        if (isset($diff->addedIndexes['primary'])) {
609 7
            $keyColumns = array_unique(array_values($diff->addedIndexes['primary']->getColumns()));
610 7
            $queryParts[] = 'ADD PRIMARY KEY (' . implode(', ', $keyColumns) . ')';
611 7
            unset($diff->addedIndexes['primary']);
612
        }
613
614 54
        $sql = [];
615 54
        $tableSql = [];
616
617 54 View Code Duplication
        if ( ! $this->onSchemaAlterTable($diff, $tableSql)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
618 54
            if (count($queryParts) > 0) {
619 27
                $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . implode(", ", $queryParts);
620
            }
621 54
            $sql = array_merge(
622 54
                $this->getPreAlterTableIndexForeignKeySQL($diff),
623 54
                $sql,
624 54
                $this->getPostAlterTableIndexForeignKeySQL($diff)
625
            );
626
        }
627
628 54
        return array_merge($sql, $tableSql, $columnSql);
629
    }
630
631
    /**
632
     * {@inheritDoc}
633
     */
634 54
    protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff)
635
    {
636 54
        $sql = [];
637 54
        $table = $diff->getName($this)->getQuotedName($this);
638
639 54
        foreach ($diff->changedIndexes as $changedIndex) {
640 11
            $sql = array_merge($sql, $this->getPreAlterTableAlterPrimaryKeySQL($diff, $changedIndex));
641
        }
642
643 54
        foreach ($diff->removedIndexes as $remKey => $remIndex) {
644 6
            $sql = array_merge($sql, $this->getPreAlterTableAlterPrimaryKeySQL($diff, $remIndex));
645
646 6
            foreach ($diff->addedIndexes as $addKey => $addIndex) {
647 2
                if ($remIndex->getColumns() == $addIndex->getColumns()) {
648
649 2
                    $indexClause = 'INDEX ' . $addIndex->getName();
650
651 2
                    if ($addIndex->isPrimary()) {
652
                        $indexClause = 'PRIMARY KEY';
653 2
                    } elseif ($addIndex->isUnique()) {
654 2
                        $indexClause = 'UNIQUE INDEX ' . $addIndex->getName();
655
                    }
656
657 2
                    $query = 'ALTER TABLE ' . $table . ' DROP INDEX ' . $remIndex->getName() . ', ';
658 2
                    $query .= 'ADD ' . $indexClause;
659 2
                    $query .= ' (' . $this->getIndexFieldDeclarationListSQL($addIndex->getQuotedColumns($this)) . ')';
660
661 2
                    $sql[] = $query;
662
663 2
                    unset($diff->removedIndexes[$remKey]);
664 2
                    unset($diff->addedIndexes[$addKey]);
665
666 6
                    break;
667
                }
668
            }
669
        }
670
671 54
        $engine = 'INNODB';
672
673 54
        if ($diff->fromTable instanceof Table && $diff->fromTable->hasOption('engine')) {
674 2
            $engine = strtoupper(trim($diff->fromTable->getOption('engine')));
675
        }
676
677
        // Suppress foreign key constraint propagation on non-supporting engines.
678 54
        if ('INNODB' !== $engine) {
679 2
            $diff->addedForeignKeys   = [];
680 2
            $diff->changedForeignKeys = [];
681 2
            $diff->removedForeignKeys = [];
682
        }
683
684 54
        $sql = array_merge(
685 54
            $sql,
686 54
            $this->getPreAlterTableAlterIndexForeignKeySQL($diff),
687 54
            parent::getPreAlterTableIndexForeignKeySQL($diff),
688 54
            $this->getPreAlterTableRenameIndexForeignKeySQL($diff)
689
        );
690
691 54
        return $sql;
692
    }
693
694
    /**
695
     * @param TableDiff $diff
696
     * @param Index     $index
697
     *
698
     * @return string[]
699
     */
700 17
    private function getPreAlterTableAlterPrimaryKeySQL(TableDiff $diff, Index $index)
701
    {
702 17
        $sql = [];
703
704 17
        if (! $index->isPrimary() || ! $diff->fromTable instanceof Table) {
705 6
            return $sql;
706
        }
707
708 11
        $tableName = $diff->getName($this)->getQuotedName($this);
709
710
        // Dropping primary keys requires to unset autoincrement attribute on the particular column first.
711 11 View Code Duplication
        foreach ($index->getColumns() as $columnName) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
712 11
            if (! $diff->fromTable->hasColumn($columnName)) {
713 2
                continue;
714
            }
715
716 11
            $column = $diff->fromTable->getColumn($columnName);
717
718 11
            if ($column->getAutoincrement() === true) {
719 6
                $column->setAutoincrement(false);
720
721 6
                $sql[] = 'ALTER TABLE ' . $tableName . ' MODIFY ' .
722 6
                    $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray());
723
724
                // original autoincrement information might be needed later on by other parts of the table alteration
725 11
                $column->setAutoincrement(true);
726
            }
727
        }
728
729 11
        return $sql;
730
    }
731
732
    /**
733
     * @param TableDiff $diff The table diff to gather the SQL for.
734
     *
735
     * @return array
736
     */
737 54
    private function getPreAlterTableAlterIndexForeignKeySQL(TableDiff $diff)
738
    {
739 54
        $sql = [];
740 54
        $table = $diff->getName($this)->getQuotedName($this);
741
742 54
        foreach ($diff->changedIndexes as $changedIndex) {
743
            // Changed primary key
744 11
            if ($changedIndex->isPrimary() && $diff->fromTable instanceof Table) {
745 9 View Code Duplication
                foreach ($diff->fromTable->getPrimaryKeyColumns() as $columnName) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
746 9
                    $column = $diff->fromTable->getColumn($columnName);
747
748
                    // Check if an autoincrement column was dropped from the primary key.
749 9
                    if ($column->getAutoincrement() && ! in_array($columnName, $changedIndex->getColumns())) {
750
                        // The autoincrement attribute needs to be removed from the dropped column
751
                        // before we can drop and recreate the primary key.
752 2
                        $column->setAutoincrement(false);
753
754 2
                        $sql[] = 'ALTER TABLE ' . $table . ' MODIFY ' .
755 2
                            $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray());
756
757
                        // Restore the autoincrement attribute as it might be needed later on
758
                        // by other parts of the table alteration.
759 11
                        $column->setAutoincrement(true);
760
                    }
761
                }
762
            }
763
        }
764
765 54
        return $sql;
766
    }
767
768
    /**
769
     * @param TableDiff $diff The table diff to gather the SQL for.
770
     *
771
     * @return array
772
     */
773 28
    protected function getPreAlterTableRenameIndexForeignKeySQL(TableDiff $diff)
774
    {
775 28
        $sql = [];
776 28
        $tableName = $diff->getName($this)->getQuotedName($this);
777
778 28 View Code Duplication
        foreach ($this->getRemainingForeignKeyConstraintsRequiringRenamedIndexes($diff) as $foreignKey) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
779 1
            if (! in_array($foreignKey, $diff->changedForeignKeys, true)) {
780 1
                $sql[] = $this->getDropForeignKeySQL($foreignKey, $tableName);
781
            }
782
        }
783
784 28
        return $sql;
785
    }
786
787
    /**
788
     * Returns the remaining foreign key constraints that require one of the renamed indexes.
789
     *
790
     * "Remaining" here refers to the diff between the foreign keys currently defined in the associated
791
     * table and the foreign keys to be removed.
792
     *
793
     * @param TableDiff $diff The table diff to evaluate.
794
     *
795
     * @return array
796
     */
797 28
    private function getRemainingForeignKeyConstraintsRequiringRenamedIndexes(TableDiff $diff)
798
    {
799 28
        if (empty($diff->renamedIndexes) || ! $diff->fromTable instanceof Table) {
800 23
            return [];
801
        }
802
803 5
        $foreignKeys = [];
804
        /** @var \Doctrine\DBAL\Schema\ForeignKeyConstraint[] $remainingForeignKeys */
805 5
        $remainingForeignKeys = array_diff_key(
806 5
            $diff->fromTable->getForeignKeys(),
807 5
            $diff->removedForeignKeys
808
        );
809
810 5
        foreach ($remainingForeignKeys as $foreignKey) {
811 1
            foreach ($diff->renamedIndexes as $index) {
812 1
                if ($foreignKey->intersectsIndexColumns($index)) {
813 1
                    $foreignKeys[] = $foreignKey;
814
815 1
                    break;
816
                }
817
            }
818
        }
819
820 5
        return $foreignKeys;
821
    }
822
823
    /**
824
     * {@inheritdoc}
825
     */
826 54
    protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff)
827
    {
828 54
        return array_merge(
829 54
            parent::getPostAlterTableIndexForeignKeySQL($diff),
830 54
            $this->getPostAlterTableRenameIndexForeignKeySQL($diff)
831
        );
832
    }
833
834
    /**
835
     * @param TableDiff $diff The table diff to gather the SQL for.
836
     *
837
     * @return array
838
     */
839 28
    protected function getPostAlterTableRenameIndexForeignKeySQL(TableDiff $diff)
840
    {
841 28
        $sql = [];
842 28
        $tableName = (false !== $diff->newName)
843 2
            ? $diff->getNewName()->getQuotedName($this)
844 28
            : $diff->getName($this)->getQuotedName($this);
845
846 28 View Code Duplication
        foreach ($this->getRemainingForeignKeyConstraintsRequiringRenamedIndexes($diff) as $foreignKey) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
847 1
            if (! in_array($foreignKey, $diff->changedForeignKeys, true)) {
848 1
                $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableName);
849
            }
850
        }
851
852 28
        return $sql;
853
    }
854
855
    /**
856
     * {@inheritDoc}
857
     */
858 29 View Code Duplication
    protected function getCreateIndexSQLFlags(Index $index)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
859
    {
860 29
        $type = '';
861 29
        if ($index->isUnique()) {
862 6
            $type .= 'UNIQUE ';
863 23
        } elseif ($index->hasFlag('fulltext')) {
864 2
            $type .= 'FULLTEXT ';
865 21
        } elseif ($index->hasFlag('spatial')) {
866 2
            $type .= 'SPATIAL ';
867
        }
868
869 29
        return $type;
870
    }
871
872
    /**
873
     * {@inheritDoc}
874
     */
875 38
    public function getIntegerTypeDeclarationSQL(array $field)
876
    {
877 38
        return 'INT' . $this->_getCommonIntegerTypeDeclarationSQL($field);
878
    }
879
880
    /**
881
     * {@inheritDoc}
882
     */
883
    public function getBigIntTypeDeclarationSQL(array $field)
884
    {
885
        return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($field);
886
    }
887
888
    /**
889
     * {@inheritDoc}
890
     */
891
    public function getSmallIntTypeDeclarationSQL(array $field)
892
    {
893
        return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($field);
894
    }
895
896
    /**
897
     * {@inheritdoc}
898
     */
899 12
    public function getFloatDeclarationSQL(array $field)
900
    {
901 12
        return 'DOUBLE PRECISION' . $this->getUnsignedDeclaration($field);
902
    }
903
904
    /**
905
     * {@inheritdoc}
906
     */
907 12
    public function getDecimalTypeDeclarationSQL(array $columnDef)
908
    {
909 12
        return parent::getDecimalTypeDeclarationSQL($columnDef) . $this->getUnsignedDeclaration($columnDef);
910
    }
911
912
    /**
913
     * Get unsigned declaration for a column.
914
     *
915
     * @param array $columnDef
916
     *
917
     * @return string
918
     */
919 62
    private function getUnsignedDeclaration(array $columnDef)
920
    {
921 62
        return ! empty($columnDef['unsigned']) ? ' UNSIGNED' : '';
922
    }
923
924
    /**
925
     * {@inheritDoc}
926
     */
927 38
    protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef)
928
    {
929 38
        $autoinc = '';
930 38
        if ( ! empty($columnDef['autoincrement'])) {
931 6
            $autoinc = ' AUTO_INCREMENT';
932
        }
933
934 38
        return $this->getUnsignedDeclaration($columnDef) . $autoinc;
935
    }
936
937
    /**
938
     * {@inheritDoc}
939
     */
940 14
    public function getAdvancedForeignKeyOptionsSQL(\Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey)
941
    {
942 14
        $query = '';
943 14
        if ($foreignKey->hasOption('match')) {
944
            $query .= ' MATCH ' . $foreignKey->getOption('match');
945
        }
946 14
        $query .= parent::getAdvancedForeignKeyOptionsSQL($foreignKey);
947
948 14
        return $query;
949
    }
950
951
    /**
952
     * {@inheritDoc}
953
     */
954 20 View Code Duplication
    public function getDropIndexSQL($index, $table=null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
955
    {
956 20
        if ($index instanceof Index) {
957 15
            $indexName = $index->getQuotedName($this);
958 5
        } elseif (is_string($index)) {
959 5
            $indexName = $index;
960
        } else {
961
            throw new \InvalidArgumentException('MysqlPlatform::getDropIndexSQL() expects $index parameter to be string or \Doctrine\DBAL\Schema\Index.');
962
        }
963
964 20
        if ($table instanceof Table) {
965
            $table = $table->getQuotedName($this);
966 20
        } elseif (!is_string($table)) {
967
            throw new \InvalidArgumentException('MysqlPlatform::getDropIndexSQL() expects $table parameter to be string or \Doctrine\DBAL\Schema\Table.');
968
        }
969
970 20
        if ($index instanceof Index && $index->isPrimary()) {
971
            // mysql primary keys are always named "PRIMARY",
972
            // so we cannot use them in statements because of them being keyword.
973 13
            return $this->getDropPrimaryKeySQL($table);
974
        }
975
976 7
        return 'DROP INDEX ' . $indexName . ' ON ' . $table;
977
    }
978
979
    /**
980
     * @param string $table
981
     *
982
     * @return string
983
     */
984 13
    protected function getDropPrimaryKeySQL($table)
985
    {
986 13
        return 'ALTER TABLE ' . $table . ' DROP PRIMARY KEY';
987
    }
988
989
    /**
990
     * {@inheritDoc}
991
     */
992 2
    public function getSetTransactionIsolationSQL($level)
993
    {
994 2
        return 'SET SESSION TRANSACTION ISOLATION LEVEL ' . $this->_getTransactionIsolationLevelSQL($level);
995
    }
996
997
    /**
998
     * {@inheritDoc}
999
     */
1000 4
    public function getName()
1001
    {
1002 4
        return 'mysql';
1003
    }
1004
1005
    /**
1006
     * {@inheritDoc}
1007
     */
1008
    public function getReadLockSQL()
1009
    {
1010
        return 'LOCK IN SHARE MODE';
1011
    }
1012
1013
    /**
1014
     * {@inheritDoc}
1015
     */
1016 11
    protected function initializeDoctrineTypeMappings()
1017
    {
1018 11
        $this->doctrineTypeMapping = [
1019
            'tinyint'       => 'boolean',
1020
            'smallint'      => 'smallint',
1021
            'mediumint'     => 'integer',
1022
            'int'           => 'integer',
1023
            'integer'       => 'integer',
1024
            'bigint'        => 'bigint',
1025
            'tinytext'      => 'text',
1026
            'mediumtext'    => 'text',
1027
            'longtext'      => 'text',
1028
            'text'          => 'text',
1029
            'varchar'       => 'string',
1030
            'string'        => 'string',
1031
            'char'          => 'string',
1032
            'date'          => 'date',
1033
            'datetime'      => 'datetime',
1034
            'timestamp'     => 'datetime',
1035
            'time'          => 'time',
1036
            'float'         => 'float',
1037
            'double'        => 'float',
1038
            'real'          => 'float',
1039
            'decimal'       => 'decimal',
1040
            'numeric'       => 'decimal',
1041
            'year'          => 'date',
1042
            'longblob'      => 'blob',
1043
            'blob'          => 'blob',
1044
            'mediumblob'    => 'blob',
1045
            'tinyblob'      => 'blob',
1046
            'binary'        => 'binary',
1047
            'varbinary'     => 'binary',
1048
            'set'           => 'simple_array',
1049
        ];
1050 11
    }
1051
1052
    /**
1053
     * {@inheritDoc}
1054
     */
1055 30
    public function getVarcharMaxLength()
1056
    {
1057 30
        return 65535;
1058
    }
1059
1060
    /**
1061
     * {@inheritdoc}
1062
     */
1063 4
    public function getBinaryMaxLength()
1064
    {
1065 4
        return 65535;
1066
    }
1067
1068
    /**
1069
     * {@inheritDoc}
1070
     */
1071 62
    protected function getReservedKeywordsClass()
1072
    {
1073 62
        return Keywords\MySQLKeywords::class;
1074
    }
1075
1076
    /**
1077
     * {@inheritDoc}
1078
     *
1079
     * MySQL commits a transaction implicitly when DROP TABLE is executed, however not
1080
     * if DROP TEMPORARY TABLE is executed.
1081
     */
1082 View Code Duplication
    public function getDropTemporaryTableSQL($table)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1083
    {
1084
        if ($table instanceof Table) {
1085
            $table = $table->getQuotedName($this);
1086
        } elseif (!is_string($table)) {
1087
            throw new \InvalidArgumentException('getDropTemporaryTableSQL() expects $table parameter to be string or \Doctrine\DBAL\Schema\Table.');
1088
        }
1089
1090
        return 'DROP TEMPORARY TABLE ' . $table;
1091
    }
1092
1093
    /**
1094
     * Gets the SQL Snippet used to declare a BLOB column type.
1095
     *     TINYBLOB   : 2 ^  8 - 1 = 255
1096
     *     BLOB       : 2 ^ 16 - 1 = 65535
1097
     *     MEDIUMBLOB : 2 ^ 24 - 1 = 16777215
1098
     *     LONGBLOB   : 2 ^ 32 - 1 = 4294967295
1099
     *
1100
     * @param array $field
1101
     *
1102
     * @return string
1103
     */
1104 6 View Code Duplication
    public function getBlobTypeDeclarationSQL(array $field)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1105
    {
1106 6
        if ( ! empty($field['length']) && is_numeric($field['length'])) {
1107 4
            $length = $field['length'];
1108
1109 4
            if ($length <= static::LENGTH_LIMIT_TINYBLOB) {
1110 2
                return 'TINYBLOB';
1111
            }
1112
1113 4
            if ($length <= static::LENGTH_LIMIT_BLOB) {
1114 2
                return 'BLOB';
1115
            }
1116
1117 4
            if ($length <= static::LENGTH_LIMIT_MEDIUMBLOB) {
1118 4
                return 'MEDIUMBLOB';
1119
            }
1120
        }
1121
1122 6
        return 'LONGBLOB';
1123
    }
1124
1125
    /**
1126
     * {@inheritdoc}
1127
     */
1128 42
    public function quoteStringLiteral($str)
1129
    {
1130 42
        $str = str_replace('\\', '\\\\', $str); // MySQL requires backslashes to be escaped aswell.
1131
1132 42
        return parent::quoteStringLiteral($str);
1133
    }
1134
1135
    /**
1136
     * {@inheritdoc}
1137
     */
1138 1
    public function getDefaultTransactionIsolationLevel()
1139
    {
1140 1
        return Connection::TRANSACTION_REPEATABLE_READ;
1141
    }
1142
}
1143