diff --git a/c_src/esqlite3_nif.c b/c_src/esqlite3_nif.c index 5dd7378..d1972bb 100644 --- a/c_src/esqlite3_nif.c +++ b/c_src/esqlite3_nif.c @@ -440,7 +440,6 @@ do_get_autocommit(ErlNifEnv *env, esqlite3 *conn) } */ -/* static ERL_NIF_TERM make_binary(ErlNifEnv *env, const void *bytes, unsigned int size) { @@ -457,7 +456,6 @@ make_binary(ErlNifEnv *env, const void *bytes, unsigned int size) return term; } -*/ /* static ERL_NIF_TERM @@ -1217,39 +1215,43 @@ esqlite_reset(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) /* * Get the column names of the prepared statement. + */ static ERL_NIF_TERM esqlite_column_names(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { - esqlite3 *conn; esqlite3_stmt *stmt; - esqlite_command *cmd = NULL; - ErlNifPid pid; - if(argc != 4) + if(argc != 1) { return enif_make_badarg(env); - if(!enif_get_resource(env, argv[0], esqlite3_type, (void **) &conn)) + } + + if(!enif_get_resource(env, argv[0], esqlite3_stmt_type, (void **) &stmt)) { return enif_make_badarg(env); - if(!enif_get_resource(env, argv[1], esqlite3_stmt_type, (void **) &stmt)) - return enif_make_badarg(env); - if(!enif_is_ref(env, argv[2])) - return make_error_tuple(env, "invalid_ref"); - if(!enif_get_local_pid(env, argv[3], &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"); + if(!stmt->statement) { + return enif_raise_exception(env, make_atom(env, "no_prepared_statement")); + } - cmd->type = cmd_column_names; - cmd->ref = enif_make_copy(cmd->env, argv[2]); - cmd->pid = pid; - cmd->stmt = enif_make_copy(cmd->env, argv[1]); + ERL_NIF_TERM column_names = enif_make_list(env, 0); - return push_command(env, conn, cmd); + int size = sqlite3_column_count(stmt->statement); + if(size < 0) { + return enif_raise_exception(env, make_atom(env, "invalid_column_count")); + } + + for(int i=size; i-- > 0; ) { + const char *name = sqlite3_column_name(stmt->statement, i); + if(name == NULL) { + return enif_raise_exception(env, make_atom(env, "sqlite3_malloc_failure")); + } + + ERL_NIF_TERM ename = make_binary(env, name, strlen(name)); + column_names = enif_make_list_cell(env, ename, column_names); + } + + return column_names; } - */ /* * Get the column types of the prepared statement. @@ -1533,6 +1535,9 @@ static ErlNifFunc nif_funcs[] = { {"prepare", 2, esqlite_prepare}, + {"column_names", 1, esqlite_column_names}, + // {"column_types", 1, esqlite_column_types}, + {"interrupt", 1, esqlite_interrupt, ERL_NIF_DIRTY_JOB_IO_BOUND} /* @@ -1548,8 +1553,6 @@ static ErlNifFunc nif_funcs[] = { // TODO: {"esqlite_bind", 3, esqlite_bind_named}, {"bind", 5, esqlite_bind}, - {"column_names", 4, esqlite_column_names}, - {"column_types", 4, esqlite_column_types}, {"backup_init", 6, esqlite_backup_init}, {"backup_step", 5, esqlite_backup_step}, diff --git a/src/esqlite3.erl b/src/esqlite3.erl index ab93360..629750c 100644 --- a/src/esqlite3.erl +++ b/src/esqlite3.erl @@ -20,7 +20,9 @@ %% higher-level export -export([ open/1, close/1, - prepare/2 + prepare/2, + + column_names/1 % set_update_hook/2, set_update_hook/3, % exec/2, exec/3, exec/4, @@ -33,7 +35,6 @@ % bind/2, bind/3, % fetchone/1, % fetchall/1, fetchall/2, fetchall/3, -% column_names/1, column_names/2, % column_types/1, column_types/2, % backup_init/4, backup_init/5, % backup_finish/1, backup_finish/2, @@ -447,15 +448,9 @@ prepare(#esqlite3{db=Connection}, Sql) -> % %% @doc Return the column names of the prepared statement. %% -%-spec column_names(statement()) -> {atom()}. -%column_names(Stmt) -> -% column_names(Stmt, ?DEFAULT_TIMEOUT). - -%-spec column_names(statement(), timeout()) -> {atom()}. -%column_names(#statement{raw_statement=RawStatement, raw_connection=RawConnection}, Timeout) -> -% Ref = make_ref(), -% ok = esqlite3_nif:column_names(RawConnection, RawStatement, Ref, self()), -% receive_answer(RawConnection, Ref, Timeout). +-spec column_names(esqlite3_stmt()) -> [binary()]. +column_names(#esqlite3_stmt{stmt=Stmt}) -> + esqlite3_nif:column_names(Stmt). %% @doc Return the column types of the prepared statement. %% diff --git a/src/esqlite3_nif.erl b/src/esqlite3_nif.erl index 74d2b37..2498a24 100644 --- a/src/esqlite3_nif.erl +++ b/src/esqlite3_nif.erl @@ -24,6 +24,8 @@ close/1, prepare/2, + column_names/1, + interrupt/1 % set_update_hook/4, % exec/4, @@ -36,7 +38,6 @@ % finalize/4, % bind/5, -% column_names/4, % column_types/4, % backup_init/6, @@ -140,9 +141,9 @@ prepare(_Connection, _Sql) -> %% @doc Retrieve the column names of the prepared statement %% -%-spec column_names(esqlite3(), esqlite3_stmt(), reference(), pid()) -> ok | {error, _}. -%column_names(_Db, _Stmt, _Ref, _Dest) -> -% erlang:nif_error(nif_library_not_loaded). +-spec column_names(esqlite3_stmt()) -> list() | {error, _}. +column_names(_Stmt) -> + erlang:nif_error(nif_library_not_loaded). %% @doc Retrieve the column types of the prepared statement %% diff --git a/test/esqlite_test.erl b/test/esqlite_test.erl index e2b2050..523d5cc 100644 --- a/test/esqlite_test.erl +++ b/test/esqlite_test.erl @@ -48,6 +48,24 @@ prepare_after_close_test() -> ?assertMatch({error, {misuse, _}}, esqlite3:prepare(C, "select 1")), ok. +column_names_test() -> + {ok, C} = esqlite3:open(":memory:"), + + {ok, Stmt} = esqlite3:prepare(C, "select 1 as one"), + ?assertEqual([<<"one">>], esqlite3:column_names(Stmt)), + + {ok, Stmt1} = esqlite3:prepare(C, <<"select 1 as 😀"/utf8>>), + ?assertEqual([<<"😀"/utf8>>], esqlite3:column_names(Stmt1)), + + {ok, Stmt2} = esqlite3:prepare(C, <<"select 1">>), + ?assertEqual([<<"1">>], esqlite3:column_names(Stmt2)), + + {ok, Stmt3} = esqlite3:prepare(C, <<"select 1, 2, 3">>), + ?assertEqual([<<"1">>, <<"2">>, <<"3">>], esqlite3:column_names(Stmt3)), + + ok. + + %iodata_test() -> % {ok, C} = esqlite3:open(":memory:"),