Skip to content

Commit 773eeb7

Browse files
committed
Allow using type/2 with virtual fields
1 parent b2a2b75 commit 773eeb7

File tree

3 files changed

+39
-13
lines changed

3 files changed

+39
-13
lines changed

Diff for: lib/ecto/query/planner.ex

+19-10
Original file line numberDiff line numberDiff line change
@@ -1181,7 +1181,7 @@ defmodule Ecto.Query.Planner do
11811181

11821182
defp prewalk({:type, _, [arg, type]}, kind, query, expr, acc, adapter) do
11831183
{arg, acc} = prewalk(arg, kind, query, expr, acc, adapter)
1184-
type = field_type!(kind, query, expr, type)
1184+
type = field_type!(kind, query, expr, type, true)
11851185
{%Ecto.Query.Tagged{value: arg, tag: type, type: Ecto.Type.type(type)}, acc}
11861186
end
11871187

@@ -1700,25 +1700,31 @@ defmodule Ecto.Query.Planner do
17001700
end
17011701
end
17021702

1703-
defp field_type!(kind, query, expr, {composite, {ix, field}}) when is_integer(ix) do
1704-
{composite, type!(kind, query, expr, ix, field)}
1703+
defp field_type!(kind, query, expr, type, allow_virtuals? \\ false)
1704+
1705+
defp field_type!(kind, query, expr, {composite, {ix, field}}, allow_virtuals?) when is_integer(ix) do
1706+
{composite, type!(kind, query, expr, ix, field, allow_virtuals?)}
17051707
end
1706-
defp field_type!(kind, query, expr, {ix, field}) when is_integer(ix) do
1707-
type!(kind, query, expr, ix, field)
1708+
1709+
defp field_type!(kind, query, expr, {ix, field}, allow_virtuals?) when is_integer(ix) do
1710+
type!(kind, query, expr, ix, field, allow_virtuals?)
17081711
end
1709-
defp field_type!(_kind, _query, _expr, type) do
1712+
1713+
defp field_type!(_kind, _query, _expr, type, _) do
17101714
type
17111715
end
17121716

1713-
defp type!(_kind, _query, _expr, nil, _field), do: :any
1717+
defp type!(kind, query, expr, schema, field, allow_virtuals? \\ false)
17141718

1715-
defp type!(kind, query, expr, ix, field) when is_integer(ix) do
1719+
defp type!(_kind, _query, _expr, nil, _field, _allow_virtuals?), do: :any
1720+
1721+
defp type!(kind, query, expr, ix, field, allow_virtuals?) when is_integer(ix) do
17161722
case get_source!(kind, query, ix) do
17171723
{:fragment, _, _} ->
17181724
:any
17191725

17201726
{_, schema, _} ->
1721-
type!(kind, query, expr, schema, field)
1727+
type!(kind, query, expr, schema, field, allow_virtuals?)
17221728

17231729
%Ecto.SubQuery{select: select} ->
17241730
case subquery_type_for(select, field) do
@@ -1728,11 +1734,14 @@ defmodule Ecto.Query.Planner do
17281734
end
17291735
end
17301736

1731-
defp type!(kind, query, expr, schema, field) when is_atom(schema) do
1737+
defp type!(kind, query, expr, schema, field, allow_virtuals?) when is_atom(schema) do
17321738
cond do
17331739
type = schema.__schema__(:type, field) ->
17341740
type
17351741

1742+
type = allow_virtuals? && schema.__schema__(:virtual_type, field) ->
1743+
type
1744+
17361745
Map.has_key?(schema.__struct__(), field) ->
17371746
error! query, expr, "field `#{field}` in `#{kind}` is a virtual field in schema #{inspect schema}"
17381747

Diff for: lib/ecto/schema.ex

+15-3
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,7 @@ defmodule Ecto.Schema do
473473

