Skip to content

Commit f9ce8f3

Browse files
authored
Merge pull request rails#54114 from byroot/join-binds
Don't eagerly compile StringJoin nodes
2 parents 1b327ad + ab08fcd commit f9ce8f3

File tree

3 files changed

+36
-28
lines changed

3 files changed

+36
-28
lines changed

activerecord/lib/active_record/associations/join_dependency/join_association.rb

Lines changed: 25 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -38,41 +38,39 @@ def join_constraints(foreign_table, foreign_klass, join_type, alias_tracker)
3838
chain << [reflection, table]
3939
end
4040

41-
base_klass.with_connection do |connection|
42-
# The chain starts with the target table, but we want to end with it here (makes
43-
# more sense in this context), so we reverse
44-
chain.reverse_each do |reflection, table|
45-
klass = reflection.klass
41+
# The chain starts with the target table, but we want to end with it here (makes
42+
# more sense in this context), so we reverse
43+
chain.reverse_each do |reflection, table|
44+
klass = reflection.klass
4645

47-
scope = reflection.join_scope(table, foreign_table, foreign_klass)
46+
scope = reflection.join_scope(table, foreign_table, foreign_klass)
4847

49-
unless scope.references_values.empty?
50-
associations = scope.eager_load_values | scope.includes_values
48+
unless scope.references_values.empty?
49+
associations = scope.eager_load_values | scope.includes_values
5150

52-
unless associations.empty?
53-
scope.joins! scope.construct_join_dependency(associations, Arel::Nodes::OuterJoin)
54-
end
51+
unless associations.empty?
52+
scope.joins! scope.construct_join_dependency(associations, Arel::Nodes::OuterJoin)
5553
end
54+
end
5655

57-
arel = scope.arel(alias_tracker.aliases)
58-
nodes = arel.constraints.first
56+
arel = scope.arel(alias_tracker.aliases)
57+
nodes = arel.constraints.first
5958

60-
if nodes.is_a?(Arel::Nodes::And)
61-
others = nodes.children.extract! do |node|
62-
!Arel.fetch_attribute(node) { |attr| attr.relation.name == table.name }
63-
end
59+
if nodes.is_a?(Arel::Nodes::And)
60+
others = nodes.children.extract! do |node|
61+
!Arel.fetch_attribute(node) { |attr| attr.relation.name == table.name }
6462
end
63+
end
6564

66-
joins << join_type.new(table, Arel::Nodes::On.new(nodes))
65+
joins << join_type.new(table, Arel::Nodes::On.new(nodes))
6766

68-
if others && !others.empty?
69-
joins.concat arel.join_sources
70-
append_constraints(connection, joins.last, others)
71-
end
72-
73-
# The current table in this iteration becomes the foreign table in the next
74-
foreign_table, foreign_klass = table, klass
67+
if others && !others.empty?
68+
joins.concat arel.join_sources
69+
append_constraints(joins.last, others)
7570
end
71+
72+
# The current table in this iteration becomes the foreign table in the next
73+
foreign_table, foreign_klass = table, klass
7674
end
7775

7876
joins
@@ -91,10 +89,10 @@ def strict_loading?
9189
end
9290

9391
private
94-
def append_constraints(connection, join, constraints)
92+
def append_constraints(join, constraints)
9593
if join.is_a?(Arel::Nodes::StringJoin)
9694
join_string = Arel::Nodes::And.new(constraints.unshift join.left)
97-
join.left = Arel.sql(connection.visitor.compile(join_string))
95+
join.left = join_string
9896
else
9997
right = join.right
10098
right.expr = Arel::Nodes::And.new(constraints.unshift right.expr)

activerecord/test/cases/associations/join_model_test.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -772,6 +772,12 @@ def test_proper_error_message_for_eager_load_and_includes_association_errors
772772
assert_equal("Can't join 'Post' to association named 'nonexistent_relation'; perhaps you misspelled it?", includes_and_eager_load_error.message)
773773
end
774774

775+
def test_eager_association_with_scope_with_string_joins
776+
assert_nothing_raised do
777+
Post.joins(:very_special_comment_with_string_joins).first
778+
end
779+
end
780+
775781
private
776782
# create dynamic Post models to allow different dependency options
777783
def find_post_with_dependency(post_id, association, association_name, dependency)

activerecord/test/models/post.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ def greeting
4242
scope :most_commented, lambda { |comments_count|
4343
joins(:comments)
4444
.group("posts.id")
45-
.having("count(comments.id) >= #{comments_count}")
45+
.having("count(comments.id) >= ?", comments_count)
4646
}
4747

4848
belongs_to :author
@@ -113,6 +113,10 @@ def greeting
113113
has_one :very_special_comment
114114
has_one :very_special_comment_with_post, -> { includes(:post) }, class_name: "VerySpecialComment"
115115
has_one :very_special_comment_with_post_with_joins, -> { joins(:post).order("posts.id") }, class_name: "VerySpecialComment"
116+
has_one :very_special_comment_with_string_joins, -> {
117+
joins("JOIN posts AS p1 ON comments.post_id = p1.id")
118+
.where.not(p1: { id: 999999 })
119+
}, class_name: "VerySpecialComment"
116120
has_many :special_comments
117121
has_many :nonexistent_comments, -> { where "comments.id < 0" }, class_name: "Comment"
118122

0 commit comments

Comments
 (0)