1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Set precision 6 by default for datetime

💇‍♀️

Set precision 6 by detauls for datime columns when using sqlite3_adapter

 Set precision 6 by detauls for datime columns when using postgresql_adapter

Update activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb

Co-authored-by: Zachary Scott <zzakscott@gmail.com>

Update tests precision

Add datetime precision by using the migration compatibility layer

Add default precision when adding new column for a table

Fix compatibility tests

Add changelog entry

Update postgres test cases

Custom type does not support precision

Typo ⌨️

Fix specific mysql2 schema

💇‍♀️
This commit is contained in:
Roberto Miranda 2021-05-26 16:12:39 +01:00
parent 9053b8f77f
commit c2a6f618d2
11 changed files with 100 additions and 17 deletions

View file

@ -1,3 +1,9 @@
* Set precision 6 by default for `datetime` columns
By default, datetime columns will have microseconds precision instead of seconds precision.
*Roberto Miranda*
* Allow preloading of associations with instance dependent scopes
*John Hawthorn*, *John Crepezzi*, *Adam Hess*, *Eileen M. Uchitelle*, *Dinah Shi*

View file

@ -411,6 +411,12 @@ module ActiveRecord
end
end
if @conn.supports_datetime_with_precision?
if type == :datetime && !options.key?(:precision)
options[:precision] = 6
end
end
@columns_hash[name] = new_column_definition(name, type, **options)
if index

View file

@ -613,6 +613,12 @@ module ActiveRecord
def add_column(table_name, column_name, type, **options)
return if options[:if_not_exists] == true && column_exists?(table_name, column_name)
if supports_datetime_with_precision?
if type == :datetime && !options.key?(:precision)
options[:precision] = 6
end
end
at = create_alter_table table_name
at.add_column(column_name, type, **options)
execute schema_creation.accept at

View file

@ -295,6 +295,11 @@ module ActiveRecord
options[:null] = true if options[:null].nil?
super
end
def column(name, type, index: nil, **options)
options[:precision] ||= nil
super
end
end
def add_reference(table_name, ref_name, **options)
@ -324,6 +329,14 @@ module ActiveRecord
super
end
def add_column(table_name, column_name, type, **options)
if type == :datetime
options[:precision] ||= nil
end
super
end
private
def compatible_table_definition(t)
class << t

View file

@ -125,7 +125,7 @@ class PostgresqlTimestampMigrationTest < ActiveRecord::PostgreSQLTestCase
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::NATIVE_DATABASE_TYPES[:datetimes_as_enum] = { name: "custom_time_format" }
with_postgresql_datetime_type(:datetimes_as_enum) do
ActiveRecord::Migration.new.add_column :postgresql_timestamp_with_zones, :times, :datetime
ActiveRecord::Migration.new.add_column :postgresql_timestamp_with_zones, :times, :datetime, precision: nil
assert_equal({ "data_type" => "USER-DEFINED", "udt_name" => "custom_time_format" },
PostgresqlTimestampWithZone.connection.execute("select data_type, udt_name from information_schema.columns where column_name = 'times'").to_a.first)

View file

@ -48,8 +48,8 @@ if supports_datetime_with_precision?
unless current_adapter?(:Mysql2Adapter)
def test_no_datetime_precision_isnt_truncated_on_assignment
@connection.create_table(:foos, force: true)
@connection.add_column :foos, :created_at, :datetime
@connection.add_column :foos, :updated_at, :datetime, precision: 6
@connection.add_column :foos, :created_at, :datetime, precision: nil
@connection.add_column :foos, :updated_at, :datetime
time = ::Time.now.change(nsec: 123)
foo = Foo.new(created_at: time, updated_at: time)

View file

@ -91,13 +91,13 @@ if current_adapter?(:PostgreSQLAdapter)
output = dump_table_schema("defaults")
if ActiveRecord::Base.connection.database_version >= 100000
assert_match %r/t\.date\s+"modified_date",\s+default: -> { "CURRENT_DATE" }/, output
assert_match %r/t\.datetime\s+"modified_time",\s+default: -> { "CURRENT_TIMESTAMP" }/, output
assert_match %r/t\.datetime\s+"modified_time",\s+precision: 6,\s+default: -> { "CURRENT_TIMESTAMP" }/, output
else
assert_match %r/t\.date\s+"modified_date",\s+default: -> { "\('now'::text\)::date" }/, output
assert_match %r/t\.datetime\s+"modified_time",\s+default: -> { "now\(\)" }/, output
assert_match %r/t\.datetime\s+"modified_time",\s+precision: 6,\s+default: -> { "now\(\)" }/, output
end
assert_match %r/t\.date\s+"modified_date_function",\s+default: -> { "now\(\)" }/, output
assert_match %r/t\.datetime\s+"modified_time_function",\s+default: -> { "now\(\)" }/, output
assert_match %r/t\.datetime\s+"modified_time_function",\s+precision: 6,\s+default: -> { "now\(\)" }/, output
end
end
end

View file

