Failed Conditions
Push — master ( 656579...2742cd )
by Marco
11:55
created

getPreAlterTableRenameIndexForeignKeySQL()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 6

Duplication

Lines 5
Ratio 41.67 %

Code Coverage

Tests 7
CRAP Score 3

Importance

Changes 0
Metric Value
dl 5
loc 12
ccs 7
cts 7
cp 1
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 6
nc 3
nop 1
crap 3
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 9
    protected function doModifyLimitQuery($query, $limit, $offset)
61
    {
62 9
        if ($limit !== null) {
63 6
            $query .= ' LIMIT ' . $limit;
64 6
            if ($offset !== null) {
65 6
                $query .= ' OFFSET ' . $offset;
66
            }
67 3
        } elseif ($offset !== null) {
68 3
            $query .= ' LIMIT 18446744073709551615 OFFSET ' . $offset;
69
        }
70
71 9
        return $query;
72
    }
73
74
    /**
75
     * {@inheritDoc}
76
     */
77 82
    public function getIdentifierQuoteCharacter()
78
    {
79 82
        return '`';
80
    }
81
82
    /**
83
     * {@inheritDoc}
84
     */
85 3
    public function getRegexpExpression()
86
    {
87 3
        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 3
    public function getConcatExpression()
114
    {
115 3
        $args = func_get_args();
116
117 3
        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 3
    public function getListDatabasesSQL()
142
    {
143 3
        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 6 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 6
        if ($currentDatabase) {
163 6
            $currentDatabase = $this->quoteStringLiteral($currentDatabase);
164 6
            $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 6
                   "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 3
    public function getListViewsSQL($database)
180
    {
181 3
        $database = $this->quoteStringLiteral($database);
182
183 3
        return "SELECT * FROM information_schema.VIEWS WHERE TABLE_SCHEMA = " . $database;
184
    }
185
186
    /**
187
     * {@inheritDoc}
188
     */
189 9
    public function getListTableForeignKeysSQL($table, $database = null)
190
    {
191 9
        $table = $this->quoteStringLiteral($table);
192
193 9
        if (null !== $database) {
194 6
            $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 9
               "  c.table_name = $table */ WHERE k.table_name = $table";
203
204 9
        $databaseNameSql = null === $database ? 'DATABASE()' : $database;
205
206 9
        $sql .= " AND k.table_schema = $databaseNameSql /*!50116 AND c.constraint_schema = $databaseNameSql */";
207 9
        $sql .= " AND k.`REFERENCED_COLUMN_NAME` is not NULL";
208
209 9
        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 45
    protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed)
232
    {
233 45
        return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(255)')
234 45
                : ($length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)');
235
    }
236
237
    /**
238
     * {@inheritdoc}
239
     */
240 3
    protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed)
241
    {
242 3
        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 15 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 15
        if ( ! empty($field['length']) && is_numeric($field['length'])) {
259 4
            $length = $field['length'];
260
261 4
            if ($length <= static::LENGTH_LIMIT_TINYTEXT) {
262 3
                return 'TINYTEXT';
263
            }
264
265 4
            if ($length <= static::LENGTH_LIMIT_TEXT) {
266 4
                return 'TEXT';
267
            }
268
269 3
            if ($length <= static::LENGTH_LIMIT_MEDIUMTEXT) {
270 3
                return 'MEDIUMTEXT';
271
            }
272
        }
273
274 14
        return 'LONGTEXT';
275
    }
276
277
    /**
278
     * {@inheritDoc}
279
     */
280 3 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 3
        if (isset($fieldDeclaration['version']) && $fieldDeclaration['version'] == true) {
283 3
            return 'TIMESTAMP';
284
        }
285
286 3
        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 3
    public function getBooleanTypeDeclarationSQL(array $field)
309
    {
310 3
        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 3
    public function prefersIdentityColumns()
336
    {
337 3
        return true;
338
    }
339
340
    /**
341
     * {@inheritDoc}
342
     *
343
     * MySql supports this through AUTO_INCREMENT columns.
344
     */
345 3
    public function supportsIdentityColumns()
346
    {
347 3
        return true;
348
    }
349
350
    /**
351
     * {@inheritDoc}
352
     */
353 97
    public function supportsInlineColumnComments()
354
    {
355 97
        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 6 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 6
        $table = $this->quoteStringLiteral($table);
380
381 6
        if ($database) {
382 3
            $database = $this->quoteStringLiteral($database);
383
        } else {
384 3
            $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 6
               "FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = " . $database . " AND TABLE_NAME = " . $table;
391
    }
392
393
    /**
394
     * {@inheritDoc}
395
     */
396 3
    public function getCreateDatabaseSQL($name)
397
    {
398 3
        return 'CREATE DATABASE ' . $name;
399
    }
400
401
    /**
402
     * {@inheritDoc}
403
     */
404 3
    public function getDropDatabaseSQL($name)
405
    {
406 3
        return 'DROP DATABASE ' . $name;
407
    }
408
409
    /**
410
     * {@inheritDoc}
411
     */
412 43
    protected function _getCreateTableSQL($tableName, array $columns, array $options = [])
413
    {
414 43
        $queryFields = $this->getColumnDeclarationListSQL($columns);
415
416 43
        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 43
        if (isset($options['indexes']) && ! empty($options['indexes'])) {
424 20
            foreach ($options['indexes'] as $index => $definition) {
425 20
                $queryFields .= ', ' . $this->getIndexDeclarationSQL($index, $definition);
426
            }
427
        }
428
429
        // attach all primary keys
430 43 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 15
            $keyColumns = array_unique(array_values($options['primary']));
432 15
            $queryFields .= ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')';
433
        }
434
435 43
        $query = 'CREATE ';
436
437 43
        if (!empty($options['temporary'])) {
438
            $query .= 'TEMPORARY ';
439
        }
440
441 43
        $query .= 'TABLE ' . $tableName . ' (' . $queryFields . ') ';
442 43
        $query .= $this->buildTableOptions($options);
443 43
        $query .= $this->buildPartitionOptions($options);
444
445 43
        $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 43
        $engine = 'INNODB';
447
448 43
        if (isset($options['engine'])) {
449 9
            $engine = strtoupper(trim($options['engine']));
450
        }
451
452
        // Propagate foreign key constraints only for InnoDB.
453 43 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 6
            foreach ((array) $options['foreignKeys'] as $definition) {
455 6
                $sql[] = $this->getCreateForeignKeySQL($definition, $tableName);
456
            }
457
        }
458
459 43
        return $sql;
460
    }
461
462
    /**
463
     * {@inheritdoc}
464
     */
465 100
    public function getDefaultValueDeclarationSQL($field)
466
    {
467
        // Unset the default value if the given field definition does not allow default values.
468 100
        if ($field['type'] instanceof TextType || $field['type'] instanceof BlobType) {
469 8
            $field['default'] = null;
470
        }
471
472 100
        return parent::getDefaultValueDeclarationSQL($field);
473
    }
474
475
    /**
476
     * Build SQL for table options
477
     *
478
     * @param array $options
479
     *
480
     * @return string
481
     */
482 43
    private function buildTableOptions(array $options)
483
    {
484 43
        if (isset($options['table_options'])) {
485
            return $options['table_options'];
486
        }
487
488 43
        $tableOptions = [];
489
490
        // Charset
491 43
        if ( ! isset($options['charset'])) {
492 43
            $options['charset'] = 'utf8';
493
        }
494
495 43
        $tableOptions[] = sprintf('DEFAULT CHARACTER SET %s', $options['charset']);
496
497
        // Collate
498 43
        if ( ! isset($options['collate'])) {
499 43
            $options['collate'] = 'utf8_unicode_ci';
500
        }
501
502 43
        $tableOptions[] = sprintf('COLLATE %s', $options['collate']);
503
504
        // Engine
505 43
        if ( ! isset($options['engine'])) {
506 34
            $options['engine'] = 'InnoDB';
507
        }
508
509 43
        $tableOptions[] = sprintf('ENGINE = %s', $options['engine']);
510
511
        // Auto increment
512 43
        if (isset($options['auto_increment'])) {
513
            $tableOptions[] = sprintf('AUTO_INCREMENT = %s', $options['auto_increment']);
514
        }
515
516
        // Comment
517 43 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 43
        if (isset($options['row_format'])) {
525
            $tableOptions[] = sprintf('ROW_FORMAT = %s', $options['row_format']);
526
        }
527
528 43
        return implode(' ', $tableOptions);
529
    }
530
531
    /**
532
     * Build SQL for partition options.
533
     *
534
     * @param array $options
535
     *
536
     * @return string
537
     */
538 43
    private function buildPartitionOptions(array $options)
539
    {
540 43
        return (isset($options['partition_options']))
541
            ? ' ' . $options['partition_options']
542 43
            : '';
543
    }
544
545
    /**
546
     * {@inheritDoc}
547
     */
548 79
    public function getAlterTableSQL(TableDiff $diff)
549
    {
550 79
        $columnSql = [];
551 79
        $queryParts = [];
552 79
        if ($diff->newName !== false) {
553 6
            $queryParts[] = 'RENAME TO ' . $diff->getNewName()->getQuotedName($this);
554
        }
555
556 79 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 18
            if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) {
558
                continue;
559
            }
560
561 18
            $columnArray = $column->toArray();
562 18
            $columnArray['comment'] = $this->getColumnComment($column);
563 18
            $queryParts[] = 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray);
564
        }
565
566 79 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 9
            if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) {
568
                continue;
569
            }
570
571 9
            $queryParts[] =  'DROP ' . $column->getQuotedName($this);
572
        }
