Completed
Pull Request — master (#1393)
by
unknown
01:51
created

MysqlAdapter::getPrimaryKey()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 24

Duplication

Lines 23
Ratio 95.83 %

Code Coverage

Tests 11
CRAP Score 2

Importance

Changes 0
Metric Value
dl 23
loc 24
rs 9.536
c 0
b 0
f 0
ccs 11
cts 11
cp 1
cc 2
nc 2
nop 1
crap 2
1
<?php
2
/**
3
 * Phinx
4
 *
5
 * (The MIT license)
6
 * Copyright (c) 2015 Rob Morgan
7
 *
8
 * Permission is hereby granted, free of charge, to any person obtaining a copy
9
 * of this software and associated * documentation files (the "Software"), to
10
 * deal in the Software without restriction, including without limitation the
11
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
12
 * sell copies of the Software, and to permit persons to whom the Software is
13
 * furnished to do so, subject to the following conditions:
14
 *
15
 * The above copyright notice and this permission notice shall be included in
16
 * all copies or substantial portions of the Software.
17
 *
18
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
24
 * IN THE SOFTWARE.
25
 *
26
 * @package    Phinx
27
 * @subpackage Phinx\Db\Adapter
28
 */
29
namespace Phinx\Db\Adapter;
30
31
use Cake\Database\Connection;
32
use Cake\Database\Driver\Mysql as MysqlDriver;
33
use Phinx\Db\Table\Column;
34
use Phinx\Db\Table\ForeignKey;
35
use Phinx\Db\Table\Index;
36
use Phinx\Db\Table\Table;
37
use Phinx\Db\Util\AlterInstructions;
38
use Phinx\Util\Literal;
39
40
/**
41
 * Phinx MySQL Adapter.
42
 *
43
 * @author Rob Morgan <[email protected]>
44
 */
45
class MysqlAdapter extends PdoAdapter implements AdapterInterface
46
{
47
48
    protected $signedColumnTypes = ['integer' => true, 'biginteger' => true, 'float' => true, 'decimal' => true, 'boolean' => true];
49
50
    const TEXT_TINY = 255;
51
    const TEXT_SMALL = 255; /* deprecated, alias of TEXT_TINY */
52
    const TEXT_REGULAR = 65535;
53
    const TEXT_MEDIUM = 16777215;
54
    const TEXT_LONG = 4294967295;
55
56
    // According to https://dev.mysql.com/doc/refman/5.0/en/blob.html BLOB sizes are the same as TEXT
57
    const BLOB_TINY = 255;
58
    const BLOB_SMALL = 255; /* deprecated, alias of BLOB_TINY */
59
    const BLOB_REGULAR = 65535;
60
    const BLOB_MEDIUM = 16777215;
61
    const BLOB_LONG = 4294967295;
62
63
    const INT_TINY = 255;
64
    const INT_SMALL = 65535;
65
    const INT_MEDIUM = 16777215;
66
    const INT_REGULAR = 4294967295;
67
    const INT_BIG = 18446744073709551615;
68
69
    const BIT = 64;
70 80
71
    const TYPE_YEAR = 'year';
72 80
73 80
    /**
74
     * {@inheritdoc}
75
     */
76
    public function connect()
77
    {
78
        if ($this->connection === null) {
79 80
            if (!class_exists('PDO') || !in_array('mysql', \PDO::getAvailableDrivers(), true)) {
80 80
                // @codeCoverageIgnoreStart
81
                throw new \RuntimeException('You need to enable the PDO_Mysql extension for Phinx to run properly.');
82 80
                // @codeCoverageIgnoreEnd
83
            }
84 80
85
            $db = null;
86
            $options = $this->getOptions();
87
88
            $dsn = 'mysql:';
89 80
90 80
            if (!empty($options['unix_socket'])) {
91 80
                // use socket connection
92 80
                $dsn .= 'unix_socket=' . $options['unix_socket'];
93
            } else {
94
                // use network connection
95 80
                $dsn .= 'host=' . $options['host'];
96
                if (!empty($options['port'])) {
97
                    $dsn .= ';port=' . $options['port'];
98 80
                }
99
            }
100
101
            $dsn .= ';dbname=' . $options['name'];
102 80
103
            // charset support
104
            if (!empty($options['charset'])) {
105
                $dsn .= ';charset=' . $options['charset'];
106 80
            }
107 80
108
            $driverOptions = [\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION];
109
110 80
            // support arbitrary \PDO::MYSQL_ATTR_* driver options and pass them to PDO
111
            // http://php.net/manual/en/ref.pdo-mysql.php#pdo-mysql.constants
112 View Code Duplication
            foreach ($options as $key => $option) {
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...
113 80
                if (strpos($key, 'mysql_attr_') === 0) {
114 80
                    $driverOptions[constant('\PDO::' . strtoupper($key))] = $option;
115 1
                }
116 1
            }
117 1
118 1
            try {
119
                $db = new \PDO($dsn, $options['user'], $options['pass'], $driverOptions);
120
            } catch (\PDOException $exception) {
121 80
                throw new \InvalidArgumentException(sprintf(
122 80
                    'There was a problem connecting to the database: %s',
123 80
                    $exception->getMessage()
124
                ));
125
            }
126
127
            $this->setConnection($db);
128 81
        }
129
    }
130 81
131 81
    /**
132
     * {@inheritdoc}
133
     */
134
    public function disconnect()
135
    {
136 6
        $this->connection = null;
137
    }
138 6
139
    /**
140
     * {@inheritdoc}
141
     */
142
    public function hasTransactions()
143
    {
144 6
        return true;
145
    }
146 6
147 6
    /**
148
     * {@inheritdoc}
149
     */
150
    public function beginTransaction()
151
    {
152 6
        $this->execute('START TRANSACTION');
153
    }
154 6
155 6
    /**
156
     * {@inheritdoc}
157
     */
158
    public function commitTransaction()
159
    {
160 1
        $this->execute('COMMIT');
161
    }
162 1
163 1
    /**
164
     * {@inheritdoc}
165
     */
166
    public function rollbackTransaction()
167
    {
168 112
        $this->execute('ROLLBACK');
169
    }
170 112
171
    /**
172
     * {@inheritdoc}
173
     */
174
    public function quoteTableName($tableName)
175
    {
176 112
        return str_replace('.', '`.`', $this->quoteColumnName($tableName));
177
    }
178 112
179
    /**
180
     * {@inheritdoc}
181
     */
182
    public function quoteColumnName($columnName)
183
    {
184 82
        return '`' . str_replace('`', '``', $columnName) . '`';
185
    }
186 82
187
    /**
188 82
     * {@inheritdoc}
189
     */
190 View Code Duplication
    public function hasTable($tableName)
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...
191 82
    {
192 82
        $options = $this->getOptions();
193
194 82
        $exists = $this->fetchRow(sprintf(
195
            "SELECT TABLE_NAME
196 82
            FROM INFORMATION_SCHEMA.TABLES
197
            WHERE TABLE_SCHEMA = '%s' AND TABLE_NAME = '%s'",
198
            $options['name'],
199
            $tableName
200
        ));
201
202 82
        return !empty($exists);
203
    }
204
205
    /**
206 82
     * {@inheritdoc}
207
     */
208 82
    public function createTable(Table $table, array $columns = [], array $indexes = [])
209 82
    {
210
        // This method is based on the MySQL docs here: http://dev.mysql.com/doc/refman/5.1/en/create-index.html
211
        $defaultOptions = [
212 82
            'engine' => 'InnoDB',
213 82
            'collation' => 'utf8_general_ci'
214 68
        ];
215 68
216 68
        $options = array_merge(
217 68
            $defaultOptions,
218 68
            array_intersect_key($this->getOptions(), $defaultOptions),
219
            $table->getOptions()
220 68
        );
221 68
222 82
        // Add the default primary key
223
        if (!isset($options['id']) || (isset($options['id']) && $options['id'] === true)) {
224 2
            $column = new Column();
225 2
            $column->setName('id')
226 2
                   ->setType('integer')
227 2
                   ->setSigned(isset($options['signed']) ? $options['signed'] : true)
228
                   ->setIdentity(true);
229 2
230 2
            array_unshift($columns, $column);
231 2
            $options['primary_key'] = 'id';
232
        } elseif (isset($options['id']) && is_string($options['id'])) {
233
            // Handle id => "field_name" to support AUTO_INCREMENT
234
            $column = new Column();
235
            $column->setName($options['id'])
236 82
                   ->setType('integer')
237 82
                   ->setSigned(isset($options['signed']) ? $options['signed'] : true)
238 82
                   ->setIdentity(true);
239 82
240
            array_unshift($columns, $column);
241
            $options['primary_key'] = $options['id'];
242 82
        }
243 82
244 82
        // TODO - process table options like collation etc
245 82
246 82
        // process table engine (default to InnoDB)
247
        $optionsStr = 'ENGINE = InnoDB';
248
        if (isset($options['engine'])) {
249 82
            $optionsStr = sprintf('ENGINE = %s', $options['engine']);
250 2
        }
251 2
252
        // process table collation
253 82
        if (isset($options['collation'])) {
254 82
            $charset = explode('_', $options['collation']);
255 82
            $optionsStr .= sprintf(' CHARACTER SET %s', $charset[0]);
256 82
            $optionsStr .= sprintf(' COLLATE %s', $options['collation']);
257 82
        }
258
259
        // set the table comment
260 82
        if (isset($options['comment'])) {
261 82
            $optionsStr .= sprintf(" COMMENT=%s ", $this->getConnection()->quote($options['comment']));
262 82
        }
263 82
264 81
        // set the table row format
265 82
        if (isset($options['row_format'])) {
266
            $optionsStr .= sprintf(" ROW_FORMAT=%s ", $options['row_format']);
267
        }
268 2
269 2
        $sql = 'CREATE TABLE ';
270 2
        $sql .= $this->quoteTableName($table->getName()) . ' (';
271 2
        foreach ($columns as $column) {
272 2
            $sql .= $this->quoteColumnName($column->getName()) . ' ' . $this->getColumnSqlDefinition($column) . ', ';
273 2
        }
274 2
275 2
        // set the primary key(s)
276 2 View Code Duplication
        if (isset($options['primary_key'])) {
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...
277 2
            $sql = rtrim($sql);
278 82
            $sql .= ' PRIMARY KEY (';
279 82
            if (is_string($options['primary_key'])) { // handle primary_key => 'id'
280 1
                $sql .= $this->quoteColumnName($options['primary_key']);
281
            } elseif (is_array($options['primary_key'])) { // handle primary_key => array('tag_id', 'resource_id')
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
282
                $sql .= implode(',', array_map([$this, 'quoteColumnName'], $options['primary_key']));
283
            }
284 82
            $sql .= ')';
285 82
        } else {
286 10
            $sql = substr(rtrim($sql), 0, -1); // no primary keys
287 82
        }
288
289
        // set the indexes
290 82
        foreach ($indexes as $index) {
291 82
            $sql .= ', ' . $this->getIndexSqlDefinition($index);
292 2
        }
293 82
294
        $sql .= ') ' . $optionsStr;
295 82
        $sql = rtrim($sql) . ';';
296 82
297
        // execute the sql
298
        $this->execute($sql);
299 82
    }
300 82
301
    /**
302
     * {@inheritdoc}
303
     */
304
    protected function getChangePrimaryKeyInstructions(Table $table, $newColumns)
305 5
    {
306
        $instructions = new AlterInstructions();
307 5
308 5
        // Drop the existing primary key
309
        $primaryKey = $this->getPrimaryKey($table->getName());
310
        if (!empty($primaryKey['columns'])) {
311
            foreach ($primaryKey['columns'] as $column) {
312
                $sql = sprintf(
313 5
                    'MODIFY %s INT NOT NULL',
314
                    $this->quoteColumnName($column)
315 5
                );
316 5
                $instructions->addAlter($sql);
317
            }
318
            $instructions->addAlter('DROP PRIMARY KEY');
319
        }
320
321 1
        // Add the primary key(s)
322
        if (!empty($newColumns)) {
323 1
            $sql = 'ADD PRIMARY KEY (';
324 1
            if (is_string($newColumns)) { // handle primary_key => 'id'
325 1
                $sql .= $this->quoteColumnName($newColumns);
326 1
            } elseif (is_array($newColumns)) { // handle primary_key => array('tag_id', 'resource_id')
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
327
                $sql .= implode(',', array_map([$this, 'quoteColumnName'], $newColumns));
328 1
            } else {
329 1
                throw new \InvalidArgumentException(sprintf(
330
                    "Invalid value for primary key: %s",
331
                    json_encode($newColumns)
332
                ));
333
            }
334 12
            $sql .= ')';
335
            $instructions->addAlter($sql);
336 12
        }
337 12
338 12
        return $instructions;
339 12
    }
340
341 12
    /**
342 12
     * {@inheritdoc}
343 12
     */
344 12
    protected function getChangeCommentInstructions(Table $table, string $newComment = null)
345 12
    {
346 12
        $instructions = new AlterInstructions();
347
348 12
        // passing 'null' is to remove table comment
349 12
        $newComment = ($newComment !== null)
350 12
            ? $newComment
351
            : '';
352 12
        $sql = sprintf(" COMMENT=%s ", $this->getConnection()->quote($newComment));
353 3
        $instructions->addAlter($sql);
354 3
355
        return $instructions;
356 12
    }
357 12
358
    /**
359 12
     * {@inheritdoc}
360
     */
361 View Code Duplication
    protected function getRenameTableInstructions($tableName, $newTableName)
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...
362
    {
363
        $sql = sprintf(
364
            'RENAME TABLE %s TO %s',
365 79
            $this->quoteTableName($tableName),
366
            $this->quoteTableName($newTableName)
367 79
        );
368 79
369 79
        return new AlterInstructions([], [$sql]);
370 77
    }
371
372 77
    /**
373
     * {@inheritdoc}
374 21
     */
375
    protected function getDropTableInstructions($tableName)
376
    {
377
        $sql = sprintf('DROP TABLE %s', $this->quoteTableName($tableName));
378
379
        return new AlterInstructions([], [$sql]);
380
    }
381
382
    /**
383 95
     * {@inheritdoc}
384
     */
385 95
    public function truncateTable($tableName)
386 10
    {
387 95
        $sql = sprintf(
388 79
            'TRUNCATE TABLE %s',
389 79
            $this->quoteTableName($tableName)
390 95
        );
391
392
        $this->execute($sql);
393
    }
394
395
    /**
396 18
     * {@inheritdoc}
397
     */
398 18
    public function getColumns($tableName)
399 18
    {
400 18
        $columns = [];
401 18
        $rows = $this->fetchAll(sprintf('SHOW COLUMNS FROM %s', $this->quoteTableName($tableName)));
402 18
        foreach ($rows as $columnInfo) {
403 18
            $phinxType = $this->getPhinxType($columnInfo['Type']);
404
405 18
            $column = new Column();
406 2
            $column->setName($columnInfo['Field'])
407 2
                   ->setNull($columnInfo['Null'] !== 'NO')
408
                   ->setDefault($columnInfo['Default'])
409 18
                   ->setType($phinxType['name'])
410 18
                   ->setSigned(strpos($columnInfo['Type'], 'unsigned') === false)
411
                   ->setLimit($phinxType['limit']);
412
413
            if ($columnInfo['Extra'] === 'auto_increment') {
414
                $column->setIdentity(true);
415 7
            }
416
417 7
            if (isset($phinxType['values'])) {
418 7
                $column->setValues($phinxType['values']);
419 7
            }
420 5
421 5
            $columns[] = $column;
422 5
        }
423 1
424 1
        return $columns;
425 5
    }
426
427 5
    /**
428 5
     * {@inheritdoc}
429 5
     */
430 5 View Code Duplication
    public function hasColumn($tableName, $columnName)
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...
431 5
    {
432 5
        $rows = $this->fetchAll(sprintf('SHOW COLUMNS FROM %s', $this->quoteTableName($tableName)));
433
        foreach ($rows as $column) {
434 5
            if (strcasecmp($column['Field'], $columnName) === 0) {
435 5
                return true;
436 5
            }
437
        }
438 6
439
        return false;
440 2
    }
441
442
    /**
443 2
     * {@inheritdoc}
444
     */
445
    protected function getAddColumnInstructions(Table $table, Column $column)
446
    {
447
        $alter = sprintf(
448
            'ADD %s %s',
449 5
            $this->quoteColumnName($column->getName()),
450
            $this->getColumnSqlDefinition($column)
451 5
        );
452 5
453 5
        if ($column->getAfter()) {
454 5
            $alter .= ' AFTER ' . $this->quoteColumnName($column->getAfter());
455 5
        }
456 5
457 5
        return new AlterInstructions([$alter]);
458 5
    }
459
460 5
    /**
461 5
     * {@inheritdoc}
462 5
     */
463
    protected function getRenameColumnInstructions($tableName, $columnName, $newColumnName)
464
    {
465
        $rows = $this->fetchAll(sprintf('SHOW FULL COLUMNS FROM %s', $this->quoteTableName($tableName)));
466
467 5
        foreach ($rows as $row) {
468
            if (strcasecmp($row['Field'], $columnName) === 0) {
469 5
                $null = ($row['Null'] == 'NO') ? 'NOT NULL' : 'NULL';
470 5
                $comment = isset($row['Comment']) ? ' COMMENT ' . '\'' . addslashes($row['Comment']) . '\'' : '';
471 5
                $extra = ' ' . strtoupper($row['Extra']);
472 5
                if (!is_null($row['Default'])) {
473 5
                    $extra .= $this->getDefaultValueDefinition($row['Default']);
474 5
                }
475 5
                $definition = $row['Type'] . ' ' . $null . $extra . $comment;
476 5
477
                $alter = sprintf(
478
                    'CHANGE COLUMN %s %s %s',
479
                    $this->quoteColumnName($columnName),
480
                    $this->quoteColumnName($newColumnName),
481
                    $definition
482
                );
483
484 19
                return new AlterInstructions([$alter]);
485
            }
486 19
        }
487 19
488 19
        throw new \InvalidArgumentException(sprintf(
489 18
            'The specified column doesn\'t exist: ' .
490 18
            $columnName
491 18
        ));
492 18
    }
493 19
494 19
    /**
495
     * {@inheritdoc}
496
     */
497
    protected function getChangeColumnInstructions($tableName, $columnName, Column $newColumn)
498
    {
499
        $after = $newColumn->getAfter() ? ' AFTER ' . $this->quoteColumnName($newColumn->getAfter()) : '';
500 14
        $alter = sprintf(
501
            'CHANGE %s %s %s%s',
502 14
            $this->quoteColumnName($columnName),
503 6
            $this->quoteColumnName($newColumn->getName()),
504 6
            $this->getColumnSqlDefinition($newColumn),
505
            $after
506 14
        );
507 14
508
        return new AlterInstructions([$alter]);
509 14
    }
510 14
511 12
    /**
512
     * {@inheritdoc}
513 13
     */
514
    protected function getDropColumnInstructions($tableName, $columnName)
515 11
    {
516
        $alter = sprintf('DROP COLUMN %s', $this->quoteColumnName($columnName));
517
518
        return new AlterInstructions([$alter]);
519
    }
520
521 1
    /**
522
     * Get an array of indexes from a particular table.
523 1
     *
524
     * @param string $tableName Table Name
525 1
     * @return array
526 1
     */
527 1
    protected function getIndexes($tableName)
528
    {
529 1
        $indexes = [];
530
        $rows = $this->fetchAll(sprintf('SHOW INDEXES FROM %s', $this->quoteTableName($tableName)));
531 View Code Duplication
        foreach ($rows as $row) {
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...
532
            if (!isset($indexes[$row['Key_name']])) {
533
                $indexes[$row['Key_name']] = ['columns' => []];
534
            }
535
            $indexes[$row['Key_name']]['columns'][] = strtolower($row['Column_name']);
536
        }
537 4
538
        return $indexes;
539 4
    }
540 4
541 4
    /**
542 4
     * {@inheritdoc}
543 4
     */
544 4 View Code Duplication
    public function hasIndex($tableName, $columns)
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...
545 4
    {
546 4
        if (is_string($columns)) {
547
            $columns = [$columns]; // str to array
548
        }
549
550
        $columns = array_map('strtolower', $columns);
551 3
        $indexes = $this->getIndexes($tableName);
552
553 3
        foreach ($indexes as $index) {
554 2
            if ($columns == $index['columns']) {
555 2
                return true;
556
            }
557 3
        }
558 3
559
        return false;
560 3
    }
561 3
562 3
    /**
563 3
     * {@inheritdoc}
564 3
     */
565 3 View Code Duplication
    public function hasIndexByName($tableName, $indexName)
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...
566 3
    {
567 3
        $indexes = $this->getIndexes($tableName);
568 3
569 3
        foreach ($indexes as $name => $index) {
570
            if ($name === $indexName) {
571 3
                return true;
572 1
            }
573
        }
574
575
        return false;
576
    }
577 2
578
    /**
579 2
     * {@inheritdoc}
580
     */
581 2
    protected function getAddIndexInstructions(Table $table, Index $index)
582
    {
583 2
        $instructions = new AlterInstructions();
584 2
585 2
        if ($index->getType() == Index::FULLTEXT) {
586 2
            // Must be executed separately
587 2
            // SQLSTATE[HY000]: General error: 1795 InnoDB presently supports one FULLTEXT index creation at a time
588 2
            $alter = sprintf(
589 2
                'ALTER TABLE %s ADD %s',
590 2
                $this->quoteTableName($table->getName()),
591 2
                $this->getIndexSqlDefinition($index)
592
            );
593 2
594
            $instructions->addPostStep($alter);
595
        } else {
596
            $alter = sprintf(
597
                'ADD %s',
598
                $this->getIndexSqlDefinition($index)
599 21
            );
600
601 21
            $instructions->addAlter($alter);
602 5
        }
603 5
604 21
        return $instructions;
605 21
    }
606 6
607 4
    /**
608
     * {@inheritdoc}
609 4
     */
610
    protected function getDropIndexByColumnsInstructions($tableName, $columns)
611 15
    {
612 12
        if (is_string($columns)) {
613 10
            $columns = [$columns]; // str to array
614
        }
615 11
616 11
        $indexes = $this->getIndexes($tableName);
617
        $columns = array_map('strtolower', $columns);
618
619 View Code Duplication
        foreach ($indexes as $indexName => $index) {
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...
620
            if ($columns == $index['columns']) {
621
                return new AlterInstructions([sprintf(
622
                    'DROP INDEX %s',
623
                    $this->quoteColumnName($indexName)
624
                )]);
625
            }
626 22
        }
627
628 22
        throw new \InvalidArgumentException(sprintf(
629 22
            "The specified index on columns '%s' does not exist",
630
            implode(',', $columns)
631
        ));
632
    }
633
634
    /**
635
     * {@inheritdoc}
636
     */
637
    protected function getDropIndexByNameInstructions($tableName, $indexName)
638
    {
639
640 22
        $indexes = $this->getIndexes($tableName);
641
642 22 View Code Duplication
        foreach ($indexes as $name => $index) {
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...
643 22
            if ($name === $indexName) {
644 19
                return new AlterInstructions([sprintf(
645 19
                    'DROP INDEX %s',
646 19
                    $this->quoteColumnName($indexName)
647 19
                )]);
648 22
            }
649 22
        }
650
651
        throw new \InvalidArgumentException(sprintf(
652
            "The specified index name '%s' does not exist",
653
            $indexName
654
        ));
655 15
    }
656
657 15
    /**
658 15
     * {@inheritdoc}
659 15
     */
660 15 View Code Duplication
    public function hasPrimaryKey($tableName, $columns, $constraint = 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...
661 15
    {
662 15
        $primaryKey = $this->getPrimaryKey($tableName);
663 15
664 15
        if (empty($primaryKey['constraint'])) {
665
            return false;
666
        }
667
668
        if ($constraint) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $constraint of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
669 8
            return ($primaryKey['constraint'] === $constraint);
670
        } else {
671 8
            if (is_string($columns)) {
672 3
                $columns = [$columns]; // str to array
673 3
            }
674
            $missingColumns = array_diff($columns, $primaryKey['columns']);
675
676 8
            return empty($missingColumns);
677 8
        }
678 8
    }
679 8
680 8
    /**
681
     * Get the primary key from a particular table.
682 8
     *
683 8
     * @param string $tableName Table Name
684 8
     * @return array
685
     */
686 7 View Code Duplication
    public function getPrimaryKey($tableName)
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...
687 7
    {
688
        $rows = $this->fetchAll(sprintf(
689
            "SELECT
690
                k.constraint_name,
691
                k.column_name
692
            FROM information_schema.table_constraints t
693
            JOIN information_schema.key_column_usage k
694
                USING(constraint_name,table_name)
695 7
            WHERE t.constraint_type='PRIMARY KEY'
696 7
                AND t.table_name='%s'",
697
            $tableName
698 7
        ));
699 7
700 7
        $primaryKey = [
701 7
            'columns' => [],
702 7
        ];
703
        foreach ($rows as $row) {
704 7
            $primaryKey['constraint'] = $row['constraint_name'];
705
            $primaryKey['columns'][] = $row['column_name'];
706
        }
707
708
        return $primaryKey;
709 96
    }
710
711
    /**
712 96
     * {@inheritdoc}
713 87
     */
714 View Code Duplication
    public function hasForeignKey($tableName, $columns, $constraint = 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...
715 96
    {
716 4
        if (is_string($columns)) {
717
            $columns = [$columns]; // str to array
718 96
        }
719 9
        $foreignKeys = $this->getForeignKeys($tableName);
720
        if ($constraint) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $constraint of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
721
            if (isset($foreignKeys[$constraint])) {
722 6
                return !empty($foreignKeys[$constraint]);
723 6
            }
724 6
725 6
            return false;
726 6
        } else {
727 6
            foreach ($foreignKeys as $key) {
728 6
                if ($columns == $key['columns']) {
729 6
                    return true;
730
                }
731 5
            }
732
733 5
            return false;
734
        }
735 95
    }
736 5
737
    /**
738 95
     * Get an array of foreign keys from a particular table.
739 3
     *
740
     * @param string $tableName Table Name
741 95
     * @return array
742 1
     */
743 View Code Duplication
    protected function getForeignKeys($tableName)
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...
744
    {
745 1
        $foreignKeys = [];
746 1
        $rows = $this->fetchAll(sprintf(
747 1
            "SELECT
748 1
              CONSTRAINT_NAME,
749 1
              TABLE_NAME,
750 1
              COLUMN_NAME,
751 1
              REFERENCED_TABLE_NAME,
752 1
              REFERENCED_COLUMN_NAME
753
            FROM information_schema.KEY_COLUMN_USAGE
754 1
            WHERE REFERENCED_TABLE_SCHEMA = DATABASE()
755
              AND REFERENCED_TABLE_NAME IS NOT NULL
756 1
              AND TABLE_NAME = '%s'
757
            ORDER BY POSITION_IN_UNIQUE_CONSTRAINT",
758 95
            $tableName
759 82
        ));
760
        foreach ($rows as $row) {
761
            $foreignKeys[$row['CONSTRAINT_NAME']]['table'] = $row['TABLE_NAME'];
762 6
            $foreignKeys[$row['CONSTRAINT_NAME']]['columns'][] = $row['COLUMN_NAME'];
763 6
            $foreignKeys[$row['CONSTRAINT_NAME']]['referenced_table'] = $row['REFERENCED_TABLE_NAME'];
764 6
            $foreignKeys[$row['CONSTRAINT_NAME']]['referenced_columns'][] = $row['REFERENCED_COLUMN_NAME'];
765 6
        }
766 6
767 6
        return $foreignKeys;
768
    }
769 6
770 6
    /**
771 6
     * {@inheritdoc}
772 6
     */
773 6 View Code Duplication
    protected function getAddForeignKeyInstructions(Table $table, ForeignKey $foreignKey)
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...
774 6
    {
775 6
        $alter = sprintf(
776 2
            'ADD %s',
777 2
            $this->getForeignKeySqlDefinition($foreignKey)
778 6
        );
779
780 5
        return new AlterInstructions([$alter]);
781 82
    }
782 76
783 76
    /**
784 82
     * {@inheritdoc}
785
     */
786 86
    protected function getDropForeignKeyInstructions($tableName, $constraint)
787 82
    {
788
        $alter = sprintf(
789 86
            'DROP FOREIGN KEY %s',
790 7
            $constraint
791
        );
792 84
793 5
        return new AlterInstructions([$alter]);
794
    }
795 83
796 7
    /**
797
     * {@inheritdoc}
798 83
     */
799 80
    protected function getDropForeignKeyByColumnsInstructions($tableName, $columns)
800
    {
801 83
        $instructions = new AlterInstructions();
802 4
803 View Code Duplication
        foreach ($columns 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...
804 83
            $rows = $this->fetchAll(sprintf(
805 4
                "SELECT
806
                    CONSTRAINT_NAME
807 83
                  FROM information_schema.KEY_COLUMN_USAGE
808 80
                  WHERE REFERENCED_TABLE_SCHEMA = DATABASE()
809
                    AND REFERENCED_TABLE_NAME IS NOT NULL
810 10
                    AND TABLE_NAME = '%s'
811 2
                    AND COLUMN_NAME = '%s'
812
                  ORDER BY POSITION_IN_UNIQUE_CONSTRAINT",
813 10
                $tableName,
814 10
                $column
815 10
            ));
816 10
817 5
            foreach ($rows as $row) {
818 8
                $instructions->merge($this->getDropForeignKeyInstructions($tableName, $row['CONSTRAINT_NAME']));
819 5
            }
820
        }
821 6
822 4
        if (empty($instructions->getAlterParts())) {
823
            throw new \InvalidArgumentException(sprintf(
824 2
                "Not foreign key on columns '%s' exist",
825
                implode(',', $columns)
826
            ));
827
        }
828
829
        return $instructions;
830 2
    }
831
832
    /**
833 2
     * {@inheritdoc}
834 2
     */
835 2
    public function getSqlType($type, $limit = null)
836
    {
837
        switch ($type) {
838
            case static::PHINX_TYPE_FLOAT:
839
            case static::PHINX_TYPE_DECIMAL:
840
            case static::PHINX_TYPE_DATE:
841
            case static::PHINX_TYPE_ENUM:
842
            case static::PHINX_TYPE_SET:
843
            case static::PHINX_TYPE_JSON:
844
            // Geospatial database types
845
            case static::PHINX_TYPE_GEOMETRY:
846 17
            case static::PHINX_TYPE_POINT:
847
            case static::PHINX_TYPE_LINESTRING:
848 17
            case static::PHINX_TYPE_POLYGON:
849 17
                return ['name' => $type];
850 1
            case static::PHINX_TYPE_DATETIME:
851
            case static::PHINX_TYPE_TIMESTAMP:
852 16
            case static::PHINX_TYPE_TIME:
853 16
                return ['name' => $type, 'limit' => $limit];
854 16
            case static::PHINX_TYPE_STRING:
855 16
                return ['name' => 'varchar', 'limit' => $limit ?: 255];
856 14
            case static::PHINX_TYPE_CHAR:
857 14
                return ['name' => 'char', 'limit' => $limit ?: 255];
858 16 View Code Duplication
            case static::PHINX_TYPE_TEXT:
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...
859 4
                if ($limit) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $limit of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
860 4
                    $sizes = [
861 16
                        // Order matters! Size must always be tested from longest to shortest!
862 3
                        'longtext' => static::TEXT_LONG,
863 3
                        'mediumtext' => static::TEXT_MEDIUM,
864 3
                        'text' => static::TEXT_REGULAR,
865
                        'tinytext' => static::TEXT_SMALL,
866 16
                    ];
867 6
                    foreach ($sizes as $name => $length) {
868 6
                        if ($limit >= $length) {
869 3
                            return ['name' => $name];
870 3
                        }
871 6
                    }
872 16
                }
873 5
874 5
                return ['name' => 'text'];
875 1
            case static::PHINX_TYPE_BINARY:
876 1
                return ['name' => 'binary', 'limit' => $limit ?: 255];
877 5
            case static::PHINX_TYPE_VARBINARY:
878 2
                return ['name' => 'varbinary', 'limit' => $limit ?: 255];
879 2 View Code Duplication
            case static::PHINX_TYPE_BLOB:
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...
880 5
                if ($limit) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $limit of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
881 16
                    $sizes = [
882 2
                        // Order matters! Size must always be tested from longest to shortest!
883 2
                        'longblob' => static::BLOB_LONG,
884 2
                        'mediumblob' => static::BLOB_MEDIUM,
885 16
                        'blob' => static::BLOB_REGULAR,
886 2
                        'tinyblob' => static::BLOB_SMALL,
887 2
                    ];
888 2
                    foreach ($sizes as $name => $length) {
889 16
                        if ($limit >= $length) {
890 2
                            return ['name' => $name];
891 2
                        }
892 2
                    }
893 16
                }
894 15
895 15
                return ['name' => 'blob'];
896 12
            case static::PHINX_TYPE_BIT:
897 12
                return ['name' => 'bit', 'limit' => $limit ?: 64];
898 15
            case static::PHINX_TYPE_INTEGER:
899 11
                if ($limit && $limit >= static::INT_TINY) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $limit of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
900 6
                    $sizes = [
901 4
                        // Order matters! Size must always be tested from longest to shortest!
902 4
                        'bigint' => static::INT_BIG,
903 6
                        'int' => static::INT_REGULAR,
904 6
                        'mediumint' => static::INT_MEDIUM,
905 10
                        'smallint' => static::INT_SMALL,
906 2
                        'tinyint' => static::INT_TINY,
907 2
                    ];
908 10
                    $limits = [
909 1
                        'int' => 11,
910 1
                        'bigint' => 20,
911 1
                    ];
912 10
                    foreach ($sizes as $name => $length) {
913 1
                        if ($limit >= $length) {
914 1
                            $def = ['name' => $name];
915 1
                            if (isset($limits[$name])) {
916 10
                                $def['limit'] = $limits[$name];
917 1
                            }
918 1
919 1
                            return $def;
920 10
                        }
921 2
                    }
922 2
                } elseif (!$limit) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $limit of type integer|null is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
923 2
                    $limit = 11;
924 9
                }