@ -300,11 +300,11 @@ module ActiveRecord
assert_equal :datetime, column.type
if current_adapter?(:PostgreSQLAdapter)
assert_equal "timestamp without time zone", column.sql_type
assert_equal "timestamp(6) without time zone", column.sql_type
elsif current_adapter?(:Mysql2Adapter)
assert_equal "datetime", column.sql_type
assert_equal "datetime(6)", column.sql_type
else
assert_equal connection.type_to_sql("datetime"), column.sql_type
assert_equal connection.type_to_sql("datetime(6)"), column.sql_type
end
end
@ -318,7 +318,7 @@ module ActiveRecord
column = connection.columns(:testings).find { |c| c.name == "foo" }
assert_equal :datetime, column.type
assert_equal "timestamp with time zone", column.sql_type
assert_equal "timestamp(6) with time zone", column.sql_type
end
end
end
@ -341,7 +341,7 @@ module ActiveRecord
elsif current_adapter?(:OracleAdapter)
assert_equal "TIMESTAMP(6)", column.sql_type
else
assert_equal connection.type_to_sql("datetime"), column.sql_type
assert_equal connection.type_to_sql("datetime(6)"), column.sql_type
end
end

View file

@ -335,6 +335,58 @@ module ActiveRecord
assert connection.index_exists?(:testings, [:gizmo_type, :gizmo_id], name: :index_testings_on_gizmo_type_and_gizmo_id)
end
def test_datetime_doesnt_set_precision_on_create_table
migration = Class.new(ActiveRecord::Migration[4.2]) {
def migrate(x)
create_table :more_testings do |t|
t.datetime :published_at
end
end
}.new
ActiveRecord::Migrator.new(:up, [migration], @schema_migration).migrate
assert connection.column_exists?(:more_testings, :published_at, **precision_implicit_default)
ensure
connection.drop_table :more_testings rescue nil
end
def test_datetime_doesnt_set_precision_on_change_table
create_migration = Class.new(ActiveRecord::Migration[4.2]) {
def migrate(x)
create_table :more_testings do |t|
t.datetime :published_at
end
end
}.new
change_migration = Class.new(ActiveRecord::Migration[4.2]) {
def migrate(x)
change_table :more_testings do |t|
t.datetime :published_at, default: Time.now
end
end
}.new
ActiveRecord::Migrator.new(:up, [create_migration, change_migration], @schema_migration).migrate
assert connection.column_exists?(:more_testings, :published_at, **precision_implicit_default)
ensure
connection.drop_table :more_testings rescue nil
end
def test_datetime_doesnt_set_precision_on_add_column
migration = Class.new(ActiveRecord::Migration[4.2]) {
def migrate(x)
add_column :testings, :published_at, :datetime, default: Time.now
end
}.new
ActiveRecord::Migrator.new(:up, [migration], @schema_migration).migrate
assert connection.column_exists?(:testings, :published_at, **precision_implicit_default)
end
private
def precision_implicit_default
if current_adapter?(:Mysql2Adapter)

View file

@ -837,7 +837,7 @@ class SchemaDumperDefaultsTest < ActiveRecord::TestCase
assert_match %r{t\.string\s+"string_with_default",.*?default: "Hello!"}, output
assert_match %r{t\.date\s+"date_with_default",\s+default: "2014-06-05"}, output
assert_match %r{t\.datetime\s+"datetime_with_default",\s+default: "2014-06-05 07:17:04"}, output
assert_match %r{t\.datetime\s+"datetime_with_default",\s+precision: 6,\s+default: "2014-06-05 07:17:04"}, output
assert_match %r{t\.time\s+"time_with_default",\s+default: "2000-01-01 07:17:04"}, output
assert_match %r{t\.decimal\s+"decimal_with_default",\s+precision: 20,\s+scale: 10,\s+default: "1234567890.0123456789"}, output
end
@ -847,8 +847,8 @@ class SchemaDumperDefaultsTest < ActiveRecord::TestCase
output = dump_table_schema("infinity_defaults")
assert_match %r{t\.float\s+"float_with_inf_default",\s+default: ::Float::INFINITY}, output
assert_match %r{t\.float\s+"float_with_nan_default",\s+default: ::Float::NAN}, output
assert_match %r{t\.datetime\s+"beginning_of_time",\s+default: -::Float::INFINITY}, output
assert_match %r{t\.datetime\s+"end_of_time",\s+default: ::Float::INFINITY}, output
assert_match %r{t\.datetime\s+"beginning_of_time",\s+precision: 6,\s+default: -::Float::INFINITY}, output
assert_match %r{t\.datetime\s+"end_of_time",\s+precision: 6,\s+default: ::Float::INFINITY}, output
assert_match %r{t\.date\s+"date_with_neg_inf_default",\s+default: -::Float::INFINITY}, output
assert_match %r{t\.date\s+"date_with_pos_inf_default",\s+default: ::Float::INFINITY}, output
end

View file

@ -3,13 +3,13 @@
ActiveRecord::Schema.define do
if supports_datetime_with_precision?
create_table :datetime_defaults, force: true do |t|
t.datetime :modified_datetime, default: -> { "CURRENT_TIMESTAMP" }
t.datetime :precise_datetime, precision: 6, default: -> { "CURRENT_TIMESTAMP(6)" }
t.datetime :modified_datetime, precision: nil, default: -> { "CURRENT_TIMESTAMP" }
t.datetime :precise_datetime, default: -> { "CURRENT_TIMESTAMP(6)" }
end
create_table :timestamp_defaults, force: true do |t|
t.timestamp :nullable_timestamp
t.timestamp :modified_timestamp, default: -> { "CURRENT_TIMESTAMP" }
t.timestamp :modified_timestamp, precision: nil, default: -> { "CURRENT_TIMESTAMP" }
t.timestamp :precise_timestamp, precision: 6, default: -> { "CURRENT_TIMESTAMP(6)" }
end
end