%% @doc Executes SQL query(es) using %% %% simple query protocol %% %% Squery can not have placeholders. %% Squery may contain many semicolon-separated queries %% ``` %% > Query %% < (RowDescription? %% < DataRow* %% < CommandComplete)+ %% < ReadyForQuery %% --- %% > Query when len(strip(Query)) == 0 %% < EmptyQueryResponse %% < ReadyForQuery %% ''' -module(epgsql_cmd_squery). -behaviour(epgsql_command). -export([init/1, execute/2, handle_message/4]). -export_type([response/0]). -type response_single() :: {ok, Count :: non_neg_integer(), Cols :: [epgsql:column()], Rows :: [tuple()]} | {ok, Count :: non_neg_integer()} | {ok, Cols :: [epgsql:column()], Rows :: [tuple()]} | {error, epgsql:query_error()}. -type response() :: response_single() | [response_single()]. -include("protocol.hrl"). -record(squery, {query :: iodata(), columns = [], decoder}). init(Sql) -> #squery{query = Sql}. execute(Sock, #squery{query = Q} = State) -> epgsql_sock:send(Sock, ?SIMPLEQUERY, [Q, 0]), {ok, Sock, State}. handle_message(?ROW_DESCRIPTION, <>, Sock, State) -> Codec = epgsql_sock:get_codec(Sock), Columns = epgsql_wire:decode_columns(Count, Bin, Codec), Decoder = epgsql_wire:build_decoder(Columns, Codec), epgsql_sock:notify(Sock, {columns, Columns}), {noaction, Sock, State#squery{columns = Columns, decoder = Decoder}}; handle_message(?DATA_ROW, <<_Count:?int16, Bin/binary>>, Sock, #squery{decoder = Decoder} = St) -> Row = epgsql_wire:decode_data(Bin, Decoder), {add_row, Row, Sock, St}; handle_message(?COMMAND_COMPLETE, Bin, Sock, #squery{columns = Cols} = St) -> Complete = epgsql_wire:decode_complete(Bin), Rows = epgsql_sock:get_rows(Sock), Result = case Complete of {_, Count} when Cols == [] -> {ok, Count}; {_, Count} -> {ok, Count, Cols, Rows}; _ -> {ok, Cols, Rows} end, {add_result, Result, {complete, Complete}, Sock, St}; handle_message(?EMPTY_QUERY, _, Sock, St) -> {add_result, {ok, [], []}, {complete, empty}, Sock, St}; handle_message(?READY_FOR_QUERY, _Status, Sock, _State) -> %% We return single result if there is only one or list of results if %% there are more than one Result = case epgsql_sock:get_results(Sock) of [Res] -> Res; Res -> Res end, {finish, Result, done, Sock}; handle_message(?ERROR, Error, Sock, St) -> Result = {error, Error}, {add_result, Result, Result, Sock, St}; handle_message(_, _, _, _) -> unknown.