Lighthouse has a new layout. Prefer the old one? Return to the old layout, and switch back any time from the link at the top of each page.

This project is archived and is in readonly mode.

collection_singular_ids= method raises an exception when primary_key is string (Rails 3)

#5125

Using collection_singular_ids= (method generated by has_many) raises an exception when class primary_key is string.

Example:

class Role < ActiveRecord::Base
  has_many :function_roles
  has_many :roles, :through => :function_roles
end

class Function < ActiveRecord::Base
  set_primary_key :short_name # this is string
end

Role.first.function_ids = ['ManagePermissions']

ActiveRecord::StatementInvalid: PGError: ERROR:  operator does not exist: character varying = integer
LINE 1: ...  "functions" WHERE     ("functions"."short_name" = 0) LIMIT...
                                                             ^
HINT:  No operator matches the given name and argument type(s). You might need to add explicit type casts.
: SELECT     "functions".* FROM       "functions" WHERE     ("functions"."short_name" = 0) LIMIT 1
    from /home/rupert/.rvm/gems/ruby-1.9.2-head/gems/activerecord-3.0.0.beta4/lib/active_record/connection_adapters/abstract_adapter.rb:210:in `rescue in log'
    from /home/rupert/.rvm/gems/ruby-1.9.2-head/gems/activerecord-3.0.0.beta4/lib/active_record/connection_adapters/abstract_adapter.rb:200:in `log'
    from /home/rupert/.rvm/gems/ruby-1.9.2-head/gems/activerecord-3.0.0.beta4/lib/active_record/connection_adapters/postgresql_adapter.rb:464:in `execute'
    from /home/rupert/.rvm/gems/ruby-1.9.2-head/gems/activerecord-3.0.0.beta4/lib/active_record/connection_adapters/postgresql_adapter.rb:954:in `select_raw'
    from /home/rupert/.rvm/gems/ruby-1.9.2-head/gems/activerecord-3.0.0.beta4/lib/active_record/connection_adapters/postgresql_adapter.rb:941:in `select'
    from /home/rupert/.rvm/gems/ruby-1.9.2-head/gems/activerecord-3.0.0.beta4/lib/active_record/connection_adapters/abstract/database_statements.rb:7:in `select_all'
    from /home/rupert/.rvm/gems/ruby-1.9.2-head/gems/activerecord-3.0.0.beta4/lib/active_record/connection_adapters/abstract/query_cache.rb:56:in `select_all'
    from /home/rupert/.rvm/gems/ruby-1.9.2-head/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb:431:in `find_by_sql'
    from /home/rupert/.rvm/gems/ruby-1.9.2-head/gems/activerecord-3.0.0.beta4/lib/active_record/relation.rb:64:in `to_a'
    from /home/rupert/.rvm/gems/ruby-1.9.2-head/gems/activerecord-3.0.0.beta4/lib/active_record/relation/finder_methods.rb:324:in `find_first'
    from /home/rupert/.rvm/gems/ruby-1.9.2-head/gems/activerecord-3.0.0.beta4/lib/active_record/relation/finder_methods.rb:117:in `first'
    from /home/rupert/.rvm/gems/ruby-1.9.2-head/gems/activerecord-3.0.0.beta4/lib/active_record/relation/finder_methods.rb:282:in `find_one'
    from /home/rupert/.rvm/gems/ruby-1.9.2-head/gems/activerecord-3.0.0.beta4/lib/active_record/relation/finder_methods.rb:274:in `find_with_ids'
    from /home/rupert/.rvm/gems/ruby-1.9.2-head/gems/activerecord-3.0.0.beta4/lib/active_record/relation/finder_methods.rb:102:in `find'
    from /home/rupert/.rvm/gems/ruby-1.9.2-head/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb:403:in `find'
    from /home/rupert/.rvm/gems/ruby-1.9.2-head/gems/activerecord-3.0.0.beta4/lib/active_record/associations.rb:1423:in `block in collection_accessor_methods'
    from (irb):16

I think it can be easily fixed:

Remove this "map(&:to_i)" from active_record/associations.rb:1422

def collection_accessor_methods(reflection, association_proxy_class, writer = true)
.
.
.
    define_method("#{reflection.name.to_s.singularize}_ids=") do |new_value|
      ids = (new_value || []).reject { |nid| nid.blank? }.map(&:to_i)  # There is no need to map it to integer here in my opinion.
      send("#{reflection.name}=", reflection.klass.find(ids).index_by(&:id).values_at(*ids))
.
.
.

Reported by Robert Pankowecki · July 15th, 2010 @ 05:53 PM

State: resolved
Milestone: 3.x
Assigned to: José Valim José Valim
Importance: Low

