Error in ActiveRecord::Associations::AliasTracker: undefined method `left' for... #8748

Closed
manuelmeurer opened this Issue Jan 4, 2013 · 8 comments

Comments

Projects
None yet
6 participants
@manuelmeurer
Contributor

manuelmeurer commented Jan 4, 2013

White working with a project called RocketTag (https://github.com/bradphelan/rocket_tag) I encountered a confusing error in ActiveRecord::Associations::AliasTracker:

NoMethodError: undefined method `left' for :count:Symbol
# /Users/me/.rbenv/versions/1.9.3-p327-perf/lib/ruby/gems/1.9.1/gems/activerecord-3.2.6/lib/active_record/associations/alias_tracker.rb:64:in `block in initial_count_for'

bradphelan/rocket_tag#47
https://groups.google.com/forum/?fromgroups=#!topic/rubyonrails-core/7IyNreMKhQs

What it boils down to is that the following code triggers the NoMethodError: undefined methodleft' for...` error:

class Foo < ActiveRecord::Base
  has_many :bars
end

class Bar < ActiveRecord::Base
  belongs_to :foo
end

q1 = Foo.joins(:bars).select('bars.id').as('bars')
q2 = Foo.from(q1).includes(:bars)

q2.present?
NoMethodError - undefined method `left' for "bars.id":Arel::SqlLiteral:
  (gem) activerecord-3.2.10/lib/active_record/associations/alias_tracker.rb:64:in `block in initial_count_for'
  (gem) arel-3.0.2/lib/arel/visitors/depth_first.rb:12:in `call'
  (gem) arel-3.0.2/lib/arel/visitors/depth_first.rb:12:in `visit'
  (gem) arel-3.0.2/lib/arel/visitors/depth_first.rb:159:in `block in visit_Array'
  (gem) arel-3.0.2/lib/arel/visitors/depth_first.rb:159:in `each'
  (gem) arel-3.0.2/lib/arel/visitors/depth_first.rb:159:in `visit_Array'
  (gem) arel-3.0.2/lib/arel/visitors/visitor.rb:19:in `visit'
  (gem) arel-3.0.2/lib/arel/visitors/depth_first.rb:11:in `visit'
  (gem) arel-3.0.2/lib/arel/visitors/depth_first.rb:135:in `visit_Arel_Nodes_SelectCore'
  (gem) arel-3.0.2/lib/arel/visitors/visitor.rb:19:in `visit'
  (gem) arel-3.0.2/lib/arel/visitors/depth_first.rb:11:in `visit'
  (gem) arel-3.0.2/lib/arel/visitors/depth_first.rb:159:in `block in visit_Array'
  (gem) arel-3.0.2/lib/arel/visitors/depth_first.rb:159:in `each'
  (gem) arel-3.0.2/lib/arel/visitors/depth_first.rb:159:in `visit_Array'
  (gem) arel-3.0.2/lib/arel/visitors/visitor.rb:19:in `visit'
  (gem) arel-3.0.2/lib/arel/visitors/depth_first.rb:11:in `visit'
  (gem) arel-3.0.2/lib/arel/visitors/depth_first.rb:143:in `visit_Arel_Nodes_SelectStatement'
  (gem) arel-3.0.2/lib/arel/visitors/visitor.rb:19:in `visit'
  (gem) arel-3.0.2/lib/arel/visitors/depth_first.rb:11:in `visit'
  (gem) arel-3.0.2/lib/arel/visitors/depth_first.rb:16:in `unary'
  (gem) arel-3.0.2/lib/arel/visitors/visitor.rb:19:in `visit'
  (gem) arel-3.0.2/lib/arel/visitors/depth_first.rb:11:in `visit'
  (gem) arel-3.0.2/lib/arel/visitors/depth_first.rb:61:in `binary'
  (gem) arel-3.0.2/lib/arel/visitors/visitor.rb:19:in `visit'
  (gem) arel-3.0.2/lib/arel/visitors/depth_first.rb:11:in `visit'
  (gem) arel-3.0.2/lib/arel/visitors/visitor.rb:5:in `accept'
  (gem) arel-3.0.2/lib/arel/nodes/node.rb:42:in `each'
  (gem) activerecord-3.2.10/lib/active_record/associations/alias_tracker.rb:57:in `map'
  (gem) activerecord-3.2.10/lib/active_record/associations/alias_tracker.rb:57:in `initial_count_for'
  (gem) activerecord-3.2.10/lib/active_record/associations/alias_tracker.rb:12:in `block in initialize'
  (gem) activerecord-3.2.10/lib/active_record/associations/alias_tracker.rb:30:in `yield'
  (gem) activerecord-3.2.10/lib/active_record/associations/alias_tracker.rb:30:in `aliased_name_for'
  (gem) activerecord-3.2.10/lib/active_record/associations/join_dependency.rb:17:in `initialize'
  (gem) activerecord-3.2.10/lib/active_record/relation/finder_methods.rb:225:in `new'
  (gem) activerecord-3.2.10/lib/active_record/relation/finder_methods.rb:225:in `construct_relation_for_association_calculations'
  (gem) activerecord-3.2.10/lib/active_record/relation/calculations.rb:157:in `calculate'
  (gem) activerecord-3.2.10/lib/active_record/relation/calculations.rb:162:in `calculate'
  (gem) activerecord-3.2.10/lib/active_record/relation/calculations.rb:58:in `count'
  (gem) activerecord-3.2.10/lib/active_record/relation.rb:210:in `empty?'
  (gem) mail-2.4.4/lib/mail/core_extensions/object.rb:8:in `blank?'
  (gem) activesupport-3.2.10/lib/active_support/core_ext/object/blank.rb:21:in `present?'

