Auto-generated find queries adding bogus WHERE clauses

[Sorry for the poor subject; this is really hard to explain]

I’m trying to run a search over several objects which themselves contain
multiple model objects, like so:

class Collection << ActiveRecord::Base
def findpkg
self.repositories.each do |r|
p = r.packages.find(:all, :conditions => “name like ‘%pkg%’”)
end
end
end

Unfortunately, AR proceeds to generate SQL that looks like this:

Package Load (0.000548) SELECT * FROM packages WHERE
(packages.repository_id = 3 AND (name like ‘%pkg%’))
Package Load (0.000167) SELECT * FROM packages WHERE
(packages.repository_id = 4 AND (packages.repository_id = 3
AND (name like ‘%pkg%’)))
Package Load (0.000151) SELECT * FROM packages WHERE
(packages.repository_id = 5 AND (packages.repository_id = 4
AND (packages.repository_id = 3 AND (name like ‘%pkg%’))))
Package Load (0.000160) SELECT * FROM packages WHERE
(packages.repository_id = 13 AND (packages.repository_id = 5
AND (packages.repository_id = 4 AND (packages.repository_id = 3
AND (name like ‘%pkg%’)))))

Note that each successive query prepends an extra clause, instead of
replacing the previous repository_id clause.

Has anyone seen anything like this? I can provide relevant chunks of
code
on request, but I’m not doing anything exciting in my models – just
standard sorts of has_many relationships. I’ll turn this into a bug
report
if needed, I just want to get a reality check before I go nuts.

  • Matt

[Please respect MFT to rails-core; I think most of the discussion of
this,
if any, will be more topical there]

On Fri, Feb 10, 2006 at 01:33:32PM +1100, Matthew P. wrote:

class Collection << ActiveRecord::Base
def findpkg
self.repositories.each do |r|
p = r.packages.find(:all, :conditions => “name like ‘%pkg%’”)
end
end
end

The bug is that HasManyAssociation#find modifies it’s arguments, so the
above (slightly fictionalised) variant is misleading; the real code is:

def findpkg(*args)
self.repositories.each do |r|
p = r.packages.find(*args)
# Do things with p
end
end

Adding “puts args.inspect” before and after the call to find() shows
that
args is in fact being modified, by the fact that the options object is
the
same as args[1] on the entry to HasManyAssociation#find. This simple
patch
makes the whole problem go away:

------8<------
— has_many_association.rb 2006-02-10 16:37:32.000000000 +1100
+++ has_many_association.rb.orig 2006-02-10 16:37:21.000000000
+1100
@@ -51,7 +51,7 @@
end

   def find(*args)
  •    options = Base.send(:extract_options_from_args!, args).dup
    
  •    options = Base.send(:extract_options_from_args!, args)
    
       # If using a custom finder_sql, scan the entire collection.
       if @options[:finder_sql]
    

------>8------

I’m planning on reporting this in Trac, but I’m curious about people’s
opinions – is it reasonable to assume that arguments which appear, on
the
surface, to be read-only may be modified by called methods?

  • Matt