From e709732b9423a922842f172e7cd00a1f49c93388 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 2 Jan 2017 13:15:56 -0500 Subject: [PATCH 1/2] [Rails5] Fix arity of all type initialize methods. --- .../connection_adapters/sqlserver/type/datetime.rb | 5 +++++ .../connection_adapters/sqlserver/type/money.rb | 2 +- .../connection_adapters/sqlserver/type/small_money.rb | 2 +- .../connection_adapters/sqlserver/type/unicode_varchar.rb | 2 +- .../sqlserver/type/unicode_varchar_max.rb | 2 +- .../connection_adapters/sqlserver/type/varbinary.rb | 2 +- .../connection_adapters/sqlserver/type/varbinary_max.rb | 2 +- .../connection_adapters/sqlserver/type/varchar.rb | 2 +- .../connection_adapters/sqlserver/type/varchar_max.rb | 2 +- 9 files changed, 13 insertions(+), 8 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/type/datetime.rb b/lib/active_record/connection_adapters/sqlserver/type/datetime.rb index b6cd2f208..eaef14901 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/datetime.rb @@ -6,6 +6,11 @@ class DateTime < ActiveRecord::Type::DateTime include TimeValueFractional + def initialize(*args) + super + @precision = nil if self.class == DateTime + end + def sqlserver_type 'datetime'.freeze end diff --git a/lib/active_record/connection_adapters/sqlserver/type/money.rb b/lib/active_record/connection_adapters/sqlserver/type/money.rb index e0c93d02a..8d6eed882 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/money.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/money.rb @@ -4,7 +4,7 @@ module SQLServer module Type class Money < Decimal - def initialize(options = {}) + def initialize(*args) super @precision = 19 @scale = 4 diff --git a/lib/active_record/connection_adapters/sqlserver/type/small_money.rb b/lib/active_record/connection_adapters/sqlserver/type/small_money.rb index 0ba487895..5c4dfd49d 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/small_money.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/small_money.rb @@ -4,7 +4,7 @@ module SQLServer module Type class SmallMoney < Money - def initialize(options = {}) + def initialize(*args) super @precision = 10 @scale = 4 diff --git a/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb b/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb index 008320c79..8bcaab4b5 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb @@ -4,7 +4,7 @@ module SQLServer module Type class UnicodeVarchar < UnicodeChar - def initialize(options = {}) + def initialize(*args) super @limit = 4000 if @limit.to_i == 0 end diff --git a/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb b/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb index 5f6990a0b..df6b455a8 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb @@ -4,7 +4,7 @@ module SQLServer module Type class UnicodeVarcharMax < UnicodeVarchar - def initialize(options = {}) + def initialize(*args) super @limit = 2_147_483_647 end diff --git a/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb b/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb index 265af265b..dffe7ee61 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb @@ -4,7 +4,7 @@ module SQLServer module Type class Varbinary < Binary - def initialize(options = {}) + def initialize(*args) super @limit = 8000 if @limit.to_i == 0 end diff --git a/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb b/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb index a251c268a..01087c616 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb @@ -4,7 +4,7 @@ module SQLServer module Type class VarbinaryMax < Varbinary - def initialize(options = {}) + def initialize(*args) super @limit = 2_147_483_647 end diff --git a/lib/active_record/connection_adapters/sqlserver/type/varchar.rb b/lib/active_record/connection_adapters/sqlserver/type/varchar.rb index b3091718a..0e50a17e1 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/varchar.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/varchar.rb @@ -4,7 +4,7 @@ module SQLServer module Type class Varchar < Char - def initialize(options = {}) + def initialize(*args) super @limit = 8000 if @limit.to_i == 0 end diff --git a/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb b/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb index 79fa56464..6d063b5f8 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb @@ -4,7 +4,7 @@ module SQLServer module Type class VarcharMax < Varchar - def initialize(options = {}) + def initialize(*args) super @limit = 2_147_483_647 end From 8893ee9b1db60d3f68d845a02d9c11dce8fa2cb5 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Wed, 4 Jan 2017 21:23:06 -0500 Subject: [PATCH 2/2] [Rails5] Support for `supports_datetime_with_precision`. --- CHANGELOG.md | 2 +- .../sqlserver/schema_statements.rb | 14 +++ .../sqlserver/table_definition.rb | 86 ++++++++++++------- .../sqlserver/type/datetime.rb | 5 -- .../sqlserver/type/time_value_fractional.rb | 33 +++++-- .../connection_adapters/sqlserver_adapter.rb | 11 ++- test/cases/coerced_tests.rb | 26 ++++++ test/cases/column_test_sqlserver.rb | 71 +++++++++++---- 8 files changed, 179 insertions(+), 69 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a421fb6cd..7f1d0578c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ #### Added -* ... +* Support for `supports_datetime_with_precision`. #### Changed diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 492345221..a05d17581 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -188,6 +188,16 @@ def type_to_sql(type, limit = nil, precision = nil, scale = nil) when 5..8 then 'bigint' else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with precision 0 instead.") end + when 'datetime2' + column_type_sql = super + if precision + if (0..7) === precision + column_type_sql << "(#{precision})" + else + raise(ActiveRecordError, "The dattime2 type has precision of #{precision}. The allowed range of precision is from 0 to 7") + end + end + column_type_sql else super end @@ -202,6 +212,10 @@ def columns_for_distinct(columns, orders) [super, *order_columns].join(', ') end + def update_table_definition(table_name, base) + SQLServer::Table.new(table_name, base) + end + def change_column_null(table_name, column_name, allow_null, default = nil) table_id = SQLServer::Utils.extract_identifiers(table_name) column_id = SQLServer::Utils.extract_identifiers(column_name) diff --git a/lib/active_record/connection_adapters/sqlserver/table_definition.rb b/lib/active_record/connection_adapters/sqlserver/table_definition.rb index fd2f6eb1d..730e48c40 100644 --- a/lib/active_record/connection_adapters/sqlserver/table_definition.rb +++ b/lib/active_record/connection_adapters/sqlserver/table_definition.rb @@ -1,7 +1,8 @@ module ActiveRecord module ConnectionAdapters module SQLServer - class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition + + module ColumnMethods def primary_key(name, type = :primary_key, **options) return super unless type == :uuid @@ -10,67 +11,90 @@ def primary_key(name, type = :primary_key, **options) column name, type, options end - def real(name, options = {}) - column(name, :real, options) + def real(*args, **options) + args.each { |name| column(name, :real, options) } + end + + def money(*args, **options) + args.each { |name| column(name, :money, options) } end - def money(name, options = {}) - column(name, :money, options) + def datetime(*args, **options) + args.each do |name| + if options[:precision] + datetime2(name, options) + else + column(name, :datetime, options) + end + end end - def datetime2(name, options = {}) - column(name, :datetime2, options) + def datetime2(*args, **options) + args.each { |name| column(name, :datetime2, options) } end - def datetimeoffset(name, options = {}) - column(name, :datetimeoffset, options) + def datetimeoffset(*args, **options) + args.each { |name| column(name, :datetimeoffset, options) } end - def smallmoney(name, options = {}) - column(name, :smallmoney, options) + def smallmoney(*args, **options) + args.each { |name| column(name, :smallmoney, options) } end - def char(name, options = {}) - column(name, :char, options) + def char(*args, **options) + args.each { |name| column(name, :char, options) } end - def varchar(name, options = {}) - column(name, :varchar, options) + def varchar(*args, **options) + args.each { |name| column(name, :varchar, options) } end - def varchar_max(name, options = {}) - column(name, :varchar_max, options) + def varchar_max(*args, **options) + args.each { |name| column(name, :varchar_max, options) } end - def text_basic(name, options = {}) - column(name, :text_basic, options) + def text_basic(*args, **options) + args.each { |name| column(name, :text_basic, options) } end - def nchar(name, options = {}) - column(name, :nchar, options) + def nchar(*args, **options) + args.each { |name| column(name, :nchar, options) } end - def ntext(name, options = {}) - column(name, :ntext, options) + def ntext(*args, **options) + args.each { |name| column(name, :ntext, options) } end - def binary_basic(name, options = {}) - column(name, :binary_basic, options) + def binary_basic(*args, **options) + args.each { |name| column(name, :binary_basic, options) } end - def varbinary(name, options = {}) - column(name, :varbinary, options) + def varbinary(*args, **options) + args.each { |name| column(name, :varbinary, options) } end - def uuid(name, options = {}) - column(name, :uniqueidentifier, options) + def uuid(*args, **options) + args.each { |name| column(name, :uniqueidentifier, options) } end - def ss_timestamp(name, options = {}) - column(name, :ss_timestamp, options) + def ss_timestamp(*args, **options) + args.each { |name| column(name, :ss_timestamp, options) } end end + + class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition + include ColumnMethods + + def new_column_definition(name, type, options) + type = :datetime2 if type == :datetime && options[:precision] + super name, type, options + end + end + + class Table < ActiveRecord::ConnectionAdapters::Table + include ColumnMethods + end end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/datetime.rb b/lib/active_record/connection_adapters/sqlserver/type/datetime.rb index eaef14901..b6cd2f208 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/datetime.rb @@ -6,11 +6,6 @@ class DateTime < ActiveRecord::Type::DateTime include TimeValueFractional - def initialize(*args) - super - @precision = nil if self.class == DateTime - end - def sqlserver_type 'datetime'.freeze end diff --git a/lib/active_record/connection_adapters/sqlserver/type/time_value_fractional.rb b/lib/active_record/connection_adapters/sqlserver/type/time_value_fractional.rb index d8a2ff1cd..412958795 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/time_value_fractional.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/time_value_fractional.rb @@ -9,18 +9,20 @@ module TimeValueFractional def apply_seconds_precision(value) return value if !value.respond_to?(fractional_property) || value.send(fractional_property).zero? - frac_seconds = if fractional_scale == 0 - 0 - else - seconds = value.send(fractional_property).to_f / fractional_operator.to_f - seconds = ((seconds * (1 / fractional_precision)).round / (1 / fractional_precision)).round(fractional_scale) - (seconds * fractional_operator).to_i - end - value.change fractional_property => frac_seconds + value.change fractional_property => seconds_precision(value) + end + + def seconds_precision(value) + return 0 if fractional_scale == 0 + seconds = value.send(fractional_property).to_f / fractional_operator.to_f + seconds = ((seconds * (1 / fractional_precision)).round / (1 / fractional_precision)).round(fractional_scale) + (seconds * fractional_operator).round(0).to_i end def quote_fractional(value) - seconds = (value.send(fractional_property).to_f / fractional_operator.to_f).round(fractional_scale) + return 0 if fractional_scale == 0 + frac_seconds = seconds_precision(value) + seconds = (frac_seconds.to_f / fractional_operator.to_f).round(fractional_scale) seconds.to_d.to_s.split('.').last.to(fractional_scale-1) end @@ -52,6 +54,11 @@ module TimeValueFractional2 private + def seconds_precision(value) + seconds = super + seconds > fractional_max ? fractional_scale_max : seconds + end + def fractional_property :nsec end @@ -68,6 +75,14 @@ def fractional_scale precision end + def fractional_max + 999999999 + end + + def fractional_scale_max + ('9' * fractional_scale) + ('0' * (fractional_digits - fractional_scale)) + end + end end diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index aa3171a72..e7f0944a8 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -128,7 +128,7 @@ def supports_views? end def supports_datetime_with_precision? - false + true end def supports_json? @@ -268,10 +268,13 @@ def initialize_type_map(m) m.register_type 'real', SQLServer::Type::Real.new # Date and Time m.register_type 'date', SQLServer::Type::Date.new - m.register_type 'datetime', SQLServer::Type::DateTime.new - m.register_type %r{\Adatetime2}i do |sql_type| + m.register_type %r{\Adatetime} do |sql_type| precision = extract_precision(sql_type) - SQLServer::Type::DateTime2.new precision: precision + if precision + SQLServer::Type::DateTime2.new precision: precision + else + SQLServer::Type::DateTime.new + end end m.register_type %r{\Adatetimeoffset}i do |sql_type| precision = extract_precision(sql_type) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 85bfd871c..b63826da0 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -612,3 +612,29 @@ def test_types_of_virtual_columns_are_not_changed_on_round_trip_coerced assert_equal 5, dumped.posts_count end end + + + + +class DateTimePrecisionTest < ActiveRecord::TestCase + # Original test had `7` which we support vs `8` which we use. + coerce_tests! :test_invalid_datetime_precision_raises_error + def test_invalid_datetime_precision_raises_error_coerced + assert_raises ActiveRecord::ActiveRecordError do + @connection.create_table(:foos, force: true) do |t| + t.timestamps precision: 8 + end + end + end + + # Original test uses `datetime` vs `datetime2` type which we dynamically use. + coerce_tests! :test_schema_dump_includes_datetime_precision + def test_schema_dump_includes_datetime_precision_coerced + @connection.create_table(:foos, force: true) do |t| + t.timestamps precision: 6 + end + output = dump_table_schema("foos") + assert_match %r{t\.datetime2\s+"created_at",\s+precision: 6,\s+null: false$}, output + assert_match %r{t\.datetime2\s+"updated_at",\s+precision: 6,\s+null: false$}, output + end +end diff --git a/test/cases/column_test_sqlserver.rb b/test/cases/column_test_sqlserver.rb index d3a2e217e..661288029 100644 --- a/test/cases/column_test_sqlserver.rb +++ b/test/cases/column_test_sqlserver.rb @@ -309,26 +309,43 @@ def assert_obj_set_and_save(attribute, value) col.sql_type.must_equal 'datetime' col.type.must_equal :datetime col.null.must_equal true - col.default.must_equal Time.utc(1753, 01, 01, 00, 00, 00, 123000), "Microseconds were <#{col.default.usec}> vs <123000>" - obj.datetime.must_equal Time.utc(1753, 01, 01, 00, 00, 00, 123000), "Microseconds were <#{obj.datetime.usec}> vs <123000>" + time = Time.utc 1753, 01, 01, 00, 00, 00, 123000 + col.default.must_equal time, "Microseconds were <#{col.default.usec}> vs <123000>" + obj.datetime.must_equal time, "Microseconds were <#{obj.datetime.usec}> vs <123000>" col.default_function.must_be_nil type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::DateTime type.limit.must_be_nil type.precision.must_be_nil type.scale.must_be_nil + obj.save! + obj.must_equal obj.class.where(datetime: time).first # Can save to proper accuracy and return again. - obj.datetime = Time.utc(2010, 04, 01, 12, 34, 56, 3000) - obj.datetime.must_equal Time.utc(2010, 04, 01, 12, 34, 56, 3000), "Microseconds were <#{obj.datetime.usec}> vs <3000>" + time = Time.utc 2010, 04, 01, 12, 34, 56, 3000 + obj.datetime = time + obj.datetime.must_equal time, "Microseconds were <#{obj.datetime.usec}> vs <3000>" obj.save! - obj.datetime.must_equal Time.utc(2010, 04, 01, 12, 34, 56, 3000), "Microseconds were <#{obj.reload.datetime.usec}> vs <3000>" + obj.datetime.must_equal time, "Microseconds were <#{obj.datetime.usec}> vs <3000>" obj.reload - obj.datetime.must_equal Time.utc(2010, 04, 01, 12, 34, 56, 3000), "Microseconds were <#{obj.reload.datetime.usec}> vs <3000>" + obj.datetime.must_equal time, "Microseconds were <#{obj.datetime.usec}> vs <3000>" + obj.must_equal obj.class.where(datetime: time).first # Will cast to true DB value on attribute write, save and return again. - obj.datetime = Time.utc(2010, 04, 01, 12, 34, 56, 234567) - obj.datetime.must_equal Time.utc(2010, 04, 01, 12, 34, 56, 233000), "Microseconds were <#{obj.datetime.usec}> vs <233000>" + time = Time.utc 2010, 04, 01, 12, 34, 56, 234567 + time2 = Time.utc 2010, 04, 01, 12, 34, 56, 233000 + obj.datetime = time + obj.datetime.must_equal time2, "Microseconds were <#{obj.datetime.usec}> vs <233000>" + obj.save! + obj.datetime.must_equal time2, "Microseconds were <#{obj.datetime.usec}> vs <233000>" + obj.reload + obj.datetime.must_equal time2, "Microseconds were <#{obj.datetime.usec}> vs <233000>" + obj.must_equal obj.class.where(datetime: time).first + obj.must_equal obj.class.where(datetime: time2).first + # Set and find nil. + obj.datetime = nil + obj.datetime.must_be_nil obj.save! - obj.reload.datetime.must_equal Time.utc(2010, 04, 01, 12, 34, 56, 233000), "Microseconds were <#{obj.reload.datetime.usec}> vs <233000>" + obj.datetime.must_be_nil + obj.must_equal obj.class.where(datetime: nil).first end it 'datetime2' do @@ -337,27 +354,38 @@ def assert_obj_set_and_save(attribute, value) col.sql_type.must_equal 'datetime2(7)' col.type.must_equal :datetime2 col.null.must_equal true - col.default.must_equal Time.utc(9999, 12, 31, 23, 59, 59, Rational(999999900, 1000)), "Nanoseconds were <#{col.default.nsec}> vs <999999900>" - obj.datetime2_7.must_equal Time.utc(9999, 12, 31, 23, 59, 59, Rational(999999900, 1000)), "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <999999900>" + time = Time.utc 9999, 12, 31, 23, 59, 59, Rational(999999900, 1000) + col.default.must_equal time, "Nanoseconds were <#{col.default.nsec}> vs <999999900>" + obj.datetime2_7.must_equal time, "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <999999900>" col.default_function.must_be_nil type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::DateTime2 type.limit.must_be_nil type.precision.must_equal 7 type.scale.must_be_nil + obj.save! + obj.must_equal obj.class.where(datetime2_7: time).first # Can save 100 nanosecond precisoins and return again. - obj.datetime2_7 = Time.utc(9999, 12, 31, 23, 59, 59, Rational(123456755, 1000)) - obj.datetime2_7.must_equal Time.utc(9999, 12, 31, 23, 59, 59, Rational(123456800, 1000)), "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <123456800>" + time = Time.utc 9999, 12, 31, 23, 59, 59, Rational(123456755, 1000) + time2 = Time.utc 9999, 12, 31, 23, 59, 59, Rational(123456800, 1000) + obj.datetime2_7 = time + obj.datetime2_7.must_equal time2, "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <123456800>" obj.save! - obj.datetime2_7.must_equal Time.utc(9999, 12, 31, 23, 59, 59, Rational(123456800, 1000)), "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <123456800>" + obj.datetime2_7.must_equal time2, "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <123456800>" obj.reload - obj.datetime2_7.must_equal Time.utc(9999, 12, 31, 23, 59, 59, Rational(123456800, 1000)), "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <123456800>" + obj.datetime2_7.must_equal time2, "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <123456800>" + obj.must_equal obj.class.where(datetime2_7: time).first + obj.must_equal obj.class.where(datetime2_7: time2).first # Can save small fraction nanosecond precisoins and return again. - obj.datetime2_7 = Time.utc(2008, 6, 21, 13, 30, 0, Rational(15020, 1000)) - obj.datetime2_7.must_equal Time.utc(2008, 6, 21, 13, 30, 0, Rational(15000, 1000)), "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <15000>" + time = Time.utc 2008, 6, 21, 13, 30, 0, Rational(15020, 1000) + time2 = Time.utc 2008, 6, 21, 13, 30, 0, Rational(15000, 1000) + obj.datetime2_7 = time + obj.datetime2_7.must_equal time2, "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <15000>" obj.save! - obj.reload.datetime2_7.must_equal Time.utc(2008, 6, 21, 13, 30, 0, Rational(15000, 1000)), "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <15000>" - # With other precisions. + obj.reload.datetime2_7.must_equal time2, "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <15000>" + obj.must_equal obj.class.where(datetime2_7: time).first + obj.must_equal obj.class.where(datetime2_7: time2).first + # datetime2_3 time = Time.utc 9999, 12, 31, 23, 59, 59, Rational(123456789, 1000) col = column('datetime2_3') connection.lookup_cast_type_from_column(col).precision.must_equal 3 @@ -365,12 +393,16 @@ def assert_obj_set_and_save(attribute, value) obj.datetime2_3.must_equal time.change(nsec: 123000000), "Nanoseconds were <#{obj.datetime2_3.nsec}> vs <123000000>" obj.save! ; obj.reload obj.datetime2_3.must_equal time.change(nsec: 123000000), "Nanoseconds were <#{obj.datetime2_3.nsec}> vs <123000000>" + obj.must_equal obj.class.where(datetime2_3: time).first + # datetime2_1 col = column('datetime2_1') connection.lookup_cast_type_from_column(col).precision.must_equal 1 obj.datetime2_1 = time obj.datetime2_1.must_equal time.change(nsec: 100000000), "Nanoseconds were <#{obj.datetime2_1.nsec}> vs <100000000>" obj.save! ; obj.reload obj.datetime2_1.must_equal time.change(nsec: 100000000), "Nanoseconds were <#{obj.datetime2_1.nsec}> vs <100000000>" + obj.must_equal obj.class.where(datetime2_1: time).first + # datetime2_0 col = column('datetime2_0') connection.lookup_cast_type_from_column(col).precision.must_equal 0 time = Time.utc 2016, 4, 19, 16, 45, 40, 771036 @@ -378,6 +410,7 @@ def assert_obj_set_and_save(attribute, value) obj.datetime2_0.must_equal time.change(nsec: 0), "Nanoseconds were <#{obj.datetime2_0.nsec}> vs <0>" obj.save! ; obj.reload obj.datetime2_0.must_equal time.change(nsec: 0), "Nanoseconds were <#{obj.datetime2_0.nsec}> vs <0>" + obj.must_equal obj.class.where(datetime2_0: time).first end it 'datetimeoffset' do