epgsql_binary.erl 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. %%% Copyright (C) 2008 - Will Glozer. All rights reserved.
  2. -module(epgsql_binary).
  3. -export([new_codec/1,
  4. update_type_cache/2,
  5. type2oid/2, oid2type/2,
  6. encode/3, decode/3, supports/1]).
  7. -record(codec, {
  8. type2oid = [],
  9. oid2type = []
  10. }).
  11. -include("epgsql_binary.hrl").
  12. -define(datetime, (get(datetime_mod))).
  13. -define(INET, 2).
  14. -define(INET6, 3).
  15. -define(IP_SIZE, 4).
  16. -define(IP6_SIZE, 16).
  17. -define(MAX_IP_MASK, 32).
  18. -define(MAX_IP6_MASK, 128).
  19. -define(JSONB_VERSION_1, 1).
  20. new_codec([]) -> #codec{}.
  21. update_type_cache(TypeInfos, Codec) ->
  22. Type2Oid = lists:flatmap(
  23. fun({NameBin, ElementOid, ArrayOid}) ->
  24. Name = erlang:binary_to_atom(NameBin, utf8),
  25. [{Name, ElementOid}, {{array, Name}, ArrayOid}]
  26. end,
  27. TypeInfos),
  28. Oid2Type = [{Oid, Type} || {Type, Oid} <- Type2Oid],
  29. Codec#codec{type2oid = Type2Oid, oid2type = Oid2Type}.
  30. oid2type(Oid, #codec{oid2type = Oid2Type}) ->
  31. case epgsql_types:oid2type(Oid) of
  32. {unknown_oid, _} ->
  33. proplists:get_value(Oid, Oid2Type, {unknown_oid, Oid});
  34. Type -> Type
  35. end.
  36. type2oid(Type, #codec{type2oid = Type2Oid}) ->
  37. case epgsql_types:type2oid(Type) of
  38. {unknown_type, _} ->
  39. proplists:get_value(Type, Type2Oid, {unknown_type, Type});
  40. Oid -> Oid
  41. end.
  42. encode(_Any, null, _) -> <<-1:?int32>>;
  43. encode(_Any, undefined, _) -> <<-1:?int32>>;
  44. encode(bool, true, _) -> <<1:?int32, 1:1/big-signed-unit:8>>;
  45. encode(bool, false, _) -> <<1:?int32, 0:1/big-signed-unit:8>>;
  46. encode(int2, N, _) -> <<2:?int32, N:1/big-signed-unit:16>>;
  47. encode(int4, N, _) -> <<4:?int32, N:1/big-signed-unit:32>>;
  48. encode(int8, N, _) -> <<8:?int32, N:1/big-signed-unit:64>>;
  49. encode(float4, N, _) -> <<4:?int32, N:1/big-float-unit:32>>;
  50. encode(float8, N, _) -> <<8:?int32, N:1/big-float-unit:64>>;
  51. encode(bpchar, C, _) when is_integer(C) -> <<1:?int32, C:1/big-unsigned-unit:8>>;
  52. encode(bpchar, B, _) when is_binary(B) -> <<(byte_size(B)):?int32, B/binary>>;
  53. encode(time = Type, B, _) -> ?datetime:encode(Type, B);
  54. encode(timetz = Type, B, _) -> ?datetime:encode(Type, B);
  55. encode(date = Type, B, _) -> ?datetime:encode(Type, B);
  56. encode(timestamp = Type, B, _) -> ?datetime:encode(Type, B);
  57. encode(timestamptz = Type, B, _) -> ?datetime:encode(Type, B);
  58. encode(interval = Type, B, _) -> ?datetime:encode(Type, B);
  59. encode(bytea, B, _) when is_binary(B) -> <<(byte_size(B)):?int32, B/binary>>;
  60. encode(text, B, _) when is_binary(B) -> <<(byte_size(B)):?int32, B/binary>>;
  61. encode(varchar, B, _) when is_binary(B) -> <<(byte_size(B)):?int32, B/binary>>;
  62. encode(json, B, _) when is_binary(B) -> <<(byte_size(B)):?int32, B/binary>>;
  63. encode(jsonb, B, _) when is_binary(B) -> <<(byte_size(B) + 1):?int32, ?JSONB_VERSION_1:8, B/binary>>;
  64. encode(uuid, B, _) when is_binary(B) -> encode_uuid(B);
  65. encode({array, char}, L, Codec) when is_list(L) -> encode_array(bpchar, type2oid(bpchar, Codec), L, Codec);
  66. encode({array, Type}, L, Codec) when is_list(L) -> encode_array(Type, type2oid(Type, Codec), L, Codec);
  67. encode(hstore, {L}, _) when is_list(L) -> encode_hstore(L);
  68. encode(point, {X,Y}, _) -> encode_point({X,Y});
  69. encode(geometry, Data, _) -> encode_geometry(Data);
  70. encode(cidr, B, Codec) -> encode(bytea, encode_net(B), Codec);
  71. encode(inet, B, Codec) -> encode(bytea, encode_net(B), Codec);
  72. encode(int4range, R, _) when is_tuple(R) -> encode_int4range(R);
  73. encode(int8range, R, _) when is_tuple(R) -> encode_int8range(R);
  74. encode(Type, L, Codec) when is_list(L) -> encode(Type, list_to_binary(L), Codec);
  75. encode(_Type, _Value, _) -> {error, unsupported}.
  76. decode(bool, <<1:1/big-signed-unit:8>>, _) -> true;
  77. decode(bool, <<0:1/big-signed-unit:8>>, _) -> false;
  78. decode(bpchar, <<C:1/big-unsigned-unit:8>>, _) -> C;
  79. decode(int2, <<N:1/big-signed-unit:16>>, _) -> N;
  80. decode(int4, <<N:1/big-signed-unit:32>>, _) -> N;
  81. decode(int8, <<N:1/big-signed-unit:64>>, _) -> N;
  82. decode(float4, <<N:1/big-float-unit:32>>, _) -> N;
  83. decode(float8, <<N:1/big-float-unit:64>>, _) -> N;
  84. decode(record, <<_:?int32, Rest/binary>>, Codec) -> list_to_tuple(decode_record(Rest, [], Codec));
  85. decode(jsonb, <<?JSONB_VERSION_1:8, Value/binary>>, _) -> Value;
  86. decode(time = Type, B, _) -> ?datetime:decode(Type, B);
  87. decode(timetz = Type, B, _) -> ?datetime:decode(Type, B);
  88. decode(date = Type, B, _) -> ?datetime:decode(Type, B);
  89. decode(timestamp = Type, B, _) -> ?datetime:decode(Type, B);
  90. decode(timestamptz = Type, B, _) -> ?datetime:decode(Type, B);
  91. decode(interval = Type, B, _) -> ?datetime:decode(Type, B);
  92. decode(uuid, B, _) -> decode_uuid(B);
  93. decode(hstore, Hstore, _) -> decode_hstore(Hstore);
  94. decode(inet, B, _) -> decode_net(B);
  95. decode(cidr, B, _) -> decode_net(B);
  96. decode({array, _Type}, B, Codec) -> decode_array(B, Codec);
  97. decode(point, B, _) -> decode_point(B);
  98. decode(geometry, B, _) -> ewkb:decode_geometry(B);
  99. decode(int4range, B, _) -> decode_int4range(B);
  100. decode(int8range, B, _) -> decode_int8range(B);
  101. decode(_Other, Bin, _) -> Bin.
  102. encode_array(Type, Oid, A, Codec) ->
  103. {Data, {NDims, Lengths}} = encode_array(Type, A, 0, [], Codec),
  104. Lens = [<<N:?int32, 1:?int32>> || N <- lists:reverse(Lengths)],
  105. Hdr = <<NDims:?int32, 0:?int32, Oid:?int32>>,
  106. Bin = iolist_to_binary([Hdr, Lens, Data]),
  107. <<(byte_size(Bin)):?int32, Bin/binary>>.
  108. encode_array(_Type, [], NDims, Lengths, _Codec) ->
  109. {<<>>, {NDims, Lengths}};
  110. encode_array(Type, [H | _] = Array, NDims, Lengths, Codec) when not is_list(H) ->
  111. F = fun(E, Len) -> {encode(Type, E, Codec), Len + 1} end,
  112. {Data, Len} = lists:mapfoldl(F, 0, Array),
  113. {Data, {NDims + 1, [Len | Lengths]}};
  114. encode_array(uuid, [_H | _] = Array, NDims, Lengths, Codec) ->
  115. F = fun(E, Len) -> {encode(uuid, E, Codec), Len + 1} end,
  116. {Data, Len} = lists:mapfoldl(F, 0, Array),
  117. {Data, {NDims + 1, [Len | Lengths]}};
  118. encode_array(Type, Array, NDims, Lengths, Codec) ->
  119. Lengths2 = [length(Array) | Lengths],
  120. F = fun(A2, {_NDims, _Lengths}) -> encode_array(Type, A2, NDims, Lengths2, Codec) end,
  121. {Data, {NDims2, Lengths3}} = lists:mapfoldl(F, {NDims, Lengths2}, Array),
  122. {Data, {NDims2 + 1, Lengths3}}.
  123. encode_uuid(U) when is_binary(U) ->
  124. encode_uuid(binary_to_list(U));
  125. encode_uuid(U) ->
  126. Hex = [H || H <- U, H =/= $-],
  127. {ok, [Int], _} = io_lib:fread("~16u", Hex),
  128. <<16:?int32,Int:128>>.
  129. encode_hstore(HstoreEntries) ->
  130. Body = << <<(encode_hstore_entry(Entry))/binary>> || Entry <- HstoreEntries >>,
  131. <<(byte_size(Body) + 4):?int32, (length(HstoreEntries)):?int32, Body/binary>>.
  132. encode_hstore_entry({Key, Value}) ->
  133. <<(encode_hstore_key(Key))/binary, (encode_hstore_value(Value))/binary>>.
  134. encode_hstore_key(Key) -> encode_hstore_string(Key).
  135. encode_hstore_value(null) -> <<-1:?int32>>;
  136. encode_hstore_value(undefined) -> <<-1:?int32>>;
  137. encode_hstore_value(Val) -> encode_hstore_string(Val).
  138. encode_hstore_string(Str) when is_list(Str) -> encode_hstore_string(list_to_binary(Str));
  139. encode_hstore_string(Str) when is_atom(Str) -> encode_hstore_string(atom_to_binary(Str, utf8));
  140. encode_hstore_string(Str) when is_integer(Str) ->
  141. %% FIXME - we can use integer_to_binary when we deprecate R15
  142. encode_hstore_string(list_to_binary(integer_to_list(Str)));
  143. encode_hstore_string(Str) when is_float(Str) ->
  144. encode_hstore_string(iolist_to_binary(io_lib:format("~w", [Str])));
  145. encode_hstore_string(Str) when is_binary(Str) -> <<(byte_size(Str)):?int32, Str/binary>>.
  146. encode_net({{_, _, _, _} = IP, Mask}) ->
  147. Bin = list_to_binary(tuple_to_list(IP)),
  148. <<?INET, Mask, 1, ?IP_SIZE, Bin/binary>>;
  149. encode_net({{_, _, _, _, _, _, _, _} = IP, Mask}) ->
  150. Bin = << <<X:16>> || X <- tuple_to_list(IP) >>,
  151. <<?INET6, Mask, 1, ?IP6_SIZE, Bin/binary>>;
  152. encode_net({_, _, _, _} = IP) ->
  153. Bin = list_to_binary(tuple_to_list(IP)),
  154. <<?INET, ?MAX_IP_MASK, 0, ?IP_SIZE, Bin/binary>>;
  155. encode_net({_, _, _, _, _, _, _, _} = IP) ->
  156. Bin = << <<X:16>> || X <- tuple_to_list(IP) >>,
  157. <<?INET6, ?MAX_IP6_MASK, 0, ?IP6_SIZE, Bin/binary>>.
  158. decode_array(<<NDims:?int32, _HasNull:?int32, Oid:?int32, Rest/binary>>, Codec) ->
  159. {Dims, Data} = erlang:split_binary(Rest, NDims * 2 * 4),
  160. Lengths = [Len || <<Len:?int32, _LBound:?int32>> <= Dims],
  161. Type = oid2type(Oid, Codec),
  162. {Array, <<>>} = decode_array(Data, Type, Lengths, Codec),
  163. Array.
  164. decode_array(Data, _Type, [], _Codec) ->
  165. {[], Data};
  166. decode_array(Data, Type, [Len], Codec) ->
  167. decode_elements(Data, Type, [], Len, Codec);
  168. decode_array(Data, Type, [Len | T], Codec) ->
  169. F = fun(_N, Rest) -> decode_array(Rest, Type, T, Codec) end,
  170. lists:mapfoldl(F, Data, lists:seq(1, Len)).
  171. decode_elements(Rest, _Type, Acc, 0, _Codec) ->
  172. {lists:reverse(Acc), Rest};
  173. decode_elements(<<-1:?int32, Rest/binary>>, Type, Acc, N, Codec) ->
  174. decode_elements(Rest, Type, [null | Acc], N - 1, Codec);
  175. decode_elements(<<Len:?int32, Value:Len/binary, Rest/binary>>, Type, Acc, N, Codec) ->
  176. Value2 = decode(Type, Value, Codec),
  177. decode_elements(Rest, Type, [Value2 | Acc], N - 1, Codec).
  178. decode_record(<<>>, Acc, _Codec) ->
  179. lists:reverse(Acc);
  180. decode_record(<<_Type:?int32, -1:?int32, Rest/binary>>, Acc, Codec) ->
  181. decode_record(Rest, [null | Acc], Codec);
  182. decode_record(<<Type:?int32, Len:?int32, Value:Len/binary, Rest/binary>>, Acc, Codec) ->
  183. Value2 = decode(oid2type(Type, Codec), Value, Codec),
  184. decode_record(Rest, [Value2 | Acc], Codec).
  185. decode_uuid(<<U0:32, U1:16, U2:16, U3:16, U4:48>>) ->
  186. Format = "~8.16.0b-~4.16.0b-~4.16.0b-~4.16.0b-~12.16.0b",
  187. iolist_to_binary(io_lib:format(Format, [U0, U1, U2, U3, U4])).
  188. decode_hstore(<<NumElements:?int32, Elements/binary>>) ->
  189. {decode_hstore1(NumElements, Elements, [])}.
  190. decode_hstore1(0, _Elements, Acc) -> Acc;
  191. decode_hstore1(N, <<KeyLen:?int32, Key:KeyLen/binary, -1:?int32, Rest/binary>>, Acc) ->
  192. decode_hstore1(N - 1, Rest, [{Key, null} | Acc]);
  193. decode_hstore1(N, <<KeyLen:?int32, Key:KeyLen/binary, ValLen:?int32, Value:ValLen/binary, Rest/binary>>, Acc) ->
  194. decode_hstore1(N - 1, Rest, [{Key, Value} | Acc]).
  195. encode_point({X, Y}) when is_number(X), is_number(Y) ->
  196. <<X:1/big-float-unit:64, Y:1/big-float-unit:64>>.
  197. decode_point(<<X:1/big-float-unit:64, Y:1/big-float-unit:64>>) ->
  198. {X, Y}.
  199. encode_geometry(Data) ->
  200. Bin = ewkb:encode_geometry(Data),
  201. Size = byte_size(Bin),
  202. <<Size:?int32, Bin/binary>>.
  203. decode_net(<<?INET, Mask, 1, ?IP_SIZE, Bin/binary>>) ->
  204. {list_to_tuple(binary_to_list(Bin)), Mask};
  205. decode_net(<<?INET6, Mask, 1, ?IP6_SIZE, Bin/binary>>) ->
  206. {list_to_tuple([X || <<X:16>> <= Bin]), Mask};
  207. decode_net(<<?INET, ?MAX_IP_MASK, 0, ?IP_SIZE, Bin/binary>>) ->
  208. list_to_tuple(binary_to_list(Bin));
  209. decode_net(<<?INET6, ?MAX_IP6_MASK, 0, ?IP6_SIZE, Bin/binary>>) ->
  210. list_to_tuple([X || <<X:16>> <= Bin]).
  211. %% @doc encode an int4range
  212. encode_int4range({minus_infinity, plus_infinity}) ->
  213. <<1:?int32, 24:1/big-signed-unit:8>>;
  214. encode_int4range({From, plus_infinity}) ->
  215. FromInt = to_int(From),
  216. <<9:?int32, 18:1/big-signed-unit:8, 4:?int32, FromInt:?int32>>;
  217. encode_int4range({minus_infinity, To}) ->
  218. ToInt = to_int(To),
  219. <<9:?int32, 8:1/big-signed-unit:8, 4:?int32, ToInt:?int32>>;
  220. encode_int4range({From, To}) ->
  221. FromInt = to_int(From),
  222. ToInt = to_int(To),
  223. <<17:?int32, 2:1/big-signed-unit:8, 4:?int32, FromInt:?int32, 4:?int32, ToInt:?int32>>.
  224. %% @doc encode an int8range
  225. encode_int8range({minus_infinity, plus_infinity}) ->
  226. <<1:?int32, 24:1/big-signed-unit:8>>;
  227. encode_int8range({From, plus_infinity}) ->
  228. FromInt = to_int(From),
  229. <<13:?int32, 18:1/big-signed-unit:8, 8:?int32, FromInt:?int64>>;
  230. encode_int8range({minus_infinity, To}) ->
  231. ToInt = to_int(To),
  232. <<13:?int32, 8:1/big-signed-unit:8, 8:?int32, ToInt:?int64>>;
  233. encode_int8range({From, To}) ->
  234. FromInt = to_int(From),
  235. ToInt = to_int(To),
  236. <<25:?int32, 2:1/big-signed-unit:8, 8:?int32, FromInt:?int64, 8:?int32, ToInt:?int64>>.
  237. to_int(N) when is_integer(N) -> N;
  238. to_int(S) when is_list(S) -> erlang:list_to_integer(S);
  239. to_int(B) when is_binary(B) -> erlang:binary_to_integer(B).
  240. %% @doc decode an int4range
  241. decode_int4range(<<2:1/big-signed-unit:8, 4:?int32, From:?int32, 4:?int32, To:?int32>>) -> {From, To};
  242. decode_int4range(<<8:1/big-signed-unit:8, 4:?int32, To:?int32>>) -> {minus_infinity, To};
  243. decode_int4range(<<18:1/big-signed-unit:8, 4:?int32, From:?int32>>) -> {From, plus_infinity};
  244. decode_int4range(<<24:1/big-signed-unit:8>>) -> {minus_infinity, plus_infinity}.
  245. %% @doc decode an int8range
  246. decode_int8range(<<2:1/big-signed-unit:8, 8:?int32, From:?int64, 8:?int32, To:?int64>>) -> {From, To};
  247. decode_int8range(<<8:1/big-signed-unit:8, 8:?int32, To:?int64>>) -> {minus_infinity, To};
  248. decode_int8range(<<18:1/big-signed-unit:8, 8:?int32, From:?int64>>) -> {From, plus_infinity};
  249. decode_int8range(<<24:1/big-signed-unit:8>>) -> {minus_infinity, plus_infinity}.
  250. supports(bool) -> true;
  251. supports(bpchar) -> true;
  252. supports(int2) -> true;
  253. supports(int4) -> true;
  254. supports(int8) -> true;
  255. supports(float4) -> true;
  256. supports(float8) -> true;
  257. supports(bytea) -> true;
  258. supports(text) -> true;
  259. supports(varchar) -> true;
  260. supports(record) -> true;
  261. supports(date) -> true;
  262. supports(time) -> true;
  263. supports(timetz) -> true;
  264. supports(timestamp) -> true;
  265. supports(timestamptz) -> true;
  266. supports(interval) -> true;
  267. supports(uuid) -> true;
  268. supports(hstore) -> true;
  269. supports(cidr) -> true;
  270. supports(inet) -> true;
  271. supports(geometry) -> true;
  272. supports(point) -> true;
  273. supports(json) -> true;
  274. supports(jsonb) -> true;
  275. supports({array, bool}) -> true;
  276. supports({array, int2}) -> true;
  277. supports({array, int4}) -> true;
  278. supports({array, int8}) -> true;
  279. supports({array, float4}) -> true;
  280. supports({array, float8}) -> true;
  281. supports({array, char}) -> true;
  282. supports({array, text}) -> true;
  283. supports({array, date}) -> true;
  284. supports({array, time}) -> true;
  285. supports({array, timetz}) -> true;
  286. supports({array, timestamp}) -> true;
  287. supports({array, timestamptz}) -> true;
  288. supports({array, interval}) -> true;
  289. supports({array, hstore}) -> true;
  290. supports({array, varchar}) -> true;
  291. supports({array, uuid}) -> true;
  292. supports({array, cidr}) -> true;
  293. supports({array, inet}) -> true;
  294. supports({array, record}) -> true;
  295. supports({array, json}) -> true;
  296. supports({array, jsonb}) -> true;
  297. supports(int4range) -> true;
  298. supports(int8range) -> true;
  299. supports(_Type) -> false.