From 5a137a52b51f7e689ff484ad63c72c52359207f5 Mon Sep 17 00:00:00 2001 From: Vladimir Dronnikov Date: Tue, 4 Mar 2025 11:41:37 +0300 Subject: [PATCH] cope with distinct: true --- lib/ecto/adapters/postgres/connection.ex | 16 +++++++++ test/ecto/adapters/postgres_test.exs | 46 ++++++++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/lib/ecto/adapters/postgres/connection.ex b/lib/ecto/adapters/postgres/connection.ex index 13cc10e50..c281184c4 100644 --- a/lib/ecto/adapters/postgres/connection.ex +++ b/lib/ecto/adapters/postgres/connection.ex @@ -533,6 +533,22 @@ if Code.ensure_loaded?(Postgrex) do do: "TRUE" defp select_fields(fields, sources, query) do + # NB: append order_by fields if distinct: true specified + fields = + case query.distinct do + %Ecto.Query.ByExpr{expr: true} -> + order_by_fields = + Enum.map(query.order_bys, &Keyword.values(&1.expr)) |> List.flatten() + + Enum.reduce(order_by_fields, fields, fn + {{:., _, [{:&, [], [idx]}, _]}, _, _} = field, acc when idx > 0 -> acc ++ [field] + _field, acc -> acc + end) + + _ -> + fields + end + Enum.map_intersperse(fields, ", ", fn {:&, _, [idx]} -> case elem(sources, idx) do diff --git a/test/ecto/adapters/postgres_test.exs b/test/ecto/adapters/postgres_test.exs index 9bb21a610..b3d8b7c3e 100644 --- a/test/ecto/adapters/postgres_test.exs +++ b/test/ecto/adapters/postgres_test.exs @@ -579,6 +579,52 @@ defmodule Ecto.Adapters.PostgresTest do ~s{SELECT DISTINCT ON (s0."x") s0."x" FROM "schema" AS s0 ORDER BY s0."x" DESC} end + test "distinct true with assocs" do + query = + Schema + |> join(:inner, [r], assoc(r, :permalink)) + |> order_by([r, p], [p.id]) + |> distinct([r], true) + |> select([r], r.x) + |> plan() + + assert all(query) == + ~s{SELECT DISTINCT s0."x", s1."id" FROM "schema" AS s0 INNER JOIN "schema3" AS s1 ON s1."id" = s0."y" ORDER BY s1."id"} + + query = + Schema + |> join(:inner, [r], assoc(r, :permalink)) + |> order_by([r, p], [p.id]) + |> distinct([r], true) + |> select([r], struct(r, [:x])) + |> plan() + + assert all(query) == + ~s{SELECT DISTINCT s0."x", s1."id" FROM "schema" AS s0 INNER JOIN "schema3" AS s1 ON s1."id" = s0."y" ORDER BY s1."id"} + + query = + Schema + |> join(:inner, [r], subquery(from p in Schema3, where: not is_nil(p.binary)), on: true) + |> order_by([r, p], [p.id]) + |> distinct([r], true) + |> select([r], struct(r, [:x])) + |> plan() + + assert all(query) == + ~s{SELECT DISTINCT s0."x", s1."id" FROM "schema" AS s0 INNER JOIN (SELECT ss0."id" AS "id", ss0."list1" AS "list1", ss0."list2" AS "list2", ss0."binary" AS "binary" FROM "schema3" AS ss0 WHERE (NOT (ss0."binary" IS NULL))) AS s1 ON TRUE ORDER BY s1."id"} + + query = + Schema + |> join(:inner, [r], assoc(r, :permalink)) + |> order_by([r], [r.id]) + |> distinct([r], true) + |> select([r], r.x) + |> plan() + + assert all(query) == + ~s{SELECT DISTINCT s0."x" FROM "schema" AS s0 INNER JOIN "schema3" AS s1 ON s1."id" = s0."y" ORDER BY s0."id"} + end + test "coalesce" do query = Schema |> select([s], coalesce(s.x, 5)) |> plan() assert all(query) == ~s{SELECT coalesce(s0."x", 5) FROM "schema" AS s0}