diff --git a/CHANGELOG.md b/CHANGELOG.md index ffe011f..cff751e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/src/DMLQueryBuilder.php b/src/DMLQueryBuilder.php index 4df2fe5..077954c 100644 --- a/src/DMLQueryBuilder.php +++ b/src/DMLQueryBuilder.php @@ -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; @@ -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'; } /** diff --git a/tests/Provider/CommandProvider.php b/tests/Provider/CommandProvider.php index d97a322..2b44353 100644 --- a/tests/Provider/CommandProvider.php +++ b/tests/Provider/CommandProvider.php @@ -25,7 +25,7 @@ public static function batchInsert(): array $batchInsert = parent::batchInsert(); $batchInsert['multirow']['expected'] = << [ ':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) { diff --git a/tests/Provider/QueryBuilderProvider.php b/tests/Provider/QueryBuilderProvider.php index 9a38538..94e7332 100644 --- a/tests/Provider/QueryBuilderProvider.php +++ b/tests/Provider/QueryBuilderProvider.php @@ -51,11 +51,12 @@ public static function batchInsert(): array DbHelper::changeSqlForOracleBatchInsert($batchInsert['bool-false, bool2-null']['expected']); $batchInsert['wrong']['expected'] = <<