Skip to content

Commit 82fcf6c

Browse files
Allow take with tuple fragment sources (#4666)
1 parent 3ab6a20 commit 82fcf6c

File tree

3 files changed

+53
-2
lines changed

3 files changed

+53
-2
lines changed

integration_test/cases/repo.exs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -782,6 +782,19 @@ defmodule Ecto.Integration.RepoTest do
782782
assert_raise Ecto.NoResultsError, fn -> query |> last |> TestRepo.one! end
783783
end
784784

785+
test "fragment source mapped to schema" do
786+
query = from f in {fragment("select 1 as num"), Barebone}
787+
assert %Barebone{num: 1} = TestRepo.one(query)
788+
end
789+
790+
test "fragment source mapped to schema with take" do
791+
query = from f in {fragment("select 1 as visits"), Post}, select: struct(f, [:visits])
792+
assert %Post{visits: 1} = TestRepo.one(query)
793+
794+
query = from f in {fragment("select 1 as visits"), Post}, select: map(f, [:visits])
795+
assert TestRepo.one(query) == %{visits: 1}
796+
end
797+
785798
test "exists?" do
786799
TestRepo.insert!(%Post{title: "1", visits: 2})
787800
TestRepo.insert!(%Post{title: "2", visits: 1})

lib/ecto/query/planner.ex

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2220,11 +2220,15 @@ defmodule Ecto.Query.Planner do
22202220
error!(query, "struct/2 in select expects a source with a schema")
22212221

22222222
{{:ok, {kind, fields}}, {source, schema, prefix}} when is_binary(source) ->
2223-
dumper = if schema, do: schema.__schema__(:dump), else: %{}
2223+
{types, fields} = select_dump_for_schema(schema, List.wrap(fields), ix, drop)
22242224
schema = if kind == :map, do: nil, else: schema
2225-
{types, fields} = select_dump(List.wrap(fields), dumper, ix, drop)
22262225
{{:source, {source, schema}, prefix || query.prefix, types}, fields}
22272226

2227+
{{:ok, {kind, fields}}, {{:fragment, _, _} = source, schema, prefix}} ->
2228+
{types, fields} = select_dump_for_schema(schema, List.wrap(fields), ix, drop)
2229+
schema = if kind == :map, do: nil, else: schema
2230+
{{:source, {source, schema}, prefix, types}, fields}
2231+
22282232
{{:ok, {_, fields}}, _} ->
22292233
{{:map, Enum.map(fields, &{&1, {:value, :any}})},
22302234
Enum.map(fields, &select_field(&1, ix, :always))}
@@ -2258,6 +2262,11 @@ defmodule Ecto.Query.Planner do
22582262
end
22592263
end
22602264

2265+
defp select_dump_for_schema(schema, fields, ix, drop) do
2266+
dumper = if schema, do: schema.__schema__(:dump), else: %{}
2267+
select_dump(List.wrap(fields), dumper, ix, drop)
2268+
end
2269+
22612270
defp select_dump(fields, dumper, ix, drop) do
22622271
fields
22632272
|> Enum.reverse()

test/ecto/query/planner_test.exs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -958,6 +958,23 @@ defmodule Ecto.Query.PlannerTest do
958958
assert [:all, {:from, {{:fragment, _, _}, Barebone, _, _}, []}] = cache_key
959959
end
960960

961+
test "plan: tuple source with fragment and take" do
962+
{query, cast_params, dump_params, cache_key} =
963+
plan(from f in {fragment("? as text", ^"hi"), Post}, select: struct(f, [:text]))
964+
965+
assert query.select.take == %{0 => {:struct, [:text]}}
966+
assert {{{:fragment, [], _}, Post, nil}} = query.sources
967+
assert cast_params == ["hi"]
968+
assert dump_params == ["hi"]
969+
970+
assert [
971+
:all,
972+
{:take, %{0 => {:struct, [:text]}}},
973+
{:from, {{:fragment, _, _}, Post, _, _}, []},
974+
{:select, {:&, [], [0]}}
975+
] = cache_key
976+
end
977+
961978
describe "plan: CTEs" do
962979
test "with uncacheable queries are uncacheable" do
963980
{_, _, _, cache} =
@@ -2601,6 +2618,18 @@ defmodule Ecto.Query.PlannerTest do
26012618
assert query.select.fields == [{{:., [writable: :always], [{:&, [], [0]}, :num]}, [], []}]
26022619
end
26032620

2621+
test "normalize: tuple source with fragment and take" do
2622+
{query, _, _, select} =
2623+
normalize_with_params(
2624+
from f in {fragment("? as text", ^"hi"), Post}, select: struct(f, [:text])
2625+
)
2626+
2627+
%{from: {_, {:source, {{:fragment, _, _}, Post}, nil, types}}} = select
2628+
assert types == [text: :string]
2629+
assert {{:fragment, _, _}, Post} = query.from.source
2630+
assert query.select.fields == [{{:., [writable: :always], [{:&, [], [0]}, :text]}, [], []}]
2631+
end
2632+
26042633
describe "normalize: subqueries in boolean expressions" do
26052634
test "replaces {:subquery, index} with an Ecto.SubQuery struct" do
26062635
subquery = from(p in Post, select: p.visits)

0 commit comments

Comments
 (0)