hstore.ex (2723B)
1 defmodule Postgrex.Extensions.HStore do 2 @moduledoc false 3 import Postgrex.BinaryUtils, warn: false 4 use Postgrex.BinaryExtension, type: "hstore" 5 6 def init(opts), do: Keyword.fetch!(opts, :decode_binary) 7 8 def encode(_) do 9 quote location: :keep do 10 %{} = map -> 11 data = unquote(__MODULE__).encode_hstore(map) 12 [<<IO.iodata_length(data)::int32()>> | data] 13 14 other -> 15 raise DBConnection.EncodeError, Postgrex.Utils.encode_msg(other, "a map") 16 end 17 end 18 19 def decode(mode) do 20 quote do 21 <<len::int32(), data::binary-size(len)>> -> 22 unquote(__MODULE__).decode_hstore(data, unquote(mode)) 23 end 24 end 25 26 ## Helpers 27 28 def encode_hstore(hstore_map) do 29 keys_and_values = 30 Enum.reduce(hstore_map, "", fn {key, value}, acc -> 31 [acc, encode_hstore_key(key), encode_hstore_value(value)] 32 end) 33 34 [<<map_size(hstore_map)::int32()>> | keys_and_values] 35 end 36 37 defp encode_hstore_key(key) when is_binary(key) do 38 encode_hstore_value(key) 39 end 40 41 defp encode_hstore_key(key) when is_nil(key) do 42 raise ArgumentError, "hstore keys cannot be nil!" 43 end 44 45 defp encode_hstore_value(nil) do 46 <<-1::int32()>> 47 end 48 49 defp encode_hstore_value(value) when is_binary(value) do 50 value_byte_size = byte_size(value) 51 <<value_byte_size::int32()>> <> value 52 end 53 54 def decode_hstore(<<_length::int32(), pairs::binary>>, :reference) do 55 decode_hstore_ref(pairs, %{}) 56 end 57 58 def decode_hstore(<<_length::int32(), pairs::binary>>, :copy) do 59 decode_hstore_copy(pairs, %{}) 60 end 61 62 defp decode_hstore_ref(<<>>, acc) do 63 acc 64 end 65 66 # in the case of a NULL value, there won't be a length 67 defp decode_hstore_ref( 68 <<key_length::int32(), key::binary(key_length), -1::int32(), rest::binary>>, 69 acc 70 ) do 71 decode_hstore_ref(rest, Map.put(acc, key, nil)) 72 end 73 74 defp decode_hstore_ref( 75 <<key_length::int32(), key::binary(key_length), value_length::int32(), 76 value::binary(value_length), rest::binary>>, 77 acc 78 ) do 79 decode_hstore_ref(rest, Map.put(acc, key, value)) 80 end 81 82 defp decode_hstore_copy(<<>>, acc) do 83 acc 84 end 85 86 # in the case of a NULL value, there won't be a length 87 defp decode_hstore_copy( 88 <<key_length::int32(), key::binary(key_length), -1::int32(), rest::binary>>, 89 acc 90 ) do 91 decode_hstore_copy(rest, Map.put(acc, :binary.copy(key), nil)) 92 end 93 94 defp decode_hstore_copy( 95 <<key_length::int32(), key::binary(key_length), value_length::int32(), 96 value::binary(value_length), rest::binary>>, 97 acc 98 ) do 99 decode_hstore_copy(rest, Map.put(acc, :binary.copy(key), :binary.copy(value))) 100 end 101 end