mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Merge pull request #42993 from sambostock/use-precision-in-updated-at-upsert
Use correct precision when touching `updated_at` column in upsert
This commit is contained in:
commit
0b5c7210af
8 changed files with 68 additions and 3 deletions
|
@ -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 ssl support for postgresql database tasks
|
||||
|
||||
Add `PGSSLMODE`, `PGSSLCERT`, `PGSSLKEY` and `PGSSLROOTCERT` to pg_env from database config
|
||||
|
|
|
@ -441,6 +441,19 @@ module ActiveRecord
|
|||
end
|
||||
end
|
||||
|
||||
# This is a safe default, even if not high precision on all databases
|
||||
HIGH_PRECISION_CURRENT_TIMESTAMP = Arel.sql("CURRENT_TIMESTAMP").freeze # :nodoc:
|
||||
private_constant :HIGH_PRECISION_CURRENT_TIMESTAMP
|
||||
|
||||
# Returns an Arel SQL literal for the CURRENT_TIMESTAMP for usage with
|
||||
# arbitrary precision date/time columns.
|
||||
#
|
||||
# Adapters supporting datetime with precision should override this to
|
||||
# provide as much precision as is available.
|
||||
def high_precision_current_timestamp
|
||||
HIGH_PRECISION_CURRENT_TIMESTAMP
|
||||
end
|
||||
|
||||
private
|
||||
def execute_batch(statements, name = nil)
|
||||
statements.each do |statement|
|
||||
|
|
|
@ -76,6 +76,15 @@ module ActiveRecord
|
|||
end
|
||||
alias :exec_update :exec_delete
|
||||
|
||||
# https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_current-timestamp
|
||||
# https://dev.mysql.com/doc/refman/5.7/en/date-and-time-type-syntax.html
|
||||
HIGH_PRECISION_CURRENT_TIMESTAMP = Arel.sql("CURRENT_TIMESTAMP(6)").freeze # :nodoc:
|
||||
private_constant :HIGH_PRECISION_CURRENT_TIMESTAMP
|
||||
|
||||
def high_precision_current_timestamp
|
||||
HIGH_PRECISION_CURRENT_TIMESTAMP
|
||||
end
|
||||
|
||||
private
|
||||
def raw_execute(sql, name, async: false)
|
||||
# make sure we carry over any changes to ActiveRecord.default_timezone that have been
|
||||
|
|
|
@ -123,6 +123,14 @@ module ActiveRecord
|
|||
execute("ROLLBACK", "TRANSACTION")
|
||||
end
|
||||
|
||||
# From https://www.postgresql.org/docs/current/functions-datetime.html#FUNCTIONS-DATETIME-CURRENT
|
||||
HIGH_PRECISION_CURRENT_TIMESTAMP = Arel.sql("CURRENT_TIMESTAMP").freeze # :nodoc:
|
||||
private_constant :HIGH_PRECISION_CURRENT_TIMESTAMP
|
||||
|
||||
def high_precision_current_timestamp
|
||||
HIGH_PRECISION_CURRENT_TIMESTAMP
|
||||
end
|
||||
|
||||
private
|
||||
def execute_batch(statements, name = nil)
|
||||
execute(combine_multi_statements(statements))
|
||||
|
|
|
@ -97,6 +97,15 @@ module ActiveRecord
|
|||
reset_read_uncommitted
|
||||
end
|
||||
|
||||
# https://stackoverflow.com/questions/17574784
|
||||
# https://www.sqlite.org/lang_datefunc.html
|
||||
HIGH_PRECISION_CURRENT_TIMESTAMP = Arel.sql("STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')").freeze # :nodoc:
|
||||
private_constant :HIGH_PRECISION_CURRENT_TIMESTAMP
|
||||
|
||||
def high_precision_current_timestamp
|
||||
HIGH_PRECISION_CURRENT_TIMESTAMP
|
||||
end
|
||||
|
||||
private
|
||||
def reset_read_uncommitted
|
||||
read_uncommitted = Thread.current.thread_variable_get("read_uncommitted")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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?
|
||||
|
||||
|
|
|
@ -119,8 +119,13 @@ ActiveRecord::Schema.define do
|
|||
t.index :isbn, where: "published_on IS NOT NULL", unique: true
|
||||
t.index "(lower(external_id))", unique: true if supports_expression_index?
|
||||
|
||||
t.datetime :created_at
|
||||
t.datetime :updated_at
|
||||
if supports_datetime_with_precision?
|
||||
t.datetime :created_at, precision: 6
|
||||
t.datetime :updated_at, precision: 6
|
||||
else
|
||||
t.datetime :created_at
|
||||
t.datetime :updated_at
|
||||
end
|
||||
t.date :updated_on
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in a new issue