Skip to content

Commit fe3bf20

Browse files
committed
add error_info/1
1 parent 5cf7bc5 commit fe3bf20

File tree

4 files changed

+79
-29
lines changed

4 files changed

+79
-29
lines changed

c_src/sqlite3_nif.c

+35
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,40 @@ exqlite_changes(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
334334
return make_ok_tuple(env, enif_make_int(env, changes));
335335
}
336336

337+
static ERL_NIF_TERM
338+
exqlite_error_info(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
339+
{
340+
assert(env);
341+
342+
connection_t* conn = NULL;
343+
344+
if (argc != 1) {
345+
return enif_make_badarg(env);
346+
}
347+
348+
if (!enif_get_resource(env, argv[0], connection_type, (void**)&conn)) {
349+
return make_error_tuple(env, "invalid_connection");
350+
}
351+
352+
if (conn->db == NULL) {
353+
return make_error_tuple(env, "connection_closed");
354+
}
355+
356+
int code = sqlite3_errcode(conn->db);
357+
int extended_code = sqlite3_extended_errcode(conn->db);
358+
const char *errstr = sqlite3_errstr(extended_code);
359+
const char *errmsg = sqlite3_errmsg(conn->db);
360+
361+
ERL_NIF_TERM info = enif_make_new_map(env);
362+
enif_make_map_put(env, info, make_atom(env, "errcode"), enif_make_int(env, code), &info);
363+
enif_make_map_put(env, info, make_atom(env, "extended_errcode"), enif_make_int(env, extended_code), &info);
364+
enif_make_map_put(env, info, make_atom(env, "errstr"), make_binary(env, errstr, strlen(errstr)), &info);
365+
enif_make_map_put(env, info, make_atom(env, "errmsg"), make_binary(env, errmsg, strlen(errmsg)), &info);
366+
enif_make_map_put(env, info, make_atom(env, "error_offset"), enif_make_int(env, sqlite3_error_offset(conn->db)), &info);
367+
368+
return info;
369+
}
370+
337371
///
338372
/// @brief Prepares an Sqlite3 statement for execution
339373
///
@@ -1115,6 +1149,7 @@ static ErlNifFunc nif_funcs[] = {
11151149
{"enable_load_extension", 2, exqlite_enable_load_extension, ERL_NIF_DIRTY_JOB_IO_BOUND},
11161150
{"set_update_hook", 2, exqlite_set_update_hook, ERL_NIF_DIRTY_JOB_IO_BOUND},
11171151
{"set_log_hook", 1, exqlite_set_log_hook, ERL_NIF_DIRTY_JOB_IO_BOUND},
1152+
{"error_info", 1, exqlite_error_info, ERL_NIF_DIRTY_JOB_IO_BOUND}
11181153
};
11191154

11201155
ERL_NIF_INIT(Elixir.Exqlite.Nif, nif_funcs, on_load, NULL, NULL, on_unload)

lib/exqlite/nif.ex

+35-20
Original file line numberDiff line numberDiff line change
@@ -4,66 +4,81 @@ defmodule Exqlite.Nif do
44
@compile {:autoload, false}
55
@on_load {:load_nif, 0}
66

7-
# TODO it's not just a string
8-
@type error :: String.t() | Keyword.t()
7+
@type usage_error ::
8+
{:error,
9+
reason ::
10+
atom
11+
| {:wrong_type, term}
12+
| Keyword.t()}
13+
14+
@type sqlite_error :: {:error, rc :: integer, msg :: String.t()}
15+
@type error :: usage_error | sqlite_error
916

1017
def load_nif do
1118
path = :filename.join(:code.priv_dir(:exqlite), ~c"sqlite3_nif")
1219
:erlang.load_nif(path, 0)
1320
end
1421

15-
@spec open(charlist, [Exqlite.open_flag()]) :: {:ok, Exqlite.conn()} | {:error, error}
22+
@spec open(charlist, [Exqlite.open_flag()]) :: {:ok, Exqlite.conn()} | error
1623
def open(_path, _flags), do: :erlang.nif_error(:not_loaded)
1724

18-
@spec close(Exqlite.conn()) :: :ok | {:error, error}
25+
@spec close(Exqlite.conn()) :: :ok | error
1926
def close(_conn), do: :erlang.nif_error(:not_loaded)
2027

21-
@spec execute(Exqlite.conn(), iodata) :: :ok | {:error, error}
28+
@spec execute(Exqlite.conn(), iodata) :: :ok | error
2229
def execute(_conn, _sql), do: :erlang.nif_error(:not_loaded)
2330

24-
@spec changes(Exqlite.conn()) :: {:ok, non_neg_integer} | {:error, error}
31+
@spec changes(Exqlite.conn()) :: {:ok, non_neg_integer} | error
2532
def changes(_conn), do: :erlang.nif_error(:not_loaded)
2633

27-
@spec prepare(Exqlite.conn(), iodata) :: {:ok, Exqlite.stmt()} | {:error, error}
34+
@spec prepare(Exqlite.conn(), iodata) :: {:ok, Exqlite.stmt()} | error
2835
def prepare(_conn, _sql), do: :erlang.nif_error(:not_loaded)
2936

30-
@spec bind(Exqlite.conn(), Exqlite.stmt(), [Exqlite.bind_arg()]) ::
31-
:ok | {:error, error}
37+
@spec bind(Exqlite.conn(), Exqlite.stmt(), [Exqlite.bind_arg()]) :: :ok | error
3238
def bind(_conn, _stmt, _args), do: :erlang.nif_error(:not_loaded)
3339

3440
@spec step(Exqlite.conn(), Exqlite.stmt()) ::
35-
{:row, Exqlite.returned_row()} | :done | {:error, error}
41+
{:row, Exqlite.returned_row()} | :done | error
3642
def step(_conn, _stmt), do: :erlang.nif_error(:not_loaded)
3743

3844
@spec multi_step(Exqlite.conn(), Exqlite.stmt(), non_neg_integer) ::
39-
{:rows | :done, [Exqlite.returned_row()]} | {:error, error}
45+
{:rows | :done, [Exqlite.returned_row()]} | error
4046
def multi_step(_conn, _stmt, _max_rows), do: :erlang.nif_error(:not_loaded)
4147

42-
@spec columns(Exqlite.conn(), Exqlite.stmt()) :: {:ok, [String.t()]} | {:error, error}
48+
@spec columns(Exqlite.conn(), Exqlite.stmt()) :: {:ok, [String.t()]} | error
4349
def columns(_conn, _stmt), do: :erlang.nif_error(:not_loaded)
4450

45-
@spec last_insert_rowid(Exqlite.conn()) :: {:ok, non_neg_integer} | {:error, error}
51+
@spec last_insert_rowid(Exqlite.conn()) :: {:ok, non_neg_integer} | error
4652
def last_insert_rowid(_conn), do: :erlang.nif_error(:not_loaded)
4753

4854
@spec transaction_status(Exqlite.conn()) ::
49-
{:ok, :transaction | :idle} | {:error, error}
55+
{:ok, :transaction | :idle} | error
5056
def transaction_status(_conn), do: :erlang.nif_error(:not_loaded)
5157

52-
@spec serialize(Exqlite.conn(), charlist) :: {:ok, binary} | {:error, error}
58+
@spec serialize(Exqlite.conn(), charlist) :: {:ok, binary} | error
5359
def serialize(_conn, _database), do: :erlang.nif_error(:not_loaded)
5460

55-
@spec deserialize(Exqlite.conn(), charlist, iodata) :: :ok | {:error, error}
61+
@spec deserialize(Exqlite.conn(), charlist, iodata) :: :ok | error
5662
def deserialize(_conn, _database, _serialized), do: :erlang.nif_error(:not_loaded)
5763

58-
@spec release(Exqlite.stmt()) :: :ok | {:error, error}
64+
@spec release(Exqlite.stmt()) :: :ok | error
5965
def release(_stmt), do: :erlang.nif_error(:not_loaded)
6066

61-
@spec enable_load_extension(Exqlite.conn(), integer) :: :ok | {:error, error}
67+
@spec enable_load_extension(Exqlite.conn(), integer) :: :ok | error
6268
def enable_load_extension(_conn, _flag), do: :erlang.nif_error(:not_loaded)
6369

64-
@spec set_update_hook(Exqlite.conn(), pid) :: :ok | {:error, error}
70+
@spec set_update_hook(Exqlite.conn(), pid) :: :ok | error
6571
def set_update_hook(_conn, _pid), do: :erlang.nif_error(:not_loaded)
6672

67-
@spec set_log_hook(pid) :: :ok | {:error, error}
73+
@spec set_log_hook(pid) :: :ok | error
6874
def set_log_hook(_pid), do: :erlang.nif_error(:not_loaded)
75+
76+
@spec error_info(Exqlite.conn()) :: %{
77+
errcode: integer,
78+
extended_errcode: integer,
79+
errstr: String.t(),
80+
errmsg: String.t(),
81+
error_offset: integer
82+
}
83+
def error_info(_conn), do: :erlang.nif_error(:not_loaded)
6984
end

lib/exqlite/usage_error.ex

+1-9
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,5 @@ defmodule Exqlite.UsageError do
1010
"""
1111

1212
defexception [:message]
13-
14-
@type t :: %__MODULE__{
15-
message:
16-
String.t()
17-
| :invalid_statement
18-
| :invalid_connection
19-
| :arguments_wrong_length
20-
| {:wrong_type, term}
21-
}
13+
@type t :: %__MODULE__{message: String.t()}
2214
end

test/exqlite/extensions_test.exs

+8
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@ defmodule Exqlite.ExtensionsTest do
2626
"select load_extension(?)",
2727
[ExSqlean.path_for("re")]
2828
)
29+
30+
assert Exqlite.Nif.error_info(conn) == %{
31+
errcode: 1,
32+
extended_errcode: 1,
33+
errstr: "SQL logic error",
34+
errmsg: "not authorized",
35+
error_offset: -1
36+
}
2937
end
3038

3139
test "works for 're' (regex)", %{conn: conn} do

0 commit comments

Comments
 (0)