573
574 79 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 23
            if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) {
576
                continue;
577
            }
578
579
            /* @var $columnDiff \Doctrine\DBAL\Schema\ColumnDiff */
580 23
            $column = $columnDiff->column;
581 23
            $columnArray = $column->toArray();
582
583
            // Don't propagate default value changes for unsupported column types.
584 23
            if ($columnDiff->hasChanged('default') &&
585 23
                count($columnDiff->changedProperties) === 1 &&
586 23
                ($columnArray['type'] instanceof TextType || $columnArray['type'] instanceof BlobType)
587
            ) {
588 2
                continue;
589
            }
590
591 21
            $columnArray['comment'] = $this->getColumnComment($column);
592 21
            $queryParts[] =  'CHANGE ' . ($columnDiff->getOldColumnName()->getQuotedName($this)) . ' '
593 21
                    . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray);
594
        }
595
596 79 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 12
            if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) {
598
                continue;
599
            }
600
601 12
            $oldColumnName = new Identifier($oldColumnName);
602 12
            $columnArray = $column->toArray();
603 12
            $columnArray['comment'] = $this->getColumnComment($column);
604 12
            $queryParts[] =  'CHANGE ' . $oldColumnName->getQuotedName($this) . ' '
605 12
                    . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray);
606
        }
