pgsql_binary.erl 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. %%% Copyright (C) 2008 - Will Glozer. All rights reserved.
  2. -module(pgsql_binary).
  3. -export([encode/2, decode/2, supports/1]).
  4. -include("pgsql_binary.hrl").
  5. -define(datetime, (get(datetime_mod))).
  6. encode(_Any, null) -> <<-1:?int32>>;
  7. encode(bool, true) -> <<1:?int32, 1:1/big-signed-unit:8>>;
  8. encode(bool, false) -> <<1:?int32, 0:1/big-signed-unit:8>>;
  9. encode(int2, N) -> <<2:?int32, N:1/big-signed-unit:16>>;
  10. encode(int4, N) -> <<4:?int32, N:1/big-signed-unit:32>>;
  11. encode(int8, N) -> <<8:?int32, N:1/big-signed-unit:64>>;
  12. encode(float4, N) -> <<4:?int32, N:1/big-float-unit:32>>;
  13. encode(float8, N) -> <<8:?int32, N:1/big-float-unit:64>>;
  14. encode(bpchar, C) when is_integer(C) -> <<1:?int32, C:1/big-unsigned-unit:8>>;
  15. encode(bpchar, B) when is_binary(B) -> <<(byte_size(B)):?int32, B/binary>>;
  16. encode(time = Type, B) -> ?datetime:encode(Type, B);
  17. encode(timetz = Type, B) -> ?datetime:encode(Type, B);
  18. encode(date = Type, B) -> ?datetime:encode(Type, B);
  19. encode(timestamp = Type, B) -> ?datetime:encode(Type, B);
  20. encode(timestamptz = Type, B) -> ?datetime:encode(Type, B);
  21. encode(interval = Type, B) -> ?datetime:encode(Type, B);
  22. encode(bytea, B) when is_binary(B) -> <<(byte_size(B)):?int32, B/binary>>;
  23. encode(text, B) when is_binary(B) -> <<(byte_size(B)):?int32, B/binary>>;
  24. encode(varchar, B) when is_binary(B) -> <<(byte_size(B)):?int32, B/binary>>;
  25. encode(uuid, B) when is_binary(B) -> encode_uuid(B);
  26. encode(boolarray, L) when is_list(L) -> encode_array(bool, L);
  27. encode(int2array, L) when is_list(L) -> encode_array(int2, L);
  28. encode(int4array, L) when is_list(L) -> encode_array(int4, L);
  29. encode(int8array, L) when is_list(L) -> encode_array(int8, L);
  30. encode(float4array, L) when is_list(L) -> encode_array(float4, L);
  31. encode(float8array, L) when is_list(L) -> encode_array(float8, L);
  32. encode(chararray, L) when is_list(L) -> encode_array(bpchar, L);
  33. encode(textarray, L) when is_list(L) -> encode_array(text, L);
  34. encode(datearray, L) when is_list(L) -> encode_array(date, L);
  35. encode(timearray, L) when is_list(L) -> encode_array(time, L);
  36. encode(timetzarray, L) when is_list(L) -> encode_array(timetz, L);
  37. encode(timestamparray, L) when is_list(L) -> encode_array(timestamp, L);
  38. encode(timestamptzarray, L) when is_list(L) -> encode_array(timestamptz, L);
  39. encode(intervalarray, L) when is_list(L) -> encode_array(interval, L);
  40. encode(uuidarray, L) when is_list(L) -> encode_array(uuid, L);
  41. encode(Type, L) when is_list(L) -> encode(Type, list_to_binary(L));
  42. encode(_Type, _Value) -> {error, unsupported}.
  43. decode(bool, <<1:1/big-signed-unit:8>>) -> true;
  44. decode(bool, <<0:1/big-signed-unit:8>>) -> false;
  45. decode(bpchar, <<C:1/big-unsigned-unit:8>>) -> C;
  46. decode(int2, <<N:1/big-signed-unit:16>>) -> N;
  47. decode(int4, <<N:1/big-signed-unit:32>>) -> N;
  48. decode(int8, <<N:1/big-signed-unit:64>>) -> N;
  49. decode(float4, <<N:1/big-float-unit:32>>) -> N;
  50. decode(float8, <<N:1/big-float-unit:64>>) -> N;
  51. decode(record, <<_:?int32, Rest/binary>>) -> list_to_tuple(decode_record(Rest, []));
  52. decode(time = Type, B) -> ?datetime:decode(Type, B);
  53. decode(timetz = Type, B) -> ?datetime:decode(Type, B);
  54. decode(date = Type, B) -> ?datetime:decode(Type, B);
  55. decode(timestamp = Type, B) -> ?datetime:decode(Type, B);
  56. decode(timestamptz = Type, B) -> ?datetime:decode(Type, B);
  57. decode(interval = Type, B) -> ?datetime:decode(Type, B);
  58. decode(uuid, B) -> decode_uuid(B);
  59. decode(boolarray, B) -> decode_array(B);
  60. decode(int2array, B) -> decode_array(B);
  61. decode(int4array, B) -> decode_array(B);
  62. decode(int8array, B) -> decode_array(B);
  63. decode(float4array, B) -> decode_array(B);
  64. decode(float8array, B) -> decode_array(B);
  65. decode(chararray, B) -> decode_array(B);
  66. decode(textarray, B) -> decode_array(B);
  67. decode(datearray, B) -> decode_array(B);
  68. decode(timearray, B) -> decode_array(B);
  69. decode(timetzarray, B) -> decode_array(B);
  70. decode(timestamparray, B) -> decode_array(B);
  71. decode(timestamptzarray, B) -> decode_array(B);
  72. decode(intervalarray, B) -> decode_array(B);
  73. decode(uuidarray, B) -> decode_array(B);
  74. decode(_Other, Bin) -> Bin.
  75. encode_array(Type, A) ->
  76. {Data, {NDims, Lengths}} = encode_array(Type, A, 0, []),
  77. Oid = pgsql_types:type2oid(Type),
  78. Lens = [<<N:?int32, 1:?int32>> || N <- lists:reverse(Lengths)],
  79. Hdr = <<NDims:?int32, 0:?int32, Oid:?int32>>,
  80. Bin = iolist_to_binary([Hdr, Lens, Data]),
  81. <<(byte_size(Bin)):?int32, Bin/binary>>.
  82. encode_array(_Type, [], NDims, Lengths) ->
  83. {<<>>, {NDims, Lengths}};
  84. encode_array(Type, [H | _] = Array, NDims, Lengths) when not is_list(H) ->
  85. F = fun(E, Len) -> {encode(Type, E), Len + 1} end,
  86. {Data, Len} = lists:mapfoldl(F, 0, Array),
  87. {Data, {NDims + 1, [Len | Lengths]}};
  88. encode_array(uuid, [H | _] = Array, NDims, Lengths) ->
  89. F = fun(E, Len) -> {encode(uuid, E), Len + 1} end,
  90. {Data, Len} = lists:mapfoldl(F, 0, Array),
  91. {Data, {NDims + 1, [Len | Lengths]}};
  92. encode_array(Type, Array, NDims, Lengths) ->
  93. Lengths2 = [length(Array) | Lengths],
  94. F = fun(A2, {_NDims, _Lengths}) -> encode_array(Type, A2, NDims, Lengths2) end,
  95. {Data, {NDims2, Lengths3}} = lists:mapfoldl(F, {NDims, Lengths2}, Array),
  96. {Data, {NDims2 + 1, Lengths3}}.
  97. encode_uuid(U) when is_binary(U) ->
  98. encode_uuid(binary_to_list(U));
  99. encode_uuid(U) ->
  100. Hex = [H || H <- U, H =/= $-],
  101. {ok, [Int], _} = io_lib:fread("~16u", Hex),
  102. <<16:?int32,Int:128>>.
  103. decode_array(<<NDims:?int32, _HasNull:?int32, Oid:?int32, Rest/binary>>) ->
  104. {Dims, Data} = erlang:split_binary(Rest, NDims * 2 * 4),
  105. Lengths = [Len || <<Len:?int32, _LBound:?int32>> <= Dims],
  106. Type = pgsql_types:oid2type(Oid),
  107. {Array, <<>>} = decode_array(Data, Type, Lengths),
  108. Array.
  109. decode_array(Data, _Type, []) ->
  110. {[], Data};
  111. decode_array(Data, Type, [Len]) ->
  112. decode_elements(Data, Type, [], Len);
  113. decode_array(Data, Type, [Len | T]) ->
  114. F = fun(_N, Rest) -> decode_array(Rest, Type, T) end,
  115. lists:mapfoldl(F, Data, lists:seq(1, Len)).
  116. decode_elements(Rest, _Type, Acc, 0) ->
  117. {lists:reverse(Acc), Rest};
  118. decode_elements(<<-1:?int32, Rest/binary>>, Type, Acc, N) ->
  119. decode_elements(Rest, Type, [null | Acc], N - 1);
  120. decode_elements(<<Len:?int32, Value:Len/binary, Rest/binary>>, Type, Acc, N) ->
  121. Value2 = decode(Type, Value),
  122. decode_elements(Rest, Type, [Value2 | Acc], N - 1).
  123. decode_record(<<>>, Acc) ->
  124. lists:reverse(Acc);
  125. decode_record(<<_Type:?int32, -1:?int32, Rest/binary>>, Acc) ->
  126. decode_record(Rest, [null | Acc]);
  127. decode_record(<<Type:?int32, Len:?int32, Value:Len/binary, Rest/binary>>, Acc) ->
  128. Value2 = decode(pgsql_types:oid2type(Type), Value),
  129. decode_record(Rest, [Value2 | Acc]).
  130. decode_uuid(<<U0:32, U1:16, U2:16, U3:16, U4:48>>) ->
  131. Format = "~8.16.0b-~4.16.0b-~4.16.0b-~4.16.0b-~12.16.0b",
  132. iolist_to_binary(io_lib:format(Format, [U0, U1, U2, U3, U4])).
  133. supports(bool) -> true;
  134. supports(bpchar) -> true;
  135. supports(int2) -> true;
  136. supports(int4) -> true;
  137. supports(int8) -> true;
  138. supports(float4) -> true;
  139. supports(float8) -> true;
  140. supports(bytea) -> true;
  141. supports(text) -> true;
  142. supports(varchar) -> true;
  143. supports(record) -> true;
  144. supports(date) -> true;
  145. supports(time) -> true;
  146. supports(timetz) -> true;
  147. supports(timestamp) -> true;
  148. supports(timestamptz) -> true;
  149. supports(interval) -> true;
  150. supports(uuid) -> true;
  151. supports(boolarray) -> true;
  152. supports(int2array) -> true;
  153. supports(int4array) -> true;
  154. supports(int8array) -> true;
  155. supports(float4array) -> true;
  156. supports(float8array) -> true;
  157. supports(chararray) -> true;
  158. supports(textarray) -> true;
  159. supports(datearray) -> true;
  160. supports(timearray) -> true;
  161. supports(timetzarray) -> true;
  162. supports(timestamparray) -> true;
  163. supports(timestamptzarray) -> true;
  164. supports(intervalarray) -> true;
  165. supports(varchararray) -> true;
  166. supports(uuidarray) -> true;
  167. supports(_Type) -> false.