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

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

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