Merge pull request #84 from greenfork/add-named-parameters
Add named parameters support
This commit is contained in:
@@ -10,3 +10,4 @@
|
||||
|
||||
Aleph Archives
|
||||
Qing Liang <qing.liang.cn@gmail.com>
|
||||
Dmitry Matveyev <public@greenfork.me>
|
||||
|
||||
@@ -753,6 +753,38 @@ esqlite_bind_null(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
|
||||
return make_atom(env, "ok");
|
||||
}
|
||||
|
||||
static ERL_NIF_TERM
|
||||
esqlite_bind_parameter_index(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
|
||||
{
|
||||
esqlite3_stmt *stmt;
|
||||
ErlNifBinary bin;
|
||||
int index;
|
||||
ERL_NIF_TERM eos = enif_make_int(env, 0);
|
||||
|
||||
if(argc != 2) {
|
||||
return enif_make_badarg(env);
|
||||
}
|
||||
|
||||
if(!enif_get_resource(env, argv[0], esqlite3_stmt_type, (void **) &stmt)) {
|
||||
return enif_make_badarg(env);
|
||||
}
|
||||
|
||||
if(!stmt->statement) {
|
||||
return enif_raise_exception(env, make_atom(env, "no_prepared_statement"));
|
||||
}
|
||||
|
||||
if(!enif_inspect_iolist_as_binary(env, enif_make_list2(env, argv[1], eos), &bin)) {
|
||||
return enif_make_badarg(env);
|
||||
}
|
||||
|
||||
index = sqlite3_bind_parameter_index(stmt->statement, bin.data);
|
||||
if(index == 0) {
|
||||
return enif_make_atom(env, "error");
|
||||
}
|
||||
|
||||
return make_ok_tuple(env, enif_make_int(env, index));
|
||||
}
|
||||
|
||||
static ERL_NIF_TERM
|
||||
esqlite_step(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
|
||||
{
|
||||
@@ -1202,6 +1234,7 @@ static ErlNifFunc nif_funcs[] = {
|
||||
{"bind_text", 3, esqlite_bind_text},
|
||||
{"bind_blob", 3, esqlite_bind_blob},
|
||||
{"bind_null", 2, esqlite_bind_null},
|
||||
{"bind_parameter_index", 2, esqlite_bind_parameter_index},
|
||||
|
||||
{"step", 1, esqlite_step, ERL_NIF_DIRTY_JOB_IO_BOUND},
|
||||
{"reset", 1, esqlite_reset},
|
||||
|
||||
@@ -295,8 +295,17 @@ prepare(#esqlite3{db=Connection}, Sql, PrepareFlags) ->
|
||||
%% @doc Bind an array of values as parameters of a prepared statement
|
||||
-spec bind(Statement, Args) -> Result when
|
||||
Statement :: esqlite3_stmt(),
|
||||
Args :: list(),
|
||||
Args :: list() | map(),
|
||||
Result :: ok | {error, _}.
|
||||
%% Named parameters
|
||||
bind(#esqlite3_stmt{}=Statement, Args) when is_map(Args) ->
|
||||
bind(Statement, maps:to_list(Args));
|
||||
bind(#esqlite3_stmt{}=Statement, [{_Type, _ParameterName, _Value} | _] = Args) ->
|
||||
bind2(Statement, Args);
|
||||
bind(#esqlite3_stmt{}=Statement, [{ParameterName, _Value} | _] = Args)
|
||||
when is_binary(ParameterName); is_list(ParameterName) ->
|
||||
bind2(Statement, Args);
|
||||
%% Anonymous parameters
|
||||
bind(#esqlite3_stmt{}=Statement, Args) when is_list(Args) ->
|
||||
bind1(Statement, 1, Args).
|
||||
|
||||
@@ -310,6 +319,33 @@ bind1(Statement, Column, [Arg | Args]) ->
|
||||
Error
|
||||
end.
|
||||
|
||||
bind2(_Statement, []) ->
|
||||
ok;
|
||||
bind2(#esqlite3_stmt{stmt=Stmt}=Statement, [{Type, ParameterName, Value} | Args]) ->
|
||||
case esqlite3_nif:bind_parameter_index(Stmt, ParameterName) of
|
||||
{ok, Column} ->
|
||||
case bind_arg(Statement, Column, {Type, Value}) of
|
||||
ok ->
|
||||
bind2(Statement, Args);
|
||||
{error, _}=Error ->
|
||||
Error
|
||||
end;
|
||||
error ->
|
||||
{error, named_parameter_not_found}
|
||||
end;
|
||||
bind2(#esqlite3_stmt{stmt=Stmt}=Statement, [{ParameterName, Value} | Args]) ->
|
||||
case esqlite3_nif:bind_parameter_index(Stmt, ParameterName) of
|
||||
{ok, Column} ->
|
||||
case bind_arg(Statement, Column, Value) of
|
||||
ok ->
|
||||
bind2(Statement, Args);
|
||||
{error, _}=Error ->
|
||||
Error
|
||||
end;
|
||||
error ->
|
||||
{error, named_parameter_not_found}
|
||||
end.
|
||||
|
||||
% Bind with automatic tyoe conversion
|
||||
bind_arg(Statement, Column, undefined) ->
|
||||
bind_null(Statement, Column);
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
bind_text/3,
|
||||
bind_blob/3,
|
||||
bind_null/2,
|
||||
bind_parameter_index/2,
|
||||
|
||||
step/1,
|
||||
|
||||
@@ -194,6 +195,13 @@ bind_blob(_Statement, _Index, _Value) ->
|
||||
bind_null(_Statement, _Index) ->
|
||||
erlang:nif_error(nif_library_not_loaded).
|
||||
|
||||
-spec bind_parameter_index(Statement, ParameterName) -> Result when
|
||||
Statement :: esqlite3_stmt_ref(),
|
||||
ParameterName :: iodata(),
|
||||
Result :: {ok, integer()} | error.
|
||||
bind_parameter_index(_Statement, _ParameterName) ->
|
||||
erlang:nif_error(nif_library_not_loaded).
|
||||
|
||||
-spec step(Statement) -> StepResult when
|
||||
Statement :: esqlite3_stmt_ref(),
|
||||
StepResult :: row() | '$done' | error().
|
||||
|
||||
@@ -281,6 +281,26 @@ bind_for_queries_test() ->
|
||||
|
||||
ok.
|
||||
|
||||
named_bind_for_queries_test() ->
|
||||
{ok, Db} = esqlite3:open(":memory:"),
|
||||
|
||||
ok = esqlite3:exec(Db, "begin;"),
|
||||
ok = esqlite3:exec(Db, "create table test_table(one varchar(10), two int);"),
|
||||
ok = esqlite3:exec(Db, "commit;"),
|
||||
|
||||
?assertEqual([[1]], esqlite3:q(Db, <<"SELECT count(type) FROM sqlite_master WHERE type='table' AND name=:name;">>,
|
||||
[{<<":name">>, "test_table"}])),
|
||||
?assertEqual([[1]], esqlite3:q(Db, <<"SELECT count(type) FROM sqlite_master WHERE type='table' AND name=$name;">>,
|
||||
[{"$name", test_table}])),
|
||||
?assertEqual([[1]], esqlite3:q(Db, <<"SELECT count(type) FROM sqlite_master WHERE type='table' AND name=@name;">>,
|
||||
[{"@name", <<"test_table">>}])),
|
||||
?assertEqual([[1]], esqlite3:q(Db, <<"SELECT count(type) FROM sqlite_master WHERE type='table' AND name=:name;">>,
|
||||
#{":name" => test_table})),
|
||||
?assertEqual([[1]], esqlite3:q(Db, <<"SELECT count(type) FROM sqlite_master WHERE type='table' AND name=:name;">>,
|
||||
[{text, ":name", "test_table"}])),
|
||||
|
||||
ok.
|
||||
|
||||
column_names2_test() ->
|
||||
{ok, Db} = esqlite3:open(":memory:"),
|
||||
ok = esqlite3:exec(Db, "begin;"),
|
||||
|
||||
Reference in New Issue
Block a user