adapter.ex (5005B)
1 defmodule Ecto.Adapter do 2 @moduledoc """ 3 Specifies the minimal API required from adapters. 4 """ 5 6 @type t :: module 7 8 @typedoc """ 9 The metadata returned by the adapter `c:init/1`. 10 11 It must be a map and Ecto itself will always inject 12 two keys into the meta: 13 14 * the `:cache` key, which as ETS table that can be used as a cache (if available) 15 * the `:pid` key, which is the PID returned by the child spec returned in `c:init/1` 16 17 """ 18 @type adapter_meta :: %{optional(:stacktrace) => boolean(), optional(any()) => any()} 19 20 @doc """ 21 The callback invoked in case the adapter needs to inject code. 22 """ 23 @macrocallback __before_compile__(env :: Macro.Env.t()) :: Macro.t() 24 25 @doc """ 26 Ensure all applications necessary to run the adapter are started. 27 """ 28 @callback ensure_all_started(config :: Keyword.t(), type :: :permanent | :transient | :temporary) :: 29 {:ok, [atom]} | {:error, atom} 30 31 @doc """ 32 Initializes the adapter supervision tree by returning the children and adapter metadata. 33 """ 34 @callback init(config :: Keyword.t()) :: {:ok, :supervisor.child_spec(), adapter_meta} 35 36 @doc """ 37 Checks out a connection for the duration of the given function. 38 39 In case the adapter provides a pool, this guarantees all of the code 40 inside the given `fun` runs against the same connection, which 41 might improve performance by for instance allowing multiple related 42 calls to the datastore to share cache information: 43 44 Repo.checkout(fn -> 45 for _ <- 100 do 46 Repo.insert!(%Post{}) 47 end 48 end) 49 50 If the adapter does not provide a pool, just calling the passed function 51 and returning its result are enough. 52 53 If the adapter provides a pool, it is supposed to "check out" one of the 54 pool connections for the duration of the function call. Which connection 55 is checked out is not passed to the calling function, so it should be done 56 using a stateful method like using the current process' dictionary, process 57 tracking, or some kind of other lookup method. Make sure that this stored 58 connection is then used in the other callbacks implementations, such as 59 `Ecto.Adapter.Queryable` and `Ecto.Adapter.Schema`. 60 """ 61 @callback checkout(adapter_meta, config :: Keyword.t(), (() -> result)) :: result when result: var 62 63 @doc """ 64 Returns true if a connection has been checked out. 65 """ 66 @callback checked_out?(adapter_meta) :: boolean 67 68 @doc """ 69 Returns the loaders for a given type. 70 71 It receives the primitive type and the Ecto type (which may be 72 primitive as well). It returns a list of loaders with the given 73 type usually at the end. 74 75 This allows developers to properly translate values coming from 76 the adapters into Ecto ones. For example, if the database does not 77 support booleans but instead returns 0 and 1 for them, you could 78 add: 79 80 def loaders(:boolean, type), do: [&bool_decode/1, type] 81 def loaders(_primitive, type), do: [type] 82 83 defp bool_decode(0), do: {:ok, false} 84 defp bool_decode(1), do: {:ok, true} 85 86 All adapters are required to implement a clause for `:binary_id` types, 87 since they are adapter specific. If your adapter does not provide binary 88 ids, you may simply use `Ecto.UUID`: 89 90 def loaders(:binary_id, type), do: [Ecto.UUID, type] 91 def loaders(_primitive, type), do: [type] 92 93 """ 94 @callback loaders(primitive_type :: Ecto.Type.primitive(), ecto_type :: Ecto.Type.t()) :: 95 [(term -> {:ok, term} | :error) | Ecto.Type.t()] 96 97 @doc """ 98 Returns the dumpers for a given type. 99 100 It receives the primitive type and the Ecto type (which may be 101 primitive as well). It returns a list of dumpers with the given 102 type usually at the beginning. 103 104 This allows developers to properly translate values coming from 105 the Ecto into adapter ones. For example, if the database does not 106 support booleans but instead returns 0 and 1 for them, you could 107 add: 108 109 def dumpers(:boolean, type), do: [type, &bool_encode/1] 110 def dumpers(_primitive, type), do: [type] 111 112 defp bool_encode(false), do: {:ok, 0} 113 defp bool_encode(true), do: {:ok, 1} 114 115 All adapters are required to implement a clause for :binary_id types, 116 since they are adapter specific. If your adapter does not provide 117 binary ids, you may simply use `Ecto.UUID`: 118 119 def dumpers(:binary_id, type), do: [type, Ecto.UUID] 120 def dumpers(_primitive, type), do: [type] 121 122 """ 123 @callback dumpers(primitive_type :: Ecto.Type.primitive(), ecto_type :: Ecto.Type.t()) :: 124 [(term -> {:ok, term} | :error) | Ecto.Type.t()] 125 126 @doc """ 127 Returns the adapter metadata from its `c:init/1` callback. 128 129 It expects a process name of a repository. The name is either 130 an atom or a PID. For a given repository, you often want to 131 call this function based on the repository dynamic repo: 132 133 Ecto.Adapter.lookup_meta(repo.get_dynamic_repo()) 134 135 """ 136 def lookup_meta(repo_name_or_pid) do 137 Ecto.Repo.Registry.lookup(repo_name_or_pid) 138 end 139 end