Skip to content

Commit

Permalink
Support in subquery
Browse files Browse the repository at this point in the history
  • Loading branch information
josevalim committed Apr 27, 2020
1 parent 377fdba commit eda1939
Show file tree
Hide file tree
Showing 7 changed files with 67 additions and 4 deletions.
4 changes: 4 additions & 0 deletions lib/ecto/adapters/myxql/connection.ex
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,10 @@ if Code.ensure_loaded?(MyXQL) do
[expr(left, sources, query), " IN (", args, ?)]
end

defp expr({:in, _, [left, %Ecto.SubQuery{} = subquery]}, sources, query) do
[expr(left, sources, query), " IN ", expr(subquery, sources, query)]
end

defp expr({:in, _, [left, right]}, sources, query) do
[expr(left, sources, query), " = ANY(", expr(right, sources, query), ?)]
end
Expand Down
4 changes: 4 additions & 0 deletions lib/ecto/adapters/postgres/connection.ex
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,10 @@ if Code.ensure_loaded?(Postgrex) do
[expr(left, sources, query), " = ANY($", Integer.to_string(ix + 1), ?)]
end

defp expr({:in, _, [left, %Ecto.SubQuery{} = subquery]}, sources, query) do
[expr(left, sources, query), " IN ", expr(subquery, sources, query)]
end

defp expr({:in, _, [left, right]}, sources, query) do
[expr(left, sources, query), " = ANY(", expr(right, sources, query), ?)]
end
Expand Down
19 changes: 16 additions & 3 deletions lib/ecto/adapters/tds/connection.ex
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,14 @@ if Code.ensure_loaded?(Tds) do
defp operator_to_boolean(:and), do: " AND "
defp operator_to_boolean(:or), do: " OR "

defp parens_for_select([first_expr | _] = expr) do
if is_binary(first_expr) and String.starts_with?(first_expr, ["SELECT", "select"]) do
[?(, expr, ?)]
else
expr
end
end

defp paren_expr(true, _sources, _query) do
["(1 = 1)"]
end
Expand Down Expand Up @@ -667,6 +675,10 @@ if Code.ensure_loaded?(Tds) do
[expr(left, sources, query), " IN (", args | ")"]
end

defp expr({:in, _, [left, %Ecto.SubQuery{} = subquery]}, sources, query) do
[expr(left, sources, query), " IN ", expr(subquery, sources, query)]
end

defp expr({:in, _, [left, right]}, sources, query) do
[expr(left, sources, query), " = ANY(", expr(right, sources, query) | ")"]
end
Expand All @@ -685,18 +697,19 @@ if Code.ensure_loaded?(Tds) do

defp expr(%Ecto.SubQuery{query: query}, sources, _query) do
query = put_in(query.aliases[@parent_as], sources)
all(query, [?s])
[?(, all(query, [?s]), ?)]
end

defp expr({:fragment, _, [kw]}, _sources, query) when is_list(kw) or tuple_size(kw) == 3 do
error!(query, "Tds adapter does not support keyword or interpolated fragments")
end

