epgsql_cmd_squery.erl 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. %% @doc Executes SQL query(es) using
  2. %% <a href="https://www.postgresql.org/docs/current/protocol-flow.html#id-1.10.5.7.4">
  3. %% simple query protocol</a>
  4. %%
  5. %% Squery can not have placeholders.
  6. %% Squery may contain many semicolon-separated queries
  7. %% ```
  8. %% > Query
  9. %% < (RowDescription?
  10. %% < DataRow*
  11. %% < CommandComplete)+
  12. %% < ReadyForQuery
  13. %% ---
  14. %% > Query when len(strip(Query)) == 0
  15. %% < EmptyQueryResponse
  16. %% < ReadyForQuery
  17. %% '''
  18. -module(epgsql_cmd_squery).
  19. -behaviour(epgsql_command).
  20. -export([init/1, execute/2, handle_message/4]).
  21. -export_type([response/0]).
  22. -type response_single() ::
  23. {ok, Count :: non_neg_integer(), Cols :: [epgsql:column()], Rows :: [tuple()]}
  24. | {ok, Count :: non_neg_integer()}
  25. | {ok, Cols :: [epgsql:column()], Rows :: [tuple()]}
  26. | {error, epgsql:query_error()}.
  27. -type response() :: response_single() | [response_single()].
  28. -include("protocol.hrl").
  29. -record(squery,
  30. {query :: iodata(),
  31. columns = [],
  32. decoder}).
  33. init(Sql) ->
  34. #squery{query = Sql}.
  35. execute(Sock, #squery{query = Q} = State) ->
  36. epgsql_sock:send(Sock, ?SIMPLEQUERY, [Q, 0]),
  37. {ok, Sock, State}.
  38. handle_message(?ROW_DESCRIPTION, <<Count:?int16, Bin/binary>>, Sock, State) ->
  39. Codec = epgsql_sock:get_codec(Sock),
  40. Columns = epgsql_wire:decode_columns(Count, Bin, Codec),
  41. Decoder = epgsql_wire:build_decoder(Columns, Codec),
  42. epgsql_sock:notify(Sock, {columns, Columns}),
  43. {noaction, Sock, State#squery{columns = Columns,
  44. decoder = Decoder}};
  45. handle_message(?DATA_ROW, <<_Count:?int16, Bin/binary>>,
  46. Sock, #squery{decoder = Decoder} = St) ->
  47. Row = epgsql_wire:decode_data(Bin, Decoder),
  48. {add_row, Row, Sock, St};
  49. handle_message(?COMMAND_COMPLETE, Bin, Sock, #squery{columns = Cols} = St) ->
  50. Complete = epgsql_wire:decode_complete(Bin),
  51. Rows = epgsql_sock:get_rows(Sock),
  52. Result = case Complete of
  53. {_, Count} when Cols == [] ->
  54. {ok, Count};
  55. {_, Count} ->
  56. {ok, Count, Cols, Rows};
  57. _ ->
  58. {ok, Cols, Rows}
  59. end,
  60. {add_result, Result, {complete, Complete}, Sock, St};
  61. handle_message(?EMPTY_QUERY, _, Sock, St) ->
  62. {add_result, {ok, [], []}, {complete, empty}, Sock, St};
  63. handle_message(?READY_FOR_QUERY, _Status, Sock, _State) ->
  64. %% We return single result if there is only one or list of results if
  65. %% there are more than one
  66. Result = case epgsql_sock:get_results(Sock) of
  67. [Res] -> Res;
  68. Res -> Res
  69. end,
  70. {finish, Result, done, Sock};
  71. handle_message(?ERROR, Error, Sock, St) ->
  72. Result = {error, Error},
  73. {add_result, Result, Result, Sock, St};
  74. handle_message(_, _, _, _) ->
  75. unknown.