GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( ecf3ef...78a151 )
by Robert
11:40
created

Schema::quoteSimpleTableName()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
ccs 2
cts 2
cp 1
cc 2
eloc 2
nc 2
nop 1
crap 2
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\pgsql;
9
10
use yii\base\NotSupportedException;
11
use yii\db\CheckConstraint;
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\ViewFinderTrait;
20
use yii\helpers\ArrayHelper;
21
22
/**
23
 * Schema is the class for retrieving metadata from a PostgreSQL database
24
 * (version 9.x and above).
25
 *
26
 * @author Gevik Babakhani <[email protected]>
27
 * @since 2.0
28
 */
29
class Schema extends \yii\db\Schema implements ConstraintFinderInterface
30
{
31
    use ViewFinderTrait;
32
    use ConstraintFinderTrait;
33
34
    const TYPE_JSONB = 'jsonb';
35
36
    /**
37
     * @var string the default schema used for the current session.
38
     */
39
    public $defaultSchema = 'public';
40
    /**
41
     * {@inheritdoc}
42
     */
43
    public $columnSchemaClass = 'yii\db\pgsql\ColumnSchema';
44
    /**
45
     * @var array mapping from physical column types (keys) to abstract
46
     * column types (values)
47
     * @see http://www.postgresql.org/docs/current/static/datatype.html#DATATYPE-TABLE
48
     */
49
    public $typeMap = [
50
        'bit' => self::TYPE_INTEGER,
51
        'bit varying' => self::TYPE_INTEGER,
52
        'varbit' => self::TYPE_INTEGER,
53
54
        'bool' => self::TYPE_BOOLEAN,
55
        'boolean' => self::TYPE_BOOLEAN,
56
57
        'box' => self::TYPE_STRING,
58
        'circle' => self::TYPE_STRING,
59
        'point' => self::TYPE_STRING,
60
        'line' => self::TYPE_STRING,
61
        'lseg' => self::TYPE_STRING,
62
        'polygon' => self::TYPE_STRING,
63
        'path' => self::TYPE_STRING,
64
65
        'character' => self::TYPE_CHAR,
66
        'char' => self::TYPE_CHAR,
67
        'bpchar' => self::TYPE_CHAR,
68
        'character varying' => self::TYPE_STRING,
69
        'varchar' => self::TYPE_STRING,
70
        'text' => self::TYPE_TEXT,
71
72
        'bytea' => self::TYPE_BINARY,
73
74
        'cidr' => self::TYPE_STRING,
75
        'inet' => self::TYPE_STRING,
76
        'macaddr' => self::TYPE_STRING,
77
78
        'real' => self::TYPE_FLOAT,
79
        'float4' => self::TYPE_FLOAT,
80
        'double precision' => self::TYPE_DOUBLE,
81
        'float8' => self::TYPE_DOUBLE,
82
        'decimal' => self::TYPE_DECIMAL,
83
        'numeric' => self::TYPE_DECIMAL,
84
85
        'money' => self::TYPE_MONEY,
86
87
        'smallint' => self::TYPE_SMALLINT,
88
        'int2' => self::TYPE_SMALLINT,
89
        'int4' => self::TYPE_INTEGER,
90
        'int' => self::TYPE_INTEGER,
91
        'integer' => self::TYPE_INTEGER,
92
        'bigint' => self::TYPE_BIGINT,
93
        'int8' => self::TYPE_BIGINT,
94
        'oid' => self::TYPE_BIGINT, // should not be used. it's pg internal!
95
96
        'smallserial' => self::TYPE_SMALLINT,
97
        'serial2' => self::TYPE_SMALLINT,
98
        'serial4' => self::TYPE_INTEGER,
99
        'serial' => self::TYPE_INTEGER,
100
        'bigserial' => self::TYPE_BIGINT,
101
        'serial8' => self::TYPE_BIGINT,
102
        'pg_lsn' => self::TYPE_BIGINT,
103
104
        'date' => self::TYPE_DATE,
105
        'interval' => self::TYPE_STRING,
106
        'time without time zone' => self::TYPE_TIME,
107
        'time' => self::TYPE_TIME,
108
        'time with time zone' => self::TYPE_TIME,
109
        'timetz' => self::TYPE_TIME,
110
        'timestamp without time zone' => self::TYPE_TIMESTAMP,
111
        'timestamp' => self::TYPE_TIMESTAMP,
112
        'timestamp with time zone' => self::TYPE_TIMESTAMP,
113
        'timestamptz' => self::TYPE_TIMESTAMP,
114
        'abstime' => self::TYPE_TIMESTAMP,
115
116
        'tsquery' => self::TYPE_STRING,
117
        'tsvector' => self::TYPE_STRING,
118
        'txid_snapshot' => self::TYPE_STRING,
119
120
        'unknown' => self::TYPE_STRING,
121
122
        'uuid' => self::TYPE_STRING,
123
        'json' => self::TYPE_JSON,
124
        'jsonb' => self::TYPE_JSON,
125
        'xml' => self::TYPE_STRING,
126
    ];
127
128
    /**
129
     * @inheritDoc
130
     */
131
    protected $tableQuoteCharacter = '"';
132
133
134
    /**
135
     * @inheritDoc
136
     */
137 75
    protected function resolveTableName($name)
138
    {
139 75
        $resolvedName = new TableSchema();
140 75
        $parts = explode('.', str_replace('"', '', $name));
141 75
        if (isset($parts[1])) {
142
            $resolvedName->schemaName = $parts[0];
143
            $resolvedName->name = $parts[1];
144
        } else {
145 75
            $resolvedName->schemaName = $this->defaultSchema;
146 75
            $resolvedName->name = $name;
147
        }
148 75
        $resolvedName->fullName = ($resolvedName->schemaName !== $this->defaultSchema ? $resolvedName->schemaName . '.' : '') . $resolvedName->name;
149 75
        return $resolvedName;
150
    }
151
152
    /**
153
     * @inheritDoc
154
     */
155 2
    protected function findSchemaNames()
156
    {
157 2
        static $sql = <<<'SQL'
158
SELECT "ns"."nspname"
159
FROM "pg_namespace" AS "ns"
160
WHERE "ns"."nspname" != 'information_schema' AND "ns"."nspname" NOT LIKE 'pg_%'
161
ORDER BY "ns"."nspname" ASC
162
SQL;
163
164 2
        return $this->db->createCommand($sql)->queryColumn();
165
    }
166
167
    /**
168
     * @inheritDoc
169
     */
170 5
    protected function findTableNames($schema = '')
171
    {
172 5
        if ($schema === '') {
173 5
            $schema = $this->defaultSchema;
174
        }
175
        $sql = <<<'SQL'
176 5
SELECT c.relname AS table_name
177
FROM pg_class c
178
INNER JOIN pg_namespace ns ON ns.oid = c.relnamespace
179
WHERE ns.nspname = :schemaName AND c.relkind IN ('r','v','m','f')
180
ORDER BY c.relname
181
SQL;
182 5
        return $this->db->createCommand($sql, [':schemaName' => $schema])->queryColumn();
183
    }
184
185
    /**
186
     * @inheritDoc
187
     */
188 319
    protected function loadTableSchema($name)
189
    {
190 319
        $table = new TableSchema();
191 319
        $this->resolveTableNames($table, $name);
192 319
        if ($this->findColumns($table)) {
193 310
            $this->findConstraints($table);
194 310
            return $table;
195
        }
196
197 21
        return null;
198
    }
199
200
    /**
201
     * @inheritDoc
202
     */
203 35
    protected function loadTablePrimaryKey($tableName)
204
    {
205 35
        return $this->loadTableConstraints($tableName, 'primaryKey');
206
    }
207
208
    /**
209
     * @inheritDoc
210
     */
211 4
    protected function loadTableForeignKeys($tableName)
212
    {
213 4
        return $this->loadTableConstraints($tableName, 'foreignKeys');
214
    }
215
216
    /**
217
     * @inheritDoc
218
     */
219 32
    protected function loadTableIndexes($tableName)
220
    {
221 32
        static $sql = <<<'SQL'
222
SELECT
223
    "ic"."relname" AS "name",
224
    "ia"."attname" AS "column_name",
225
    "i"."indisunique" AS "index_is_unique",
226
    "i"."indisprimary" AS "index_is_primary"
227
FROM "pg_class" AS "tc"
228
INNER JOIN "pg_namespace" AS "tcns"
229
    ON "tcns"."oid" = "tc"."relnamespace"
230
INNER JOIN "pg_index" AS "i"
231
    ON "i"."indrelid" = "tc"."oid"
232
INNER JOIN "pg_class" AS "ic"
233
    ON "ic"."oid" = "i"."indexrelid"
234
INNER JOIN "pg_attribute" AS "ia"
235
    ON "ia"."attrelid" = "i"."indrelid" AND "ia"."attnum" = ANY ("i"."indkey")
236
WHERE "tcns"."nspname" = :schemaName AND "tc"."relname" = :tableName
237
ORDER BY "ia"."attnum" ASC
238
SQL;
239
240 32
        $resolvedName = $this->resolveTableName($tableName);
241 32
        $indexes = $this->db->createCommand($sql, [
242 32
            ':schemaName' => $resolvedName->schemaName,
243 32
            ':tableName' => $resolvedName->name,
244 32
        ])->queryAll();
245 32
        $indexes = $this->normalizePdoRowKeyCase($indexes, true);
246 32
        $indexes = ArrayHelper::index($indexes, null, 'name');
247 32
        $result = [];
248 32
        foreach ($indexes as $name => $index) {
249 29
            $result[] = new IndexConstraint([
250 29
                'isPrimary' => (bool) $index[0]['index_is_primary'],
251 29
                'isUnique' => (bool) $index[0]['index_is_unique'],
252 29
                'name' => $name,
253 29
                'columnNames' => ArrayHelper::getColumn($index, 'column_name'),
254
            ]);
255
        }
256
257 32
        return $result;
258
    }
259
260
    /**
261
     * @inheritDoc
262
     */
263 13
    protected function loadTableUniques($tableName)
264
    {
265 13
        return $this->loadTableConstraints($tableName, 'uniques');
266
    }
267
268
    /**
269
     * @inheritDoc
270
     */
271 13
    protected function loadTableChecks($tableName)
272
    {
273 13
        return $this->loadTableConstraints($tableName, 'checks');
274
    }
275
276
    /**
277
     * @inheritDoc
278
     * @throws NotSupportedException if this method is called.
279
     */
280 12
    protected function loadTableDefaultValues($tableName)
0 ignored issues
show
Unused Code introduced by
The parameter $tableName is not used and could be removed.

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

Loading history...
281
    {
282 12
        throw new NotSupportedException('PostgreSQL does not support default value constraints.');
283
    }
284
285
    /**
286
     * Creates a query builder for the PostgreSQL database.
287
     * @return QueryBuilder query builder instance
288
     */
289 314
    public function createQueryBuilder()
290
    {
291 314
        return new QueryBuilder($this->db);
292
    }
293
294
    /**
295
     * Resolves the table name and schema name (if any).
296
     * @param TableSchema $table the table metadata object
297
     * @param string $name the table name
298
     */
299 319
    protected function resolveTableNames($table, $name)
300
    {
301 319
        $parts = explode('.', str_replace('"', '', $name));
302
303 319
        if (isset($parts[1])) {
304
            $table->schemaName = $parts[0];
305
            $table->name = $parts[1];
306
        } else {
307 319
            $table->schemaName = $this->defaultSchema;
308 319
            $table->name = $name;
309
        }
310
311 319
        $table->fullName = $table->schemaName !== $this->defaultSchema ? $table->schemaName . '.' . $table->name : $table->name;
312 319
    }
313
314
    /**
315
     * {@inheritdoc]
316
     */
317
    protected function findViewNames($schema = '')
318
    {
319
        if ($schema === '') {
320
            $schema = $this->defaultSchema;
321
        }
322
        $sql = <<<'SQL'
323
SELECT c.relname AS table_name
324
FROM pg_class c
325
INNER JOIN pg_namespace ns ON ns.oid = c.relnamespace
326
WHERE ns.nspname = :schemaName AND (c.relkind = 'v' OR c.relkind = 'm')
327
ORDER BY c.relname
328
SQL;
329
        return $this->db->createCommand($sql, [':schemaName' => $schema])->queryColumn();
330
    }
331
332
    /**
333
     * Collects the foreign key column details for the given table.
334
     * @param TableSchema $table the table metadata
335
     */
336 310
    protected function findConstraints($table)
337
    {
338 310
        $tableName = $this->quoteValue($table->name);
339 310
        $tableSchema = $this->quoteValue($table->schemaName);
340
341
        //We need to extract the constraints de hard way since:
342
        //http://www.postgresql.org/message-id/[email protected]
343
344
        $sql = <<<SQL
345
select
346
    ct.conname as constraint_name,
347
    a.attname as column_name,
348
    fc.relname as foreign_table_name,
349
    fns.nspname as foreign_table_schema,
350
    fa.attname as foreign_column_name
351
from
352
    (SELECT ct.conname, ct.conrelid, ct.confrelid, ct.conkey, ct.contype, ct.confkey, generate_subscripts(ct.conkey, 1) AS s
353
       FROM pg_constraint ct
354
    ) AS ct
355
    inner join pg_class c on c.oid=ct.conrelid
356
    inner join pg_namespace ns on c.relnamespace=ns.oid
357
    inner join pg_attribute a on a.attrelid=ct.conrelid and a.attnum = ct.conkey[ct.s]
358
    left join pg_class fc on fc.oid=ct.confrelid
359
    left join pg_namespace fns on fc.relnamespace=fns.oid
360
    left join pg_attribute fa on fa.attrelid=ct.confrelid and fa.attnum = ct.confkey[ct.s]
361
where
362
    ct.contype='f'
363 310
    and c.relname={$tableName}
364 310
    and ns.nspname={$tableSchema}
365
order by
366
    fns.nspname, fc.relname, a.attnum
367
SQL;
368
369 310
        $constraints = [];
370 310
        foreach ($this->db->createCommand($sql)->queryAll() as $constraint) {
371 151
            if ($this->db->slavePdo->getAttribute(\PDO::ATTR_CASE) === \PDO::CASE_UPPER) {
372
                $constraint = array_change_key_case($constraint, CASE_LOWER);
373
            }
374 151
            if ($constraint['foreign_table_schema'] !== $this->defaultSchema) {
375
                $foreignTable = $constraint['foreign_table_schema'] . '.' . $constraint['foreign_table_name'];
376
            } else {
377 151
                $foreignTable = $constraint['foreign_table_name'];
378
            }
379 151
            $name = $constraint['constraint_name'];
380 151
            if (!isset($constraints[$name])) {
381 151
                $constraints[$name] = [
382 151
                    'tableName' => $foreignTable,
383
                    'columns' => [],
384
                ];
385
            }
386 151
            $constraints[$name]['columns'][$constraint['column_name']] = $constraint['foreign_column_name'];
387
        }
388 310
        foreach ($constraints as $name => $constraint) {
389 151
            $table->foreignKeys[$name] = array_merge([$constraint['tableName']], $constraint['columns']);
390
        }
391 310
    }
392
393
    /**
394
     * Gets information about given table unique indexes.
395
     * @param TableSchema $table the table metadata
396
     * @return array with index and column names
397
     */
398 1
    protected function getUniqueIndexInformation($table)
399
    {
400
        $sql = <<<'SQL'
401 1
SELECT
402
    i.relname as indexname,
403
    pg_get_indexdef(idx.indexrelid, k + 1, TRUE) AS columnname
404
FROM (
405
  SELECT *, generate_subscripts(indkey, 1) AS k
406
  FROM pg_index
407
) idx
408
INNER JOIN pg_class i ON i.oid = idx.indexrelid
409
INNER JOIN pg_class c ON c.oid = idx.indrelid
410
INNER JOIN pg_namespace ns ON c.relnamespace = ns.oid
411
WHERE idx.indisprimary = FALSE AND idx.indisunique = TRUE
412
AND c.relname = :tableName AND ns.nspname = :schemaName
413
ORDER BY i.relname, k
414
SQL;
415
416 1
        return $this->db->createCommand($sql, [
417 1
            ':schemaName' => $table->schemaName,
418 1
            ':tableName' => $table->name,
419 1
        ])->queryAll();
420
    }
421
422
    /**
423
     * Returns all unique indexes for the given table.
424
     *
425
     * Each array element is of the following structure:
426
     *
427
     * ```php
428
     * [
429
     *     'IndexName1' => ['col1' [, ...]],
430
     *     'IndexName2' => ['col2' [, ...]],
431
     * ]
432
     * ```
433
     *
434
     * @param TableSchema $table the table metadata
435
     * @return array all unique indexes for the given table.
436
     */
437 1
    public function findUniqueIndexes($table)
438
    {
439 1
        $uniqueIndexes = [];
440
441 1
        $rows = $this->getUniqueIndexInformation($table);
442 1
        foreach ($rows as $row) {
443 1
            if ($this->db->slavePdo->getAttribute(\PDO::ATTR_CASE) === \PDO::CASE_UPPER) {
444
                $row = array_change_key_case($row, CASE_LOWER);
445
            }
446 1
            $column = $row['columnname'];
447 1
            if (!empty($column) && $column[0] === '"') {
448
                // postgres will quote names that are not lowercase-only
449
                // https://github.com/yiisoft/yii2/issues/10613
450 1
                $column = substr($column, 1, -1);
451
            }
452 1
            $uniqueIndexes[$row['indexname']][] = $column;
453
        }
454
455 1
        return $uniqueIndexes;
456
    }
457
458
    /**
459
     * Collects the metadata of table columns.
460
     * @param TableSchema $table the table metadata
461
     * @return bool whether the table exists in the database
462
     */
463 319
    protected function findColumns($table)
464
    {
465 319
        $tableName = $this->db->quoteValue($table->name);
466 319
        $schemaName = $this->db->quoteValue($table->schemaName);
467
        $sql = <<<SQL
468
SELECT
469
    d.nspname AS table_schema,
470
    c.relname AS table_name,
471
    a.attname AS column_name,
472
    COALESCE(td.typname, tb.typname, t.typname) AS data_type,
473
    COALESCE(td.typtype, tb.typtype, t.typtype) AS type_type,
474
    a.attlen AS character_maximum_length,
475
    pg_catalog.col_description(c.oid, a.attnum) AS column_comment,
476
    a.atttypmod AS modifier,
477
    a.attnotnull = false AS is_nullable,
478
    CAST(pg_get_expr(ad.adbin, ad.adrelid) AS varchar) AS column_default,
479
    coalesce(pg_get_expr(ad.adbin, ad.adrelid) ~ 'nextval',false) AS is_autoinc,
480
    CASE WHEN COALESCE(td.typtype, tb.typtype, t.typtype) = 'e'::char
481
        THEN array_to_string((SELECT array_agg(enumlabel) FROM pg_enum WHERE enumtypid = COALESCE(td.oid, tb.oid, a.atttypid))::varchar[], ',')
482
        ELSE NULL
483
    END AS enum_values,
484
    CASE atttypid
485
         WHEN 21 /*int2*/ THEN 16
486
         WHEN 23 /*int4*/ THEN 32
487
         WHEN 20 /*int8*/ THEN 64
488
         WHEN 1700 /*numeric*/ THEN
489
              CASE WHEN atttypmod = -1
490
               THEN null
491
               ELSE ((atttypmod - 4) >> 16) & 65535
492
               END
493
         WHEN 700 /*float4*/ THEN 24 /*FLT_MANT_DIG*/
494
         WHEN 701 /*float8*/ THEN 53 /*DBL_MANT_DIG*/
495
         ELSE null
496
      END   AS numeric_precision,
497
      CASE
498
        WHEN atttypid IN (21, 23, 20) THEN 0
499
        WHEN atttypid IN (1700) THEN
500
        CASE
501
            WHEN atttypmod = -1 THEN null
502
            ELSE (atttypmod - 4) & 65535
503
        END
504
           ELSE null
505
      END AS numeric_scale,
506
    CAST(
507
             information_schema._pg_char_max_length(information_schema._pg_truetypid(a, t), information_schema._pg_truetypmod(a, t))
508
             AS numeric
509
    ) AS size,
510
    a.attnum = any (ct.conkey) as is_pkey,
511
    COALESCE(NULLIF(a.attndims, 0), NULLIF(t.typndims, 0), (t.typcategory='A')::int) AS dimension
512
FROM
513
    pg_class c
514
    LEFT JOIN pg_attribute a ON a.attrelid = c.oid
515
    LEFT JOIN pg_attrdef ad ON a.attrelid = ad.adrelid AND a.attnum = ad.adnum
516
    LEFT JOIN pg_type t ON a.atttypid = t.oid
517
    LEFT JOIN pg_type tb ON (a.attndims > 0 OR t.typcategory='A') AND t.typelem > 0 AND t.typelem = tb.oid OR t.typbasetype > 0 AND t.typbasetype = tb.oid
518
    LEFT JOIN pg_type td ON t.typndims > 0 AND t.typbasetype > 0 AND tb.typelem = td.oid
519
    LEFT JOIN pg_namespace d ON d.oid = c.relnamespace
520
    LEFT JOIN pg_constraint ct ON ct.conrelid = c.oid AND ct.contype = 'p'
521
WHERE
522
    a.attnum > 0 AND t.typname != ''
523 319
    AND c.relname = {$tableName}
524 319
    AND d.nspname = {$schemaName}
525
ORDER BY
526
    a.attnum;
527
SQL;
528 319
        $columns = $this->db->createCommand($sql)->queryAll();
529 319
        if (empty($columns)) {
530 21
            return false;
531
        }
532 310
        foreach ($columns as $column) {
533 310
            if ($this->db->slavePdo->getAttribute(\PDO::ATTR_CASE) === \PDO::CASE_UPPER) {
534
                $column = array_change_key_case($column, CASE_LOWER);
535
            }
536 310
            $column = $this->loadColumnSchema($column);
537 310
            $table->columns[$column->name] = $column;
538 310
            if ($column->isPrimaryKey) {
539 275
                $table->primaryKey[] = $column->name;
540 275
                if ($table->sequenceName === null && preg_match("/nextval\\('\"?\\w+\"?\.?\"?\\w+\"?'(::regclass)?\\)/", $column->defaultValue) === 1) {
541 152
                    $table->sequenceName = preg_replace(['/nextval/', '/::/', '/regclass/', '/\'\)/', '/\(\'/'], '', $column->defaultValue);
0 ignored issues
show
Documentation Bug introduced by
It seems like preg_replace(array('/nex... $column->defaultValue) can also be of type array<integer,string>. However, the property $sequenceName is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
542
                }
543 275
                $column->defaultValue = null;
544 308
            } elseif ($column->defaultValue) {
545 112
                if ($column->type === 'timestamp' && $column->defaultValue === 'now()') {
546 30
                    $column->defaultValue = new Expression($column->defaultValue);
547 112
                } elseif ($column->type === 'boolean') {
548 106
                    $column->defaultValue = ($column->defaultValue === 'true');
549 37
                } elseif (stripos($column->dbType, 'bit') === 0 || stripos($column->dbType, 'varbit') === 0) {
550 30
                    $column->defaultValue = bindec(trim($column->defaultValue, 'B\''));
551 37
                } elseif (preg_match("/^'(.*?)'::/", $column->defaultValue, $matches)) {
552 31
                    $column->defaultValue = $column->phpTypecast($matches[1]);
553 36
                } elseif (preg_match('/^(\()?(.*?)(?(1)\))(?:::.+)?$/', $column->defaultValue, $matches)) {
554 36
                    if ($matches[2] === 'NULL') {
555 7
                        $column->defaultValue = null;
556
                    } else {
557 36
                        $column->defaultValue = $column->phpTypecast($matches[2]);
558
                    }
559
                } else {
560 310
                    $column->defaultValue = $column->phpTypecast($column->defaultValue);
561
                }
562
            }
563
        }
564
565 310
        return true;
566
    }
567
568
    /**
569
     * Loads the column information into a [[ColumnSchema]] object.
570
     * @param array $info column information
571
     * @return ColumnSchema the column schema object
572
     */
573 310
    protected function loadColumnSchema($info)
574
    {
575
        /** @var ColumnSchema $column */
576 310
        $column = $this->createColumnSchema();
577 310
        $column->allowNull = $info['is_nullable'];
578 310
        $column->autoIncrement = $info['is_autoinc'];
579 310
        $column->comment = $info['column_comment'];
580 310
        $column->dbType = $info['data_type'];
581 310
        $column->defaultValue = $info['column_default'];
582 310
        $column->enumValues = ($info['enum_values'] !== null) ? explode(',', str_replace(["''"], ["'"], $info['enum_values'])) : null;
0 ignored issues
show
Documentation Bug introduced by
It seems like $info['enum_values'] !==...'enum_values'])) : null can be null. However, the property $enumValues is declared as array. Maybe change the type of the property to array|null or add a type check?

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

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

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

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

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

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

Loading history...
583 310
        $column->unsigned = false; // has no meaning in PG
584 310
        $column->isPrimaryKey = $info['is_pkey'];
585 310
        $column->name = $info['column_name'];
586 310
        $column->precision = $info['numeric_precision'];
587 310
        $column->scale = $info['numeric_scale'];
588 310
        $column->size = $info['size'] === null ? null : (int) $info['size'];
589 310
        $column->dimension = (int)$info['dimension'];
590 310
        if (isset($this->typeMap[$column->dbType])) {
591 310
            $column->type = $this->typeMap[$column->dbType];
592
        } else {
593
            $column->type = self::TYPE_STRING;
594
        }
595 310
        $column->phpType = $this->getColumnPhpType($column);
596
597 310
        return $column;
598
    }