Not sure how to proceed from here...

@timraymond

This comment has been minimized.

Show comment Hide comment
@timraymond

timraymond Jan 16, 2013

Contributor

Could you post a minimal rails app with this issue? It would make it a little easier to experiment with the problem.

Contributor

timraymond commented Jan 16, 2013

Could you post a minimal rails app with this issue? It would make it a little easier to experiment with the problem.

@manuelmeurer

This comment has been minimized.

Show comment Hide comment
@manuelmeurer

manuelmeurer Jan 20, 2013

Contributor

Sure, here's the app: https://github.com/krautcomputing/test_app_rails_issue_8748
I set up the two models and the code that produces the error in application#test.
If you open the root URL, you should see the error message.
Let me know if I can try anything else!

Contributor

manuelmeurer commented Jan 20, 2013

Sure, here's the app: https://github.com/krautcomputing/test_app_rails_issue_8748
I set up the two models and the code that produces the error in application#test.
If you open the root URL, you should see the error message.
Let me know if I can try anything else!

@mmozuras

This comment has been minimized.

Show comment Hide comment
@mmozuras

mmozuras Jan 20, 2013

Contributor

I reproduced the same error with an activerecord test also:

def test_8748
  real_count = Author.calculate(:count, 'authors.id', :joins => :posts, :distinct => true)

  authors = Author.joins(:posts).select('posts.id').as('posts')
  from_authors = Author.from(authors).includes('posts')

  assert_equal real_count, from_authors.count
end

I also tried what would happen if I add a simple check (return 0 unless join.kind_of?(Arel::Nodes::Join)) to initial_count_for:

def initial_count_for(name)
  return 0 if Arel::Table === table_joins

  # quoted_name should be downcased as some database adapters (Oracle) return quoted name in uppercase
  quoted_name = connection.quote_table_name(name).downcase

  counts = table_joins.map do |join|
    return 0 unless join.kind_of?(Arel::Nodes::Join)

    if join.is_a?(Arel::Nodes::StringJoin)
      # Table names + table aliases
      join.left.downcase.scan(
        /join(?:\s+\w+)?\s+(\S+\s+)?#{quoted_name}\son/
      ).size
    else
      join.left.table_name == name ? 1 : 0
    end
end

Then it fails a little bit differently:

ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: authors.id: SELECT COUNT(DISTINCT "authors"."id") FROM (SELECT posts.id FROM "authors" INNER JOIN "posts" ON "posts"."author_id" = "authors"."id") posts LEFT OUTER JOIN "posts" ON "posts"."author_id" = "authors"."id"
Contributor

