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.

QueryBuilder::truncateTable()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * @link https://www.yiiframework.com/
4
 * @copyright Copyright (c) 2008 Yii Software LLC
5
 * @license https://www.yiiframework.com/license/
6
 */
7
8
namespace yii\db\pgsql;
9
10
use yii\base\InvalidArgumentException;
11
use yii\db\Constraint;
12
use yii\db\Expression;
13
use yii\db\ExpressionInterface;
14
use yii\db\Query;
15
use yii\db\PdoValue;
16
use yii\helpers\StringHelper;
17
18
/**
19
 * QueryBuilder is the query builder for PostgreSQL databases.
20
 *
21
 * @author Gevik Babakhani <[email protected]>
22
 * @since 2.0
23
 */
24
class QueryBuilder extends \yii\db\QueryBuilder
25
{
26
    /**
27
     * Defines a UNIQUE index for [[createIndex()]].
28
     * @since 2.0.6
29
     */
30
    const INDEX_UNIQUE = 'unique';
31
    /**
32
     * Defines a B-tree index for [[createIndex()]].
33
     * @since 2.0.6
34
     */
35
    const INDEX_B_TREE = 'btree';
36
    /**
37
     * Defines a hash index for [[createIndex()]].
38
     * @since 2.0.6
39
     */
40
    const INDEX_HASH = 'hash';
41
    /**
42
     * Defines a GiST index for [[createIndex()]].
43
     * @since 2.0.6
44
     */
45
    const INDEX_GIST = 'gist';
46
    /**
47
     * Defines a GIN index for [[createIndex()]].
48
     * @since 2.0.6
49
     */
50
    const INDEX_GIN = 'gin';
51
52
    /**
53
     * @var array mapping from abstract column types (keys) to physical column types (values).
54
     */
55
    public $typeMap = [
56
        Schema::TYPE_PK => 'serial NOT NULL PRIMARY KEY',
0 ignored issues
show
Bug introduced by
The type yii\db\pgsql\Schema was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
57
        Schema::TYPE_UPK => 'serial NOT NULL PRIMARY KEY',
58
        Schema::TYPE_BIGPK => 'bigserial NOT NULL PRIMARY KEY',
59
        Schema::TYPE_UBIGPK => 'bigserial NOT NULL PRIMARY KEY',
60
        Schema::TYPE_CHAR => 'char(1)',
61
        Schema::TYPE_STRING => 'varchar(255)',
62
        Schema::TYPE_TEXT => 'text',
63
        Schema::TYPE_TINYINT => 'smallint',
64
        Schema::TYPE_SMALLINT => 'smallint',
65
        Schema::TYPE_INTEGER => 'integer',
66
        Schema::TYPE_BIGINT => 'bigint',
67
        Schema::TYPE_FLOAT => 'double precision',
68
        Schema::TYPE_DOUBLE => 'double precision',
69
        Schema::TYPE_DECIMAL => 'numeric(10,0)',
70
        Schema::TYPE_DATETIME => 'timestamp(0)',
71
        Schema::TYPE_TIMESTAMP => 'timestamp(0)',
72
        Schema::TYPE_TIME => 'time(0)',
73
        Schema::TYPE_DATE => 'date',
74
        Schema::TYPE_BINARY => 'bytea',
75
        Schema::TYPE_BOOLEAN => 'boolean',
76
        Schema::TYPE_MONEY => 'numeric(19,4)',
77
        Schema::TYPE_JSON => 'jsonb',
78
    ];
79
80
81
    /**
82
     * {@inheritdoc}
83
     */
84 637
    protected function defaultConditionClasses()
85
    {
86 637
        return array_merge(parent::defaultConditionClasses(), [
87 637
            'ILIKE' => 'yii\db\conditions\LikeCondition',
88 637
            'NOT ILIKE' => 'yii\db\conditions\LikeCondition',
89 637
            'OR ILIKE' => 'yii\db\conditions\LikeCondition',
90 637
            'OR NOT ILIKE' => 'yii\db\conditions\LikeCondition',
91 637
        ]);
92
    }
93
94
    /**
95
     * {@inheritdoc}
96
     */
97 637
    protected function defaultExpressionBuilders()
98
    {
99 637
        return array_merge(parent::defaultExpressionBuilders(), [
100 637
            'yii\db\ArrayExpression' => 'yii\db\pgsql\ArrayExpressionBuilder',
101 637
            'yii\db\JsonExpression' => 'yii\db\pgsql\JsonExpressionBuilder',
102 637
        ]);
103
    }
104
105
    /**
106
     * Builds a SQL statement for creating a new index.
107
     * @param string $name the name of the index. The name will be properly quoted by the method.
108
     * @param string $table the table that the new index will be created for. The table name will be properly quoted by the method.
109
     * @param string|array $columns the column(s) that should be included in the index. If there are multiple columns,
110
     * separate them with commas or use an array to represent them. Each column name will be properly quoted
111
     * by the method, unless a parenthesis is found in the name.
112
     * @param bool|string $unique whether to make this a UNIQUE index constraint. You can pass `true` or [[INDEX_UNIQUE]] to create
113
     * a unique index, `false` to make a non-unique index using the default index type, or one of the following constants to specify
114
     * the index method to use: [[INDEX_B_TREE]], [[INDEX_HASH]], [[INDEX_GIST]], [[INDEX_GIN]].
115
     * @return string the SQL statement for creating a new index.
116
     * @see https://www.postgresql.org/docs/8.2/sql-createindex.html
117
     */
118 6
    public function createIndex($name, $table, $columns, $unique = false)
119
    {
120 6
        if ($unique === self::INDEX_UNIQUE || $unique === true) {
121 4
            $index = false;
122 4
            $unique = true;
123
        } else {
124 3
            $index = $unique;
125 3
            $unique = false;
126
        }
127
128 6
        return ($unique ? 'CREATE UNIQUE INDEX ' : 'CREATE INDEX ') .
129 6
        $this->db->quoteTableName($name) . ' ON ' .
130 6
        $this->db->quoteTableName($table) .
131 6
        ($index !== false ? " USING $index" : '') .
132 6
        ' (' . $this->buildColumns($columns) . ')';
133
    }
134
135
    /**
136
     * Builds a SQL statement for dropping an index.
137
     * @param string $name the name of the index to be dropped. The name will be properly quoted by the method.
138
     * @param string $table the table whose index is to be dropped. The name will be properly quoted by the method.
139
     * @return string the SQL statement for dropping an index.
140
     */
141 3
    public function dropIndex($name, $table)
142
    {
143 3
        if (strpos($table, '.') !== false && strpos($name, '.') === false) {
144 1
            if (strpos($table, '{{') !== false) {
145 1
                $table = preg_replace('/\\{\\{(.*?)\\}\\}/', '\1', $table);
146 1
                list($schema, $table) = explode('.', $table);
147 1
                if (strpos($schema, '%') === false) {
148 1
                    $name = $schema . '.' . $name;
149
                } else {
150 1
                    $name = '{{' . $schema . '.' . $name . '}}';
151
                }
152
            } else {
153
                list($schema) = explode('.', $table);
154
                $name = $schema . '.' . $name;
155
            }
156
        }
157 3
        return 'DROP INDEX ' . $this->db->quoteTableName($name);
158
    }
159
160
    /**
161
     * Builds a SQL statement for renaming a DB table.
162
     * @param string $oldName the table to be renamed. The name will be properly quoted by the method.
163
     * @param string $newName the new table name. The name will be properly quoted by the method.
164
     * @return string the SQL statement for renaming a DB table.
165
     */
166 2
    public function renameTable($oldName, $newName)
167
    {
168 2
        return 'ALTER TABLE ' . $this->db->quoteTableName($oldName) . ' RENAME TO ' . $this->db->quoteTableName($newName);
169
    }
170
171
    /**
172
     * Creates a SQL statement for resetting the sequence value of a table's primary key.
173
     * The sequence will be reset such that the primary key of the next new row inserted
174
     * will have the specified value or 1.
175
     * @param string $tableName the name of the table whose primary key sequence will be reset
176
     * @param mixed $value the value for the primary key of the next new row inserted. If this is not set,
177
     * the next new row's primary key will have a value 1.
178
     * @return string the SQL statement for resetting sequence
179
     * @throws InvalidArgumentException if the table does not exist or there is no sequence associated with the table.
180
     */
181 7
    public function resetSequence($tableName, $value = null)
182
    {
183 7
        $table = $this->db->getTableSchema($tableName);
184 7
        if ($table !== null && $table->sequenceName !== null) {
185
            // c.f. https://www.postgresql.org/docs/8.1/functions-sequence.html
186 7
            $sequence = $this->db->quoteTableName($table->sequenceName);
187 7
            $tableName = $this->db->quoteTableName($tableName);
188 7
            if ($value === null) {
189 2
                $key = $this->db->quoteColumnName(reset($table->primaryKey));
190 2
                $value = "(SELECT COALESCE(MAX({$key}),0) FROM {$tableName})+1";
191
            } else {
192 7
                $value = (int) $value;
193
            }
194
195 7
            return "SELECT SETVAL('$sequence',$value,false)";
196
        } elseif ($table === null) {
197
            throw new InvalidArgumentException("Table not found: $tableName");
198
        }
199
200
        throw new InvalidArgumentException("There is not sequence associated with table '$tableName'.");
201
    }
202
203
    /**
204
     * Builds a SQL statement for enabling or disabling integrity check.
205
     * @param bool $check whether to turn on or off the integrity check.
206
     * @param string $schema the schema of the tables.
207
     * @param string $table the table name.
208
     * @return string the SQL statement for checking integrity
209
     */
210
    public function checkIntegrity($check = true, $schema = '', $table = '')
211
    {
212
        $enable = $check ? 'ENABLE' : 'DISABLE';
213
        $schema = $schema ?: $this->db->getSchema()->defaultSchema;
214
        $tableNames = $table ? [$table] : $this->db->getSchema()->getTableNames($schema);
215
        $viewNames = $this->db->getSchema()->getViewNames($schema);
0 ignored issues
show
Bug introduced by
The method getViewNames() does not exist on yii\db\Schema. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

215
        $viewNames = $this->db->getSchema()->/** @scrutinizer ignore-call */ getViewNames($schema);
Loading history...
216
        $tableNames = array_diff($tableNames, $viewNames);
217
        $command = '';
218
219
        foreach ($tableNames as $tableName) {
220
            $tableName = $this->db->quoteTableName("{$schema}.{$tableName}");
221
            $command .= "ALTER TABLE $tableName $enable TRIGGER ALL; ";
222
        }
223
224
        // enable to have ability to alter several tables
225
        $this->db->getMasterPdo()->setAttribute(\PDO::ATTR_EMULATE_PREPARES, true);
226
227
        return $command;
228
    }
229
230
    /**
231
     * Builds a SQL statement for truncating a DB table.
232
     * Explicitly restarts identity for PGSQL to be consistent with other databases which all do this by default.
233
     * @param string $table the table to be truncated. The name will be properly quoted by the method.
234
     * @return string the SQL statement for truncating a DB table.
235
     */
236 1
    public function truncateTable($table)
237
    {
238 1
        return 'TRUNCATE TABLE ' . $this->db->quoteTableName($table) . ' RESTART IDENTITY';
239
    }
240
241
    /**
242
     * Builds a SQL statement for changing the definition of a column.
243
     * @param string $table the table whose column is to be changed. The table name will be properly quoted by the method.
244
     * @param string $column the name of the column to be changed. The name will be properly quoted by the method.
245
     * @param string $type the new column type. The [[getColumnType()]] method will be invoked to convert abstract
246
     * column type (if any) into the physical one. Anything that is not recognized as abstract type will be kept
247
     * in the generated SQL. For example, 'string' will be turned into 'varchar(255)', while 'string not null'
248
     * will become 'varchar(255) not null'. You can also use PostgreSQL-specific syntax such as `SET NOT NULL`.
249
     * @return string the SQL statement for changing the definition of a column.
250
     */
251 2
    public function alterColumn($table, $column, $type)
252
    {
253 2
        $columnName = $this->db->quoteColumnName($column);
254 2
        $tableName = $this->db->quoteTableName($table);
255
256
        // https://github.com/yiisoft/yii2/issues/4492
257
        // https://www.postgresql.org/docs/9.1/sql-altertable.html
258 2
        if (preg_match('/^(DROP|SET|RESET)\s+/i', $type)) {
259 1
            return "ALTER TABLE {$tableName} ALTER COLUMN {$columnName} {$type}";
260
        }
261
262 2
        $type = 'TYPE ' . $this->getColumnType($type);
263
264 2
        $multiAlterStatement = [];
265 2
        $constraintPrefix = preg_replace('/[^a-z0-9_]/i', '', $table . '_' . $column);
266
267 2
        if (preg_match('/\s+DEFAULT\s+(["\']?\w*["\']?)/i', $type, $matches)) {
268 1
            $type = preg_replace('/\s+DEFAULT\s+(["\']?\w*["\']?)/i', '', $type);
269 1
            $multiAlterStatement[] = "ALTER COLUMN {$columnName} SET DEFAULT {$matches[1]}";
270
        } else {
271
            // safe to drop default even if there was none in the first place
272 2
            $multiAlterStatement[] = "ALTER COLUMN {$columnName} DROP DEFAULT";
273
        }
274
275 2
        $type = preg_replace('/\s+NOT\s+NULL/i', '', $type, -1, $count);
276 2
        if ($count) {
277 1
            $multiAlterStatement[] = "ALTER COLUMN {$columnName} SET NOT NULL";
278
        } else {
279
            // remove additional null if any
280 2
            $type = preg_replace('/\s+NULL/i', '', $type);
281
            // safe to drop not null even if there was none in the first place
282 2
            $multiAlterStatement[] = "ALTER COLUMN {$columnName} DROP NOT NULL";
283
        }
284
285 2
        if (preg_match('/\s+CHECK\s+\((.+)\)/i', $type, $matches)) {
286 1
            $type = preg_replace('/\s+CHECK\s+\((.+)\)/i', '', $type);
287 1
            $multiAlterStatement[] = "ADD CONSTRAINT {$constraintPrefix}_check CHECK ({$matches[1]})";
288
        }
289
290 2
        $type = preg_replace('/\s+UNIQUE/i', '', $type, -1, $count);
291 2
        if ($count) {
292 1
            $multiAlterStatement[] = "ADD UNIQUE ({$columnName})";
293
        }
294
295
        // add what's left at the beginning
296 2
        array_unshift($multiAlterStatement, "ALTER COLUMN {$columnName} {$type}");
297
298 2
        return 'ALTER TABLE ' . $tableName . ' ' . implode(', ', $multiAlterStatement);
299
    }
300
301
    /**
302
     * {@inheritdoc}
303
     */
304 214
    public function insert($table, $columns, &$params)
305
    {
306 214
        return parent::insert($table, $this->normalizeTableRowData($table, $columns), $params);
307
    }
308
309
    /**
310
     * {@inheritdoc}
311
     * @see https://www.postgresql.org/docs/9.5/static/sql-insert.html#SQL-ON-CONFLICT
312
     * @see https://stackoverflow.com/questions/1109061/insert-on-duplicate-update-in-postgresql/8702291#8702291
313
     */
314 41
    public function upsert($table, $insertColumns, $updateColumns, &$params)
315
    {
316 41
        $insertColumns = $this->normalizeTableRowData($table, $insertColumns);
317 41
        if (!is_bool($updateColumns)) {
318 7
            $updateColumns = $this->normalizeTableRowData($table, $updateColumns);
319
        }
320 41
        if (version_compare($this->db->getServerVersion(), '9.5', '<')) {
321
            return $this->oldUpsert($table, $insertColumns, $updateColumns, $params);
0 ignored issues
show
Bug introduced by
It seems like $updateColumns can also be of type yii\db\Query; however, parameter $updateColumns of yii\db\pgsql\QueryBuilder::oldUpsert() does only seem to accept array|boolean, maybe add an additional type check? ( Ignorable by Annotation )

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

321
            return $this->oldUpsert($table, $insertColumns, /** @scrutinizer ignore-type */ $updateColumns, $params);
Loading history...
322
        }
323
324 41
        return $this->newUpsert($table, $insertColumns, $updateColumns, $params);
0 ignored issues
show
Bug introduced by
It seems like $updateColumns can also be of type yii\db\Query; however, parameter $updateColumns of yii\db\pgsql\QueryBuilder::newUpsert() does only seem to accept array|boolean, maybe add an additional type check? ( Ignorable by Annotation )

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

324
        return $this->newUpsert($table, $insertColumns, /** @scrutinizer ignore-type */ $updateColumns, $params);
Loading history...
325
    }
326
327
    /**
328
     * [[upsert()]] implementation for PostgreSQL 9.5 or higher.
329
     * @param string $table
330
     * @param array|Query $insertColumns
331
     * @param array|bool $updateColumns
332
     * @param array $params
333
     * @return string
334
     */
335 41
    private function newUpsert($table, $insertColumns, $updateColumns, &$params)
336
    {
337 41
        $insertSql = $this->insert($table, $insertColumns, $params);
338 41
        list($uniqueNames, , $updateNames) = $this->prepareUpsertColumns($table, $insertColumns, $updateColumns);
339 41
        if (empty($uniqueNames)) {
340 3
            return $insertSql;
341
        }
342 38
        if ($updateNames === []) {
343
            // there are no columns to update
344 1
            $updateColumns = false;
345
        }
346
347 38
        if ($updateColumns === false) {
348 5
            return "$insertSql ON CONFLICT DO NOTHING";
349
        }
350
351 33
        if ($updateColumns === true) {
352 27
            $updateColumns = [];
353 27
            foreach ($updateNames as $name) {
354 27
                $updateColumns[$name] = new Expression('EXCLUDED.' . $this->db->quoteColumnName($name));
355
            }
356
        }
357 33
        list($updates, $params) = $this->prepareUpdateSets($table, $updateColumns, $params);
358 33
        return $insertSql . ' ON CONFLICT (' . implode(', ', $uniqueNames) . ') DO UPDATE SET ' . implode(', ', $updates);
359
    }
360
361
    /**
362
     * [[upsert()]] implementation for PostgreSQL older than 9.5.
363
     * @param string $table
364
     * @param array|Query $insertColumns
365
     * @param array|bool $updateColumns
366
     * @param array $params
367
     * @return string
368
     */
369
    private function oldUpsert($table, $insertColumns, $updateColumns, &$params)
370
    {
371
        /** @var Constraint[] $constraints */
372
        list($uniqueNames, $insertNames, $updateNames) = $this->prepareUpsertColumns($table, $insertColumns, $updateColumns, $constraints);
373
        if (empty($uniqueNames)) {
374
            return $this->insert($table, $insertColumns, $params);
375
        }
376
        if ($updateNames === []) {
377
            // there are no columns to update
378
            $updateColumns = false;
379
        }
380
381
        /** @var Schema $schema */
382
        $schema = $this->db->getSchema();
383
        if (!$insertColumns instanceof Query) {
384
            $tableSchema = $schema->getTableSchema($table);
385
            $columnSchemas = $tableSchema !== null ? $tableSchema->columns : [];
386
            foreach ($insertColumns as $name => $value) {
387
                // NULLs and numeric values must be type hinted in order to be used in SET assigments
388
                // NVM, let's cast them all
389
                if (isset($columnSchemas[$name])) {
390
                    $phName = self::PARAM_PREFIX . count($params);
391
                    $params[$phName] = $value;
392
                    $insertColumns[$name] = new Expression("CAST($phName AS {$columnSchemas[$name]->dbType})");
393
                }
394
            }
395
        }
396
        list(, $placeholders, $values, $params) = $this->prepareInsertValues($table, $insertColumns, $params);
397
        $updateCondition = ['or'];
398
        $insertCondition = ['or'];
399
        $quotedTableName = $schema->quoteTableName($table);
400
        foreach ($constraints as $constraint) {
401
            $constraintUpdateCondition = ['and'];
402
            $constraintInsertCondition = ['and'];
403
            foreach ($constraint->columnNames as $name) {
404
                $quotedName = $schema->quoteColumnName($name);
405
                $constraintUpdateCondition[] = "$quotedTableName.$quotedName=\"EXCLUDED\".$quotedName";
406
                $constraintInsertCondition[] = "\"upsert\".$quotedName=\"EXCLUDED\".$quotedName";
407
            }
408
            $updateCondition[] = $constraintUpdateCondition;
409
            $insertCondition[] = $constraintInsertCondition;
410
        }
411
        $withSql = 'WITH "EXCLUDED" (' . implode(', ', $insertNames)
412
            . ') AS (' . (!empty($placeholders) ? 'VALUES (' . implode(', ', $placeholders) . ')' : ltrim($values, ' ')) . ')';
413
        if ($updateColumns === false) {
414
            $selectSubQuery = (new Query())
415
                ->select(new Expression('1'))
416
                ->from($table)
417
                ->where($updateCondition);
418
            $insertSelectSubQuery = (new Query())
419
                ->select($insertNames)
420
                ->from('EXCLUDED')
421
                ->where(['not exists', $selectSubQuery]);
422
            $insertSql = $this->insert($table, $insertSelectSubQuery, $params);
423
            return "$withSql $insertSql";
424
        }
425
426
        if ($updateColumns === true) {
427
            $updateColumns = [];
428
            foreach ($updateNames as $name) {
429
                $quotedName = $this->db->quoteColumnName($name);
430
                if (strrpos($quotedName, '.') === false) {
431
                    $quotedName = '"EXCLUDED".' . $quotedName;
432
                }
433
                $updateColumns[$name] = new Expression($quotedName);
434
            }
435
        }
436
        list($updates, $params) = $this->prepareUpdateSets($table, $updateColumns, $params);
437
        $updateSql = 'UPDATE ' . $this->db->quoteTableName($table) . ' SET ' . implode(', ', $updates)
438
            . ' FROM "EXCLUDED" ' . $this->buildWhere($updateCondition, $params)
439
            . ' RETURNING ' . $this->db->quoteTableName($table) . '.*';
440
        $selectUpsertSubQuery = (new Query())
441
            ->select(new Expression('1'))
442
            ->from('upsert')
443
            ->where($insertCondition);
444
        $insertSelectSubQuery = (new Query())
445
            ->select($insertNames)
446
            ->from('EXCLUDED')
447
            ->where(['not exists', $selectUpsertSubQuery]);
448
        $insertSql = $this->insert($table, $insertSelectSubQuery, $params);
449
        return "$withSql, \"upsert\" AS ($updateSql) $insertSql";
450
    }
451
452
    /**
453
     * {@inheritdoc}
454
     */
455 33
    public function update($table, $columns, $condition, &$params)
456
    {
457 33
        return parent::update($table, $this->normalizeTableRowData($table, $columns), $condition, $params);
0 ignored issues
show
Bug introduced by
It seems like $this->normalizeTableRowData($table, $columns) can also be of type yii\db\Query; however, parameter $columns of yii\db\QueryBuilder::update() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

457
        return parent::update($table, /** @scrutinizer ignore-type */ $this->normalizeTableRowData($table, $columns), $condition, $params);
Loading history...
458
    }
459
460
    /**
461
     * Normalizes data to be saved into the table, performing extra preparations and type converting, if necessary.
462
     *
463
     * @param string $table the table that data will be saved into.
464
     * @param array|Query $columns the column data (name => value) to be saved into the table or instance
465
     * of [[yii\db\Query|Query]] to perform INSERT INTO ... SELECT SQL statement.
466
     * Passing of [[yii\db\Query|Query]] is available since version 2.0.11.
467
     * @return array|Query normalized columns
468
     * @since 2.0.9
469
     */
470 228
    private function normalizeTableRowData($table, $columns)
471
    {
472 228
        if ($columns instanceof Query) {
473 14
            return $columns;
474
        }
475
476 220
        if (($tableSchema = $this->db->getSchema()->getTableSchema($table)) !== null) {
477 220
            $columnSchemas = $tableSchema->columns;
478 220
            foreach ($columns as $name => $value) {
479 219
                if (isset($columnSchemas[$name]) && $columnSchemas[$name]->type === Schema::TYPE_BINARY && is_string($value)) {
480 53
                    $columns[$name] = new PdoValue($value, \PDO::PARAM_LOB); // explicitly setup PDO param type for binary column
481
                }
482
            }
483
        }
484
485 220
        return $columns;
486
    }
487
488
    /**
489
     * {@inheritdoc}
490
     */
491 22
    public function batchInsert($table, $columns, $rows, &$params = [])
492
    {
493 22
        if (empty($rows)) {
494 2
            return '';
495
        }
496
497 21
        $schema = $this->db->getSchema();
498 21
        if (($tableSchema = $schema->getTableSchema($table)) !== null) {
499 21
            $columnSchemas = $tableSchema->columns;
500
        } else {
501
            $columnSchemas = [];
502
        }
503
504 21
        $values = [];
505 21
        foreach ($rows as $row) {
506 19
            $vs = [];
507 19
            foreach ($row as $i => $value) {
508 19
                if (isset($columns[$i], $columnSchemas[$columns[$i]])) {
509 16
                    $value = $columnSchemas[$columns[$i]]->dbTypecast($value);
510
                }
511 19
                if (is_string($value)) {
512 8
                    $value = $schema->quoteValue($value);
513 14
                } elseif (is_float($value)) {
514
                    // ensure type cast always has . as decimal separator in all locales
515 1
                    $value = StringHelper::floatToString($value);
516 14
                } elseif ($value === true) {
517 4
                    $value = 'TRUE';
518 14
                } elseif ($value === false) {
519 6
                    $value = 'FALSE';
520 11
                } elseif ($value === null) {
521 4
                    $value = 'NULL';
522 8
                } elseif ($value instanceof ExpressionInterface) {
523 6
                    $value = $this->buildExpression($value, $params);
524
                }
525 19
                $vs[] = $value;
526
            }
527 19
            $values[] = '(' . implode(', ', $vs) . ')';
528
        }
529 21
        if (empty($values)) {
530 2
            return '';
531
        }
532
533 19
        foreach ($columns as $i => $name) {
534 18
            $columns[$i] = $schema->quoteColumnName($name);
535
        }
536
537 19
        return 'INSERT INTO ' . $schema->quoteTableName($table)
538 19
        . ' (' . implode(', ', $columns) . ') VALUES ' . implode(', ', $values);
539
    }
540
}
541