Passed
Push — master ( 385fe1...5adb55 )
by Alexander
10:01
created

framework/db/Schema.php (3 issues)

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;
9
10
use Yii;
11
use yii\base\BaseObject;
12
use yii\base\InvalidCallException;
13
use yii\base\InvalidConfigException;
14
use yii\base\NotSupportedException;
15
use yii\caching\Cache;
16
use yii\caching\CacheInterface;
17
use yii\caching\TagDependency;
18
19
/**
20
 * Schema is the base class for concrete DBMS-specific schema classes.
21
 *
22
 * Schema represents the database schema information that is DBMS specific.
23
 *
24
 * @property string $lastInsertID The row ID of the last row inserted, or the last value retrieved from the
25
 * sequence object. This property is read-only.
26
 * @property QueryBuilder $queryBuilder The query builder for this connection. This property is read-only.
27
 * @property string[] $schemaNames All schema names in the database, except system schemas. This property is
28
 * read-only.
29
 * @property string $serverVersion Server version as a string. This property is read-only.
30
 * @property string[] $tableNames All table names in the database. This property is read-only.
31
 * @property TableSchema[] $tableSchemas The metadata for all tables in the database. Each array element is an
32
 * instance of [[TableSchema]] or its child class. This property is read-only.
33
 * @property string $transactionIsolationLevel The transaction isolation level to use for this transaction.
34
 * This can be one of [[Transaction::READ_UNCOMMITTED]], [[Transaction::READ_COMMITTED]],
35
 * [[Transaction::REPEATABLE_READ]] and [[Transaction::SERIALIZABLE]] but also a string containing DBMS specific
36
 * syntax to be used after `SET TRANSACTION ISOLATION LEVEL`. This property is write-only.
37
 *
38
 * @author Qiang Xue <[email protected]>
39
 * @author Sergey Makinen <[email protected]>
40
 * @since 2.0
41
 */
