Skip to content

Commit e25ea9e

Browse files
Pass parent aliases to combination queries (#561)
1 parent dcf596f commit e25ea9e

8 files changed

Lines changed: 105 additions & 42 deletions

File tree

lib/ecto/adapters/myxql/connection.ex

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ if Code.ensure_loaded?(MyXQL) do
8989
group_by = group_by(query, sources)
9090
having = having(query, sources)
9191
window = window(query, sources)
92-
combinations = combinations(query)
92+
combinations = combinations(query, as_prefix)
9393
order_by = order_by(query, sources)
9494
limit = limit(query, sources)
9595
offset = offset(query, sources)
@@ -581,14 +581,14 @@ if Code.ensure_loaded?(MyXQL) do
581581
[" OFFSET " | expr(expr, sources, query)]
582582
end
583583

584-
defp combinations(%{combinations: combinations}) do
584+
defp combinations(%{combinations: combinations}, as_prefix) do
585585
Enum.map(combinations, fn
586-
{:union, query} -> [" UNION (", all(query), ")"]
587-
{:union_all, query} -> [" UNION ALL (", all(query), ")"]
588-
{:except, query} -> [" EXCEPT (", all(query), ")"]
589-
{:except_all, query} -> [" EXCEPT ALL (", all(query), ")"]
590-
{:intersect, query} -> [" INTERSECT (", all(query), ")"]
591-
{:intersect_all, query} -> [" INTERSECT ALL (", all(query), ")"]
586+
{:union, query} -> [" UNION (", all(query, as_prefix), ")"]
587+
{:union_all, query} -> [" UNION ALL (", all(query, as_prefix), ")"]
588+
{:except, query} -> [" EXCEPT (", all(query, as_prefix), ")"]
589+
{:except_all, query} -> [" EXCEPT ALL (", all(query, as_prefix), ")"]
590+
{:intersect, query} -> [" INTERSECT (", all(query, as_prefix), ")"]
591+
{:intersect_all, query} -> [" INTERSECT ALL (", all(query, as_prefix), ")"]
592592
end)
593593
end
594594

@@ -688,6 +688,12 @@ if Code.ensure_loaded?(MyXQL) do
688688
end
689689

690690
defp expr(%Ecto.SubQuery{query: query}, sources, parent_query) do
691+
combinations =
692+
Enum.map(query.combinations, fn {type, combination_query} ->
693+
{type, put_in(combination_query.aliases[@parent_as], {parent_query, sources})}
694+
end)
695+
696+
query = put_in(query.combinations, combinations)
691697
query = put_in(query.aliases[@parent_as], {parent_query, sources})
692698
[?(, all(query, subquery_as_prefix(sources)), ?)]
693699
end

lib/ecto/adapters/postgres/connection.ex

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ if Code.ensure_loaded?(Postgrex) do
169169
group_by = group_by(query, sources)
170170
having = having(query, sources)
171171
window = window(query, sources)
172-
combinations = combinations(query)
172+
combinations = combinations(query, as_prefix)
173173
order_by = order_by(query, order_by_distinct, sources)
174174
limit = limit(query, sources)
175175
offset = offset(query, sources)
@@ -782,14 +782,14 @@ if Code.ensure_loaded?(Postgrex) do
782782
[" OFFSET " | expr(expr, sources, query)]
783783
end
784784

785-
defp combinations(%{combinations: combinations}) do
785+
defp combinations(%{combinations: combinations}, as_prefix) do
786786
Enum.map(combinations, fn
787-
{:union, query} -> [" UNION (", all(query), ")"]
788-
{:union_all, query} -> [" UNION ALL (", all(query), ")"]
789-
{:except, query} -> [" EXCEPT (", all(query), ")"]
790-
{:except_all, query} -> [" EXCEPT ALL (", all(query), ")"]
791-
{:intersect, query} -> [" INTERSECT (", all(query), ")"]
792-
{:intersect_all, query} -> [" INTERSECT ALL (", all(query), ")"]
787+
{:union, query} -> [" UNION (", all(query, as_prefix), ")"]
788+
{:union_all, query} -> [" UNION ALL (", all(query, as_prefix), ")"]
789+
{:except, query} -> [" EXCEPT (", all(query, as_prefix), ")"]
790+
{:except_all, query} -> [" EXCEPT ALL (", all(query, as_prefix), ")"]
791+
{:intersect, query} -> [" INTERSECT (", all(query, as_prefix), ")"]
792+
{:intersect_all, query} -> [" INTERSECT ALL (", all(query, as_prefix), ")"]
793793
end)
794794
end
795795

@@ -877,6 +877,12 @@ if Code.ensure_loaded?(Postgrex) do
877877
end
878878

879879
defp expr(%Ecto.SubQuery{query: query}, sources, parent_query) do
880+
combinations =
881+
Enum.map(query.combinations, fn {type, combination_query} ->
882+
{type, put_in(combination_query.aliases[@parent_as], {parent_query, sources})}
883+
end)
884+
885+
query = put_in(query.combinations, combinations)
880886
query = put_in(query.aliases[@parent_as], {parent_query, sources})
881887
[?(, all(query, subquery_as_prefix(sources)), ?)]
882888
end

lib/ecto/adapters/tds/connection.ex

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ if Code.ensure_loaded?(Tds) do
150150
group_by = group_by(query, sources)
151151
having = having(query, sources)
152152
_window = window(query, sources)
153-
combinations = combinations(query)
153+
combinations = combinations(query, as_prefix)
154154
order_by = order_by(query, sources)
155155
# limit = is handled in select (TOP X)
156156
offset = offset(query, sources)
@@ -645,14 +645,14 @@ if Code.ensure_loaded?(Tds) do
645645
defp lock(%{lock: binary}, _sources) when is_binary(binary), do: [" OPTION (", binary, ?)]
646646
defp lock(%{lock: expr} = query, sources), do: [" OPTION (", expr(expr, sources, query), ?)]
647647

