diff --git a/c_src/esqlite3_nif.c b/c_src/esqlite3_nif.c index a91de62..69cec18 100644 --- a/c_src/esqlite3_nif.c +++ b/c_src/esqlite3_nif.c @@ -1196,7 +1196,7 @@ static ErlNifFunc nif_funcs[] = { */ {"set_update_hook", 2, esqlite_set_update_hook}, - {"exec", 2, esqlite_exec}, + {"exec", 2, esqlite_exec, ERL_NIF_DIRTY_JOB_IO_BOUND}, {"prepare", 3, esqlite_prepare}, {"column_names", 1, esqlite_column_names}, @@ -1220,8 +1220,8 @@ static ErlNifFunc nif_funcs[] = { {"backup_init", 4, esqlite_backup_init, ERL_NIF_DIRTY_JOB_IO_BOUND}, {"backup_remaining", 1, esqlite_backup_remaining}, {"backup_pagecount", 1, esqlite_backup_pagecount}, - {"backup_step", 2, esqlite_backup_step}, - {"backup_finish", 1, esqlite_backup_finish}, + {"backup_step", 2, esqlite_backup_step, ERL_NIF_DIRTY_JOB_IO_BOUND}, + {"backup_finish", 1, esqlite_backup_finish, ERL_NIF_DIRTY_JOB_IO_BOUND}, {"memory_stats", 1, esqlite_memory_stats}, {"status", 2, esqlite_status} diff --git a/src/esqlite3.erl b/src/esqlite3.erl index fafa4e7..f226c26 100644 --- a/src/esqlite3.erl +++ b/src/esqlite3.erl @@ -51,6 +51,7 @@ step/1, reset/1, + interrupt/1, q/2, q/3, @@ -146,6 +147,11 @@ close(#esqlite3{db=Connection}) -> error_info(#esqlite3{db=Connection}) -> esqlite3_nif:error_info(Connection). +-spec interrupt(Connection) -> Result + when Connection :: esqlite3(), + Result:: ok | {error, _}. +interrupt(#esqlite3{db=Db}) -> + esqlite3_nif:interrupt(Db). %% @doc Subscribe to database notifications. When rows are inserted deleted %% or updates, the process will receive messages: @@ -385,6 +391,7 @@ step(#esqlite3_stmt{stmt=Stmt}) -> reset(#esqlite3_stmt{stmt=Stmt}) -> esqlite3_nif:reset(Stmt). + %% @doc Return the column names of the prepared statement. %% -spec column_names(Statement) -> Names diff --git a/test/esqlite_test.erl b/test/esqlite_test.erl index 3c65466..85d2510 100644 --- a/test/esqlite_test.erl +++ b/test/esqlite_test.erl @@ -497,26 +497,37 @@ sqlite_source_id_test() -> esqlite3:step(Stmt)), ok. -%interrupt_on_timeout_test() -> -% {ok, Db} = esqlite3:open(":memory:"), -% CreateTableQuery = "CREATE TABLE all_numbers_in_the_world (number int not null);", -% ok = esqlite3:exec(CreateTableQuery, Db), -% VeryLongQuery = " -% WITH RECURSIVE -% for(i) AS (VALUES(1) UNION ALL SELECT i+1 FROM for WHERE i < 10000000) -% INSERT INTO all_numbers_in_the_world SELECT i FROM for; -% ", -% try -% ok = esqlite3:exec(VeryLongQuery, [], Db, 10) -% catch -% {error, timeout, _} -> -% ?assertMatch([{0}], esqlite3:q("SELECT COUNT(*) FROM all_numbers_in_the_world", Db)), -% %% There is now a stale answer, because the recursive query was interrupted. -% receive -% {esqlite3, _, {error, {interrupt, "interrupted"}}} -> -% ok -% end -% end. +interrupt_on_timeout_test() -> + {ok, Db1} = esqlite3:open("file:memdb1?mode=memory&cache=shared"), + % {ok, Db2} = esqlite3:open("file:memdb1?mode=memory&cache=shared"), + + Self = self(), + + F = fun() -> + CreateTableQuery = "CREATE TABLE all_numbers_in_the_world (number int not null);", + ok = esqlite3:exec(Db1, CreateTableQuery), + VeryLongQuery = " + WITH RECURSIVE + for(i) AS (VALUES(1) UNION ALL SELECT i+1 FROM for WHERE i < 10000000) + INSERT INTO all_numbers_in_the_world SELECT i FROM for; + ", + + %% The query was interrupted + Self ! {msg, esqlite3:exec(Db1, VeryLongQuery)} + end, + + spawn(F), + timer:sleep(10), + ok = esqlite3:interrupt(Db1), + + %% The query was interrupted, so no result + ?assertEqual([[0]], esqlite3:q(Db1, "SELECT COUNT(*) FROM all_numbers_in_the_world")), + + %% We should have gotten an interrupt error. + Msg = receive {msg, M} -> M end, + ?assertEqual({error, 9}, Msg), + + ok. garbage_collect_test() -> F = fun() ->