From e04da6dcc7c94f247cd72b14ba3f0ad16545e450 Mon Sep 17 00:00:00 2001 From: Maas-Maarten Zeeman Date: Mon, 21 Nov 2011 11:47:01 +0100 Subject: [PATCH] Adding column names functionality --- c_src/esqlite3_nif.c | 53 +++++++++++++++++++++++++++++++++++++++++++ src/esqlite3.erl | 7 ++++-- src/esqlite3_nif.erl | 11 +++++++-- test/esqlite_test.erl | 17 +++++++++++++- 4 files changed, 83 insertions(+), 5 deletions(-) diff --git a/c_src/esqlite3_nif.c b/c_src/esqlite3_nif.c index 1bafdd9..f4407c8 100644 --- a/c_src/esqlite3_nif.c +++ b/c_src/esqlite3_nif.c @@ -59,6 +59,7 @@ typedef enum { cmd_prepare, cmd_bind, cmd_step, + cmd_column_names, cmd_close, cmd_stop } command_type; @@ -381,6 +382,13 @@ do_step(ErlNifEnv *env, sqlite3_stmt *stmt) if(rc == SQLITE_ROW) return make_row(env, stmt); + return make_error_tuple(env, "unexpected_return_value"); +} + +static ERL_NIF_TERM +do_column_names(ErlNifEnv *env, sqlite3_stmt *stmt) +{ + fprintf(stderr, "Called column names\n"); return _atom_ok; } @@ -414,6 +422,8 @@ evaluate_command(esqlite_command *cmd, esqlite_connection *conn) return do_step(cmd->env, cmd->stmt); case cmd_bind: return do_bind(cmd->env, conn->db, cmd->stmt, cmd->arg); + case cmd_column_names: + return do_column_names(cmd->env, cmd->stmt); case cmd_close: return do_close(cmd->env, conn, cmd->arg); default: @@ -682,6 +692,48 @@ esqlite_step(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) return _atom_ok; } +/* + * Step to a prepared statement + */ +static ERL_NIF_TERM +esqlite_column_names(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) +{ + esqlite_statement *stmt; + esqlite_command *cmd = NULL; + ErlNifPid pid; + + if(argc != 3) + return enif_make_badarg(env); + if(!enif_get_resource(env, argv[0], esqlite_statement_type, (void **) &stmt)) + return enif_make_badarg(env); + if(!enif_is_ref(env, argv[1])) + return make_error_tuple(env, "invalid_ref"); + if(!enif_get_local_pid(env, argv[2], &pid)) + return make_error_tuple(env, "invalid_pid"); + + if(!stmt->statement) + return make_error_tuple(env, "no_prepared_statement"); + + cmd = command_create(); + if(!cmd) + return make_error_tuple(env, "command_create_failed"); + + cmd->type = cmd_column_names; + cmd->ref = enif_make_copy(cmd->env, argv[1]); + cmd->pid = pid; + cmd->stmt = stmt->statement; + + if(!stmt->connection) + return make_error_tuple(env, "no_connection"); + if(!stmt->connection->commands) + return make_error_tuple(env, "no_command_queue"); + + if(!queue_push(stmt->connection->commands, cmd)) + return make_error_tuple(env, "command_push_failed"); + + return _atom_ok; +} + /* * Close the database */ @@ -750,6 +802,7 @@ static ErlNifFunc nif_funcs[] = { {"step", 3, esqlite_step}, // {"esqlite_bind", 3, esqlite_bind_named}, {"bind", 4, esqlite_bind}, + {"column_names", 3, esqlite_column_names}, {"close", 3, esqlite_close} }; diff --git a/src/esqlite3.erl b/src/esqlite3.erl index 4f2d922..c8636d3 100644 --- a/src/esqlite3.erl +++ b/src/esqlite3.erl @@ -71,7 +71,8 @@ foreach(F, Sql, Connection) -> %% foreach_s(F, Statement) -> case try_step(Statement, 0) of - '$done' -> ok; + '$done' -> + ok; Row when is_tuple(Row) -> F(Row), foreach_s(F, Statement) @@ -82,7 +83,7 @@ map_s(F, Statement) -> case try_step(Statement, 0) of '$done' -> []; - Row -> + Row when is_tuple(Row) -> [F(Row) | map_s(F, Statement)] end. @@ -95,11 +96,13 @@ do_steps(Statement) -> [Row | do_steps(Statement)] end. +%% Try the step, when the database is busy, try_step(_Statement, Tries) when Tries > 5 -> throw(too_many_tries); try_step(Statement, Tries) -> case esqlite3:step(Statement) of '$busy' -> + timer:sleep(100 * Tries), try_step(Statement, Tries + 1); Something -> Something diff --git a/src/esqlite3_nif.erl b/src/esqlite3_nif.erl index b473028..d689aa2 100644 --- a/src/esqlite3_nif.erl +++ b/src/esqlite3_nif.erl @@ -27,6 +27,7 @@ prepare/4, step/3, bind/4, + column_names/3, close/3 ]). @@ -70,16 +71,22 @@ prepare(_Db, _Ref, _Dest, _Sql) -> %% @doc %% -%% @spec step(connection(), reference(), pid()) -> ok | {error, message()} +%% @spec step(statement(), reference(), pid()) -> ok | {error, message()} step(_Stmt, _Ref, _Dest) -> exit(nif_library_not_loaded). %% @doc Bind parameters to a prepared statement. %% -%% @spec bind(connection(), reference(), pid(), []) -> ok | {error, message()} +%% @spec bind(statement(), reference(), pid(), []) -> ok | {error, message()} bind(_Stmt, _Ref, _Dest, _Args) -> exit(nif_library_not_loaded). +%% @doc Retrieve the column names of the prepared statement +%% +%% @spec column_names(statement(), reference(), pid()) -> {ok, tuple()} | {error, message()} +column_names(_Stmt, _Ref, _Dest) -> + exit(nif_library_not_loaded). + %% @doc Close the connection. %% %% @spec close(connection(), reference(), pid()) -> ok | {error, message()} diff --git a/test/esqlite_test.erl b/test/esqlite_test.erl index 2400d2b..898e2f7 100644 --- a/test/esqlite_test.erl +++ b/test/esqlite_test.erl @@ -74,6 +74,21 @@ bind_test() -> ok. +column_names_test() -> + {ok, Db} = esqlite3:open(":memory:"), + ok = esqlite3:exec("begin;", Db), + ok = esqlite3:exec("create table test_table(one varchar(10), two int);", Db), + ok = esqlite3:exec(["insert into test_table values(", "\"hello1\"", ",", "10" ");"], Db), + ok = esqlite3:exec("commit;", Db), + + {ok, Stmt} = esqlite3:prepare("select * from test_table", Db), + + test = esqlite3:column_names(Stmt), + + ok. + + + foreach_test() -> {ok, Db} = esqlite3:open(":memory:"), ok = esqlite3:exec("begin;", Db), @@ -93,7 +108,7 @@ foreach_test() -> end end, - esqlite3:foreach(F, "select * from test_table", Db), + esqlite3:foreach(F, "select * from test_table;", Db), 10 = get("hello1"), 11 = get("hello2"),