diff --git a/src/QueryBuilder/AbstractDMLQueryBuilder.php b/src/QueryBuilder/AbstractDMLQueryBuilder.php index 03ff04fdf..40809c8ae 100644 --- a/src/QueryBuilder/AbstractDMLQueryBuilder.php +++ b/src/QueryBuilder/AbstractDMLQueryBuilder.php @@ -67,6 +67,14 @@ public function batchInsert(string $table, array $columns, iterable $rows, array return ''; } + while ($rows instanceof IteratorAggregate) { + $rows = $rows->getIterator(); + } + + if ($rows instanceof Iterator && !$rows->valid()) { + return ''; + } + $columns = $this->extractColumnNames($rows, $columns); $values = $this->prepareBatchInsertValues($table, $rows, $columns, $params); @@ -149,11 +157,12 @@ protected function prepareBatchInsertValues(string $table, iterable $rows, array /** @var string[] $columnNames */ $columnNames = array_values($columns); $columnKeys = array_combine($columnNames, $columnNames); + $columnNulls = array_fill_keys($columnNames, 'NULL'); $columnSchemas = $this->schema->getTableSchema($table)?->getColumns() ?? []; foreach ($rows as $row) { $i = 0; - $placeholders = $columnKeys; + $placeholders = $columnNulls; /** @var int|string $key */ foreach ($row as $key => $value) { @@ -181,12 +190,12 @@ protected function prepareBatchInsertValues(string $table, iterable $rows, array /** * Extract column names from columns and rows. * - * @param iterable $rows The rows to be batch inserted into the table. + * @param array[]|Iterator $rows The rows to be batch inserted into the table. * @param string[] $columns The column names. * * @return string[] The column names. */ - protected function extractColumnNames(iterable $rows, array $columns): array + protected function extractColumnNames(array|Iterator $rows, array $columns): array { $columns = $this->getNormalizeColumnNames('', $columns); @@ -194,14 +203,9 @@ protected function extractColumnNames(iterable $rows, array $columns): array return $columns; } - while ($rows instanceof IteratorAggregate && !$rows instanceof Iterator) { - $rows = $rows->getIterator(); - } - if ($rows instanceof Iterator) { $row = $rows->current(); } else { - /** @var array[] $rows */ $row = reset($rows); } diff --git a/tests/Common/CommonCommandTest.php b/tests/Common/CommonCommandTest.php index 89710879f..1f09b5f59 100644 --- a/tests/Common/CommonCommandTest.php +++ b/tests/Common/CommonCommandTest.php @@ -305,7 +305,7 @@ public function testAddUnique(string $name, string $tableName, array|string $col public function testBatchInsert( string $table, array $columns, - array $values, + iterable $values, string $expected, array $expectedParams = [], int $insertedRow = 1 diff --git a/tests/Provider/CommandProvider.php b/tests/Provider/CommandProvider.php index 689426f80..b85319649 100644 --- a/tests/Provider/CommandProvider.php +++ b/tests/Provider/CommandProvider.php @@ -5,6 +5,8 @@ namespace Yiisoft\Db\Tests\Provider; use ArrayIterator; +use IteratorAggregate; +use Traversable; use Yiisoft\Db\Command\DataType; use Yiisoft\Db\Command\Param; use Yiisoft\Db\Expression\Expression; @@ -462,7 +464,7 @@ public static function batchInsert(): array ':qp3' => true, ], ], - 'empty columns and Traversable' => [ + 'empty columns and a Traversable value' => [ 'type', [], 'values' => [new ArrayIterator(['int_col' => '1.0', 'float_col' => '2', 'char_col' => 10, 'bool_col' => 1])], @@ -479,6 +481,28 @@ public static function batchInsert(): array ':qp3' => true, ], ], + 'empty columns and Traversable values' => [ + 'type', + [], + 'values' => new class implements IteratorAggregate { + public function getIterator(): Traversable + { + yield ['int_col' => '1.0', 'float_col' => '2', 'char_col' => 10, 'bool_col' => 1]; + } + }, + 'expected' => DbHelper::replaceQuotes( + << [ + ':qp0' => 1, + ':qp1' => 2.0, + ':qp2' => '10', + ':qp3' => true, + ], + ], ]; } diff --git a/tests/Provider/QueryBuilderProvider.php b/tests/Provider/QueryBuilderProvider.php index f37f97ebf..2db3eee8b 100644 --- a/tests/Provider/QueryBuilderProvider.php +++ b/tests/Provider/QueryBuilderProvider.php @@ -252,6 +252,18 @@ public static function batchInsert(): array ':qp3' => 1, ], ], + 'skipped columns' => [ + 'customer', + ['email', 'name', 'address'], + 'values' => [['email' => 'hello@localhost', 'address' => 'Earth, Solar System']], + 'expected' => DbHelper::replaceQuotes( + << [':qp0' => 'hello@localhost', ':qp1' => 'Earth, Solar System'], + ], ]; }