|
@@ -1,15 +1,18 @@
|
|
|
-%% > Bind
|
|
|
-%% < BindComplete
|
|
|
-%% > Execute
|
|
|
-%% < DataRow*
|
|
|
-%% < CommandComplete
|
|
|
-%% -- Repeated many times --
|
|
|
+%% > {Bind
|
|
|
+%% < BindComplete
|
|
|
+%% > Execute
|
|
|
+%% < DataRow*
|
|
|
+%% < CommandComplete}*
|
|
|
%% > Sync
|
|
|
%% < ReadyForQuery
|
|
|
-module(epgsql_cmd_batch).
|
|
|
-behaviour(epgsql_command).
|
|
|
-export([init/1, execute/2, handle_message/4]).
|
|
|
--export_type([response/0]).
|
|
|
+-export_type([arguments/0, response/0]).
|
|
|
+
|
|
|
+-type arguments() ::
|
|
|
+ {epgsql:statement(), [ [epgsql:bind_param()] ]} |
|
|
|
+ [{epgsql:statement(), [epgsql:bind_param()]}].
|
|
|
|
|
|
-type response() :: [{ok, Count :: non_neg_integer(), Rows :: [tuple()]}
|
|
|
| {ok, Count :: non_neg_integer()}
|
|
@@ -20,13 +23,18 @@
|
|
|
-include("protocol.hrl").
|
|
|
|
|
|
-record(batch,
|
|
|
- {batch :: [{#statement{}, list()}],
|
|
|
- decoder}).
|
|
|
+ {batch :: [ [epgsql:bind_param()] ] | [{#statement{}, [epgsql:bind_param()]}],
|
|
|
+ statement :: #statement{} | undefined,
|
|
|
+ decoder :: epgsql_wire:row_decoder() | undefined}).
|
|
|
|
|
|
-init(Batch) ->
|
|
|
+-spec init(arguments()) -> #batch{}.
|
|
|
+init({#statement{} = Statement, Batch}) ->
|
|
|
+ #batch{statement = Statement,
|
|
|
+ batch = Batch};
|
|
|
+init(Batch) when is_list(Batch) ->
|
|
|
#batch{batch = Batch}.
|
|
|
|
|
|
-execute(Sock, #batch{batch = Batch} = State) ->
|
|
|
+execute(Sock, #batch{batch = Batch, statement = undefined} = State) ->
|
|
|
Codec = epgsql_sock:get_codec(Sock),
|
|
|
Commands =
|
|
|
lists:foldr(
|
|
@@ -43,10 +51,28 @@ execute(Sock, #batch{batch = Batch} = State) ->
|
|
|
[{?SYNC, []}],
|
|
|
Batch),
|
|
|
epgsql_sock:send_multi(Sock, Commands),
|
|
|
+ {ok, Sock, State};
|
|
|
+execute(Sock, #batch{batch = Batch,
|
|
|
+ statement = #statement{name = StatementName,
|
|
|
+ columns = Columns,
|
|
|
+ types = Types}} = State) ->
|
|
|
+ Codec = epgsql_sock:get_codec(Sock),
|
|
|
+ BinFormats = epgsql_wire:encode_formats(Columns),
|
|
|
+ Commands =
|
|
|
+ lists:foldr(
|
|
|
+ fun(Parameters, Acc) ->
|
|
|
+ TypedParameters = lists:zip(Types, Parameters),
|
|
|
+ BinParams = epgsql_wire:encode_parameters(TypedParameters, Codec),
|
|
|
+ [{?BIND, [0, StatementName, 0, BinParams, BinFormats]},
|
|
|
+ {?EXECUTE, [0, <<0:?int32>>]} | Acc]
|
|
|
+ end,
|
|
|
+ [{?SYNC, []}],
|
|
|
+ Batch),
|
|
|
+ epgsql_sock:send_multi(Sock, Commands),
|
|
|
{ok, Sock, State}.
|
|
|
|
|
|
-handle_message(?BIND_COMPLETE, <<>>, Sock, #batch{batch = [{Stmt, _} | _]} = State) ->
|
|
|
- #statement{columns = Columns} = Stmt,
|
|
|
+handle_message(?BIND_COMPLETE, <<>>, Sock, State) ->
|
|
|
+ Columns = current_cols(State),
|
|
|
Codec = epgsql_sock:get_codec(Sock),
|
|
|
Decoder = epgsql_wire:build_decoder(Columns, Codec),
|
|
|
{noaction, Sock, State#batch{decoder = Decoder}};
|
|
@@ -58,7 +84,8 @@ handle_message(?DATA_ROW, <<_Count:?int16, Bin/binary>>, Sock,
|
|
|
%% Sock1 = epgsql_sock:add_result(Sock, {complete, empty}, {ok, [], []}),
|
|
|
%% {noaction, Sock1};
|
|
|
handle_message(?COMMAND_COMPLETE, Bin, Sock,
|
|
|
- #batch{batch = [{#statement{columns = Columns}, _} | Batch]} = State) ->
|
|
|
+ #batch{batch = [_ | Batch]} = State) ->
|
|
|
+ Columns = current_cols(State),
|
|
|
Complete = epgsql_wire:decode_complete(Bin),
|
|
|
Rows = epgsql_sock:get_rows(Sock),
|
|
|
Result = case Complete of
|
|
@@ -79,3 +106,14 @@ handle_message(?ERROR, Error, Sock, #batch{batch = [_ | Batch]} = State) ->
|
|
|
{add_result, Result, Result, Sock, State#batch{batch = Batch}};
|
|
|
handle_message(_, _, _, _) ->
|
|
|
unknown.
|
|
|
+
|
|
|
+%% Helpers
|
|
|
+
|
|
|
+current_cols(Batch) ->
|
|
|
+ #statement{columns = Columns} = current_stmt(Batch),
|
|
|
+ Columns.
|
|
|
+
|
|
|
+current_stmt(#batch{batch = [{Stmt, _} | _], statement = undefined}) ->
|
|
|
+ Stmt;
|
|
|
+current_stmt(#batch{statement = #statement{} = Stmt}) ->
|
|
|
+ Stmt.
|