diff --git a/lib/ecto/query/planner.ex b/lib/ecto/query/planner.ex index 129abc24a7..1615f78d16 100644 --- a/lib/ecto/query/planner.ex +++ b/lib/ecto/query/planner.ex @@ -1282,8 +1282,11 @@ defmodule Ecto.Query.Planner do error!(query, "queries that do not have a schema need to explicitly pass a :select clause") end - def ensure_select(%{select: nil, from: %{source: {:fragment, _, _}}} = query, true) do - error!(query, "queries from a fragment need to explicitly pass a :select clause") + def ensure_select(%{select: nil, from: %{source: {:fragment, [], _}}} = query, true) do + error!( + query, + "queries from a fragment need to explicitly pass a :select clause or use the `:columns` option" + ) end def ensure_select(%{select: nil} = query, true) do @@ -2248,8 +2251,13 @@ defmodule Ecto.Query.Planner do {{:map, Enum.map(fields, &{&1, {:value, :any}})}, Enum.map(fields, &select_field(&1, ix, :always))} - {:error, {:fragment, _, _}} -> - {{:value, :map}, [{:&, [], [ix]}]} + {:error, {:fragment, meta, _}} -> + if columns = meta[:column_names] do + {{:map, Enum.map(columns, &{&1, {:value, :any}})}, + Enum.map(columns, &select_field(&1, ix, :always))} + else + {{:value, :map}, [{:&, [], [ix]}]} + end {:error, {:values, _, [types, _]}} -> fields = Keyword.keys(types) diff --git a/test/ecto/query/planner_test.exs b/test/ecto/query/planner_test.exs index f128b838bb..df91554426 100644 --- a/test/ecto/query/planner_test.exs +++ b/test/ecto/query/planner_test.exs @@ -2502,6 +2502,12 @@ defmodule Ecto.Query.PlannerTest do end end + test "normalize: select source on fragment with columns" do + query = from f in fragment("select 1", columns: [:x]) + {_, _, _, select} = normalize_with_params(query) + assert %{from: {_, {:map, [x: {:value, :any}]}}} = select + end + test "normalize: select with map/2" do query = Post |> select([p], map(p, [:id, :title])) |> normalize() assert query.select.expr == {:&, [], [0]} diff --git a/test/ecto/query/subquery_test.exs b/test/ecto/query/subquery_test.exs index 8ce3601d43..1718f29232 100644 --- a/test/ecto/query/subquery_test.exs +++ b/test/ecto/query/subquery_test.exs @@ -288,7 +288,7 @@ defmodule Ecto.Query.SubqueryTest do test "raises on fragment source without :select" do query = from f in fragment("select 1 as x") - assert_raise Ecto.SubQueryError, ~r/queries from a fragment need to explicitly pass a :select clause in query/, fn -> + assert_raise Ecto.SubQueryError, ~r/queries from a fragment need to explicitly pass a :select clause/, fn -> plan(from(subquery(query), [])) end end