diff --git a/lib/ecto/adapters/postgres/connection.ex b/lib/ecto/adapters/postgres/connection.ex index 95e89388..f7e804aa 100644 --- a/lib/ecto/adapters/postgres/connection.ex +++ b/lib/ecto/adapters/postgres/connection.ex @@ -953,23 +953,23 @@ if Code.ensure_loaded?(Postgrex) do defp expr({:in, _, [left, right]}, sources, query) when is_list(right) do args = Enum.map_intersperse(right, ?,, &expr(&1, sources, query)) - [expr(left, sources, query), " IN (", args, ?)] + [maybe_paren(left, sources, query), " IN (", args, ?)] end defp expr({:in, _, [left, {:^, _, [ix, _]}]}, sources, query) do - [expr(left, sources, query), " = ANY($", Integer.to_string(ix + 1), ?)] + [maybe_paren(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)] + [maybe_paren(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), ?)] + [maybe_paren(left, sources, query), " = ANY(", expr(right, sources, query), ?)] end defp expr({:is_nil, _, [arg]}, sources, query) do - [expr(arg, sources, query) | " IS NULL"] + [maybe_paren(arg, sources, query) | " IS NULL"] end defp expr({:not, _, [expr]}, sources, query) do diff --git a/test/ecto/adapters/postgres_test.exs b/test/ecto/adapters/postgres_test.exs index 764c0a4c..77f59997 100644 --- a/test/ecto/adapters/postgres_test.exs +++ b/test/ecto/adapters/postgres_test.exs @@ -831,6 +831,9 @@ defmodule Ecto.Adapters.PostgresTest do query = "schema" |> select([r], r.x == is_nil(r.y)) |> plan() assert all(query) == ~s{SELECT s0."x" = (s0."y" IS NULL) FROM "schema" AS s0} + + query = "schema" |> select([r], is_nil(not r.x)) |> plan() + assert all(query) == ~s{SELECT (NOT (s0."x")) IS NULL FROM "schema" AS s0} end test "fragments" do @@ -1096,6 +1099,15 @@ 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} + + query = "schema" |> select([e], (not e.x) in [true, false]) |> plan() + assert all(query) == ~s{SELECT (NOT (s0."x")) IN (TRUE,FALSE) FROM "schema" AS s0} + + query = "schema" |> select([e], (not e.x) in ^[true, false]) |> plan() + assert all(query) == ~s{SELECT (NOT (s0."x")) = ANY($1) FROM "schema" AS s0} + + query = "schema" |> select([e], (not e.x) in e.w) |> plan() + assert all(query) == ~s{SELECT (NOT (s0."x")) = ANY(s0."w") FROM "schema" AS s0} end test "in subquery" do @@ -1106,6 +1118,14 @@ defmodule Ecto.Adapters.PostgresTest do ~s{SELECT c0."x" FROM "comments" AS c0 } <> ~s{WHERE (c0."post_id" IN (SELECT sp0."id" FROM "posts" AS sp0 WHERE (sp0."title" = $1)))} + query = + "comments" + |> select([c], (not c.published) in subquery(from(p in "posts", select: p.published))) + |> plan() + + assert all(query) == + ~s{SELECT (NOT (c0."published")) IN (SELECT sp0."published" AS "published" FROM "posts" AS sp0) FROM "comments" AS c0} + posts = subquery("posts" |> where(title: parent_as(:comment).subtitle) |> select([p], p.id)) query =