Add specs docs and refactor (#70)
* Changed old style specs to more up to date specs * Added documentation * Changed all @spec to -spec's * Bump version number
This commit is contained in:
committed by
GitHub
parent
df3be87201
commit
852c3286cf
@@ -425,7 +425,11 @@ bind_cell(ErlNifEnv *env, const ERL_NIF_TERM cell, sqlite3_stmt *stmt, unsigned
|
|||||||
return sqlite3_bind_double(stmt, i, the_double);
|
return sqlite3_bind_double(stmt, i, the_double);
|
||||||
|
|
||||||
if(enif_get_atom(env, cell, the_atom, sizeof(the_atom), ERL_NIF_LATIN1)) {
|
if(enif_get_atom(env, cell, the_atom, sizeof(the_atom), ERL_NIF_LATIN1)) {
|
||||||
if(strcmp("undefined", the_atom) == 0) {
|
if(strncmp("undefined", the_atom, strlen("undefined")) == 0) {
|
||||||
|
return sqlite3_bind_null(stmt, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(strncmp("null", the_atom, strlen("null")) == 0) {
|
||||||
return sqlite3_bind_null(stmt, i);
|
return sqlite3_bind_null(stmt, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{application, esqlite,
|
{application, esqlite,
|
||||||
[
|
[
|
||||||
{description, "sqlite nif interface"},
|
{description, "sqlite nif interface"},
|
||||||
{vsn, "0.5.0"},
|
{vsn, "0.5.1"},
|
||||||
{modules, [esqlite3, esqlite3_nif]},
|
{modules, [esqlite3, esqlite3_nif]},
|
||||||
{registered, []},
|
{registered, []},
|
||||||
{licenses, ["Apache"]},
|
{licenses, ["Apache"]},
|
||||||
|
|||||||
195
src/esqlite3.erl
195
src/esqlite3.erl
@@ -38,29 +38,59 @@
|
|||||||
fetchall/3,
|
fetchall/3,
|
||||||
column_names/1, column_names/2,
|
column_names/1, column_names/2,
|
||||||
column_types/1, column_types/2,
|
column_types/1, column_types/2,
|
||||||
close/1, close/2]).
|
close/1, close/2,
|
||||||
|
flush/0
|
||||||
|
]).
|
||||||
|
|
||||||
-export([q/2, q/3, q/4, map/3, map/4, foreach/3, foreach/4]).
|
-export([q/2, q/3, q/4, map/3, map/4, foreach/3, foreach/4]).
|
||||||
|
|
||||||
-define(DEFAULT_TIMEOUT, 5000).
|
-define(DEFAULT_TIMEOUT, infinity).
|
||||||
-define(DEFAULT_CHUNK_SIZE, 5000).
|
-define(DEFAULT_CHUNK_SIZE, 5000).
|
||||||
|
|
||||||
%%
|
%%
|
||||||
|
|
||||||
-type connection() :: {connection, reference(), term()}.
|
-type connection() :: {connection, reference(), term()}.
|
||||||
-type statement() :: {statement, term(), connection()}.
|
-type statement() :: {statement, term(), connection()}.
|
||||||
-type sql() :: iodata().
|
-type sql() :: iodata().
|
||||||
|
|
||||||
|
%% erlang -> sqlite type conversions
|
||||||
|
%%
|
||||||
|
%% 'undefined' -> null
|
||||||
|
%% 'null' -> null
|
||||||
|
%% atom() -> text
|
||||||
|
%% int() -> int or int64
|
||||||
|
%% float() -> double
|
||||||
|
%% string() -> text
|
||||||
|
%% binary() -> text
|
||||||
|
|
||||||
|
-type rowid() :: integer().
|
||||||
|
-type row() :: tuple(). % tuple of cell_type
|
||||||
|
-type cell_type() :: undefined | integer() | binary() | float().
|
||||||
|
|
||||||
|
-export_types([connection/0, statement/0, sql/0, row/0, row_id/0, cell_type/0]).
|
||||||
|
|
||||||
%% @doc Opens a sqlite3 database mentioned in Filename.
|
%% @doc Opens a sqlite3 database mentioned in Filename.
|
||||||
%%
|
%%
|
||||||
-spec open(FileName) -> {ok, connection()} | {error, _} when
|
%% The standard supplied sqlite3 library supports uri filenames, which makes
|
||||||
FileName :: string().
|
%% it possible to open the connection to the database in read-only mode. More
|
||||||
|
%% information about this can be found here: [https://sqlite.org/uri.html]
|
||||||
|
%%
|
||||||
|
%% Example:
|
||||||
|
%%
|
||||||
|
%% ```open("file:data.db")'''
|
||||||
|
%% Opens "data.db" in the current working directory
|
||||||
|
%% ```open("file:data.db?mode=ro&cache=private")'''
|
||||||
|
%% Opens "data.db" in read only mode with a private cache
|
||||||
|
%% ```open("file:memdb1?mode=memory&cache=shared")'''
|
||||||
|
%% Opens a shared memory database named memdb1 with a shared cache.
|
||||||
|
%%
|
||||||
|
-spec open(string()) -> {ok, connection()} | {error, _}.
|
||||||
open(Filename) ->
|
open(Filename) ->
|
||||||
open(Filename, ?DEFAULT_TIMEOUT).
|
open(Filename, ?DEFAULT_TIMEOUT).
|
||||||
|
|
||||||
%% @doc Open a database connection
|
%% @doc Like open/1, but with an additional timeout.
|
||||||
%%
|
%%
|
||||||
-spec open(Filename, timeout()) -> {ok, connection()} | {error, _} when
|
-spec open(string(), timeout()) -> {ok, connection()} | {error, _}.
|
||||||
Filename :: string().
|
|
||||||
open(Filename, Timeout) ->
|
open(Filename, Timeout) ->
|
||||||
{ok, Connection} = esqlite3_nif:start(),
|
{ok, Connection} = esqlite3_nif:start(),
|
||||||
|
|
||||||
@@ -73,16 +103,20 @@ open(Filename, Timeout) ->
|
|||||||
Error
|
Error
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% @doc Subscribe to database notifications
|
%% @doc Subscribe to database notifications. When rows are inserted deleted
|
||||||
%% Messages will come in the shape {action, table, id}
|
%% or updates, the process will receive messages:
|
||||||
%% Where action will be insert | update | delete
|
%% ```{insert, string(), rowid()}'''
|
||||||
%% and table will be a string
|
%% When a new row has been inserted.
|
||||||
%% and id will be an integer
|
%% ```{delete, string(), rowid()}'''
|
||||||
|
%% When a new row has been deleted.
|
||||||
|
%% ```{update, string(), rowid()}'''
|
||||||
|
%% When a row has been updated.
|
||||||
%%
|
%%
|
||||||
-spec set_update_hook(pid(), connection()) -> ok | {error, term()}.
|
-spec set_update_hook(pid(), connection()) -> ok | {error, term()}.
|
||||||
set_update_hook(Pid, Connection) ->
|
set_update_hook(Pid, Connection) ->
|
||||||
set_update_hook(Pid, Connection, ?DEFAULT_TIMEOUT).
|
set_update_hook(Pid, Connection, ?DEFAULT_TIMEOUT).
|
||||||
|
|
||||||
|
%% @doc Same as set_update_hook, but with an additional timeout parameter.
|
||||||
-spec set_update_hook(pid(), connection(), timeout()) -> ok | {error, term()}.
|
-spec set_update_hook(pid(), connection(), timeout()) -> ok | {error, term()}.
|
||||||
set_update_hook(Pid, {connection, _Ref, Connection}, Timeout) ->
|
set_update_hook(Pid, {connection, _Ref, Connection}, Timeout) ->
|
||||||
Ref = make_ref(),
|
Ref = make_ref(),
|
||||||
@@ -92,35 +126,29 @@ set_update_hook(Pid, {connection, _Ref, Connection}, Timeout) ->
|
|||||||
%% @doc Execute a sql statement, returns a list with tuples.
|
%% @doc Execute a sql statement, returns a list with tuples.
|
||||||
-spec q(sql(), connection()) -> list(tuple()) | {error, term()}.
|
-spec q(sql(), connection()) -> list(tuple()) | {error, term()}.
|
||||||
q(Sql, Connection) ->
|
q(Sql, Connection) ->
|
||||||
q(Sql, [], Connection).
|
q(Sql, [], Connection, ?DEFAULT_TIMEOUT).
|
||||||
|
|
||||||
%% @doc Execute statement, bind args and return a list with tuples as result.
|
%% @doc Execute statement, bind args and return a list with tuples as result.
|
||||||
-spec q(sql(), list(), connection()) -> list(tuple()) | {error, term()}.
|
-spec q(sql(), list(), connection()) -> list(tuple()) | {error, term()}.
|
||||||
q(Sql, [], Connection) ->
|
|
||||||
case prepare(Sql, Connection) of
|
|
||||||
{ok, Statement} ->
|
|
||||||
fetchall(Statement);
|
|
||||||
{error, _Msg}=Error ->
|
|
||||||
throw(Error)
|
|
||||||
end;
|
|
||||||
q(Sql, Args, Connection) ->
|
q(Sql, Args, Connection) ->
|
||||||
case prepare(Sql, Connection) of
|
q(Sql, Args, Connection, ?DEFAULT_TIMEOUT).
|
||||||
{ok, Statement} ->
|
|
||||||
ok = bind(Statement, Args),
|
|
||||||
fetchall(Statement);
|
|
||||||
{error, _Msg}=Error ->
|
|
||||||
throw(Error)
|
|
||||||
end.
|
|
||||||
|
|
||||||
%% @doc Execute statement, bind args and return a list with tuples as result restricted by timeout.
|
%% @doc Execute statement, bind args and return a list with tuples as result restricted by timeout.
|
||||||
-spec q(sql(), list(), connection(), timeout()) -> list(tuple()) | {error, term()}.
|
-spec q(sql(), list(), connection(), timeout()) -> list(row()) | {error, term()}.
|
||||||
|
q(Sql, [], Connection, Timeout) ->
|
||||||
|
case prepare(Sql, Connection, Timeout) of
|
||||||
|
{ok, Statement} ->
|
||||||
|
fetchall(Statement, ?DEFAULT_CHUNK_SIZE, Timeout);
|
||||||
|
{error, _Msg}=Error ->
|
||||||
|
Error
|
||||||
|
end;
|
||||||
q(Sql, Args, Connection, Timeout) ->
|
q(Sql, Args, Connection, Timeout) ->
|
||||||
case prepare(Sql, Connection, Timeout) of
|
case prepare(Sql, Connection, Timeout) of
|
||||||
{ok, Statement} ->
|
{ok, Statement} ->
|
||||||
ok = bind(Statement, Args),
|
ok = bind(Statement, Args, Timeout),
|
||||||
fetchall(Statement, ?DEFAULT_CHUNK_SIZE, Timeout);
|
fetchall(Statement, ?DEFAULT_CHUNK_SIZE, Timeout);
|
||||||
{error, _Msg}=Error ->
|
{error, _Msg}=Error ->
|
||||||
throw(Error)
|
Error
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% @doc Execute statement and return a list with the result of F for each row.
|
%% @doc Execute statement and return a list with the result of F for each row.
|
||||||
@@ -229,7 +257,7 @@ map_s(F, Statement) when is_function(F, 2) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
%%
|
%%
|
||||||
%%-spec fetchone(statement()) -> tuple().
|
-spec fetchone(statement()) -> tuple().
|
||||||
fetchone(Statement) ->
|
fetchone(Statement) ->
|
||||||
case try_multi_step(Statement, 1, [], 0) of
|
case try_multi_step(Statement, 1, [], 0) of
|
||||||
{'$done', []} -> ok;
|
{'$done', []} -> ok;
|
||||||
@@ -239,7 +267,6 @@ fetchone(Statement) ->
|
|||||||
|
|
||||||
%% @doc Fetch all records
|
%% @doc Fetch all records
|
||||||
%% @param Statement is prepared sql statement
|
%% @param Statement is prepared sql statement
|
||||||
%% @spec fetchall(statement()) -> list(tuple()) | {error, term()}.
|
|
||||||
-spec fetchall(statement()) ->
|
-spec fetchall(statement()) ->
|
||||||
list(tuple()) |
|
list(tuple()) |
|
||||||
{error, term()}.
|
{error, term()}.
|
||||||
@@ -250,7 +277,6 @@ fetchall(Statement) ->
|
|||||||
%% @param Statement is prepared sql statement
|
%% @param Statement is prepared sql statement
|
||||||
%% @param ChunkSize is a count of rows to read from sqlite and send to erlang process in one bulk.
|
%% @param ChunkSize is a count of rows to read from sqlite and send to erlang process in one bulk.
|
||||||
%% Decrease this value if rows are heavy. Default value is 5000 (DEFAULT_CHUNK_SIZE).
|
%% Decrease this value if rows are heavy. Default value is 5000 (DEFAULT_CHUNK_SIZE).
|
||||||
%% @spec fetchall(statement(), pos_integer()) -> list(tuple()) | {error, term()}.
|
|
||||||
-spec fetchall(statement(), pos_integer()) ->
|
-spec fetchall(statement(), pos_integer()) ->
|
||||||
list(tuple()) |
|
list(tuple()) |
|
||||||
{error, term()}.
|
{error, term()}.
|
||||||
@@ -262,7 +288,6 @@ fetchall(Statement, ChunkSize) ->
|
|||||||
%% @param ChunkSize is a count of rows to read from sqlite and send to erlang process in one bulk.
|
%% @param ChunkSize is a count of rows to read from sqlite and send to erlang process in one bulk.
|
||||||
%% Decrease this value if rows are heavy. Default value is 5000 (DEFAULT_CHUNK_SIZE).
|
%% Decrease this value if rows are heavy. Default value is 5000 (DEFAULT_CHUNK_SIZE).
|
||||||
%% @param Timeout is timeout per each request of the one bulk
|
%% @param Timeout is timeout per each request of the one bulk
|
||||||
%% @spec fetchall(statement()) -> list(tuple()) | {error, term()}.
|
|
||||||
-spec fetchall(statement(), pos_integer(), timeout()) ->
|
-spec fetchall(statement(), pos_integer(), timeout()) ->
|
||||||
list(tuple()) |
|
list(tuple()) |
|
||||||
{error, term()}.
|
{error, term()}.
|
||||||
@@ -272,7 +297,7 @@ fetchall(Statement, ChunkSize, Timeout) ->
|
|||||||
{error, _} = E -> E
|
{error, _} = E -> E
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% return rows in revers order
|
%% return rows in reverse order
|
||||||
-spec fetchall_internal(statement(), pos_integer(), list(tuple()), timeout()) ->
|
-spec fetchall_internal(statement(), pos_integer(), list(tuple()), timeout()) ->
|
||||||
{'$done', list(tuple())} |
|
{'$done', list(tuple())} |
|
||||||
{error, term()}.
|
{error, term()}.
|
||||||
@@ -308,36 +333,33 @@ try_multi_step(Statement, ChunkSize, Rest, Tries, Timeout) ->
|
|||||||
Else -> Else
|
Else -> Else
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% @doc Execute Sql statement, returns the number of affected rows.
|
%% @doc Execute Sql statement.
|
||||||
%%
|
%%
|
||||||
%% @spec exec(iolist(), connection()) -> integer() | {error, error_message()}
|
-spec exec(sql(), connection()) -> ok | {error, _}.
|
||||||
exec(Sql, Connection) ->
|
exec(Sql, Connection) ->
|
||||||
exec(Sql, Connection, ?DEFAULT_TIMEOUT).
|
exec(Sql, [], Connection, ?DEFAULT_TIMEOUT).
|
||||||
|
|
||||||
%% @doc Execute
|
-spec exec(sql(), list(cell_type()), connection()) -> ok | {error, _}.
|
||||||
%%
|
exec(Sql, Params, Connection) ->
|
||||||
%% @spec exec(iolist(), connection(), timeout()) -> integer() | {error, error_message()}
|
exec(Sql, Params, Connection, ?DEFAULT_TIMEOUT).
|
||||||
exec(Sql, {connection, _Ref, Connection}, Timeout) ->
|
|
||||||
|
-spec exec(sql(), list(cell_type()), connection(), timeout()) -> ok | {error, _}.
|
||||||
|
exec(Sql, [], {connection, _Ref, Connection}, Timeout) ->
|
||||||
Ref = make_ref(),
|
Ref = make_ref(),
|
||||||
ok = esqlite3_nif:exec(Connection, Ref, self(), Sql),
|
ok = esqlite3_nif:exec(Connection, Ref, self(), Sql),
|
||||||
receive_answer(Ref, Timeout);
|
receive_answer(Ref, Timeout);
|
||||||
|
exec(Sql, Params, Connection, Timeout) ->
|
||||||
%% @spec exec(iolist(), list(term()), connection()) -> integer() | {error, error_message()}
|
|
||||||
exec(Sql, Params, {connection, _, _}=Connection) when is_list(Params) ->
|
|
||||||
exec(Sql, Params, Connection, ?DEFAULT_TIMEOUT).
|
|
||||||
|
|
||||||
%% @spec exec(iolist(), list(term()), connection(), timeout()) -> integer() | {error, error_message()}
|
|
||||||
exec(Sql, Params, {connection, _, _}=Connection, Timeout) when is_list(Params) ->
|
|
||||||
{ok, Statement} = prepare(Sql, Connection, Timeout),
|
{ok, Statement} = prepare(Sql, Connection, Timeout),
|
||||||
bind(Statement, Params),
|
bind(Statement, Params),
|
||||||
step(Statement, Timeout).
|
step(Statement, Timeout).
|
||||||
|
|
||||||
|
|
||||||
%% @doc Return the number of affected rows of last statement.
|
%% @doc Return the number of affected rows of last statement.
|
||||||
|
-spec changes(connection()) -> non_neg_integer().
|
||||||
changes(Connection) ->
|
changes(Connection) ->
|
||||||
changes(Connection, ?DEFAULT_TIMEOUT).
|
changes(Connection, ?DEFAULT_TIMEOUT).
|
||||||
|
|
||||||
%% @doc Return the number of affected rows of last statement.
|
-spec changes(connection(), timeout()) -> non_neg_integer().
|
||||||
changes({connection, _Ref, Connection}, Timeout) ->
|
changes({connection, _Ref, Connection}, Timeout) ->
|
||||||
Ref = make_ref(),
|
Ref = make_ref(),
|
||||||
ok = esqlite3_nif:changes(Connection, Ref, self()),
|
ok = esqlite3_nif:changes(Connection, Ref, self()),
|
||||||
@@ -345,38 +367,40 @@ changes({connection, _Ref, Connection}, Timeout) ->
|
|||||||
|
|
||||||
%% @doc Insert records, returns the last rowid.
|
%% @doc Insert records, returns the last rowid.
|
||||||
%%
|
%%
|
||||||
%% @spec insert(iolist(), connection()) -> {ok, integer()} | {error, error_message()}
|
-spec insert(sql(), connection()) -> {ok, rowid()} | {error, _}.
|
||||||
insert(Sql, Connection) ->
|
insert(Sql, Connection) ->
|
||||||
insert(Sql, Connection, ?DEFAULT_TIMEOUT).
|
insert(Sql, Connection, ?DEFAULT_TIMEOUT).
|
||||||
|
|
||||||
%% @doc Insert
|
%% @doc Like insert/2, but with extra timeout parameter.
|
||||||
%%
|
-spec insert(sql(), connection(), timeout()) -> {ok, rowid()} | {error, _}.
|
||||||
%% @spec insert(iolist(), connection(), timeout()) -> {ok, integer()} | {error, error_message()}
|
|
||||||
insert(Sql, {connection, _Ref, Connection}, Timeout) ->
|
insert(Sql, {connection, _Ref, Connection}, Timeout) ->
|
||||||
Ref = make_ref(),
|
Ref = make_ref(),
|
||||||
ok = esqlite3_nif:insert(Connection, Ref, self(), Sql),
|
ok = esqlite3_nif:insert(Connection, Ref, self(), Sql),
|
||||||
receive_answer(Ref, Timeout).
|
receive_answer(Ref, Timeout).
|
||||||
|
|
||||||
%% @doc Get autocommit
|
%% @doc Check if the connection is in auto-commit mode.
|
||||||
|
%% See: [https://sqlite.org/c3ref/get_autocommit.html] for more details.
|
||||||
%%
|
%%
|
||||||
%% @spec get_autocommit(connection) -> true | false
|
-spec get_autocommit(connection()) -> true | false.
|
||||||
get_autocommit(Connection) ->
|
get_autocommit(Connection) ->
|
||||||
get_autocommit(Connection, ?DEFAULT_TIMEOUT).
|
get_autocommit(Connection, ?DEFAULT_TIMEOUT).
|
||||||
|
|
||||||
|
%% @doc Like autocommit/1, but with an extra timeout attribute.
|
||||||
|
-spec get_autocommit(connection(), timeout()) -> true | false.
|
||||||
get_autocommit({connection, _Ref, Connection}, Timeout) ->
|
get_autocommit({connection, _Ref, Connection}, Timeout) ->
|
||||||
Ref = make_ref(),
|
Ref = make_ref(),
|
||||||
ok = esqlite3_nif:get_autocommit(Connection, Ref, self()),
|
ok = esqlite3_nif:get_autocommit(Connection, Ref, self()),
|
||||||
receive_answer(Ref, Timeout).
|
receive_answer(Ref, Timeout).
|
||||||
|
|
||||||
%% @doc Prepare a statement
|
%% @doc Compile a SQL statement. Returns a cached compiled statement which can be used in
|
||||||
|
%% queries.
|
||||||
%%
|
%%
|
||||||
%% @spec prepare(iolist(), connection()) -> {ok, prepared_statement()} | {error, error_message()}
|
-spec prepare(sql(), connection()) -> {ok, statement()} | {error, _}.
|
||||||
prepare(Sql, Connection) ->
|
prepare(Sql, Connection) ->
|
||||||
prepare(Sql, Connection, ?DEFAULT_TIMEOUT).
|
prepare(Sql, Connection, ?DEFAULT_TIMEOUT).
|
||||||
|
|
||||||
%% @doc
|
%% @doc Like prepare/2, but with an extra timeout value.
|
||||||
%%
|
-spec prepare(sql(), connection(), timeout()) -> {ok, statement()} | {error, _}.
|
||||||
%% @spec(iolist(), connection(), timeout()) -> {ok, prepared_statement()} | {error, error_message()}
|
|
||||||
prepare(Sql, {connection, _Ref, Connection}=C, Timeout) ->
|
prepare(Sql, {connection, _Ref, Connection}=C, Timeout) ->
|
||||||
Ref = make_ref(),
|
Ref = make_ref(),
|
||||||
ok = esqlite3_nif:prepare(Connection, Ref, self(), Sql),
|
ok = esqlite3_nif:prepare(Connection, Ref, self(), Sql),
|
||||||
@@ -387,14 +411,13 @@ prepare(Sql, {connection, _Ref, Connection}=C, Timeout) ->
|
|||||||
|
|
||||||
%% @doc Step
|
%% @doc Step
|
||||||
%%
|
%%
|
||||||
%% @spec step(prepared_statement()) -> tuple()
|
-spec step(statement()) -> tuple() | '$busy' | '$done'.
|
||||||
step(Stmt) ->
|
step(Stmt) ->
|
||||||
step(Stmt, ?DEFAULT_TIMEOUT).
|
step(Stmt, ?DEFAULT_TIMEOUT).
|
||||||
|
|
||||||
%% @doc
|
%% @doc
|
||||||
%%
|
%%
|
||||||
%% @spec step(prepared_statement(), timeout()) -> tuple()
|
-spec step(statement(), timeout()) -> tuple() | '$busy' | '$done'.
|
||||||
-spec step(term(), timeout()) -> tuple() | '$busy' | '$done'.
|
|
||||||
step({statement, Stmt, {connection, _, Conn}}, Timeout) ->
|
step({statement, Stmt, {connection, _, Conn}}, Timeout) ->
|
||||||
Ref = make_ref(),
|
Ref = make_ref(),
|
||||||
ok = esqlite3_nif:multi_step(Conn, Stmt, 1, Ref, self()),
|
ok = esqlite3_nif:multi_step(Conn, Stmt, 1, Ref, self()),
|
||||||
@@ -419,7 +442,7 @@ multi_step({statement, Stmt, {connection, _, Conn}}, ChunkSize, Timeout) ->
|
|||||||
|
|
||||||
%% @doc Reset the prepared statement back to its initial state.
|
%% @doc Reset the prepared statement back to its initial state.
|
||||||
%%
|
%%
|
||||||
%% @spec reset(prepared_statement()) -> ok | {error, error_message()}
|
-spec reset(statement()) -> ok | {error, _}.
|
||||||
reset({statement, Stmt, {connection, _, Conn}}) ->
|
reset({statement, Stmt, {connection, _, Conn}}) ->
|
||||||
Ref = make_ref(),
|
Ref = make_ref(),
|
||||||
ok = esqlite3_nif:reset(Conn, Stmt, Ref, self()),
|
ok = esqlite3_nif:reset(Conn, Stmt, Ref, self()),
|
||||||
@@ -427,13 +450,12 @@ reset({statement, Stmt, {connection, _, Conn}}) ->
|
|||||||
|
|
||||||
%% @doc Bind values to prepared statements
|
%% @doc Bind values to prepared statements
|
||||||
%%
|
%%
|
||||||
%% @spec bind(prepared_statement(), value_list()) -> ok | {error, error_message()}
|
-spec bind(statement(), list(cell_type())) -> ok | {error, _}.
|
||||||
bind(Stmt, Args) ->
|
bind(Stmt, Args) ->
|
||||||
bind(Stmt, Args, ?DEFAULT_TIMEOUT).
|
bind(Stmt, Args, ?DEFAULT_TIMEOUT).
|
||||||
|
|
||||||
%% @doc Bind values to prepared statements
|
%% @doc Bind values to prepared statements
|
||||||
%%
|
-spec bind(statement(), list(cell_type()), timeout()) -> ok | {error, _}.
|
||||||
%% @spec bind(prepared_statement(), [], timeout()) -> ok | {error, error_message()}
|
|
||||||
bind({statement, Stmt, {connection, _, Conn}}, Args, Timeout) ->
|
bind({statement, Stmt, {connection, _, Conn}}, Args, Timeout) ->
|
||||||
Ref = make_ref(),
|
Ref = make_ref(),
|
||||||
ok = esqlite3_nif:bind(Conn, Stmt, Ref, self(), Args),
|
ok = esqlite3_nif:bind(Conn, Stmt, Ref, self(), Args),
|
||||||
@@ -464,36 +486,39 @@ column_types({statement, Stmt, {connection, _, Conn}}, Timeout) ->
|
|||||||
receive_answer(Ref, Timeout).
|
receive_answer(Ref, Timeout).
|
||||||
|
|
||||||
%% @doc Close the database
|
%% @doc Close the database
|
||||||
%%
|
|
||||||
%% @spec close(connection()) -> ok | {error, error_message()}
|
|
||||||
-spec close(connection()) -> ok | {error, _}.
|
-spec close(connection()) -> ok | {error, _}.
|
||||||
close(Connection) ->
|
close(Connection) ->
|
||||||
close(Connection, ?DEFAULT_TIMEOUT).
|
close(Connection, ?DEFAULT_TIMEOUT).
|
||||||
|
|
||||||
%% @doc Close the database
|
%% @doc Close the database
|
||||||
%%
|
|
||||||
%% @spec close(connection(), integer()) -> ok | {error, error_message()}
|
|
||||||
-spec close(connection(), timeout()) -> ok | {error, _}.
|
-spec close(connection(), timeout()) -> ok | {error, _}.
|
||||||
close({connection, _Ref, Connection}, Timeout) ->
|
close({connection, _Ref, Connection}, Timeout) ->
|
||||||
Ref = make_ref(),
|
Ref = make_ref(),
|
||||||
ok = esqlite3_nif:close(Connection, Ref, self()),
|
ok = esqlite3_nif:close(Connection, Ref, self()),
|
||||||
receive_answer(Ref, Timeout).
|
receive_answer(Ref, Timeout).
|
||||||
|
|
||||||
|
|
||||||
|
%% @doc Flush any stale answers left in the mailbox of the current process.
|
||||||
|
%% This can happen if there has been a timeout. Normally the nif functions
|
||||||
|
%% are called with the default 'infinite' timeout, so calling this is not
|
||||||
|
%% needed.
|
||||||
|
-spec flush() -> ok.
|
||||||
|
flush() ->
|
||||||
|
flush_answers().
|
||||||
|
|
||||||
|
|
||||||
%% Internal functions
|
%% Internal functions
|
||||||
|
|
||||||
receive_answer(Ref, Timeout) ->
|
receive_answer(Ref, Timeout) ->
|
||||||
Start = os:timestamp(),
|
|
||||||
receive
|
receive
|
||||||
{esqlite3, Ref, Resp} ->
|
{esqlite3, Ref, Resp} -> Resp
|
||||||
Resp;
|
after
|
||||||
{esqlite3, _, _}=StaleAnswer ->
|
Timeout -> throw({error, timeout, Ref})
|
||||||
error_logger:warning_msg("Esqlite3: Ignoring stale answer ~p~n", [StaleAnswer]),
|
end.
|
||||||
PassedMics = timer:now_diff(os:timestamp(), Start) div 1000,
|
|
||||||
NewTimeout = case Timeout - PassedMics of
|
flush_answers() ->
|
||||||
Passed when Passed < 0 -> 0;
|
receive
|
||||||
TO -> TO
|
{esqlite3, _, _} -> flush_answers()
|
||||||
end,
|
after
|
||||||
receive_answer(Ref, NewTimeout)
|
0 -> ok
|
||||||
after Timeout ->
|
|
||||||
throw({error, timeout, Ref})
|
|
||||||
end.
|
end.
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ init() ->
|
|||||||
|
|
||||||
%% @doc Start a low level thread which will can handle sqlite3 calls.
|
%% @doc Start a low level thread which will can handle sqlite3 calls.
|
||||||
%%
|
%%
|
||||||
%% @spec start() -> {ok, connection()} | {error, msg()}
|
-spec start() -> {ok, esqlite:connection()} | {error, any()}.
|
||||||
start() ->
|
start() ->
|
||||||
erlang:nif_error(nif_library_not_loaded).
|
erlang:nif_error(nif_library_not_loaded).
|
||||||
|
|
||||||
@@ -59,8 +59,7 @@ start() ->
|
|||||||
%% Sends an asynchronous open command over the connection and returns
|
%% Sends an asynchronous open command over the connection and returns
|
||||||
%% ok immediately. When the database is opened
|
%% ok immediately. When the database is opened
|
||||||
%%
|
%%
|
||||||
%% @spec open(connection(), reference(), pid(), string()) -> ok | {error, message()}
|
-spec open(esqlite:connection(), reference(), pid(), string()) -> ok | {error, any()}.
|
||||||
|
|
||||||
open(_Db, _Ref, _Dest, _Filename) ->
|
open(_Db, _Ref, _Dest, _Filename) ->
|
||||||
erlang:nif_error(nif_library_not_loaded).
|
erlang:nif_error(nif_library_not_loaded).
|
||||||
|
|
||||||
@@ -75,7 +74,7 @@ set_update_hook(_Db, _Ref, _Dest, _Pid) ->
|
|||||||
%% When the statement is executed Dest will receive message {Ref, answer()}
|
%% When the statement is executed Dest will receive message {Ref, answer()}
|
||||||
%% with answer() integer | {error, reason()}
|
%% with answer() integer | {error, reason()}
|
||||||
%%
|
%%
|
||||||
%% @spec exec(connection(), Ref::reference(), Dest::pid(), string()) -> ok | {error, message()}
|
-spec exec(esqlite:connection(), reference(), pid(), string()) -> ok | {error, any()}.
|
||||||
exec(_Db, _Ref, _Dest, _Sql) ->
|
exec(_Db, _Ref, _Dest, _Sql) ->
|
||||||
erlang:nif_error(nif_library_not_loaded).
|
erlang:nif_error(nif_library_not_loaded).
|
||||||
|
|
||||||
@@ -89,61 +88,60 @@ changes(_Db, _Ref, _Dest) ->
|
|||||||
|
|
||||||
%% @doc
|
%% @doc
|
||||||
%%
|
%%
|
||||||
%% @spec prepare(connection(), reference(), pid(), string()) -> ok | {error, message()}
|
-spec prepare(esqlite:connection(), reference(), pid(), string()) -> ok | {error, any()}.
|
||||||
prepare(_Db, _Ref, _Dest, _Sql) ->
|
prepare(_Db, _Ref, _Dest, _Sql) ->
|
||||||
erlang:nif_error(nif_library_not_loaded).
|
erlang:nif_error(nif_library_not_loaded).
|
||||||
|
|
||||||
%% @doc
|
%% @doc
|
||||||
%%
|
%%
|
||||||
%% @spec multi_step(statement(), pos_integer(), reference(), pid()) -> {term(), list(tuple)} | {error, message()}
|
-spec multi_step(esqlite:connection(), esqlite:statement(), pos_integer(), reference(), pid()) -> ok | {error, any()}.
|
||||||
multi_step(_Db, _Stmt, _Chunk_Size, _Ref, _Dest) ->
|
multi_step(_Db, _Stmt, _Chunk_Size, _Ref, _Dest) ->
|
||||||
erlang:nif_error(nif_library_not_loaded).
|
erlang:nif_error(nif_library_not_loaded).
|
||||||
|
|
||||||
%% @doc
|
%% @doc
|
||||||
%%
|
%%
|
||||||
%% @spec reset(statement(), reference(), pid()) -> ok | {error, message()}
|
-spec reset(esqlite:connection(), esqlite:statement(), reference(), pid()) -> ok | {error, any()}.
|
||||||
reset(_Db, _Stmt, _Ref, _Dest) ->
|
reset(_Db, _Stmt, _Ref, _Dest) ->
|
||||||
erlang:nif_error(nif_library_not_loaded).
|
erlang:nif_error(nif_library_not_loaded).
|
||||||
|
|
||||||
%% @doc
|
%% @doc
|
||||||
%%
|
%%
|
||||||
%%
|
-spec finalize(esqlite:connection(), esqlite:statement(), reference(), pid()) -> ok | {error, any()}.
|
||||||
finalize(_Db, _Stmt, _Ref, _Dest) ->
|
finalize(_Db, _Stmt, _Ref, _Dest) ->
|
||||||
erlang:nif_error(nif_library_not_loaded).
|
erlang:nif_error(nif_library_not_loaded).
|
||||||
|
|
||||||
%% @doc Bind parameters to a prepared statement.
|
%% @doc Bind parameters to a prepared statement.
|
||||||
%%
|
%%
|
||||||
%% @spec bind(connection(), statement(), reference(), pid(), []) -> ok | {error, message()}
|
-spec bind(esqlite:connection(), esqlite:statement(), reference(), pid(), list(any())) -> ok | {error, any()}.
|
||||||
bind(_Db, _Stmt, _Ref, _Dest, _Args) ->
|
bind(_Db, _Stmt, _Ref, _Dest, _Args) ->
|
||||||
erlang:nif_error(nif_library_not_loaded).
|
erlang:nif_error(nif_library_not_loaded).
|
||||||
|
|
||||||
%% @doc Retrieve the column names of the prepared statement
|
%% @doc Retrieve the column names of the prepared statement
|
||||||
%%
|
%%
|
||||||
%% @spec column_names(connection(), statement(), reference(), pid()) -> {ok, tuple()} | {error, message()}
|
-spec column_names(esqlite:connection(), esqlite:statement(), reference(), pid()) -> ok | {error, any()}.
|
||||||
column_names(_Db, _Stmt, _Ref, _Dest) ->
|
column_names(_Db, _Stmt, _Ref, _Dest) ->
|
||||||
erlang:nif_error(nif_library_not_loaded).
|
erlang:nif_error(nif_library_not_loaded).
|
||||||
|
|
||||||
%% @doc Retrieve the column types of the prepared statement
|
%% @doc Retrieve the column types of the prepared statement
|
||||||
%%
|
%%
|
||||||
%% @spec column_types(connection(), statement(), reference(), pid()) -> {ok, tuple()} | {error, message()}
|
-spec column_types(esqlite:connection(), esqlite:statement(), reference(), pid()) -> ok | {error, any()}.
|
||||||
column_types(_Db, _Stmt, _Ref, _Dest) ->
|
column_types(_Db, _Stmt, _Ref, _Dest) ->
|
||||||
erlang:nif_error(nif_library_not_loaded).
|
erlang:nif_error(nif_library_not_loaded).
|
||||||
|
|
||||||
%% @doc Close the connection.
|
%% @doc Close the connection.
|
||||||
%%
|
%%
|
||||||
%% @spec close(connection(), reference(), pid()) -> ok | {error, message()}
|
-spec close(esqlite:connection(), reference(), pid()) -> ok | {error, any()}.
|
||||||
close(_Db, _Ref, _Dest) ->
|
close(_Db, _Ref, _Dest) ->
|
||||||
erlang:nif_error(nif_library_not_loaded).
|
erlang:nif_error(nif_library_not_loaded).
|
||||||
|
|
||||||
|
|
||||||
%% @doc Insert record
|
%% @doc Insert record
|
||||||
%%
|
%%
|
||||||
%% @spec insert(connection(), Ref::reference(), Dest::pid(), string()) -> {ok, integer()} | {error, message()}
|
-spec insert(esqlite:connection(), reference(), pid(), esqlite:sql()) -> ok | {error, any()}.
|
||||||
insert(_Db, _Ref, _Dest, _Sql) ->
|
insert(_Db, _Ref, _Dest, _Sql) ->
|
||||||
erlang:nif_error(nif_library_not_loaded).
|
erlang:nif_error(nif_library_not_loaded).
|
||||||
|
|
||||||
%% @doc Get automcommit
|
%% @doc Get automcommit
|
||||||
%%
|
%%
|
||||||
%% @spec get_autocommit(connection(), Ref::reference(), Dest::pid()) -> true | false
|
-spec get_autocommit(esqlite:connection(), reference(), pid()) -> ok | {error, any()}.
|
||||||
get_autocommit(_Db, _Ref, _Dest) ->
|
get_autocommit(_Db, _Ref, _Dest) ->
|
||||||
erlang:nif_error(nif_library_not_loaded).
|
erlang:nif_error(nif_library_not_loaded).
|
||||||
|
|||||||
@@ -424,8 +424,11 @@ garbage_collect_test() ->
|
|||||||
F = fun() ->
|
F = fun() ->
|
||||||
{ok, Db} = esqlite3:open(":memory:"),
|
{ok, Db} = esqlite3:open(":memory:"),
|
||||||
[] = esqlite3:q("create table test(one, two, three)", Db),
|
[] = esqlite3:q("create table test(one, two, three)", Db),
|
||||||
|
[] = esqlite3:q("insert into test values(1, '2', 3.0)", Db),
|
||||||
{ok, Stmt} = esqlite3:prepare("select * from test", Db),
|
{ok, Stmt} = esqlite3:prepare("select * from test", Db),
|
||||||
'$done' = esqlite3:step(Stmt)
|
{row, {1, <<"2">>, 3.0}} = esqlite3:step(Stmt),
|
||||||
|
'$done' = esqlite3:step(Stmt),
|
||||||
|
ok = esqlite3:close(Db)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
[spawn(F) || _X <- lists:seq(0,30)],
|
[spawn(F) || _X <- lists:seq(0,30)],
|
||||||
@@ -438,5 +441,3 @@ garbage_collect_test() ->
|
|||||||
|
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user