Passed
Pull Request — master (#40)
by Wilmer
14:04
created

MysqlQueryBuilder   C

Complexity

Total Complexity 56

Size/Duplication

Total Lines 644
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 151
dl 0
loc 644
rs 5.5199
c 0
b 0
f 0
wmc 56

23 Methods

Rating   Name   Duplication   Size   Complexity  
A supportsFractionalSeconds() 0 5 1
A hasOffset() 0 6 2
A prepareInsertValues() 0 17 6
A dropPrimaryKey() 0 4 1
A defaultTimeTypeMap() 0 17 2
A upsert() 0 23 5
A dropUnique() 0 3 1
A getColumnType() 0 5 1
A hasLimit() 0 4 1
A renameColumn() 0 32 6
A resetSequence() 0 23 5
A dropCheck() 0 3 1
A buildLimit() 0 21 4
A checkIntegrity() 0 3 2
A createIndex() 0 7 2
A dropCommentFromColumn() 0 3 1
A addCheck() 0 3 1
A getColumnDefinition() 0 21 6
A dropForeignKey() 0 5 1
A addCommentOnTable() 0 3 1
A defaultExpressionBuilders() 0 4 1
A dropCommentFromTable() 0 3 1
A addCommentOnColumn() 0 30 4

How to fix   Complexity   

Complex Class

Complex classes like MysqlQueryBuilder often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use MysqlQueryBuilder, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Db\Mysql\Query;
6
7
use Yiisoft\Db\Exception\Exception;
8
use Yiisoft\Db\Exception\InvalidArgumentException;
9
use Yiisoft\Db\Exception\InvalidConfigException;
10
use Yiisoft\Db\Exception\NotSupportedException;
11
use Yiisoft\Db\Expression\Expression;
12
use Yiisoft\Db\Expression\ExpressionBuilder;
13
use Yiisoft\Db\Expression\JsonExpression;
14
use Yiisoft\Db\Mysql\Expression\JsonExpressionBuilder;
15
use Yiisoft\Db\Mysql\Schema\MysqlColumnSchemaBuilder;
16
use Yiisoft\Db\Mysql\Schema\MysqlSchema;
17
use Yiisoft\Db\Query\Query;
18
use Yiisoft\Db\Query\QueryBuilder;
19
20
final class MysqlQueryBuilder extends QueryBuilder
21
{
22
    /**
23
     * @var array mapping from abstract column types (keys) to physical column types (values).
24
     */
25
    protected array $typeMap = [
26
        MysqlSchema::TYPE_PK => 'int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY',
27
        MysqlSchema::TYPE_UPK => 'int(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY',
28
        MysqlSchema::TYPE_BIGPK => 'bigint(20) NOT NULL AUTO_INCREMENT PRIMARY KEY',
29
        MysqlSchema::TYPE_UBIGPK => 'bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY',
30
        MysqlSchema::TYPE_CHAR => 'char(1)',
31
        MysqlSchema::TYPE_STRING => 'varchar(255)',
32
        MysqlSchema::TYPE_TEXT => 'text',
33
        MysqlSchema::TYPE_TINYINT => 'tinyint(3)',
34
        MysqlSchema::TYPE_SMALLINT => 'smallint(6)',
35
        MysqlSchema::TYPE_INTEGER => 'int(11)',
36
        MysqlSchema::TYPE_BIGINT => 'bigint(20)',
37
        MysqlSchema::TYPE_FLOAT => 'float',
38
        MysqlSchema::TYPE_DOUBLE => 'double',
39
        MysqlSchema::TYPE_DECIMAL => 'decimal(10,0)',
40
        MysqlSchema::TYPE_DATE => 'date',
41
        MysqlSchema::TYPE_BINARY => 'blob',
42
        MysqlSchema::TYPE_BOOLEAN => 'tinyint(1)',
43
        MysqlSchema::TYPE_MONEY => 'decimal(19,4)',
44
        MysqlSchema::TYPE_JSON => 'json'
45
    ];
46
47
    /**
48
     * Contains array of default expression builders. Extend this method and override it, if you want to change default
49
     * expression builders for this query builder.
50
     *
51
     * @return array
52
     *
53
     * See {@see ExpressionBuilder} docs for details.
54
     */
55
    protected function defaultExpressionBuilders(): array
56
    {
57
        return \array_merge(parent::defaultExpressionBuilders(), [
58
            JsonExpression::class => JsonExpressionBuilder::class,
59
        ]);
60
    }
61
62
    /**
63
     * Builds a SQL statement for renaming a column.
64
     *
65
     * @param string $table   the table whose column is to be renamed. The name will be properly quoted by the method.
66
     * @param string $oldName the old name of the column. The name will be properly quoted by the method.
67
     * @param string $newName the new name of the column. The name will be properly quoted by the method.
68
     *
69
     * @throws Exception
70
     *
71
     * @return string the SQL statement for renaming a DB column.
72
     */
73
    public function renameColumn(string $table, string $oldName, string $newName): string
74
    {
75
        $quotedTable = $this->db->quoteTableName($table);
0 ignored issues
show
Bug introduced by
The method quoteTableName() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

75
        /** @scrutinizer ignore-call */ 
76
        $quotedTable = $this->db->quoteTableName($table);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
76
77
        $row = $this->db->createCommand('SHOW CREATE TABLE ' . $quotedTable)->queryOne();
78
79
        if ($row === false) {
80
            throw new Exception("Unable to find column '$oldName' in table '$table'.");
81
        }
82
83
        if (isset($row['Create Table'])) {
84
            $sql = $row['Create Table'];
85
        } else {
86
            $row = \array_values($row);
87
            $sql = $row[1];
88
        }
89
90
        if (\preg_match_all('/^\s*`(.*?)`\s+(.*?),?$/m', $sql, $matches)) {
91
            foreach ($matches[1] as $i => $c) {
92
                if ($c === $oldName) {
93
                    return "ALTER TABLE $quotedTable CHANGE "
94
                        . $this->db->quoteColumnName($oldName) . ' '
95
                        . $this->db->quoteColumnName($newName) . ' '
96
                        . $matches[2][$i];
97
                }
98
            }
99
        }
100
101
        /* try to give back a SQL anyway */
102
        return "ALTER TABLE $quotedTable CHANGE "
103
            . $this->db->quoteColumnName($oldName) . ' '
104
            . $this->db->quoteColumnName($newName);
105
    }
106
107
    /**
108
     * Builds a SQL statement for creating a new index.
109
     *
110
     * @param string $name the name of the index. The name will be properly quoted by the method.
111
     * @param string $table the table that the new index will be created for. The table name will be properly quoted by
112
     * the method.
113
     * @param string|array $columns the column(s) that should be included in the index. If there are multiple columns,
114
     * separate them with commas or use an array to represent them. Each column name will be properly quoted by the
115
     * method, unless a parenthesis is found in the name.
116
     * @param bool $unique whether to add UNIQUE constraint on the created index.
117
     *
118
     * @throws Exception
119
     * @throws InvalidArgumentException
120
     * @throws InvalidConfigException
121
     * @throws NotSupportedException
122
     *
123
     * @return string the SQL statement for creating a new index.
124
     *
125
     * {@see https://bugs.mysql.com/bug.php?id=48875}
126
     */
127
    public function createIndex(string $name, string $table, $columns, bool $unique = false): string
128
    {
129
        return 'ALTER TABLE '
130
            . $this->db->quoteTableName($table)
131
            . ($unique ? ' ADD UNIQUE INDEX ' : ' ADD INDEX ')
132
            . $this->db->quoteTableName($name)
133
            . ' (' . $this->buildColumns($columns) . ')';
134
    }
135
136
    /**
137
     * Builds a SQL statement for dropping a foreign key constraint.
138
     *
139
     * @param string $name the name of the foreign key constraint to be dropped. The name will be properly quoted by the
140
     * method.
141
     * @param string $table the table whose foreign is to be dropped. The name will be properly quoted by the method.
142
     *
143
     * @throws Exception
144
     * @throws InvalidConfigException
145
     * @throws NotSupportedException
146
     *
147
     * @return string the SQL statement for dropping a foreign key constraint.
148
     */
149
    public function dropForeignKey(string $name, string $table): string
150
    {
151
        return 'ALTER TABLE '
152
            . $this->db->quoteTableName($table)
153
            . ' DROP FOREIGN KEY ' . $this->db->quoteColumnName($name);
154
    }
155
156
    /**
157
     * Builds a SQL statement for removing a primary key constraint to an existing table.
158
     *
159
     * @param string $name the name of the primary key constraint to be removed.
160
     * @param string $table the table that the primary key constraint will be removed from.
161
     *
162
     * @throws Exception
163
     * @throws InvalidConfigException
164
     * @throws NotSupportedException
165
     *
166
     * @return string the SQL statement for removing a primary key constraint from an existing table.
167
     */
168
    public function dropPrimaryKey(string $name, string $table): string
169
    {
170
        return 'ALTER TABLE '
171
            . $this->db->quoteTableName($table) . ' DROP PRIMARY KEY';
172
    }
173
174
    /**
175
     * Creates a SQL command for dropping an unique constraint.
176
     *
177
     * @param string $name the name of the unique constraint to be dropped. The name will be properly quoted by the
178
     * method.
179
     * @param string $table the table whose unique constraint is to be dropped. The name will be properly quoted by the
180
     * method.
181
     *
182
     * @throws Exception
183
     * @throws InvalidConfigException
184
     * @throws NotSupportedException
185
     *
186
     * @return string the SQL statement for dropping an unique constraint.
187
     */
188
    public function dropUnique(string $name, string $table): string
189
    {
190
        return $this->dropIndex($name, $table);
191
    }
192
193
    /**
194
     * @param string $name
195
     * @param string $table
196
     * @param string $expression
197
     *
198
     * @throws NotSupportedException Method not supported by Mysql.
199
     *
200
     * @return string the SQL statement for adding a check constraint to an existing table.
201
     */
202
    public function addCheck(string $name, string $table, string $expression): string
203
    {
204
        throw new NotSupportedException(__METHOD__ . ' is not supported by MySQL.');
205
    }
206
207
    /**
208
     * @param string $name
209
     * @param string $table
210
     *
211
     * @throws NotSupportedException Method not supported by Mysql.
212
     *
213
     * @return string the SQL statement for adding a check constraint to an existing table.
214
     */
215
    public function dropCheck(string $name, string $table): string
216
    {
217
        throw new NotSupportedException(__METHOD__ . ' is not supported by MySQL.');
218
    }
219
220
    /**
221
     * Creates a SQL statement for resetting the sequence value of a table's primary key.
222
     *
223
     * The sequence will be reset such that the primary key of the next new row inserted will have the specified value
224
     * or 1.
225
     *
226
     * @param string $tableName the name of the table whose primary key sequence will be reset.
227
     * @param mixed $value the value for the primary key of the next new row inserted. If this is not set, the next new
228
     * row's primary key will have a value 1.
229
     *
230
     * @throws Exception
231
     * @throws InvalidArgumentException
232
     * @throws InvalidConfigException
233
     * @throws NotSupportedException
234
     *
235
     * @return string the SQL statement for resetting sequence.
236
     */
237
    public function resetSequence(string $tableName, $value = null): string
238
    {
239
        $table = $this->db->getTableSchema($tableName);
240
241
        if ($table !== null && $table->getSequenceName() !== null) {
242
            $tableName = $this->db->quoteTableName($tableName);
243
244
            if ($value === null) {
245
                $pk = $table->getPrimaryKey();
246
                $key = \reset($pk);
247
                $value = $this->db->createCommand("SELECT MAX(`$key`) FROM $tableName")->queryScalar() + 1;
248
            } else {
249
                $value = (int) $value;
250
            }
251
252
            return "ALTER TABLE $tableName AUTO_INCREMENT=$value";
253
        }
254
255
        if ($table === null) {
256
            throw new \InvalidArgumentException("Table not found: $tableName");
257
        }
258
259
        throw new \InvalidArgumentException("There is no sequence associated with table '$tableName'.");
260
    }
261
262
    /**
263
     * Builds a SQL statement for enabling or disabling integrity check.
264
     *
265
     * @param bool $check  whether to turn on or off the integrity check.
266
     * @param string $schema the schema of the tables. Meaningless for MySQL.
267
     * @param string $table  the table name. Meaningless for MySQL.
268
     *
269
     * @return string the SQL statement for checking integrity.
270
     */
271
    public function checkIntegrity(string $schema = '', string $table = '', bool $check = true): string
272
    {
273
        return 'SET FOREIGN_KEY_CHECKS = ' . ($check ? 1 : 0);
274
    }
275
276
    /**
277
     * @param int|object|null $limit
278
     * @param int|object|null $offset
279
     *
280
     * @return string the LIMIT and OFFSET clauses.
281
     */
282
    public function buildLimit($limit, $offset): string
283
    {
284
        $sql = '';
285
286
        if ($this->hasLimit($limit)) {
287
            $sql = 'LIMIT ' . $limit;
0 ignored issues
show
Bug introduced by
Are you sure $limit of type integer|null|object can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

287
            $sql = 'LIMIT ' . /** @scrutinizer ignore-type */ $limit;
Loading history...
288
289
            if ($this->hasOffset($offset)) {
290
                $sql .= ' OFFSET ' . $offset;
0 ignored issues
show
Bug introduced by
Are you sure $offset of type integer|null|object can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

290
                $sql .= ' OFFSET ' . /** @scrutinizer ignore-type */ $offset;
Loading history...
291
            }
292
        } elseif ($this->hasOffset($offset)) {
293
            /**
294
             * limit is not optional in MySQL.
295
             *
296
             * http://stackoverflow.com/a/271650/1106908
297
             * http://dev.mysql.com/doc/refman/5.0/en/select.html#idm47619502796240
298
             */
299
            $sql = "LIMIT $offset, 18446744073709551615"; // 2^64-1
300
        }
301
302
        return $sql;
303
    }
304
305
    /**
306
     * Checks to see if the given limit is effective.
307
     *
308
     * @param mixed $limit the given limit.
309
     *
310
     * @return bool whether the limit is effective.
311
     */
312
    protected function hasLimit($limit): bool
313
    {
314
        /** In MySQL limit argument must be non negative integer constant */
315
        return \ctype_digit((string) $limit);
316
    }
317
318
    /**
319
     * Checks to see if the given offset is effective.
320
     *
321
     * @param mixed $offset the given offset.
322
     *
323
     * @return bool whether the offset is effective.
324
     */
325
    protected function hasOffset($offset): bool
326
    {
327
        /** In MySQL offset argument must be non negative integer constant */
328
        $offset = (string) $offset;
329
330
        return \ctype_digit($offset) && $offset !== '0';
331
    }
332
333
    /**
334
     * Prepares a `VALUES` part for an `INSERT` SQL statement.
335
     *
336
     * @param string $table the table that new rows will be inserted into.
337
     * @param array|Query $columns the column data (name => value) to be inserted into the table or instance of
338
     * {@see \Yiisoft\Db\Query\Query|Query} to perform INSERT INTO ... SELECT SQL statement.
339
     * @param array $params the binding parameters that will be generated by this method. They should be bound to the DB
340
     * command later.
341
     *
342
     * @throws Exception
343
     * @throws InvalidArgumentException
344
     * @throws InvalidConfigException
345
     * @throws NotSupportedException
346
     *
347
     * @return array array of column names, placeholders, values and params.
348
     */
349
    protected function prepareInsertValues(string $table, $columns, array $params = []): array
350
    {
351
        [$names, $placeholders, $values, $params] = parent::prepareInsertValues($table, $columns, $params);
352
        if (!$columns instanceof Query && empty($names)) {
353
            $tableSchema = $this->db->getSchema()->getTableSchema($table);
354
            $columns = $tableSchema->getColumns();
355
            if ($tableSchema !== null) {
356
                $columns = !empty($tableSchema->getPrimaryKey())
357
                    ? $tableSchema->getPrimaryKey() : [\reset($columns)->getName()];
358
                foreach ($columns as $name) {
359
                    $names[] = $this->db->quoteColumnName($name);
360
                    $placeholders[] = 'DEFAULT';
361
                }
362
            }
363
        }
364
365
        return [$names, $placeholders, $values, $params];
366
    }
367
368
    /**
369
     * Creates an SQL statement to insert rows into a database table if they do not already exist (matching unique
370
     * constraints), or update them if they do.
371
     *
372
     * For example,
373
     *
374
     * ```php
375
     * $sql = $queryBuilder->upsert('pages', [
376
     *     'name' => 'Front page',
377
     *     'url' => 'http://example.com/', // url is unique
378
     *     'visits' => 0,
379
     * ], [
380
     *     'visits' => new \Yiisoft\Db\Expression('visits + 1'),
381
     * ], $params);
382
     * ```
383
     *
384
     * The method will properly escape the table and column names.
385
     *
386
     * @param string $table the table that new rows will be inserted into/updated in.
387
     * @param array|Query $insertColumns the column data (name => value) to be inserted into the table or instance of
388
     * {@see Query} to perform `INSERT INTO ... SELECT` SQL statement.
389
     * @param array|bool $updateColumns the column data (name => value) to be updated if they already exist. If `true`
390
     * is passed, the column data will be updated to match the insert column data. If `false` is passed, no update will
391
     * be performed if the column data already exists.
392
     * @param array $params the binding parameters that will be generated by this method. They should be bound to the DB
393
     * command later.
394
     *
395
     * @throws Exception
396
     * @throws InvalidConfigException
397
     * @throws NotSupportedException if this is not supported by the underlying DBMS.
398
     *
399
     * @return string the resulting SQL.
400
     */
401
    public function upsert(string $table, $insertColumns, $updateColumns, array &$params): string
402
    {
403
        $insertSql = $this->insert($table, $insertColumns, $params);
404
405
        [$uniqueNames, , $updateNames] = $this->prepareUpsertColumns($table, $insertColumns, $updateColumns);
406
407
        if (empty($uniqueNames)) {
408
            return $insertSql;
409
        }
410
411
        if ($updateColumns === true) {
412
            $updateColumns = [];
413
            foreach ($updateNames as $name) {
414
                $updateColumns[$name] = new Expression('VALUES(' . $this->db->quoteColumnName($name) . ')');
415
            }
416
        } elseif ($updateColumns === false) {
417
            $name = $this->db->quoteColumnName(\reset($uniqueNames));
418
            $updateColumns = [$name => new Expression($this->db->quoteTableName($table) . '.' . $name)];
419
        }
420
421
        [$updates, $params] = $this->prepareUpdateSets($table, $updateColumns, $params);
422
423
        return $insertSql . ' ON DUPLICATE KEY UPDATE ' . \implode(', ', $updates);
424
    }
425
426
    /**
427
     * Builds a SQL command for adding comment to column.
428
     *
429
     * @param string $table the table whose column is to be commented. The table name will be properly quoted by the
430
     * method.
431
     * @param string $column the name of the column to be commented. The column name will be properly quoted by the
432
     * method.
433
     * @param string $comment the text of the comment to be added. The comment will be properly quoted by the method.
434
     *
435
     * @throws Exception
436
     * @throws InvalidConfigException
437
     * @throws NotSupportedException
438
     *
439
     * @return string the SQL statement for adding comment on column.
440
     */
441
    public function addCommentOnColumn(string $table, string $column, string $comment): string
442
    {
443
        /** Strip existing comment which may include escaped quotes */
444
        $definition = \trim(
445
            \preg_replace(
446
                "/COMMENT '(?:''|[^'])*'/i",
447
                '',
448
                $this->getColumnDefinition($table, $column)
449
            )
450
        );
451
452
        $checkRegex = '/CHECK *(\(([^()]|(?-2))*\))/';
453
454
        $check = \preg_match($checkRegex, $definition, $checkMatches);
455
456
        if ($check === 1) {
457
            $definition = \preg_replace($checkRegex, '', $definition);
458
        }
459
460
        $alterSql = 'ALTER TABLE ' . $this->db->quoteTableName($table)
461
            . ' CHANGE ' . $this->db->quoteColumnName($column)
462
            . ' ' . $this->db->quoteColumnName($column)
463
            . (empty($definition) ? '' : ' ' . $definition)
464
            . ' COMMENT ' . $this->db->quoteValue($comment);
465
466
        if ($check === 1) {
467
            $alterSql .= ' ' . $checkMatches[0];
468
        }
469
470
        return $alterSql;
471
    }
472
473
    /**
474
     * Builds a SQL command for adding comment to table.
475
     *
476
     * @param string $table the table whose column is to be commented. The table name will be properly quoted by the
477
     * method.
478
     * @param string $comment the text of the comment to be added. The comment will be properly quoted by the method.
479
     *
480
     * @throws Exception
481
     * @throws InvalidConfigException
482
     * @throws NotSupportedException
483
     *
484
     * @return string the SQL statement for adding comment on table.
485
     */
486
    public function addCommentOnTable(string $table, string $comment): string
487
    {
488
        return 'ALTER TABLE ' . $this->db->quoteTableName($table) . ' COMMENT ' . $this->db->quoteValue($comment);
489
    }
490
491
    /**
492
     * Builds a SQL command for adding comment to column.
493
     *
494
     * @param string $table the table whose column is to be commented. The table name will be properly quoted by the
495
     * method.
496
     * @param string $column the name of the column to be commented. The column name will be properly quoted by the
497
     * method.
498
     *
499
     * @throws Exception
500
     * @throws InvalidConfigException
501
     * @throws NotSupportedException
502
     *
503
     * @return string the SQL statement for adding comment on column.
504
     */
505
    public function dropCommentFromColumn(string $table, string $column): string
506
    {
507
        return $this->addCommentOnColumn($table, $column, '');
508
    }
509
510
    /**
511
     * Builds a SQL command for adding comment to table.
512
     *
513
     * @param string $table the table whose column is to be commented. The table name will be properly quoted by the
514
     * method.
515
     *
516
     * @throws Exception
517
     * @throws InvalidConfigException
518
     * @throws NotSupportedException
519
     *
520
     * @return string the SQL statement for adding comment on column.
521
     */
522
    public function dropCommentFromTable(string $table): string
523
    {
524
        return $this->addCommentOnTable($table, '');
525
    }
526
527
    /**
528
     * Gets column definition.
529
     *
530
     * @param string $table table name.
531
     * @param string $column column name.
532
     *
533
     * @throws Exception in case when table does not contain column.
534
     *
535
     * @return string|null the column definition.
536
     */
537
    private function getColumnDefinition(string $table, string $column): ?string
538
    {
539
        $quotedTable = $this->db->quoteTableName($table);
540
541
        $row = $this->db->createCommand('SHOW CREATE TABLE ' . $quotedTable)->queryOne();
542
543
        if ($row === false) {
544
            throw new Exception("Unable to find column '$column' in table '$table'.");
545
        }
546
547
        if (!isset($row['Create Table'])) {
548
            $row = \array_values($row);
549
            $sql = $row[1];
550
        } else {
551
            $sql = $row['Create Table'];
552
        }
553
554
        if (\preg_match_all('/^\s*`(.*?)`\s+(.*?),?$/m', $sql, $matches)) {
555
            foreach ($matches[1] as $i => $c) {
556
                if ($c === $column) {
557
                    return $matches[2][$i];
558
                }
559
            }
560
        }
561
    }
562
563
    /**
564
     * Converts an abstract column type into a physical column type.
565
     *
566
     * The conversion is done using the type map specified in {@see typeMap}.
567
     * The following abstract column types are supported (using MySQL as an example to explain the corresponding
568
     * physical types):
569
     *
570
     * - `pk`: an auto-incremental primary key type, will be converted into "int(11) NOT NULL AUTO_INCREMENT PRIMARY
571
     *    KEY"
572
     * - `bigpk`: an auto-incremental primary key type, will be converted into "bigint(20) NOT NULL AUTO_INCREMENT
573
     *    PRIMARY KEY"
574
     * - `upk`: an unsigned auto-incremental primary key type, will be converted into "int(10) UNSIGNED NOT NULL
575
     *    AUTO_INCREMENT PRIMARY KEY"
576
     * - `char`: char type, will be converted into "char(1)"
577
     * - `string`: string type, will be converted into "varchar(255)"
578
     * - `text`: a long string type, will be converted into "text"
579
     * - `smallint`: a small integer type, will be converted into "smallint(6)"
580
     * - `integer`: integer type, will be converted into "int(11)"
581
     * - `bigint`: a big integer type, will be converted into "bigint(20)"
582
     * - `boolean`: boolean type, will be converted into "tinyint(1)"
583
     * - `float``: float number type, will be converted into "float"
584
     * - `decimal`: decimal number type, will be converted into "decimal"
585
     * - `datetime`: datetime type, will be converted into "datetime"
586
     * - `timestamp`: timestamp type, will be converted into "timestamp"
587
     * - `time`: time type, will be converted into "time"
588
     * - `date`: date type, will be converted into "date"
589
     * - `money`: money type, will be converted into "decimal(19,4)"
590
     * - `binary`: binary data type, will be converted into "blob"
591
     *
592
     * If the abstract type contains two or more parts separated by spaces (e.g. "string NOT NULL"), then only the first
593
     * part will be converted, and the rest of the parts will be appended to the converted result.
594
     *
595
     * For example, 'string NOT NULL' is converted to 'varchar(255) NOT NULL'.
596
     *
597
     * For some of the abstract types you can also specify a length or precision constraint by appending it in round
598
     * brackets directly to the type.
599
     *
600
     * For example `string(32)` will be converted into "varchar(32)" on a MySQL database. If the underlying DBMS does
601
     * not support these kind of constraints for a type it will be ignored.
602
     *
603
     * If a type cannot be found in {@see typeMap}, it will be returned without any change.
604
     *
605
     * @param string|ColumnSchemaBuilder $type abstract column type
0 ignored issues
show
Bug introduced by
The type Yiisoft\Db\Mysql\Query\ColumnSchemaBuilder was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
606
     *
607
     * @throws Exception
608
     * @throws InvalidConfigException
609
     *
610
     * @return string physical column type.
611
     */
612
    public function getColumnType($type): string
613
    {
614
        $this->typeMap = \array_merge($this->typeMap, $this->defaultTimeTypeMap());
615
616
        return parent::getColumnType($type);
617
    }
618
619
    /**
620
     * Returns the map for default time type.
621
     *
622
     * If the version of MySQL is lower than 5.6.4, then the types will be without fractional seconds, otherwise with
623
     * fractional seconds.
624
     *
625
     * @throws Exception
626
     * @throws InvalidConfigException
627
     *
628
     * @return array
629
     */
630
    private function defaultTimeTypeMap(): array
631
    {
632
        $map = [
633
            MysqlSchema::TYPE_DATETIME => 'datetime',
634
            MysqlSchema::TYPE_TIMESTAMP => 'timestamp',
635
            MysqlSchema::TYPE_TIME => 'time',
636
        ];
637
638
        if ($this->supportsFractionalSeconds()) {
639
            $map = [
640
                MysqlSchema::TYPE_DATETIME => 'datetime(0)',
641
                MysqlSchema::TYPE_TIMESTAMP => 'timestamp(0)',
642
                MysqlSchema::TYPE_TIME => 'time(0)',
643
            ];
644
        }
645
646
        return $map;
647
    }
648
649
    /**
650
     * Checks the ability to use fractional seconds.
651
     *
652
     * @return bool
653
     *
654
     * @throws Exception
655
     * @throws InvalidConfigException
656
     *
657
     * {@see https://dev.mysql.com/doc/refman/5.6/en/fractional-seconds.html}
658
     */
659
    private function supportsFractionalSeconds(): bool
660
    {
661
        $version = $this->db->getSlavePdo()->getAttribute(\PDO::ATTR_SERVER_VERSION);
662
663
        return \version_compare($version, '5.6.4', '>=');
664
    }
665
}
666