607
608 79
        if (isset($diff->addedIndexes['primary'])) {
609 10
            $keyColumns = array_unique(array_values($diff->addedIndexes['primary']->getColumns()));
610 10
            $queryParts[] = 'ADD PRIMARY KEY (' . implode(', ', $keyColumns) . ')';
611 10
            unset($diff->addedIndexes['primary']);
612
        }
613
614 79
        $sql = [];
615 79
        $tableSql = [];
616
617 79 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 79
            if (count($queryParts) > 0) {
619 40
                $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . implode(", ", $queryParts);
620
            }
621 79
            $sql = array_merge(
622 79
                $this->getPreAlterTableIndexForeignKeySQL($diff),
623 79
                $sql,
624 79
                $this->getPostAlterTableIndexForeignKeySQL($diff)
625
            );
626
        }
627
628 79
        return array_merge($sql, $tableSql, $columnSql);
629
    }
630
631
    /**
632
     * {@inheritDoc}
633
     */
634 79
    protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff)
635
    {
636 79
        $sql = [];
637 79
        $table = $diff->getName($this)->getQuotedName($this);
638
639 79
        foreach ($diff->changedIndexes as $changedIndex) {
640 16
            $sql = array_merge($sql, $this->getPreAlterTableAlterPrimaryKeySQL($diff, $changedIndex));
641
        }
642
643 79
        foreach ($diff->removedIndexes as $remKey => $remIndex) {
644 9
            $sql = array_merge($sql, $this->getPreAlterTableAlterPrimaryKeySQL($diff, $remIndex));
645
646 9
            foreach ($diff->addedIndexes as $addKey => $addIndex) {
647 3
                if ($remIndex->getColumns() == $addIndex->getColumns()) {
648
649 3
                    $indexClause = 'INDEX ' . $addIndex->getName();
650
651 3
                    if ($addIndex->isPrimary()) {
652
                        $indexClause = 'PRIMARY KEY';
653 3
                    } elseif ($addIndex->isUnique()) {
654 3
                        $indexClause = 'UNIQUE INDEX ' . $addIndex->getName();
655
                    }
656
657 3
                    $query = 'ALTER TABLE ' . $table . ' DROP INDEX ' . $remIndex->getName() . ', ';
658 3
                    $query .= 'ADD ' . $indexClause;
659 3
                    $query .= ' (' . $this->getIndexFieldDeclarationListSQL($addIndex->getQuotedColumns($this)) . ')';
660
661 3
                    $sql[] = $query;
662
663 3
                    unset($diff->removedIndexes[$remKey]);
664 3
                    unset($diff->addedIndexes[$addKey]);
665
666 9
                    break;
667
                }
668
            }
669
        }
670
671 79
        $engine = 'INNODB';
672
673 79
        if ($diff->fromTable instanceof Table && $diff->fromTable->hasOption('engine')) {
674 3
            $engine = strtoupper(trim($diff->fromTable->getOption('engine')));
675
        }
676
677
        // Suppress foreign key constraint propagation on non-supporting engines.
678 79
        if ('INNODB' !== $engine) {
679 3
            $diff->addedForeignKeys   = [];
680 3
            $diff->changedForeignKeys = [];
681 3
            $diff->removedForeignKeys = [];
682
        }
683
684 79
        $sql = array_merge(
685 79
            $sql,
686 79
            $this->getPreAlterTableAlterIndexForeignKeySQL($diff),
687 79
            parent::getPreAlterTableIndexForeignKeySQL($diff),
688 79
            $this->getPreAlterTableRenameIndexForeignKeySQL($diff)
689
        );
690
691 79
        return $sql;
692
    }
