rails-sqlserver / activerecord-sqlserver-adapter

SQL Server Adapter For Rails
MIT License
975 stars 563 forks source link

Rails 7 adapter raises NoMethodError in `views_real_column_name` for Models over DB Views #1027

Closed frederikspang closed 1 year ago

frederikspang commented 2 years ago

Issue

Exception is thrown when model is wrapping DB Views.

Expected behavior

It should run simple SELECT queries, like is the case with < v7. And which is the case for normal table selects

Actual behavior

raises NoMethodError in schema_statements.rb:600 in views_real_column_name

How to reproduce

  1. Set up simple view (create view x as select a,b,c from TBL_Y)
  2. Set up simple model as
class MyModelForView < ApplicationRecord
  self.table_name = 'DBNAME..v_SOME_VIEW'
  self.primary_key = 'view_id'

  scope :some_scope, ->(v) { where(some_value: v) }
end

MyModelForView.first #=> NoMethodError
MyModelForView.some_scope(123) => NoMethodError

Details

                            Version: freetds v1.1.6
             freetds.conf directory: /etc/freetds
     MS db-lib source compatibility: no
        Sybase binary compatibility: yes
                      Thread safety: yes
                      iconv library: yes
                        TDS version: auto
                              iODBC: no
                           unixodbc: yes
              SSPI "trusted" logins: no
                           Kerberos: yes
                            OpenSSL: no
                             GnuTLS: yes
                               MARS: yes

Full backtrace of error

"/gems/activerecord-sqlserver-adapter-7.0.0.0/lib/active_record/connection_adapters/sqlserver/schema_statements.rb:600:in `views_real_column_name'",
"/gems/activerecord-sqlserver-adapter-7.0.0.0/lib/active_record/connection_adapters/sqlserver/schema_statements.rb:410:in `block in column_definitions'",
"/gems/activerecord-7.0.2.4/lib/active_record/result.rb:69:in `each'",
"/gems/activerecord-7.0.2.4/lib/active_record/result.rb:69:in `each'",
"/gems/activerecord-sqlserver-adapter-7.0.0.0/lib/active_record/connection_adapters/sqlserver/schema_statements.rb:381:in `map'",
"/gems/activerecord-sqlserver-adapter-7.0.0.0/lib/active_record/connection_adapters/sqlserver/schema_statements.rb:381:in `column_definitions'",
"/gems/activerecord-sqlserver-adapter-7.0.0.0/lib/active_record/connection_adapters/sqlserver/schema_statements.rb:70:in `columns'",
"/gems/activerecord-7.0.2.4/lib/active_record/connection_adapters/schema_cache.rb:117:in `block in columns'",
"/gems/activerecord-7.0.2.4/lib/active_record/connection_adapters/schema_cache.rb:116:in `fetch'",
"/gems/activerecord-7.0.2.4/lib/active_record/connection_adapters/schema_cache.rb:116:in `columns'",
"/gems/activerecord-7.0.2.4/lib/active_record/connection_adapters/schema_cache.rb:125:in `block in columns_hash'",
"/gems/activerecord-7.0.2.4/lib/active_record/connection_adapters/schema_cache.rb:124:in `fetch'",
"/gems/activerecord-7.0.2.4/lib/active_record/connection_adapters/schema_cache.rb:124:in `columns_hash'",
"/gems/activerecord-7.0.2.4/lib/active_record/model_schema.rb:568:in `load_schema!'",
"/gems/activerecord-7.0.2.4/lib/active_record/attributes.rb:264:in `load_schema!'",
"/gems/activerecord-7.0.2.4/lib/active_record/encryption/encryptable_record.rb:124:in `load_schema!'",
"/gems/activerecord-7.0.2.4/lib/active_record/model_schema.rb:554:in `block in load_schema'",
"/gems/activerecord-7.0.2.4/lib/active_record/model_schema.rb:551:in `synchronize'",
"/gems/activerecord-7.0.2.4/lib/active_record/model_schema.rb:551:in `load_schema'",
"/gems/activerecord-7.0.2.4/lib/active_record/model_schema.rb:417:in `attribute_types'",
"/gems/activerecord-7.0.2.4/lib/active_record/model_schema.rb:443:in `type_for_attribute'",
"/gems/activerecord-7.0.2.4/lib/active_record/type_caster/map.rb:16:in `type_for_attribute'",
"/gems/activerecord-7.0.2.4/lib/arel/table.rb:107:in `type_for_attribute'",
"/gems/activerecord-7.0.2.4/lib/active_record/table_metadata.rb:18:in `type'",
"/gems/activerecord-7.0.2.4/lib/active_record/relation/predicate_builder.rb:59:in `build'",
"/gems/activerecord-7.0.2.4/lib/active_record/relation/predicate_builder.rb:54:in `[]'",
"/gems/activerecord-7.0.2.4/lib/active_record/relation/predicate_builder.rb:126:in `block in expand_from_hash'",
"/gems/activerecord-7.0.2.4/lib/active_record/relation/predicate_builder.rb:79:in `each'",
"/gems/activerecord-7.0.2.4/lib/active_record/relation/predicate_builder.rb:79:in `flat_map'",
"/gems/activerecord-7.0.2.4/lib/active_record/relation/predicate_builder.rb:79:in `expand_from_hash'",
"/gems/activerecord-7.0.2.4/lib/active_record/relation/predicate_builder.rb:25:in `build_from_hash'",
"/gems/activerecord-7.0.2.4/lib/active_record/relation/query_methods.rb:1292:in `build_where_clause'",
"/gems/activerecord-7.0.2.4/lib/active_record/relation/query_methods.rb:735:in `where!'",
"/gems/activerecord-7.0.2.4/lib/active_record/relation/query_methods.rb:730:in `where'",
"/app/models/model_booking.rb:7:in `block in <class:ModelBooking>'",
"/gems/activerecord-7.0.2.4/lib/active_record/relation.rb:435:in `instance_exec'",
"/gems/activerecord-7.0.2.4/lib/active_record/relation.rb:435:in `block in _exec_scope'",
"/gems/activerecord-7.0.2.4/lib/active_record/relation.rb:880:in `_scoping'",
"/gems/activerecord-7.0.2.4/lib/active_record/relation.rb:435:in `_exec_scope'",
"/gems/activerecord-7.0.2.4/lib/active_record/scoping/named.rb:175:in `block in scope'"
aidanharan commented 2 years ago

@frederikspang Would you be able to create a script that recreates the issue as I haven't been able to? See https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/wiki/How-to-report-a-bug

frederikspang commented 2 years ago

@aidanharan I'll give it a go as soon as possible.

frederikspang commented 2 years ago

@aidanharan My initial attempts to make a reproduction case, actually revealed the error. We run a multi-database setup, within same DBMS, meaning our view(and table_name) defined as MY_DB..v_my_view. I have created a PR to accommodate for this.

https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1029

This atleast solves this case. There may exist other cases for this same error, and you may have another ruby-preference than my solution. Please let me know, so I can adjust accordingly.