Skip to content

Commit

Permalink
Batch insert with empty columns (#251)
Browse files Browse the repository at this point in the history
  • Loading branch information
Tigrov authored Jan 9, 2024
1 parent 0b28ffb commit 704f56c
Show file tree
Hide file tree
Showing 4 changed files with 24 additions and 39 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

- Enh #248: Change property `Schema::$typeMap` to constant `Schema::TYPE_MAP` (@Tigrov)
- Bug #250: Fix `Command::insertWithReturningPks()` method for table without primary keys (@Tigrov)
- Enh #251: Allow to use `DMLQueryBuilderInterface::batchInsert()` method with empty columns (@Tigrov)

## 1.2.0 November 12, 2023

Expand Down
48 changes: 11 additions & 37 deletions src/DMLQueryBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
use Yiisoft\Db\Exception\InvalidConfigException;
use Yiisoft\Db\Exception\NotSupportedException;
use Yiisoft\Db\Expression\Expression;
use Yiisoft\Db\Expression\ExpressionInterface;
use Yiisoft\Db\Query\QueryInterface;
use Yiisoft\Db\QueryBuilder\AbstractDMLQueryBuilder;

Expand All @@ -35,49 +34,24 @@ public function batchInsert(string $table, array $columns, iterable $rows, array
return '';
}

$values = [];
$columns = $this->getNormalizeColumnNames('', $columns);
$columnNames = array_values($columns);
$columnKeys = array_fill_keys($columnNames, false);
$columnSchemas = $this->schema->getTableSchema($table)?->getColumns() ?? [];

foreach ($rows as $row) {
$i = 0;
$placeholders = $columnKeys;

foreach ($row as $key => $value) {
/** @psalm-suppress MixedArrayTypeCoercion */
$columnName = $columns[$key] ?? (isset($columnKeys[$key]) ? $key : $columnNames[$i] ?? $i);
/** @psalm-suppress MixedArrayTypeCoercion */
if (isset($columnSchemas[$columnName])) {
$value = $columnSchemas[$columnName]->dbTypecast($value);
}

if ($value instanceof ExpressionInterface) {
$placeholders[$columnName] = $this->queryBuilder->buildExpression($value, $params);
} else {
$placeholders[$columnName] = $this->queryBuilder->bindParam($value, $params);
}

++$i;
}

$values[] = '(' . implode(', ', $placeholders) . ')';
}
$columns = $this->extractColumnNames($rows, $columns);
$values = $this->prepareBatchInsertValues($table, $rows, $columns, $params);

if (empty($values)) {
return '';
}

$columnNames = array_map(
[$this->quoter, 'quoteColumnName'],
$columnNames,
);
$tableAndColumns = ' INTO ' . $this->quoter->quoteTableName($table);

if (count($columns) > 0) {
$quotedColumnNames = array_map([$this->quoter, 'quoteColumnName'], $columns);

$tableAndColumns .= ' (' . implode(', ', $quotedColumnNames) . ')';
}

$tableAndColumns = ' INTO ' . $this->quoter->quoteTableName($table)
. ' (' . implode(', ', $columnNames) . ') VALUES ';
$tableAndColumns .= ' VALUES ';

return 'INSERT ALL ' . $tableAndColumns . implode($tableAndColumns, $values) . ' SELECT 1 FROM SYS.DUAL';
return 'INSERT ALL' . $tableAndColumns . implode($tableAndColumns, $values) . ' SELECT 1 FROM SYS.DUAL';
}

/**
Expand Down
11 changes: 10 additions & 1 deletion tests/Provider/CommandProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public static function batchInsert(): array
$batchInsert = parent::batchInsert();

$batchInsert['multirow']['expected'] = <<<SQL
INSERT ALL INTO "type" ("int_col", "float_col", "char_col", "bool_col") VALUES (:qp0, :qp1, :qp2, :qp3) INTO "type" ("int_col", "float_col", "char_col", "bool_col") VALUES (:qp4, :qp5, :qp6, :qp7) SELECT 1 FROM SYS.DUAL
INSERT ALL INTO "type" ("int_col", "float_col", "char_col", "bool_col") VALUES (:qp0, :qp1, :qp2, :qp3) INTO "type" ("int_col", "float_col", "char_col", "bool_col") VALUES (:qp4, :qp5, :qp6, :qp7) SELECT 1 FROM SYS.DUAL
SQL;
$batchInsert['multirow']['expectedParams'][':qp3'] = '1';
$batchInsert['multirow']['expectedParams'][':qp7'] = '0';
Expand Down Expand Up @@ -55,6 +55,15 @@ public static function batchInsert(): array
'with shuffled indexes of values' => [
':qp0' => '1',
],
'empty columns and associative values' => [
':qp3' => '1',
],
'empty columns and objects' => [
':qp3' => '1',
],
'empty columns and Traversable' => [
':qp3' => '1',
],
];

foreach ($replaceParams as $key => $expectedParams) {
Expand Down
3 changes: 2 additions & 1 deletion tests/Provider/QueryBuilderProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,12 @@ public static function batchInsert(): array
DbHelper::changeSqlForOracleBatchInsert($batchInsert['bool-false, bool2-null']['expected']);

$batchInsert['wrong']['expected'] = <<<SQL
INSERT ALL INTO {{%type}} ("float_col", "time") VALUES (:qp0, now()) INTO {{%type}} ("float_col", "time") VALUES (:qp1, now()) SELECT 1 FROM SYS.DUAL
INSERT ALL INTO {{%type}} ("float_col", "time") VALUES (:qp0, now()) INTO {{%type}} ("float_col", "time") VALUES (:qp1, now()) SELECT 1 FROM SYS.DUAL
SQL;

DbHelper::changeSqlForOracleBatchInsert($batchInsert['bool-false, time-now()']['expected']);
DbHelper::changeSqlForOracleBatchInsert($batchInsert['column table names are not checked']['expected']);
DbHelper::changeSqlForOracleBatchInsert($batchInsert['empty columns and non-exists table']['expected']);

$batchInsert['bool-false, bool2-null']['expectedParams'][':qp0'] = '0';
$batchInsert['bool-false, time-now()']['expectedParams'][':qp0'] = '0';
Expand Down

0 comments on commit 704f56c

Please sign in to comment.