42
abstract class Schema extends BaseObject
43
{
44
    // The following are the supported abstract column data types.
45
    const TYPE_PK = 'pk';
46
    const TYPE_UPK = 'upk';
47
    const TYPE_BIGPK = 'bigpk';
48
    const TYPE_UBIGPK = 'ubigpk';
49
    const TYPE_CHAR = 'char';
50
    const TYPE_STRING = 'string';
51
    const TYPE_TEXT = 'text';
52
    const TYPE_TINYINT = 'tinyint';
53
    const TYPE_SMALLINT = 'smallint';
54
    const TYPE_INTEGER = 'integer';
55
    const TYPE_BIGINT = 'bigint';
56
    const TYPE_FLOAT = 'float';
57
    const TYPE_DOUBLE = 'double';
58
    const TYPE_DECIMAL = 'decimal';
59
    const TYPE_DATETIME = 'datetime';
60
    const TYPE_TIMESTAMP = 'timestamp';
61
    const TYPE_TIME = 'time';
62
    const TYPE_DATE = 'date';
63
    const TYPE_BINARY = 'binary';
64
    const TYPE_BOOLEAN = 'boolean';
65
    const TYPE_MONEY = 'money';
66
    const TYPE_JSON = 'json';
67
    /**
68
     * Schema cache version, to detect incompatibilities in cached values when the
69
     * data format of the cache changes.
70
     */
71
    const SCHEMA_CACHE_VERSION = 1;
72
73
    /**
74
     * @var Connection the database connection
75
     */
76
    public $db;
77
    /**
78
     * @var string the default schema name used for the current session.
79
     */
80
    public $defaultSchema;
81
    /**
82
     * @var array map of DB errors and corresponding exceptions
83
     * If left part is found in DB error message exception class from the right part is used.
84
     */
85
    public $exceptionMap = [
86
        'SQLSTATE[23' => 'yii\db\IntegrityException',
87
    ];
88
    /**
89
     * @var string|array column schema class or class config
90
     * @since 2.0.11
91
     */
92
    public $columnSchemaClass = 'yii\db\ColumnSchema';
93
94
    /**
95
     * @var string|string[] character used to quote schema, table, etc. names.
96
     * An array of 2 characters can be used in case starting and ending characters are different.
97
     * @since 2.0.14
98
     */
99
    protected $tableQuoteCharacter = "'";
100
    /**
101
     * @var string|string[] character used to quote column names.
102
     * An array of 2 characters can be used in case starting and ending characters are different.
103
     * @since 2.0.14
104
     */
105
    protected $columnQuoteCharacter = '"';
106
107
    /**
108
     * @var array list of ALL schema names in the database, except system schemas
109
     */
110
    private $_schemaNames;
111
    /**
112
     * @var array list of ALL table names in the database
113
     */
114
    private $_tableNames = [];
115
    /**
116
     * @var array list of loaded table metadata (table name => metadata type => metadata).
117
     */
118
    private $_tableMetadata = [];
119
    /**
120
     * @var QueryBuilder the query builder for this database
121
     */
122
    private $_builder;
123
    /**
124
     * @var string server version as a string.
125
     */
126
    private $_serverVersion;
127
128
129
    /**
130
     * Resolves the table name and schema name (if any).
131
     * @param string $name the table name
132
     * @return TableSchema [[TableSchema]] with resolved table, schema, etc. names.
133
     * @throws NotSupportedException if this method is not supported by the DBMS.
134
     * @since 2.0.13
135
     */
136
    protected function resolveTableName($name)
137
    {
138
        throw new NotSupportedException(get_class($this) . ' does not support resolving table names.');
139
    }
140
141
    /**
142
     * Returns all schema names in the database, including the default one but not system schemas.
143
     * This method should be overridden by child classes in order to support this feature
144
     * because the default implementation simply throws an exception.
145
     * @return array all schema names in the database, except system schemas.
146
     * @throws NotSupportedException if this method is not supported by the DBMS.
147
     * @since 2.0.4
148
     */
149
    protected function findSchemaNames()
150
    {
151
        throw new NotSupportedException(get_class($this) . ' does not support fetching all schema names.');
152
    }
153
154
    /**
155
     * Returns all table names in the database.
156
     * This method should be overridden by child classes in order to support this feature
157
     * because the default implementation simply throws an exception.
158
     * @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema.
159
     * @return array all table names in the database. The names have NO schema name prefix.
160
     * @throws NotSupportedException if this method is not supported by the DBMS.
161
     */
162
    protected function findTableNames($schema = '')
163
    {
164
        throw new NotSupportedException(get_class($this) . ' does not support fetching all table names.');
165
    }
166
167
    /**
168
     * Loads the metadata for the specified table.
169
     * @param string $name table name
170
     * @return TableSchema|null DBMS-dependent table metadata, `null` if the table does not exist.
171
     */
172
    abstract protected function loadTableSchema($name);
173
174
    /**
175
     * Creates a column schema for the database.
176
     * This method may be overridden by child classes to create a DBMS-specific column schema.
177
     * @return ColumnSchema column schema instance.
178
     * @throws InvalidConfigException if a column schema class cannot be created.
179
     */
180 1068
    protected function createColumnSchema()
181
    {
182 1068
        return Yii::createObject($this->columnSchemaClass);
183
    }
184
185
    /**
186
     * Obtains the metadata for the named table.
187
     * @param string $name table name. The table name may contain schema name if any. Do not quote the table name.
188
     * @param bool $refresh whether to reload the table schema even if it is found in the cache.
189
     * @return TableSchema|null table metadata. `null` if the named table does not exist.
190
     */
191 1217
    public function getTableSchema($name, $refresh = false)
192
    {
193 1217
        return $this->getTableMetadata($name, 'schema', $refresh);
194
    }
195
196
    /**
197
     * Returns the metadata for all tables in the database.
198
     * @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema name.
199
     * @param bool $refresh whether to fetch the latest available table schemas. If this is `false`,
200
     * cached data may be returned if available.
201
     * @return TableSchema[] the metadata for all tables in the database.
202
     * Each array element is an instance of [[TableSchema]] or its child class.
203
     */
204 12
    public function getTableSchemas($schema = '', $refresh = false)
205
    {
206 12
        return $this->getSchemaMetadata($schema, 'schema', $refresh);
207
    }
208
209
    /**
210
     * Returns all schema names in the database, except system schemas.
211
     * @param bool $refresh whether to fetch the latest available schema names. If this is false,
212
     * schema names fetched previously (if available) will be returned.
213
     * @return string[] all schema names in the database, except system schemas.
214
     * @since 2.0.4
215
     */
216 2
    public function getSchemaNames($refresh = false)
217
    {
218 2
        if ($this->_schemaNames === null || $refresh) {
219 2
            $this->_schemaNames = $this->findSchemaNames();
220
        }
221
222 2
        return $this->_schemaNames;
223
    }
224
225
    /**
226
     * Returns all table names in the database.
227
     * @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema name.
228
     * If not empty, the returned table names will be prefixed with the schema name.
229
     * @param bool $refresh whether to fetch the latest available table names. If this is false,
230
     * table names fetched previously (if available) will be returned.
231
     * @return string[] all table names in the database.
232
     */
233 18
    public function getTableNames($schema = '', $refresh = false)
234
    {
235 18
        if (!isset($this->_tableNames[$schema]) || $refresh) {
236 18
            $this->_tableNames[$schema] = $this->findTableNames($schema);
237
        }
238
239 18
        return $this->_tableNames[$schema];
240
    }
241
242
    /**
243
     * @return QueryBuilder the query builder for this connection.
244
     */
245 1100
    public function getQueryBuilder()
246
    {
247 1100
        if ($this->_builder === null) {
248 1045
            $this->_builder = $this->createQueryBuilder();
249
        }
250
251 1100
        return $this->_builder;
252
    }
253
254
    /**
255
     * Determines the PDO type for the given PHP data value.
256
     * @param mixed $data the data whose PDO type is to be determined
257
     * @return int the PDO type
258
     * @see https://secure.php.net/manual/en/pdo.constants.php
259
     */
260 1139
    public function getPdoType($data)
261
    {
262 1139
        static $typeMap = [
263
            // php type => PDO type
264
            'boolean' => \PDO::PARAM_BOOL,
265
            'integer' => \PDO::PARAM_INT,
266
            'string' => \PDO::PARAM_STR,
267
            'resource' => \PDO::PARAM_LOB,
268
            'NULL' => \PDO::PARAM_NULL,
269
        ];
270 1139
        $type = gettype($data);
271
272 1139
        return isset($typeMap[$type]) ? $typeMap[$type] : \PDO::PARAM_STR;
273
    }
274
275
    /**
276
     * Refreshes the schema.
277
     * This method cleans up all cached table schemas so that they can be re-created later
278
     * to reflect the database schema change.
279
     */
280 46
    public function refresh()
281
    {
282
        /* @var $cache CacheInterface */
283 46
        $cache = is_string($this->db->schemaCache) ? Yii::$app->get($this->db->schemaCache, false) : $this->db->schemaCache;
284 46
        if ($this->db->enableSchemaCache && $cache instanceof CacheInterface) {
285 1
            TagDependency::invalidate($cache, $this->getCacheTag());
286
        }
287 46
        $this->_tableNames = [];
288 46
        $this->_tableMetadata = [];
289 46
    }
290
291
    /**
292
     * Refreshes the particular table schema.
293
     * This method cleans up cached table schema so that it can be re-created later
294
     * to reflect the database schema change.
295
     * @param string $name table name.
296
     * @since 2.0.6
297
     */
298 175
    public function refreshTableSchema($name)
299
    {
300 175
        $rawName = $this->getRawTableName($name);
301 175
        unset($this->_tableMetadata[$rawName]);
302 175
        $this->_tableNames = [];
303
        /* @var $cache CacheInterface */
304 175
        $cache = is_string($this->db->schemaCache) ? Yii::$app->get($this->db->schemaCache, false) : $this->db->schemaCache;
305 175
        if ($this->db->enableSchemaCache && $cache instanceof CacheInterface) {
306 22
            $cache->delete($this->getCacheKey($rawName));
307
        }
308 175
    }
309
310
    /**
311
     * Creates a query builder for the database.
312
     * This method may be overridden by child classes to create a DBMS-specific query builder.
313
     * @return QueryBuilder query builder instance
314
     */
315
    public function createQueryBuilder()
316
    {
317
        return new QueryBuilder($this->db);
318
    }
319
320
    /**
321
     * Create a column schema builder instance giving the type and value precision.
322
     *
323
     * This method may be overridden by child classes to create a DBMS-specific column schema builder.
324
     *
325
     * @param string $type type of the column. See [[ColumnSchemaBuilder::$type]].
326
     * @param int|string|array $length length or precision of the column. See [[ColumnSchemaBuilder::$length]].
327
     * @return ColumnSchemaBuilder column schema builder instance
328
     * @since 2.0.6
329
     */
330 12
    public function createColumnSchemaBuilder($type, $length = null)
331
    {
332 12
        return new ColumnSchemaBuilder($type, $length);
333
    }
334
335
    /**
336
     * Returns all unique indexes for the given table.
337
     *
338
     * Each array element is of the following structure:
339
     *
340
     * ```php
341
     * [
342
     *  'IndexName1' => ['col1' [, ...]],
343
     *  'IndexName2' => ['col2' [, ...]],
344
     * ]
345
     * ```
346
     *
347
     * This method should be overridden by child classes in order to support this feature
348
     * because the default implementation simply throws an exception
349
     * @param TableSchema $table the table metadata
350
     * @return array all unique indexes for the given table.
351
     * @throws NotSupportedException if this method is called
352
     */
353
    public function findUniqueIndexes($table)
354
    {
355
        throw new NotSupportedException(get_class($this) . ' does not support getting unique indexes information.');
356
    }
357
358
    /**
359
     * Returns the ID of the last inserted row or sequence value.
360
     * @param string $sequenceName name of the sequence object (required by some DBMS)
361
     * @return string the row ID of the last row inserted, or the last value retrieved from the sequence object
362
     * @throws InvalidCallException if the DB connection is not active
363
     * @see https://secure.php.net/manual/en/function.PDO-lastInsertId.php
364
     */
365 90
    public function getLastInsertID($sequenceName = '')
366
    {
367 90
        if ($this->db->isActive) {
368 90
            return $this->db->pdo->lastInsertId($sequenceName === '' ? null : $this->quoteTableName($sequenceName));
369
        }
370
371
        throw new InvalidCallException('DB Connection is not active.');
372
    }
373
374
    /**
375
     * @return bool whether this DBMS supports [savepoint](http://en.wikipedia.org/wiki/Savepoint).
376
     */
377 8
    public function supportsSavepoint()
378
    {
379 8
        return $this->db->enableSavepoint;
380
    }
381
382
    /**
383
     * Creates a new savepoint.
384
     * @param string $name the savepoint name
385
     */
386 4
    public function createSavepoint($name)
387
    {
388 4
        $this->db->createCommand("SAVEPOINT $name")->execute();
389 4
    }
390
391
    /**
392
     * Releases an existing savepoint.
393
     * @param string $name the savepoint name
394
     */
395
    public function releaseSavepoint($name)
396
    {
397
        $this->db->createCommand("RELEASE SAVEPOINT $name")->execute();
398
    }
399
400
    /**
401
     * Rolls back to a previously created savepoint.
402
     * @param string $name the savepoint name
403
     */
404 4
    public function rollBackSavepoint($name)
405
    {
406 4
        $this->db->createCommand("ROLLBACK TO SAVEPOINT $name")->execute();
407 4
    }
408
409
    /**
410
     * Sets the isolation level of the current transaction.
411
     * @param string $level The transaction isolation level to use for this transaction.
412
     * This can be one of [[Transaction::READ_UNCOMMITTED]], [[Transaction::READ_COMMITTED]], [[Transaction::REPEATABLE_READ]]
413
     * and [[Transaction::SERIALIZABLE]] but also a string containing DBMS specific syntax to be used
414
     * after `SET TRANSACTION ISOLATION LEVEL`.
415
     * @see http://en.wikipedia.org/wiki/Isolation_%28database_systems%29#Isolation_levels
416
     */
417 6
    public function setTransactionIsolationLevel($level)
418
    {
419 6
        $this->db->createCommand("SET TRANSACTION ISOLATION LEVEL $level")->execute();
420 6
    }
421
422
    /**
423
     * Executes the INSERT command, returning primary key values.
424
     * @param string $table the table that new rows will be inserted into.
425
     * @param array $columns the column data (name => value) to be inserted into the table.
426
     * @return array|false primary key values or false if the command fails
427
     * @since 2.0.4
428
     */
429 88
    public function insert($table, $columns)
430
    {
431 88
        $command = $this->db->createCommand()->insert($table, $columns);
432 88
        if (!$command->execute()) {
433
            return false;
434
        }
435 88
        $tableSchema = $this->getTableSchema($table);
436 88
        $result = [];
437 88
        foreach ($tableSchema->primaryKey as $name) {
438 85
            if ($tableSchema->columns[$name]->autoIncrement) {
439 81
                $result[$name] = $this->getLastInsertID($tableSchema->sequenceName);
440 81
                break;
441
            }
442
443 6
            $result[$name] = isset($columns[$name]) ? $columns[$name] : $tableSchema->columns[$name]->defaultValue;
444
        }
445
446 88
        return $result;
447
    }
448
449
    /**
450
     * Quotes a string value for use in a query.
451
     * Note that if the parameter is not a string, it will be returned without change.
452
     * @param string $str string to be quoted
453
     * @return string the properly quoted string
454
     * @see https://secure.php.net/manual/en/function.PDO-quote.php
455
     */
456 1134
    public function quoteValue($str)
457
    {
458 1134
        if (!is_string($str)) {
459 4
            return $str;
460
        }
461
462 1134
        if (($value = $this->db->getSlavePdo()->quote($str)) !== false) {
0 ignored issues
show
Deprecated Code introduced by
The function yii\db\Connection::getSlavePdo() has been deprecated: since 2.0.36. Use [[getReplicaPdo()]] instead. ( Ignorable by Annotation )

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

462
        if (($value = /** @scrutinizer ignore-deprecated */ $this->db->getSlavePdo()->quote($str)) !== false) {

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...
463 1134
            return $value;
464
        }
465
466
        // the driver doesn't support quote (e.g. oci)
467
        return "'" . addcslashes(str_replace("'", "''", $str), "\000\n\r\\\032") . "'";
468
    }
469
470
    /**
471
     * Quotes a table name for use in a query.
472
     * If the table name contains schema prefix, the prefix will also be properly quoted.
473
     * If the table name is already quoted or contains '(' or '{{',
474
     * then this method will do nothing.
475
     * @param string $name table name
476
     * @return string the properly quoted table name
477
     * @see quoteSimpleTableName()
478
     */
479 1364
    public function quoteTableName($name)
480
    {
481
482 1364
        if (strpos($name, '(') === 0 && strpos($name, ')') === strlen($name) - 1) {
483 4
            return $name;
484
        }
485 1364
        if (strpos($name, '{{') !== false) {
486 403
            return $name;
487
        }
488 1348
        if (strpos($name, '.') === false) {
489 1344
            return $this->quoteSimpleTableName($name);
490
        }
491 13
        $parts = $this->getTableNameParts($name);
492 13
        foreach ($parts as $i => $part) {
493 13
            $parts[$i] = $this->quoteSimpleTableName($part);
494
        }
495 13
        return implode('.', $parts);
496
    }
497
498
    /**
499
     * Splits full table name into parts
500
     * @param string $name
501
     * @return array
502
     * @since 2.0.22
503
     */
504 13
    protected function getTableNameParts($name)
505
    {
506 13
        return explode('.', $name);
507
    }
508
509
    /**
510
     * Quotes a column name for use in a query.
511
     * If the column name contains prefix, the prefix will also be properly quoted.
512
     * If the column name is already quoted or contains '(', '[[' or '{{',
513
     * then this method will do nothing.
514
     * @param string $name column name
515
     * @return string the properly quoted column name
516
     * @see quoteSimpleColumnName()
517
     */
518 1522
    public function quoteColumnName($name)
519
    {
520 1522
        if (strpos($name, '(') !== false || strpos($name, '[[') !== false) {
521 145
            return $name;
522
        }
523 1513
        if (($pos = strrpos($name, '.')) !== false) {
524 204
            $prefix = $this->quoteTableName(substr($name, 0, $pos)) . '.';
525 204
            $name = substr($name, $pos + 1);
526
        } else {
527 1506
            $prefix = '';
528
        }
529 1513
        if (strpos($name, '{{') !== false) {
530 4
            return $name;
531
        }
532
533 1513
        return $prefix . $this->quoteSimpleColumnName($name);
534
    }
535
536
    /**
537
     * Quotes a simple table name for use in a query.
538
     * A simple table name should contain the table name only without any schema prefix.
539
     * If the table name is already quoted, this method will do nothing.
540
     * @param string $name table name
541
     * @return string the properly quoted table name
542
     */
543 1463
    public function quoteSimpleTableName($name)
544
    {
545 1463
        if (is_string($this->tableQuoteCharacter)) {
546 1463
            $startingCharacter = $endingCharacter = $this->tableQuoteCharacter;
547
        } else {
548
            list($startingCharacter, $endingCharacter) = $this->tableQuoteCharacter;
549
        }
550 1463
        return strpos($name, $startingCharacter) !== false ? $name : $startingCharacter . $name . $endingCharacter;
551
    }
552
553
    /**
554
     * Quotes a simple column name for use in a query.
555
     * A simple column name should contain the column name only without any prefix.
556
     * If the column name is already quoted or is the asterisk character '*', this method will do nothing.
557
     * @param string $name column name
558
     * @return string the properly quoted column name
559
     */
560 1513
    public function quoteSimpleColumnName($name)
561
    {
562 1513
        if (is_string($this->tableQuoteCharacter)) {
563 1513
            $startingCharacter = $endingCharacter = $this->columnQuoteCharacter;
564
        } else {
565
            list($startingCharacter, $endingCharacter) = $this->columnQuoteCharacter;
566
        }
567 1513
        return $name === '*' || strpos($name, $startingCharacter) !== false ? $name : $startingCharacter . $name . $endingCharacter;
568
    }
569
570
    /**
571
     * Unquotes a simple table name.
572
     * A simple table name should contain the table name only without any schema prefix.
573
     * If the table name is not quoted, this method will do nothing.
574
     * @param string $name table name.
575
     * @return string unquoted table name.
576
     * @since 2.0.14
577
     */
578
    public function unquoteSimpleTableName($name)
579
    {
580
        if (is_string($this->tableQuoteCharacter)) {
581
            $startingCharacter = $this->tableQuoteCharacter;
582
        } else {
583
            $startingCharacter = $this->tableQuoteCharacter[0];
584
        }
585
        return strpos($name, $startingCharacter) === false ? $name : substr($name, 1, -1);
586
    }
587
588
    /**
589
     * Unquotes a simple column name.
590
     * A simple column name should contain the column name only without any prefix.
591
     * If the column name is not quoted or is the asterisk character '*', this method will do nothing.
592
     * @param string $name column name.
593
     * @return string unquoted column name.
594
     * @since 2.0.14
595
     */
596
    public function unquoteSimpleColumnName($name)
597
    {
598
        if (is_string($this->columnQuoteCharacter)) {
599
            $startingCharacter = $this->columnQuoteCharacter;
600
        } else {
601
            $startingCharacter = $this->columnQuoteCharacter[0];
602
        }
603
        return strpos($name, $startingCharacter) === false ? $name : substr($name, 1, -1);
604
    }
605
606
    /**
607
     * Returns the actual name of a given table name.
608
     * This method will strip off curly brackets from the given table name
609
     * and replace the percentage character '%' with [[Connection::tablePrefix]].
610
     * @param string $name the table name to be converted
611
     * @return string the real name of the given table name
612
     */
613 1402
    public function getRawTableName($name)
614
    {
615 1402
        if (strpos($name, '{{') !== false) {
616 496
            $name = preg_replace('/\\{\\{(.*?)\\}\\}/', '\1', $name);
617
618 496
            return str_replace('%', $this->db->tablePrefix, $name);
619
        }
620
621 1020
        return $name;
622
    }
623
624
    /**
625
     * Extracts the PHP type from abstract DB type.
626
     * @param ColumnSchema $column the column schema information
627
     * @return string PHP type name
628
     */
629 1068
    protected function getColumnPhpType($column)
630
    {
631 1068
        static $typeMap = [
632
            // abstract type => php type
633
            self::TYPE_TINYINT => 'integer',
634
            self::TYPE_SMALLINT => 'integer',
635
            self::TYPE_INTEGER => 'integer',
636
            self::TYPE_BIGINT => 'integer',
637
            self::TYPE_BOOLEAN => 'boolean',
638
            self::TYPE_FLOAT => 'double',
639
            self::TYPE_DOUBLE => 'double',
640
            self::TYPE_BINARY => 'resource',
641
            self::TYPE_JSON => 'array',
642
        ];
643 1068
        if (isset($typeMap[$column->type])) {
644 1060
            if ($column->type === 'bigint') {
645 45
                return PHP_INT_SIZE === 8 && !$column->unsigned ? 'integer' : 'string';
646 1060
            } elseif ($column->type === 'integer') {
647 1060
                return PHP_INT_SIZE === 4 && $column->unsigned ? 'string' : 'integer';
648
            }
649
650 499
            return $typeMap[$column->type];
651
        }
652
653 1030
        return 'string';
654
    }
655
656
    /**
657
     * Converts a DB exception to a more concrete one if possible.
658
     *
659
     * @param \Exception $e
660
     * @param string $rawSql SQL that produced exception
661
     * @return Exception
662
     */
663 36
    public function convertException(\Exception $e, $rawSql)
664
    {
665 36
        if ($e instanceof Exception) {
666
            return $e;
667
        }
668
669 36
        $exceptionClass = '\yii\db\Exception';
670 36
        foreach ($this->exceptionMap as $error => $class) {
671 36
            if (strpos($e->getMessage(), $error) !== false) {
672 36
                $exceptionClass = $class;
673
            }
674
        }
675 36
        $message = $e->getMessage() . "\nThe SQL being executed was: $rawSql";
676 36
        $errorInfo = $e instanceof \PDOException ? $e->errorInfo : null;
677 36
        return new $exceptionClass($message, $errorInfo, (int)$e->getCode(), $e);
678
    }
679
680
    /**
681
     * Returns a value indicating whether a SQL statement is for read purpose.
682
     * @param string $sql the SQL statement
683
     * @return bool whether a SQL statement is for read purpose.
684
     */
685 9
    public function isReadQuery($sql)
686
    {
687 9
        $pattern = '/^\s*(SELECT|SHOW|DESCRIBE)\b/i';
688 9
        return preg_match($pattern, $sql) > 0;
689
    }
690
691
    /**
692
     * Returns a server version as a string comparable by [[\version_compare()]].
693
     * @return string server version as a string.
694
     * @since 2.0.14
695
     */
696 412
    public function getServerVersion()
697
    {
698 412
        if ($this->_serverVersion === null) {
699 412
            $this->_serverVersion = $this->db->getSlavePdo()->getAttribute(\PDO::ATTR_SERVER_VERSION);
0 ignored issues
show
Deprecated Code introduced by
The function yii\db\Connection::getSlavePdo() has been deprecated: since 2.0.36. Use [[getReplicaPdo()]] instead. ( Ignorable by Annotation )

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

699
            $this->_serverVersion = /** @scrutinizer ignore-deprecated */ $this->db->getSlavePdo()->getAttribute(\PDO::ATTR_SERVER_VERSION);

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...
700
        }
701 412
        return $this->_serverVersion;
702
    }
703
704
    /**
705
     * Returns the cache key for the specified table name.
706
     * @param string $name the table name.
707
     * @return mixed the cache key.
708
     */
709 25
    protected function getCacheKey($name)
710
    {
711
        return [
712 25
            __CLASS__,
713 25
            $this->db->dsn,
714 25
            $this->db->username,
715 25
            $this->getRawTableName($name),
716
        ];
717
    }
718
719
    /**
720
     * Returns the cache tag name.
721
     * This allows [[refresh()]] to invalidate all cached table schemas.
722
     * @return string the cache tag name
723
     */
724 25
    protected function getCacheTag()
725
    {
726 25
        return md5(serialize([
727 25
            __CLASS__,
728 25
            $this->db->dsn,
729 25
            $this->db->username,
730
        ]));
731
    }
732
733
    /**
734
     * Returns the metadata of the given type for the given table.
735
     * If there's no metadata in the cache, this method will call
736
     * a `'loadTable' . ucfirst($type)` named method with the table name to obtain the metadata.
737
     * @param string $name table name. The table name may contain schema name if any. Do not quote the table name.
738
     * @param string $type metadata type.
739
     * @param bool $refresh whether to reload the table metadata even if it is found in the cache.
740
     * @return mixed metadata.
741
     * @since 2.0.13
742
     */
743 1400
    protected function getTableMetadata($name, $type, $refresh)
744
    {
745 1400
        $cache = null;
746 1400
        if ($this->db->enableSchemaCache && !in_array($name, $this->db->schemaCacheExclude, true)) {
747 25
            $schemaCache = is_string($this->db->schemaCache) ? Yii::$app->get($this->db->schemaCache, false) : $this->db->schemaCache;
748 25
            if ($schemaCache instanceof CacheInterface) {
749 25
                $cache = $schemaCache;
750
            }
751
        }
752 1400
        $rawName = $this->getRawTableName($name);
753 1400
        if (!isset($this->_tableMetadata[$rawName])) {
754 1356
            $this->loadTableMetadataFromCache($cache, $rawName);
755
        }
756 1400
        if ($refresh || !array_key_exists($type, $this->_tableMetadata[$rawName])) {
757 1356
            $this->_tableMetadata[$rawName][$type] = $this->{'loadTable' . ucfirst($type)}($rawName);
758 1308
            $this->saveTableMetadataToCache($cache, $rawName);
759
        }
760
761 1352
        return $this->_tableMetadata[$rawName][$type];
762
    }
763
764
    /**
765
     * Returns the metadata of the given type for all tables in the given schema.
766
     * This method will call a `'getTable' . ucfirst($type)` named method with the table name
767
     * and the refresh flag to obtain the metadata.
768
     * @param string $schema the schema of the metadata. Defaults to empty string, meaning the current or default schema name.
769
     * @param string $type metadata type.
770
     * @param bool $refresh whether to fetch the latest available table metadata. If this is `false`,
771
     * cached data may be returned if available.
772
     * @return array array of metadata.
773
     * @since 2.0.13
774
     */
775 12
    protected function getSchemaMetadata($schema, $type, $refresh)
776
    {
777 12
        $metadata = [];
778 12
        $methodName = 'getTable' . ucfirst($type);
779 12
        foreach ($this->getTableNames($schema, $refresh) as $name) {
780 12
            if ($schema !== '') {
781
                $name = $schema . '.' . $name;
782
            }
783 12
            $tableMetadata = $this->$methodName($name, $refresh);
784 12
            if ($tableMetadata !== null) {
785 12
                $metadata[] = $tableMetadata;
786
            }
787
        }
788
789 12
        return $metadata;
790
    }
791
792
    /**
793
     * Sets the metadata of the given type for the given table.
794
     * @param string $name table name.
795
     * @param string $type metadata type.
796
     * @param mixed $data metadata.
797
     * @since 2.0.13
798
     */
799 214
    protected function setTableMetadata($name, $type, $data)
800
    {
801 214
        $this->_tableMetadata[$this->getRawTableName($name)][$type] = $data;
802 214
    }
803
804
    /**
805
     * Changes row's array key case to lower if PDO's one is set to uppercase.
806
     * @param array $row row's array or an array of row's arrays.
807
     * @param bool $multiple whether multiple rows or a single row passed.
808
     * @return array normalized row or rows.
809
     * @since 2.0.13
810
     */
811 237
    protected function normalizePdoRowKeyCase(array $row, $multiple)
812
    {
813 237
        if ($this->db->getSlavePdo()->getAttribute(\PDO::ATTR_CASE) !== \PDO::CASE_UPPER) {
0 ignored issues
show
Deprecated Code introduced by
The function yii\db\Connection::getSlavePdo() has been deprecated: since 2.0.36. Use [[getReplicaPdo()]] instead. ( Ignorable by Annotation )

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

813
        if (/** @scrutinizer ignore-deprecated */ $this->db->getSlavePdo()->getAttribute(\PDO::ATTR_CASE) !== \PDO::CASE_UPPER) {

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...
814 196
            return $row;
815
        }
816
817 41
        if ($multiple) {
818 41
            return array_map(function (array $row) {
819 40
                return array_change_key_case($row, CASE_LOWER);
820 41
            }, $row);
821
        }
822
823
        return array_change_key_case($row, CASE_LOWER);
824
    }
825
826
    /**
827
     * Tries to load and populate table metadata from cache.
828
     * @param Cache|null $cache
829
     * @param string $name
830
     */
831 1356
    private function loadTableMetadataFromCache($cache, $name)
832
    {
833 1356
        if ($cache === null) {
834 1331
            $this->_tableMetadata[$name] = [];
835 1331
            return;
836
        }
837
838 25
        $metadata = $cache->get($this->getCacheKey($name));
839 25
        if (!is_array($metadata) || !isset($metadata['cacheVersion']) || $metadata['cacheVersion'] !== static::SCHEMA_CACHE_VERSION) {
840 25
            $this->_tableMetadata[$name] = [];
841 25
            return;
842
        }
843
844 3
        unset($metadata['cacheVersion']);
845 3
        $this->_tableMetadata[$name] = $metadata;
846 3
    }
847
848
    /**
849
     * Saves table metadata to cache.
850
     * @param Cache|null $cache
851
     * @param string $name
852
     */
853 1308
    private function saveTableMetadataToCache($cache, $name)
854
    {
855 1308
        if ($cache === null) {
856 1283
            return;
857
        }
858
859 25
        $metadata = $this->_tableMetadata[$name];
860 25
        $metadata['cacheVersion'] = static::SCHEMA_CACHE_VERSION;
861 25
        $cache->set(
862 25
            $this->getCacheKey($name),
863 25
            $metadata,
864 25
            $this->db->schemaCacheDuration,
865 25
            new TagDependency(['tags' => $this->getCacheTag()])
866
        );
867 25
    }
868
}
869