extension.ex (4440B)
1 defmodule Postgrex.Extension do 2 @moduledoc """ 3 An extension knows how to encode and decode PostgreSQL types to and 4 from Elixir values. 5 6 Custom extensions can be enabled via `Postgrex.Types.define/3`. 7 `Postgrex.Types.define/3` must be called on its own file, outside of 8 any module and function, as it only needs to be defined once during 9 compilation. 10 11 For example to support label trees using the text encoding format: 12 13 defmodule MyApp.LTree do 14 @behaviour Postgrex.Extension 15 16 # It can be memory efficient to copy the decoded binary because a 17 # reference counted binary that points to a larger binary will be passed 18 # to the decode/4 callback. Copying the binary can allow the larger 19 # binary to be garbage collected sooner if the copy is going to be kept 20 # for a longer period of time. See `:binary.copy/1` for more 21 # information. 22 def init(opts) do 23 Keyword.get(opts, :decode_copy, :copy) 24 end 25 26 # Use this extension when `type` from %Postgrex.TypeInfo{} is "ltree" 27 def matching(_state), do: [type: "ltree"] 28 29 # Use the text format, "ltree" does not have a binary format. 30 def format(_state), do: :text 31 32 # Use quoted expression to encode a string that is the same as 33 # postgresql's ltree text format. The quoted expression should contain 34 # clauses that match those of a `case` or `fn`. Encoding matches on the 35 # value and returns encoded `iodata()`. The first 4 bytes in the 36 # `iodata()` must be the byte size of the rest of the encoded data, as a 37 # signed 32bit big endian integer. 38 def encode(_state) do 39 quote do 40 bin when is_binary(bin) -> 41 [<<byte_size(bin) :: signed-size(32)>> | bin] 42 end 43 end 44 45 # Use quoted expression to decode the data to a string. Decoding matches 46 # on an encoded binary with the same signed 32bit big endian integer 47 # length header. 48 def decode(:reference) do 49 quote do 50 <<len::signed-size(32), bin::binary-size(len)>> -> 51 bin 52 end 53 end 54 def decode(:copy) do 55 quote do 56 <<len::signed-size(32), bin::binary-size(len)>> -> 57 :binary.copy(bin) 58 end 59 end 60 end 61 62 This example could be used in a custom types module: 63 64 Postgrex.Types.define(MyApp.Types, [MyApp.LTree]) 65 66 Or pass in opts for the extension that will be passed to the `init/1` callback: 67 68 Postgrex.Types.define(MyApp.Types, [{MyApp.LTree, [decode_copy: :copy]}]) 69 70 """ 71 72 @type t :: module 73 @type state :: term 74 75 @doc """ 76 Should perform any initialization of the extension. The function receives the 77 user options. The state returned from this function will be passed to other 78 callbacks. 79 """ 80 @callback init(Keyword.t()) :: state 81 82 @doc """ 83 Prelude defines properties and values that are attached to the body of 84 the types module. 85 """ 86 @callback prelude(state) :: Macro.t() 87 88 @doc """ 89 Specifies the types the extension matches, see `Postgrex.TypeInfo` for 90 specification of the fields. 91 """ 92 @callback matching(state) :: [ 93 type: String.t(), 94 send: String.t(), 95 receive: String.t(), 96 input: String.t(), 97 output: String.t() 98 ] 99 100 @doc """ 101 Returns the format the type should be encoded as. See 102 http://www.postgresql.org/docs/9.4/static/protocol-overview.html#PROTOCOL-FORMAT-CODES. 103 """ 104 @callback format(state) :: :binary | :text 105 106 @doc """ 107 Returns a quoted list of clauses that encode an Elixir value to iodata. 108 109 It must use a signed 32 bit big endian integer byte length header. 110 111 def encode(_) do 112 quote do 113 integer -> 114 <<8 :: signed-32, integer :: signed-64>> 115 end 116 end 117 118 """ 119 @callback encode(state) :: Macro.t() 120 121 @doc """ 122 Returns a quoted list of clauses that decode a binary to an Elixir value. 123 124 The pattern must use binary syntax and decode a fixed length using the signed 125 32 bit big endian integer byte length header. 126 127 def decode(_) do 128 quote do 129 # length header is in bytes 130 <<len :: signed-32, integer :: signed-size(len)-unit(8)>> -> 131 integer 132 end 133 end 134 """ 135 @callback decode(state) :: Macro.t() 136 137 @optional_callbacks [prelude: 1] 138 end