queryable.ex (4870B)
1 defmodule Ecto.Adapter.Queryable do 2 @moduledoc """ 3 Specifies the query API required from adapters. 4 5 If your adapter is only able to respond to one or a couple of the query functions, 6 add custom implementations of those functions directly to the Repo 7 by using `c:Ecto.Adapter.__before_compile__/1` instead. 8 """ 9 10 @typedoc "Proxy type to the adapter meta" 11 @type adapter_meta :: Ecto.Adapter.adapter_meta() 12 13 @typedoc "Ecto.Query metadata fields (stored in cache)" 14 @type query_meta :: %{sources: tuple, preloads: term, select: map} 15 16 @typedoc """ 17 Cache query metadata that is passed to `c:execute/5`. 18 19 The cache can be in 3 states, documented below. 20 21 If `{:nocache, prepared}` is given, it means the query was 22 not and cannot be cached. The `prepared` value is the value 23 returned by `c:prepare/2`. 24 25 If `{:cache, cache_function, prepared}` is given, it means 26 the query can be cached and it must be cached by calling 27 the `cache_function` function with the cache entry of your 28 choice. Once `cache_function` is called, the next time the 29 same query is given to `c:execute/5`, it will receive the 30 `:cached` tuple. 31 32 If `{:cached, update_function, reset_function, cached}` is 33 given, it means the query has been cached. You may call 34 `update_function/1` if you want to update the cached result. 35 Or you may call `reset_function/1`, with a new prepared query, 36 to force the query to be cached again. If `reset_function/1` 37 is called, the next time the same query is given to 38 `c:execute/5`, it will receive the `:cache` tuple. 39 """ 40 @type query_cache :: {:nocache, prepared} 41 | {:cache, cache_function :: (cached -> :ok), prepared} 42 | {:cached, update_function :: (cached -> :ok), reset_function :: (prepared -> :ok), cached} 43 44 @type prepared :: term 45 @type cached :: term 46 @type options :: Keyword.t() 47 @type selected :: term 48 49 @doc """ 50 Commands invoked to prepare a query. 51 52 It is used on `c:Ecto.Repo.all/2`, `c:Ecto.Repo.update_all/3`, 53 and `c:Ecto.Repo.delete_all/2`. If returns a tuple, saying if 54 this query can be cached or not, and the `prepared` query. 55 The `prepared` query is any term that will be passed to the 56 adapter's `c:execute/5`. 57 """ 58 @callback prepare(atom :: :all | :update_all | :delete_all, query :: Ecto.Query.t()) :: 59 {:cache, prepared} | {:nocache, prepared} 60 61 @doc """ 62 Executes a previously prepared query. 63 64 The `query_meta` field is a map containing some of the fields 65 found in the `Ecto.Query` struct, after they have been normalized. 66 For example, the values `selected` by the query, which then have 67 to be returned, can be found in `query_meta`. 68 69 The `query_cache` and its state is documented in `t:query_cache/0`. 70 71 The `params` is the list of query parameters. For example, for 72 a query such as `from Post, where: [id: ^123]`, `params` will be 73 `[123]`. 74 75 Finally, `options` is a keyword list of options given to the 76 `Repo` operation that triggered the adapter call. Any option is 77 allowed, as this is a mechanism to allow users of Ecto to customize 78 how the adapter behaves per operation. 79 80 It must return a tuple containing the number of entries and 81 the result set as a list of lists. The entries in the actual 82 list will depend on what has been selected by the query. The 83 result set may also be `nil`, if no value is being selected. 84 """ 85 @callback execute(adapter_meta, query_meta, query_cache, params :: list(), options) :: 86 {non_neg_integer, [[selected]] | nil} 87 88 @doc """ 89 Streams a previously prepared query. 90 91 See `c:execute/5` for a description of arguments. 92 93 It returns a stream of values. 94 """ 95 @callback stream(adapter_meta, query_meta, query_cache, params :: list(), options) :: 96 Enumerable.t 97 98 @doc """ 99 Plans and prepares a query for the given repo, leveraging its query cache. 100 101 This operation uses the query cache if one is available. 102 """ 103 def prepare_query(operation, repo_name_or_pid, queryable) do 104 %{adapter: adapter, cache: cache} = Ecto.Repo.Registry.lookup(repo_name_or_pid) 105 106 {_meta, prepared, _cast_params, dump_params} = 107 queryable 108 |> Ecto.Queryable.to_query() 109 |> Ecto.Query.Planner.ensure_select(operation == :all) 110 |> Ecto.Query.Planner.query(operation, cache, adapter, 0) 111 112 {prepared, dump_params} 113 end 114 115 @doc """ 116 Plans a query using the given adapter. 117 118 This does not expect the repository and therefore does not leverage the cache. 119 """ 120 def plan_query(operation, adapter, queryable) do 121 query = Ecto.Queryable.to_query(queryable) 122 {query, params, _key} = Ecto.Query.Planner.plan(query, operation, adapter) 123 {cast_params, dump_params} = Enum.unzip(params) 124 {query, _} = Ecto.Query.Planner.normalize(query, operation, adapter, 0) 125 {query, cast_params, dump_params} 126 end 127 end