diff --git a/CHANGELOG.md b/CHANGELOG.md index 63173aedb..6c0a92b65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ #### Fixed - [#1318](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1318) Reverse order of values when upserting +- [#1321](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1321) Fix SQL statement to calculate `updated_at` when upserting ## v8.0.5 diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index b1cacc3e4..0473afb8e 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -585,7 +585,7 @@ def joining_on_columns_with_uniqueness_constraints(columns_with_uniqueness_const def build_sql_for_recording_timestamps_when_updating(insert:) insert.model.timestamp_attributes_for_update_in_model.filter_map do |column_name| if insert.send(:touch_timestamp_attribute?, column_name) - "target.#{quote_column_name(column_name)}=CASE WHEN (#{insert.updatable_columns.map { |column| "(COALESCE(target.#{quote_column_name(column)}, 'NULL') = COALESCE(source.#{quote_column_name(column)}, 'NULL'))" }.join(" AND ")}) THEN target.#{quote_column_name(column_name)} ELSE #{high_precision_current_timestamp} END," + "target.#{quote_column_name(column_name)}=CASE WHEN (#{insert.updatable_columns.map { |column| "(source.#{quote_column_name(column)} = target.#{quote_column_name(column)} OR (source.#{quote_column_name(column)} IS NULL AND target.#{quote_column_name(column)} IS NULL))" }.join(" AND ")}) THEN target.#{quote_column_name(column_name)} ELSE #{high_precision_current_timestamp} END," end end.join end diff --git a/test/cases/insert_all_test_sqlserver.rb b/test/cases/insert_all_test_sqlserver.rb index c48dbeae6..6367d57d4 100644 --- a/test/cases/insert_all_test_sqlserver.rb +++ b/test/cases/insert_all_test_sqlserver.rb @@ -2,6 +2,7 @@ require "cases/helper_sqlserver" require "models/book" +require "models/sqlserver/recurring_task" class InsertAllTestSQLServer < ActiveRecord::TestCase # Test ported from the Rails `main` branch that is not on the `8-0-stable` branch. @@ -27,4 +28,18 @@ def test_upsert_all_only_applies_last_value_when_given_duplicate_identifiers ] assert_equal "expected_new_name", Book.find(112).name end + + test "upsert_all recording of timestamps works with mixed datatypes" do + task = RecurringTask.create!( + key: "abcdef", + priority: 5 + ) + + RecurringTask.upsert_all([{ + id: task.id, + priority: nil + }]) + + assert_not_equal task.updated_at, RecurringTask.find(task.id).updated_at + end end diff --git a/test/models/sqlserver/recurring_task.rb b/test/models/sqlserver/recurring_task.rb new file mode 100644 index 000000000..aecf7c462 --- /dev/null +++ b/test/models/sqlserver/recurring_task.rb @@ -0,0 +1,3 @@ +class RecurringTask < ActiveRecord::Base + self.table_name = "recurring_tasks" +end diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index a5160f791..f4b600d64 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -360,4 +360,12 @@ name varchar(255) ) TABLE_IN_OTHER_SCHEMA_USED_BY_MODEL + + create_table "recurring_tasks", force: true do |t| + t.string :key + t.integer :priority, default: 0 + + t.datetime2 :created_at + t.datetime2 :updated_at + end end