925 2
926 2
                return ['name' => 'int', 'limit' => $limit];
927 2
            case static::PHINX_TYPE_BIG_INTEGER:
928 8
                return ['name' => 'bigint', 'limit' => 20];
929 2
            case static::PHINX_TYPE_BOOLEAN:
930 2
                return ['name' => 'tinyint', 'limit' => 1];
931 2
            case static::PHINX_TYPE_UUID:
932
                return ['name' => 'char', 'limit' => 36];
933
            case static::TYPE_YEAR:
934
                if (!$limit || in_array($limit, [2, 4])) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $limit of type integer|null is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
935 16
                    $limit = 4;
936
                }
937
938 15
                return ['name' => 'year', 'limit' => $limit];
939 15
            default:
940
                throw new \RuntimeException('The type: "' . $type . '" is not supported.');
941 15
        }
942
    }
943 15
944 3
    /**
945 3
     * Returns Phinx type by SQL type
946
     *
947 15
     * @param string $sqlTypeDef
948
     * @throws \RuntimeException
949
     * @internal param string $sqlType SQL type
950
     * @returns string Phinx type
951
     */
952
    public function getPhinxType($sqlTypeDef)
953
    {
954 83
        $matches = [];
955
        if (!preg_match('/^([\w]+)(\(([\d]+)*(,([\d]+))*\))*(.+)*$/', $sqlTypeDef, $matches)) {
956 83
            throw new \RuntimeException('Column type ' . $sqlTypeDef . ' is not supported');
957
        } else {
958 83
            $limit = null;
959 1
            $precision = null;
960 1
            $type = $matches[1];
961 82
            if (count($matches) > 2) {
962
                $limit = $matches[3] ? (int)$matches[3] : null;
963 83
            }
964
            if (count($matches) > 4) {
965
                $precision = (int)$matches[5];
966
            }
967 View Code Duplication
            if ($type === 'tinyint' && $limit === 1) {
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...
968 4
                $type = static::PHINX_TYPE_BOOLEAN;
969
                $limit = null;
970 4
            }
971 4
            switch ($type) {
972 4
                case 'varchar':
973
                    $type = static::PHINX_TYPE_STRING;
974 4
                    if ($limit === 255) {
975 4
                        $limit = null;
976
                    }
977 4
                    break;
978 3 View Code Duplication
                case 'char':
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...
979 3
                    $type = static::PHINX_TYPE_CHAR;
980
                    if ($limit === 255) {
981 3
                        $limit = null;
982
                    }
983 3
                    if ($limit === 36) {
984
                        $type = static::PHINX_TYPE_UUID;
985
                    }
986
                    break;
987
                case 'tinyint':
988
                    $type = static::PHINX_TYPE_INTEGER;
989 81
                    $limit = static::INT_TINY;
990
                    break;
991 81
                case 'smallint':
992 81
                    $type = static::PHINX_TYPE_INTEGER;
993
                    $limit = static::INT_SMALL;
994
                    break;
995
                case 'mediumint':
996
                    $type = static::PHINX_TYPE_INTEGER;
997
                    $limit = static::INT_MEDIUM;
998
                    break;
999
                case 'int':
1000 89
                    $type = static::PHINX_TYPE_INTEGER;
1001
                    if ($limit === 11) {
1002 89
                        $limit = null;
1003
                    }
1004 89
                    break;
1005 89
                case 'bigint':
1006 89
                    if ($limit === 20) {
1007 2
                        $limit = null;
1008 89
                    }
1009 86
                    $type = static::PHINX_TYPE_BIG_INTEGER;
1010 86
                    break;
1011 89
                case 'bit':
1012 5
                    $type = static::PHINX_TYPE_BIT;
1013 5
                    if ($limit === 64) {
1014 89
                        $limit = null;
1015 89
                    }
1016 89
                    break;
1017 89
                case 'blob':
1018 89
                    $type = static::PHINX_TYPE_BINARY;
1019 89
                    break;
1020
                case 'tinyblob':
1021 89
                    $type = static::PHINX_TYPE_BINARY;
1022 2
                    $limit = static::BLOB_TINY;
1023 2
                    break;
1024
                case 'mediumblob':
1025 89
                    $type = static::PHINX_TYPE_BINARY;
1026 1
                    $limit = static::BLOB_MEDIUM;
1027 1
                    break;
1028
                case 'longblob':
1029 89
                    $type = static::PHINX_TYPE_BINARY;
1030
                    $limit = static::BLOB_LONG;
1031
                    break;
1032
                case 'tinytext':
1033
                    $type = static::PHINX_TYPE_TEXT;
1034
                    $limit = static::TEXT_TINY;
1035
                    break;
1036
                case 'mediumtext':
1037
                    $type = static::PHINX_TYPE_TEXT;
1038 16
                    $limit = static::TEXT_MEDIUM;
1039
                    break;
1040 16
                case 'longtext':
1041 16
                    $type = static::PHINX_TYPE_TEXT;
1042 16
                    $limit = static::TEXT_LONG;
1043 2
                    break;
1044 2
            }
1045
1046 16
            // Call this to check if parsed type is supported.
1047 5
            $this->getSqlType($type, $limit);
1048 5
1049
            $phinxType = [
1050 16
                'name' => $type,
1051 1
                'limit' => $limit,
1052 1
                'precision' => $precision
1053
            ];
1054 16
1055
            if (static::PHINX_TYPE_ENUM == $type) {
1056 16
                $phinxType['values'] = explode("','", trim($matches[6], "()'"));
1057 5
            }
1058 5
1059
            return $phinxType;
1060 16
        }
1061
    }