mmozuras commented Jan 20, 2013

I reproduced the same error with an activerecord test also:

def test_8748
  real_count = Author.calculate(:count, 'authors.id', :joins => :posts, :distinct => true)

  authors = Author.joins(:posts).select('posts.id').as('posts')
  from_authors = Author.from(authors).includes('posts')

  assert_equal real_count, from_authors.count
end

I also tried what would happen if I add a simple check (return 0 unless join.kind_of?(Arel::Nodes::Join)) to initial_count_for:

def initial_count_for(name)
  return 0 if Arel::Table === table_joins

  # quoted_name should be downcased as some database adapters (Oracle) return quoted name in uppercase
  quoted_name = connection.quote_table_name(name).downcase

  counts = table_joins.map do |join|
    return 0 unless join.kind_of?(Arel::Nodes::Join)

    if join.is_a?(Arel::Nodes::StringJoin)
      # Table names + table aliases
      join.left.downcase.scan(
        /join(?:\s+\w+)?\s+(\S+\s+)?#{quoted_name}\son/
      ).size
    else
      join.left.table_name == name ? 1 : 0
    end
end

Then it fails a little bit differently:

ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: authors.id: SELECT COUNT(DISTINCT "authors"."id") FROM (SELECT posts.id FROM "authors" INNER JOIN "posts" ON "posts"."author_id" = "authors"."id") posts LEFT OUTER JOIN "posts" ON "posts"."author_id" = "authors"."id"
@senny

This comment has been minimized.

Show comment Hide comment
@senny

senny Feb 4, 2014

Member

Is this still an issue on master? I tried to run the test-case provided by @mmozuras but I did not get the described error. Please post a test-case to reproduce the problem. You can use this script as a foundation.

Member

senny commented Feb 4, 2014

Is this still an issue on master? I tried to run the test-case provided by @mmozuras but I did not get the described error. Please post a test-case to reproduce the problem. You can use this script as a foundation.

@senny senny added the needs feedback label Feb 4, 2014

@manuelmeurer

This comment has been minimized.

Show comment Hide comment
@manuelmeurer

manuelmeurer Mar 5, 2014

Contributor

I just tried and I'm getting a no such table: posts: https://gist.github.com/manuelmeurer/9371941 Not sure if I'm overlooking something obvious...

Contributor

manuelmeurer commented Mar 5, 2014

I just tried and I'm getting a no such table: posts: https://gist.github.com/manuelmeurer/9371941 Not sure if I'm overlooking something obvious...

@mmozuras

This comment has been minimized.

Show comment Hide comment
@mmozuras

mmozuras Mar 6, 2014

Contributor

@senny Tried on master, but couldn't reproduce 👍

Contributor

mmozuras commented Mar 6, 2014

@senny Tried on master, but couldn't reproduce 👍

@robin850

This comment has been minimized.

Show comment Hide comment
@robin850

robin850 Mar 7, 2014

Member

@manuelmeurer : The gist may be hard to write for master since you're hit by #11824 when using select (as far as I can understand).

Member

robin850 commented Mar 7, 2014

@manuelmeurer : The gist may be hard to write for master since you're hit by #11824 when using select (as far as I can understand).

@rails-bot rails-bot added the stale label Aug 19, 2014

@rails-bot

This comment has been minimized.

Show comment Hide comment
@rails-bot

rails-bot Aug 19, 2014

This issue has been automatically marked as stale because it has not been commented on for at least
three months.

The resources of the Rails team are limited, and so we are asking for your help.

If you can still reproduce this error on the 4-1-stable, 4-0-stable branches or on master,
please reply with all of the information you have about it in order to keep the issue open.

Thank you for all your contributions.

This issue has been automatically marked as stale because it has not been commented on for at least
three months.

The resources of the Rails team are limited, and so we are asking for your help.

If you can still reproduce this error on the 4-1-stable, 4-0-stable branches or on master,
please reply with all of the information you have about it in order to keep the issue open.

Thank you for all your contributions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment