diff --git a/lib/ecto/query/planner.ex b/lib/ecto/query/planner.ex index 685700e695..7a831a0247 100644 --- a/lib/ecto/query/planner.ex +++ b/lib/ecto/query/planner.ex @@ -2258,8 +2258,8 @@ defmodule Ecto.Query.Planner do {{:ok, {:struct, _}}, {:fragment, _, _}} -> error!(query, "it is not possible to return a struct subset of a fragment") - {{:ok, {:struct, fields}}, %Ecto.SubQuery{select: select}} -> - subquery_select_fields(select, fields, ix, query) + {{:ok, {kind, fields}}, %Ecto.SubQuery{select: select}} -> + subquery_select_fields(kind, select, fields, ix, query) {{:ok, {_, []}}, {_, _, _}} -> error!( @@ -2336,16 +2336,20 @@ defmodule Ecto.Query.Planner do end) end - defp subquery_select_fields(select, requested_fields, ix, query) do + defp subquery_select_fields(kind, select, requested_fields, ix, query) do available_fields = subquery_source_fields(select) requested_fields = List.wrap(requested_fields) schema = - case select do - {:source, {_, schema}, _, _} when not is_nil(schema) -> schema + case {kind, select} do + {kind, {:source, {_, schema}, _, _}} when not is_nil(schema) and kind != :map -> + schema - _ -> - error!(query, "it is not possible to return a struct subset of a subquery that does not return a schema struct") + {kind, _} when kind != :struct -> + nil + + {:struct, _} -> + error!(query, "it is not possible to return a struct subset of a subquery that does not return a schema struct") end types = @@ -2355,7 +2359,7 @@ defmodule Ecto.Query.Planner do {field, type} :error -> - error!(query, "field `#{field}` in struct/2 is not available in the subquery. " <> + error!(query, "field `#{field}` is not available in the subquery. " <> "Subquery only returns fields: #{inspect(available_fields)}") end end) diff --git a/test/ecto/query/planner_test.exs b/test/ecto/query/planner_test.exs index b403ca4818..9689da3983 100644 --- a/test/ecto/query/planner_test.exs +++ b/test/ecto/query/planner_test.exs @@ -2670,6 +2670,20 @@ defmodule Ecto.Query.PlannerTest do ] = query.select.fields end + test "normalze: select list of fields from subquery source" do + {_, _, _, select} = subquery(Post) |> select([p], [:title]) |> normalize_with_params() + %{from: {_, {:source, {_, postprocess_schema}, _, types}}} = select + assert postprocess_schema == Post + assert types == [title: :string] + end + + test "normalze: select map/2 from subquery source" do + {_, _, _, select} = subquery(Post) |> select([p], map(p, [:title])) |> normalize_with_params() + %{from: {_, {:source, {_, postprocess_schema}, _, types}}} = select + assert postprocess_schema == nil + assert types == [title: :string] + end + test "normalize: select with :%{}" do query = Post |> select([p], %{p | title: "foo"}) |> normalize() assert query.select.expr == {:%{}, [], [{:|, [], [{:&, [], [0]}, [title: "foo"]]}]}