1062 16
1063
    /**
1064
     * {@inheritdoc}
1065
     */
1066
    public function createDatabase($name, $options = [])
1067
    {
1068
        $charset = isset($options['charset']) ? $options['charset'] : 'utf8';
1069
1070 View Code Duplication
        if (isset($options['collation'])) {
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...
1071 17
            $this->execute(sprintf('CREATE DATABASE `%s` DEFAULT CHARACTER SET `%s` COLLATE `%s`', $name, $charset, $options['collation']));
1072
        } else {
1073 17
            $this->execute(sprintf('CREATE DATABASE `%s` DEFAULT CHARACTER SET `%s`', $name, $charset));
1074 17
        }
1075 5
    }
1076 5
1077 17
    /**
1078 17
     * {@inheritdoc}
1079 17
     */
1080 17
    public function hasDatabase($name)
1081 17
    {
1082 17
        $rows = $this->fetchAll(
1083 17
            sprintf(
1084 17
                'SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = \'%s\'',
1085 17
                $name
1086 17
            )
1087 17
        );
1088 2
1089 2
        foreach ($rows as $row) {
1090 17
            if (!empty($row)) {
1091 2
                return true;
1092 2
            }
1093 17
        }
1094
1095
        return false;
1096
    }
1097
1098
    /**
1099
     * {@inheritdoc}
1100
     */
