Skip to content

Commit c975ec2

Browse files
committed
Fix CTE construction when passed arrays with more than 2 queries
1 parent 0ad2334 commit c975ec2

File tree

3 files changed

+40
-5
lines changed

3 files changed

+40
-5
lines changed

activerecord/lib/active_record/relation/query_methods.rb

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1915,7 +1915,15 @@ def build_with_expression_from_value(value)
19151915
when Arel::Nodes::SqlLiteral then Arel::Nodes::Grouping.new(value)
19161916
when ActiveRecord::Relation then value.arel
19171917
when Arel::SelectManager then value
1918-
when Array then value.map { |q| build_with_expression_from_value(q) }.reduce { |result, value| result.union(:all, value) }
1918+
when Array
1919+
parts = value.map do |query|
1920+
with_expression = build_with_expression_from_value(query)
1921+
with_expression.try(:ast) || with_expression
1922+
end
1923+
1924+
parts.reduce do |result, value|
1925+
Arel::Nodes::UnionAll.new(result, value)
1926+
end
19191927
else
19201928
raise ArgumentError, "Unsupported argument type: `#{value}` #{value.class}"
19211929
end

activerecord/lib/arel/visitors/sqlite.rb

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,31 @@ def visit_Arel_Nodes_IsDistinctFrom(o, collector)
3333
collector << " IS NOT "
3434
visit o.right, collector
3535
end
36+
37+
# Queries used in UNION should not be wrapped by parentheses,
38+
# because it is an invalid syntax in SQLite.
39+
def infix_value_with_paren(o, collector, value, suppress_parens = false)
40+
collector << "( " unless suppress_parens
41+
42+
left = o.left.is_a?(Nodes::Grouping) ? o.left.expr : o.left
43+
collector = if left.class == o.class
44+
infix_value_with_paren(left, collector, value, true)
45+
else
46+
grouping_parentheses left, collector, false
47+
end
48+
49+
collector << value
50+
51+
right = o.right.is_a?(Nodes::Grouping) ? o.right.expr : o.right
52+
collector = if right.class == o.class
53+
infix_value_with_paren(right, collector, value, true)
54+
else
55+
grouping_parentheses right, collector, false
56+
end
57+
58+
collector << " )" unless suppress_parens
59+
collector
60+
end
3661
end
3762
end
3863
end

activerecord/test/cases/relation/with_test.rb

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ module ActiveRecord
99
class WithTest < ActiveRecord::TestCase
1010
fixtures :comments, :posts, :companies
1111

12+
SPECIAL_POSTS = [2].freeze
1213
POSTS_WITH_TAGS = [1, 2, 7, 8, 9, 10, 11].freeze
1314
POSTS_WITH_COMMENTS = [1, 2, 4, 5, 7].freeze
1415
POSTS_WITH_MULTIPLE_COMMENTS = [1, 4, 5].freeze
@@ -63,13 +64,14 @@ def test_with_when_invalid_params_are_passed
6364

6465
def test_with_when_passing_arrays
6566
relation = Post
66-
.with(posts_with_tags_or_comments: [
67-
Post.where("tags_count > 0"),
67+
.with(posts_with_special_type_or_tags_or_comments: [
68+
Post.where(type: "SpecialPost"),
69+
Arel.sql("SELECT * FROM posts WHERE tags_count > 0"), # arel node on purpose
6870
Post.where("legacy_comments_count > 0")
6971
])
70-
.from("posts_with_tags_or_comments AS posts")
72+
.from("posts_with_special_type_or_tags_or_comments AS posts")
7173

72-
assert_equal (POSTS_WITH_TAGS + POSTS_WITH_COMMENTS).sort, relation.order(:id).pluck(:id)
74+
assert_equal (SPECIAL_POSTS + POSTS_WITH_TAGS + POSTS_WITH_COMMENTS).sort, relation.order(:id).pluck(:id)
7375
end
7476

7577
def test_with_recursive

0 commit comments

Comments
 (0)