squeel icon indicating copy to clipboard operation
squeel copied to clipboard

`Cannot visit Arel::SelectManager` when counting a join to a subselect

Open shepmaster opened this issue 11 years ago • 7 comments

It's entirely possible that the title isn't comprehensive enough, but it's a close start...

For full reproduction, check out my repo with all the models.

Synopsis

I have a scope that returns Users that have an Address. I want to get a list of all users and include a new column indicating if the user has an address:

def self.has_address
  joins { addresses }
end

def self.with_has_address
  a = User.has_address
  joins { a.as('addressable').on { id.eq(addressable.id) }.outer }
    .select { ["users.*", addressable.id.as('addressable')] }
end

This code works fine, until I attempt to get the count of users:

User.with_has_address
# SELECT users.*, "addressable"."id" AS addressable
# FROM "users"
# LEFT OUTER JOIN (
#   SELECT "users".* FROM "users"
#   INNER JOIN "addresses" ON "addresses"."user_id" = "users"."id"
# ) addressable
# ON "users"."id" = "addressable"."id"
#
# => #<ActiveRecord::Relation [#<User id: 1, created_at: "2014-10-23 15:41:43", updated_at: "2014-10-23 15:41:43", active: nil, parent_id: nil>]>

User.with_has_address.count
# => TypeError: Cannot visit Arel::SelectManager

shepmaster avatar Oct 23 '14 16:10 shepmaster

The problem lies here. join.subquery.left.arel returns a Arel::SelectManager, which is not acceptable to Arel. I'm not sure what should be given in its place, though.

teliosdev avatar Jun 26 '15 10:06 teliosdev

I made this modification and it seems to work:

--- a/lib/squeel/adapters/active_record/4.1/relation_extensions.rb
+++ b/lib/squeel/adapters/active_record/4.1/relation_extensions.rb
@@ -301,7 +301,7 @@ module Squeel
           def build_join_from_subquery(subquery_joins)
             subquery_joins.map do |join|
               join.type.new(
-                Arel::Nodes::TableAlias.new(join.subquery.left.arel, join.subquery.right),
+                Arel::Nodes::TableAlias.new(Arel::Nodes::Grouping.new(join.subquery.left.arel.ast), join.subquery.right),
                 Arel::Nodes::On.new(where_visit(join.constraints))
               )
             end

teliosdev avatar Jun 26 '15 10:06 teliosdev

@medcat, your patch fixed the issue for me, thanks. @bigxiang: I wrote a test for this and it passes. I can make a pull request if you guys want. Here's the commit for @medcat's patch in case you want to take a look at it.

edit: after running the Travis CI builds, I see that the test is breaking for Rails < 4.0. I'll do some debugging to see if there's a workaround for that.

mchavarriagam avatar Aug 02 '15 16:08 mchavarriagam

@medcat @mchavarriagam thanks so much for working on this! I wish I could remember exactly where I had this in our production code so I could give it a shot :smile_cat:

However, it does look like it fixes the issue I created in the repo in the first comment, so that's super promising!

shepmaster avatar Aug 10 '15 00:08 shepmaster

This commit breaks 4.2 subquery bind_params

            notes_relation = Person.first.notes
            klazz_name = "Person"
            relation = Person.joins { notes_relation.as("notes").on { (~id == notes.notable_id) & (notes.notable_type == klazz_name) }.outer }.where { notes.note != nil }

bind values for $1 and $2 are missing and query fails

danielnc avatar Feb 09 '17 14:02 danielnc

I've implemented a quick hack that seems to solve this issue Help is appreciated: https://github.com/BraneLabs/squeel/commit/970ef16d04758422306217a6cc3d8486c6a7360a

Fixes #344 too

danielnc avatar Feb 09 '17 16:02 danielnc

Is it possible to merge that with master?

rapides avatar Feb 21 '17 11:02 rapides