Skip to content

Commit

Permalink
Merge pull request #12 from thalesmg/20241219-fix-idatetime-fn-clause
Browse files Browse the repository at this point in the history
fix: handle bad datetime parameters more gracefully
  • Loading branch information
thalesmg authored Dec 19, 2024
2 parents 8e01403 + 852c89b commit 4177442
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 21 deletions.
37 changes: 23 additions & 14 deletions src/commands/epgsql_cmd_prepared_query2.erl
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
init({Name, Parameters}) ->
#pquery2{name = Name, params = Parameters}.

execute(Sock, #pquery2{name = Name, params = Params} = State) ->
execute(Sock, #pquery2{name = Name} = State) ->
case maps:get(Name, epgsql_sock:get_stmts(Sock), undefined) of
undefined ->
Error = #error{
Expand All @@ -43,19 +43,28 @@ execute(Sock, #pquery2{name = Name, params = Params} = State) ->
extra = []
},
{finish, {error, Error}, Sock};
#statement{types = Types} = Stmt ->
TypedParams = zip(Name, Types, Params),
#statement{name = StatementName, columns = Columns} = Stmt,
Codec = epgsql_sock:get_codec(Sock),
Bin1 = epgsql_wire:encode_parameters(TypedParams, Codec),
Bin2 = epgsql_wire:encode_formats(Columns),
Commands =
[
epgsql_wire:encode_bind("", StatementName, Bin1, Bin2),
epgsql_wire:encode_execute("", 0),
epgsql_wire:encode_sync()
],
{send_multi, Commands, Sock, State#pquery2{stmt = Stmt}}
#statement{} = Stmt ->
do_execute(Sock, State, Stmt)
end.

do_execute(Sock, State, Statement) ->
#pquery2{name = Name, params = Params} = State,
#statement{types = Types, name = StatementName, columns = Columns} = Statement,
TypedParams = zip(Name, Types, Params),
Codec = epgsql_sock:get_codec(Sock),
try
Bin1 = epgsql_wire:encode_parameters(TypedParams, Codec),
Bin2 = epgsql_wire:encode_formats(Columns),
Commands =
[
epgsql_wire:encode_bind("", StatementName, Bin1, Bin2),
epgsql_wire:encode_execute("", 0),
epgsql_wire:encode_sync()
],
{send_multi, Commands, Sock, State#pquery2{stmt = Statement}}
catch
throw:#{} = Context ->
{finish, {error, Context}, Sock}
end.

zip(Name, Types, Params) ->
Expand Down
2 changes: 1 addition & 1 deletion src/epgsql.app.src
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{application, epgsql,
[{description, "PostgreSQL Client"},
{vsn, "4.7.1.3"},
{vsn, "4.7.1.4"},
{modules, []},
{registered, []},
{applications, [kernel,
Expand Down
2 changes: 1 addition & 1 deletion src/epgsql.appup.src
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
%% -*- mode: erlang -*-
{"4.7.1.3",
{"4.7.1.4",
[
{<<"4\\.6\\.[0-9]">>, [
{load_module, epgsql, brutal_purge, soft_purge, []},
Expand Down
4 changes: 3 additions & 1 deletion src/epgsql_idatetime.erl
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,9 @@ i2timestamp2(D, T) ->

timestamp2i({Date, Time}) ->
D = date2j(Date) - ?POSTGRES_EPOC_JDATE,
D * ?USECS_PER_DAY + time2i(Time).
D * ?USECS_PER_DAY + time2i(Time);
timestamp2i(X) ->
throw(bad_param).

now2i({MegaSecs, Secs, MicroSecs}) ->
(MegaSecs * 1000000 + Secs) * 1000000 + MicroSecs - ?POSTGRES_EPOC_USECS.
Expand Down
17 changes: 13 additions & 4 deletions src/epgsql_wire.erl
Original file line number Diff line number Diff line change
Expand Up @@ -265,10 +265,19 @@ encode_parameters([], Count, Formats, Values, _Codec) ->
[<<Count:?int16>>, Formats, <<Count:?int16>> | lists:reverse(Values)];

encode_parameters([P | T], Count, Formats, Values, Codec) ->
{Format, Value} = encode_parameter(P, Codec),
Formats2 = <<Formats/binary, Format:?int16>>,
Values2 = [Value | Values],
encode_parameters(T, Count + 1, Formats2, Values2, Codec).
try
{Format, Value} = encode_parameter(P, Codec),
Formats2 = <<Formats/binary, Format:?int16>>,
Values2 = [Value | Values],
encode_parameters(T, Count + 1, Formats2, Values2, Codec)
catch
throw:bad_param ->
{Type, Value0} = P,
throw(#{reason => bad_param,
index => Count,
type => Type,
value => Value0})
end.

%% @doc encode single 'typed' parameter
-spec encode_parameter({Type, Val :: any()},
Expand Down
19 changes: 19 additions & 0 deletions test/epgsql_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ groups() ->

prepared_query,
prepared_query2,
prepared_query2_bad_params,
select,
insert,
update,
Expand Down Expand Up @@ -488,6 +489,24 @@ prepared_query2(Config) ->
Module:prepared_query2(C, "non_existent_query", [4])
end).

%% Checks that we don't crash ungracefully if user provides parameters that cannot be
%% encoded by the intended codec.
prepared_query2_bad_params(Config) ->
Module = ?config(module, Config),
epgsql_ct:with_connection(Config, fun(C) ->
Name = "bad_params",
Column = get_type_col(timestamp),
SQL = io_lib:format("insert into test_table2(~s) values ($1)", [Column]),
{ok, _} = Module:parse2(C, Name, SQL, []),
{error, #{ reason := bad_param
, index := 0
, type := timestamp
, value := <<"2024-06-30 00:10:00">>
}} =
Module:prepared_query2(C, Name, [<<"2024-06-30 00:10:00">>]),
ok
end).

select(Config) ->
Module = ?config(module, Config),
epgsql_ct:with_connection(Config, fun(C) ->
Expand Down

0 comments on commit 4177442

Please sign in to comment.