diff --git a/CHANGELOG.md b/CHANGELOG.md index aec8b33..e486e73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ - Bug #233: Refactor `DMLQueryBuilder`, related with yiisoft/db#746 (@Tigrov) - Enh #230: Improve column type #230 (@Tigrov) - Bug #240: Remove `RECURSIVE` expression from CTE queries (@Tigrov) +- Bug #242: Fix `AbstractDMLQueryBuilder::batchInsert()` for values as associative arrays, + related with yiisoft/db#769 (@Tigrov) - Enh #243: Move methods from `Command` to `AbstractPdoCommand` class (@Tigrov) ## 1.1.0 July 24, 2023 diff --git a/src/DMLQueryBuilder.php b/src/DMLQueryBuilder.php index ae03ccc..4df2fe5 100644 --- a/src/DMLQueryBuilder.php +++ b/src/DMLQueryBuilder.php @@ -37,25 +37,31 @@ public function batchInsert(string $table, array $columns, iterable $rows, array $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 = []; - - foreach ($row as $value) { - if (isset($columns[$i], $columnSchemas[$columns[$i]])) { - $value = $columnSchemas[$columns[$i]]->dbTypecast($value); + $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[] = $this->queryBuilder->buildExpression($value, $params); + $placeholders[$columnName] = $this->queryBuilder->buildExpression($value, $params); } else { - $placeholders[] = $this->queryBuilder->bindParam($value, $params); + $placeholders[$columnName] = $this->queryBuilder->bindParam($value, $params); } ++$i; } + $values[] = '(' . implode(', ', $placeholders) . ')'; } @@ -63,13 +69,13 @@ public function batchInsert(string $table, array $columns, iterable $rows, array return ''; } - $columns = array_map( + $columnNames = array_map( [$this->quoter, 'quoteColumnName'], - $columns, + $columnNames, ); $tableAndColumns = ' INTO ' . $this->quoter->quoteTableName($table) - . ' (' . implode(', ', $columns) . ') VALUES '; + . ' (' . implode(', ', $columnNames) . ') VALUES '; 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 65cd034..d97a322 100644 --- a/tests/Provider/CommandProvider.php +++ b/tests/Provider/CommandProvider.php @@ -10,6 +10,7 @@ use Yiisoft\Db\Oracle\Tests\Support\TestTrait; use Yiisoft\Db\Tests\Support\DbHelper; +use function array_merge; use function json_encode; use function serialize; @@ -29,17 +30,37 @@ public static function batchInsert(): array $batchInsert['multirow']['expectedParams'][':qp3'] = '1'; $batchInsert['multirow']['expectedParams'][':qp7'] = '0'; - DbHelper::changeSqlForOracleBatchInsert($batchInsert['issue11242']['expected']); - $batchInsert['issue11242']['expectedParams'][':qp3'] = '1'; - - DbHelper::changeSqlForOracleBatchInsert($batchInsert['table name with column name with brackets']['expected']); - $batchInsert['table name with column name with brackets']['expectedParams'][':qp3'] = '0'; - - DbHelper::changeSqlForOracleBatchInsert($batchInsert['batchInsert binds params from expression']['expected']); - $batchInsert['batchInsert binds params from expression']['expectedParams'][':qp3'] = '0'; + $replaceParams = [ + 'issue11242' => [ + ':qp3' => '1', + ], + 'table name with column name with brackets' => [ + ':qp3' => '0', + ], + 'batchInsert binds params from expression' => [ + ':qp3' => '0', + ], + 'with associative values with different keys' => [ + ':qp3' => '1', + ], + 'with associative values with different keys and columns with keys' => [ + ':qp3' => '1', + ], + 'with associative values with keys of column names' => [ + ':qp0' => '1', + ], + 'with associative values with keys of column keys' => [ + ':qp0' => '1', + ], + 'with shuffled indexes of values' => [ + ':qp0' => '1', + ], + ]; - DbHelper::changeSqlForOracleBatchInsert($batchInsert['with associative values']['expected']); - $batchInsert['with associative values']['expectedParams'][':qp3'] = '1'; + foreach ($replaceParams as $key => $expectedParams) { + DbHelper::changeSqlForOracleBatchInsert($batchInsert[$key]['expected']); + $batchInsert[$key]['expectedParams'] = array_merge($batchInsert[$key]['expectedParams'], $expectedParams); + } return $batchInsert; }