diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e78dbf..fb4d1dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -59,6 +59,7 @@ - Enh #352: Support column's collation (@Tigrov) - New #358: Add `Connection::getColumnBuilderClass()` method (@Tigrov) - New #357: Implement `ArrayMergeBuilder`, `LongestBuilder` and `ShortestBuilder` classes (@Tigrov) +- Enh #360: Refactor `DMLQueryBuilder::upsert()` method (@Tigrov) ## 1.3.0 March 21, 2024 diff --git a/src/DMLQueryBuilder.php b/src/DMLQueryBuilder.php index e4b8c29..a1cbaae 100644 --- a/src/DMLQueryBuilder.php +++ b/src/DMLQueryBuilder.php @@ -6,10 +6,10 @@ use InvalidArgumentException; use Yiisoft\Db\Exception\NotSupportedException; -use Yiisoft\Db\Expression\Expression; use Yiisoft\Db\Query\QueryInterface; use Yiisoft\Db\QueryBuilder\AbstractDMLQueryBuilder; +use function array_combine; use function array_fill; use function array_key_first; use function array_map; @@ -85,7 +85,7 @@ public function upsert( foreach ($columnNames as $name) { $quotedName = $this->quoter->quoteColumnName($name); - $constraintCondition[] = "$quotedTableName.$quotedName=\"EXCLUDED\".$quotedName"; + $constraintCondition[] = "$quotedTableName.$quotedName=EXCLUDED.$quotedName"; } $onCondition[] = $constraintCondition; @@ -96,41 +96,26 @@ public function upsert( [, $placeholders, $values, $params] = $this->prepareInsertValues($table, $insertColumns, $params); if (!empty($placeholders)) { - $usingSelectValues = []; - - foreach ($insertNames as $index => $name) { - $usingSelectValues[$name] = new Expression($placeholders[$index]); - } - - $values = $this->queryBuilder->buildSelect($usingSelectValues, $params) - . ' ' . $this->queryBuilder->buildFrom(['DUAL'], $params); + $values = $this->buildSimpleSelect(array_combine($insertNames, $placeholders)) . ' FROM "DUAL"'; } $insertValues = []; $quotedInsertNames = array_map($this->quoter->quoteColumnName(...), $insertNames); foreach ($quotedInsertNames as $quotedName) { - $insertValues[] = '"EXCLUDED".' . $quotedName; + $insertValues[] = 'EXCLUDED.' . $quotedName; } - $mergeSql = 'MERGE INTO ' . $quotedTableName . ' USING (' . $values . ') "EXCLUDED" ON (' . $on . ')'; + $mergeSql = 'MERGE INTO ' . $quotedTableName . ' USING (' . $values . ') EXCLUDED ON (' . $on . ')'; $insertSql = 'INSERT (' . implode(', ', $quotedInsertNames) . ')' . ' VALUES (' . implode(', ', $insertValues) . ')'; - if ($updateColumns === false || $updateNames === []) { + if (empty($updateColumns) || $updateNames === []) { /** there are no columns to update */ return "$mergeSql WHEN NOT MATCHED THEN $insertSql"; } - if ($updateColumns === true) { - $updateColumns = []; - /** @psalm-var string[] $updateNames */ - foreach ($updateNames as $name) { - $updateColumns[$name] = new Expression('"EXCLUDED".' . $this->quoter->quoteColumnName($name)); - } - } - - $updates = $this->prepareUpdateSets($table, $updateColumns, $params); + $updates = $this->prepareUpsertSets($table, $updateColumns, $updateNames, $params); $updateSql = 'UPDATE SET ' . implode(', ', $updates); return "$mergeSql WHEN MATCHED THEN $updateSql WHEN NOT MATCHED THEN $insertSql"; diff --git a/tests/Provider/QueryBuilderProvider.php b/tests/Provider/QueryBuilderProvider.php index 4436c19..108d822 100644 --- a/tests/Provider/QueryBuilderProvider.php +++ b/tests/Provider/QueryBuilderProvider.php @@ -137,61 +137,61 @@ public static function upsert(): array $concreteData = [ 'regular values' => [ 3 => << [ 3 => << [ 2 => ['address' => 'foo {{city}}', 'status' => 2, 'orders' => new Expression('"T_upsert"."orders" + 1')], 3 => << [ 3 => << [ 3 => << [ 2 => ['address' => 'foo {{city}}', 'status' => 2, 'orders' => new Expression('"T_upsert"."orders" + 1')], 3 => << [ 3 => << [ 1 => ['{{%T_upsert}}.[[email]]' => 'dynamic@example.com', '[[ts]]' => new Expression('ROUND((SYSDATE - DATE \'1970-01-01\')*24*60*60)')], 3 => << [ 1 => ['{{%T_upsert}}.[[email]]' => 'dynamic@example.com', '[[ts]]' => new Expression('ROUND((SYSDATE - DATE \'1970-01-01\')*24*60*60)')], 2 => ['[[orders]]' => new Expression('"T_upsert"."orders" + 1')], 3 => << [ 1 => ['{{%T_upsert}}.[[email]]' => 'dynamic@example.com', '[[ts]]' => new Expression('ROUND((SYSDATE - DATE \'1970-01-01\')*24*60*60)')], 3 => << [ @@ -204,7 +204,7 @@ public static function upsert(): array )->from('DUAL'), 2 => ['ts' => 0, '[[orders]]' => new Expression('"T_upsert"."orders" + 1')], 3 => << [ @@ -216,17 +216,17 @@ public static function upsert(): array ], )->from('DUAL'), 3 => << [ 3 => << [ 3 => << [ diff --git a/tests/QueryBuilderTest.php b/tests/QueryBuilderTest.php index 258dad0..d9174f8 100644 --- a/tests/QueryBuilderTest.php +++ b/tests/QueryBuilderTest.php @@ -484,7 +484,7 @@ public function testUpdate( array|string $condition, array $params, string $expectedSql, - array $expectedParams, + array $expectedParams = [], ): void { parent::testUpdate($table, $columns, $condition, $params, $expectedSql, $expectedParams); }