693
694
    /**
695
     * @param TableDiff $diff
696
     * @param Index     $index
697
     *
698
     * @return string[]
699
     */
700 25
    private function getPreAlterTableAlterPrimaryKeySQL(TableDiff $diff, Index $index)
701
    {
702 25
        $sql = [];
703
704 25
        if (! $index->isPrimary() || ! $diff->fromTable instanceof Table) {
705 9
            return $sql;
706
        }
707
708 16
        $tableName = $diff->getName($this)->getQuotedName($this);
709
710
        // Dropping primary keys requires to unset autoincrement attribute on the particular column first.
711 16 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 16
            if (! $diff->fromTable->hasColumn($columnName)) {
713 3
                continue;
714
            }
715
716 16
            $column = $diff->fromTable->getColumn($columnName);
717
718 16
            if ($column->getAutoincrement() === true) {
719 9
                $column->setAutoincrement(false);
720
721 9
                $sql[] = 'ALTER TABLE ' . $tableName . ' MODIFY ' .
722 9
                    $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 16
                $column->setAutoincrement(true);
726
            }
727
        }
728
729 16
        return $sql;
730
    }
731
732
    /**
733
     * @param TableDiff $diff The table diff to gather the SQL for.
734
     *
735
     * @return array
736
     */
737 79
    private function getPreAlterTableAlterIndexForeignKeySQL(TableDiff $diff)