599
600
    /**
601
     * {@inheritdoc}
602
     */
603 35
    public function insert($table, $columns)
604
    {
605 35
        $params = [];
606 35
        $sql = $this->db->getQueryBuilder()->insert($table, $columns, $params);
607 35
        $returnColumns = $this->getTableSchema($table)->primaryKey;
608 35
        if (!empty($returnColumns)) {
609 24
            $returning = [];
610 24
            foreach ((array) $returnColumns as $name) {
611 24
                $returning[] = $this->quoteColumnName($name);
612
            }
613 24
            $sql .= ' RETURNING ' . implode(', ', $returning);
614
        }
615
616 35
        $command = $this->db->createCommand($sql, $params);
617 35
        $command->prepare(false);
618 35
        $result = $command->queryOne();
619
620 35
        return !$command->pdoStatement->rowCount() ? false : $result;
621
    }
622
623
    /**
624
     * Loads multiple types of constraints and returns the specified ones.
625
     * @param string $tableName table name.
626
     * @param string $returnType return type:
627
     * - primaryKey
628
     * - foreignKeys
629
     * - uniques
630
     * - checks
631
     * @return mixed constraints.
632
     */
633 65
    private function loadTableConstraints($tableName, $returnType)
634
    {
635 65
        static $sql = <<<'SQL'
636
SELECT
637
    "c"."conname" AS "name",
638
    "a"."attname" AS "column_name",
639
    "c"."contype" AS "type",
640
    "ftcns"."nspname" AS "foreign_table_schema",
641
    "ftc"."relname" AS "foreign_table_name",
642
    "fa"."attname" AS "foreign_column_name",
643
    "c"."confupdtype" AS "on_update",
644
    "c"."confdeltype" AS "on_delete",
645
    "c"."consrc" AS "check_expr"
646
FROM "pg_class" AS "tc"
647
INNER JOIN "pg_namespace" AS "tcns"
648
    ON "tcns"."oid" = "tc"."relnamespace"
649
INNER JOIN "pg_constraint" AS "c"
650
    ON "c"."conrelid" = "tc"."oid"
651
INNER JOIN "pg_attribute" AS "a"
652
    ON "a"."attrelid" = "c"."conrelid" AND "a"."attnum" = ANY ("c"."conkey")
653
LEFT JOIN "pg_class" AS "ftc"
654
    ON "ftc"."oid" = "c"."confrelid"
655
LEFT JOIN "pg_namespace" AS "ftcns"
656
    ON "ftcns"."oid" = "ftc"."relnamespace"
657
LEFT JOIN "pg_attribute" "fa"
658
    ON "fa"."attrelid" = "c"."confrelid" AND "fa"."attnum" = ANY ("c"."confkey")
659
WHERE "tcns"."nspname" = :schemaName AND "tc"."relname" = :tableName
660
ORDER BY "a"."attnum" ASC, "fa"."attnum" ASC
661
SQL;
662 65
        static $actionTypes = [
663
            'a' => 'NO ACTION',
664
            'r' => 'RESTRICT',
665
            'c' => 'CASCADE',
666
            'n' => 'SET NULL',
667
            'd' => 'SET DEFAULT',
668
        ];
669
670 65
        $resolvedName = $this->resolveTableName($tableName);
671 65
        $constraints = $this->db->createCommand($sql, [
672 65
            ':schemaName' => $resolvedName->schemaName,
673 65
            ':tableName' => $resolvedName->name,
674 65
        ])->queryAll();
675 65
        $constraints = $this->normalizePdoRowKeyCase($constraints, true);
676 65
        $constraints = ArrayHelper::index($constraints, null, ['type', 'name']);
677
        $result = [
678 65
            'primaryKey' => null,
679
            'foreignKeys' => [],
680
            'uniques' => [],
681
            'checks' => [],
682
        ];
683 65
        foreach ($constraints as $type => $names) {
684 65
            foreach ($names as $name => $constraint) {
685
                switch ($type) {
686 65
                    case 'p':
687 50
                        $result['primaryKey'] = new Constraint([
688 50
                            'name' => $name,
689 50
                            'columnNames' => ArrayHelper::getColumn($constraint, 'column_name'),
690
                        ]);
691 50
                        break;
692 59
                    case 'f':
693 13
                        $result['foreignKeys'][] = new ForeignKeyConstraint([
694 13
                            'name' => $name,
695 13
                            'columnNames' => array_keys(array_count_values(ArrayHelper::getColumn($constraint, 'column_name'))),
696 13
                            'foreignSchemaName' => $constraint[0]['foreign_table_schema'],
697 13
                            'foreignTableName' => $constraint[0]['foreign_table_name'],
698 13
                            'foreignColumnNames' => array_keys(array_count_values(ArrayHelper::getColumn($constraint, 'foreign_column_name'))),
699 13
                            'onDelete' => isset($actionTypes[$constraint[0]['on_delete']]) ? $actionTypes[$constraint[0]['on_delete']] : null,
700 13
                            'onUpdate' => isset($actionTypes[$constraint[0]['on_update']]) ? $actionTypes[$constraint[0]['on_update']] : null,
701
                        ]);
702 13
                        break;
703 47
                    case 'u':
704 46
                        $result['uniques'][] = new Constraint([
705 46
                            'name' => $name,
706 46
                            'columnNames' => ArrayHelper::getColumn($constraint, 'column_name'),
707
                        ]);
708 46
                        break;
709 10
                    case 'c':
710 10
                        $result['checks'][] = new CheckConstraint([
711 10
                            'name' => $name,
712 10
                            'columnNames' => ArrayHelper::getColumn($constraint, 'column_name'),
713 10
                            'expression' => $constraint[0]['check_expr'],
714
                        ]);
715 65
                        break;
716
                }
717
            }
718
        }
719 65
        foreach ($result as $type => $data) {
720 65
            $this->setTableMetadata($tableName, $type, $data);
721
        }
722
723 65
        return $result[$returnType];
724
    }
725
}
726