Completed
Push — 2.0.15-dev ( f8560b...702618 )
by Carsten
75:18 queued 70:51
created

Schema   F

Complexity

Total Complexity 68

Size/Duplication

Total Lines 550
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 15

Test Coverage

Coverage 84.65%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 68
c 1
b 0
f 0
lcom 1
cbo 15
dl 0
loc 550
ccs 171
cts 202
cp 0.8465
rs 2.9411

19 Methods

Rating   Name   Duplication   Size   Complexity  
A resolveTableName() 0 14 3
A findTableNames() 0 9 2
A loadTableSchema() 0 12 2
A loadTablePrimaryKey() 0 4 1
A loadTableForeignKeys() 0 4 1
B loadTableIndexes() 0 32 3
A loadTableUniques() 0 4 1
A loadTableChecks() 0 4 1
A loadTableDefaultValues() 0 4 1
A createQueryBuilder() 0 4 1
A resolveTableNames() 0 11 2
C loadColumnSchema() 0 59 18
C findColumns() 0 30 8
A getCreateTableSql() 0 12 2
C findConstraints() 0 59 9
A findUniqueIndexes() 0 16 3
A createColumnSchemaBuilder() 0 4 1
A isOldMysql() 0 9 2
C loadTableConstraints() 0 90 7

How to fix   Complexity   

Complex Class

Complex classes like Schema 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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 Schema, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * @link http://www.yiiframework.com/
4
 * @copyright Copyright (c) 2008 Yii Software LLC
5
 * @license http://www.yiiframework.com/license/
6
 */
7
8
namespace yii\db\mysql;
9
10
use yii\base\InvalidConfigException;
11
use yii\base\NotSupportedException;
12
use yii\db\Constraint;
13
use yii\db\ConstraintFinderInterface;
14
use yii\db\ConstraintFinderTrait;
15
use yii\db\Exception;
16
use yii\db\Expression;
17
use yii\db\ForeignKeyConstraint;
18
use yii\db\IndexConstraint;
19
use yii\db\TableSchema;
20
use yii\helpers\ArrayHelper;
21
22
/**
23
 * Schema is the class for retrieving metadata from a MySQL database (version 4.1.x and 5.x).
24
 *
25
 * @author Qiang Xue <[email protected]>
26
 * @since 2.0
27
 */
