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