648-
defp combinations(%{combinations: combinations}) do
648+
defp combinations(%{combinations: combinations}, as_prefix) do
649649
Enum.map(combinations, fn
650-
{:union, query} -> [" UNION (", all(query), ")"]
651-
{:union_all, query} -> [" UNION ALL (", all(query), ")"]
652-
{:except, query} -> [" EXCEPT (", all(query), ")"]
653-
{:except_all, query} -> [" EXCEPT ALL (", all(query), ")"]
654-
{:intersect, query} -> [" INTERSECT (", all(query), ")"]
655-
{:intersect_all, query} -> [" INTERSECT ALL (", all(query), ")"]
650+
{:union, query} -> [" UNION (", all(query, as_prefix), ")"]
651+
{:union_all, query} -> [" UNION ALL (", all(query, as_prefix), ")"]
652+
{:except, query} -> [" EXCEPT (", all(query, as_prefix), ")"]
653+
{:except_all, query} -> [" EXCEPT ALL (", all(query, as_prefix), ")"]
654+
{:intersect, query} -> [" INTERSECT (", all(query, as_prefix), ")"]
655+
{:intersect_all, query} -> [" INTERSECT ALL (", all(query, as_prefix), ")"]
656656
end)
657657
end
658658

@@ -775,6 +775,12 @@ if Code.ensure_loaded?(Tds) do
775775
end
776776

777777
defp expr(%Ecto.SubQuery{query: query}, sources, parent_query) do
778+
combinations =
779+
Enum.map(query.combinations, fn {type, combination_query} ->
780+
{type, put_in(combination_query.aliases[@parent_as], {parent_query, sources})}
781+
end)
782+
783+
query = put_in(query.combinations, combinations)
778784
query = put_in(query.aliases[@parent_as], {parent_query, sources})
779785
[?(, all(query, subquery_as_prefix(sources)), ?)]
780786
end

mix.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"},
66
"deep_merge": {:hex, :deep_merge, "0.2.0", "c1050fa2edf4848b9f556fba1b75afc66608a4219659e3311d9c9427b5b680b3", [:mix], [], "hexpm", "e3bf435a54ed27b0ba3a01eb117ae017988804e136edcbe8a6a14c310daa966e"},
77
"earmark_parser": {:hex, :earmark_parser, "1.4.33", "3c3fd9673bb5dcc9edc28dd90f50c87ce506d1f71b70e3de69aa8154bc695d44", [:mix], [], "hexpm", "2d526833729b59b9fdb85785078697c72ac5e5066350663e5be6a1182da61b8f"},
8-
"ecto": {:git, "https://github.com/elixir-ecto/ecto.git", "d28ed617128734cf2cc52d371e8664993d61a260", []},
8+
"ecto": {:git, "https://github.com/elixir-ecto/ecto.git", "64104e17dcdeb6df9484ec0d6ef0daa4622a05c0", []},
99
"ex_doc": {:hex, :ex_doc, "0.30.5", "aa6da96a5c23389d7dc7c381eba862710e108cee9cfdc629b7ec021313900e9e", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "88a1e115dcb91cefeef7e22df4a6ebbe4634fbf98b38adcbc25c9607d6d9d8e6"},
1010
"jason": {:hex, :jason, "1.4.0", "e855647bc964a44e2f67df589ccf49105ae039d4179db7f6271dfd3843dc27e6", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "79a3791085b2a0f743ca04cec0f7be26443738779d09302e01318f97bdb82121"},
1111
"makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"},

