Failed Conditions
Push — master ( c8e6c9...9f843b )
by Marco
11:13
created

MySqlPlatform::getListDatabasesSQL()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 1
nc 1
nop 0
crap 1
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], $diff->addedIndexes[$addKey]);
664
665 9
                    break;
666
                }
667
            }
668
        }
669
670 79
        $engine = 'INNODB';
671
672 79
        if ($diff->fromTable instanceof Table && $diff->fromTable->hasOption('engine')) {
673 3
            $engine = strtoupper(trim($diff->fromTable->getOption('engine')));
674
        }
675
676
        // Suppress foreign key constraint propagation on non-supporting engines.
677 79
        if ('INNODB' !== $engine) {
678 3
            $diff->addedForeignKeys   = [];
679 3
            $diff->changedForeignKeys = [];
680 3
            $diff->removedForeignKeys = [];
681
        }
682
683 79
        $sql = array_merge(
684 79
            $sql,
685 79
            $this->getPreAlterTableAlterIndexForeignKeySQL($diff),
686 79
            parent::getPreAlterTableIndexForeignKeySQL($diff),
687 79
            $this->getPreAlterTableRenameIndexForeignKeySQL($diff)
688
        );
689
690 79
        return $sql;
691
    }
692
693
    /**
694
     * @param TableDiff $diff
695
     * @param Index     $index
696
     *
697
     * @return string[]
698
     */
699 25
    private function getPreAlterTableAlterPrimaryKeySQL(TableDiff $diff, Index $index)
700
    {
701 25
        $sql = [];
702
703 25
        if (! $index->isPrimary() || ! $diff->fromTable instanceof Table) {
704 9
            return $sql;
705
        }
706
707 16
        $tableName = $diff->getName($this)->getQuotedName($this);
708
709
        // Dropping primary keys requires to unset autoincrement attribute on the particular column first.
710 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...
711 16
            if (! $diff->fromTable->hasColumn($columnName)) {
712 3
                continue;
713
            }
714
715 16
            $column = $diff->fromTable->getColumn($columnName);
716
717 16
            if ($column->getAutoincrement() === true) {
718 9
                $column->setAutoincrement(false);
719
720 9
                $sql[] = 'ALTER TABLE ' . $tableName . ' MODIFY ' .
721 9
                    $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray());
722
723
                // original autoincrement information might be needed later on by other parts of the table alteration
724 16
                $column->setAutoincrement(true);
725
            }
726
        }
727
728 16
        return $sql;
729
    }
730
731
    /**
732
     * @param TableDiff $diff The table diff to gather the SQL for.
733
     *
734
     * @return array
735
     */
736 79
    private function getPreAlterTableAlterIndexForeignKeySQL(TableDiff $diff)
737
    {
738 79
        $sql = [];
739 79
        $table = $diff->getName($this)->getQuotedName($this);
740
741 79
        foreach ($diff->changedIndexes as $changedIndex) {
742
            // Changed primary key
743 16
            if ($changedIndex->isPrimary() && $diff->fromTable instanceof Table) {
744 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...
745 13
                    $column = $diff->fromTable->getColumn($columnName);
746
747
                    // Check if an autoincrement column was dropped from the primary key.
748 13
                    if ($column->getAutoincrement() && ! in_array($columnName, $changedIndex->getColumns())) {
749
                        // The autoincrement attribute needs to be removed from the dropped column
750
                        // before we can drop and recreate the primary key.
751 3
                        $column->setAutoincrement(false);
752
753 3
                        $sql[] = 'ALTER TABLE ' . $table . ' MODIFY ' .
754 3
                            $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray());
755
756
                        // Restore the autoincrement attribute as it might be needed later on
757
                        // by other parts of the table alteration.
758 16
                        $column->setAutoincrement(true);
759
                    }
760
                }
761
            }
762
        }
763
764 79
        return $sql;
765
    }
766
767
    /**
768
     * @param TableDiff $diff The table diff to gather the SQL for.
769
     *
770
     * @return array
771
     */
772 53
    protected function getPreAlterTableRenameIndexForeignKeySQL(TableDiff $diff)
773
    {
774 53
        $sql = [];
775 53
        $tableName = $diff->getName($this)->getQuotedName($this);
776
777 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...
778 2
            if (! in_array($foreignKey, $diff->changedForeignKeys, true)) {
779 2
                $sql[] = $this->getDropForeignKeySQL($foreignKey, $tableName);
780
            }
781
        }
782
783 53
        return $sql;
784
    }
785
786
    /**
787
     * Returns the remaining foreign key constraints that require one of the renamed indexes.
788
     *
789
     * "Remaining" here refers to the diff between the foreign keys currently defined in the associated
790
     * table and the foreign keys to be removed.
791
     *
792
     * @param TableDiff $diff The table diff to evaluate.
793
     *
794
     * @return array
795
     */
796 53
    private function getRemainingForeignKeyConstraintsRequiringRenamedIndexes(TableDiff $diff)
797
    {
798 53
        if (empty($diff->renamedIndexes) || ! $diff->fromTable instanceof Table) {
799 43
            return [];
800
        }
801
802 10
        $foreignKeys = [];
803
        /** @var \Doctrine\DBAL\Schema\ForeignKeyConstraint[] $remainingForeignKeys */
804 10
        $remainingForeignKeys = array_diff_key(
805 10
            $diff->fromTable->getForeignKeys(),
806 10
            $diff->removedForeignKeys
807
        );
808
809 10
        foreach ($remainingForeignKeys as $foreignKey) {
810 2
            foreach ($diff->renamedIndexes as $index) {
811 2
                if ($foreignKey->intersectsIndexColumns($index)) {
812 2
                    $foreignKeys[] = $foreignKey;
813
814 2
                    break;
815
                }
816
            }
817
        }
818
819 10
        return $foreignKeys;
820
    }
821
822
    /**
823
     * {@inheritdoc}
824
     */
825 79
    protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff)
826
    {
827 79
        return array_merge(
828 79
            parent::getPostAlterTableIndexForeignKeySQL($diff),
829 79
            $this->getPostAlterTableRenameIndexForeignKeySQL($diff)
830
        );
831
    }
832
833
    /**
834
     * @param TableDiff $diff The table diff to gather the SQL for.
835
     *
836
     * @return array
837
     */
838 53
    protected function getPostAlterTableRenameIndexForeignKeySQL(TableDiff $diff)
839
    {
840 53
        $sql = [];
841 53
        $tableName = (false !== $diff->newName)
842 4
            ? $diff->getNewName()->getQuotedName($this)
843 53
            : $diff->getName($this)->getQuotedName($this);
844
845 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...
846 2
            if (! in_array($foreignKey, $diff->changedForeignKeys, true)) {
847 2
                $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableName);
848
            }
849
        }
850
851 53
        return $sql;
852
    }
853
854
    /**
855
     * {@inheritDoc}
856
     */
857 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...
858
    {
859 45
        $type = '';
860 45
        if ($index->isUnique()) {
861 9
            $type .= 'UNIQUE ';
862 36
        } elseif ($index->hasFlag('fulltext')) {
863 3
            $type .= 'FULLTEXT ';
864 33
        } elseif ($index->hasFlag('spatial')) {
865 3
            $type .= 'SPATIAL ';
866
        }
867
868 45
        return $type;
869
    }
870
871
    /**
872
     * {@inheritDoc}
873
     */
874 56
    public function getIntegerTypeDeclarationSQL(array $field)
875
    {
876 56
        return 'INT' . $this->_getCommonIntegerTypeDeclarationSQL($field);
877
    }
878
879
    /**
880
     * {@inheritDoc}
881
     */
882
    public function getBigIntTypeDeclarationSQL(array $field)
883
    {
884
        return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($field);
885
    }
886
887
    /**
888
     * {@inheritDoc}
889
     */
890
    public function getSmallIntTypeDeclarationSQL(array $field)
891
    {
892
        return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($field);
893
    }
894
895
    /**
896
     * {@inheritdoc}
897
     */
898 18
    public function getFloatDeclarationSQL(array $field)
899
    {
900 18
        return 'DOUBLE PRECISION' . $this->getUnsignedDeclaration($field);
901
    }
