Use full precision for `updated_at` in `insert_all`/`upsert_all`

`CURRENT_TIMESTAMP` provides differing precision depending on the database,
and not all databases support explicitly specifying additional precision.

Instead, we delegate to the new `connection.high_precision_current_timestamp`
for the SQL to produce a high precision timestamp on the current database.
This commit is contained in:
Sam Bostock 2021-08-10 17:31:36 -04:00
parent 58cfa9259d
commit 213c4c5b50
No known key found for this signature in database
GPG Key ID: 96D854C4833F2660
3 changed files with 22 additions and 1 deletions

View File

@ -1,3 +1,15 @@
* Use full precision for `updated_at` in `insert_all`/`upsert_all`
`CURRENT_TIMESTAMP` provides differing precision depending on the database,
and not all databases support explicitly specifying additional precision.
Instead, we delegate to the new `connection.high_precision_current_timestamp`
for the SQL to produce a high precision timestamp on the current database.
Fixes #42992
*Sam Bostock*
* Add config option for ignoring tables when dumping the schema cache.
Applications can now be configured to ignore certain tables when dumping the schema cache.

View File

@ -198,7 +198,7 @@ module ActiveRecord
def touch_model_timestamps_unless(&block)
model.timestamp_attributes_for_update_in_model.filter_map do |column_name|
if touch_timestamp_attribute?(column_name)
"#{column_name}=(CASE WHEN (#{updatable_columns.map(&block).join(" AND ")}) THEN #{model.quoted_table_name}.#{column_name} ELSE CURRENT_TIMESTAMP END),"
"#{column_name}=(CASE WHEN (#{updatable_columns.map(&block).join(" AND ")}) THEN #{model.quoted_table_name}.#{column_name} ELSE #{connection.high_precision_current_timestamp} END),"
end
end.join
end

View File

@ -364,6 +364,15 @@ class InsertAllTest < ActiveRecord::TestCase
assert_equal Time.now.year, Book.find(101).updated_on.year
end
def test_upsert_all_respects_updated_at_precision_when_touched_implicitly
skip unless supports_insert_on_duplicate_update? && supports_datetime_with_precision?
Book.insert_all [{ id: 101, name: "Out of the Silent Planet", published_on: Date.new(1938, 4, 1), updated_at: 5.years.ago, updated_on: 5.years.ago }]
Book.upsert_all [{ id: 101, name: "Out of the Silent Planet", published_on: Date.new(1938, 4, 8) }]
assert_not_predicate Book.find(101).updated_at.usec, :zero?, "updated_at should have sub-second precision"
end
def test_upsert_all_uses_given_updated_at_over_implicit_updated_at
skip unless supports_insert_on_duplicate_update?