pgsql_wire.erl 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. -module(pgsql_wire).
  2. -export([decode_message/1,
  3. decode_error/1,
  4. decode_strings/1,
  5. decode_columns/2,
  6. encode/1,
  7. encode/2,
  8. encode_types/1,
  9. format/1]).
  10. -include("pgsql.hrl").
  11. -include("pgsql_binary.hrl").
  12. decode_message(<<Type:8, Len:?int32, Rest/binary>> = Bin) ->
  13. Len2 = Len - 4,
  14. case Rest of
  15. <<Data:Len2/binary, Tail/binary>> ->
  16. case Type of
  17. $E ->
  18. {{error, decode_error(Data)}, Tail};
  19. _ ->
  20. {{Type, Data}, Tail}
  21. end;
  22. _Other ->
  23. Bin
  24. end;
  25. decode_message(Bin) ->
  26. Bin.
  27. %% decode a single null-terminated string
  28. %% TODO signature changed, returns [Str, Rest], old code expects {Str, Rest}
  29. decode_string(Bin) ->
  30. binary:split(Bin, <<0>>).
  31. %% decode multiple null-terminated string
  32. decode_strings(Bin) ->
  33. [<<>> | T] = lists:reverse(binary:split(Bin, <<0>>, [global])),
  34. lists:reverse(T).
  35. %% decode field
  36. decode_fields(Bin) ->
  37. decode_fields(Bin, []).
  38. decode_fields(<<0>>, Acc) ->
  39. Acc;
  40. decode_fields(<<Type:8, Rest/binary>>, Acc) ->
  41. [Str, Rest2] = decode_string(Rest),
  42. decode_fields(Rest2, [{Type, Str} | Acc]).
  43. %% decode ErrorResponse
  44. %% TODO add fields from http://www.postgresql.org/docs/9.0/interactive/protocol-error-fields.html
  45. decode_error(Bin) ->
  46. Fields = decode_fields(Bin),
  47. Error = #error{
  48. severity = lower_atom(proplists:get_value($S, Fields)),
  49. code = proplists:get_value($C, Fields),
  50. message = proplists:get_value($M, Fields),
  51. extra = decode_error_extra(Fields)},
  52. Error.
  53. decode_error_extra(Fields) ->
  54. Types = [{$D, detail}, {$H, hint}, {$P, position}],
  55. decode_error_extra(Types, Fields, []).
  56. decode_error_extra([], _Fields, Extra) ->
  57. Extra;
  58. decode_error_extra([{Type, Name} | T], Fields, Extra) ->
  59. case proplists:get_value(Type, Fields) of
  60. undefined -> decode_error_extra(T, Fields, Extra);
  61. Value -> decode_error_extra(T, Fields, [{Name, Value} | Extra])
  62. end.
  63. lower_atom(Str) when is_binary(Str) ->
  64. lower_atom(binary_to_list(Str));
  65. lower_atom(Str) when is_list(Str) ->
  66. list_to_atom(string:to_lower(Str)).
  67. encode(Data) ->
  68. Bin = iolist_to_binary(Data),
  69. <<(byte_size(Bin) + 4):?int32, Bin/binary>>.
  70. encode(Type, Data) ->
  71. Bin = iolist_to_binary(Data),
  72. <<Type:8, (byte_size(Bin) + 4):?int32, Bin/binary>>.
  73. %% decode data
  74. decode_data(Columns, Bin) ->
  75. decode_data(Columns, Bin, []).
  76. decode_data([], _Bin, Acc) ->
  77. list_to_tuple(lists:reverse(Acc));
  78. decode_data([_C | T], <<-1:?int32, Rest/binary>>, Acc) ->
  79. decode_data(T, Rest, [null | Acc]);
  80. decode_data([C | T], <<Len:?int32, Value:Len/binary, Rest/binary>>, Acc) ->
  81. case C of
  82. #column{type = Type, format = 1} -> Value2 = pgsql_binary:decode(Type, Value);
  83. #column{} -> Value2 = Value
  84. end,
  85. decode_data(T, Rest, [Value2 | Acc]).
  86. %% decode column information
  87. decode_columns(Count, Bin) ->
  88. decode_columns(Count, Bin, []).
  89. decode_columns(0, _Bin, Acc) ->
  90. lists:reverse(Acc);
  91. decode_columns(N, Bin, Acc) ->
  92. {Name, Rest} = decode_string(Bin),
  93. <<_Table_Oid:?int32, _Attrib_Num:?int16, Type_Oid:?int32,
  94. Size:?int16, Modifier:?int32, Format:?int16, Rest2/binary>> = Rest,
  95. Desc = #column{
  96. name = Name,
  97. type = pgsql_types:oid2type(Type_Oid),
  98. size = Size,
  99. modifier = Modifier,
  100. format = Format},
  101. decode_columns(N - 1, Rest2, [Desc | Acc]).
  102. %% decode command complete msg
  103. decode_complete(<<"SELECT", 0>>) -> select;
  104. decode_complete(<<"SELECT", _/binary>>) -> select;
  105. decode_complete(<<"BEGIN", 0>>) -> 'begin';
  106. decode_complete(<<"ROLLBACK", 0>>) -> rollback;
  107. decode_complete(Bin) ->
  108. {Str, _} = decode_string(Bin),
  109. case string:tokens(binary_to_list(Str), " ") of
  110. ["INSERT", _Oid, Rows] -> {insert, list_to_integer(Rows)};
  111. ["UPDATE", Rows] -> {update, list_to_integer(Rows)};
  112. ["DELETE", Rows] -> {delete, list_to_integer(Rows)};
  113. ["MOVE", Rows] -> {move, list_to_integer(Rows)};
  114. ["FETCH", Rows] -> {fetch, list_to_integer(Rows)};
  115. [Type | _Rest] -> pgsql_sock:lower_atom(Type)
  116. end.
  117. %% encode types
  118. encode_types(Types) ->
  119. encode_types(Types, 0, <<>>).
  120. encode_types([], Count, Acc) ->
  121. <<Count:?int16, Acc/binary>>;
  122. encode_types([Type | T], Count, Acc) ->
  123. case Type of
  124. undefined -> Oid = 0;
  125. _Any -> Oid = pgsql_types:type2oid(Type)
  126. end,
  127. encode_types(T, Count + 1, <<Acc/binary, Oid:?int32>>).
  128. %% encode column formats
  129. encode_formats(Columns) ->
  130. encode_formats(Columns, 0, <<>>).
  131. encode_formats([], Count, Acc) ->
  132. <<Count:?int16, Acc/binary>>;
  133. encode_formats([#column{format = Format} | T], Count, Acc) ->
  134. encode_formats(T, Count + 1, <<Acc/binary, Format:?int16>>).
  135. format(Type) ->
  136. case pgsql_binary:supports(Type) of
  137. true -> 1;
  138. false -> 0
  139. end.
  140. %% encode parameters
  141. encode_parameters(Parameters) ->
  142. encode_parameters(Parameters, 0, <<>>, <<>>).
  143. encode_parameters([], Count, Formats, Values) ->
  144. <<Count:?int16, Formats/binary, Count:?int16, Values/binary>>;
  145. encode_parameters([P | T], Count, Formats, Values) ->
  146. {Format, Value} = encode_parameter(P),
  147. Formats2 = <<Formats/binary, Format:?int16>>,
  148. Values2 = <<Values/binary, Value/binary>>,
  149. encode_parameters(T, Count + 1, Formats2, Values2).
  150. %% encode parameter
  151. encode_parameter({Type, Value}) ->
  152. case pgsql_binary:encode(Type, Value) of
  153. Bin when is_binary(Bin) -> {1, Bin};
  154. {error, unsupported} -> encode_parameter(Value)
  155. end;
  156. encode_parameter(A) when is_atom(A) -> {0, encode_list(atom_to_list(A))};
  157. encode_parameter(B) when is_binary(B) -> {0, <<(byte_size(B)):?int32, B/binary>>};
  158. encode_parameter(I) when is_integer(I) -> {0, encode_list(integer_to_list(I))};
  159. encode_parameter(F) when is_float(F) -> {0, encode_list(float_to_list(F))};
  160. encode_parameter(L) when is_list(L) -> {0, encode_list(L)}.
  161. encode_list(L) ->
  162. Bin = list_to_binary(L),
  163. <<(byte_size(Bin)):?int32, Bin/binary>>.