Permalink
Show file tree
Hide file tree
5 comments
on commit
sign in to comment.
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Set the inverse when association queries are refined
Suppose Man has_many interests, and inverse_of is used.
Man.first.interests.first.man will correctly execute two queries,
avoiding the need for a third query when Interest#man is called. This is
because CollectionAssociation#first calls set_inverse_instance.
However Man.first.interests.where("1=1").first.man will execute three
queries, even though this is obviously a subset of the records in the
association.
This is because calling where("1=1") spawns a new Relation object from
the CollectionProxy object, and the Relation has no knowledge of the
association, so it cannot set the inverse instance.
This commit solves the problem by making relations spawned from
CollectionProxies return a new Relation subclass called
AssociationRelation, which does know about associations. Records loaded
from this class will get the inverse instance set properly.
Fixes #5717.
Live commit from La Conf! ✨ - Loading branch information
Showing
4 changed files
with
33 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| module ActiveRecord | ||
| class AssociationRelation < Relation | ||
| def initialize(klass, table, association) | ||
| super(klass, table) | ||
| @association = association | ||
| end | ||
|
|
||
| private | ||
|
|
||
| def exec_queries | ||
| super.each { |r| @association.set_inverse_instance r } | ||
| end | ||
| end | ||
| end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
d7abe91There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jonleighton❤️
I'd tried to fix this in a similar way as we'd discussed but kept running into stack errors - I think it was the default_scope that was causing my problems.
d7abe91There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup, during his talk, that's exactly what happened. Jon basically mentioned that the insight came from looking at the second line of the backtrace instead of the first, which points at the overridden
#newon relation, and it clicked.d7abe91There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, I wonder whether we can extend
AssociationRelationfurther to ensure that laterwhereconditions don't override the association condition (i.e. GitHub email incident). wdyt?d7abe91There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@pixeltrix I actually tried to write a test which would corrupt the association condition, but wasn't able to get anything that actually worked. If you can create a failing test case then yeah let's definitely try add that extra safety, but in my earlier research (it was a few weeks ago) it didn't seem to be possible (at least not without severely fucking with AR internals).
d7abe91There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem only exists on 3-2-stable because of a fluke of implementation on master and 4-0-stable. The nodes in
where_valuesfrom the association condition have an@engineinstance variable ofActiveRecord::Basewhereas any added throughwherehave an@engineinstance variable ofModel. This causesseen.include?(w.left)to be false even though it's the same table and column.