902
903
    /**
904
     * {@inheritdoc}
905
     */
906 18
    public function getDecimalTypeDeclarationSQL(array $columnDef)
907
    {
908 18
        return parent::getDecimalTypeDeclarationSQL($columnDef) . $this->getUnsignedDeclaration($columnDef);
909
    }
910
911
    /**
912
     * Get unsigned declaration for a column.
913
     *
914
     * @param array $columnDef
915
     *
916
     * @return string
917
     */
918 92
    private function getUnsignedDeclaration(array $columnDef)
919
    {
920 92
        return ! empty($columnDef['unsigned']) ? ' UNSIGNED' : '';
921
    }
922
923
    /**
924
     * {@inheritDoc}
925
     */
926 56
    protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef)
927
    {
928 56
        $autoinc = '';
929 56
        if ( ! empty($columnDef['autoincrement'])) {
930 9
            $autoinc = ' AUTO_INCREMENT';
931
        }
932
933 56
        return $this->getUnsignedDeclaration($columnDef) . $autoinc;
934
    }
935
936
    /**
937
     * {@inheritDoc}
938
     */
939 21
    public function getAdvancedForeignKeyOptionsSQL(\Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey)
940
    {
941 21
        $query = '';
942 21
        if ($foreignKey->hasOption('match')) {
943
            $query .= ' MATCH ' . $foreignKey->getOption('match');
944
        }
945 21
        $query .= parent::getAdvancedForeignKeyOptionsSQL($foreignKey);
946
947 21
        return $query;
948
    }
949
950
    /**
951
     * {@inheritDoc}
952
     */
953 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...
954
    {
955 32
        if ($index instanceof Index) {
956 22
            $indexName = $index->getQuotedName($this);
957 10
        } elseif (is_string($index)) {
958 10
            $indexName = $index;
959
        } else {
960
            throw new \InvalidArgumentException('MysqlPlatform::getDropIndexSQL() expects $index parameter to be string or \Doctrine\DBAL\Schema\Index.');
961
        }
962
963 32
        if ($table instanceof Table) {
964
            $table = $table->getQuotedName($this);
965 32
        } elseif (!is_string($table)) {
966
            throw new \InvalidArgumentException('MysqlPlatform::getDropIndexSQL() expects $table parameter to be string or \Doctrine\DBAL\Schema\Table.');
967
        }
968
969 32
        if ($index instanceof Index && $index->isPrimary()) {
970
            // mysql primary keys are always named "PRIMARY",
971
            // so we cannot use them in statements because of them being keyword.
972 19
            return $this->getDropPrimaryKeySQL($table);
973
        }
974
975 13
        return 'DROP INDEX ' . $indexName . ' ON ' . $table;
976
    }
977
978
    /**
979
     * @param string $table
980
     *
981
     * @return string
982
     */
983 19
    protected function getDropPrimaryKeySQL($table)
984
    {
985 19
        return 'ALTER TABLE ' . $table . ' DROP PRIMARY KEY';
986
    }
987
988
    /**
989
     * {@inheritDoc}
990
     */
991 3
    public function getSetTransactionIsolationSQL($level)
992
    {
993 3
        return 'SET SESSION TRANSACTION ISOLATION LEVEL ' . $this->_getTransactionIsolationLevelSQL($level);
994
    }
995
996
    /**
997
     * {@inheritDoc}
998
     */
999 6
    public function getName()
1000
    {
1001 6
        return 'mysql';
1002
    }
1003
1004
    /**
1005
     * {@inheritDoc}
1006
     */
1007
    public function getReadLockSQL()
1008
    {
1009
        return 'LOCK IN SHARE MODE';
1010
    }
1011
1012
    /**
1013
     * {@inheritDoc}
1014
     */
1015 17
    protected function initializeDoctrineTypeMappings()
