diff --git a/CHANGELOG.md b/CHANGELOG.md index f3b69448..14768718 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,6 +56,7 @@ - New #391: Add `Connection::getColumnBuilderClass()` method (@Tigrov) - New #390: Implement `ArrayMergeBuilder`, `GreatestBuilder`, `LeastBuilder`, `LengthBuilder`, `LongestBuilder` and `ShortestBuilder` classes (@Tigrov) +- Enh #393: Refactor `DMLQueryBuilder::upsert()` method (@Tigrov) ## 1.2.0 March 21, 2024 diff --git a/src/DMLQueryBuilder.php b/src/DMLQueryBuilder.php index a7d7ac08..12bd6784 100644 --- a/src/DMLQueryBuilder.php +++ b/src/DMLQueryBuilder.php @@ -9,7 +9,6 @@ use InvalidArgumentException; use Yiisoft\Db\Exception\InvalidConfigException; use Yiisoft\Db\Exception\NotSupportedException; -use Yiisoft\Db\Expression\Expression; use Yiisoft\Db\Query\QueryInterface; use Yiisoft\Db\QueryBuilder\AbstractDMLQueryBuilder; @@ -213,7 +212,7 @@ private function prepareUpsertParts( foreach ($columnNames as $name) { $quotedName = $this->quoter->quoteColumnName($name); - $constraintCondition[] = "$quotedTableName.$quotedName=[EXCLUDED].$quotedName"; + $constraintCondition[] = "$quotedTableName.$quotedName=EXCLUDED.$quotedName"; } $onCondition[] = $constraintCondition; @@ -227,18 +226,18 @@ private function prepareUpsertParts( $mergeSql = 'MERGE ' . $quotedTableName . ' WITH (HOLDLOCK) USING (' . (!empty($placeholders) ? 'VALUES (' . implode(', ', $placeholders) . ')' : $values) - . ') AS [EXCLUDED] (' . implode(', ', $quotedInsertNames) . ') ' . "ON ($on)"; + . ') AS EXCLUDED (' . implode(', ', $quotedInsertNames) . ') ' . "ON ($on)"; $insertValues = []; foreach ($quotedInsertNames as $quotedName) { - $insertValues[] = '[EXCLUDED].' . $quotedName; + $insertValues[] = 'EXCLUDED.' . $quotedName; } $insertSql = 'INSERT (' . implode(', ', $quotedInsertNames) . ')' . ' VALUES (' . implode(', ', $insertValues) . ')'; - if ($updateColumns === false || $updateNames === []) { + if (empty($updateColumns) || $updateNames === []) { /** there are no columns to update */ return [ $mergeSql, @@ -247,16 +246,7 @@ private function prepareUpsertParts( ]; } - 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); return [ $mergeSql, diff --git a/tests/Provider/QueryBuilderProvider.php b/tests/Provider/QueryBuilderProvider.php index b5396ea2..8bc980cb 100644 --- a/tests/Provider/QueryBuilderProvider.php +++ b/tests/Provider/QueryBuilderProvider.php @@ -421,81 +421,81 @@ public static function upsert(): array { $concreteData = [ 'regular values' => [ - 3 => 'MERGE [T_upsert] WITH (HOLDLOCK) USING (VALUES (:qp0, :qp1, :qp2, :qp3)) AS [EXCLUDED] ' . - '([email], [address], [status], [profile_id]) ON ([T_upsert].[email]=[EXCLUDED].[email]) ' . - 'WHEN MATCHED THEN UPDATE SET [address]=[EXCLUDED].[address], [status]=[EXCLUDED].[status], [profile_id]=[EXCLUDED].[profile_id] ' . - 'WHEN NOT MATCHED THEN INSERT ([email], [address], [status], [profile_id]) VALUES ([EXCLUDED].[email], ' . - '[EXCLUDED].[address], [EXCLUDED].[status], [EXCLUDED].[profile_id]);', + 3 => 'MERGE [T_upsert] WITH (HOLDLOCK) USING (VALUES (:qp0, :qp1, :qp2, :qp3)) AS EXCLUDED ' . + '([email], [address], [status], [profile_id]) ON ([T_upsert].[email]=EXCLUDED.[email]) ' . + 'WHEN MATCHED THEN UPDATE SET [address]=EXCLUDED.[address], [status]=EXCLUDED.[status], [profile_id]=EXCLUDED.[profile_id] ' . + 'WHEN NOT MATCHED THEN INSERT ([email], [address], [status], [profile_id]) VALUES (EXCLUDED.[email], ' . + 'EXCLUDED.[address], EXCLUDED.[status], EXCLUDED.[profile_id]);', ], 'regular values with unique at not the first position' => [ - 3 => 'MERGE [T_upsert] WITH (HOLDLOCK) USING (VALUES (:qp0, :qp1, :qp2, :qp3)) AS [EXCLUDED] ' . - '([address], [email], [status], [profile_id]) ON ([T_upsert].[email]=[EXCLUDED].[email]) ' . - 'WHEN MATCHED THEN UPDATE SET [address]=[EXCLUDED].[address], [status]=[EXCLUDED].[status], [profile_id]=[EXCLUDED].[profile_id] ' . + 3 => 'MERGE [T_upsert] WITH (HOLDLOCK) USING (VALUES (:qp0, :qp1, :qp2, :qp3)) AS EXCLUDED ' . + '([address], [email], [status], [profile_id]) ON ([T_upsert].[email]=EXCLUDED.[email]) ' . + 'WHEN MATCHED THEN UPDATE SET [address]=EXCLUDED.[address], [status]=EXCLUDED.[status], [profile_id]=EXCLUDED.[profile_id] ' . 'WHEN NOT MATCHED THEN INSERT ([address], [email], [status], [profile_id]) VALUES (' . - '[EXCLUDED].[address], [EXCLUDED].[email], [EXCLUDED].[status], [EXCLUDED].[profile_id]);', + 'EXCLUDED.[address], EXCLUDED.[email], EXCLUDED.[status], EXCLUDED.[profile_id]);', ], 'regular values with update part' => [ - 3 => 'MERGE [T_upsert] WITH (HOLDLOCK) USING (VALUES (:qp0, :qp1, :qp2, :qp3)) AS [EXCLUDED] ' . - '([email], [address], [status], [profile_id]) ON ([T_upsert].[email]=[EXCLUDED].[email]) ' . - 'WHEN MATCHED THEN UPDATE SET [address]=:qp4, [status]=:qp5, [orders]=T_upsert.orders + 1 ' . + 3 => 'MERGE [T_upsert] WITH (HOLDLOCK) USING (VALUES (:qp0, :qp1, :qp2, :qp3)) AS EXCLUDED ' . + '([email], [address], [status], [profile_id]) ON ([T_upsert].[email]=EXCLUDED.[email]) ' . + 'WHEN MATCHED THEN UPDATE SET [address]=:qp4, [status]=2, [orders]=T_upsert.orders + 1 ' . 'WHEN NOT MATCHED THEN INSERT ([email], [address], [status], [profile_id]) ' . - 'VALUES ([EXCLUDED].[email], [EXCLUDED].[address], [EXCLUDED].[status], [EXCLUDED].[profile_id]);', + 'VALUES (EXCLUDED.[email], EXCLUDED.[address], EXCLUDED.[status], EXCLUDED.[profile_id]);', ], 'regular values without update part' => [ - 3 => 'MERGE [T_upsert] WITH (HOLDLOCK) USING (VALUES (:qp0, :qp1, :qp2, :qp3)) AS [EXCLUDED] ' . - '([email], [address], [status], [profile_id]) ON ([T_upsert].[email]=[EXCLUDED].[email]) ' . + 3 => 'MERGE [T_upsert] WITH (HOLDLOCK) USING (VALUES (:qp0, :qp1, :qp2, :qp3)) AS EXCLUDED ' . + '([email], [address], [status], [profile_id]) ON ([T_upsert].[email]=EXCLUDED.[email]) ' . 'WHEN NOT MATCHED THEN INSERT ([email], [address], [status], [profile_id]) ' . - 'VALUES ([EXCLUDED].[email], [EXCLUDED].[address], [EXCLUDED].[status], [EXCLUDED].[profile_id]);', + 'VALUES (EXCLUDED.[email], EXCLUDED.[address], EXCLUDED.[status], EXCLUDED.[profile_id]);', ], 'query' => [ 3 => 'MERGE [T_upsert] WITH (HOLDLOCK) USING ' . '(SELECT [email], 2 AS [status] FROM [customer] WHERE [name] = :qp0 ' . - 'ORDER BY (SELECT NULL) OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY) AS [EXCLUDED] ' . - '([email], [status]) ON ([T_upsert].[email]=[EXCLUDED].[email]) ' . - 'WHEN MATCHED THEN UPDATE SET [status]=[EXCLUDED].[status] ' . - 'WHEN NOT MATCHED THEN INSERT ([email], [status]) VALUES ([EXCLUDED].[email], [EXCLUDED].[status]);', + 'ORDER BY (SELECT NULL) OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY) AS EXCLUDED ' . + '([email], [status]) ON ([T_upsert].[email]=EXCLUDED.[email]) ' . + 'WHEN MATCHED THEN UPDATE SET [status]=EXCLUDED.[status] ' . + 'WHEN NOT MATCHED THEN INSERT ([email], [status]) VALUES (EXCLUDED.[email], EXCLUDED.[status]);', ], 'query with update part' => [ 3 => 'MERGE [T_upsert] WITH (HOLDLOCK) USING (SELECT [email], 2 AS [status] FROM [customer] ' . - 'WHERE [name] = :qp0 ORDER BY (SELECT NULL) OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY) AS [EXCLUDED] ' . - '([email], [status]) ON ([T_upsert].[email]=[EXCLUDED].[email]) ' . - 'WHEN MATCHED THEN UPDATE SET [address]=:qp1, [status]=:qp2, [orders]=T_upsert.orders + 1 ' . - 'WHEN NOT MATCHED THEN INSERT ([email], [status]) VALUES ([EXCLUDED].[email], [EXCLUDED].[status]);', + 'WHERE [name] = :qp0 ORDER BY (SELECT NULL) OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY) AS EXCLUDED ' . + '([email], [status]) ON ([T_upsert].[email]=EXCLUDED.[email]) ' . + 'WHEN MATCHED THEN UPDATE SET [address]=:qp1, [status]=2, [orders]=T_upsert.orders + 1 ' . + 'WHEN NOT MATCHED THEN INSERT ([email], [status]) VALUES (EXCLUDED.[email], EXCLUDED.[status]);', ], 'query without update part' => [ 3 => 'MERGE [T_upsert] WITH (HOLDLOCK) USING (SELECT [email], 2 AS [status] FROM [customer] ' . - 'WHERE [name] = :qp0 ORDER BY (SELECT NULL) OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY) AS [EXCLUDED] ' . - '([email], [status]) ON ([T_upsert].[email]=[EXCLUDED].[email]) ' . - 'WHEN NOT MATCHED THEN INSERT ([email], [status]) VALUES ([EXCLUDED].[email], [EXCLUDED].[status]);', + 'WHERE [name] = :qp0 ORDER BY (SELECT NULL) OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY) AS EXCLUDED ' . + '([email], [status]) ON ([T_upsert].[email]=EXCLUDED.[email]) ' . + 'WHEN NOT MATCHED THEN INSERT ([email], [status]) VALUES (EXCLUDED.[email], EXCLUDED.[status]);', ], 'values and expressions' => [ 1 => ['{{%T_upsert}}.[[email]]' => 'dynamic@example.com', '[[ts]]' => new Expression('CONVERT(bigint, CURRENT_TIMESTAMP)')], - 3 => 'MERGE {{%T_upsert}} WITH (HOLDLOCK) USING (VALUES (:qp0, CONVERT(bigint, CURRENT_TIMESTAMP))) AS [EXCLUDED] ' . - '([email], [ts]) ON ({{%T_upsert}}.[email]=[EXCLUDED].[email]) ' . - 'WHEN MATCHED THEN UPDATE SET [ts]=[EXCLUDED].[ts] ' . - 'WHEN NOT MATCHED THEN INSERT ([email], [ts]) VALUES ([EXCLUDED].[email], [EXCLUDED].[ts]);', + 3 => 'MERGE {{%T_upsert}} WITH (HOLDLOCK) USING (VALUES (:qp0, CONVERT(bigint, CURRENT_TIMESTAMP))) AS EXCLUDED ' . + '([email], [ts]) ON ({{%T_upsert}}.[email]=EXCLUDED.[email]) ' . + 'WHEN MATCHED THEN UPDATE SET [ts]=EXCLUDED.[ts] ' . + 'WHEN NOT MATCHED THEN INSERT ([email], [ts]) VALUES (EXCLUDED.[email], EXCLUDED.[ts]);', ], 'values and expressions with update part' => [ 1 => ['{{%T_upsert}}.[[email]]' => 'dynamic@example.com', '[[ts]]' => new Expression('CONVERT(bigint, CURRENT_TIMESTAMP)')], - 3 => 'MERGE {{%T_upsert}} WITH (HOLDLOCK) USING (VALUES (:qp0, CONVERT(bigint, CURRENT_TIMESTAMP))) AS [EXCLUDED] ' . - '([email], [ts]) ON ({{%T_upsert}}.[email]=[EXCLUDED].[email]) ' . + 3 => 'MERGE {{%T_upsert}} WITH (HOLDLOCK) USING (VALUES (:qp0, CONVERT(bigint, CURRENT_TIMESTAMP))) AS EXCLUDED ' . + '([email], [ts]) ON ({{%T_upsert}}.[email]=EXCLUDED.[email]) ' . 'WHEN MATCHED THEN UPDATE SET [orders]=T_upsert.orders + 1 ' . - 'WHEN NOT MATCHED THEN INSERT ([email], [ts]) VALUES ([EXCLUDED].[email], [EXCLUDED].[ts]);', + 'WHEN NOT MATCHED THEN INSERT ([email], [ts]) VALUES (EXCLUDED.[email], EXCLUDED.[ts]);', ], 'values and expressions without update part' => [ 1 => ['{{%T_upsert}}.[[email]]' => 'dynamic@example.com', '[[ts]]' => new Expression('CONVERT(bigint, CURRENT_TIMESTAMP)')], - 3 => 'MERGE [T_upsert] WITH (HOLDLOCK) USING (VALUES (:qp0, CONVERT(bigint, CURRENT_TIMESTAMP))) AS [EXCLUDED] ' . - '([email], [ts]) ON ([T_upsert].[email]=[EXCLUDED].[email]) ' . - 'WHEN NOT MATCHED THEN INSERT ([email], [ts]) VALUES ([EXCLUDED].[email], [EXCLUDED].[ts]);', + 3 => 'MERGE [T_upsert] WITH (HOLDLOCK) USING (VALUES (:qp0, CONVERT(bigint, CURRENT_TIMESTAMP))) AS EXCLUDED ' . + '([email], [ts]) ON ([T_upsert].[email]=EXCLUDED.[email]) ' . + 'WHEN NOT MATCHED THEN INSERT ([email], [ts]) VALUES (EXCLUDED.[email], EXCLUDED.[ts]);', ], 'query, values and expressions with update part' => [ @@ -507,9 +507,9 @@ public static function upsert(): array ], ), 3 => 'MERGE {{%T_upsert}} WITH (HOLDLOCK) USING (SELECT :phEmail AS [email], CONVERT(bigint, CURRENT_TIMESTAMP) AS [[ts]]) ' . - 'AS [EXCLUDED] ([email], [[ts]]) ON ({{%T_upsert}}.[email]=[EXCLUDED].[email]) ' . - 'WHEN MATCHED THEN UPDATE SET [ts]=:qp1, [orders]=T_upsert.orders + 1 ' . - 'WHEN NOT MATCHED THEN INSERT ([email], [[ts]]) VALUES ([EXCLUDED].[email], [EXCLUDED].[[ts]]);', + 'AS EXCLUDED ([email], [ts]) ON ({{%T_upsert}}.[email]=EXCLUDED.[email]) ' . + 'WHEN MATCHED THEN UPDATE SET [ts]=0, [orders]=T_upsert.orders + 1 ' . + 'WHEN NOT MATCHED THEN INSERT ([email], [ts]) VALUES (EXCLUDED.[email], EXCLUDED.[ts]);', ], 'query, values and expressions without update part' => [ @@ -521,18 +521,18 @@ public static function upsert(): array ], ), 3 => 'MERGE [T_upsert] WITH (HOLDLOCK) USING (SELECT :phEmail AS [email], CONVERT(bigint, CURRENT_TIMESTAMP) AS [[ts]]) ' . - 'AS [EXCLUDED] ([email], [[ts]]) ON ([T_upsert].[email]=[EXCLUDED].[email]) ' . - 'WHEN NOT MATCHED THEN INSERT ([email], [[ts]]) VALUES ([EXCLUDED].[email], [EXCLUDED].[[ts]]);', + 'AS EXCLUDED ([email], [ts]) ON ([T_upsert].[email]=EXCLUDED.[email]) ' . + 'WHEN NOT MATCHED THEN INSERT ([email], [ts]) VALUES (EXCLUDED.[email], EXCLUDED.[ts]);', ], 'no columns to update' => [ - 3 => 'MERGE [T_upsert_1] WITH (HOLDLOCK) USING (VALUES (:qp0)) AS [EXCLUDED] ' . - '([a]) ON ([T_upsert_1].[a]=[EXCLUDED].[a]) ' . - 'WHEN NOT MATCHED THEN INSERT ([a]) VALUES ([EXCLUDED].[a]);', + 3 => 'MERGE [T_upsert_1] WITH (HOLDLOCK) USING (VALUES (:qp0)) AS EXCLUDED ' . + '([a]) ON ([T_upsert_1].[a]=EXCLUDED.[a]) ' . + 'WHEN NOT MATCHED THEN INSERT ([a]) VALUES (EXCLUDED.[a]);', ], 'no columns to update with unique' => [ - 3 => 'MERGE [T_upsert] WITH (HOLDLOCK) USING (VALUES (:qp0)) AS [EXCLUDED] ' . - '([email]) ON ([T_upsert].[email]=[EXCLUDED].[email]) ' . - 'WHEN NOT MATCHED THEN INSERT ([email]) VALUES ([EXCLUDED].[email]);', + 3 => 'MERGE [T_upsert] WITH (HOLDLOCK) USING (VALUES (:qp0)) AS EXCLUDED ' . + '([email]) ON ([T_upsert].[email]=EXCLUDED.[email]) ' . + 'WHEN NOT MATCHED THEN INSERT ([email]) VALUES (EXCLUDED.[email]);', ], 'no unique columns in table - simple insert' => [ 3 => 'INSERT INTO {{%animal}} ([type]) VALUES (:qp0);', @@ -561,39 +561,39 @@ public static function upsertReturning(): array $upsert['regular values without update part'][4] = 'SET NOCOUNT ON;' . 'DECLARE @temporary_inserted TABLE ([id] int);DECLARE @temp int;' - . 'MERGE [T_upsert] WITH (HOLDLOCK) USING (VALUES (:qp0, :qp1, :qp2, :qp3)) AS [EXCLUDED]' - . ' ([email], [address], [status], [profile_id]) ON ([T_upsert].[email]=[EXCLUDED].[email])' + . 'MERGE [T_upsert] WITH (HOLDLOCK) USING (VALUES (:qp0, :qp1, :qp2, :qp3)) AS EXCLUDED' + . ' ([email], [address], [status], [profile_id]) ON ([T_upsert].[email]=EXCLUDED.[email])' . ' WHEN MATCHED THEN UPDATE SET @temp=1' . ' WHEN NOT MATCHED THEN INSERT ([email], [address], [status], [profile_id])' - . ' VALUES ([EXCLUDED].[email], [EXCLUDED].[address], [EXCLUDED].[status], [EXCLUDED].[profile_id])' + . ' VALUES (EXCLUDED.[email], EXCLUDED.[address], EXCLUDED.[status], EXCLUDED.[profile_id])' . ' OUTPUT INSERTED.[id] INTO @temporary_inserted;' . 'SELECT * FROM @temporary_inserted;'; $upsert['query without update part'][4] = 'SET NOCOUNT ON;' . 'DECLARE @temporary_inserted TABLE ([id] int);' . 'DECLARE @temp int;MERGE [T_upsert] WITH (HOLDLOCK) USING (SELECT [email], 2 AS [status] FROM [customer]' - . ' WHERE [name] = :qp0 ORDER BY (SELECT NULL) OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY) AS [EXCLUDED]' - . ' ([email], [status]) ON ([T_upsert].[email]=[EXCLUDED].[email])' + . ' WHERE [name] = :qp0 ORDER BY (SELECT NULL) OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY) AS EXCLUDED' + . ' ([email], [status]) ON ([T_upsert].[email]=EXCLUDED.[email])' . ' WHEN MATCHED THEN UPDATE SET @temp=1' - . ' WHEN NOT MATCHED THEN INSERT ([email], [status]) VALUES ([EXCLUDED].[email], [EXCLUDED].[status])' + . ' WHEN NOT MATCHED THEN INSERT ([email], [status]) VALUES (EXCLUDED.[email], EXCLUDED.[status])' . ' OUTPUT INSERTED.[id] INTO @temporary_inserted;' . 'SELECT * FROM @temporary_inserted;'; $upsert['values and expressions without update part'][4] = 'SET NOCOUNT ON;' . 'DECLARE @temporary_inserted TABLE ([id] int);' . 'DECLARE @temp int;' - . 'MERGE [T_upsert] WITH (HOLDLOCK) USING (VALUES (:qp0, CONVERT(bigint, CURRENT_TIMESTAMP))) AS [EXCLUDED]' - . ' ([email], [ts]) ON ([T_upsert].[email]=[EXCLUDED].[email])' + . 'MERGE [T_upsert] WITH (HOLDLOCK) USING (VALUES (:qp0, CONVERT(bigint, CURRENT_TIMESTAMP))) AS EXCLUDED' + . ' ([email], [ts]) ON ([T_upsert].[email]=EXCLUDED.[email])' . ' WHEN MATCHED THEN UPDATE SET @temp=1' - . ' WHEN NOT MATCHED THEN INSERT ([email], [ts]) VALUES ([EXCLUDED].[email], [EXCLUDED].[ts])' + . ' WHEN NOT MATCHED THEN INSERT ([email], [ts]) VALUES (EXCLUDED.[email], EXCLUDED.[ts])' . ' OUTPUT INSERTED.[id] INTO @temporary_inserted;' . 'SELECT * FROM @temporary_inserted;'; $upsert['query, values and expressions without update part'][4] = 'SET NOCOUNT ON;' . 'DECLARE @temporary_inserted TABLE ([id] int);' . 'DECLARE @temp int;' . 'MERGE [T_upsert] WITH (HOLDLOCK)' - . ' USING (SELECT :phEmail AS [email], CONVERT(bigint, CURRENT_TIMESTAMP) AS [[ts]]) AS [EXCLUDED]' - . ' ([email], [[ts]]) ON ([T_upsert].[email]=[EXCLUDED].[email])' + . ' USING (SELECT :phEmail AS [email], CONVERT(bigint, CURRENT_TIMESTAMP) AS [[ts]]) AS EXCLUDED' + . ' ([email], [ts]) ON ([T_upsert].[email]=EXCLUDED.[email])' . ' WHEN MATCHED THEN UPDATE SET @temp=1' - . ' WHEN NOT MATCHED THEN INSERT ([email], [[ts]]) VALUES ([EXCLUDED].[email], [EXCLUDED].[[ts]])' + . ' WHEN NOT MATCHED THEN INSERT ([email], [ts]) VALUES (EXCLUDED.[email], EXCLUDED.[ts])' . ' OUTPUT INSERTED.[id] INTO @temporary_inserted;' . 'SELECT * FROM @temporary_inserted;'; $upsert['no unique columns in table - simple insert'][4] = 'SET NOCOUNT ON;' @@ -605,19 +605,19 @@ public static function upsertReturning(): array $upsert['no columns to update'][4] = 'SET NOCOUNT ON;' . 'DECLARE @temporary_inserted TABLE ([a] int);' . 'DECLARE @temp int;' - . 'MERGE [T_upsert_1] WITH (HOLDLOCK) USING (VALUES (:qp0)) AS [EXCLUDED]' - . ' ([a]) ON ([T_upsert_1].[a]=[EXCLUDED].[a])' + . 'MERGE [T_upsert_1] WITH (HOLDLOCK) USING (VALUES (:qp0)) AS EXCLUDED' + . ' ([a]) ON ([T_upsert_1].[a]=EXCLUDED.[a])' . ' WHEN MATCHED THEN UPDATE SET @temp=1' - . ' WHEN NOT MATCHED THEN INSERT ([a]) VALUES ([EXCLUDED].[a])' + . ' WHEN NOT MATCHED THEN INSERT ([a]) VALUES (EXCLUDED.[a])' . ' OUTPUT INSERTED.[a] INTO @temporary_inserted;' . 'SELECT * FROM @temporary_inserted;'; $upsert['no columns to update with unique'][4] = 'SET NOCOUNT ON;' . 'DECLARE @temporary_inserted TABLE ([id] int);' . 'DECLARE @temp int;' - . 'MERGE [T_upsert] WITH (HOLDLOCK) USING (VALUES (:qp0)) AS [EXCLUDED]' - . ' ([email]) ON ([T_upsert].[email]=[EXCLUDED].[email])' + . 'MERGE [T_upsert] WITH (HOLDLOCK) USING (VALUES (:qp0)) AS EXCLUDED' + . ' ([email]) ON ([T_upsert].[email]=EXCLUDED.[email])' . ' WHEN MATCHED THEN UPDATE SET @temp=1' - . ' WHEN NOT MATCHED THEN INSERT ([email]) VALUES ([EXCLUDED].[email])' + . ' WHEN NOT MATCHED THEN INSERT ([email]) VALUES (EXCLUDED.[email])' . ' OUTPUT INSERTED.[id] INTO @temporary_inserted;' . 'SELECT * FROM @temporary_inserted;'; @@ -629,11 +629,11 @@ public static function upsertReturning(): array true, ['id_1', 'id_2'], 'SET NOCOUNT ON;DECLARE @temporary_inserted TABLE ([id_1] int, [id_2] decimal(5,2));' - . 'MERGE [notauto_pk] WITH (HOLDLOCK) USING (VALUES (:qp0, :qp1, :qp2)) AS [EXCLUDED]' - . ' ([id_1], [id_2], [type]) ON (([notauto_pk].[id_1]=[EXCLUDED].[id_1])' - . ' AND ([notauto_pk].[id_2]=[EXCLUDED].[id_2])) WHEN MATCHED THEN UPDATE SET [type]=[EXCLUDED].[type]' + . 'MERGE [notauto_pk] WITH (HOLDLOCK) USING (VALUES (:qp0, :qp1, :qp2)) AS EXCLUDED' + . ' ([id_1], [id_2], [type]) ON (([notauto_pk].[id_1]=EXCLUDED.[id_1])' + . ' AND ([notauto_pk].[id_2]=EXCLUDED.[id_2])) WHEN MATCHED THEN UPDATE SET [type]=EXCLUDED.[type]' . ' WHEN NOT MATCHED THEN INSERT ([id_1], [id_2], [type])' - . ' VALUES ([EXCLUDED].[id_1], [EXCLUDED].[id_2], [EXCLUDED].[type])' + . ' VALUES (EXCLUDED.[id_1], EXCLUDED.[id_2], EXCLUDED.[type])' . ' OUTPUT INSERTED.[id_1],INSERTED.[id_2] INTO @temporary_inserted;SELECT * FROM @temporary_inserted;', [':qp0' => 1, ':qp1' => 2.5, ':qp2' => 'Test'], ], @@ -650,12 +650,12 @@ public static function upsertReturning(): array ['email' => 'test@example.com', 'address' => 'test address', 'status' => 1, 'profile_id' => 1], true, [], - 'MERGE [T_upsert] WITH (HOLDLOCK) USING (VALUES (:qp0, :qp1, :qp2, :qp3)) AS [EXCLUDED]' - . ' ([email], [address], [status], [profile_id]) ON ([T_upsert].[email]=[EXCLUDED].[email])' - . ' WHEN MATCHED THEN UPDATE SET [address]=[EXCLUDED].[address], [status]=[EXCLUDED].[status],' - . ' [profile_id]=[EXCLUDED].[profile_id]' + 'MERGE [T_upsert] WITH (HOLDLOCK) USING (VALUES (:qp0, :qp1, :qp2, :qp3)) AS EXCLUDED' + . ' ([email], [address], [status], [profile_id]) ON ([T_upsert].[email]=EXCLUDED.[email])' + . ' WHEN MATCHED THEN UPDATE SET [address]=EXCLUDED.[address], [status]=EXCLUDED.[status],' + . ' [profile_id]=EXCLUDED.[profile_id]' . ' WHEN NOT MATCHED THEN INSERT ([email], [address], [status], [profile_id])' - . ' VALUES ([EXCLUDED].[email], [EXCLUDED].[address], [EXCLUDED].[status], [EXCLUDED].[profile_id]);', + . ' VALUES (EXCLUDED.[email], EXCLUDED.[address], EXCLUDED.[status], EXCLUDED.[profile_id]);', [':qp0' => 'test@example.com', ':qp1' => 'test address', ':qp2' => 1, ':qp3' => 1], ], 'return all columns' => [ @@ -666,10 +666,10 @@ public static function upsertReturning(): array 'SET NOCOUNT ON;DECLARE @temporary_inserted TABLE ([id] int, [ts] int NULL, [email] varchar(128),' . ' [recovery_email] varchar(128) NULL, [address] text NULL, [status] tinyint, [orders] int,' . ' [profile_id] int NULL);MERGE [T_upsert] WITH (HOLDLOCK) USING (VALUES (:qp0, :qp1, :qp2, :qp3))' - . ' AS [EXCLUDED] ([email], [address], [status], [profile_id]) ON ([T_upsert].[email]=[EXCLUDED].[email])' - . ' WHEN MATCHED THEN UPDATE SET [address]=[EXCLUDED].[address], [status]=[EXCLUDED].[status],' - . ' [profile_id]=[EXCLUDED].[profile_id] WHEN NOT MATCHED THEN INSERT ([email], [address], [status], [profile_id])' - . ' VALUES ([EXCLUDED].[email], [EXCLUDED].[address], [EXCLUDED].[status], [EXCLUDED].[profile_id])' + . ' AS EXCLUDED ([email], [address], [status], [profile_id]) ON ([T_upsert].[email]=EXCLUDED.[email])' + . ' WHEN MATCHED THEN UPDATE SET [address]=EXCLUDED.[address], [status]=EXCLUDED.[status],' + . ' [profile_id]=EXCLUDED.[profile_id] WHEN NOT MATCHED THEN INSERT ([email], [address], [status], [profile_id])' + . ' VALUES (EXCLUDED.[email], EXCLUDED.[address], EXCLUDED.[status], EXCLUDED.[profile_id])' . ' OUTPUT INSERTED.[id],INSERTED.[ts],INSERTED.[email],INSERTED.[recovery_email],INSERTED.[address],' . 'INSERTED.[status],INSERTED.[orders],INSERTED.[profile_id] INTO @temporary_inserted;' . 'SELECT * FROM @temporary_inserted;', diff --git a/tests/QueryBuilderTest.php b/tests/QueryBuilderTest.php index 1c4692d2..ce5a5915 100644 --- a/tests/QueryBuilderTest.php +++ b/tests/QueryBuilderTest.php @@ -444,7 +444,7 @@ public function testUpdate( array|string $condition, array $params, string $expectedSql, - array $expectedParams, + array $expectedParams = [], ): void { parent::testUpdate($table, $columns, $condition, $params, $expectedSql, $expectedParams); }