test/ecto/adapters/myxql_test.exs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -141,8 +141,8 @@ defmodule Ecto.Adapters.MyXQLTest do
141141
~s{WITH RECURSIVE `tree` AS } <>
142142
~s{(SELECT sc0.`id` AS `id`, 1 AS `depth` FROM `categories` AS sc0 WHERE (sc0.`parent_id` IS NULL) } <>
143143
~s{UNION ALL } <>
144-
~s{(SELECT c0.`id`, t1.`depth` + 1 FROM `categories` AS c0 } <>
145-
~s{INNER JOIN `tree` AS t1 ON t1.`id` = c0.`parent_id`)) } <>
144+
~s{(SELECT sc0.`id`, st1.`depth` + 1 FROM `categories` AS sc0 } <>
145+
~s{INNER JOIN `tree` AS st1 ON st1.`id` = sc0.`parent_id`)) } <>
146146
~s{SELECT s0.`x`, t1.`id`, CAST(t1.`depth` AS unsigned) } <>
147147
~s{FROM `schema` AS s0 } <>
148148
~s{INNER JOIN `tree` AS t1 ON t1.`id` = s0.`category_id`}
@@ -276,11 +276,26 @@ defmodule Ecto.Adapters.MyXQLTest do
276276
~s{(WITH RECURSIVE `tree` AS } <>
277277
~s{(SELECT ssc0.`id` AS `id`, ssc0.`parent_id` AS `parent_id` FROM `categories` AS ssc0 WHERE (ssc0.`id` = c0.`id`) } <>
278278
~s{UNION ALL } <>
279-
~s{(SELECT c0.`id`, c0.`parent_id` FROM `categories` AS c0 } <>
280-
~s{INNER JOIN `tree` AS t1 ON t1.`parent_id` = c0.`id`)) } <>
279+
~s{(SELECT ssc0.`id`, ssc0.`parent_id` FROM `categories` AS ssc0 } <>
280+
~s{INNER JOIN `tree` AS sst1 ON sst1.`parent_id` = ssc0.`id`)) } <>
281281
~s{SELECT GROUP_CONCAT(st0.`id` SEPARATOR ' / ') AS `breadcrumbs` FROM `tree` AS st0) AS s1 ON TRUE}
282282
end
283283

284+
test "parent binding subquery and combination" do
285+
right_query = from(c in "right_categories", where: c.id == parent_as(:c).id, select: c.id)
286+
left_query = from(c in "left_categories", where: c.id == parent_as(:c).id, select: c.id)
287+
union_query = union(left_query, ^right_query)
288+
query = from(c in "categories", as: :c, where: c.id in subquery(union_query), select: c.id) |> plan()
289+
290+
assert all(query) ==
291+
~s{SELECT c0.`id` FROM `categories` AS c0 } <>
292+
~s{WHERE (} <>
293+
~s{c0.`id` IN } <>
294+
~s{(SELECT sl0.`id` FROM `left_categories` AS sl0 WHERE (sl0.`id` = c0.`id`) } <>
295+
~s{UNION } <>
296+
~s{(SELECT sr0.`id` FROM `right_categories` AS sr0 WHERE (sr0.`id` = c0.`id`))))}
297+
end
298+
284299
test "CTE with update statement" do
285300
cte_query =
286301
"categories"

