diff --git a/c_src/esqlite_nif.c b/c_src/esqlite_nif.c index f641dc1..8b9f4ad 100644 --- a/c_src/esqlite_nif.c +++ b/c_src/esqlite_nif.c @@ -228,6 +228,33 @@ do_prepare(ErlNifEnv *env, esqlite_connection *conn, const ERL_NIF_TERM arg) return make_ok_tuple(env, esqlite_stmt); } +static ERL_NIF_TERM +do_bind(ErlNifEnv *env, sqlite3_stmt *stmt, const ERL_NIF_TERM arg) +{ + int parameter_count = sqlite3_bind_parameter_count(stmt); + int i, is_list; + ERL_NIF_TERM list, head, tail; + unsigned int list_length; + + is_list = enif_get_list_length(env, arg, &list_length); + if(!is_list) + return make_error_tuple(env, "bad_arg_list"); + if(parameter_count != list_length) + return make_error_tuple(env, "args_wrong_length"); + + + list = arg; + for(i = 0; i < list_length; i++) { + enif_get_list_cell(env, list, &head, &tail); + + /* TODO: do a bind, based on the type of erlang parameter received */ + sqlite3_bind_int(stmt, i, i); + list = tail; + } + + return _atom_ok; +} + static ERL_NIF_TERM make_binary(ErlNifEnv *env, const void *bytes, unsigned int size) { @@ -290,7 +317,7 @@ make_row(ErlNifEnv *env, sqlite3_stmt *statement) } static ERL_NIF_TERM -do_step(ErlNifEnv *env, esqlite_connection *conn, sqlite3_stmt *stmt) +do_step(ErlNifEnv *env, sqlite3_stmt *stmt) { int rc = sqlite3_step(stmt); @@ -331,7 +358,9 @@ evaluate_command(esqlite_command *cmd, esqlite_connection *conn) case cmd_prepare: return do_prepare(cmd->env, conn, cmd->arg); case cmd_step: - return do_step(cmd->env, conn, cmd->stmt); + return do_step(cmd->env, cmd->stmt); + case cmd_bind: + return do_bind(cmd->env, cmd->stmt, cmd->arg); case cmd_close: return do_close(cmd->env, conn, cmd->arg); default: @@ -373,7 +402,7 @@ esqlite_connection_run(void *arg) * Start the processing thread */ static ERL_NIF_TERM -start_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +esqlite_start(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { esqlite_connection *conn; ERL_NIF_TERM db_conn; @@ -409,7 +438,7 @@ start_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) * Open the database */ static ERL_NIF_TERM -esqlite_open_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) +esqlite_open(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { esqlite_connection *db; esqlite_command *cmd = NULL; @@ -448,7 +477,7 @@ esqlite_open_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) * Execute the sql statement */ static ERL_NIF_TERM -esqlite_exec_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) +esqlite_exec(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { esqlite_connection *db; esqlite_command *cmd = NULL; @@ -482,11 +511,13 @@ esqlite_exec_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) return _atom_ok; } + + /* * Prepare the sql statement */ static ERL_NIF_TERM -esqlite_prepare_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) +esqlite_prepare(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { esqlite_connection *conn; esqlite_command *cmd = NULL; @@ -520,7 +551,7 @@ esqlite_prepare_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) * Bind a variable to a prepared statement */ static ERL_NIF_TERM -esqlite_bind_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) +esqlite_bind(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { esqlite_statement *stmt; esqlite_command *cmd = NULL; @@ -542,6 +573,7 @@ esqlite_bind_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) cmd->type = cmd_bind; cmd->ref = enif_make_copy(cmd->env, argv[1]); cmd->pid = pid; + cmd->stmt = stmt->statement; cmd->arg = enif_make_copy(cmd->env, argv[3]); if(!stmt->connection) @@ -559,7 +591,7 @@ esqlite_bind_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) * Step to a prepared statement */ static ERL_NIF_TERM -esqlite_step_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) +esqlite_step(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { esqlite_statement *stmt; esqlite_command *cmd = NULL; @@ -601,7 +633,7 @@ esqlite_step_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) * Close the database */ static ERL_NIF_TERM -esqlite_close_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) +esqlite_close(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { esqlite_connection *conn; esqlite_command *cmd = NULL; @@ -658,14 +690,14 @@ on_load(ErlNifEnv* env, void** priv, ERL_NIF_TERM info) } static ErlNifFunc nif_funcs[] = { - {"esqlite_start", 0, start_nif}, - {"esqlite_open", 4, esqlite_open_nif}, - {"esqlite_exec", 4, esqlite_exec_nif}, - {"esqlite_prepare", 4, esqlite_prepare_nif}, - {"esqlite_step", 3, esqlite_step_nif}, + {"esqlite_start", 0, esqlite_start}, + {"esqlite_open", 4, esqlite_open}, + {"esqlite_exec", 4, esqlite_exec}, + {"esqlite_prepare", 4, esqlite_prepare}, + {"esqlite_step", 3, esqlite_step}, // {"esqlite_bind", 3, esqlite_bind_named}, - // {"esqlite_bind", 2, esqlite_bind}, - {"esqlite_close", 3, esqlite_close_nif} + {"esqlite_bind", 4, esqlite_bind}, + {"esqlite_close", 3, esqlite_close} }; ERL_NIF_INIT(esqlite, nif_funcs, on_load, NULL, NULL, NULL); diff --git a/src/esqlite.erl b/src/esqlite.erl index 123fd60..386a207 100644 --- a/src/esqlite.erl +++ b/src/esqlite.erl @@ -5,7 +5,7 @@ -module(esqlite). -author("Maas-Maarten Zeeman "). --export([open/1, open/2, exec/2, prepare/2, prepare/3, step/1, step/2, exec/3, close/1, close/2]). +-export([open/1, open/2, exec/2, prepare/2, prepare/3, step/1, step/2, bind/2, bind/3, exec/3, close/1, close/2]). -on_load(init/0). @@ -65,6 +65,16 @@ step(Stmt, Timeout) -> ok = esqlite_step(Stmt, Ref, self()), receive_answer(Ref, Timeout). +%% @doc Bind +%% +bind(Stmt, Args) -> + bind(Stmt, Args, ?DEFAULT_TIMEOUT). + +bind(Stmt, Args, Timeout) -> + Ref = make_ref(), + ok = esqlite_bind(Stmt, Ref, self(), Args), + receive_answer(Ref, Timeout). + %% @doc Close the database %% close(Db) -> @@ -91,6 +101,9 @@ esqlite_prepare(_Db, _Ref, _Dest, _Sql) -> esqlite_step(_Stmt, _Ref, _Dest) -> exit(nif_library_not_loaded). +esqlite_bind(_Stmt, _Ref, _Dest, _Args) -> + exit(nif_library_not_loaded). + esqlite_close(_Db, _Ref, _Dest) -> exit(nif_library_not_loaded). diff --git a/test/esqlite_test.erl b/test/esqlite_test.erl index 10055a5..58b07f9 100644 --- a/test/esqlite_test.erl +++ b/test/esqlite_test.erl @@ -48,6 +48,27 @@ prepare_test() -> ok. +bind_test() -> + {ok, Db} = esqlite:open(":memory:"), + ok = esqlite:exec(Db, "begin;"), + ok = esqlite:exec(Db, "create table test_table(one varchar(10), two int);"), + ok = esqlite:exec(Db, ["insert into test_table values(", "\"hello1\"", ",", "10" ");"]), + ok = esqlite:exec(Db, ["insert into test_table values(", "\"hello2\"", ",", "11" ");"]), + ok = esqlite:exec(Db, ["insert into test_table values(", "\"hello3\"", ",", "12" ");"]), + ok = esqlite:exec(Db, ["insert into test_table values(", "\"hello4\"", ",", "13" ");"]), + ok = esqlite:exec(Db, "commit;"), + + %% Create a prepared statement + {ok, Statement} = esqlite:prepare(Db, "insert into test_table values(?, ?)"), + esqlite:bind(Statement, ["one", 2]), + esqlite:step(Statement), + esqlite:bind(Statement, ["three", 4]), + esqlite:step(Statement), + + {ok, S1} = esqlite:prepare(Db, "select * from test_table where one = 'one'"), + [{"one", 2}] = esqlite:step(S1), + ok. + exec(Statement) -> exec(Statement, [], 0). @@ -78,11 +99,11 @@ exec(Statement, Acc, Tries) -> % api... -q(Sql, Connection) -> - ok. +%% q(Sql, Connection) -> +%% ok. -q(Sql, Args, Connection) -> - ok. +%% q(Sql, Args, Connection) -> +%% ok. %% @@ -99,8 +120,8 @@ q(Sql, Args, Connection) -> %% return %% {ok, Result} -- result kan dan headers, rows of request-id (voor async) %% {error, ... -q(Sql, Args, Options, Connection) -> - ok. +%% q(Sql, Args, Options, Connection) -> +%% ok.