Skip to content

Commit f1a26dc

Browse files
authored
Raise exception when statement is prepared for a different connection than executed (#354)
For functions `step`, `multi_step` and `columns` check if the statement was prepared for the same connection as the one on which they are run. If not, raise `ArgumentError` explaining the mismatch. Closes #353
1 parent e94f2d6 commit f1a26dc

3 files changed

Lines changed: 79 additions & 2 deletions

File tree

c_src/sqlite3_nif.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -971,6 +971,10 @@ exqlite_multi_step(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
971971
return make_error_tuple(env, am_invalid_chunk_size);
972972
}
973973

974+
if (conn != statement->conn) {
975+
return enif_raise_exception(env, enif_make_atom(env, "cross_connection_call"));
976+
}
977+
974978
connection_acquire_lock(conn);
975979
connection_stash_caller(conn, env);
976980

@@ -1043,6 +1047,10 @@ exqlite_step(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
10431047
return make_error_tuple(env, am_invalid_statement);
10441048
}
10451049

1050+
if (conn != statement->conn) {
1051+
return enif_raise_exception(env, enif_make_atom(env, "cross_connection_call"));
1052+
}
1053+
10461054
connection_acquire_lock(conn);
10471055
connection_stash_caller(conn, env);
10481056

@@ -1104,6 +1112,10 @@ exqlite_columns(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
11041112
return make_error_tuple(env, am_invalid_statement);
11051113
}
11061114

1115+
if (conn != statement->conn) {
1116+
return enif_raise_exception(env, enif_make_atom(env, "cross_connection_call"));
1117+
}
1118+
11071119
statement_acquire_lock(statement);
11081120
if (statement->statement == NULL) {
11091121
statement_release_lock(statement);

lib/exqlite/sqlite3.ex

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -324,10 +324,19 @@ defmodule Exqlite.Sqlite3 do
324324
end
325325

326326
@spec columns(db(), statement()) :: {:ok, [binary()]} | {:error, reason()}
327-
def columns(conn, statement), do: Sqlite3NIF.columns(conn, statement)
327+
def columns(conn, statement) do
328+
Sqlite3NIF.columns(conn, statement)
329+
rescue
330+
e -> handle_nif_exception(e, __STACKTRACE__)
331+
end
328332

329333
@spec step(db(), statement()) :: :done | :busy | {:row, row()} | {:error, reason()}
330-
def step(conn, statement), do: Sqlite3NIF.step(conn, statement)
334+
def step(conn, statement) do
335+
Sqlite3NIF.step(conn, statement)
336+
rescue
337+
e ->
338+
handle_nif_exception(e, __STACKTRACE__)
339+
end
331340

332341
@spec multi_step(db(), statement()) ::
333342
:busy | {:rows, [row()]} | {:done, [row()]} | {:error, reason()}
@@ -352,6 +361,9 @@ defmodule Exqlite.Sqlite3 do
352361
{:done, rows} ->
353362
{:done, Enum.reverse(rows)}
354363
end
364+
rescue
365+
e ->
366+
handle_nif_exception(e, __STACKTRACE__)
355367
end
356368

357369
@spec last_insert_rowid(db()) :: {:ok, integer()}
@@ -652,4 +664,12 @@ defmodule Exqlite.Sqlite3 do
652664
defp type_extensions do
653665
Application.get_env(:exqlite, :type_extensions)
654666
end
667+
668+
defp handle_nif_exception(%ErlangError{original: :cross_connection_call}, _) do
669+
raise(ArgumentError,
670+
message: "Statement was prepared for a different connection, which is illegal"
671+
)
672+
end
673+
674+
defp handle_nif_exception(e, stacktrace), do: reraise(e, stacktrace)
655675
end

test/exqlite/sqlite3_test.exs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,21 @@ defmodule Exqlite.Sqlite3Test do
550550
{:ok, statement} = Sqlite3.prepare(conn, "select * from test")
551551
assert {:ok, ["👋", "✍️"]} = Sqlite3.columns(conn, statement)
552552
end
553+
554+
test "raises exception when statement was prepared for another connection" do
555+
{:ok, connection_a} = Sqlite3.open(":memory:")
556+
{:ok, connection_b} = Sqlite3.open(":memory:")
557+
558+
{:ok, statement_b} = Sqlite3.prepare(connection_b, "select 'connection b'")
559+
560+
assert_raise(
561+
ArgumentError,
562+
"Statement was prepared for a different connection, which is illegal",
563+
fn ->
564+
Sqlite3.columns(connection_a, statement_b)
565+
end
566+
)
567+
end
553568
end
554569

555570
describe ".step/2" do
@@ -613,6 +628,21 @@ defmodule Exqlite.Sqlite3Test do
613628
"unsupported type: %ArgumentError{message: \"argument error\"}",
614629
fn -> Sqlite3.bind(statement, [%ArgumentError{}]) end
615630
end
631+
632+
test "raises exception when statement was prepared for another connection" do
633+
{:ok, connection_a} = Sqlite3.open(":memory:")
634+
{:ok, connection_b} = Sqlite3.open(":memory:")
635+
636+
{:ok, statement_b} = Sqlite3.prepare(connection_b, "select 'connection b'")
637+
638+
assert_raise(
639+
ArgumentError,
640+
"Statement was prepared for a different connection, which is illegal",
641+
fn ->
642+
Sqlite3.step(connection_a, statement_b)
643+
end
644+
)
645+
end
616646
end
617647

618648
describe ".multi_step/3" do
@@ -638,6 +668,21 @@ defmodule Exqlite.Sqlite3Test do
638668
{:done, rows} = Sqlite3.multi_step(conn, statement, 4)
639669
assert rows == [[5, "five"], [6, "six"]]
640670
end
671+
672+
test "raises exception when statement was prepared for another connection" do
673+
{:ok, connection_a} = Sqlite3.open(":memory:")
674+
{:ok, connection_b} = Sqlite3.open(":memory:")
675+
676+
{:ok, statement_b} = Sqlite3.prepare(connection_b, "select 'connection b'")
677+
678+
assert_raise(
679+
ArgumentError,
680+
"Statement was prepared for a different connection, which is illegal",
681+
fn ->
682+
Sqlite3.multi_step(connection_a, statement_b)
683+
end
684+
)
685+
end
641686
end
642687

643688
describe ".multi_step/2" do

0 commit comments

Comments
 (0)