Packages
ecto
0.8.1
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/repo/queryable.ex
defmodule Ecto.Repo.Queryable do
# The module invoked by user defined repos
# for query related functionality.
@moduledoc false
alias Ecto.Queryable
alias Ecto.Query.Builder
alias Ecto.Query.Planner
require Ecto.Query
@doc """
Implementation for `Ecto.Repo.all/2`
"""
def all(repo, adapter, queryable, opts) when is_list(opts) do
{query, params} =
Queryable.to_query(queryable)
|> Planner.query([])
adapter.all(repo, query, params, opts)
|> Ecto.Repo.Assoc.query(query)
|> Ecto.Repo.Preloader.query(repo, query, to_select(query.select))
end
@doc """
Implementation for `Ecto.Repo.get/3`
"""
def get(repo, adapter, queryable, id, opts) do
one(repo, adapter, query_for_get(queryable, id), opts)
end
@doc """
Implementation for `Ecto.Repo.get!/3`
"""
def get!(repo, adapter, queryable, id, opts) do
one!(repo, adapter, query_for_get(queryable, id), opts)
end
@doc """
Implementation for `Ecto.Repo.one/2`
"""
def one(repo, adapter, queryable, opts) do
case all(repo, adapter, queryable, opts) do
[one] -> one
[] -> nil
other -> raise Ecto.MultipleResultsError, queryable: queryable, count: length(other)
end
end
@doc """
Implementation for `Ecto.Repo.one!/2`
"""
def one!(repo, adapter, queryable, opts) do
case all(repo, adapter, queryable, opts) do
[one] -> one
[] -> raise Ecto.NoResultsError, queryable: queryable
other -> raise Ecto.MultipleResultsError, queryable: queryable, count: length(other)
end
end
@doc """
Implementation for `Ecto.Repo.update_all/3`
"""
def update_all(repo, adapter, queryable, values, opts) do
{binds, expr} = Ecto.Query.Builder.From.escape(queryable)
{updates, params} =
Enum.map_reduce(values, %{}, fn {field, expr}, params ->
{expr, params} = Builder.escape(expr, {0, field}, params, binds)
{{field, {Builder.primitive_type(expr, binds), expr}}, params}
end)
params = Builder.escape_params(params)
quote do
Ecto.Repo.Queryable.update_all(unquote(repo), unquote(adapter),
unquote(expr), unquote(updates), unquote(params), unquote(opts))
end
end
@doc """
Runtime callback for `Ecto.Repo.update_all/3`
"""
def update_all(repo, adapter, queryable, updates, params, opts) when is_list(opts) do
query = Queryable.to_query(queryable)
if updates == [] do
message = "no fields given to `update_all`"
raise ArgumentError, message
end
# If we have a model in the query, let's use it for casting.
{updates, params} = cast_update_all(query, updates, params)
{query, params} =
Queryable.to_query(queryable)
|> Planner.query(params, only_where: true)
adapter.update_all(repo, query, updates, params, opts)
end
@doc """
Implementation for `Ecto.Repo.delete_all/2`
"""
def delete_all(repo, adapter, queryable, opts) when is_list(opts) do
{query, params} =
Queryable.to_query(queryable)
|> Planner.query([], only_where: true)
adapter.delete_all(repo, query, params, opts)
end
## Helpers
defp to_select(select) do
expr = select.expr
# The planner always put the from as the first
# entry in the query, avoiding fetching it multiple
# times even if it appears multiple times in the query.
# So we always need to handle it specially.
from? = match?([{:&, _, [0]}|_], select.fields)
&to_select(&1, expr, from?)
end
defp to_select(row, expr, true),
do: transform_row(expr, hd(row), tl(row)) |> elem(0)
defp to_select(row, expr, false),
do: transform_row(expr, nil, row) |> elem(0)
defp transform_row({:{}, _, list}, from, values) do
{result, values} = transform_row(list, from, values)
{List.to_tuple(result), values}
end
defp transform_row({left, right}, from, values) do
{[left, right], values} = transform_row([left, right], from, values)
{{left, right}, values}
end
defp transform_row(list, from, values) when is_list(list) do
Enum.map_reduce(list, values, &transform_row(&1, from, &2))
end
defp transform_row(%Ecto.Query.Tagged{tag: tag}, _from, values) when not is_nil(tag) do
[value|values] = values
{Ecto.Type.load!(tag, value), values}
end
defp transform_row({:&, _, [0]}, from, values) do
{from, values}
end
defp transform_row({{:., _, [{:&, _, [_]}, _]}, meta, []}, _from, values) do
[value|values] = values
if type = Keyword.get(meta, :ecto_type) do
{Ecto.Type.load!(type, value), values}
else
{value, values}
end
end
defp transform_row(_, _from, values) do
[value|values] = values
{value, values}
end
defp query_for_get(queryable, id) do
query = Queryable.to_query(queryable)
model = assert_model!(query)
primary_key = primary_key_field!(model)
Ecto.Query.from(x in query, where: field(x, ^primary_key) == ^id)
end
defp cast_update_all(%{from: {_source, model}}, updates, params) when model != nil do
# Check all fields are valid but don't use dump as they are expressions
updates = for {field, {expected, expr}} <- updates do
type = model.__schema__(:field, field)
unless type do
raise Ecto.ChangeError,
message: "field `#{inspect model}.#{field}` in `update_all` does not exist in the model source"
end
if expected != :any and !Ecto.Type.match?(type, expected) do
raise Ecto.ChangeError,
message: "field `#{inspect model}.#{field}` in `update_all` does not type check. " <>
"It has type #{inspect type} but a type #{inspect expected} was given"
end
{field, expr}
end
# Properly cast parameters.
params = Enum.map params, fn
{v, {0, field}} ->
type = model.__schema__(:field, field)
cast_and_dump(:update_all, type, v)
{v, type} ->
cast_and_dump(:update_all, type, v)
end
{updates, params}
end
defp cast_update_all(%{}, updates, params) do
updates = for {field, {_type, expr}} <- updates, do: {field, expr}
{updates, params}
end
defp assert_model!(query) do
case query.from do
{_source, model} when model != nil ->
model
_ ->
raise Ecto.QueryError,
query: query,
message: "expected a from expression with a model"
end
end
defp cast_and_dump(kind, type, v) do
case Ecto.Type.cast(type, v) do
{:ok, v} ->
Ecto.Type.dump!(type, v)
:error ->
raise ArgumentError,
"value `#{inspect v}` in `#{kind}` cannot be cast to type #{inspect type}"
end
end
defp primary_key_field!(model) when is_atom(model) do
model.__schema__(:primary_key) ||
raise Ecto.NoPrimaryKeyError, model: model
end
end