Packages
ecto
2.0.6
3.14.1
3.14.0
3.13.6
3.13.5
3.13.4
3.13.3
3.13.2
3.13.1
3.13.0
3.12.6
3.12.5
3.12.4
3.12.3
3.12.2
3.12.1
3.12.0
3.11.2
3.11.1
3.11.0
3.10.3
3.10.2
3.10.1
3.10.0
3.9.6
3.9.5
3.9.4
3.9.3
3.9.2
3.9.1
3.9.0
3.8.4
3.8.3
3.8.2
3.8.1
3.8.0
3.7.2
3.7.1
3.7.0
3.6.2
3.6.1
3.6.0
3.5.8
3.5.7
3.5.6
3.5.5
3.5.4
3.5.3
3.5.2
3.5.1
3.5.0
3.5.0-rc.1
3.5.0-rc.0
3.4.6
3.4.5
3.4.4
3.4.3
3.4.2
3.4.1
3.4.0
3.3.4
3.3.3
3.3.2
3.3.1
3.3.0
3.2.5
3.2.4
3.2.3
3.2.2
3.2.1
3.2.0
3.1.7
3.1.6
3.1.5
3.1.4
3.1.3
3.1.2
3.1.1
3.1.0
3.0.9
3.0.8
3.0.7
3.0.6
3.0.5
3.0.4
3.0.3
3.0.2
3.0.1
3.0.0
3.0.0-rc.1
3.0.0-rc.0
2.2.12
2.2.11
2.2.10
2.2.9
2.2.8
2.2.7
2.2.6
2.2.5
2.2.4
2.2.3
2.2.2
2.2.1
2.2.0
2.2.0-rc.1
2.2.0-rc.0
2.1.6
2.1.5
2.1.4
2.1.3
2.1.2
2.1.1
2.1.0
2.1.0-rc.5
2.1.0-rc.4
2.1.0-rc.3
2.1.0-rc.2
2.1.0-rc.1
2.1.0-rc.0
2.0.6
2.0.5
2.0.4
2.0.3
2.0.2
2.0.1
2.0.0
2.0.0-rc.6
2.0.0-rc.5
2.0.0-rc.4
2.0.0-rc.3
2.0.0-rc.2
2.0.0-rc.1
2.0.0-rc.0
2.0.0-beta.2
2.0.0-beta.1
2.0.0-beta.0
1.1.9
1.1.8
1.1.7
1.1.6
1.1.5
1.1.4
1.1.3
1.1.2
1.1.1
1.1.0
1.0.7
1.0.6
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
0.16.0
0.15.0
0.14.3
0.14.2
0.14.1
0.14.0
0.13.1
0.13.0
0.12.1
0.12.0
0.12.0-rc
0.11.3
0.11.2
0.11.1
0.11.0
0.10.3
0.10.2
0.10.1
0.10.0
0.9.0
0.8.1
0.8.0
0.7.2
0.7.1
0.7.0
0.6.0
0.5.1
0.5.0
0.4.0
0.3.0
0.2.8
0.2.7
0.2.6
0.2.5
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.0
A toolkit for data mapping and language integrated query for Elixir
Current section
Files
Jump to
Current section
Files
lib/ecto/query/builder/select.ex
defmodule Ecto.Query.Builder.Select do
@moduledoc false
alias Ecto.Query.Builder
@doc """
Escapes a select.
It allows tuples, lists and variables at the top level. Inside the
tuples and lists query expressions are allowed.
## Examples
iex> escape({1, 2}, [], __ENV__)
{{:{}, [], [:{}, [], [1, 2]]}, {%{}, %{}}}
iex> escape([1, 2], [], __ENV__)
{[1, 2], {%{}, %{}}}
iex> escape(quote(do: x), [x: 0], __ENV__)
{{:{}, [], [:&, [], [0]]}, {%{}, %{}}}
"""
@spec escape(Macro.t, Keyword.t, Macro.Env.t) :: {Macro.t, {%{}, %{}}}
def escape({:^, _, [interpolated]}, _vars, _env) do
fields = quote(do: Ecto.Query.Builder.Select.fields!(:select, unquote(interpolated)))
{{:{}, [], [:&, [], [0]]}, {%{}, %{0 => {:any, fields}}}}
end
def escape(other, vars, env) do
if take?(other) do
{{:{}, [], [:&, [], [0]]}, {%{}, %{0 => {:any, other}}}}
else
escape(other, {%{}, %{}}, vars, env)
end
end
# Tuple
defp escape({left, right}, params_take, vars, env) do
escape({:{}, [], [left, right]}, params_take, vars, env)
end
# Tuple
defp escape({:{}, _, list}, params_take, vars, env) do
{list, params_take} = Enum.map_reduce(list, params_take, &escape(&1, &2, vars, env))
expr = {:{}, [], [:{}, [], list]}
{expr, params_take}
end
# Map
defp escape({:%{}, _, [{:|, _, [data, pairs]}]}, params_take, vars, env) do
{data, params_take} = escape(data, params_take, vars, env)
{pairs, params_take} = escape_pairs(pairs, params_take, vars, env)
{{:{}, [], [:%{}, [], [{:{}, [], [:|, [], [data, pairs]]}]]}, params_take}
end
defp escape({:%{}, _, pairs}, params_take, vars, env) do
{pairs, params_take} = escape_pairs(pairs, params_take, vars, env)
{{:{}, [], [:%{}, [], pairs]}, params_take}
end
# List
defp escape(list, params_take, vars, env) when is_list(list) do
Enum.map_reduce(list, params_take, &escape(&1, &2, vars, env))
end
# map/struct(var, [:foo, :bar])
defp escape({tag, _, [{var, _, context}, fields]}, {params, take}, vars, _env)
when tag in [:map, :struct] and is_atom(var) and is_atom(context) do
taken =
case fields do
{:^, _, [interpolated]} ->
quote(do: Ecto.Query.Builder.Select.fields!(unquote(tag), unquote(interpolated)))
_ when is_list(fields) ->
fields
true ->
Builder.error! "`#{tag}/2` in `select` expects either a literal or "
"an interpolated list of atom fields"
end
expr = Builder.escape_var(var, vars)
take = Map.put(take, Builder.find_var!(var, vars), {tag, taken})
{expr, {params, take}}
end
# var
defp escape({var, _, context}, params_take, vars, _env)
when is_atom(var) and is_atom(context) do
expr = Builder.escape_var(var, vars)
{expr, params_take}
end
defp escape(other, {params, take}, vars, env) do
{other, params} = Builder.escape(other, :any, params, vars, env)
{other, {params, take}}
end
defp escape_pairs(pairs, params_take, vars, env) do
Enum.map_reduce pairs, params_take, fn({k, v}, acc) ->
{k, acc} = escape_key(k, acc, vars, env)
{v, acc} = escape(v, acc, vars, env)
{{k, v}, acc}
end
end
defp escape_key(k, params_take, _vars, _env) when is_atom(k) do
{k, params_take}
end
defp escape_key(k, params_take, vars, env) do
escape(k, params_take, vars, env)
end
@doc """
Called at runtime to verify a field.
"""
def fields!(tag, fields) do
if take?(fields) do
fields
else
raise ArgumentError,
"expected a list of fields in `#{tag}/2` inside `select`, got: `#{inspect fields}`"
end
end
defp take?(fields) do
is_list(fields) and Enum.all?(fields, fn
{k, v} when is_atom(k) -> take?(List.wrap(v))
k when is_atom(k) -> true
_ -> false
end)
end
@doc """
Builds a quoted expression.
The quoted expression should evaluate to a query at runtime.
If possible, it does all calculations at compile time to avoid
runtime work.
"""
@spec build(Macro.t, [Macro.t], Macro.t, Macro.Env.t) :: Macro.t
def build(query, binding, expr, env) do
binding = Builder.escape_binding(binding)
{expr, {params, take}} = escape(expr, binding, env)
params = Builder.escape_params(params)
take = {:%{}, [], Map.to_list(take)}
select = quote do: %Ecto.Query.SelectExpr{
expr: unquote(expr),
params: unquote(params),
file: unquote(env.file),
line: unquote(env.line),
take: unquote(take)}
Builder.apply_query(query, __MODULE__, [select], env)
end
@doc """
The callback applied by `build/4` to build the query.
"""
@spec apply(Ecto.Queryable.t, term) :: Ecto.Query.t
def apply(query, select) do
query = Ecto.Queryable.to_query(query)
if query.select do
Builder.error! "only one select expression is allowed in query"
else
%{query | select: select}
end
end
end