1101
    public function dropDatabase($name)
1102 2
    {
1103
        $this->execute(sprintf('DROP DATABASE IF EXISTS `%s`', $name));
1104 2
    }
1105
1106
    /**
1107 2
     * Gets the MySQL Column Definition for a Column object.
1108
     *
1109
     * @param \Phinx\Db\Table\Column $column Column
1110
     * @return string
1111 2
     */
1112 2
    protected function getColumnSqlDefinition(Column $column)
1113
    {
1114 2
        if ($column->getType() instanceof Literal) {
1115
            $def = (string)$column->getType();
1116 2
        } else {
1117
            $sqlType = $this->getSqlType($column->getType(), $column->getLimit());
1118
            $def = strtoupper($sqlType['name']);
1119
        }
1120
        if ($column->getPrecision() && $column->getScale()) {
1121
            $def .= '(' . $column->getPrecision() . ',' . $column->getScale() . ')';
1122
        } elseif (isset($sqlType['limit'])) {
1123 85
            $def .= '(' . $sqlType['limit'] . ')';
1124
        }
1125 85 View Code Duplication
        if (($values = $column->getValues()) && is_array($values)) {
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...
1126
            $def .= "('" . implode("', '", $values) . "')";
1127
        }
1128
        $def .= $column->getEncoding() ? ' CHARACTER SET ' . $column->getEncoding() : '';
1129
        $def .= $column->getCollation() ? ' COLLATE ' . $column->getCollation() : '';
1130
        $def .= (!$column->isSigned() && isset($this->signedColumnTypes[$column->getType()])) ? ' unsigned' : '';
1131
        $def .= ($column->isNull() == false) ? ' NOT NULL' : ' NULL';
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
1132
        $def .= $column->isIdentity() ? ' AUTO_INCREMENT' : '';
1133
        $def .= $this->getDefaultValueDefinition($column->getDefault(), $column->getType());
0 ignored issues
show
Bug introduced by
It seems like $column->getType() targeting Phinx\Db\Table\Column::getType() can also be of type object<Phinx\Util\Literal>; however, Phinx\Db\Adapter\PdoAdap...efaultValueDefinition() does only seem to accept string|null, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
1134
1135
        if ($column->getComment()) {
1136
            $def .= ' COMMENT ' . $this->getConnection()->quote($column->getComment());
1137
        }
1138
1139
        if ($column->getUpdate()) {
1140
            $def .= ' ON UPDATE ' . $column->getUpdate();
1141
        }
1142
1143
        return $def;
1144
    }
