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\cubrid; |
||||||
9 | |||||||
10 | use yii\base\NotSupportedException; |
||||||
11 | use yii\db\Constraint; |
||||||
12 | use yii\db\ConstraintFinderInterface; |
||||||
13 | use yii\db\ConstraintFinderTrait; |
||||||
14 | use yii\db\Expression; |
||||||
15 | use yii\db\ForeignKeyConstraint; |
||||||
16 | use yii\db\IndexConstraint; |
||||||
17 | use yii\db\TableSchema; |
||||||
18 | use yii\db\Transaction; |
||||||
19 | use yii\helpers\ArrayHelper; |
||||||
20 | |||||||
21 | /** |
||||||
22 | * Schema is the class for retrieving metadata from a CUBRID database (version 9.3.x and higher). |
||||||
23 | * |
||||||
24 | * @author Carsten Brandt <[email protected]> |
||||||
25 | * @since 2.0 |
||||||
26 | */ |
||||||
27 | class Schema extends \yii\db\Schema implements ConstraintFinderInterface |
||||||
28 | { |
||||||
29 | use ConstraintFinderTrait; |
||||||
30 | |||||||
31 | /** |
||||||
32 | * @var array mapping from physical column types (keys) to abstract column types (values) |
||||||
33 | * Please refer to [CUBRID manual](http://www.cubrid.org/manual/91/en/sql/datatype.html) for |
||||||
34 | * details on data types. |
||||||
35 | */ |
||||||
36 | public $typeMap = [ |
||||||
37 | // Numeric data types |
||||||
38 | 'short' => self::TYPE_SMALLINT, |
||||||
39 | 'smallint' => self::TYPE_SMALLINT, |
||||||
40 | 'int' => self::TYPE_INTEGER, |
||||||
41 | 'integer' => self::TYPE_INTEGER, |
||||||
42 | 'bigint' => self::TYPE_BIGINT, |
||||||
43 | 'numeric' => self::TYPE_DECIMAL, |
||||||
44 | 'decimal' => self::TYPE_DECIMAL, |
||||||
45 | 'float' => self::TYPE_FLOAT, |
||||||
46 | 'real' => self::TYPE_FLOAT, |
||||||
47 | 'double' => self::TYPE_DOUBLE, |
||||||
48 | 'double precision' => self::TYPE_DOUBLE, |
||||||
49 | 'monetary' => self::TYPE_MONEY, |
||||||
50 | // Date/Time data types |
||||||
51 | 'date' => self::TYPE_DATE, |
||||||
52 | 'time' => self::TYPE_TIME, |
||||||
53 | 'timestamp' => self::TYPE_TIMESTAMP, |
||||||
54 | 'datetime' => self::TYPE_DATETIME, |
||||||
55 | // String data types |
||||||
56 | 'char' => self::TYPE_CHAR, |
||||||
57 | 'varchar' => self::TYPE_STRING, |
||||||
58 | 'char varying' => self::TYPE_STRING, |
||||||
59 | 'nchar' => self::TYPE_CHAR, |
||||||
60 | 'nchar varying' => self::TYPE_STRING, |
||||||
61 | 'string' => self::TYPE_STRING, |
||||||
62 | // BLOB/CLOB data types |
||||||
63 | 'blob' => self::TYPE_BINARY, |
||||||
64 | 'clob' => self::TYPE_BINARY, |
||||||
65 | // Bit string data types |
||||||
66 | 'bit' => self::TYPE_INTEGER, |
||||||
67 | 'bit varying' => self::TYPE_INTEGER, |
||||||
68 | // Collection data types (considered strings for now) |
||||||
69 | 'set' => self::TYPE_STRING, |
||||||
70 | 'multiset' => self::TYPE_STRING, |
||||||
71 | 'list' => self::TYPE_STRING, |
||||||
72 | 'sequence' => self::TYPE_STRING, |
||||||
73 | 'enum' => self::TYPE_STRING, |
||||||
74 | ]; |
||||||
75 | /** |
||||||
76 | * @var array map of DB errors and corresponding exceptions |
||||||
77 | * If left part is found in DB error message exception class from the right part is used. |
||||||
78 | */ |
||||||
79 | public $exceptionMap = [ |
||||||
80 | 'Operation would have caused one or more unique constraint violations' => 'yii\db\IntegrityException', |
||||||
81 | ]; |
||||||
82 | |||||||
83 | /** |
||||||
84 | * {@inheritdoc} |
||||||
85 | */ |
||||||
86 | protected $tableQuoteCharacter = '"'; |
||||||
87 | |||||||
88 | |||||||
89 | /** |
||||||
90 | * {@inheritdoc} |
||||||
91 | */ |
||||||
92 | protected function findTableNames($schema = '') |
||||||
93 | { |
||||||
94 | $pdo = $this->db->getSlavePdo(); |
||||||
95 | $tables = $pdo->cubrid_schema(\PDO::CUBRID_SCH_TABLE); |
||||||
0 ignored issues
–
show
Bug
introduced
by
Loading history...
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
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...
|
|||||||
96 | $tableNames = []; |
||||||
97 | foreach ($tables as $table) { |
||||||
98 | // do not list system tables |
||||||
99 | if ($table['TYPE'] != 0) { |
||||||
100 | $tableNames[] = $table['NAME']; |
||||||
101 | } |
||||||
102 | } |
||||||
103 | |||||||
104 | return $tableNames; |
||||||
105 | } |
||||||
106 | |||||||
107 | /** |
||||||
108 | * {@inheritdoc} |
||||||
109 | */ |
||||||
110 | protected function loadTableSchema($name) |
||||||
111 | { |
||||||
112 | $pdo = $this->db->getSlavePdo(); |
||||||
113 | |||||||
114 | $tableInfo = $pdo->cubrid_schema(\PDO::CUBRID_SCH_TABLE, $name); |
||||||
115 | |||||||
116 | if (!isset($tableInfo[0]['NAME'])) { |
||||||
117 | return null; |
||||||
118 | } |
||||||
119 | |||||||
120 | $table = new TableSchema(); |
||||||
121 | $table->fullName = $table->name = $tableInfo[0]['NAME']; |
||||||
122 | |||||||
123 | $sql = 'SHOW FULL COLUMNS FROM ' . $this->quoteSimpleTableName($table->name); |
||||||
124 | $columns = $this->db->createCommand($sql)->queryAll(); |
||||||
125 | |||||||
126 | foreach ($columns as $info) { |
||||||
127 | $column = $this->loadColumnSchema($info); |
||||||
128 | $table->columns[$column->name] = $column; |
||||||
129 | } |
||||||
130 | |||||||
131 | $primaryKeys = $pdo->cubrid_schema(\PDO::CUBRID_SCH_PRIMARY_KEY, $table->name); |
||||||
132 | foreach ($primaryKeys as $key) { |
||||||
133 | $column = $table->columns[$key['ATTR_NAME']]; |
||||||
134 | $column->isPrimaryKey = true; |
||||||
135 | $table->primaryKey[] = $column->name; |
||||||
136 | if ($column->autoIncrement) { |
||||||
137 | $table->sequenceName = ''; |
||||||
138 | } |
||||||
139 | } |
||||||
140 | |||||||
141 | $foreignKeys = $pdo->cubrid_schema(\PDO::CUBRID_SCH_IMPORTED_KEYS, $table->name); |
||||||
142 | foreach ($foreignKeys as $key) { |
||||||
143 | if (isset($table->foreignKeys[$key['FK_NAME']])) { |
||||||
144 | $table->foreignKeys[$key['FK_NAME']][$key['FKCOLUMN_NAME']] = $key['PKCOLUMN_NAME']; |
||||||
145 | } else { |
||||||
146 | $table->foreignKeys[$key['FK_NAME']] = [ |
||||||
147 | $key['PKTABLE_NAME'], |
||||||
148 | $key['FKCOLUMN_NAME'] => $key['PKCOLUMN_NAME'], |
||||||
149 | ]; |
||||||
150 | } |
||||||
151 | } |
||||||
152 | |||||||
153 | return $table; |
||||||
154 | } |
||||||
155 | |||||||
156 | /** |
||||||
157 | * {@inheritdoc} |
||||||
158 | */ |
||||||
159 | protected function loadTablePrimaryKey($tableName) |
||||||
160 | { |
||||||
161 | $primaryKey = $this->db->getSlavePdo()->cubrid_schema(\PDO::CUBRID_SCH_PRIMARY_KEY, $tableName); |
||||||
0 ignored issues
–
show
|
|||||||
162 | if (empty($primaryKey)) { |
||||||
163 | return null; |
||||||
164 | } |
||||||
165 | |||||||
166 | ArrayHelper::multisort($primaryKey, 'KEY_SEQ', SORT_ASC, SORT_NUMERIC); |
||||||
167 | return new Constraint([ |
||||||
168 | 'name' => $primaryKey[0]['KEY_NAME'], |
||||||
169 | 'columnNames' => ArrayHelper::getColumn($primaryKey, 'ATTR_NAME'), |
||||||
170 | ]); |
||||||
171 | } |
||||||
172 | |||||||
173 | /** |
||||||
174 | * {@inheritdoc} |
||||||
175 | */ |
||||||
176 | protected function loadTableForeignKeys($tableName) |
||||||
177 | { |
||||||
178 | static $actionTypes = [ |
||||||
179 | 0 => 'CASCADE', |
||||||
180 | 1 => 'RESTRICT', |
||||||
181 | 2 => 'NO ACTION', |
||||||
182 | 3 => 'SET NULL', |
||||||
183 | ]; |
||||||
184 | |||||||
185 | $foreignKeys = $this->db->getSlavePdo()->cubrid_schema(\PDO::CUBRID_SCH_IMPORTED_KEYS, $tableName); |
||||||
0 ignored issues
–
show
|
|||||||
186 | $foreignKeys = ArrayHelper::index($foreignKeys, null, 'FK_NAME'); |
||||||
187 | ArrayHelper::multisort($foreignKeys, 'KEY_SEQ', SORT_ASC, SORT_NUMERIC); |
||||||
188 | $result = []; |
||||||
189 | foreach ($foreignKeys as $name => $foreignKey) { |
||||||
190 | $result[] = new ForeignKeyConstraint([ |
||||||
191 | 'name' => $name, |
||||||
192 | 'columnNames' => ArrayHelper::getColumn($foreignKey, 'FKCOLUMN_NAME'), |
||||||
193 | 'foreignTableName' => $foreignKey[0]['PKTABLE_NAME'], |
||||||
194 | 'foreignColumnNames' => ArrayHelper::getColumn($foreignKey, 'PKCOLUMN_NAME'), |
||||||
195 | 'onDelete' => isset($actionTypes[$foreignKey[0]['DELETE_RULE']]) ? $actionTypes[$foreignKey[0]['DELETE_RULE']] : null, |
||||||
196 | 'onUpdate' => isset($actionTypes[$foreignKey[0]['UPDATE_RULE']]) ? $actionTypes[$foreignKey[0]['UPDATE_RULE']] : null, |
||||||
197 | ]); |
||||||
198 | } |
||||||
199 | |||||||
200 | return $result; |
||||||
201 | } |
||||||
202 | |||||||
203 | /** |
||||||
204 | * {@inheritdoc} |
||||||
205 | */ |
||||||
206 | protected function loadTableIndexes($tableName) |
||||||
207 | { |
||||||
208 | return $this->loadTableConstraints($tableName, 'indexes'); |
||||||
209 | } |
||||||
210 | |||||||
211 | /** |
||||||
212 | * {@inheritdoc} |
||||||
213 | */ |
||||||
214 | protected function loadTableUniques($tableName) |
||||||
215 | { |
||||||
216 | return $this->loadTableConstraints($tableName, 'uniques'); |
||||||
217 | } |
||||||
218 | |||||||
219 | /** |
||||||
220 | * {@inheritdoc} |
||||||
221 | * @throws NotSupportedException if this method is called. |
||||||
222 | */ |
||||||
223 | protected function loadTableChecks($tableName) |
||||||
224 | { |
||||||
225 | throw new NotSupportedException('CUBRID does not support check constraints.'); |
||||||
226 | } |
||||||
227 | |||||||
228 | /** |
||||||
229 | * {@inheritdoc} |
||||||
230 | * @throws NotSupportedException if this method is called. |
||||||
231 | */ |
||||||
232 | protected function loadTableDefaultValues($tableName) |
||||||
233 | { |
||||||
234 | throw new NotSupportedException('CUBRID does not support default value constraints.'); |
||||||
235 | } |
||||||
236 | |||||||
237 | /** |
||||||
238 | * {@inheritdoc} |
||||||
239 | */ |
||||||
240 | public function releaseSavepoint($name) |
||||||
241 | { |
||||||
242 | // does nothing as cubrid does not support this |
||||||
243 | } |
||||||
244 | |||||||
245 | /** |
||||||
246 | * Creates a query builder for the CUBRID database. |
||||||
247 | * @return QueryBuilder query builder instance |
||||||
248 | */ |
||||||
249 | public function createQueryBuilder() |
||||||
250 | { |
||||||
251 | return new QueryBuilder($this->db); |
||||||
252 | } |
||||||
253 | |||||||
254 | /** |
||||||
255 | * Loads the column information into a [[ColumnSchema]] object. |
||||||
256 | * @param array $info column information |
||||||
257 | * @return \yii\db\ColumnSchema the column schema object |
||||||
258 | */ |
||||||
259 | protected function loadColumnSchema($info) |
||||||
260 | { |
||||||
261 | $column = $this->createColumnSchema(); |
||||||
262 | |||||||
263 | $column->name = $info['Field']; |
||||||
264 | $column->allowNull = $info['Null'] === 'YES'; |
||||||
265 | $column->isPrimaryKey = false; // primary key will be set by loadTableSchema() later |
||||||
266 | $column->autoIncrement = stripos($info['Extra'], 'auto_increment') !== false; |
||||||
267 | |||||||
268 | $column->dbType = $info['Type']; |
||||||
269 | $column->unsigned = strpos($column->dbType, 'unsigned') !== false; |
||||||
270 | |||||||
271 | $column->type = self::TYPE_STRING; |
||||||
272 | if (preg_match('/^([\w ]+)(?:\(([^\)]+)\))?$/', $column->dbType, $matches)) { |
||||||
273 | $type = strtolower($matches[1]); |
||||||
274 | $column->dbType = $type . (isset($matches[2]) ? "({$matches[2]})" : ''); |
||||||
275 | if (isset($this->typeMap[$type])) { |
||||||
276 | $column->type = $this->typeMap[$type]; |
||||||
277 | } |
||||||
278 | if (!empty($matches[2])) { |
||||||
279 | if ($type === 'enum') { |
||||||
280 | $values = preg_split('/\s*,\s*/', $matches[2]); |
||||||
281 | foreach ($values as $i => $value) { |
||||||
282 | $values[$i] = trim($value, "'"); |
||||||
283 | } |
||||||
284 | $column->enumValues = $values; |
||||||
285 | } else { |
||||||
286 | $values = explode(',', $matches[2]); |
||||||
287 | $column->size = $column->precision = (int) $values[0]; |
||||||
288 | if (isset($values[1])) { |
||||||
289 | $column->scale = (int) $values[1]; |
||||||
290 | } |
||||||
291 | if ($column->size === 1 && $type === 'bit') { |
||||||
292 | $column->type = 'boolean'; |
||||||
293 | } elseif ($type === 'bit') { |
||||||
294 | if ($column->size > 32) { |
||||||
295 | $column->type = 'bigint'; |
||||||
296 | } elseif ($column->size === 32) { |
||||||
297 | $column->type = 'integer'; |
||||||
298 | } |
||||||
299 | } |
||||||
300 | } |
||||||
301 | } |
||||||
302 | } |
||||||
303 | |||||||
304 | $column->phpType = $this->getColumnPhpType($column); |
||||||
305 | |||||||
306 | if ($column->isPrimaryKey) { |
||||||
307 | return $column; |
||||||
308 | } |
||||||
309 | |||||||
310 | if ($column->type === 'timestamp' && $info['Default'] === 'SYS_TIMESTAMP' || |
||||||
311 | $column->type === 'datetime' && $info['Default'] === 'SYS_DATETIME' || |
||||||
312 | $column->type === 'date' && $info['Default'] === 'SYS_DATE' || |
||||||
313 | $column->type === 'time' && $info['Default'] === 'SYS_TIME' |
||||||
314 | ) { |
||||||
315 | $column->defaultValue = new Expression($info['Default']); |
||||||
316 | } elseif (isset($type) && $type === 'bit') { |
||||||
317 | $column->defaultValue = hexdec(trim($info['Default'], 'X\'')); |
||||||
318 | } else { |
||||||
319 | $column->defaultValue = $column->phpTypecast($info['Default']); |
||||||
320 | } |
||||||
321 | |||||||
322 | return $column; |
||||||
323 | } |
||||||
324 | |||||||
325 | /** |
||||||
326 | * Determines the PDO type for the given PHP data value. |
||||||
327 | * @param mixed $data the data whose PDO type is to be determined |
||||||
328 | * @return int the PDO type |
||||||
329 | * @see https://secure.php.net/manual/en/pdo.constants.php |
||||||
330 | */ |
||||||
331 | public function getPdoType($data) |
||||||
332 | { |
||||||
333 | static $typeMap = [ |
||||||
334 | // php type => PDO type |
||||||
335 | 'boolean' => \PDO::PARAM_INT, // PARAM_BOOL is not supported by CUBRID PDO |
||||||
336 | 'integer' => \PDO::PARAM_INT, |
||||||
337 | 'string' => \PDO::PARAM_STR, |
||||||
338 | 'resource' => \PDO::PARAM_LOB, |
||||||
339 | 'NULL' => \PDO::PARAM_NULL, |
||||||
340 | ]; |
||||||
341 | $type = gettype($data); |
||||||
342 | |||||||
343 | return isset($typeMap[$type]) ? $typeMap[$type] : \PDO::PARAM_STR; |
||||||
344 | } |
||||||
345 | |||||||
346 | /** |
||||||
347 | * {@inheritdoc} |
||||||
348 | * @see http://www.cubrid.org/manual/91/en/sql/transaction.html#database-concurrency |
||||||
349 | */ |
||||||
350 | public function setTransactionIsolationLevel($level) |
||||||
351 | { |
||||||
352 | // translate SQL92 levels to CUBRID levels: |
||||||
353 | switch ($level) { |
||||||
354 | case Transaction::SERIALIZABLE: |
||||||
355 | $level = '6'; // SERIALIZABLE |
||||||
356 | break; |
||||||
357 | case Transaction::REPEATABLE_READ: |
||||||
358 | $level = '5'; // REPEATABLE READ CLASS with REPEATABLE READ INSTANCES |
||||||
359 | break; |
||||||
360 | case Transaction::READ_COMMITTED: |
||||||
361 | $level = '4'; // REPEATABLE READ CLASS with READ COMMITTED INSTANCES |
||||||
362 | break; |
||||||
363 | case Transaction::READ_UNCOMMITTED: |
||||||
364 | $level = '3'; // REPEATABLE READ CLASS with READ UNCOMMITTED INSTANCES |
||||||
365 | break; |
||||||
366 | } |
||||||
367 | parent::setTransactionIsolationLevel($level); |
||||||
368 | } |
||||||
369 | |||||||
370 | /** |
||||||
371 | * {@inheritdoc} |
||||||
372 | */ |
||||||
373 | public function createColumnSchemaBuilder($type, $length = null) |
||||||
374 | { |
||||||
375 | return new ColumnSchemaBuilder($type, $length, $this->db); |
||||||
376 | } |
||||||
377 | |||||||
378 | /** |
||||||
379 | * Loads multiple types of constraints and returns the specified ones. |
||||||
380 | * @param string $tableName table name. |
||||||
381 | * @param string $returnType return type: |
||||||
382 | * - indexes |
||||||
383 | * - uniques |
||||||
384 | * @return mixed constraints. |
||||||
385 | */ |
||||||
386 | private function loadTableConstraints($tableName, $returnType) |
||||||
387 | { |
||||||
388 | $constraints = $this->db->getSlavePdo()->cubrid_schema(\PDO::CUBRID_SCH_CONSTRAINT, $tableName); |
||||||
0 ignored issues
–
show
|
|||||||
389 | $constraints = ArrayHelper::index($constraints, null, ['TYPE', 'NAME']); |
||||||
390 | ArrayHelper::multisort($constraints, 'KEY_ORDER', SORT_ASC, SORT_NUMERIC); |
||||||
391 | $result = [ |
||||||
392 | 'indexes' => [], |
||||||
393 | 'uniques' => [], |
||||||
394 | ]; |
||||||
395 | foreach ($constraints as $type => $names) { |
||||||
396 | foreach ($names as $name => $constraint) { |
||||||
397 | $isUnique = in_array((int) $type, [0, 2], true); |
||||||
398 | $result['indexes'][] = new IndexConstraint([ |
||||||
399 | 'isPrimary' => (bool) $constraint[0]['PRIMARY_KEY'], |
||||||
400 | 'isUnique' => $isUnique, |
||||||
401 | 'name' => $name, |
||||||
402 | 'columnNames' => ArrayHelper::getColumn($constraint, 'ATTR_NAME'), |
||||||
403 | ]); |
||||||
404 | if ($isUnique) { |
||||||
405 | $result['uniques'][] = new Constraint([ |
||||||
406 | 'name' => $name, |
||||||
407 | 'columnNames' => ArrayHelper::getColumn($constraint, 'ATTR_NAME'), |
||||||
408 | ]); |
||||||
409 | } |
||||||
410 | } |
||||||
411 | } |
||||||
412 | foreach ($result as $type => $data) { |
||||||
413 | $this->setTableMetadata($tableName, $type, $data); |
||||||
414 | } |
||||||
415 | |||||||
416 | return $result[$returnType]; |
||||||
417 | } |
||||||
418 | } |
||||||
419 |