From 5342848e6b924a5fbdce2b48872804fcbf78830e Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 3 Jan 2016 09:02:24 -0500 Subject: [PATCH] Support 2008 Datatypes Using TDSVER=7.3. Fixes #433 * All datetime casting uses the `Time::DATE_FORMATS[:_sqlserver_*]` formats set after connection. * Removed `SQLServer::Utils.with_sqlserver_db_date_formats` helper and `quoted_date` hacks. * Removed `Quoter` value type which allowed column => type special case quoting. cc @sgrif * Every time datatype has perfect micro/nano second handling. Includes schema dumping support. --- CHANGELOG.md | 15 ++ README.md | 66 ++++--- appveyor.yml | 4 +- .../connection_adapters/sqlserver/quoting.rb | 20 +-- .../sqlserver/schema_statements.rb | 5 + .../sqlserver/table_definition.rb | 8 + .../connection_adapters/sqlserver/type.rb | 4 +- .../sqlserver/type/date.rb | 9 + .../sqlserver/type/datetime.rb | 30 ++-- .../sqlserver/type/datetime2.rb | 17 ++ .../sqlserver/type/datetimeoffset.rb | 31 ++++ .../sqlserver/type/quoter.rb | 32 ---- .../sqlserver/type/smalldatetime.rb | 12 +- .../sqlserver/type/time.rb | 42 ++--- .../sqlserver/type/time_value_fractional.rb | 72 ++++++++ .../connection_adapters/sqlserver/utils.rb | 12 -- .../connection_adapters/sqlserver_adapter.rb | 17 +- test/cases/coerced_tests.rb | 7 + test/cases/column_test_sqlserver.rb | 167 +++++++++++++----- test/cases/schema_dumper_test_sqlserver.rb | 39 ++-- test/schema/datatypes/2012.sql | 26 +-- test/schema/sqlserver_specific_schema.rb | 26 +-- 22 files changed, 436 insertions(+), 225 deletions(-) create mode 100644 lib/active_record/connection_adapters/sqlserver/type/datetime2.rb create mode 100644 lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb delete mode 100644 lib/active_record/connection_adapters/sqlserver/type/quoter.rb create mode 100644 lib/active_record/connection_adapters/sqlserver/type/time_value_fractional.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index fb91a7cc8..91e230827 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,26 @@ ## v4.2.7 +#### Added + +* Support 2008 Datatypes Using TDSVER=7.3. Fixes #433 + #### Changed * Test now use latest v0.9.5 of TinyTDS. * Make linked servers stronger. Fixes #427. Thanks @jippeholwerda * Use proper module for the `sqlserver_connection` method. Fixes #431. Thanks @jippeholwerda +* All datetime casting using the `Time::DATE_FORMATS[:_sqlserver_*]` formats set after connection. + +#### Removed + +* The `SQLServer::Utils.with_sqlserver_db_date_formats` helper and `quoted_date` hacks. +* The `Quoter` value type which allowed column => type special case quoting. + +#### Fixed + +* Every time datatype has perfect micro/nano second handling. +* All supported datatypes dump defaults properly to schema.rb ## v4.2.6 diff --git a/README.md b/README.md index 6b552ac05..bec8e6a15 100644 --- a/README.md +++ b/README.md @@ -25,36 +25,50 @@ Account.execute_procedure :update_totals, named: 'params' #### Native Data Type Support -We support every data type supported by FreeTDS and then a few more. All simplified Rails types in migrations will coorespond to a matching SQL Server national data type. Here is a basic chart. Always check the `initialize_native_database_types` method for an updated list. +We support every data type supported by FreeTDS and then a few more. All simplified Rails types in migrations will coorespond to a matching SQL Server national (unicode) data type. Here is a basic chart. Always check the `initialize_native_database_types` method for an updated list. ```ruby -integer: { name: 'int', limit: 4 } -bigint: { name: 'bigint' } -boolean: { name: 'bit' } -decimal: { name: 'decimal' } -money: { name: 'money' } -smallmoney: { name: 'smallmoney' } -float: { name: 'float' } -real: { name: 'real' } -date: { name: 'date' } -datetime: { name: 'datetime' } -timestamp: { name: 'datetime' } -time: { name: 'time' } -char: { name: 'char' } -varchar: { name: 'varchar', limit: 8000 } -varchar_max: { name: 'varchar(max)' } -text_basic: { name: 'text' } -nchar: { name: 'nchar' } -string: { name: 'nvarchar', limit: 4000 } -text: { name: 'nvarchar(max)' } -ntext: { name: 'ntext' } -binary_basic: { name: 'binary' } -varbinary: { name: 'varbinary', limit: 8000 } -binary: { name: 'varbinary(max)' } -uuid: { name: 'uniqueidentifier' } -ss_timestamp: { name: 'timestamp' } +integer: { name: 'int', limit: 4 } +bigint: { name: 'bigint' } +boolean: { name: 'bit' } +decimal: { name: 'decimal' } +money: { name: 'money' } +smallmoney: { name: 'smallmoney' } +float: { name: 'float' } +real: { name: 'real' } +date: { name: 'date' } +datetime: { name: 'datetime' } +datetime2: { name: 'datetime2', precision: 7 } +datetimeoffset: { name: 'datetimeoffset', precision: 7 } +smalldatetime: { name: 'smalldatetime' } +timestamp: { name: 'datetime' } +time: { name: 'time' } +char: { name: 'char' } +varchar: { name: 'varchar', limit: 8000 } +varchar_max: { name: 'varchar(max)' } +text_basic: { name: 'text' } +nchar: { name: 'nchar' } +string: { name: 'nvarchar', limit: 4000 } +text: { name: 'nvarchar(max)' } +ntext: { name: 'ntext' } +binary_basic: { name: 'binary' } +varbinary: { name: 'varbinary', limit: 8000 } +binary: { name: 'varbinary(max)' } +uuid: { name: 'uniqueidentifier' } +ss_timestamp: { name: 'timestamp' } ``` +The following types require TDS version 7.3 with TinyTDS. This requires the latest FreeTDS v0.95 or higher. + +* date +* datetime2 +* datetimeoffset +* time + +Set `tds_version` in your database.yml or the `TDSVER` environment variable to `7.3` to ensure you are using the proper protocol version till 7.3 becomes the default. + +**Zone Conversion** - The `[datetimeoffset]` type is the only ActiveRecord time based datatype that does not cast the zone to ActiveRecord's default - typically UTC. As intended, this datatype is meant to maintain the zone you pass to it and/or retreived from the database. + #### Force Schema To Lowercase diff --git a/appveyor.yml b/appveyor.yml index f46f1d02a..450e2c4cd 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,7 +2,7 @@ init: - SET PATH=C:\Ruby%ruby_version%\bin;%PATH% - SET PATH=C:\MinGW\msys\1.0\bin;%PATH% - SET RAKEOPT=-rdevkit - - SET TINYTDS_VERSION=0.9.5.beta.4 + - SET TINYTDS_VERSION=0.9.5.beta.7 clone_depth: 5 skip_tags: true matrix: @@ -20,11 +20,13 @@ test_script: - timeout /t 4 /nobreak > NUL - sqlcmd -S ".\SQL2014" -U sa -P Password12! -i %APPVEYOR_BUILD_FOLDER%\test\appveyor\dbsetup.sql - bundle exec rake test ACTIVERECORD_UNITTEST_DATASERVER="localhost\SQL2014" + - bundle exec rake test ACTIVERECORD_UNITTEST_DATASERVER="localhost\SQL2014" ACTIVERECORD_UNITTEST_TDSVERSION="7.3" - ps: Stop-Service 'MSSQL$SQL2014' - ps: Start-Service 'MSSQL$SQL2012SP1' - timeout /t 4 /nobreak > NUL - sqlcmd -S ".\SQL2012SP1" -U sa -P Password12! -i %APPVEYOR_BUILD_FOLDER%\test\appveyor\dbsetup.sql - bundle exec rake test ACTIVERECORD_UNITTEST_DATASERVER="localhost\SQL2012SP1" + - bundle exec rake test ACTIVERECORD_UNITTEST_DATASERVER="localhost\SQL2012SP1" ACTIVERECORD_UNITTEST_TDSVERSION="7.3" environment: matrix: - ruby_version: "200-x64" diff --git a/lib/active_record/connection_adapters/sqlserver/quoting.rb b/lib/active_record/connection_adapters/sqlserver/quoting.rb index 3678459ae..767b4faaf 100644 --- a/lib/active_record/connection_adapters/sqlserver/quoting.rb +++ b/lib/active_record/connection_adapters/sqlserver/quoting.rb @@ -40,15 +40,10 @@ def unquoted_false end def quoted_date(value) - SQLServer::Utils.with_sqlserver_db_date_formats do - if value.acts_like?(:time) && value.respond_to?(:usec) - precision = (BigDecimal(value.usec.to_s) / 1_000_000).round(3).to_s.split('.').last - "#{super}.#{precision}" - elsif value.acts_like?(:date) - value.to_s(:_sqlserver_dateformat) - else - super - end + if value.acts_like?(:date) + Type::Date.new.type_cast_for_database(value) + else value.acts_like?(:time) + Type::DateTime.new.type_cast_for_database(value) end end @@ -59,8 +54,6 @@ def _quote(value) case value when Type::Binary::Data "0x#{value.hex}" - when SQLServer::Type::Quoter - value.quote_ss_value when String, ActiveSupport::Multibyte::Chars if value.is_utf8? "#{QUOTED_STRING_PREFIX}#{super}" @@ -72,11 +65,6 @@ def _quote(value) end end - def quoted_value_acts_like_time_filter(value) - zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal - value.respond_to?(zone_conversion_method) ? value.send(zone_conversion_method) : value - end - end end end diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 9430722a5..82fb5aaae 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -201,6 +201,9 @@ def initialize_native_database_types real: { name: 'real' }, date: { name: 'date' }, datetime: { name: 'datetime' }, + datetime2: { name: 'datetime2' }, + datetimeoffset: { name: 'datetimeoffset' }, + smalldatetime: { name: 'smalldatetime' }, timestamp: { name: 'datetime' }, time: { name: 'time' }, char: { name: 'char' }, @@ -286,6 +289,8 @@ def column_definitions(table_name) ci[:type] = case ci[:type] when /^bit|image|text|ntext|datetime$/ ci[:type] + when /^datetime2|datetimeoffset$/i + "#{ci[:type]}(#{ci[:datetime_precision]})" when /^time$/i "#{ci[:type]}(#{ci[:datetime_precision]})" when /^numeric|decimal$/i diff --git a/lib/active_record/connection_adapters/sqlserver/table_definition.rb b/lib/active_record/connection_adapters/sqlserver/table_definition.rb index b438f4844..1fcc5f757 100644 --- a/lib/active_record/connection_adapters/sqlserver/table_definition.rb +++ b/lib/active_record/connection_adapters/sqlserver/table_definition.rb @@ -18,6 +18,14 @@ def money(name, options = {}) column(name, :money, options) end + def datetime2(name, options = {}) + column(name, :datetime2, options) + end + + def datetimeoffset(name, options = {}) + column(name, :datetimeoffset, options) + end + def smallmoney(name, options = {}) column(name, :smallmoney, options) end diff --git a/lib/active_record/connection_adapters/sqlserver/type.rb b/lib/active_record/connection_adapters/sqlserver/type.rb index 298000274..d115417c7 100644 --- a/lib/active_record/connection_adapters/sqlserver/type.rb +++ b/lib/active_record/connection_adapters/sqlserver/type.rb @@ -1,5 +1,4 @@ require 'active_record/type' -require 'active_record/connection_adapters/sqlserver/type/quoter.rb' # Exact Numerics require 'active_record/connection_adapters/sqlserver/type/integer.rb' require 'active_record/connection_adapters/sqlserver/type/big_integer.rb' @@ -13,8 +12,11 @@ require 'active_record/connection_adapters/sqlserver/type/float.rb' require 'active_record/connection_adapters/sqlserver/type/real.rb' # Date and Time +require 'active_record/connection_adapters/sqlserver/type/time_value_fractional.rb' require 'active_record/connection_adapters/sqlserver/type/date.rb' require 'active_record/connection_adapters/sqlserver/type/datetime.rb' +require 'active_record/connection_adapters/sqlserver/type/datetime2.rb' +require 'active_record/connection_adapters/sqlserver/type/datetimeoffset.rb' require 'active_record/connection_adapters/sqlserver/type/smalldatetime.rb' require 'active_record/connection_adapters/sqlserver/type/time.rb' # Character Strings diff --git a/lib/active_record/connection_adapters/sqlserver/type/date.rb b/lib/active_record/connection_adapters/sqlserver/type/date.rb index 8560d69a7..ae086b1ce 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/date.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/date.rb @@ -4,6 +4,15 @@ module SQLServer module Type class Date < ActiveRecord::Type::Date + def type_cast_for_database(value) + return unless value.present? + return value if value.acts_like?(:string) + value.to_s(:_sqlserver_dateformat) + end + + def type_cast_for_schema(value) + type_cast_for_database(value).inspect + 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 df71f3a2e..0ab38f03a 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/datetime.rb @@ -4,28 +4,34 @@ module SQLServer module Type class DateTime < ActiveRecord::Type::DateTime + include TimeValueFractional + + def type_cast_for_database(value) + return super unless value.acts_like?(:time) + value = zone_conversion(value) + datetime = value.to_s(:_sqlserver_datetime) + "#{datetime}".tap do |v| + fraction = quote_fractional(value) + v << ".#{fraction}" unless fraction.to_i.zero? + end + end + def type_cast_for_schema(value) - value.acts_like?(:string) ? "'#{value}'" : super + type_cast_for_database(value).inspect end private def cast_value(value) - value = value.respond_to?(:usec) ? value : super + value = value.acts_like?(:time) ? value : super return unless value - value.change usec: cast_usec(value) - end - - def cast_usec(value) - return 0 if !value.respond_to?(:usec) || value.usec.zero? - seconds = value.usec.to_f / 1_000_000.0 - ss_seconds = ((seconds * (1 / second_precision)).round / (1 / second_precision)).round(3) - (ss_seconds * 1_000_000).to_i + cast_fractional(value) end - def second_precision - 0.00333 + def zone_conversion(value) + method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal + value.respond_to?(method) ? value.send(method) : value end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/datetime2.rb b/lib/active_record/connection_adapters/sqlserver/type/datetime2.rb new file mode 100644 index 000000000..02ac6010b --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/type/datetime2.rb @@ -0,0 +1,17 @@ +module ActiveRecord + module ConnectionAdapters + module SQLServer + module Type + class DateTime2 < DateTime + + include TimeValueFractional2 + + def type + :datetime2 + end + + end + end + end + end +end diff --git a/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb b/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb new file mode 100644 index 000000000..17b4a1a50 --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb @@ -0,0 +1,31 @@ +module ActiveRecord + module ConnectionAdapters + module SQLServer + module Type + class DateTimeOffset < DateTime2 + + def type + :datetimeoffset + end + + def type_cast_for_database(value) + return super unless value.acts_like?(:time) + value.to_s :_sqlserver_datetimeoffset + end + + def type_cast_for_schema(value) + type_cast_for_database(value).inspect + end + + + private + + def zone_conversion(value) + value + end + + end + end + end + end +end diff --git a/lib/active_record/connection_adapters/sqlserver/type/quoter.rb b/lib/active_record/connection_adapters/sqlserver/type/quoter.rb deleted file mode 100644 index b596ab40b..000000000 --- a/lib/active_record/connection_adapters/sqlserver/type/quoter.rb +++ /dev/null @@ -1,32 +0,0 @@ -module ActiveRecord - module ConnectionAdapters - module SQLServer - module Type - class Quoter - - attr_reader :value, :type - - def initialize(value, type = nil) - @value = value - @type = type - end - - def to_s - @value.to_s - end - alias_method :to_str, :to_s - - def ==(other) - other == to_s || super - end - alias_method :eql?, :== - - def quote_ss_value - type.quote_ss(value) - end - - end - end - end - end -end diff --git a/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb b/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb index 5644cc79f..42b1cdc0d 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb @@ -4,15 +4,15 @@ module SQLServer module Type class SmallDateTime < DateTime + def type + :smalldatetime + end - private - def cast_usec(value) - 0 - end + private - def cast_usec_for_database(value) - '.000' + def cast_fractional(value) + value.change usec: 0 end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/time.rb b/lib/active_record/connection_adapters/sqlserver/type/time.rb index 3e5eb51d2..36245f479 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/time.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/time.rb @@ -1,52 +1,36 @@ -Time::DATE_FORMATS[:_sqlserver_time] = '%H:%M:%S' - module ActiveRecord module ConnectionAdapters module SQLServer module Type class Time < ActiveRecord::Type::Time - def initialize(options = {}) - super - @precision = nil if @precision == 7 - end + include TimeValueFractional2 def type_cast_for_database(value) - return if value.nil? - Quoter.new super, self + return super unless value.acts_like?(:time) + time = value.to_s(:_sqlserver_time) + "#{time}".tap do |v| + fraction = quote_fractional(value) + v << ".#{fraction}" unless fraction.to_i.zero? + end end def type_cast_for_schema(value) - value.acts_like?(:string) ? "'#{value}'" : super - end - - def quote_ss(value) - return unless value - value = cast_value(value) if value.acts_like?(:string) - date = value.to_s(:_sqlserver_time) - frac = quote_usec(value) - "'#{date}.#{frac}'" + type_cast_for_database(value).inspect end private def cast_value(value) - value = value.respond_to?(:usec) ? value.change(year: 2000, month: 01, day: 01) : super + value = value.acts_like?(:time) ? value : super return if value.blank? - value.change usec: cast_usec(value) - end - - def cast_usec(value) - (usec_to_seconds_frction(value) * 1_000_000).to_i - end - - def usec_to_seconds_frction(value) - (value.usec.to_f / 1_000_000.0).round(precision || 7) + value = value.change year: 2000, month: 01, day: 01 + cast_fractional(value) end - def quote_usec(value) - usec_to_seconds_frction(value).to_s.split('.').last + def fractional_scale + precision end 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 new file mode 100644 index 000000000..cd70a7419 --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/type/time_value_fractional.rb @@ -0,0 +1,72 @@ +module ActiveRecord + module ConnectionAdapters + module SQLServer + module Type + + module TimeValueFractional + + private + + def cast_fractional(value) + return value if !value.respond_to?(fractional_property) || value.send(fractional_property).zero? + seconds = value.send(fractional_property).to_f / fractional_operator.to_f + seconds = ((seconds * (1 / fractional_precision)).round / (1 / fractional_precision)).round(fractional_scale) + frac_seconds = (seconds * fractional_operator).to_i + value.change fractional_property => frac_seconds + end + + def quote_fractional(value) + seconds = (value.send(fractional_property).to_f / fractional_operator.to_f).round(fractional_scale) + seconds.to_s.split('.').last.to(fractional_scale-1) + end + + def fractional_property + :usec + end + + def fractional_digits + 6 + end + + def fractional_operator + 10 ** fractional_digits + end + + def fractional_precision + 0.00333 + end + + def fractional_scale + 3 + end + + end + + module TimeValueFractional2 + + include TimeValueFractional + + private + + def fractional_property + :nsec + end + + def fractional_digits + 9 + end + + def fractional_precision + 0.0000001 + end + + def fractional_scale + precision + end + + end + + end + end + end +end diff --git a/lib/active_record/connection_adapters/sqlserver/utils.rb b/lib/active_record/connection_adapters/sqlserver/utils.rb index ae272e560..7d364013a 100644 --- a/lib/active_record/connection_adapters/sqlserver/utils.rb +++ b/lib/active_record/connection_adapters/sqlserver/utils.rb @@ -122,18 +122,6 @@ def extract_identifiers(name) SQLServer::Utils::Name.new(name) end - def with_sqlserver_db_date_formats - old_db_format_date = Date::DATE_FORMATS[:db] - old_db_format_time = Time::DATE_FORMATS[:db] - date_format = Date::DATE_FORMATS[:_sqlserver_dateformat] - Date::DATE_FORMATS[:db] = "#{date_format}" - Time::DATE_FORMATS[:db] = "#{date_format} %H:%M:%S" - yield - ensure - Date::DATE_FORMATS[:db] = old_db_format_date - Time::DATE_FORMATS[:db] = old_db_format_time - end - end end end diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 90c2adddd..032a29093 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -230,6 +230,14 @@ def initialize_type_map(m) # 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| + precision = extract_precision(sql_type) + SQLServer::Type::DateTime2.new precision: precision + end + m.register_type %r{\Adatetimeoffset}i do |sql_type| + precision = extract_precision(sql_type) + SQLServer::Type::DateTimeOffset.new precision: precision + end m.register_type 'smalldatetime', SQLServer::Type::SmallDateTime.new m.register_type %r{\Atime}i do |sql_type| scale = extract_scale(sql_type) @@ -362,8 +370,13 @@ def initialize_dateformatter a, b, c = @database_dateformat.each_char.to_a [a, b, c].each { |f| f.upcase! if f == 'y' } dateformat = "%#{a}-%#{b}-%#{c}" - ::Date::DATE_FORMATS[:_sqlserver_dateformat] = dateformat - ::Time::DATE_FORMATS[:_sqlserver_dateformat] = dateformat + ::Date::DATE_FORMATS[:_sqlserver_dateformat] = dateformat + ::Time::DATE_FORMATS[:_sqlserver_dateformat] = dateformat + ::Time::DATE_FORMATS[:_sqlserver_time] = '%H:%M:%S' + ::Time::DATE_FORMATS[:_sqlserver_datetime] = "#{dateformat} %H:%M:%S" + ::Time::DATE_FORMATS[:_sqlserver_datetimeoffset] = lambda { |time| + time.strftime "#{dateformat} %H:%M:%S.%9N #{time.formatted_offset}" + } end end diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index eef439b54..9d070d103 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -603,6 +603,13 @@ def test_schema_dump_keeps_large_precision_integer_columns_as_decimal_coerced end +class SchemaDumperDefaultsTest < ActiveRecord::TestCase + + # These date formats do not match ours. We got these covered in our dumper tests. + coerce_tests! :test_schema_dump_defaults_with_universally_supported_types + +end + diff --git a/test/cases/column_test_sqlserver.rb b/test/cases/column_test_sqlserver.rb index 2c507912c..07adbd86a 100644 --- a/test/cases/column_test_sqlserver.rb +++ b/test/cases/column_test_sqlserver.rb @@ -284,7 +284,7 @@ def assert_obj_set_and_save(attribute, value) col = column('date') col.sql_type.must_equal 'date' col.null.must_equal true - col.default.must_equal '0001-01-01' # TODO: None type casted default. Really want Date.civil(0001, 1, 1). + col.default.must_equal connection_dblib_73? ? Date.civil(0001, 1, 1) : '0001-01-01' obj.date.must_equal Date.civil(0001, 1, 1) col.default_function.must_equal nil type = col.cast_type @@ -312,8 +312,8 @@ def assert_obj_set_and_save(attribute, value) col = column('datetime') col.sql_type.must_equal 'datetime' col.null.must_equal true - col.default.must_equal Time.utc(1753, 01, 01, 00, 00, 00, 000) - obj.datetime.must_equal Time.utc(1753, 01, 01, 00, 00, 00, 000) + 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>" col.default_function.must_equal nil type = col.cast_type type.must_be_instance_of Type::DateTime @@ -322,7 +322,7 @@ def assert_obj_set_and_save(attribute, value) type.limit.must_equal nil type.precision.must_equal nil type.scale.must_equal nil - # Can save .003 seconds and return again. + # Can save to proper accuracy and return again. obj.datetime = Time.utc(2010, 01, 01, 12, 34, 56, 3000) obj.datetime.must_equal Time.utc(2010, 01, 01, 12, 34, 56, 3000), "Microseconds were <#{obj.datetime.usec}> vs <3000>" obj.save! @@ -334,6 +334,80 @@ def assert_obj_set_and_save(attribute, value) obj.reload.datetime.must_equal Time.utc(2010, 01, 01, 12, 34, 56, 233000), "Microseconds were <#{obj.reload.datetime.usec}> vs <233000>" end + it 'datetime2' do + skip 'datetime2 not supported in this protocal version' unless connection_dblib_73? + col = column('datetime2_7') + col.sql_type.must_equal 'datetime2(7)' + 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>" + col.default_function.must_equal nil + type = col.cast_type + type.must_be_instance_of Type::DateTime2 + type.type.must_equal :datetime2 + type.wont_be :number? + type.limit.must_equal nil + type.precision.must_equal 7 + type.scale.must_equal nil + # 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>" + obj.save! + obj.reload.datetime2_7.must_equal Time.utc(9999, 12, 31, 23, 59, 59, Rational(123456800, 1000)), "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <123456800>" + # With other precisions. + time = Time.utc 9999, 12, 31, 23, 59, 59, Rational(123456789, 1000) + col = column('datetime2_3') + col.cast_type.precision.must_equal 3 + obj.datetime2_3 = time + 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>" + col = column('datetime2_1') + col.cast_type.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>" + end + + it 'datetimeoffset' do + skip 'datetimeoffset not supported in this protocal version' unless connection_dblib_73? + col = column('datetimeoffset_7') + col.sql_type.must_equal 'datetimeoffset(7)' + col.null.must_equal true + col.default.must_equal Time.new(1984, 01, 24, 04, 20, 00, -28800).change(nsec: 123456700), "Nanoseconds <#{col.default.nsec}> vs <123456700>" + obj.datetimeoffset_7.must_equal Time.new(1984, 01, 24, 04, 20, 00, -28800).change(nsec: 123456700), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <999999900>" + col.default_function.must_equal nil + type = col.cast_type + type.must_be_instance_of Type::DateTimeOffset + type.type.must_equal :datetimeoffset + type.wont_be :number? + type.limit.must_equal nil + type.precision.must_equal 7 + type.scale.must_equal nil + # Can save 100 nanosecond precisoins and return again. + obj.datetimeoffset_7 = Time.new(2010, 01, 01, 12, 34, 56, +18000).change(nsec: 123456755) + obj.datetimeoffset_7.must_equal Time.new(2010, 01, 01, 12, 34, 56, +18000).change(nsec: 123456800), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <123456800>" + obj.save! ; obj.reload + obj.datetimeoffset_7.must_equal Time.new(2010, 01, 01, 12, 34, 56, +18000).change(nsec: 123456800), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <123456800>" + # With other precisions. + time = ActiveSupport::TimeZone['America/Los_Angeles'].local 2010, 12, 31, 23, 59, 59, Rational(123456755, 1000) + col = column('datetimeoffset_3') + col.cast_type.precision.must_equal 3 + obj.datetimeoffset_3 = time + obj.datetimeoffset_3.must_equal time.change(nsec: 123000000), "Nanoseconds were <#{obj.datetimeoffset_3.nsec}> vs <123000000>" + # TODO: FreeTDS date bug fixed: https://github.com/FreeTDS/freetds/issues/44 + return + obj.save! ; obj.reload + obj.datetimeoffset_3.must_equal time.change(nsec: 123000000), "Nanoseconds were <#{obj.datetimeoffset_3.nsec}> vs <123000000>" + col = column('datetime2_1') + col.cast_type.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>" + end + it 'smalldatetime' do col = column('smalldatetime') col.sql_type.must_equal 'smalldatetime' @@ -343,7 +417,7 @@ def assert_obj_set_and_save(attribute, value) col.default_function.must_equal nil type = col.cast_type type.must_be_instance_of Type::SmallDateTime - type.type.must_equal :datetime + type.type.must_equal :smalldatetime type.wont_be :number? type.limit.must_equal nil type.precision.must_equal nil @@ -355,7 +429,41 @@ def assert_obj_set_and_save(attribute, value) obj.reload.smalldatetime.must_equal Time.utc(2078, 06, 05, 4, 20, 00, 0), "Microseconds were <#{obj.reload.smalldatetime.usec}> vs <0>" end + it 'time(7)' do + skip 'time() not supported in this protocal version' unless connection_dblib_73? + col = column('time_7') + col.sql_type.must_equal 'time(7)' + col.null.must_equal true + col.default.must_equal Time.utc(1900, 01, 01, 04, 20, 00, Rational(288321500, 1000)), "Nanoseconds were <#{col.default.nsec}> vs <288321500>" + col.default_function.must_equal nil + type = col.cast_type + type.must_be_instance_of Type::Time + type.type.must_equal :time + type.wont_be :number? + type.limit.must_equal nil + type.precision.must_equal 7 + type.scale.must_equal nil + # Time's #usec precision (low micro) + obj.time_7 = Time.utc(2000, 01, 01, 15, 45, 00, 300) + obj.time_7.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 300), "Microseconds were <#{obj.time_7.usec}> vs <0>" + obj.time_7.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 300), "Nanoseconds were <#{obj.time_7.nsec}> vs <300>" + obj.save! ; obj.reload + obj.time_7.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 300), "Microseconds were <#{obj.time_7.usec}> vs <0>" + obj.time_7.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 300), "Nanoseconds were <#{obj.time_7.nsec}> vs <300>" + # Time's #usec precision (high micro) + obj.time_7 = Time.utc(2000, 01, 01, 15, 45, 00, 234567) + obj.time_7.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 234567), "Microseconds were <#{obj.time_7.usec}> vs <234567>" + obj.save! ; obj.reload + obj.time_7.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 234567), "Microseconds were <#{obj.time_7.usec}> vs <234567>" + # Time's #usec precision (high nano rounded) + obj.time_7 = Time.utc(2000, 01, 01, 15, 45, 00, Rational(288321545, 1000)) + obj.time_7.must_equal Time.utc(2000, 01, 01, 15, 45, 00, Rational(288321500, 1000)), "Nanoseconds were <#{obj.time_7.nsec}> vs <288321500>" + obj.save! ; obj.reload + obj.time_7.must_equal Time.utc(2000, 01, 01, 15, 45, 00, Rational(288321500, 1000)), "Nanoseconds were <#{obj.time_7.nsec}> vs <288321500>" + end + it 'time(2)' do + skip 'time() not supported in this protocal version' unless connection_dblib_73? col = column('time_2') col.sql_type.must_equal 'time(2)' col.null.must_equal true @@ -368,56 +476,21 @@ def assert_obj_set_and_save(attribute, value) type.limit.must_equal nil type.precision.must_equal 2 type.scale.must_equal nil - # Always uses ActiveRecord's 2000-01-01 convention too. + # Always uses TinyTDS/Windows 2000-01-01 convention too. obj.time_2 = Time.utc(2015, 01, 10, 15, 45, 00, 0) obj.time_2.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 0) - obj.save! - obj.reload.time_2.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 0) - # Midnight the beggining of the day. - obj.time_2 = Time.utc(2000, 01, 01).midnight.change(usec: 0) - obj.time_2.must_equal Time.utc(2000, 01, 01, 00, 00, 00, 0) - obj.save! - obj.reload.time_2.must_equal Time.utc(2000, 01, 01, 00, 00, 00, 0) - # The end of day. - obj.time_2 = Time.utc(2000, 01, 01).end_of_day.change(usec: 0) - obj.time_2.must_equal Time.utc(2000, 01, 01, 23, 59, 59, 0) - obj.save! - obj.reload.time_2.must_equal Time.utc(2000, 01, 01, 23, 59, 59, 0) + obj.save! ; obj.reload + obj.time_2.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 0) # Time's #usec precision (barely in 2 precision equal to 0.03 seconds) obj.time_2 = Time.utc(2000, 01, 01, 15, 45, 00, 30000) obj.time_2.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 30000), "Microseconds were <#{obj.time_2.usec}> vs <30000>" - obj.save! - obj.reload.time_2.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 30000), "Microseconds were <#{obj.reload.time_2.usec}> vs <30000>" + obj.save! ; obj.reload + obj.time_2.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 30000), "Microseconds were <#{obj.time_2.usec}> vs <30000>" # Time's #usec precision (below 2 precision) obj.time_2 = Time.utc(2000, 01, 01, 15, 45, 00, 4000) obj.time_2.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 0), "Microseconds were <#{obj.time_2.usec}> vs <0>" - obj.save! - obj.reload.time_2.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 0), "Microseconds were <#{obj.reload.time_2.usec}> vs <0>" - end - - it 'time(7)' do - col = column('time_7') - col.sql_type.must_equal 'time(7)' - col.null.must_equal true - col.default.must_equal nil - col.default_function.must_equal nil - type = col.cast_type - type.must_be_instance_of Type::Time - type.type.must_equal :time - type.wont_be :number? - type.limit.must_equal nil - type.precision.must_equal nil, 'so it is clean in schema dumper' - type.scale.must_equal nil - # Time's #usec precision (low) - obj.time_7 = Time.utc(2000, 01, 01, 15, 45, 00, 300) - obj.time_7.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 300), "Microseconds were <#{obj.time_7.usec}> vs <300>" - obj.save! - obj.reload.time_7.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 300), "Microseconds were <#{obj.reload.time_7.usec}> vs <300>" - # Time's #usec precision (high) - obj.time_7 = Time.utc(2000, 01, 01, 15, 45, 00, 234567) - obj.time_7.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 234567), "Microseconds were <#{obj.time_7.usec}> vs <234567>" - obj.save! - obj.reload.time_7.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 234567), "Microseconds were <#{obj.reload.time_7.usec}> vs <234567>" + obj.save! ; obj.reload + obj.time_2.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 0), "Microseconds were <#{obj.time_2.usec}> vs <0>" end # Character Strings diff --git a/test/cases/schema_dumper_test_sqlserver.rb b/test/cases/schema_dumper_test_sqlserver.rb index 0f6f5987c..7845915a4 100644 --- a/test/cases/schema_dumper_test_sqlserver.rb +++ b/test/cases/schema_dumper_test_sqlserver.rb @@ -24,11 +24,18 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase assert_line :float, type: 'float', limit: nil, precision: nil, scale: nil, default: '123.00000001' assert_line :real, type: 'real', limit: nil, precision: nil, scale: nil, default: %r{123.4[45]} # Date and Time - assert_line :date, type: 'date', limit: nil, precision: nil, scale: nil, default: "'0001-01-01'" - assert_line :datetime, type: 'datetime', limit: nil, precision: nil, scale: nil, default: "'1753-01-01 00:00:00'" - assert_line :smalldatetime, type: 'datetime', limit: nil, precision: nil, scale: nil, default: "'1901-01-01 15:45:00'" + assert_line :date, type: 'date', limit: nil, precision: nil, scale: nil, default: "\"01-01-0001\"" + assert_line :datetime, type: 'datetime', limit: nil, precision: nil, scale: nil, default: "\"01-01-1753 00:00:00.123\"" + if connection_dblib_73? + assert_line :datetime2_7, type: 'datetime2', limit: nil, precision: '7', scale: nil, default: "\"12-31-9999 23:59:59.9999999\"" + assert_line :datetime2_3, type: 'datetime2', limit: nil, precision: '3', scale: nil, default: nil + assert_line :datetime2_1, type: 'datetime2', limit: nil, precision: '1', scale: nil, default: nil + end + assert_line :smalldatetime, type: 'smalldatetime',limit: nil, precision: nil, scale: nil, default: "\"01-01-1901 15:45:00\"" + if connection_dblib_73? + assert_line :time_7, type: 'time', limit: nil, precision: '7', scale: nil, default: "\"04:20:00.2883215\"" assert_line :time_2, type: 'time', limit: nil, precision: '2', scale: nil, default: nil - assert_line :time_7, type: 'time', limit: nil, precision: nil, scale: nil, default: nil + end # Character Strings assert_line :char_10, type: 'char', limit: '10', precision: nil, scale: nil, default: "\"1234567890\"" assert_line :varchar_50, type: 'varchar', limit: '50', precision: nil, scale: nil, default: "\"test varchar_50\"" @@ -73,12 +80,14 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase assert_line :text_col, type: 'text', limit: '2147483647', precision: nil, scale: nil, default: nil assert_line :datetime_col, type: 'datetime', limit: nil, precision: nil, scale: nil, default: nil assert_line :timestamp_col, type: 'datetime', limit: nil, precision: nil, scale: nil, default: nil - assert_line :time_col, type: 'time', limit: nil, precision: nil, scale: nil, default: nil + assert_line :time_col, type: 'time', limit: nil, precision: '7', scale: nil, default: nil assert_line :date_col, type: 'date', limit: nil, precision: nil, scale: nil, default: nil assert_line :binary_col, type: 'binary', limit: '2147483647', precision: nil, scale: nil, default: nil # Our type methods. columns['real_col'].sql_type.must_equal 'real' columns['money_col'].sql_type.must_equal 'money' + columns['datetime2_col'].sql_type.must_equal 'datetime2(7)' + columns['datetimeoffset'].sql_type.must_equal 'datetimeoffset(7)' columns['smallmoney_col'].sql_type.must_equal 'smallmoney' columns['char_col'].sql_type.must_equal 'char(1)' columns['varchar_col'].sql_type.must_equal 'varchar(8000)' @@ -91,6 +100,7 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase columns['sstimestamp_col'].sql_type.must_equal 'timestamp' assert_line :real_col, type: 'real', limit: nil, precision: nil, scale: nil, default: nil assert_line :money_col, type: 'money', limit: nil, precision: '19', scale: '4', default: nil + assert_line :datetime2_col, type: 'datetime2', limit: nil, precision: '7', scale: nil, default: nil assert_line :smallmoney_col, type: 'smallmoney', limit: nil, precision: '10', scale: '4', default: nil assert_line :char_col, type: 'char', limit: '1', precision: nil, scale: nil, default: nil assert_line :varchar_col, type: 'varchar', limit: '8000', precision: nil, scale: nil, default: nil @@ -145,12 +155,19 @@ def line(column_name) def assert_line(column_name, options={}) line = line(column_name) assert line, "Count not find line with column name: #{column_name.inspect} in schema:\n#{schema}" - line.type_method.must_equal options[:type], "Type of #{options[:type].inspect} not found in:\n #{line}" if options.key?(:type) - line.limit.must_equal options[:limit], "Limit of #{options[:limit].inspect} not found in:\n #{line}" if options.key?(:limit) - line.precision.must_equal options[:precision], "Precision of #{options[:precision].inspect} not found in:\n #{line}" if options.key?(:precision) - line.scale.must_equal options[:scale], "Scale of #{options[:scale].inspect} not found in:\n #{line}" if options.key?(:scale) - line.default.must_equal options[:default], "Default of #{options[:default].inspect} not found in:\n #{line}" if options.key?(:default) && options[:default].is_a?(String) - line.default.must_match options[:default], "Default of #{options[:default].inspect} not found in:\n #{line}" if options.key?(:default) && options[:default].is_a?(Regexp) + [:type, :limit, :precision, :scale, :default].each do |key| + next unless options.key?(key) + actual = key == :type ? line.send(:type_method) : line.send(key) + expected = options[key] + message = "#{key.to_s.titleize} of #{expected.inspect} not found in:\n#{line}" + if expected.nil? + actual.must_be_nil message + elsif expected.is_a?(Regexp) + actual.must_match expected, message + else + actual.must_equal expected, message + end + end end class SchemaLine diff --git a/test/schema/datatypes/2012.sql b/test/schema/datatypes/2012.sql index 8bc5a3946..5c444551d 100644 --- a/test/schema/datatypes/2012.sql +++ b/test/schema/datatypes/2012.sql @@ -24,10 +24,16 @@ CREATE TABLE [sst_datatypes] ( [real] [real] NULL DEFAULT 123.45, -- Date and Time [date] [date] NULL DEFAULT '0001-01-01', - [datetime] [datetime] NULL DEFAULT '1753-01-01T00:00:00.000', + [datetime] [datetime] NULL DEFAULT '1753-01-01T00:00:00.123', + [datetime2_7] [datetime2](7) NULL DEFAULT '9999-12-31 23:59:59.9999999', + [datetime2_3] [datetime2](3) NULL, + [datetime2_1] [datetime2](1) NULL, + [datetimeoffset_7] [datetimeoffset](7) NULL DEFAULT '1984-01-24 04:20:00.1234567 -08:00', + [datetimeoffset_3] [datetimeoffset](3) NULL, + [datetimeoffset_1] [datetimeoffset](1) NULL, [smalldatetime] [smalldatetime] NULL DEFAULT '1901-01-01T15:45:00.000Z', + [time_7] [time](7) NULL DEFAULT '04:20:00.2883215', [time_2] [time](2) NULL, - [time_7] [time](7) NULL, -- Character Strings [char_10] [char](10) NULL DEFAULT '1234567890', [varchar_50] [varchar](50) NULL DEFAULT 'test varchar_50', @@ -46,19 +52,3 @@ CREATE TABLE [sst_datatypes] ( [uniqueidentifier] [uniqueidentifier] NULL DEFAULT NEWID(), [timestamp] [timestamp] NULL, ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] - --- Date and Time (TODO) --- -------------------- --- [datetime2_7] [datetime2](7) NULL, --- [datetimeoffset_2] [datetimeoffset](2) NULL, --- [datetimeoffset_7] [datetimeoffset](7) NULL, --- --- INSERT INTO [sst_datatypes] ([id], [datetime2_7]) VALUES ( 71, '0001-01-01T00:00:00.0000000Z' ) --- INSERT INTO [sst_datatypes] ([id], [datetime2_7]) VALUES ( 72, '1984-01-24T04:20:00.0000000-08:00' ) --- INSERT INTO [sst_datatypes] ([id], [datetime2_7]) VALUES ( 73, '9999-12-31T23:59:59.9999999Z' ) --- INSERT INTO [sst_datatypes] ([id], [datetimeoffset_2]) VALUES ( 81, '1984-01-24T04:20:00.0000000-08:00' ) -- 1984-01-24 04:20:00.00 -08:00 --- INSERT INTO [sst_datatypes] ([id], [datetimeoffset_2]) VALUES ( 82, '1984-01-24T04:20:00.0000000Z' ) -- 1984-01-24 04:20:00.00 +00:00 --- INSERT INTO [sst_datatypes] ([id], [datetimeoffset_2]) VALUES ( 83, '9999-12-31T23:59:59.9999999Z' ) -- 9999-12-31 23:59:59.99 +00:00 --- INSERT INTO [sst_datatypes] ([id], [datetimeoffset_7]) VALUES ( 84, '1984-01-24T04:20:00.0000000-08:00' ) -- 1984-01-24 04:20:00.0000000 -08:00 --- INSERT INTO [sst_datatypes] ([id], [datetimeoffset_7]) VALUES ( 85, '1984-01-24T04:20:00.0000000Z' ) -- 1984-01-24 04:20:00.0000000 +00:00 --- INSERT INTO [sst_datatypes] ([id], [datetimeoffset_7]) VALUES ( 86, '9999-12-31T23:59:59.9999999Z' ) -- 9999-12-31 23:59:59.9999999 +00:00 diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index e3c619c63..e7c865854 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -19,18 +19,20 @@ t.date :date_col t.binary :binary_col # Our type methods. - t.real :real_col - t.money :money_col - t.smallmoney :smallmoney_col - t.char :char_col - t.varchar :varchar_col - t.text_basic :text_basic_col - t.nchar :nchar_col - t.ntext :ntext_col - t.binary_basic :binary_basic_col - t.varbinary :varbinary_col - t.uuid :uuid_col - t.ss_timestamp :sstimestamp_col + t.real :real_col + t.money :money_col + t.datetime2 :datetime2_col + t.datetimeoffset :datetimeoffset + t.smallmoney :smallmoney_col + t.char :char_col + t.varchar :varchar_col + t.text_basic :text_basic_col + t.nchar :nchar_col + t.ntext :ntext_col + t.binary_basic :binary_basic_col + t.varbinary :varbinary_col + t.uuid :uuid_col + t.ss_timestamp :sstimestamp_col end # Edge Cases