Packages
ecto
1.0.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/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.Planner
require Ecto.Query
@doc """
Implementation for `Ecto.Repo.all/2`
"""
def all(repo, adapter, queryable, opts) when is_list(opts) do
execute(:all, repo, adapter, queryable, opts) |> elem(1)
end
@doc """
Implementation for `Ecto.Repo.get/3`
"""
def get(repo, adapter, queryable, id, opts) do
one(repo, adapter, query_for_get(repo, 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(repo, queryable, id), opts)
end
def get_by(repo, adapter, queryable, clauses, opts) do
one(repo, adapter, query_for_get_by(repo, queryable, clauses), opts)
end
def get_by!(repo, adapter, queryable, clauses, opts) do
one!(repo, adapter, query_for_get_by(repo, queryable, clauses), 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 """
Runtime callback for `Ecto.Repo.update_all/3`
"""
def update_all(repo, adapter, queryable, [], opts) when is_list(opts) do
update_all(repo, adapter, queryable, opts)
end
def update_all(repo, adapter, queryable, updates, opts) when is_list(opts) do
query = Ecto.Query.from q in queryable, update: ^updates
update_all(repo, adapter, query, opts)
end
defp update_all(repo, adapter, queryable, opts) do
execute(:update_all, repo, adapter, queryable, opts)
end
@doc """
Implementation for `Ecto.Repo.delete_all/2`
"""
def delete_all(repo, adapter, queryable, opts) when is_list(opts) do
execute(:delete_all, repo, adapter, queryable, opts)
end
## Helpers
def execute(operation, repo, adapter, queryable, opts) when is_list(opts) do
{meta, prepared, params} =
queryable
|> Queryable.to_query()
|> Planner.query(operation, repo, adapter)
if meta.select do
preprocess = preprocess(meta.prefix, meta.sources, adapter)
{count, rows} = adapter.execute(repo, meta, prepared, params, preprocess, opts)
{count,
rows
|> Ecto.Repo.Assoc.query(meta.assocs, meta.sources)
|> Ecto.Repo.Preloader.query(repo, meta.preloads, meta.assocs, postprocess(meta.select))}
else
adapter.execute(repo, meta, prepared, params, nil, opts)
end
end
defp preprocess(prefix, sources, adapter) do
&preprocess(&1, &2, prefix, &3, sources, adapter)
end
defp preprocess({:&, _, [ix]}, value, prefix, context, sources, adapter) do
{source, model} = elem(sources, ix)
Ecto.Schema.__load__(model, prefix, source, context, value, &adapter.load/2)
end
defp preprocess({{:., _, [{:&, _, [_]}, _]}, meta, []}, value, _prefix, _context, _sources, adapter) do
case Keyword.fetch(meta, :ecto_type) do
{:ok, type} -> load!(type, value, adapter)
:error -> value
end
end
defp preprocess(%Ecto.Query.Tagged{tag: tag}, value, _prefix, _context, _sources, adapter) do
load!(tag, value, adapter)
end
defp preprocess(_key, value, _prefix, _context, _sources, _adapter) do
value
end
defp load!(type, value, adapter) do
case adapter.load(type, value) do
{:ok, value} -> value
:error -> raise ArgumentError, "cannot load `#{inspect value}` as type #{inspect type}"
end
end
defp postprocess(%{expr: expr, fields: fields}) do
# 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]}|_], fields)
&postprocess(&1, expr, from?)
end
defp postprocess(row, expr, true),
do: transform_row(expr, hd(row), tl(row)) |> elem(0)
defp postprocess(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({:%{}, _, pairs}, from, values) do
Enum.reduce pairs, {%{}, values}, fn({key, value}, {map, values_acc}) ->
{value, new_values} = transform_row(value, from, values_acc)
{Map.put(map, key, value), new_values}
end
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({:&, _, [0]}, from, values) do
{from, values}
end
defp transform_row(_, _from, values) do
[value|values] = values
{value, values}
end
defp query_for_get(repo, _queryable, nil) do
raise ArgumentError, "cannot perform #{inspect repo}.get/2 because the given value is nil"
end
defp query_for_get(_repo, 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 query_for_get_by(repo, queryable, clauses) do
Enum.reduce(clauses, queryable, fn
{field, nil}, _query ->
raise ArgumentError, "cannot perform #{inspect repo}.get_by/2 because #{inspect field} is nil"
{field, value}, query ->
query |> Ecto.Query.where([x], field(x, ^field) == ^value)
end)
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 primary_key_field!(model) when is_atom(model) do
case model.__schema__(:primary_key) do
[field] -> field
_ -> raise Ecto.NoPrimaryKeyFieldError, model: model
end
end
end