Schema::loadColumnSchema()   F
last analyzed

Complexity

Conditions 24
Paths 196

Size

Total Lines 65
Code Lines 45

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 600

Importance

Changes 0
Metric Value
cc 24
eloc 45
nc 196
nop 1
dl 0
loc 65
ccs 0
cts 43
cp 0
crap 600
rs 3.3666
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * @link https://www.yiiframework.com/
4
 * @copyright Copyright (c) 2008 Yii Software LLC
5
 * @license https://www.yiiframework.com/license/
6
 */
7
8
namespace yii\db\cubrid;
9
10
use Yii;
11
use yii\base\NotSupportedException;
12
use yii\db\Constraint;
13
use yii\db\ConstraintFinderInterface;
14
use yii\db\ConstraintFinderTrait;
15
use yii\db\Expression;
16
use yii\db\ForeignKeyConstraint;
17
use yii\db\IndexConstraint;
18
use yii\db\TableSchema;
19
use yii\db\Transaction;
20
use yii\helpers\ArrayHelper;
21
22
/**
23
 * Schema is the class for retrieving metadata from a CUBRID database (version 9.3.x and higher).
24
 *
25
 * @author Carsten Brandt <[email protected]>
26
 * @since 2.0
27
 */
28
class Schema extends \yii\db\Schema implements ConstraintFinderInterface
29
{
30
    use ConstraintFinderTrait;
31
32
    /**
33
     * @var array mapping from physical column types (keys) to abstract column types (values)
34
     * Please refer to [CUBRID manual](https://www.cubrid.org/manual/en/9.3.0/sql/datatype.html) for
35
     * details on data types.
36
     */
37
    public $typeMap = [
38
        // Numeric data types
39
        'short' => self::TYPE_SMALLINT,
40
        'smallint' => self::TYPE_SMALLINT,
41
        'int' => self::TYPE_INTEGER,
42
        'integer' => self::TYPE_INTEGER,
43
        'bigint' => self::TYPE_BIGINT,
44
        'numeric' => self::TYPE_DECIMAL,
45
        'decimal' => self::TYPE_DECIMAL,
46
        'float' => self::TYPE_FLOAT,
47
        'real' => self::TYPE_FLOAT,
48
        'double' => self::TYPE_DOUBLE,
49
        'double precision' => self::TYPE_DOUBLE,
50
        'monetary' => self::TYPE_MONEY,
51
        // Date/Time data types
52
        'date' => self::TYPE_DATE,
53
        'time' => self::TYPE_TIME,
54
        'timestamp' => self::TYPE_TIMESTAMP,
55
        'datetime' => self::TYPE_DATETIME,
56
        // String data types
57
        'char' => self::TYPE_CHAR,
58
        'varchar' => self::TYPE_STRING,
59
        'char varying' => self::TYPE_STRING,
60
        'nchar' => self::TYPE_CHAR,
61
        'nchar varying' => self::TYPE_STRING,
62
        'string' => self::TYPE_STRING,
63
        // BLOB/CLOB data types
64
        'blob' => self::TYPE_BINARY,
65
        'clob' => self::TYPE_BINARY,
66
        // Bit string data types
67
        'bit' => self::TYPE_INTEGER,
68
        'bit varying' => self::TYPE_INTEGER,
69
        // Collection data types (considered strings for now)
70
        'set' => self::TYPE_STRING,
71
        'multiset' => self::TYPE_STRING,
72
        'list' => self::TYPE_STRING,
73
        'sequence' => self::TYPE_STRING,
74
        'enum' => self::TYPE_STRING,
75
    ];
76
    /**
77
     * @var array map of DB errors and corresponding exceptions
78
     * If left part is found in DB error message exception class from the right part is used.
79
     */
80
    public $exceptionMap = [
81
        'Operation would have caused one or more unique constraint violations' => 'yii\db\IntegrityException',
82
    ];
83
84
    /**
85
     * {@inheritdoc}
86
     */
87
    protected $tableQuoteCharacter = '"';
88
89
90
    /**
91
     * {@inheritdoc}
92
     */
93
    protected function findTableNames($schema = '')
94
    {
95
        $pdo = $this->db->getSlavePdo(true);
96
        $tables = $pdo->cubrid_schema(\PDO::CUBRID_SCH_TABLE);
0 ignored issues
show
Bug introduced by
The method cubrid_schema() does not exist on PDO. ( Ignorable by Annotation )

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

96
        /** @scrutinizer ignore-call */ 
97
        $tables = $pdo->cubrid_schema(\PDO::CUBRID_SCH_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...
Bug introduced by
The constant PDO::CUBRID_SCH_TABLE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
97
        $tableNames = [];
98
        foreach ($tables as $table) {
99
            // do not list system tables
100
            if ($table['TYPE'] != 0) {
101
                $tableNames[] = $table['NAME'];
102
            }
103
        }
104
105
        return $tableNames;
106
    }
107
108
    /**
109
     * {@inheritdoc}
110
     */
111
    protected function loadTableSchema($name)
112
    {
113
        $pdo = $this->db->getSlavePdo(true);
114
115
        $tableInfo = $pdo->cubrid_schema(\PDO::CUBRID_SCH_TABLE, $name);
0 ignored issues
show
Bug introduced by
The constant PDO::CUBRID_SCH_TABLE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
116
117
        if (!isset($tableInfo[0]['NAME'])) {
118
            return null;
119
        }
120
121
        $table = new TableSchema();
122
        $table->fullName = $table->name = $tableInfo[0]['NAME'];
123
124
        $sql = 'SHOW FULL COLUMNS FROM ' . $this->quoteSimpleTableName($table->name);
125
        $columns = $this->db->createCommand($sql)->queryAll();
126
127
        foreach ($columns as $info) {
128
            $column = $this->loadColumnSchema($info);
129
            $table->columns[$column->name] = $column;
130
        }
131
132
        $primaryKeys = $pdo->cubrid_schema(\PDO::CUBRID_SCH_PRIMARY_KEY, $table->name);
0 ignored issues
show
Bug introduced by
The constant PDO::CUBRID_SCH_PRIMARY_KEY was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
133
        foreach ($primaryKeys as $key) {
134
            $column = $table->columns[$key['ATTR_NAME']];
135
            $column->isPrimaryKey = true;
136
            $table->primaryKey[] = $column->name;
137
            if ($column->autoIncrement) {
138
                $table->sequenceName = '';
139
            }
140
        }
141
142
        $foreignKeys = $pdo->cubrid_schema(\PDO::CUBRID_SCH_IMPORTED_KEYS, $table->name);
0 ignored issues
show
Bug introduced by
The constant PDO::CUBRID_SCH_IMPORTED_KEYS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
143
        foreach ($foreignKeys as $key) {
144
            if (isset($table->foreignKeys[$key['FK_NAME']])) {
145
                $table->foreignKeys[$key['FK_NAME']][$key['FKCOLUMN_NAME']] = $key['PKCOLUMN_NAME'];
146
            } else {
147
                $table->foreignKeys[$key['FK_NAME']] = [
148
                    $key['PKTABLE_NAME'],
149
                    $key['FKCOLUMN_NAME'] => $key['PKCOLUMN_NAME'],
150
                ];
151
            }
152
        }
153
154
        return $table;
155
    }
156
157
    /**
158
     * {@inheritdoc}
159
     */
160
    protected function loadTablePrimaryKey($tableName)
161
    {
162
        $primaryKey = $this->db->getSlavePdo(true)->cubrid_schema(\PDO::CUBRID_SCH_PRIMARY_KEY, $tableName);
0 ignored issues
show
Bug introduced by
The constant PDO::CUBRID_SCH_PRIMARY_KEY was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
163
        if (empty($primaryKey)) {
164
            return null;
165
        }
166
167
        ArrayHelper::multisort($primaryKey, 'KEY_SEQ', SORT_ASC, SORT_NUMERIC);
168
        return new Constraint([
169
            'name' => $primaryKey[0]['KEY_NAME'],
170
            'columnNames' => ArrayHelper::getColumn($primaryKey, 'ATTR_NAME'),
171
        ]);
172
    }
173
174
    /**
175
     * {@inheritdoc}
176
     */
177
    protected function loadTableForeignKeys($tableName)
178
    {
179
        static $actionTypes = [
180
            0 => 'CASCADE',
181
            1 => 'RESTRICT',
182
            2 => 'NO ACTION',
183
            3 => 'SET NULL',
184
        ];
185
186
        $foreignKeys = $this->db->getSlavePdo(true)->cubrid_schema(\PDO::CUBRID_SCH_IMPORTED_KEYS, $tableName);
0 ignored issues
show
Bug introduced by
The constant PDO::CUBRID_SCH_IMPORTED_KEYS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
187
        $foreignKeys = ArrayHelper::index($foreignKeys, null, 'FK_NAME');
188
        ArrayHelper::multisort($foreignKeys, 'KEY_SEQ', SORT_ASC, SORT_NUMERIC);
189
        $result = [];
190
        foreach ($foreignKeys as $name => $foreignKey) {
191
            $result[] = new ForeignKeyConstraint([
192
                'name' => $name,
193
                'columnNames' => ArrayHelper::getColumn($foreignKey, 'FKCOLUMN_NAME'),
194
                'foreignTableName' => $foreignKey[0]['PKTABLE_NAME'],
195
                'foreignColumnNames' => ArrayHelper::getColumn($foreignKey, 'PKCOLUMN_NAME'),
196
                'onDelete' => isset($actionTypes[$foreignKey[0]['DELETE_RULE']]) ? $actionTypes[$foreignKey[0]['DELETE_RULE']] : null,
197
                'onUpdate' => isset($actionTypes[$foreignKey[0]['UPDATE_RULE']]) ? $actionTypes[$foreignKey[0]['UPDATE_RULE']] : null,
198
            ]);
199
        }
200
201
        return $result;
202
    }
203
204
    /**
205
     * {@inheritdoc}
206
     */
207
    protected function loadTableIndexes($tableName)
208
    {
209
        return $this->loadTableConstraints($tableName, 'indexes');
210
    }
211
212
    /**
213
     * {@inheritdoc}
214
     */
215
    protected function loadTableUniques($tableName)
216
    {
217
        return $this->loadTableConstraints($tableName, 'uniques');
218
    }
219
220
    /**
221
     * {@inheritdoc}
222
     * @throws NotSupportedException if this method is called.
223
     */
224
    protected function loadTableChecks($tableName)
225
    {
226
        throw new NotSupportedException('CUBRID does not support check constraints.');
227
    }
228
229
    /**
230
     * {@inheritdoc}
231
     * @throws NotSupportedException if this method is called.
232
     */
233
    protected function loadTableDefaultValues($tableName)
234
    {
235
        throw new NotSupportedException('CUBRID does not support default value constraints.');
236
    }
237
238
    /**
239
     * {@inheritdoc}
240
     */
241
    public function releaseSavepoint($name)
242
    {
243
        // does nothing as cubrid does not support this
244
    }
245
246
    /**
247
     * Creates a query builder for the CUBRID database.
248
     * @return QueryBuilder query builder instance
249
     */
250
    public function createQueryBuilder()
251
    {
252
        return Yii::createObject(QueryBuilder::className(), [$this->db]);
0 ignored issues
show
Deprecated Code introduced by
The function yii\base\BaseObject::className() has been deprecated: since 2.0.14. On PHP >=5.5, use `::class` instead. ( Ignorable by Annotation )

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

252
        return Yii::createObject(/** @scrutinizer ignore-deprecated */ QueryBuilder::className(), [$this->db]);

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

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

Loading history...
253
    }
254
255
    /**
256
     * Loads the column information into a [[ColumnSchema]] object.
257
     * @param array $info column information
258
     * @return \yii\db\ColumnSchema the column schema object
259
     */
260
    protected function loadColumnSchema($info)
261
    {
262
        $column = $this->createColumnSchema();
263
264
        $column->name = $info['Field'];
265
        $column->allowNull = $info['Null'] === 'YES';
266
        $column->isPrimaryKey = false; // primary key will be set by loadTableSchema() later
267
        $column->autoIncrement = stripos($info['Extra'], 'auto_increment') !== false;
268
269
        $column->dbType = $info['Type'];
270
        $column->unsigned = strpos($column->dbType, 'unsigned') !== false;
271
272
        $column->type = self::TYPE_STRING;
273
        if (preg_match('/^([\w ]+)(?:\(([^\)]+)\))?$/', $column->dbType, $matches)) {
274
            $type = strtolower($matches[1]);
275
            $column->dbType = $type . (isset($matches[2]) ? "({$matches[2]})" : '');
276
            if (isset($this->typeMap[$type])) {
277
                $column->type = $this->typeMap[$type];
278
            }
279
            if (!empty($matches[2])) {
280
                if ($type === 'enum') {
281
                    $values = preg_split('/\s*,\s*/', $matches[2]);
282
                    foreach ($values as $i => $value) {
283
                        $values[$i] = trim($value, "'");
284
                    }
285
                    $column->enumValues = $values;
286
                } else {
287
                    $values = explode(',', $matches[2]);
288
                    $column->size = $column->precision = (int) $values[0];
289
                    if (isset($values[1])) {
290
                        $column->scale = (int) $values[1];
291
                    }
292
                    if ($column->size === 1 && $type === 'bit') {
293
                        $column->type = 'boolean';
294
                    } elseif ($type === 'bit') {
295
                        if ($column->size > 32) {
296
                            $column->type = 'bigint';
297
                        } elseif ($column->size === 32) {
298
                            $column->type = 'integer';
299
                        }
300
                    }
301
                }
302
            }
303
        }
304
305
        $column->phpType = $this->getColumnPhpType($column);
306
307
        if ($column->isPrimaryKey) {
308
            return $column;
309
        }
310
311
        if (
312
            $column->type === 'timestamp' && $info['Default'] === 'SYS_TIMESTAMP' ||
313
            $column->type === 'datetime' && $info['Default'] === 'SYS_DATETIME' ||
314
            $column->type === 'date' && $info['Default'] === 'SYS_DATE' ||
315
            $column->type === 'time' && $info['Default'] === 'SYS_TIME'
316
        ) {
317
            $column->defaultValue = new Expression($info['Default']);
318
        } elseif (isset($type) && $type === 'bit') {
319
            $column->defaultValue = hexdec(trim($info['Default'], 'X\''));
320
        } else {
321
            $column->defaultValue = $column->phpTypecast($info['Default']);
322
        }
323
324
        return $column;
325
    }
326
327
    /**
328
     * Determines the PDO type for the given PHP data value.
329
     * @param mixed $data the data whose PDO type is to be determined
330
     * @return int the PDO type
331
     * @see https://www.php.net/manual/en/pdo.constants.php
332
     */
333
    public function getPdoType($data)
334
    {
335
        static $typeMap = [
336
            // php type => PDO type
337
            'boolean' => \PDO::PARAM_INT, // PARAM_BOOL is not supported by CUBRID PDO
338
            'integer' => \PDO::PARAM_INT,
339
            'string' => \PDO::PARAM_STR,
340
            'resource' => \PDO::PARAM_LOB,
341
            'NULL' => \PDO::PARAM_NULL,
342
        ];
343
        $type = gettype($data);
344
345
        return isset($typeMap[$type]) ? $typeMap[$type] : \PDO::PARAM_STR;
346
    }
347
348
    /**
349
     * {@inheritdoc}
350
     * @see https://www.cubrid.org/manual/en/9.3.0/sql/transaction.html#database-concurrency
351
     */
352
    public function setTransactionIsolationLevel($level)
353
    {
354
        // translate SQL92 levels to CUBRID levels:
355
        switch ($level) {
356
            case Transaction::SERIALIZABLE:
357
                $level = '6'; // SERIALIZABLE
358
                break;
359
            case Transaction::REPEATABLE_READ:
360
                $level = '5'; // REPEATABLE READ CLASS with REPEATABLE READ INSTANCES
361
                break;
362
            case Transaction::READ_COMMITTED:
363
                $level = '4'; // REPEATABLE READ CLASS with READ COMMITTED INSTANCES
364
                break;
365
            case Transaction::READ_UNCOMMITTED:
366
                $level = '3'; // REPEATABLE READ CLASS with READ UNCOMMITTED INSTANCES
367
                break;
368
        }
369
        parent::setTransactionIsolationLevel($level);
370
    }
371
372
    /**
373
     * {@inheritdoc}
374
     */
375
    public function createColumnSchemaBuilder($type, $length = null)
376
    {
377
        return new ColumnSchemaBuilder($type, $length, $this->db);
378
    }
379
380
    /**
381
     * Loads multiple types of constraints and returns the specified ones.
382
     * @param string $tableName table name.
383
     * @param string $returnType return type:
384
     * - indexes
385
     * - uniques
386
     * @return mixed constraints.
387
     */
388
    private function loadTableConstraints($tableName, $returnType)
389
    {
390
        $constraints = $this->db->getSlavePdo(true)->cubrid_schema(\PDO::CUBRID_SCH_CONSTRAINT, $tableName);
0 ignored issues
show
Bug introduced by
The constant PDO::CUBRID_SCH_CONSTRAINT was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
391
        $constraints = ArrayHelper::index($constraints, null, ['TYPE', 'NAME']);
392
        ArrayHelper::multisort($constraints, 'KEY_ORDER', SORT_ASC, SORT_NUMERIC);
393
        $result = [
394
            'indexes' => [],
395
            'uniques' => [],
396
        ];
397
        foreach ($constraints as $type => $names) {
398
            foreach ($names as $name => $constraint) {
399
                $isUnique = in_array((int) $type, [0, 2], true);
400
                $result['indexes'][] = new IndexConstraint([
401
                    'isPrimary' => (bool) $constraint[0]['PRIMARY_KEY'],
402
                    'isUnique' => $isUnique,
403
                    'name' => $name,
404
                    'columnNames' => ArrayHelper::getColumn($constraint, 'ATTR_NAME'),
405
                ]);
406
                if ($isUnique) {
407
                    $result['uniques'][] = new Constraint([
408
                        'name' => $name,
409
                        'columnNames' => ArrayHelper::getColumn($constraint, 'ATTR_NAME'),
410
                    ]);
411
                }
412
            }
413
        }
414
        foreach ($result as $type => $data) {
415
            $this->setTableMetadata($tableName, $type, $data);
416
        }
417
418
        return $result[$returnType];
419
    }
420
}
421