28
class Schema extends \yii\db\Schema implements ConstraintFinderInterface
29
{
30
    use ConstraintFinderTrait;
31
32
    /**
33
     * {@inheritdoc}
34
     */
35
    public $columnSchemaClass = 'yii\db\mysql\ColumnSchema';
36
    /**
37
     * @var bool whether MySQL used is older than 5.1.
38
     */
39
    private $_oldMysql;
40
41
42
    /**
43
     * @var array mapping from physical column types (keys) to abstract column types (values)
44
     */
45
    public $typeMap = [
46
        'tinyint' => self::TYPE_TINYINT,
47
        'bit' => self::TYPE_INTEGER,
48
        'smallint' => self::TYPE_SMALLINT,
49
        'mediumint' => self::TYPE_INTEGER,
50
        'int' => self::TYPE_INTEGER,
51
        'integer' => self::TYPE_INTEGER,
52
        'bigint' => self::TYPE_BIGINT,
53
        'float' => self::TYPE_FLOAT,
54
        'double' => self::TYPE_DOUBLE,
55
        'real' => self::TYPE_FLOAT,
56
        'decimal' => self::TYPE_DECIMAL,
57
        'numeric' => self::TYPE_DECIMAL,
58
        'tinytext' => self::TYPE_TEXT,
59
        'mediumtext' => self::TYPE_TEXT,
60
        'longtext' => self::TYPE_TEXT,
61
        'longblob' => self::TYPE_BINARY,
62
        'blob' => self::TYPE_BINARY,
63
        'text' => self::TYPE_TEXT,
64
        'varchar' => self::TYPE_STRING,
65
        'string' => self::TYPE_STRING,
66
        'char' => self::TYPE_CHAR,
67
        'datetime' => self::TYPE_DATETIME,
68
        'year' => self::TYPE_DATE,
69
        'date' => self::TYPE_DATE,
70
        'time' => self::TYPE_TIME,
71
        'timestamp' => self::TYPE_TIMESTAMP,
72
        'enum' => self::TYPE_STRING,
73
        'varbinary' => self::TYPE_BINARY,
74
        'json' => self::TYPE_JSON,
75
    ];
76
77
    /**
78
     * {@inheritdoc}
79
     */
80
    protected $tableQuoteCharacter = '`';
81
    /**
82
     * {@inheritdoc}
83
     */
84
    protected $columnQuoteCharacter = '`';
85
86
    /**
87
     * {@inheritdoc}
88
     */
89 62
    protected function resolveTableName($name)
90
    {
91 62
        $resolvedName = new TableSchema();
92 62
        $parts = explode('.', str_replace('`', '', $name));
93 62
        if (isset($parts[1])) {
94
            $resolvedName->schemaName = $parts[0];
95
            $resolvedName->name = $parts[1];
96
        } else {
97 62
            $resolvedName->schemaName = $this->defaultSchema;
98 62
            $resolvedName->name = $name;
99
        }
100 62
        $resolvedName->fullName = ($resolvedName->schemaName !== $this->defaultSchema ? $resolvedName->schemaName . '.' : '') . $resolvedName->name;
101 62
        return $resolvedName;
102
    }
103
104
    /**
105
     * {@inheritdoc}
106
     */
107 6
    protected function findTableNames($schema = '')
108
    {
109 6
        $sql = 'SHOW TABLES';
110 6
        if ($schema !== '') {
111
            $sql .= ' FROM ' . $this->quoteSimpleTableName($schema);
112
        }
113
114 6
        return $this->db->createCommand($sql)->queryColumn();
115
    }
116
117
    /**
118
     * {@inheritdoc}
119
     */
120 344
    protected function loadTableSchema($name)
121
    {
122 344
        $table = new TableSchema();
123 344
        $this->resolveTableNames($table, $name);
124
125 344
        if ($this->findColumns($table)) {
126 339
            $this->findConstraints($table);
127 339
            return $table;
128
        }
129
130 15
        return null;
131
    }
132
133
    /**
134
     * {@inheritdoc}
135
     */
136 35
    protected function loadTablePrimaryKey($tableName)
137
    {
138 35
        return $this->loadTableConstraints($tableName, 'primaryKey');
139
    }
140
141
    /**
142
     * {@inheritdoc}
143
     */
144 4
    protected function loadTableForeignKeys($tableName)
145
    {
146 4
        return $this->loadTableConstraints($tableName, 'foreignKeys');
147
    }
148
149
    /**
150
     * {@inheritdoc}
151
     */
152 32
    protected function loadTableIndexes($tableName)
153
    {
154 32
        static $sql = <<<'SQL'
155
SELECT
156
    `s`.`INDEX_NAME` AS `name`,
157
    `s`.`COLUMN_NAME` AS `column_name`,
158
    `s`.`NON_UNIQUE` ^ 1 AS `index_is_unique`,
159
    `s`.`INDEX_NAME` = 'PRIMARY' AS `index_is_primary`
160
FROM `information_schema`.`STATISTICS` AS `s`
161
WHERE `s`.`TABLE_SCHEMA` = COALESCE(:schemaName, DATABASE()) AND `s`.`INDEX_SCHEMA` = `s`.`TABLE_SCHEMA` AND `s`.`TABLE_NAME` = :tableName
162
ORDER BY `s`.`SEQ_IN_INDEX` ASC
163
SQL;
164
165 32
        $resolvedName = $this->resolveTableName($tableName);
166 32
        $indexes = $this->db->createCommand($sql, [
167 32
            ':schemaName' => $resolvedName->schemaName,
168 32
            ':tableName' => $resolvedName->name,
169 32
        ])->queryAll();
170 32
        $indexes = $this->normalizePdoRowKeyCase($indexes, true);
171 32
        $indexes = ArrayHelper::index($indexes, null, 'name');
172 32
        $result = [];
173 32
        foreach ($indexes as $name => $index) {
174 32
            $result[] = new IndexConstraint([
175 32
                'isPrimary' => (bool) $index[0]['index_is_primary'],
176 32
                'isUnique' => (bool) $index[0]['index_is_unique'],
177 32
                'name' => $name !== 'PRIMARY' ? $name : null,
178 32
                'columnNames' => ArrayHelper::getColumn($index, 'column_name'),
179
            ]);
180
        }
181
182 32
        return $result;
183
    }
184
185
    /**
186
     * {@inheritdoc}
187
     */
188 13
    protected function loadTableUniques($tableName)
189
    {
190 13
        return $this->loadTableConstraints($tableName, 'uniques');
191
    }
192
193
    /**
194
     * {@inheritdoc}
195
     * @throws NotSupportedException if this method is called.
196
     */
197 12
    protected function loadTableChecks($tableName)
0 ignored issues
show
Unused Code introduced by
The parameter $tableName is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
198
    {
199 12
        throw new NotSupportedException('MySQL does not support check constraints.');
200
    }
201
202
    /**
203
     * {@inheritdoc}
204
     * @throws NotSupportedException if this method is called.
205
     */
206 12
    protected function loadTableDefaultValues($tableName)
0 ignored issues
show
Unused Code introduced by
The parameter $tableName is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
207
    {
208 12
        throw new NotSupportedException('MySQL does not support default value constraints.');
209
    }
210
211
    /**
212
     * Creates a query builder for the MySQL database.
213
     * @return QueryBuilder query builder instance
214
     */
215 338
    public function createQueryBuilder()
216
    {
217 338
        return new QueryBuilder($this->db);
218
    }
219
220
    /**
221
     * Resolves the table name and schema name (if any).
222
     * @param TableSchema $table the table metadata object
223
     * @param string $name the table name
224
     */
225 344
    protected function resolveTableNames($table, $name)
226
    {
227 344
        $parts = explode('.', str_replace('`', '', $name));
228 344
        if (isset($parts[1])) {
229
            $table->schemaName = $parts[0];
230
            $table->name = $parts[1];
231
            $table->fullName = $table->schemaName . '.' . $table->name;
232
        } else {
233 344
            $table->fullName = $table->name = $parts[0];
234
        }
235 344
    }
236
237
    /**
238
     * Loads the column information into a [[ColumnSchema]] object.
239
     * @param array $info column information
240
     * @return ColumnSchema the column schema object
241
     */
242 339
    protected function loadColumnSchema($info)
243
    {
244 339
        $column = $this->createColumnSchema();
245
246 339
        $column->name = $info['field'];
247 339
        $column->allowNull = $info['null'] === 'YES';
248 339
        $column->isPrimaryKey = strpos($info['key'], 'PRI') !== false;
249 339
        $column->autoIncrement = stripos($info['extra'], 'auto_increment') !== false;
250 339
        $column->comment = $info['comment'];
251
252 339
        $column->dbType = $info['type'];
253 339
        $column->unsigned = stripos($column->dbType, 'unsigned') !== false;
254
255 339
        $column->type = self::TYPE_STRING;
256 339
        if (preg_match('/^(\w+)(?:\(([^\)]+)\))?/', $column->dbType, $matches)) {
257 339
            $type = strtolower($matches[1]);
258 339
            if (isset($this->typeMap[$type])) {
259 339
                $column->type = $this->typeMap[$type];
260
            }
261 339
            if (!empty($matches[2])) {
262 339
                if ($type === 'enum') {
263 25
                    preg_match_all("/'[^']*'/", $matches[2], $values);
264 25
                    foreach ($values[0] as $i => $value) {
265 25
                        $values[$i] = trim($value, "'");
266
                    }
267 25
                    $column->enumValues = $values;
0 ignored issues
show
Documentation Bug introduced by
It seems like $values can be null. However, the property $enumValues is declared as array. Maybe change the type of the property to array|null or add a type check?

Our type inference engine has found an assignment of a scalar value (like a string, an integer or null) to a property which is an array.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property.

To type hint that a parameter can be either an array or null, you can set a type hint of array and a default value of null. The PHP interpreter will then accept both an array or null for that parameter.

function aContainsB(array $needle = null, array  $haystack) {
    if (!$needle) {
        return false;
    }

    return array_intersect($haystack, $needle) == $haystack;
}

The function can be called with either null or an array for the parameter $needle but will only accept an array as $haystack.

Loading history...
268
                } else {
269 339
                    $values = explode(',', $matches[2]);
270 339
                    $column->size = $column->precision = (int) $values[0];
271 339
                    if (isset($values[1])) {
272 86
                        $column->scale = (int) $values[1];
273
                    }
274 339
                    if ($column->size === 1 && $type === 'bit') {
275 5
                        $column->type = 'boolean';
276 339
                    } elseif ($type === 'bit') {
277 25
                        if ($column->size > 32) {
278
                            $column->type = 'bigint';
279 25
                        } elseif ($column->size === 32) {
280
                            $column->type = 'integer';
281
                        }
282
                    }
283
                }
284
            }
285
        }
286
287 339
        $column->phpType = $this->getColumnPhpType($column);
288
289 339
        if (!$column->isPrimaryKey) {
290 335
            if (($column->type === 'timestamp' || $column->type ==='datetime') && $info['default'] === 'CURRENT_TIMESTAMP') {
291 27
                $column->defaultValue = new Expression('CURRENT_TIMESTAMP');
292 334
            } elseif (isset($type) && $type === 'bit') {
293 26
                $column->defaultValue = bindec(trim($info['default'], 'b\''));
294
            } else {
295 333
                $column->defaultValue = $column->phpTypecast($info['default']);
296
            }
297
        }
298
299 339
        return $column;
300
    }
301
302
    /**
303
     * Collects the metadata of table columns.
304
     * @param TableSchema $table the table metadata
305
     * @return bool whether the table exists in the database
306
     * @throws \Exception if DB query fails
307
     */
308 344
    protected function findColumns($table)
309
    {
310 344
        $sql = 'SHOW FULL COLUMNS FROM ' . $this->quoteTableName($table->fullName);
311
        try {
312 344
            $columns = $this->db->createCommand($sql)->queryAll();
313 15
        } catch (\Exception $e) {
314 15
            $previous = $e->getPrevious();
315 15
            if ($previous instanceof \PDOException && strpos($previous->getMessage(), 'SQLSTATE[42S02') !== false) {
316
                // table does not exist
317
                // https://dev.mysql.com/doc/refman/5.5/en/error-messages-server.html#error_er_bad_table_error
318 15
                return false;
319
            }
320
            throw $e;
321
        }
322 339
        foreach ($columns as $info) {
323 339
            if ($this->db->slavePdo->getAttribute(\PDO::ATTR_CASE) !== \PDO::CASE_LOWER) {
324 338
                $info = array_change_key_case($info, CASE_LOWER);
325
            }
326 339
            $column = $this->loadColumnSchema($info);
327 339
            $table->columns[$column->name] = $column;
328 339
            if ($column->isPrimaryKey) {
329 313
                $table->primaryKey[] = $column->name;
330 313
                if ($column->autoIncrement) {
331 339
                    $table->sequenceName = '';
332
                }
333
            }
334
        }
335
336 339
        return true;
337
    }
338
339
    /**
340
     * Gets the CREATE TABLE sql string.
341
     * @param TableSchema $table the table metadata
342
     * @return string $sql the result of 'SHOW CREATE TABLE'
343
     */
344 1
    protected function getCreateTableSql($table)
345
    {
346 1
        $row = $this->db->createCommand('SHOW CREATE TABLE ' . $this->quoteTableName($table->fullName))->queryOne();
347 1
        if (isset($row['Create Table'])) {
348 1
            $sql = $row['Create Table'];
349
        } else {
350
            $row = array_values($row);
351
            $sql = $row[1];
352
        }
353
354 1
        return $sql;
355
    }
356
357
    /**
358
     * Collects the foreign key column details for the given table.
359
     * @param TableSchema $table the table metadata
360
     * @throws \Exception
361
     */
362 339
    protected function findConstraints($table)
363
    {
364
        $sql = <<<'SQL'
365 339
SELECT
366
    kcu.constraint_name,
367
    kcu.column_name,
368
    kcu.referenced_table_name,
369
    kcu.referenced_column_name
370
FROM information_schema.referential_constraints AS rc
371
JOIN information_schema.key_column_usage AS kcu ON
372
    (
373
        kcu.constraint_catalog = rc.constraint_catalog OR
374
        (kcu.constraint_catalog IS NULL AND rc.constraint_catalog IS NULL)
375
    ) AND
376
    kcu.constraint_schema = rc.constraint_schema AND
377
    kcu.constraint_name = rc.constraint_name
378
WHERE rc.constraint_schema = database() AND kcu.table_schema = database()
379
AND rc.table_name = :tableName AND kcu.table_name = :tableName1
380
SQL;
381
382
        try {
383 339
            $rows = $this->db->createCommand($sql, [':tableName' => $table->name, ':tableName1' => $table->name])->queryAll();
384 339
            $constraints = [];
385
386 339
            foreach ($rows as $row) {
387 223
                $constraints[$row['constraint_name']]['referenced_table_name'] = $row['referenced_table_name'];
388 223
                $constraints[$row['constraint_name']]['columns'][$row['column_name']] = $row['referenced_column_name'];
389
            }
390
391 339
            $table->foreignKeys = [];
392 339
            foreach ($constraints as $name => $constraint) {
393 223
                $table->foreignKeys[$name] = array_merge(
394 223
                    [$constraint['referenced_table_name']],
395 339
                    $constraint['columns']
396
                );
397
            }
398
        } catch (\Exception $e) {
399
            $previous = $e->getPrevious();
400
            if (!$previous instanceof \PDOException || strpos($previous->getMessage(), 'SQLSTATE[42S02') === false) {
401
                throw $e;
402
            }
403
404
            // table does not exist, try to determine the foreign keys using the table creation sql
405
            $sql = $this->getCreateTableSql($table);
406
            $regexp = '/FOREIGN KEY\s+\(([^\)]+)\)\s+REFERENCES\s+([^\(^\s]+)\s*\(([^\)]+)\)/mi';
407
            if (preg_match_all($regexp, $sql, $matches, PREG_SET_ORDER)) {
408
                foreach ($matches as $match) {
409
                    $fks = array_map('trim', explode(',', str_replace('`', '', $match[1])));
410
                    $pks = array_map('trim', explode(',', str_replace('`', '', $match[3])));
411
                    $constraint = [str_replace('`', '', $match[2])];
412
                    foreach ($fks as $k => $name) {
413
                        $constraint[$name] = $pks[$k];
414
                    }
415
                    $table->foreignKeys[md5(serialize($constraint))] = $constraint;
416
                }
417
                $table->foreignKeys = array_values($table->foreignKeys);
418
            }
419
        }
420 339
    }
421
422
    /**
423
     * Returns all unique indexes for the given table.
424
     *
425
     * Each array element is of the following structure:
426
     *
427
     * ```php
428
     * [
429
     *     'IndexName1' => ['col1' [, ...]],
430
     *     'IndexName2' => ['col2' [, ...]],
431
     * ]
432
     * ```
433
     *
434
     * @param TableSchema $table the table metadata
435
     * @return array all unique indexes for the given table.
436
     */
437 1
    public function findUniqueIndexes($table)
438
    {
439 1
        $sql = $this->getCreateTableSql($table);
440 1
        $uniqueIndexes = [];
441
442 1
        $regexp = '/UNIQUE KEY\s+\`(.+)\`\s*\((\`.+\`)+\)/mi';
443 1
        if (preg_match_all($regexp, $sql, $matches, PREG_SET_ORDER)) {
444 1
            foreach ($matches as $match) {
445 1
                $indexName = $match[1];
446 1
                $indexColumns = array_map('trim', explode('`,`', trim($match[2], '`')));
447 1
                $uniqueIndexes[$indexName] = $indexColumns;
448
            }
449
        }
450
451 1
        return $uniqueIndexes;
452
    }
453
454
    /**
455
     * {@inheritdoc}
456
     */
457 16
    public function createColumnSchemaBuilder($type, $length = null)
458
    {
459 16
        return new ColumnSchemaBuilder($type, $length, $this->db);
460
    }
461
462
    /**
463
     * @return bool whether the version of the MySQL being used is older than 5.1.
464
     * @throws InvalidConfigException
465
     * @throws Exception
466
     * @since 2.0.13
467
     */
468
    protected function isOldMysql()
469
    {
470
        if ($this->_oldMysql === null) {
471
            $version = $this->db->getSlavePdo()->getAttribute(\PDO::ATTR_SERVER_VERSION);
472
            $this->_oldMysql = version_compare($version, '5.1', '<=');
473
        }
474
475
        return $this->_oldMysql;
476
    }
477
478
    /**
479
     * Loads multiple types of constraints and returns the specified ones.
480
     * @param string $tableName table name.
481
     * @param string $returnType return type:
482
     * - primaryKey
483
     * - foreignKeys
484
     * - uniques
485
     * @return mixed constraints.
486
     */
487 52
    private function loadTableConstraints($tableName, $returnType)
488
    {
489 52
        static $sql = <<<'SQL'
490
SELECT
491
    `kcu`.`CONSTRAINT_NAME` AS `name`,
492
    `kcu`.`COLUMN_NAME` AS `column_name`,
493
    `tc`.`CONSTRAINT_TYPE` AS `type`,
494
    CASE
495
        WHEN :schemaName IS NULL AND `kcu`.`REFERENCED_TABLE_SCHEMA` = DATABASE() THEN NULL
496
        ELSE `kcu`.`REFERENCED_TABLE_SCHEMA`
497
    END AS `foreign_table_schema`,
498
    `kcu`.`REFERENCED_TABLE_NAME` AS `foreign_table_name`,
499
    `kcu`.`REFERENCED_COLUMN_NAME` AS `foreign_column_name`,
500
    `rc`.`UPDATE_RULE` AS `on_update`,
501
    `rc`.`DELETE_RULE` AS `on_delete`,
502
    `kcu`.`ORDINAL_POSITION` AS `position`
503
FROM
504
    `information_schema`.`KEY_COLUMN_USAGE` AS `kcu`,
505
    `information_schema`.`REFERENTIAL_CONSTRAINTS` AS `rc`,
506
    `information_schema`.`TABLE_CONSTRAINTS` AS `tc`
507
WHERE
508
    `kcu`.`TABLE_SCHEMA` = COALESCE(:schemaName, DATABASE()) AND `kcu`.`CONSTRAINT_SCHEMA` = `kcu`.`TABLE_SCHEMA` AND `kcu`.`TABLE_NAME` = :tableName
509
    AND `rc`.`CONSTRAINT_SCHEMA` = `kcu`.`TABLE_SCHEMA` AND `rc`.`TABLE_NAME` = :tableName AND `rc`.`CONSTRAINT_NAME` = `kcu`.`CONSTRAINT_NAME`
510
    AND `tc`.`TABLE_SCHEMA` = `kcu`.`TABLE_SCHEMA` AND `tc`.`TABLE_NAME` = :tableName AND `tc`.`CONSTRAINT_NAME` = `kcu`.`CONSTRAINT_NAME` AND `tc`.`CONSTRAINT_TYPE` = 'FOREIGN KEY'
511
UNION
512
SELECT
513
    `kcu`.`CONSTRAINT_NAME` AS `name`,
514
    `kcu`.`COLUMN_NAME` AS `column_name`,
515
    `tc`.`CONSTRAINT_TYPE` AS `type`,
516
    NULL AS `foreign_table_schema`,
517
    NULL AS `foreign_table_name`,
518
    NULL AS `foreign_column_name`,
519
    NULL AS `on_update`,
520
    NULL AS `on_delete`,
521
    `kcu`.`ORDINAL_POSITION` AS `position`
522
FROM
523
    `information_schema`.`KEY_COLUMN_USAGE` AS `kcu`,
524
    `information_schema`.`TABLE_CONSTRAINTS` AS `tc`
525
WHERE
526
    `kcu`.`TABLE_SCHEMA` = COALESCE(:schemaName, DATABASE()) AND `kcu`.`TABLE_NAME` = :tableName
527
    AND `tc`.`TABLE_SCHEMA` = `kcu`.`TABLE_SCHEMA` AND `tc`.`TABLE_NAME` = :tableName AND `tc`.`CONSTRAINT_NAME` = `kcu`.`CONSTRAINT_NAME` AND `tc`.`CONSTRAINT_TYPE` IN ('PRIMARY KEY', 'UNIQUE')
528
ORDER BY `position` ASC
529
SQL;
530
531 52
        $resolvedName = $this->resolveTableName($tableName);
532 52
        $constraints = $this->db->createCommand($sql, [
533 52
            ':schemaName' => $resolvedName->schemaName,
534 52
            ':tableName' => $resolvedName->name,
535 52
        ])->queryAll();
536 52
        $constraints = $this->normalizePdoRowKeyCase($constraints, true);
537 52
        $constraints = ArrayHelper::index($constraints, null, ['type', 'name']);
538
        $result = [
539 52
            'primaryKey' => null,
540
            'foreignKeys' => [],
541
            'uniques' => [],
542
        ];
543 52
        foreach ($constraints as $type => $names) {
544 52
            foreach ($names as $name => $constraint) {
545
                switch ($type) {
546 52
                    case 'PRIMARY KEY':
547 41
                        $result['primaryKey'] = new Constraint([
548 41
                            'columnNames' => ArrayHelper::getColumn($constraint, 'column_name'),
549
                        ]);
550 41
                        break;
551 46
                    case 'FOREIGN KEY':
552 10
                        $result['foreignKeys'][] = new ForeignKeyConstraint([
553 10
                            'name' => $name,
554 10
                            'columnNames' => ArrayHelper::getColumn($constraint, 'column_name'),
555 10
                            'foreignSchemaName' => $constraint[0]['foreign_table_schema'],
556 10
                            'foreignTableName' => $constraint[0]['foreign_table_name'],
557 10
                            'foreignColumnNames' => ArrayHelper::getColumn($constraint, 'foreign_column_name'),
558 10
                            'onDelete' => $constraint[0]['on_delete'],
559 10
                            'onUpdate' => $constraint[0]['on_update'],
560
                        ]);
561 10
                        break;
562 37
                    case 'UNIQUE':
563 37
                        $result['uniques'][] = new Constraint([
564 37
                            'name' => $name,
565 37
                            'columnNames' => ArrayHelper::getColumn($constraint, 'column_name'),
566
                        ]);
567 52
                        break;
568
                }
569
            }
570
        }
571 52
        foreach ($result as $type => $data) {
572 52
            $this->setTableMetadata($tableName, $type, $data);
573
        }
574
575 52
        return $result[$returnType];
576
    }
577
}
578