Packages
ecto
0.2.5
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/join.ex
defmodule Ecto.Query.Builder.Join do
@moduledoc false
alias Ecto.Query.Builder
alias Ecto.Query.JoinExpr
@doc """
Escapes a join expression (not including the `on` expression).
It returns a tuple containing the binds, the on expression (if available)
and the association expression.
## Examples
iex> escape(quote(do: x in "foo"), [])
{:x, "foo", nil}
iex> escape(quote(do: "foo"), [])
{nil, "foo", nil}
iex> escape(quote(do: x in Sample), [])
{:x, {:__aliases__, [alias: false], [:Sample]}, nil}
iex> escape(quote(do: c in p.comments), [p: 0])
{:c, nil, {{:{}, [], [:&, [], [0]]}, :comments}}
"""
@spec escape(Macro.t, Keyword.t) :: {[atom], Macro.t | nil, Macro.t | nil}
def escape({:in, _, [{var, _, context}, expr]}, vars)
when is_atom(var) and is_atom(context) do
{_, expr, assoc} = escape(expr, vars)
{var, expr, assoc}
end
def escape({:in, _, [{var, _, context}, expr]}, vars)
when is_atom(var) and is_atom(context) do
{_, expr, assoc} = escape(expr, vars)
{var, expr, assoc}
end
def escape({:__aliases__, _, _} = module, _vars) do
{nil, module, nil}
end
def escape(string, _vars) when is_binary(string) do
{nil, string, nil}
end
def escape(dot, vars) do
case Builder.escape_dot(dot, vars) do
{_, _} = var_field ->
{[], nil, var_field}
:error ->
raise Ecto.QueryError, reason: "malformed `join` query expression"
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_with_binds(Macro.t, atom, [Macro.t], Macro.t, Macro.t, Macro.t, Macro.Env.t) :: {Macro.t, Keyword.t, non_neg_integer | nil}
def build_with_binds(query, qual, binding, expr, on, count_bind, env) do
binding = Builder.escape_binding(binding)
{join_bind, join_expr, join_assoc} = escape(expr, binding)
is_assoc? = not is_nil(join_assoc)
validate_qual(qual)
validate_on(on, is_assoc?)
validate_bind(join_bind, binding)
if join_bind && !count_bind do
# If count_bind is not an integer, make it a variable.
# The variable is the getter/setter storage.
count_bind = quote(do: count_bind)
count_setter = quote(do: unquote(count_bind) = Builder.count_binds(query))
end
binding = binding ++ [{join_bind, count_bind}]
join_on = escape_on(on, binding, env)
join =
quote do
%JoinExpr{qual: unquote(qual), source: unquote(join_expr),
on: unquote(join_on), assoc: unquote(join_assoc),
file: unquote(env.file), line: unquote(env.line)}
end
if is_integer(count_bind) do
count_bind = count_bind + 1
quoted = Builder.apply_query(query, __MODULE__, [join], env)
else
count_bind = quote(do: unquote(count_bind) + 1)
quoted =
quote do
query = Ecto.Queryable.to_query(unquote(query))
unquote(count_setter)
%{query | joins: query.joins ++ [unquote(join)]}
end
end
{quoted, binding, count_bind}
end
def apply(query, expr) do
query = Ecto.Queryable.to_query(query)
%{query | joins: query.joins ++ [expr]}
end
defp escape_on(nil, _binding, _env), do: nil
defp escape_on(on, binding, env) do
{on, external} = Builder.escape(on, binding)
external = Builder.escape_external(external)
quote do: %Ecto.Query.QueryExpr{
expr: unquote(on),
external: unquote(external),
line: unquote(env.line),
file: unquote(env.file)}
end
@qualifiers [:inner, :left, :right, :full]
defp validate_qual(qual) when qual in @qualifiers, do: :ok
defp validate_qual(_qual) do
raise Ecto.QueryError,
reason: "invalid join qualifier, accepted qualifiers are: " <>
Enum.map_join(@qualifiers, ", ", &"`#{inspect &1}`")
end
defp validate_on(nil, false) do
raise Ecto.QueryError,
reason: "`join` expression requires explicit `on` " <>
"expression unless it's an association join expression"
end
defp validate_on(_on, _is_assoc?), do: :ok
defp validate_bind(bind, all) do
if bind && bind in all do
raise Ecto.QueryError, reason: "variable `#{bind}` is already defined in query"
end
end
end