474474
Module.register_attribute(__MODULE__, :ecto_primary_keys, accumulate: true)
475475
Module.register_attribute(__MODULE__, :ecto_fields, accumulate: true)
476+
Module.register_attribute(__MODULE__, :ecto_virtual_fields, accumulate: true)
476477
Module.register_attribute(__MODULE__, :ecto_query_fields, accumulate: true)
477478
Module.register_attribute(__MODULE__, :ecto_field_sources, accumulate: true)
478479
Module.register_attribute(__MODULE__, :ecto_assocs, accumulate: true)
@@ -600,6 +601,7 @@ defmodule Ecto.Schema do
600601
autoupdate = @ecto_autoupdate |> Enum.reverse
601602
fields = @ecto_fields |> Enum.reverse
602603
query_fields = @ecto_query_fields |> Enum.reverse
604+
virtual_fields = @ecto_virtual_fields |> Enum.reverse
603605
field_sources = @ecto_field_sources |> Enum.reverse
604606
assocs = @ecto_assocs |> Enum.reverse
605607
embeds = @ecto_embeds |> Enum.reverse
@@ -629,6 +631,7 @@ defmodule Ecto.Schema do
629631
def __schema__(:autoupdate), do: unquote(Macro.escape(autoupdate))
630632
def __schema__(:loaded), do: unquote(Macro.escape(loaded))
631633
def __schema__(:redact_fields), do: unquote(redacted_fields)
634+
def __schema__(:virtual_fields), do: unquote(Enum.map(virtual_fields, &elem(&1, 0)))
632635

633636
def __schema__(:query) do
634637
%Ecto.Query{
@@ -639,7 +642,7 @@ defmodule Ecto.Schema do
639642
}
640643
end
641644

642-
for clauses <- Ecto.Schema.__schema__(fields, field_sources, assocs, embeds),
645+
for clauses <- Ecto.Schema.__schema__(fields, field_sources, assocs, embeds, virtual_fields),
643646
{args, body} <- clauses do
644647
def __schema__(unquote_splicing(args)), do: unquote(body)
645648
end
@@ -1919,7 +1922,9 @@ defmodule Ecto.Schema do
19191922
Module.put_attribute(mod, :ecto_redact_fields, name)
19201923
end
19211924

1922-
unless virtual? do
1925+
if virtual? do
1926+
Module.put_attribute(mod, :ecto_virtual_fields, {name, type})
1927+
else
19231928
source = opts[:source] || Module.get_attribute(mod, :field_source_mapper).(name)
19241929

19251930
if name != source do
@@ -2082,7 +2087,7 @@ defmodule Ecto.Schema do
20822087
end
20832088

20842089
@doc false
2085-
def __schema__(fields, field_sources, assocs, embeds) do
2090+
def __schema__(fields, field_sources, assocs, embeds, virtual_fields) do
20862091
load =
20872092
for {name, type} <- fields do
20882093
if alias = field_sources[name] do
@@ -2107,6 +2112,11 @@ defmodule Ecto.Schema do
21072112
{[:type, name], Macro.escape(type)}
21082113
end
21092114

2115+
virtual_types_quoted =
2116+
for {name, type} <- virtual_fields do
2117+
{[:virtual_type, name], Macro.escape(type)}
2118+
end
2119+
21102120
assoc_quoted =
21112121
for {name, refl} <- assocs do
21122122
{[:association, name], Macro.escape(refl)}
@@ -2131,6 +2141,7 @@ defmodule Ecto.Schema do
21312141
catch_all = [
21322142
{[:field_source, quote(do: _)], nil},
21332143
{[:type, quote(do: _)], nil},
2144+
{[:virtual_type, quote(do: _)], nil},
21342145
{[:association, quote(do: _)], nil},
21352146
{[:embed, quote(do: _)], nil}
21362147
]
@@ -2139,6 +2150,7 @@ defmodule Ecto.Schema do
21392150
single_arg,
21402151
field_sources_quoted,
21412152
types_quoted,
2153+
virtual_types_quoted,
21422154
assoc_quoted,
21432155
embed_quoted,
21442156
catch_all

Diff for: test/ecto/query/planner_test.exs

+5
Original file line numberDiff line numberDiff line change
@@ -998,6 +998,11 @@ defmodule Ecto.Query.PlannerTest do
998998
end
999999
end
10001000

1001+
test "normalize: allow virtual fields in type/2" do
1002+
query = from(Comment, []) |> select([c], type(fragment("1"), c.temp))
1003+
normalize(query)
1004+
end
1005+
10011006
test "normalize: validate fields in left side of in expressions" do
10021007
query = from(Post, []) |> where([p], p.id in [1, 2, 3])
10031008
normalize(query)

0 commit comments

Comments
 (0)