From f3a17584609997514bcc54e773ce270463eed451 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 3 Feb 2025 17:58:50 +0000 Subject: [PATCH 1/2] Include cast type in column Fix following https://github.com/rails/rails/pull/54333 --- .../connection_adapters/sqlserver/schema_statements.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index b390b6cb1..781b0f0ed 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -70,8 +70,10 @@ def columns(table_name) column_definitions(table_name).map do |ci| sqlserver_options = ci.slice :ordinal_position, :is_primary, :is_identity, :table_name sql_type_metadata = fetch_type_metadata ci[:type], sqlserver_options + new_column( ci[:name], + lookup_cast_type(ci[:type]), ci[:default_value], sql_type_metadata, ci[:null], @@ -83,9 +85,10 @@ def columns(table_name) end end - def new_column(name, default, sql_type_metadata, null, default_function = nil, collation = nil, comment = nil, sqlserver_options = {}) + def new_column(name, cast_type, default, sql_type_metadata, null, default_function = nil, collation = nil, comment = nil, sqlserver_options = {}) SQLServer::Column.new( name, + cast_type, default, sql_type_metadata, null, From b16d436eb10f70a26b2264082f563f05deebbf9c Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Fri, 7 Feb 2025 11:24:24 +0000 Subject: [PATCH 2/2] Fixes --- .../sqlserver/schema_creation.rb | 2 +- test/cases/coerced_tests.rb | 17 ++-- test/cases/column_test_sqlserver.rb | 82 +++++++++---------- 3 files changed, 53 insertions(+), 48 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb index f6d0e400a..21479662f 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb @@ -50,7 +50,7 @@ def visit_CreateIndexDefinition(o) end def add_column_options!(sql, options) - sql << " DEFAULT #{quote_default_expression(options[:default], options[:column])}" if options_include_default?(options) + sql << " DEFAULT #{quote_default_expression_for_column_definition(options[:default], options[:column])}" if options_include_default?(options) if options[:collation].present? sql << " COLLATE #{options[:collation]}" end diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 060d5a30a..58d760341 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -570,8 +570,8 @@ def test_create_table_with_defaults_coerce five = columns.detect { |c| c.name == "five" } assert_equal "hello", one.default - assert_equal true, connection.lookup_cast_type_from_column(two).deserialize(two.default) - assert_equal false, connection.lookup_cast_type_from_column(three).deserialize(three.default) + assert_equal true, two.fetch_cast_type(connection).deserialize(two.default) + assert_equal false, three.fetch_cast_type(connection).deserialize(three.default) assert_equal 1, four.default assert_equal "hello", five.default end @@ -1971,12 +1971,17 @@ def with_marshable_time_defaults # Revert changes @connection.change_column_default(:sst_datatypes, :datetime, current_default) if current_default.present? end - - # We need to give the full path for this to work. - undef_method :schema_dump_path - def schema_dump_path + + # We need to give the full paths for this to work. + undef_method :schema_dump_5_1_path + def schema_dump_5_1_path File.join(ARTest::SQLServer.root_activerecord, "test/assets/schema_dump_5_1.yml") end + + undef_method :schema_dump_8_0_path + def schema_dump_8_0_path + File.join(ARTest::SQLServer.root_activerecord, "test/assets/schema_dump_8_0.yml") + end end end end diff --git a/test/cases/column_test_sqlserver.rb b/test/cases/column_test_sqlserver.rb index e717a5532..5da2ab4e4 100644 --- a/test/cases/column_test_sqlserver.rb +++ b/test/cases/column_test_sqlserver.rb @@ -42,7 +42,7 @@ def assert_obj_set_and_save(attribute, value) _(col.default).must_equal 42 _(obj.bigint).must_equal 42 _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::BigInteger _(type.limit).must_equal 8 assert_obj_set_and_save :bigint, -9_223_372_036_854_775_808 @@ -57,7 +57,7 @@ def assert_obj_set_and_save(attribute, value) _(col.default).must_equal 42 _(obj.int).must_equal 42 _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::Integer _(type.limit).must_equal 4 assert_obj_set_and_save :int, -2_147_483_648 @@ -72,7 +72,7 @@ def assert_obj_set_and_save(attribute, value) _(col.default).must_equal 42 _(obj.smallint).must_equal 42 _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::SmallInteger _(type.limit).must_equal 2 assert_obj_set_and_save :smallint, -32_768 @@ -87,7 +87,7 @@ def assert_obj_set_and_save(attribute, value) _(col.default).must_equal 42 _(obj.tinyint).must_equal 42 _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::TinyInteger _(type.limit).must_equal 1 assert_obj_set_and_save :tinyint, 0 @@ -102,7 +102,7 @@ def assert_obj_set_and_save(attribute, value) _(col.default).must_equal true _(obj.bit).must_equal true _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::Boolean _(type.limit).must_be_nil obj.bit = 0 @@ -123,7 +123,7 @@ def assert_obj_set_and_save(attribute, value) _(col.default).must_equal BigDecimal("12345.01") _(obj.decimal_9_2).must_equal BigDecimal("12345.01") _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::Decimal _(type.limit).must_be_nil _(type.precision).must_equal 9 @@ -140,7 +140,7 @@ def assert_obj_set_and_save(attribute, value) _(col.default).must_equal BigDecimal("1234567.89") _(obj.decimal_16_4).must_equal BigDecimal("1234567.89") _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type.precision).must_equal 16 _(type.scale).must_equal 4 obj.decimal_16_4 = "1234567.8901001" @@ -158,7 +158,7 @@ def assert_obj_set_and_save(attribute, value) _(obj.numeric_18_0).must_equal BigDecimal("191") _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::DecimalWithoutScale _(type.limit).must_be_nil _(type.precision).must_equal 18 @@ -179,7 +179,7 @@ def assert_obj_set_and_save(attribute, value) _(col.default).must_equal BigDecimal("12345678901234567890.01") _(obj.numeric_36_2).must_equal BigDecimal("12345678901234567890.01") _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::Decimal _(type.limit).must_be_nil _(type.precision).must_equal 36 @@ -198,7 +198,7 @@ def assert_obj_set_and_save(attribute, value) _(col.default).must_equal BigDecimal("4.20") _(obj.money).must_equal BigDecimal("4.20") _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::Money _(type.limit).must_be_nil _(type.precision).must_equal 19 @@ -217,7 +217,7 @@ def assert_obj_set_and_save(attribute, value) _(col.default).must_equal BigDecimal("4.20") _(obj.smallmoney).must_equal BigDecimal("4.20") _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::SmallMoney _(type.limit).must_be_nil _(type.precision).must_equal 10 @@ -240,7 +240,7 @@ def assert_obj_set_and_save(attribute, value) _(col.default).must_equal 123.00000001 _(obj.float).must_equal 123.00000001 _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::Float _(type.limit).must_be_nil _(type.precision).must_be_nil @@ -259,7 +259,7 @@ def assert_obj_set_and_save(attribute, value) _(col.default).must_be_close_to 123.45, 0.01 _(obj.real).must_be_close_to 123.45, 0.01 _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::Real _(type.limit).must_be_nil _(type.precision).must_be_nil @@ -280,7 +280,7 @@ def assert_obj_set_and_save(attribute, value) _(col.default).must_equal Date.civil(1, 1, 1) _(obj.date).must_equal Date.civil(1, 1, 1) _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::Date _(type.limit).must_be_nil _(type.precision).must_be_nil @@ -319,7 +319,7 @@ def assert_obj_set_and_save(attribute, value) _(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 = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::DateTime _(type.limit).must_be_nil _(type.precision).must_be_nil @@ -365,14 +365,14 @@ def assert_obj_set_and_save(attribute, value) _(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 = col.fetch_cast_type(connection) _(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. + # Can save 100 nanosecond precisions and return again. 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 @@ -383,7 +383,7 @@ def assert_obj_set_and_save(attribute, value) _(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. + # Can save small fraction nanosecond precisions and return again. 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 @@ -395,7 +395,7 @@ def assert_obj_set_and_save(attribute, value) # 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 + _(col.fetch_cast_type(connection).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 @@ -403,7 +403,7 @@ def assert_obj_set_and_save(attribute, value) _(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 + _(col.fetch_cast_type(connection).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 @@ -411,7 +411,7 @@ def assert_obj_set_and_save(attribute, value) _(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 + _(col.fetch_cast_type(connection).precision).must_equal 0 time = Time.utc 2016, 4, 19, 16, 45, 40, 771036 obj.datetime2_0 = time _(obj.datetime2_0).must_equal time.change(nsec: 0), "Nanoseconds were <#{obj.datetime2_0.nsec}> vs <0>" @@ -428,7 +428,7 @@ def assert_obj_set_and_save(attribute, value) _(col.default).must_equal Time.new(1984, 1, 24, 4, 20, 0, -28800).change(nsec: 123456700), "Nanoseconds <#{col.default.nsec}> vs <123456700>" _(obj.datetimeoffset_7).must_equal Time.new(1984, 1, 24, 4, 20, 0, -28800).change(nsec: 123456700), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <999999900>" _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::DateTimeOffset _(type.limit).must_be_nil _(type.precision).must_equal 7 @@ -453,13 +453,13 @@ def assert_obj_set_and_save(attribute, value) # With other precisions. time = ActiveSupport::TimeZone["America/Los_Angeles"].local 2010, 12, 31, 23, 59, 59, Rational(123456755, 1000) col = column("datetimeoffset_3") - _(connection.lookup_cast_type_from_column(col).precision).must_equal 3 + _(col.fetch_cast_type(connection).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>" obj.save! _(obj.datetimeoffset_3).must_equal time.change(nsec: 123000000), "Nanoseconds were <#{obj.datetimeoffset_3.nsec}> vs <123000000>" col = column("datetime2_1") - _(connection.lookup_cast_type_from_column(col).precision).must_equal 1 + _(col.fetch_cast_type(connection).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! @@ -474,7 +474,7 @@ def assert_obj_set_and_save(attribute, value) _(col.default).must_equal Time.utc(1901, 1, 1, 15, 45, 0, 0) _(obj.smalldatetime).must_equal Time.utc(1901, 1, 1, 15, 45, 0, 0) _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::SmallDateTime _(type.limit).must_be_nil _(type.precision).must_be_nil @@ -495,7 +495,7 @@ def assert_obj_set_and_save(attribute, value) _(col.null).must_equal true _(col.default).must_equal Time.utc(1900, 1, 1, 4, 20, 0, Rational(288321500, 1000)), "Nanoseconds were <#{col.default.nsec}> vs <288321500>" _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::Time _(type.limit).must_be_nil _(type.precision).must_equal 7 @@ -526,7 +526,7 @@ def assert_obj_set_and_save(attribute, value) _(col.null).must_equal true _(col.default).must_be_nil _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::Time _(type.limit).must_be_nil _(type.precision).must_equal 2 @@ -555,7 +555,7 @@ def assert_obj_set_and_save(attribute, value) _(col.null).must_equal true _(col.default).must_equal Time.utc(1900, 1, 1, 15, 3, 42, Rational(62197800, 1000)), "Nanoseconds were <#{col.default.nsec}> vs <62197800>" _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::Time _(type.limit).must_be_nil _(type.precision).must_equal 7 @@ -589,7 +589,7 @@ def assert_obj_set_and_save(attribute, value) _(col.default).must_equal "1234567890" _(obj.char_10).must_equal "1234567890" _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::Char _(type.limit).must_equal 10 _(type.precision).must_be_nil @@ -609,7 +609,7 @@ def assert_obj_set_and_save(attribute, value) _(col.default).must_equal "test varchar_50" _(obj.varchar_50).must_equal "test varchar_50" _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::Varchar _(type.limit).must_equal 50 _(type.precision).must_be_nil @@ -626,7 +626,7 @@ def assert_obj_set_and_save(attribute, value) _(col.default).must_equal "test varchar_max" _(obj.varchar_max).must_equal "test varchar_max" _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::VarcharMax _(type.limit).must_equal 2_147_483_647 _(type.precision).must_be_nil @@ -643,7 +643,7 @@ def assert_obj_set_and_save(attribute, value) _(col.default).must_equal "test text" _(obj.text).must_equal "test text" _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::Text _(type.limit).must_equal 2_147_483_647 _(type.precision).must_be_nil @@ -662,7 +662,7 @@ def assert_obj_set_and_save(attribute, value) _(col.default).must_equal "12345678åå" _(obj.nchar_10).must_equal "12345678åå" _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::UnicodeChar _(type.limit).must_equal 10 _(type.precision).must_be_nil @@ -682,7 +682,7 @@ def assert_obj_set_and_save(attribute, value) _(col.default).must_equal "test nvarchar_50 åå" _(obj.nvarchar_50).must_equal "test nvarchar_50 åå" _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::UnicodeVarchar _(type.limit).must_equal 50 _(type.precision).must_be_nil @@ -699,7 +699,7 @@ def assert_obj_set_and_save(attribute, value) _(col.default).must_equal "test nvarchar_max åå" _(obj.nvarchar_max).must_equal "test nvarchar_max åå" _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::UnicodeVarcharMax _(type.limit).must_equal 2_147_483_647 _(type.precision).must_be_nil @@ -716,7 +716,7 @@ def assert_obj_set_and_save(attribute, value) _(col.default).must_equal "test ntext åå" _(obj.ntext).must_equal "test ntext åå" _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::UnicodeText _(type.limit).must_equal 2_147_483_647 _(type.precision).must_be_nil @@ -737,7 +737,7 @@ def assert_obj_set_and_save(attribute, value) _(col.null).must_equal true _(col.default).must_be_nil _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::Binary _(type.limit).must_equal 49 _(type.precision).must_be_nil @@ -758,7 +758,7 @@ def assert_obj_set_and_save(attribute, value) _(col.null).must_equal true _(col.default).must_be_nil _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::Varbinary _(type.limit).must_equal 49 _(type.precision).must_be_nil @@ -779,7 +779,7 @@ def assert_obj_set_and_save(attribute, value) _(col.null).must_equal true _(col.default).must_be_nil _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::VarbinaryMax _(type.limit).must_equal 2_147_483_647 _(type.precision).must_be_nil @@ -798,7 +798,7 @@ def assert_obj_set_and_save(attribute, value) _(col.null).must_equal true _(col.default).must_be_nil _(col.default_function).must_equal "newid()" - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::Uuid _(type.limit).must_be_nil _(type.precision).must_be_nil @@ -821,7 +821,7 @@ def assert_obj_set_and_save(attribute, value) _(col.null).must_equal true _(col.default).must_be_nil _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::Timestamp _(type.limit).must_be_nil _(type.precision).must_be_nil