From da41061b4ea1162fefe3a12b6215e4280ca42bf1 Mon Sep 17 00:00:00 2001 From: Sam Bostock Date: Thu, 19 Aug 2021 18:19:56 -0400 Subject: [PATCH] Add high_precision_current_timestamp to adapters CURRENT_TIMESTAMP provides differing precision on different databases. This method can be used in queries instead to provide a high precision current time regardless of the database being connected to. CURRENT_TIMESTAMP continues to be used as the default, unless overriden. --- .../abstract/database_statements.rb | 13 +++++++++++++ .../mysql/database_statements.rb | 9 +++++++++ .../postgresql/database_statements.rb | 8 ++++++++ .../sqlite3/database_statements.rb | 9 +++++++++ 4 files changed, 39 insertions(+) diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb index 82e0d8e613..4cdd4b7721 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb @@ -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| diff --git a/activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb b/activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb index 610c4efedb..8ea3f06e33 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb @@ -79,6 +79,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 execute_batch(statements, name = nil) combine_multi_statements(statements).each do |statement| diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb index 33010b0b92..796e89378d 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb @@ -122,6 +122,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)) diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3/database_statements.rb b/activerecord/lib/active_record/connection_adapters/sqlite3/database_statements.rb index b61ee405bd..070150ccc7 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3/database_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3/database_statements.rb @@ -95,6 +95,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")