test/ecto/adapters/postgres_test.exs

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,8 @@ defmodule Ecto.Adapters.PostgresTest do
163163
~s{WITH RECURSIVE "tree" AS } <>
164164
~s{(SELECT sc0."id" AS "id", 1 AS "depth" FROM "categories" AS sc0 WHERE (sc0."parent_id" IS NULL) } <>
165165
~s{UNION ALL } <>
166-
~s{(SELECT c0."id", t1."depth" + 1 FROM "categories" AS c0 } <>
167-
~s{INNER JOIN "tree" AS t1 ON t1."id" = c0."parent_id")) } <>
166+
~s{(SELECT sc0."id", st1."depth" + 1 FROM "categories" AS sc0 } <>
167+
~s{INNER JOIN "tree" AS st1 ON st1."id" = sc0."parent_id")) } <>
168168
~s{SELECT s0."x", t1."id", t1."depth"::bigint } <>
169169
~s{FROM "schema" AS s0 } <>
170170
~s{INNER JOIN "tree" AS t1 ON t1."id" = s0."category_id"}
@@ -195,8 +195,8 @@ defmodule Ecto.Adapters.PostgresTest do
195195
~s{WITH RECURSIVE "tree" AS MATERIALIZED} <>
196196
~s{(SELECT sc0."id" AS "id", 1 AS "depth" FROM "categories" AS sc0 WHERE (sc0."parent_id" IS NULL) } <>
197197
~s{UNION ALL } <>
198-
~s{(SELECT c0."id", t1."depth" + 1 FROM "categories" AS c0 } <>
199-
~s{INNER JOIN "tree" AS t1 ON t1."id" = c0."parent_id")) } <>
198+
~s{(SELECT sc0."id", st1."depth" + 1 FROM "categories" AS sc0 } <>
199+
~s{INNER JOIN "tree" AS st1 ON st1."id" = sc0."parent_id")) } <>
200200
~s{SELECT s0."x", t1."id", t1."depth"::bigint } <>
201201
~s{FROM "schema" AS s0 } <>
202202
~s{INNER JOIN "tree" AS t1 ON t1."id" = s0."category_id"}
@@ -227,8 +227,8 @@ defmodule Ecto.Adapters.PostgresTest do
227227
~s{WITH RECURSIVE "tree" AS NOT MATERIALIZED} <>
228228
~s{(SELECT sc0."id" AS "id", 1 AS "depth" FROM "categories" AS sc0 WHERE (sc0."parent_id" IS NULL) } <>
229229
~s{UNION ALL } <>
230-
~s{(SELECT c0."id", t1."depth" + 1 FROM "categories" AS c0 } <>
231-
~s{INNER JOIN "tree" AS t1 ON t1."id" = c0."parent_id")) } <>
230+
~s{(SELECT sc0."id", st1."depth" + 1 FROM "categories" AS sc0 } <>
231+
~s{INNER JOIN "tree" AS st1 ON st1."id" = sc0."parent_id")) } <>
232232
~s{SELECT s0."x", t1."id", t1."depth"::bigint } <>
233233
~s{FROM "schema" AS s0 } <>
234234
~s{INNER JOIN "tree" AS t1 ON t1."id" = s0."category_id"}
@@ -367,11 +367,26 @@ defmodule Ecto.Adapters.PostgresTest do
367367
~s{(WITH RECURSIVE "tree" AS } <>
368368
~s{(SELECT ssc0."id" AS "id", ssc0."parent_id" AS "parent_id" FROM "categories" AS ssc0 WHERE (ssc0."id" = c0."id") } <>
369369
~s{UNION ALL } <>
370-
~s{(SELECT c0."id", c0."parent_id" FROM "categories" AS c0 } <>
371-
~s{INNER JOIN "tree" AS t1 ON t1."parent_id" = c0."id")) } <>
370+
~s{(SELECT ssc0."id", ssc0."parent_id" FROM "categories" AS ssc0 } <>
371+
~s{INNER JOIN "tree" AS sst1 ON sst1."parent_id" = ssc0."id")) } <>
372372
~s{SELECT STRING_AGG(st0."id", ' / ') AS "breadcrumbs" FROM "tree" AS st0) AS s1 ON TRUE}
373373
end
374374

