1%%% @doc 2%%% Codec for `hstore' type. 3%%% https://www.postgresql.org/docs/current/static/hstore.html 4%%% XXX: hstore not a part of postgresql builtin datatypes, it's in contrib. 5%%% It should be enabled in postgresql by command 6%%% `CREATE EXTENSION hstore` 7%%% $PG$/contrib/hstore/ 8%%% @end 9%%% Created : 14 Oct 2017 by Sergey Prokhorov <me@seriyps.ru> 10 11-module(epgsql_codec_hstore). 12-behaviour(epgsql_codec). 13 14-export([init/2, names/0, encode/3, decode/3, decode_text/3]). 15 16-include("protocol.hrl"). 17 18-export_type([data/0]). 19 20-type data() :: data_in() | data_out(). 21 22-type key_in() :: list() | binary() | atom() | integer() | float(). 23%% jiffy-style maps 24-type data_in() :: { [{key_in(), binary()}] }. 25-type data_out() :: { [{Key :: binary(), Value :: binary()}] }. 26 27-dialyzer([{nowarn_function, [encode/3]}, no_improper_lists]). 28 29%% TODO: option for output format: proplist | jiffy-object | map 30init(_, _) -> []. 31 32names() -> 33 [hstore]. 34 35encode({Hstore}, hstore, _) when is_list(Hstore) -> 36 Size = length(Hstore), 37 %% TODO: construct improper list when support for Erl 17 will be dropped 38 Body = [[encode_key(K), encode_value(V)] 39 || {K, V} <- Hstore], 40 [<<Size:?int32>> | Body]. 41 42decode(<<Size:?int32, Elements/binary>>, hstore, _) -> 43 {do_decode(Size, Elements)}. 44 45 46encode_key(K) -> 47 encode_string(K). 48 49encode_value(null) -> 50 <<-1:?int32>>; 51encode_value(undefined) -> 52 <<-1:?int32>>; 53encode_value(V) -> 54 encode_string(V). 55 56encode_string(Str) when is_binary(Str) -> 57 <<(byte_size(Str)):?int32, Str/binary>>; 58encode_string(Str) when is_list(Str) -> 59 encode_string(list_to_binary(Str)); 60encode_string(Str) when is_atom(Str) -> 61 encode_string(atom_to_binary(Str, utf8)); 62encode_string(Str) when is_integer(Str) -> 63 encode_string(integer_to_binary(Str)); 64encode_string(Str) when is_float(Str) -> 65 encode_string(io_lib:format("~w", [Str])). 66 %% encode_string(erlang:float_to_binary(Str)). 67 68 69do_decode(0, _) -> []; 70do_decode(N, <<KeyLen:?int32, Key:KeyLen/binary, -1:?int32, Rest/binary>>) -> 71 [{Key, null} | do_decode(N - 1, Rest)]; 72do_decode(N, <<KeyLen:?int32, Key:KeyLen/binary, 73 ValLen:?int32, Value:ValLen/binary, Rest/binary>>) -> 74 [{Key, Value} | do_decode(N - 1, Rest)]. 75 76decode_text(V, _, _) -> V. 77