Packages
ecto
0.2.2
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/util.ex
defmodule Ecto.Query.Util do
@moduledoc """
This module provide utility functions on queries.
"""
alias Ecto.Query
@doc """
Look up a source with a variable.
"""
def find_source(sources, {:&, _, [ix]}) when is_tuple(sources) do
elem(sources, ix)
end
def find_source(sources, {:&, _, [ix]}) when is_list(sources) do
Enum.at(sources, ix)
end
@doc """
Look up the expression where the variable was bound.
"""
def source_expr(%Query{from: from}, {:&, _, [0]}) do
from
end
def source_expr(%Query{joins: joins}, {:&, _, [ix]}) do
Enum.at(joins, ix - 1)
end
@doc "Returns the source from a source tuple."
def source({source, _model}), do: source
@doc "Returns model from a source tuple or nil if there is none."
def model({_source, model}), do: model
# Converts internal type format to "typespec" format
@doc false
def type_to_ast({type, inner}), do: {type, [], [type_to_ast(inner)]}
def type_to_ast(type) when is_atom(type), do: {type, [], nil}
@doc false
defmacro types do
~w(boolean string integer float decimal binary datetime date time interval virtual)a
end
@doc false
defmacro poly_types do
~w(array)a
end
# Takes an elixir value and returns its ecto type
@doc false
def value_to_type(value, fun \\ nil)
def value_to_type(nil, _fun), do: {:ok, nil}
def value_to_type(value, _fun) when is_boolean(value), do: {:ok, :boolean}
def value_to_type(value, _fun) when is_binary(value), do: {:ok, :string}
def value_to_type(value, _fun) when is_integer(value), do: {:ok, :integer}
def value_to_type(value, _fun) when is_float(value), do: {:ok, :float}
def value_to_type(%Decimal{}, _fun), do: {:ok, :decimal}
def value_to_type(%Ecto.DateTime{} = dt, fun) do
values = Map.delete(dt, :__struct__) |> Map.values
types = Enum.map(values, &value_to_type(&1, fun))
res = Enum.find_value(types, fn
{:ok, :integer} -> nil
{:error, _} = err -> err
{:error, "all datetime elements have to be a literal of integer type"}
end)
res || {:ok, :datetime}
end
def value_to_type(%Ecto.Date{} = d, fun) do
values = Map.delete(d, :__struct__) |> Map.values
types = Enum.map(values, &value_to_type(&1, fun))
res = Enum.find_value(types, fn
{:ok, :integer} -> nil
{:error, _} = err -> err
{:error, "all date elements have to be a literal of integer type"}
end)
res || {:ok, :date}
end
def value_to_type(%Ecto.Time{} = t, fun) do
values = Map.delete(t, :__struct__) |> Map.values
types = Enum.map(values, &value_to_type(&1, fun))
res = Enum.find_value(types, fn
{:ok, :integer} -> nil
{:error, _} = err -> err
{:error, "all time elements have to be a literal of integer type"}
end)
res || {:ok, :time}
end
def value_to_type(%Ecto.Interval{} = dt, fun) do
values = Map.delete(dt, :__struct__) |> Map.values
types = Enum.map(values, &value_to_type(&1, fun))
res = Enum.find_value(types, fn
{:ok, :integer} -> nil
{:error, _} = err -> err
_ -> {:error, "all interval elements have to be a literal of integer type"}
end)
if res do
res
else
{:ok, :interval}
end
end
def value_to_type(%Ecto.Binary{value: binary}, fun) do
case value_to_type(binary, fun) do
{:ok, :binary} -> {:ok, :binary}
{:ok, :string} -> {:ok, :binary}
{:error, _} = err -> err
_ -> {:error, "binary/1 argument has to be a literal of binary type"}
end
end
def value_to_type(%Ecto.Array{value: list, type: type}, fun) do
unless type in types or (list == [] and nil?(type)) do
{:error, "invalid type given to `array/2`: `#{inspect type}`"}
end
elem_types = Enum.map(list, &value_to_type(&1, fun))
res = Enum.find_value(elem_types, fn
{:ok, elem_type} ->
unless type_eq?(type, elem_type) do
{:error, "all elements in array have to be of same type"}
end
{:error, _} = err ->
err
end)
if res do
res
else
{:ok, {:array, type}}
end
end
def value_to_type(value, nil), do: {:error, "`unknown type of value `#{inspect value}`"}
def value_to_type(expr, fun), do: fun.(expr)
# Returns true if value is a query literal
@doc false
def literal?(nil), do: true
def literal?(value) when is_boolean(value), do: true
def literal?(value) when is_binary(value), do: true
def literal?(value) when is_integer(value), do: true
def literal?(value) when is_float(value), do: true
def literal?(%Decimal{}), do: true
def literal?(%Ecto.DateTime{}), do: true
def literal?(%Ecto.Date{}), do: true
def literal?(%Ecto.Time{}), do: true
def literal?(%Ecto.Interval{}), do: true
def literal?(%Ecto.Binary{}), do: true
def literal?(%Ecto.Array{}), do: true
def literal?(_), do: false
# Returns true if the two types are considered equal by the type system
# Note that this does not consider casting
@doc false
def type_eq?(_, :any), do: true
def type_eq?(:any, _), do: true
def type_eq?({outer, inner1}, {outer, inner2}), do: type_eq?(inner1, inner2)
def type_eq?(type, type), do: true
def type_eq?(_, _), do: false
# Returns true if another type can be casted to the given type
@doc false
def type_castable_to?(:binary), do: true
def type_castable_to?({:array, _}), do: true
def type_castable_to?(_), do: false
# Tries to cast the given value to the specified type.
# If value cannot be casted just return it.
@doc false
def try_cast(binary, :binary) when is_binary(binary) do
%Ecto.Binary{value: binary}
end
def try_cast(list, {:array, inner}) when is_list(list) do
%Ecto.Array{value: list, type: inner}
end
def try_cast(value, _) do
value
end
# Get var for given model in query
def model_var(query, model) do
sources = Tuple.to_list(query.sources)
pos = Enum.find_index(sources, &(model(&1) == model))
{:&, [], [pos]}
end
# Find var in select clause. Returns a list of tuple and list indicies to
# find the var.
def locate_var({left, right}, var) do
locate_var({:{}, [], [left, right]}, var)
end
def locate_var({:{}, _, list}, var) do
locate_var(list, var)
end
def locate_var({:assoc, _, [left, _right]}, var) do
if left == var, do: []
end
def locate_var(list, var) when is_list(list) do
list = Stream.with_index(list)
res = Enum.find_value(list, fn {elem, ix} ->
if poss = locate_var(elem, var) do
{poss, ix}
else
nil
end
end)
case res do
{poss, pos} -> [pos|poss]
nil -> nil
end
end
def locate_var(expr, var) do
if expr == var, do: []
end
end