1016
    {
1017 17
        $this->doctrineTypeMapping = [
1018
            'tinyint'       => 'boolean',
1019
            'smallint'      => 'smallint',
1020
            'mediumint'     => 'integer',
1021
            'int'           => 'integer',
1022
            'integer'       => 'integer',
1023
            'bigint'        => 'bigint',
1024
            'tinytext'      => 'text',
1025
            'mediumtext'    => 'text',
1026
            'longtext'      => 'text',
1027
            'text'          => 'text',
1028
            'varchar'       => 'string',
1029
            'string'        => 'string',
1030
            'char'          => 'string',
1031
            'date'          => 'date',
1032
            'datetime'      => 'datetime',
1033
            'timestamp'     => 'datetime',
1034
            'time'          => 'time',
1035
            'float'         => 'float',
1036
            'double'        => 'float',
1037
            'real'          => 'float',
1038
            'decimal'       => 'decimal',
1039
            'numeric'       => 'decimal',
1040
            'year'          => 'date',
1041
            'longblob'      => 'blob',
1042
            'blob'          => 'blob',
1043
            'mediumblob'    => 'blob',
1044
            'tinyblob'      => 'blob',
1045
            'binary'        => 'binary',
1046
            'varbinary'     => 'binary',
1047
            'set'           => 'simple_array',
1048
        ];
1049 17
    }
1050
1051
    /**
1052
     * {@inheritDoc}
1053
     */
1054 45
    public function getVarcharMaxLength()
1055
    {
1056 45
        return 65535;
1057
    }
1058
1059
    /**
1060
     * {@inheritdoc}
1061
     */
1062 6
    public function getBinaryMaxLength()
1063
    {
1064 6
        return 65535;
1065
    }
1066
1067
    /**
1068
     * {@inheritDoc}
1069
     */
1070 62
    protected function getReservedKeywordsClass()
1071
    {
1072 62
        return Keywords\MySQLKeywords::class;
1073
    }
1074
1075
    /**
1076
     * {@inheritDoc}
1077
     *
1078
     * MySQL commits a transaction implicitly when DROP TABLE is executed, however not
1079
     * if DROP TEMPORARY TABLE is executed.
1080
     */
1081 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...
1082
    {
1083
        if ($table instanceof Table) {
1084
            $table = $table->getQuotedName($this);
1085
        } elseif (!is_string($table)) {
1086
            throw new \InvalidArgumentException('getDropTemporaryTableSQL() expects $table parameter to be string or \Doctrine\DBAL\Schema\Table.');
1087
        }
1088
1089
        return 'DROP TEMPORARY TABLE ' . $table;
1090
    }
1091
1092
    /**
1093
     * Gets the SQL Snippet used to declare a BLOB column type.
1094
     *     TINYBLOB   : 2 ^  8 - 1 = 255
1095
     *     BLOB       : 2 ^ 16 - 1 = 65535
1096
     *     MEDIUMBLOB : 2 ^ 24 - 1 = 16777215
1097
     *     LONGBLOB   : 2 ^ 32 - 1 = 4294967295
1098
     *
1099
     * @param array $field
1100
     *
1101
     * @return string
1102
     */
1103 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...
1104
    {
1105 8
        if ( ! empty($field['length']) && is_numeric($field['length'])) {
1106 6
            $length = $field['length'];
1107
1108 6
            if ($length <= static::LENGTH_LIMIT_TINYBLOB) {
1109 3
                return 'TINYBLOB';
1110
            }
1111
1112 6
            if ($length <= static::LENGTH_LIMIT_BLOB) {
1113 3
                return 'BLOB';
1114
            }
1115
1116 6
            if ($length <= static::LENGTH_LIMIT_MEDIUMBLOB) {
1117 6
                return 'MEDIUMBLOB';
1118
            }
1119
        }
1120
1121 8
        return 'LONGBLOB';
1122
    }
1123
1124
    /**
1125
     * {@inheritdoc}
1126
     */
1127 63
    public function quoteStringLiteral($str)
1128
    {
1129 63
        $str = str_replace('\\', '\\\\', $str); // MySQL requires backslashes to be escaped aswell.
1130
1131 63
        return parent::quoteStringLiteral($str);
1132
    }
1133
1134
    /**
1135
     * {@inheritdoc}
1136
     */
1137 1
    public function getDefaultTransactionIsolationLevel()
1138
    {
1139 1
        return Connection::TRANSACTION_REPEATABLE_READ;
1140
    }
1141
}
1142