From 0e45e9bea47e71766ca83065edc14bb812af3ee1 Mon Sep 17 00:00:00 2001 From: Maas-Maarten Zeeman Date: Sat, 21 May 2022 22:00:54 +0200 Subject: [PATCH] Re-enabled a lot of tests --- c_src/esqlite3_nif.c | 41 ++++++++- src/esqlite3.erl | 15 ++- src/esqlite3_nif.erl | 11 +++ test/esqlite_test.erl | 208 +++++++++++++++++++++--------------------- 4 files changed, 165 insertions(+), 110 deletions(-) diff --git a/c_src/esqlite3_nif.c b/c_src/esqlite3_nif.c index 30128d9..e964d25 100644 --- a/c_src/esqlite3_nif.c +++ b/c_src/esqlite3_nif.c @@ -89,7 +89,7 @@ get_sqlite3_return_code_msg(int r) case SQLITE_INTERNAL: return "internal"; case SQLITE_PERM: return "perm"; case SQLITE_ABORT: return "abort"; - case SQLITE_BUSY: return "busy"; + case SQLITE_BUSY: return "$busy"; case SQLITE_LOCKED: return "locked"; case SQLITE_NOMEM: return "nomem"; case SQLITE_READONLY: return "readonly"; @@ -112,7 +112,7 @@ get_sqlite3_return_code_msg(int r) case SQLITE_RANGE: return "range"; case SQLITE_NOTADB: return "notadb"; case SQLITE_ROW: return "row"; - case SQLITE_DONE: return "done"; + case SQLITE_DONE: return "$done"; } return "unknown"; @@ -865,6 +865,38 @@ esqlite_close(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) return make_atom(env, "ok"); } +/* + * Exec a sql statement + */ +static ERL_NIF_TERM +esqlite_exec(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) +{ + esqlite3 *conn; + ErlNifBinary bin; + int rc; + 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_type, (void **) &conn)) { + return enif_make_badarg(env); + } + + if(!enif_inspect_iolist_as_binary(env, enif_make_list2(env, argv[1], eos), &bin)) { + return enif_make_badarg(env); + } + + rc = sqlite3_exec(conn->db, (char *) bin.data, NULL, NULL, NULL); + if(rc != SQLITE_OK) { + return make_sqlite3_error_tuple(env, rc); + } + + return make_atom(env, "ok"); +} + + /* * Prepare the sql statement */ @@ -1353,9 +1385,9 @@ esqlite_step(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) case SQLITE_DONE: /* since 3.6.23.1 it is no longer required to do an explict reset. */ - return make_atom(env, "done"); + return make_atom(env, "$done"); case SQLITE_BUSY: - return make_atom(env, "busy"); + return make_atom(env, "$busy"); } return make_sqlite3_error_tuple(env, rc); @@ -1685,6 +1717,7 @@ static ErlNifFunc nif_funcs[] = { {"open", 1, esqlite_open, ERL_NIF_DIRTY_JOB_IO_BOUND}, {"close", 1, esqlite_close, ERL_NIF_DIRTY_JOB_IO_BOUND}, + {"exec", 2, esqlite_exec}, {"prepare", 3, esqlite_prepare}, {"column_names", 1, esqlite_column_names}, diff --git a/src/esqlite3.erl b/src/esqlite3.erl index 7d0be54..7e74d29 100644 --- a/src/esqlite3.erl +++ b/src/esqlite3.erl @@ -28,6 +28,7 @@ % set_update_hook/2, set_update_hook/3, + exec/2, prepare/2, prepare/3, @@ -45,7 +46,6 @@ step/1, reset/1 -% exec/2, exec/3, exec/4, % insert/2, insert/3, % % fetchone/1, @@ -391,6 +391,17 @@ changes(#esqlite3{db=Connection}) -> get_autocommit(#esqlite3{db=Connection}) -> esqlite3_nif:get_autocommit(Connection). +%% @doc Compile a SQL statement. Returns a cached compiled statement which can be used in +%% queries. +%% +-spec exec(Connection, Sql) -> ExecResult + when Connection :: esqlite3(), + Sql :: sql(), + ExecResult :: ok | {error, _}. +exec(#esqlite3{db=Connection}, Sql) -> + esqlite3_nif:exec(Connection, Sql). + + %% @doc Compile a SQL statement. Returns a cached compiled statement which can be used in %% queries. %% @@ -476,8 +487,6 @@ column_names(#esqlite3_stmt{stmt=Stmt}) -> column_decltypes(#esqlite3_stmt{stmt=Stmt}) -> esqlite3_nif:column_decltypes(Stmt). - - %% %% Backup API %% diff --git a/src/esqlite3_nif.erl b/src/esqlite3_nif.erl index 2f9b5a2..65b8fc9 100644 --- a/src/esqlite3_nif.erl +++ b/src/esqlite3_nif.erl @@ -27,6 +27,7 @@ last_insert_rowid/1, changes/1, + exec/2, prepare/3, column_names/1, @@ -97,6 +98,16 @@ open(_Filename) -> close(_Db) -> erlang:nif_error(nif_library_not_loaded). +%% @doc Execute a sql statement +%% +-spec exec(Connection, Sql) -> ExecResult + when Connection :: esqlite3(), + Sql :: sql(), + ExecResult :: ok | {error, _}. +exec(_Connection, _Sql) -> + erlang:nif_error(nif_library_not_loaded). + + %% @doc Compile a sql statement. %% -spec prepare(Connection, Sql, PrepareFlags) -> PrepareResult diff --git a/test/esqlite_test.erl b/test/esqlite_test.erl index 2ecef5c..9073427 100644 --- a/test/esqlite_test.erl +++ b/test/esqlite_test.erl @@ -83,21 +83,21 @@ step_test() -> {ok, Stmt} = esqlite3:prepare(C, "select 1, 2, 3;" ), ?assertEqual([1,2,3], esqlite3:step(Stmt)), - ?assertEqual(done, esqlite3:step(Stmt)), + ?assertEqual('$done', esqlite3:step(Stmt)), %% After the done, the statement is reset and ?assertEqual([1,2,3], esqlite3:step(Stmt)), - ?assertEqual(done, esqlite3:step(Stmt)), + ?assertEqual('$done', esqlite3:step(Stmt)), ok. +iodata_test() -> + {ok, C} = esqlite3:open(":memory:"), -%iodata_test() -> -% {ok, C} = esqlite3:open(":memory:"), -% {error, no_iodata} = esqlite3:exec(1000, C), -% {error, no_iodata} = esqlite3:insert(1000, C), + ?assertError(badarg, esqlite3:exec(C, 1000)), + ?assertError(badarg, esqlite3:exec(C, 1000)), -% ok. + ok. open_multiple_same_databases_test() -> cleanup(), @@ -117,25 +117,25 @@ open_multiple_different_databases_test() -> cleanup(), ok. -%get_autocommit_test() -> -% {ok, Db} = esqlite3:open(":memory:"), -% ok = esqlite3:exec("CREATE TABLE test (id INTEGER PRIMARY KEY, val STRING);", Db), -% true = esqlite3:get_autocommit(Db), -% ok = esqlite3:exec("BEGIN;", Db), -% false = esqlite3:get_autocommit(Db), -% ok = esqlite3:exec("INSERT INTO test (val) VALUES ('this is a test');", Db), -% ok = esqlite3:exec("COMMIT;", Db), -% true = esqlite3:get_autocommit(Db), -% ok. +get_autocommit_test() -> + {ok, Db} = esqlite3:open(":memory:"), + ok = esqlite3:exec(Db, "CREATE TABLE test (id INTEGER PRIMARY KEY, val STRING);"), + true = esqlite3:get_autocommit(Db), + ok = esqlite3:exec(Db, "BEGIN;"), + false = esqlite3:get_autocommit(Db), + ok = esqlite3:exec(Db, "INSERT INTO test (val) VALUES ('this is a test');"), + ok = esqlite3:exec(Db, "COMMIT;"), + true = esqlite3:get_autocommit(Db), + ok. -%last_insert_rowid_test() -> -% {ok, Db} = esqlite3:open(":memory:"), -% ok = esqlite3:exec("CREATE TABLE test (id INTEGER PRIMARY KEY, val STRING);", Db), -% ok = esqlite3:exec("INSERT INTO test (val) VALUES ('this is a test');", Db), -% {ok, 1} = esqlite3:last_insert_rowid(Db), -% ok = esqlite3:exec("INSERT INTO test (val) VALUES ('this is another test');", Db), -% {ok, 2} = esqlite3:last_insert_rowid(Db), -% ok. +last_insert_rowid_test() -> + {ok, Db} = esqlite3:open(":memory:"), + ok = esqlite3:exec(Db, "CREATE TABLE test (id INTEGER PRIMARY KEY, val STRING);"), + ok = esqlite3:exec(Db, "INSERT INTO test (val) VALUES ('this is a test');"), + 1 = esqlite3:last_insert_rowid(Db), + ok = esqlite3:exec(Db, "INSERT INTO test (val) VALUES ('this is another test');"), + 2 = esqlite3:last_insert_rowid(Db), + ok. %update_hook_test() -> % {ok, Db} = esqlite3:open(":memory:"), @@ -149,44 +149,44 @@ open_multiple_different_databases_test() -> % ok = receive {delete, "test", 1} -> ok after 150 -> no_message end, % ok. -%simple_query_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, 1} = esqlite3:changes(Db), -% -% ok = esqlite3:exec("insert into test_table values('hello2', 11);", Db), -% {ok, 1} = esqlite3:changes(Db), -% ok = esqlite3:exec("insert into test_table values('hello3', 12);", Db), -% {ok, 1} = esqlite3:changes(Db), -% ok = esqlite3:exec("insert into test_table values('hello4', 13);", Db), -% {ok, 1} = esqlite3:changes(Db), -% ok = esqlite3:exec("commit;", Db), -% ok = esqlite3:exec("select * from test_table;", Db), -% -% ok = esqlite3:exec("delete from test_table;", Db), -% {ok, 4} = esqlite3:changes(Db), +simple_query_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, "insert into test_table values('hello1', 10);"), + ?assertEqual(1, esqlite3:changes(Db)), -% ok. + ok = esqlite3:exec(Db, "insert into test_table values('hello2', 11);"), + ?assertEqual(1, esqlite3:changes(Db)), + ok = esqlite3:exec(Db, "insert into test_table values('hello3', 12);"), + ?assertEqual(1, esqlite3:changes(Db)), + ok = esqlite3:exec(Db, "insert into test_table values('hello4', 13);"), + ?assertEqual(1, esqlite3:changes(Db)), + ok = esqlite3:exec(Db, "commit;"), + ok = esqlite3:exec(Db, "select * from test_table;"), + + ok = esqlite3:exec(Db, "delete from test_table;"), + ?assertEqual(4, esqlite3:changes(Db)), + + ok. + +prepare2_test() -> + {ok, Db} = esqlite3:open(":memory:"), + esqlite3:exec(Db, "begin;"), + esqlite3:exec(Db, "create table test_table(one varchar(10), two int);"), + {ok, Statement} = esqlite3:prepare(Db, "insert into test_table values('one', 2)"), + + '$done' = esqlite3:step(Statement), + 1 = esqlite3:changes(Db), + + ok = esqlite3:exec(Db, "insert into test_table values('hello4', 13);"), -%prepare_test() -> -% {ok, Db} = esqlite3:open(":memory:"), -% esqlite3:exec("begin;", Db), -% esqlite3:exec("create table test_table(one varchar(10), two int);", Db), -% {ok, Statement} = esqlite3:prepare("insert into test_table values('one', 2)", Db), -% -% '$done' = esqlite3:step(Statement), -% {ok, 1} = esqlite3:changes(Db), -% -% ok = esqlite3:exec("insert into test_table values('hello4', 13);", Db), -% %% Check if the values are there. -% [{<<"one">>, 2}, {<<"hello4">>, 13}] = esqlite3:q("select * from test_table order by two", Db), -% esqlite3:exec("commit;", Db), -% esqlite3:close(Db), + %% [{<<"one">>, 2}, {<<"hello4">>, 13}] = esqlite3:q(Db, "select * from test_table order by two"), + esqlite3:exec(Db, "commit;"), + esqlite3:close(Db), -% ok. + ok. %bind_test() -> % {ok, Db} = esqlite3:open(":memory:"), @@ -339,41 +339,43 @@ open_multiple_different_databases_test() -> % {} = esqlite3:column_types(Stmt2), % % ok. -% -%nil_column_types_test() -> -% {ok, Db} = esqlite3:open(":memory:"), -% ok = esqlite3:exec("begin;", Db), -% ok = esqlite3:exec("create table t1(c1 variant);", Db), -% ok = esqlite3:exec("commit;", Db), -% {ok, Stmt} = esqlite3:prepare("select c1 + 1, c1 from t1", Db), -% {nil, variant} = esqlite3:column_types(Stmt), -% ok. +nil_column_decltypes_test() -> + {ok, Db} = esqlite3:open(":memory:"), + ok = esqlite3:exec(Db, "begin;"), + ok = esqlite3:exec(Db, "create table t1(c1 variant);"), + ok = esqlite3:exec(Db, "commit;"), -%reset_test() -> -% {ok, Db} = esqlite3:open(":memory:"), + {ok, Stmt} = esqlite3:prepare(Db, "select c1 + 1, c1 from t1"), + ?assertEqual([undefined, <<"variant">>], esqlite3:column_decltypes(Stmt)), + + ok. + +reset_test() -> + {ok, Db} = esqlite3:open(":memory:"), + + {ok, Stmt} = esqlite3:prepare(Db, "select * from (values (1), (2));"), + [1] = esqlite3:step(Stmt), + + ok = esqlite3:reset(Stmt), + + [1] = esqlite3:step(Stmt), + [2] = esqlite3:step(Stmt), + '$done' = esqlite3:step(Stmt), + + % After a done the statement is automatically reset. + [1] = esqlite3:step(Stmt), + + % Calling reset multiple times... + ok = esqlite3:reset(Stmt), + ok = esqlite3:reset(Stmt), + ok = esqlite3:reset(Stmt), + ok = esqlite3:reset(Stmt), -% {ok, Stmt} = esqlite3:prepare("select * from (values (1), (2));", Db), -% {row, {1}} = esqlite3:step(Stmt), -% -% ok = esqlite3:reset(Stmt), -% {row, {1}} = esqlite3:step(Stmt), -% {row, {2}} = esqlite3:step(Stmt), -% '$done' = esqlite3:step(Stmt), -% -% % After a done the statement is automatically reset. -% {row, {1}} = esqlite3:step(Stmt), -% -% % Calling reset multiple times... -% ok = esqlite3:reset(Stmt), -% ok = esqlite3:reset(Stmt), -% ok = esqlite3:reset(Stmt), -% ok = esqlite3:reset(Stmt), -% % The statement should still be reset. -% {row, {1}} = esqlite3:step(Stmt), -% -% ok. + [1] = esqlite3:step(Stmt), + + ok. % %foreach_test() -> % {ok, Db} = esqlite3:open(":memory:"), @@ -583,20 +585,20 @@ open_multiple_different_databases_test() -> % ok. -%sqlite_version_test() -> -% {ok, Db} = esqlite3:open(":memory:"), -% {ok, Stmt} = esqlite3:prepare("select sqlite_version() as sqlite_version;", Db), -% {sqlite_version} = esqlite3:column_names(Stmt), -%% ?assertEqual({row, {<<"3.38.0">>}}, esqlite3:step(Stmt)), -% ok. +sqlite_version_test() -> + {ok, Db} = esqlite3:open(":memory:"), + {ok, Stmt} = esqlite3:prepare(Db, "select sqlite_version() as sqlite_version;"), + [<<"sqlite_version">>] = esqlite3:column_names(Stmt), + ?assertEqual([<<"3.38.0">>], esqlite3:step(Stmt)), + ok. -%sqlite_source_id_test() -> -% {ok, Db} = esqlite3:open(":memory:"), -% {ok, Stmt} = esqlite3:prepare("select sqlite_source_id() as sqlite_source_id;", Db), -% {sqlite_source_id} = esqlite3:column_names(Stmt), -% ?assertEqual({row, {<<"2022-02-22 18:58:40 40fa792d359f84c3b9e9d6623743e1a59826274e221df1bde8f47086968a1bab">>}}, -% esqlite3:step(Stmt)), -% ok. +sqlite_source_id_test() -> + {ok, Db} = esqlite3:open(":memory:"), + {ok, Stmt} = esqlite3:prepare(Db, "select sqlite_source_id() as sqlite_source_id;"), + [<<"sqlite_source_id">>] = esqlite3:column_names(Stmt), + ?assertEqual([<<"2022-02-22 18:58:40 40fa792d359f84c3b9e9d6623743e1a59826274e221df1bde8f47086968a1bab">>], + esqlite3:step(Stmt)), + ok. %interrupt_on_timeout_test() -> % {ok, Db} = esqlite3:open(":memory:"),