738
    {
739 79
        $sql = [];
740 79
        $table = $diff->getName($this)->getQuotedName($this);
741
742 79
        foreach ($diff->changedIndexes as $changedIndex) {
743
            // Changed primary key
744 16
            if ($changedIndex->isPrimary() && $diff->fromTable instanceof Table) {
745 13 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 13
                    $column = $diff->fromTable->getColumn($columnName);
747
748
                    // Check if an autoincrement column was dropped from the primary key.
749 13
                    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 3
                        $column->setAutoincrement(false);
753
754 3
                        $sql[] = 'ALTER TABLE ' . $table . ' MODIFY ' .
755 3
                            $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 16
                        $column->setAutoincrement(true);
760
                    }
761
                }
762
            }
763
        }
764
765 79
        return $sql;
766
    }
767
768
    /**
769
     * @param TableDiff $diff The table diff to gather the SQL for.
770
     *
771
     * @return array
772
     */
773 53
    protected function getPreAlterTableRenameIndexForeignKeySQL(TableDiff $diff)
774
    {
775 53
        $sql = [];
776 53
        $tableName = $diff->getName($this)->getQuotedName($this);
777
778 53 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 2
            if (! in_array($foreignKey, $diff->changedForeignKeys, true)) {
780 2
                $sql[] = $this->getDropForeignKeySQL($foreignKey, $tableName);
781
            }
782
        }
783
784 53
        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 53
    private function getRemainingForeignKeyConstraintsRequiringRenamedIndexes(TableDiff $diff)
798
    {
799 53
        if (empty($diff->renamedIndexes) || ! $diff->fromTable instanceof Table) {
800 43
            return [];
801
        }
802
803 10
        $foreignKeys = [];
804
        /** @var \Doctrine\DBAL\Schema\ForeignKeyConstraint[] $remainingForeignKeys */
805 10
        $remainingForeignKeys = array_diff_key(
806 10
            $diff->fromTable->getForeignKeys(),
807 10
            $diff->removedForeignKeys
808
        );
809
810 10
        foreach ($remainingForeignKeys as $foreignKey) {
811 2
            foreach ($diff->renamedIndexes as $index) {
812 2
                if ($foreignKey->intersectsIndexColumns($index)) {
813 2
                    $foreignKeys[] = $foreignKey;
814
815 2
                    break;
816
                }
817
            }
818
        }
819
820 10
        return $foreignKeys;
821
    }
822
823
    /**
824
     * {@inheritdoc}
825
     */
826 79
    protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff)
827
    {
828 79
        return array_merge(
829 79
            parent::getPostAlterTableIndexForeignKeySQL($diff),
830 79
            $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 53
    protected function getPostAlterTableRenameIndexForeignKeySQL(TableDiff $diff)
840
    {
841 53
        $sql = [];
842 53
        $tableName = (false !== $diff->newName)
843 4
            ? $diff->getNewName()->getQuotedName($this)
844 53
            : $diff->getName($this)->getQuotedName($this);
845
846 53 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 2
            if (! in_array($foreignKey, $diff->changedForeignKeys, true)) {
848 2
                $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableName);
849
            }
850
        }
851
852 53
        return $sql;
853
    }
854
855
    /**
856
     * {@inheritDoc}
857
     */
858 45 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 45
        $type = '';
861 45
        if ($index->isUnique()) {
862 9
            $type .= 'UNIQUE ';
863 36
        } elseif ($index->hasFlag('fulltext')) {
864 3
            $type .= 'FULLTEXT ';
865 33
        } elseif ($index->hasFlag('spatial')) {
866 3
            $type .= 'SPATIAL ';
867
        }
868
869 45
        return $type;
870
    }
871
872
    /**
873
     * {@inheritDoc}
874
     */
875 56
    public function getIntegerTypeDeclarationSQL(array $field)