1145
1146
    /**
1147
     * Gets the MySQL Index Definition for an Index object.
1148
     *
1149
     * @param \Phinx\Db\Table\Index $index Index
1150
     * @return string
1151
     */
1152
    protected function getIndexSqlDefinition(Index $index)
1153
    {
1154
        $def = '';
1155
        $limit = '';
1156
1157
        if ($index->getType() == Index::UNIQUE) {
1158
            $def .= ' UNIQUE';
1159
        }
1160
1161
        if ($index->getType() == Index::FULLTEXT) {
1162
            $def .= ' FULLTEXT';
1163
        }
1164
1165
        $def .= ' KEY';
1166
1167
        if (is_string($index->getName())) {
1168
            $def .= ' `' . $index->getName() . '`';
1169
        }
1170
1171
        if (!is_array($index->getLimit())) {
1172
            if ($index->getLimit()) {
1173
                $limit = '(' . $index->getLimit() . ')';
1174
            }
1175
            $def .= ' (`' . implode('`,`', $index->getColumns()) . '`' . $limit . ')';
1176
        } else {
1177
            $columns = $index->getColumns();
1178
            $limits = $index->getLimit();
1179
            $def .= ' (';
1180
            foreach ($columns as $column) {
1181
                $limit = !isset($limits[$column]) || $limits[$column] <= 0 ? '' : '(' . $limits[$column] . ')';
1182
                $def .= '`' . $column . '`' . $limit . ', ';
1183
            }
1184
            $def = rtrim($def, ', ');
1185
            $def .= ' )';
1186
        }
1187
1188
        return $def;
1189
    }