defp expr({:fragment, _, parts}, sources, query) do
Enum.map_join(parts, "", fn
Enum.map(parts, fn
{:raw, part} -> part
{:expr, expr} -> expr(expr, sources, query)
end)
|> parens_for_select
end

defp expr({:datetime_add, _, [datetime, count, interval]}, sources, query) do
Expand Down Expand Up @@ -1410,7 +1423,7 @@ if Code.ensure_loaded?(Tds) do

defp get_source(query, sources, ix, source) do
{expr, name, _schema} = elem(sources, ix)
{expr || paren_expr(source, sources, query), name}
{expr || expr(source, sources, query), name}
end

defp quote_name(name) when is_atom(name) do
Expand Down
2 changes: 1 addition & 1 deletion mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"decimal": {:hex, :decimal, "1.8.1", "a4ef3f5f3428bdbc0d35374029ffcf4ede8533536fa79896dd450168d9acdf3c", [:mix], [], "hexpm", "3cb154b00225ac687f6cbd4acc4b7960027c757a5152b369923ead9ddbca7aec"},
"deep_merge": {:hex, :deep_merge, "0.2.0", "c1050fa2edf4848b9f556fba1b75afc66608a4219659e3311d9c9427b5b680b3", [:mix], [], "hexpm", "e3bf435a54ed27b0ba3a01eb117ae017988804e136edcbe8a6a14c310daa966e"},
"earmark": {:hex, :earmark, "1.4.3", "364ca2e9710f6bff494117dbbd53880d84bebb692dafc3a78eb50aa3183f2bfd", [:mix], [], "hexpm", "8cf8a291ebf1c7b9539e3cddb19e9cef066c2441b1640f13c34c1d3cfc825fec"},
"ecto": {:git, "https://github.com/elixir-ecto/ecto.git", "bfdca5ae8745af06940c46a55a9220cb83c5d97e", []},
"ecto": {:git, "https://github.com/elixir-ecto/ecto.git", "e4219cc82993675d3ae50d0f86a262a8dd829255", []},
"ex_doc": {:hex, :ex_doc, "0.21.3", "857ec876b35a587c5d9148a2512e952e24c24345552259464b98bfbb883c7b42", [:mix], [{:earmark, "~> 1.4", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm", "0db1ee8d1547ab4877c5b5dffc6604ef9454e189928d5ba8967d4a58a801f161"},
"jason": {:hex, :jason, "1.2.0", "10043418c42d2493d0ee212d3fddd25d7ffe484380afad769a0a38795938e448", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "116747dbe057794c3a3e4e143b7c8390b29f634e16c78a7f59ba75bfa6852e7f"},
"makeup": {:hex, :makeup, "1.0.0", "671df94cf5a594b739ce03b0d0316aa64312cee2574b6a44becb83cd90fb05dc", [:mix], [{:nimble_parsec, "~> 0.5.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "a10c6eb62cca416019663129699769f0c2ccf39428b3bb3c0cb38c718a0c186d"},
Expand Down
14 changes: 14 additions & 0 deletions test/ecto/adapters/myxql_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,20 @@ defmodule Ecto.Adapters.MyXQLTest do
assert all(query) == ~s{SELECT ((s0.`x` = ?) OR s0.`x` IN (?,?,?)) OR (s0.`x` = ?) FROM `schema` AS s0}
end

test "in subquery" do
posts = subquery("posts" |> where(title: ^"hello") |> select([p], p.id))
query = "comments" |> where([c], c.post_id in subquery(posts)) |> select([c], c.x) |> plan()
assert all(query) ==
~s{SELECT c0.`x` FROM `comments` AS c0 } <>
~s{WHERE (c0.`post_id` IN (SELECT sp0.`id` AS `id` FROM `posts` AS sp0 WHERE (sp0.`title` = ?)))}

posts = subquery("posts" |> where(title: parent_as(:comment).subtitle) |> select([p], p.id))
query = "comments" |> from(as: :comment) |> where([c], c.post_id in subquery(posts)) |> select([c], c.x) |> plan()
assert all(query) ==
~s{SELECT c0.`x` FROM `comments` AS c0 } <>
~s{WHERE (c0.`post_id` IN (SELECT sp0.`id` AS `id` FROM `posts` AS sp0 WHERE (sp0.`title` = c0.`subtitle`)))}
end

test "having" do
query = Schema |> having([p], p.x == p.x) |> select([p], p.x) |> plan()
assert all(query) == ~s{SELECT s0.`x` FROM `schema` AS s0 HAVING (s0.`x` = s0.`x`)}
Expand Down
14 changes: 14 additions & 0 deletions test/ecto/adapters/postgres_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -585,6 +585,20 @@ defmodule Ecto.Adapters.PostgresTest do
assert all(query) == ~s{SELECT ((s0."x" = $1) OR s0."x" = ANY($2)) OR (s0."x" = $3) FROM "schema" AS s0}
end

test "in subquery" do
posts = subquery("posts" |> where(title: ^"hello") |> select([p], p.id))
query = "comments" |> where([c], c.post_id in subquery(posts)) |> select([c], c.x) |> plan()
assert all(query) ==
~s{SELECT c0."x" FROM "comments" AS c0 } <>
~s{WHERE (c0."post_id" IN (SELECT sp0."id" AS "id" FROM "posts" AS sp0 WHERE (sp0."title" = $1)))}

posts = subquery("posts" |> where(title: parent_as(:comment).subtitle) |> select([p], p.id))
query = "comments" |> from(as: :comment) |> where([c], c.post_id in subquery(posts)) |> select([c], c.x) |> plan()
assert all(query) ==
~s{SELECT c0."x" FROM "comments" AS c0 } <>
~s{WHERE (c0."post_id" IN (SELECT sp0."id" AS "id" FROM "posts" AS sp0 WHERE (sp0."title" = c0."subtitle")))}
end

test "having" do
query = Schema |> having([p], p.x == p.x) |> select([], true) |> plan()
assert all(query) == ~s{SELECT TRUE FROM "schema" AS s0 HAVING (s0."x" = s0."x")}
Expand Down
14 changes: 14 additions & 0 deletions test/ecto/adapters/tds_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,20 @@ defmodule Ecto.Adapters.TdsTest do
~s{SELECT ((s0.[x] = @1) OR s0.[x] IN (@2,@3,@4)) OR (s0.[x] = @5) FROM [schema] AS s0}
end

test "in subquery" do
posts = subquery("posts" |> where(title: ^"hello") |> select([p], p.id))
query = "comments" |> where([c], c.post_id in subquery(posts)) |> select([c], c.x) |> plan()
assert all(query) ==
~s{SELECT c0.[x] FROM [comments] AS c0 } <>
~s{WHERE (c0.[post_id] IN (SELECT sp0.[id] AS [id] FROM [posts] AS sp0 WHERE (sp0.[title] = @1)))}

posts = subquery("posts" |> where(title: parent_as(:comment).subtitle) |> select([p], p.id))
query = "comments" |> from(as: :comment) |> where([c], c.post_id in subquery(posts)) |> select([c], c.x) |> plan()
assert all(query) ==
~s{SELECT c0.[x] FROM [comments] AS c0 } <>
~s{WHERE (c0.[post_id] IN (SELECT sp0.[id] AS [id] FROM [posts] AS sp0 WHERE (sp0.[title] = c0.[subtitle])))}
end

test "having" do
query = Schema |> having([p], p.x == p.x) |> select([p], p.x) |> plan()
assert all(query) == ~s{SELECT s0.[x] FROM [schema] AS s0 HAVING (s0.[x] = s0.[x])}
Expand Down

0 comments on commit eda1939

Please sign in to comment.