375+
test "parent binding subquery and combination" do
376+
right_query = from(c in "right_categories", where: c.id == parent_as(:c).id, select: c.id)
377+
left_query = from(c in "left_categories", where: c.id == parent_as(:c).id, select: c.id)
378+
union_query = union(left_query, ^right_query)
379+
query = from(c in "categories", as: :c, where: c.id in subquery(union_query), select: c.id) |> plan()
380+
381+
assert all(query) ==
382+
~s{SELECT c0."id" FROM "categories" AS c0 } <>
383+
~s{WHERE (} <>
384+
~s{c0."id" IN } <>
385+
~s{(SELECT sl0."id" FROM "left_categories" AS sl0 WHERE (sl0."id" = c0."id") } <>
386+
~s{UNION } <>
387+
~s{(SELECT sr0."id" FROM "right_categories" AS sr0 WHERE (sr0."id" = c0."id"))))}
388+
end
389+
375390
test "CTE with update statement" do
376391
cte_query =
377392
"categories"

test/ecto/adapters/tds_test.exs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -222,8 +222,8 @@ defmodule Ecto.Adapters.TdsTest do
222222
~s{WITH [tree] ([id],[depth]) AS (} <>
223223
~s{SELECT sc0.[id] AS [id], 1 AS [depth] FROM [categories] AS sc0 WHERE (sc0.[parent_id] IS NULL) } <>
224224
~s{UNION ALL } <>
225-
~s{(SELECT c0.[id], t1.[depth] + 1 FROM [categories] AS c0 } <>
226-
~s{INNER JOIN [tree] AS t1 ON t1.[id] = c0.[parent_id])) } <>
225+
~s{(SELECT sc0.[id], st1.[depth] + 1 FROM [categories] AS sc0 } <>
226+
~s{INNER JOIN [tree] AS st1 ON st1.[id] = sc0.[parent_id])) } <>
227227
~s{SELECT s0.[x], t1.[id], CAST(t1.[depth] AS bigint) } <>
228228
~s{FROM [schema] AS s0 } <>
229229
~s{INNER JOIN [tree] AS t1 ON t1.[id] = s0.[category_id]}
@@ -359,11 +359,26 @@ defmodule Ecto.Adapters.TdsTest do
359359
~s{(WITH [tree] ([id],[parent_id]) AS } <>
360360
~s{(SELECT ssc0.[id] AS [id], ssc0.[parent_id] AS [parent_id] FROM [categories] AS ssc0 WHERE (ssc0.[id] = c0.[id]) } <>
361361
~s{UNION ALL } <>
362-
~s{(SELECT c0.[id], c0.[parent_id] FROM [categories] AS c0 } <>
363-
~s{INNER JOIN [tree] AS t1 ON t1.[parent_id] = c0.[id])) } <>
362+
~s{(SELECT ssc0.[id], ssc0.[parent_id] FROM [categories] AS ssc0 } <>
363+
~s{INNER JOIN [tree] AS sst1 ON sst1.[parent_id] = ssc0.[id])) } <>
364364
~s{SELECT STRING_AGG(st0.[id], ' / ') AS [breadcrumbs] FROM [tree] AS st0) AS s1}
365365
end
366366

367+
test "parent binding subquery and combination" do
368+
right_query = from(c in "right_categories", where: c.id == parent_as(:c).id, select: c.id)
369+
left_query = from(c in "left_categories", where: c.id == parent_as(:c).id, select: c.id)
370+
union_query = union(left_query, ^right_query)
371+
query = from(c in "categories", as: :c, where: c.id in subquery(union_query), select: c.id) |> plan()
372+
373+
assert all(query) ==
374+
~s{SELECT c0.[id] FROM [categories] AS c0 } <>
375+
~s{WHERE (} <>
376+
~s{c0.[id] IN } <>
377+
~s{(SELECT sl0.[id] FROM [left_categories] AS sl0 WHERE (sl0.[id] = c0.[id]) } <>
378+
~s{UNION } <>
379+
~s{(SELECT sr0.[id] FROM [right_categories] AS sr0 WHERE (sr0.[id] = c0.[id]))))}
380+
end
381+
367382
test "CTE with update statement" do
368383
cte_query =
369384
"categories"

test/test_repo.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ defmodule EctoSQL.TestAdapter do
3535

3636
def checkout(_, _, _), do: raise("not implemented")
3737
def checked_out?(_), do: raise("not implemented")
38-
def delete(_, _, _, _), do: raise("not implemented")
38+
def delete(_, _, _, _, _), do: raise("not implemented")
3939
def insert_all(_, _, _, _, _, _, _, _), do: raise("not implemented")
4040
def rollback(_, _), do: raise("not implemented")
4141
def stream(_, _, _, _, _), do: raise("not implemented")

0 commit comments

Comments
 (0)