Skip to content

Commit 9f6426c

Browse files
committed
Adds Sqlite3.release/2 for prepared statements
1 parent ab64f59 commit 9f6426c

File tree

5 files changed

+64
-5
lines changed

5 files changed

+64
-5
lines changed

c_src/sqlite3_nif.c

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -712,6 +712,35 @@ exqlite_deserialize(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
712712
return make_atom(env, "ok");
713713
}
714714

715+
static ERL_NIF_TERM
716+
exqlite_release(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
717+
{
718+
assert(env);
719+
720+
statement_t* statement = NULL;
721+
connection_t* conn = NULL;
722+
723+
if (argc != 2) {
724+
return enif_make_badarg(env);
725+
}
726+
727+
if (!enif_get_resource(env, argv[0], connection_type, (void**)&conn)) {
728+
return make_error_tuple(env, "invalid_connection");
729+
}
730+
731+
if (!enif_get_resource(env, argv[1], statement_type, (void**)&statement)) {
732+
return make_error_tuple(env, "invalid_statement");
733+
}
734+
735+
if (statement->statement) {
736+
sqlite3_finalize(statement->statement);
737+
statement->statement = NULL;
738+
}
739+
enif_release_resource(statement);
740+
741+
return make_atom(env, "ok");
742+
}
743+
715744
static void
716745
connection_type_destructor(ErlNifEnv* env, void* arg)
717746
{
@@ -788,6 +817,7 @@ static ErlNifFunc nif_funcs[] = {
788817
{"transaction_status", 1, exqlite_transaction_status, ERL_NIF_DIRTY_JOB_IO_BOUND},
789818
{"serialize", 2, exqlite_serialize, ERL_NIF_DIRTY_JOB_IO_BOUND},
790819
{"deserialize", 3, exqlite_deserialize, ERL_NIF_DIRTY_JOB_IO_BOUND},
820+
{"release", 2, exqlite_release, ERL_NIF_DIRTY_JOB_IO_BOUND},
791821
};
792822

793823
ERL_NIF_INIT(Elixir.Exqlite.Sqlite3NIF, nif_funcs, on_load, NULL, NULL, NULL)

lib/exqlite/connection.ex

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,8 @@ defmodule Exqlite.Connection do
259259
This callback is called in the client process.
260260
"""
261261
@impl true
262-
def handle_close(_query, _opts, state) do
262+
def handle_close(query, _opts, state) do
263+
Sqlite3.release(state.db, query.ref)
263264
{:ok, nil, state}
264265
end
265266

@@ -274,10 +275,8 @@ defmodule Exqlite.Connection do
274275
end
275276

276277
@impl true
277-
def handle_deallocate(%Query{} = _query, _cursor, _opts, state) do
278-
# We actually don't need to do anything about the cursor. Since it is a
279-
# prepared statement, it will be garbage collected by erlang when it loses
280-
# references.
278+
def handle_deallocate(%Query{} = query, _cursor, _opts, state) do
279+
Sqlite3.release(state.db, query.ref)
281280
{:ok, nil, state}
282281
end
283282

lib/exqlite/sqlite3.ex

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,15 @@ defmodule Exqlite.Sqlite3 do
166166
Sqlite3NIF.deserialize(conn, String.to_charlist(database), serialized)
167167
end
168168

169+
@doc """
170+
Once finished with the prepared statement, call this to release the underlying
171+
resources.
172+
"""
173+
@spec release(db(), statement()) :: :ok | {:error, reason()}
174+
def release(conn, statement) do
175+
Sqlite3NIF.release(conn, statement)
176+
end
177+
169178
defp convert(%Date{} = val), do: Date.to_iso8601(val)
170179
defp convert(%DateTime{} = val), do: DateTime.to_iso8601(val)
171180
defp convert(%Time{} = val), do: Time.to_iso8601(val)

lib/exqlite/sqlite3_nif.ex

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,5 +56,8 @@ defmodule Exqlite.Sqlite3NIF do
5656
@spec deserialize(db(), String.Chars.t(), binary()) :: :ok | {:error, reason()}
5757
def deserialize(_conn, _database, _serialized), do: :erlang.nif_error(:not_loaded)
5858

59+
@spec release(db(), statement()) :: :ok | {:error, reason()}
60+
def release(_conn, _statement), do: :erlang.nif_error(:not_loaded)
61+
5962
# TODO: add statement inspection tooling https://sqlite.org/c3ref/expanded_sql.html
6063
end

test/exqlite/connection_test.exs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,4 +188,22 @@ defmodule Exqlite.ConnectionTest do
188188
assert {:ok, conn} == Connection.ping(conn)
189189
end
190190
end
191+
192+
describe ".handle_close/3" do
193+
test "releases the underlying prepared statement" do
194+
{:ok, conn} = Connection.connect(database: :memory)
195+
196+
{:ok, query, _result, conn} =
197+
%Query{statement: "create table users (id integer primary key, name text)"}
198+
|> Connection.handle_execute([], [], conn)
199+
200+
assert {:ok, nil, conn} == Connection.handle_close(query, [], conn)
201+
202+
{:ok, query, conn} =
203+
%Query{statement: "select * from users where id < ?"}
204+
|> Connection.handle_prepare([], conn)
205+
206+
assert {:ok, nil, conn} == Connection.handle_close(query, [], conn)
207+
end
208+
end
191209
end

0 commit comments

Comments
 (0)