1190
1191
    /**
1192
     * Gets the MySQL Foreign Key Definition for an ForeignKey object.
1193
     *
1194
     * @param \Phinx\Db\Table\ForeignKey $foreignKey
1195
     * @return string
1196
     */
1197 View Code Duplication
    protected function getForeignKeySqlDefinition(ForeignKey $foreignKey)
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...
1198
    {
1199
        $def = '';
1200
        if ($foreignKey->getConstraint()) {
1201
            $def .= ' CONSTRAINT ' . $this->quoteColumnName($foreignKey->getConstraint());
0 ignored issues
show
Bug introduced by
It seems like $foreignKey->getConstraint() targeting Phinx\Db\Table\ForeignKey::getConstraint() can also be of type boolean; however, Phinx\Db\Adapter\MysqlAdapter::quoteColumnName() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
1202
        }
1203
        $columnNames = [];
1204
        foreach ($foreignKey->getColumns() as $column) {
1205
            $columnNames[] = $this->quoteColumnName($column);
1206
        }
1207
        $def .= ' FOREIGN KEY (' . implode(',', $columnNames) . ')';
1208
        $refColumnNames = [];
1209
        foreach ($foreignKey->getReferencedColumns() as $column) {
1210
            $refColumnNames[] = $this->quoteColumnName($column);
1211
        }
1212
        $def .= ' REFERENCES ' . $this->quoteTableName($foreignKey->getReferencedTable()->getName()) . ' (' . implode(',', $refColumnNames) . ')';
1213
        if ($foreignKey->getOnDelete()) {
1214
            $def .= ' ON DELETE ' . $foreignKey->getOnDelete();
1215
        }
1216
        if ($foreignKey->getOnUpdate()) {
1217
            $def .= ' ON UPDATE ' . $foreignKey->getOnUpdate();
1218
        }
1219
1220
        return $def;
1221
    }