876
    {
877 56
        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 18
    public function getFloatDeclarationSQL(array $field)
900
    {
901 18
        return 'DOUBLE PRECISION' . $this->getUnsignedDeclaration($field);
902
    }
903
904
    /**
905
     * {@inheritdoc}
906
     */
907 18
    public function getDecimalTypeDeclarationSQL(array $columnDef)
908
    {
909 18
        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 92
    private function getUnsignedDeclaration(array $columnDef)
920
    {
921 92
        return ! empty($columnDef['unsigned']) ? ' UNSIGNED' : '';
922
    }
923
924
    /**
925
     * {@inheritDoc}
926
     */
927 56
    protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef)
928
    {
929 56
        $autoinc = '';
930 56
        if ( ! empty($columnDef['autoincrement'])) {
931 9
            $autoinc = ' AUTO_INCREMENT';
932
        }
933
934 56
        return $this->getUnsignedDeclaration($columnDef) . $autoinc;
935
    }
936
937
    /**
938
     * {@inheritDoc}
939
     */
940 21
    public function getAdvancedForeignKeyOptionsSQL(\Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey)
941
    {
942 21
        $query = '';
943 21
        if ($foreignKey->hasOption('match')) {
944
            $query .= ' MATCH ' . $foreignKey->getOption('match');
945
        }
946 21
        $query .= parent::getAdvancedForeignKeyOptionsSQL($foreignKey);
947
948 21
        return $query;
949
    }
950
951
    /**
952
     * {@inheritDoc}
953
     */
954 32 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 32
        if ($index instanceof Index) {
957 22
            $indexName = $index->getQuotedName($this);
958 10
        } elseif (is_string($index)) {
959 10
            $indexName = $index;
960
        } else {
961
            throw new \InvalidArgumentException('MysqlPlatform::getDropIndexSQL() expects $index parameter to be string or \Doctrine\DBAL\Schema\Index.');
962
        }
963
964 32
        if ($table instanceof Table) {
965
            $table = $table->getQuotedName($this);
966 32
        } elseif (!is_string($table)) {
967
            throw new \InvalidArgumentException('MysqlPlatform::getDropIndexSQL() expects $table parameter to be string or \Doctrine\DBAL\Schema\Table.');
968
        }
969
970 32
        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 19
            return $this->getDropPrimaryKeySQL($table);
974
        }
975
976 13
        return 'DROP INDEX ' . $indexName . ' ON ' . $table;
977
    }
978
979
    /**
980
     * @param string $table
981
     *
982
     * @return string
983
     */
984 19
    protected function getDropPrimaryKeySQL($table)
985
    {
986 19
        return 'ALTER TABLE ' . $table . ' DROP PRIMARY KEY';
987
    }
988
989
    /**
990
     * {@inheritDoc}
991
     */
992 3
    public function getSetTransactionIsolationSQL($level)
993
    {
994 3
        return 'SET SESSION TRANSACTION ISOLATION LEVEL ' . $this->_getTransactionIsolationLevelSQL($level);
995
    }
996
997
    /**
998
     * {@inheritDoc}
999
     */
1000 6
    public function getName()
1001
    {
1002 6
        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 17
    protected function initializeDoctrineTypeMappings()
1017
    {
1018 17
        $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 17
    }
1051
1052
    /**
1053
     * {@inheritDoc}
1054
     */
1055 45
    public function getVarcharMaxLength()
1056
    {
1057 45
        return 65535;
1058
    }
1059
1060
    /**
1061
     * {@inheritdoc}
1062
     */
1063 6
    public function getBinaryMaxLength()
1064
    {
1065 6
        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 8 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 8
        if ( ! empty($field['length']) && is_numeric($field['length'])) {
1107 6
            $length = $field['length'];
1108
1109 6
            if ($length <= static::LENGTH_LIMIT_TINYBLOB) {
1110 3
                return 'TINYBLOB';
1111
            }
1112
1113 6
            if ($length <= static::LENGTH_LIMIT_BLOB) {
1114 3
                return 'BLOB';
1115
            }
1116
1117 6
            if ($length <= static::LENGTH_LIMIT_MEDIUMBLOB) {
1118 6
                return 'MEDIUMBLOB';
1119
            }
1120
        }
1121
1122 8
        return 'LONGBLOB';
1123
    }
1124
1125
    /**
1126
     * {@inheritdoc}
1127
     */
1128 63
    public function quoteStringLiteral($str)
1129
    {
1130 63
        $str = str_replace('\\', '\\\\', $str); // MySQL requires backslashes to be escaped aswell.
1131
1132 63
        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