Packages
ecto
2.1.0-rc.3
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
import Kernel, except: [apply: 2]
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
{query, binding} = Builder.escape_binding(query, 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(%Ecto.Query{select: nil} = query, expr) do
%{query | select: expr}
end
def apply(%Ecto.Query{}, _expr) do
Builder.error! "only one select expression is allowed in query"
end
def apply(query, expr) do
apply(Ecto.Queryable.to_query(query), expr)
end
end