1222
1223
    /**
1224
     * Describes a database table. This is a MySQL adapter specific method.
1225
     *
1226
     * @param string $tableName Table name
1227
     * @return array
1228
     */
1229 View Code Duplication
    public function describeTable($tableName)
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...
1230
    {
1231
        $options = $this->getOptions();
1232
1233
        // mysql specific
1234
        $sql = sprintf(
1235
            "SELECT *
1236
             FROM information_schema.tables
1237
             WHERE table_schema = '%s'
1238
             AND table_name = '%s'",
1239
            $options['name'],
1240
            $tableName
1241
        );
1242
1243
        return $this->fetchRow($sql);
1244
    }
1245
1246
    /**
1247
     * Returns MySQL column types (inherited and MySQL specified).
1248
     * @return array
1249
     */
1250
    public function getColumnTypes()
1251
    {
1252
        return array_merge(parent::getColumnTypes(), ['enum', 'set', 'year', 'json']);
1253
    }
1254
1255
    /**
1256
     * {@inheritDoc}
1257
     *
1258
     */
1259 View Code Duplication
    public function getDecoratedConnection()
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...
1260
    {
1261
        $options = $this->getOptions();
1262
        $options = [
1263
            'username' => $options['user'],
1264
            'password' => $options['pass'],
1265
            'database' => $options['name'],
1266
            'quoteIdentifiers' => true,
1267
        ] + $options;
1268
1269
        $driver = new MysqlDriver($options);
1270
        if (method_exists($driver, 'setConnection')) {
1271
            $driver->setConnection($this->connection);
0 ignored issues
show
Bug introduced by
It seems like $this->connection can be null; however, setConnection() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
1272
        } else {
1273
            $driver->connection($this->connection);
0 ignored issues
show
Deprecated Code introduced by
The method Cake\Database\Driver::connection() has been deprecated with message: 3.6.0 Use getConnection()/setConnection() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
1274
        }
1275
1276
        return new Connection(['driver' => $driver] + $options);
1277
    }
1278
}
1279