Skip to content

Commit 1c5fb1f

Browse files
authored
Fix multiple nil identity columns for merge insert (#1327)
1 parent 6feabc2 commit 1c5fb1f

File tree

2 files changed

+35
-5
lines changed

2 files changed

+35
-5
lines changed

CHANGELOG.md

+5-4
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
#### Added
44

5-
- [#1301](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1301) Add support for INDEX INCLUDE.
6-
- [#1312](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1312) Add support for `insert_all` and `upsert_all`
7-
- [#1317](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1317) Reverse order of values when upserting
5+
- [#1301](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1301) Add support for `INDEX INCLUDE`.
6+
- [#1312](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1312) Add support for `insert_all` and `upsert_all`.
7+
- [#1317](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1317) Reverse order of values when upserting.
88

99
#### Changed
1010

@@ -13,6 +13,7 @@
1313
#### Fixed
1414

1515
- [#1313](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1313) Correctly retrieve the SQL Server database version.
16-
- [#1320](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1320) Fix SQL statement to calculate `updated_at` when upserting
16+
- [#1320](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1320) Fix SQL statement to calculate `updated_at` when upserting.
17+
- [#1327](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1327) Fix multiple `nil` identity columns for merge insert.
1718

1819
Please check [8-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/8-0-stable/CHANGELOG.md) for previous changes.

lib/active_record/connection_adapters/sqlserver/database_statements.rb

+30-1
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ def build_sql_for_merge_insert(insert:, insert_all:, columns_with_uniqueness_con
177177
SELECT *
178178
FROM (
179179
SELECT #{insert.send(:columns_list)}, #{partition_by_columns_with_uniqueness_constraints(columns_with_uniqueness_constraints:)}
180-
FROM (#{insert.values_list})
180+
FROM (#{merge_insert_values_list(insert:, insert_all:)})
181181
AS t1 (#{insert.send(:columns_list)})
182182
) AS ranked_source
183183
WHERE #{is_first_record_across_all_uniqueness_constraints(columns_with_uniqueness_constraints:)}
@@ -206,6 +206,35 @@ def build_sql_for_merge_insert(insert:, insert_all:, columns_with_uniqueness_con
206206
sql
207207
end
208208

209+
# For `nil` identity columns we need to ensure that the values do not match so that they are all inserted.
210+
# Method is a combination of `ActiveRecord::InsertAll#values_list` and `ActiveRecord::ConnectionAdapters::SQLServer::DatabaseStatements#default_insert_value`.
211+
def merge_insert_values_list(insert:, insert_all:)
212+
connection = insert.send(:connection)
213+
identity_index = 0
214+
215+
types = insert.send(:extract_types_from_columns_on, insert.model.table_name, keys: insert.keys_including_timestamps)
216+
217+
values_list = insert_all.map_key_with_value do |key, value|
218+
if Arel::Nodes::SqlLiteral === value
219+
value
220+
elsif insert.primary_keys.include?(key) && value.nil?
221+
column = insert.send(:column_from_key, key)
222+
223+
if column.is_identity?
224+
identity_index += 1
225+
table_name = quote(quote_table_name(column.table_name))
226+
Arel.sql("IDENT_CURRENT(#{table_name}) + (IDENT_INCR(#{table_name}) * #{identity_index})")
227+
else
228+
connection.default_insert_value(column)
229+
end
230+
else
231+
ActiveModel::Type::SerializeCastValue.serialize(type = types[key], type.cast(value))
232+
end
233+
end
234+
235+
connection.visitor.compile(Arel::Nodes::ValuesList.new(values_list))
236+
end
237+
209238
# === SQLServer Specific ======================================== #
210239

211240
def execute_procedure(proc_name, *variables)

0 commit comments

Comments
 (0)