rails-sqlserver / activerecord-sqlserver-adapter

SQL Server Adapter For Rails
MIT License
968 stars 558 forks source link

String default value not retrieved when using views #1114

Closed budiljak closed 8 months ago

budiljak commented 8 months ago

Issue

When using a view pointing to a string column, the sqlserver-adapter doesn't retrieve the default value. Instead the value is nil. This leads to a ActiveRecord::NotNullViolation if the record is saved without explicitly assigning a value to the corresponding attribute.

Script to reproduce the bug:

  require "bundler/inline"

  gemfile(true) do
    source "https://rubygems.org"
    gem "tiny_tds"

    gem "activerecord", "=7.0.4.2"
    gem "activerecord-sqlserver-adapter", git: "https://github.com/rails-sqlserver/activerecord-sqlserver-adapter", ref: "0be80a6565f56c984a74ff11abc2e52dbbae50e2"
    #gem "activerecord-sqlserver-adapter", "=7.0.4"
    #gem "activerecord-sqlserver-adapter", "7.0.3"
  end

  require "active_record"
  require "minitest/autorun"
  require "logger"

  ActiveRecord::Base.establish_connection(
    adapter:  "sqlserver",
    timeout:  5000,
    pool:     100,
    encoding: "utf8",
    database: "test_database",
    username: "SA",
    password: "StrongPassword!",
    host:     "localhost",
    port:     1433,
  )
  ActiveRecord::Base.logger = Logger.new(STDOUT)

  ActiveRecord::Schema.define do
    drop_table :bug_test_tables rescue nil

    create_table :bug_test_tables, force: true do |t|
      t.boolean :Bool_field, null: false, default: false
      t.string :string_field, null: false, default: "abc"
    end
    drop_view = "DROP VIEW IF EXISTS bug_tests;"
    create_view = "CREATE VIEW bug_tests AS SELECT id AS id, bool_field AS b, string_field as s FROM bug_test_tables"
    ActiveRecord::Base.connection.execute(drop_view)
    ActiveRecord::Base.connection.execute(create_view)
  end

  class BugTest < ActiveRecord::Base
  end

  class TestBugTest < Minitest::Test
    def setup
      # IMPORTANT: partial_inserts is false by default since Rails 7.0
      # without that ActiveRecord will not try to infer default values
      # before creating the record and hence there's no error
      ActiveRecord::Base.partial_inserts = false
      @bug_test = BugTest.new
    end

    def test_default_value
      assert_equal false, @bug_test.b
      assert_equal "abc", @bug_test.s
      @bug_test.save!
      assert_equal 1, BugTest.count
    end
  end
aidanharan commented 8 months ago

Hi @budiljak, this issue has been fixed by https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1126 and is in release v7.0.5.0

budiljak commented 8 months ago

Thanks for your efforts! :-)

budiljak commented 8 months ago

Hi @aidanharan,

unfortunately it seems like there's new bugs when retrieving the default values. If the default for a datetime column is added by a constraint statement the default is falsely retrieved as "1970-1-1 00:00:00".

The error occurs if the NULL-Default is added like this:

ALTER TABLE [dbo].[XXX] ADD CONSTRAINT [DEFULT_FOR_TEST] DEFAULT (NULL) FOR [test];

And: I have one string column without any default for which the default is retrieved as "0".

:-(

aidanharan commented 8 months ago

Hi @budiljak, would you be able to open a new issue about the date issue and include a script to reproduce it? I had a look and wasn't able to recreate it.