Activity

  1. Neeraj Singh
    Neeraj Singh
    • Importance changed from to Low

    Can submit a patch using proper guidelines?

    July 15th, 2010 @ 06:14 PM

  2. Robert Pankowecki
  3. Neeraj Singh
    Neeraj Singh
    • State changed from new to open

    This might not be a rails issue. Problem might line with Arel gem.

    I setup a model called 'brake' which has brake_id, a string, as primary key.

    I tested with arel and this is what I got:

    brakes = Table(:brakes)
    brakes.where(brakes[:brake_id].eq('b1'))
    
    # the sql that was generated is 
    SELECT     "brakes"."brake_id", "brakes"."name", "brakes"."car_id" FROM       "brakes" WHERE     "brakes"."brake_id" = 0
    

    Looks like ARel does not like primary_key being string.

    July 15th, 2010 @ 08:51 PM

  4. Robert Pankowecki
  5. Robert Pankowecki
    Robert Pankowecki

    Could not reproduce your statement about Arel

    ruby-1.9.2-head > Role.primary_key
     => "name"
    
    ruby-1.9.2-head > role = Role.arel_table
    
    ruby-1.9.2-head > role.where(role[:name].eq('role')).to_sql
     => "SELECT     \"roles\".\"name\", \"roles\".\"created_at\", \"roles\".\"updated_at\" FROM       \"roles\" WHERE     \"roles\".\"name\" = 'role'"
    

    July 15th, 2010 @ 09:26 PM

  6. Neeraj Singh
    Neeraj Singh
    • Milestone set to 3.x
    • Assigned user set to José Valim

    I had wrong settings.

    Your patch works.

    +1

    July 15th, 2010 @ 09:51 PM

  7. Robert Pankowecki
    Robert Pankowecki

    I just found that my patch brokes the method when primary_key is a number. I'm goinh to post soon a better version.

    July 19th, 2010 @ 06:22 PM

  8. Robert Pankowecki
    Robert Pankowecki

    But I have one question before: How should the method behave when the given list of ids constains unexisting id? Raise an exception or just ignore it silently?

    July 19th, 2010 @ 06:24 PM

  9. José Valim
    José Valim

    Current behavior is to ignore silently, let's leave it that way. :)

    July 21st, 2010 @ 02:27 PM

  10. Robert Pankowecki
    Robert Pankowecki

    I checked that current behavior is to raise an exception. It works like that for Rails 2.2.8 and Rails 3 so I will stay with current implementation which seems to more safe for me.

    Rails 2.3.8
    ActiveRecord::RecordNotFound: Couldn't find all Containments with IDs (1,3) (found 1 results, but was looking for 2)
        from /home/rupert/.rvm/gems/ruby-1.9.2-head@rails222/gems/activerecord-2.3.8/lib/active_record/base.rb:1643:in `find_some'
        from /home/rupert/.rvm/gems/ruby-1.9.2-head@rails222/gems/activerecord-2.3.8/lib/active_record/base.rb:1602:in `find_from_ids'
        from /home/rupert/.rvm/gems/ruby-1.9.2-head@rails222/gems/activerecord-2.3.8/lib/active_record/base.rb:619:in `find'
        from /home/rupert/.rvm/gems/ruby-1.9.2-head@rails222/gems/activerecord-2.3.8/lib/active_record/associations.rb:1337:in `block in collection_accessor_methods'
    
    Rails 3.0.0.beta4
    ActiveRecord::RecordNotFound: Couldn't find all Containments with IDs (1, 3) (found 0 results, but was looking for 2)
        from /home/rupert/.rvm/gems/ruby-1.9.2-head/gems/activerecord-3.0.0.beta4/lib/active_record/relation/finder_methods.rb:316:in `find_some'
        from /home/rupert/.rvm/gems/ruby-1.9.2-head/gems/activerecord-3.0.0.beta4/lib/active_record/relation/finder_methods.rb:277:in `find_with_ids'
        from /home/rupert/.rvm/gems/ruby-1.9.2-head/gems/activerecord-3.0.0.beta4/lib/active_record/relation/finder_methods.rb:102:in `find'
        from /home/rupert/.rvm/gems/ruby-1.9.2-head/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb:403:in `find'
        from /home/rupert/.rvm/gems/ruby-1.9.2-head/gems/activerecord-3.0.0.beta4/lib/active_record/associations.rb:1423:in `block in collection_accessor_methods'
    

    July 27th, 2010 @ 09:04 PM

  11. José Valim
    José Valim

    Does this mean I can close this ticket?

    July 27th, 2010 @ 09:15 PM

  12. Robert Pankowecki
    Robert Pankowecki

    No! I'm working on patch right at this moment to fix the behavior when the primary key is not integer. I will submit a patch in a seconds.

    July 27th, 2010 @ 09:39 PM

  13. José Valim
  14. Robert Pankowecki
    Robert Pankowecki

    I'm sending a better patch with test for string and integer primary keys (should work fine with other types of primary keys as well).

    July 27th, 2010 @ 10:01 PM

  15. Krzysztof Kuczek
    Krzysztof Kuczek

    Hi,
    I've had similar problems, the patch seems to fix it. It just works. Thank you.

    July 28th, 2010 @ 08:04 AM

  16. Repository
  17. Luis Correa d'Almeida
    Luis Correa d'Almeida

    I've just checked out the 3.0.0.rc3 and the following does not work:

    my_active_record_object.other_ids = ["uuid1", "uuid2"]

    It seems to still be casting those uuids to integers

    ActiveRecord::RecordNotFound: Couldn't find Other with ID=0

    Can anyone else confirm this?

    August 9th, 2010 @ 01:51 AM

  18. Luis Correa d'Almeida
    Luis Correa d'Almeida

    And if you pass in a valid (existing) id, you get

    ActiveRecord::AssociationTypeMismatch: Other(#28595860) expected, got NilClass(#645570)

    August 9th, 2010 @ 01:55 AM

  19. José Valim
    José Valim

    Could you please be more clear? There is no Rails 3.0.0.rc3.

    August 9th, 2010 @ 01:58 AM

  20. Luis Correa d'Almeida
    Luis Correa d'Almeida

    Sorry, that was a typo - 3.0.0.rc

    August 9th, 2010 @ 02:57 AM

  21. Luis Correa d'Almeida
    Luis Correa d'Almeida

    I checked the tag and it seems that it was tagged before this commit. So it makes sense that it is still breaking in the 3.0.0_RC tag.

    August 9th, 2010 @ 03:12 AM