Merge pull request #84 from greenfork/add-named-parameters
Add named parameters support
This commit is contained in:
@@ -10,3 +10,4 @@
|
|||||||
|
|
||||||
Aleph Archives
|
Aleph Archives
|
||||||
Qing Liang <qing.liang.cn@gmail.com>
|
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");
|
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
|
static ERL_NIF_TERM
|
||||||
esqlite_step(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
|
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_text", 3, esqlite_bind_text},
|
||||||
{"bind_blob", 3, esqlite_bind_blob},
|
{"bind_blob", 3, esqlite_bind_blob},
|
||||||
{"bind_null", 2, esqlite_bind_null},
|
{"bind_null", 2, esqlite_bind_null},
|
||||||
|
{"bind_parameter_index", 2, esqlite_bind_parameter_index},
|
||||||
|
|
||||||
{"step", 1, esqlite_step, ERL_NIF_DIRTY_JOB_IO_BOUND},
|
{"step", 1, esqlite_step, ERL_NIF_DIRTY_JOB_IO_BOUND},
|
||||||
{"reset", 1, esqlite_reset},
|
{"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
|
%% @doc Bind an array of values as parameters of a prepared statement
|
||||||
-spec bind(Statement, Args) -> Result when
|
-spec bind(Statement, Args) -> Result when
|
||||||
Statement :: esqlite3_stmt(),
|
Statement :: esqlite3_stmt(),
|
||||||
Args :: list(),
|
Args :: list() | map(),
|
||||||
Result :: ok | {error, _}.
|
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) ->
|
bind(#esqlite3_stmt{}=Statement, Args) when is_list(Args) ->
|
||||||
bind1(Statement, 1, Args).
|
bind1(Statement, 1, Args).
|
||||||
|
|
||||||
@@ -310,6 +319,33 @@ bind1(Statement, Column, [Arg | Args]) ->
|
|||||||
Error
|
Error
|
||||||
end.
|
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 with automatic tyoe conversion
|
||||||
bind_arg(Statement, Column, undefined) ->
|
bind_arg(Statement, Column, undefined) ->
|
||||||
bind_null(Statement, Column);
|
bind_null(Statement, Column);
|
||||||
|
|||||||
@@ -42,6 +42,7 @@
|
|||||||
bind_text/3,
|
bind_text/3,
|
||||||
bind_blob/3,
|
bind_blob/3,
|
||||||
bind_null/2,
|
bind_null/2,
|
||||||
|
bind_parameter_index/2,
|
||||||
|
|
||||||
step/1,
|
step/1,
|
||||||
|
|
||||||
@@ -194,6 +195,13 @@ bind_blob(_Statement, _Index, _Value) ->
|
|||||||
bind_null(_Statement, _Index) ->
|
bind_null(_Statement, _Index) ->
|
||||||
erlang:nif_error(nif_library_not_loaded).
|
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
|
-spec step(Statement) -> StepResult when
|
||||||
Statement :: esqlite3_stmt_ref(),
|
Statement :: esqlite3_stmt_ref(),
|
||||||
StepResult :: row() | '$done' | error().
|
StepResult :: row() | '$done' | error().
|
||||||
|
|||||||
@@ -281,6 +281,26 @@ bind_for_queries_test() ->
|
|||||||
|
|
||||||
ok.
|
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() ->
|
column_names2_test() ->
|
||||||
{ok, Db} = esqlite3:open(":memory:"),
|
{ok, Db} = esqlite3:open(":memory:"),
|
||||||
ok = esqlite3:exec(Db, "begin;"),
|
ok = esqlite3:exec(Db, "begin;"),
|
||||||
|
|||||||
Reference in New Issue
Block a user