diff --git a/src/esqlite3.erl b/src/esqlite3.erl index 81b247e..a1393ec 100644 --- a/src/esqlite3.erl +++ b/src/esqlite3.erl @@ -49,8 +49,17 @@ %% --type connection() :: {connection, reference(), term()}. --type statement() :: {statement, term(), connection()}. +-record(connection, { + raw_connection :: esqlite_nif:raw_connection() +}). + +-record(statement, { + raw_statement :: esqlite_nif:raw_statement(), + raw_connection :: esqlite_nif:raw_connection() +}). + +-type connection() :: #connection{}. +-type statement() :: #statement{}. -type sql() :: iodata(). %% erlang -> sqlite type conversions @@ -92,13 +101,13 @@ open(Filename) -> %% -spec open(string(), timeout()) -> {ok, connection()} | {error, _}. open(Filename, Timeout) -> - {ok, Connection} = esqlite3_nif:start(), + {ok, RawConnection} = esqlite3_nif:start(), Ref = make_ref(), - ok = esqlite3_nif:open(Connection, Ref, self(), Filename), - case receive_answer(Connection, Ref, Timeout) of + ok = esqlite3_nif:open(RawConnection, Ref, self(), Filename), + case receive_answer(RawConnection, Ref, Timeout) of ok -> - {ok, {connection, make_ref(), Connection}}; + {ok, #connection{raw_connection=RawConnection}}; {error, _Msg}=Error -> Error end. @@ -118,10 +127,10 @@ set_update_hook(Pid, Connection) -> %% @doc Same as set_update_hook, but with an additional timeout parameter. -spec set_update_hook(pid(), connection(), timeout()) -> ok | {error, term()}. -set_update_hook(Pid, {connection, _Ref, Connection}, Timeout) -> +set_update_hook(Pid, #connection{raw_connection=RawConnection}, Timeout) -> Ref = make_ref(), - ok = esqlite3_nif:set_update_hook(Connection, Ref, self(), Pid), - receive_answer(Connection, Ref, Timeout). + ok = esqlite3_nif:set_update_hook(RawConnection, Ref, self(), Pid), + receive_answer(RawConnection, Ref, Timeout). %% @doc Execute a sql statement, returns a list with tuples. -spec q(sql(), connection()) -> list(tuple()) | {error, term()}. @@ -340,16 +349,16 @@ exec(Sql, Connection) -> exec(Sql, [], Connection, ?DEFAULT_TIMEOUT). -spec exec(sql(), list(cell_type()) | connection(), connection() | timeout()) -> ok | {error, _}. -exec(Sql, {connection, _,_}=Connection, Timeout) -> +exec(Sql, #connection{}=Connection, Timeout) -> exec(Sql, [], Connection, Timeout); -exec(Sql, Params, Connection) -> +exec(Sql, Params, #connection{}=Connection) -> exec(Sql, Params, Connection, ?DEFAULT_TIMEOUT). -spec exec(sql(), list(cell_type()), connection(), timeout()) -> ok | {error, _}. -exec(Sql, [], {connection, _Ref, Connection}, Timeout) -> +exec(Sql, [], #connection{raw_connection=RawConnection}, Timeout) -> Ref = make_ref(), - ok = esqlite3_nif:exec(Connection, Ref, self(), Sql), - receive_answer(Connection, Ref, Timeout); + ok = esqlite3_nif:exec(RawConnection, Ref, self(), Sql), + receive_answer(RawConnection, Ref, Timeout); exec(Sql, Params, Connection, Timeout) -> {ok, Statement} = prepare(Sql, Connection, Timeout), bind(Statement, Params), @@ -362,10 +371,10 @@ changes(Connection) -> changes(Connection, ?DEFAULT_TIMEOUT). -spec changes(connection(), timeout()) -> non_neg_integer(). -changes({connection, _Ref, Connection}, Timeout) -> +changes(#connection{raw_connection=RawConnection}, Timeout) -> Ref = make_ref(), - ok = esqlite3_nif:changes(Connection, Ref, self()), - receive_answer(Connection, Ref, Timeout). + ok = esqlite3_nif:changes(RawConnection, Ref, self()), + receive_answer(RawConnection, Ref, Timeout). %% @doc Insert records, returns the last rowid. %% @@ -375,10 +384,10 @@ insert(Sql, Connection) -> %% @doc Like insert/2, but with extra timeout parameter. -spec insert(sql(), connection(), timeout()) -> {ok, rowid()} | {error, _}. -insert(Sql, {connection, _Ref, Connection}, Timeout) -> +insert(Sql, #connection{raw_connection=RawConnection}, Timeout) -> Ref = make_ref(), - ok = esqlite3_nif:insert(Connection, Ref, self(), Sql), - receive_answer(Connection, Ref, Timeout). + ok = esqlite3_nif:insert(RawConnection, Ref, self(), Sql), + receive_answer(RawConnection, Ref, Timeout). %% @doc Check if the connection is in auto-commit mode. %% See: [https://sqlite.org/c3ref/get_autocommit.html] for more details. @@ -389,10 +398,10 @@ get_autocommit(Connection) -> %% @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{raw_connection=RawConnection}, Timeout) -> Ref = make_ref(), - ok = esqlite3_nif:get_autocommit(Connection, Ref, self()), - receive_answer(Connection, Ref, Timeout). + ok = esqlite3_nif:get_autocommit(RawConnection, Ref, self()), + receive_answer(RawConnection, Ref, Timeout). %% @doc Compile a SQL statement. Returns a cached compiled statement which can be used in %% queries. @@ -403,12 +412,14 @@ prepare(Sql, Connection) -> %% @doc Like prepare/2, but with an extra timeout value. -spec prepare(sql(), connection(), timeout()) -> {ok, statement()} | {error, _}. -prepare(Sql, {connection, _Ref, Connection}=C, Timeout) -> +prepare(Sql, #connection{raw_connection=RawConnection}, Timeout) -> Ref = make_ref(), - ok = esqlite3_nif:prepare(Connection, Ref, self(), Sql), - case receive_answer(Connection, Ref, Timeout) of - {ok, Stmt} -> {ok, {statement, Stmt, C}}; - Else -> Else + ok = esqlite3_nif:prepare(RawConnection, Ref, self(), Sql), + case receive_answer(RawConnection, Ref, Timeout) of + {ok, Stmt} -> + {ok, #statement{raw_statement=Stmt, raw_connection=RawConnection}}; + Else -> + Else end. %% @doc Step @@ -420,10 +431,10 @@ step(Stmt) -> %% @doc %% -spec step(statement(), timeout()) -> tuple() | '$busy' | '$done'. -step({statement, Stmt, {connection, _, Conn}}, Timeout) -> +step(#statement{raw_statement=RawStatement, raw_connection=RawConnection}, Timeout) -> Ref = make_ref(), - ok = esqlite3_nif:multi_step(Conn, Stmt, 1, Ref, self()), - case receive_answer(Conn, Ref, Timeout) of + ok = esqlite3_nif:multi_step(RawConnection, RawStatement, 1, Ref, self()), + case receive_answer(RawConnection, Ref, Timeout) of {rows, [Row | []]} -> {row, Row}; {'$done', []} -> '$done'; {'$busy', []} -> '$busy'; @@ -437,18 +448,18 @@ step({statement, Stmt, {connection, _, Conn}}, Timeout) -> {'$busy', list(tuple())} | {'$done', list(tuple())} | {error, term()}. -multi_step({statement, Stmt, {connection, _, Conn}}, ChunkSize, Timeout) -> +multi_step(#statement{raw_statement=RawStatement, raw_connection=RawConnection}, ChunkSize, Timeout) -> Ref = make_ref(), - ok = esqlite3_nif:multi_step(Conn, Stmt, ChunkSize, Ref, self()), - receive_answer(Conn, Ref, Timeout). + ok = esqlite3_nif:multi_step(RawConnection, RawStatement, ChunkSize, Ref, self()), + receive_answer(RawConnection, Ref, Timeout). %% @doc Reset the prepared statement back to its initial state. %% -spec reset(statement()) -> ok | {error, _}. -reset({statement, Stmt, {connection, _, Conn}}) -> +reset(#statement{raw_statement=RawStatement, raw_connection=RawConnection}) -> Ref = make_ref(), - ok = esqlite3_nif:reset(Conn, Stmt, Ref, self()), - receive_answer(Conn, Ref, ?DEFAULT_TIMEOUT). + ok = esqlite3_nif:reset(RawConnection, RawStatement, Ref, self()), + receive_answer(RawConnection, Ref, ?DEFAULT_TIMEOUT). %% @doc Bind values to prepared statements %% @@ -458,10 +469,10 @@ bind(Stmt, Args) -> %% @doc Bind values to prepared statements -spec bind(statement(), list(cell_type()), timeout()) -> ok | {error, _}. -bind({statement, Stmt, {connection, _, Conn}}, Args, Timeout) -> +bind(#statement{raw_statement=RawStatement, raw_connection=RawConnection}, Args, Timeout) -> Ref = make_ref(), - ok = esqlite3_nif:bind(Conn, Stmt, Ref, self(), Args), - receive_answer(Conn, Ref, Timeout). + ok = esqlite3_nif:bind(RawConnection, RawStatement, Ref, self(), Args), + receive_answer(RawConnection, Ref, Timeout). %% @doc Return the column names of the prepared statement. %% @@ -470,22 +481,22 @@ column_names(Stmt) -> column_names(Stmt, ?DEFAULT_TIMEOUT). -spec column_names(statement(), timeout()) -> {atom()}. -column_names({statement, Stmt, {connection, _, Conn}}, Timeout) -> +column_names(#statement{raw_statement=RawStatement, raw_connection=RawConnection}, Timeout) -> Ref = make_ref(), - ok = esqlite3_nif:column_names(Conn, Stmt, Ref, self()), - receive_answer(Conn, Ref, Timeout). + ok = esqlite3_nif:column_names(RawConnection, RawStatement, Ref, self()), + receive_answer(RawConnection, Ref, Timeout). %% @doc Return the column types of the prepared statement. %% -spec column_types(statement()) -> {atom()}. -column_types(Stmt) -> - column_types(Stmt, ?DEFAULT_TIMEOUT). +column_types(Statement) -> + column_types(Statement, ?DEFAULT_TIMEOUT). -spec column_types(statement(), timeout()) -> {atom()}. -column_types({statement, Stmt, {connection, _, Conn}}, Timeout) -> +column_types(#statement{raw_statement=RawStatement, raw_connection=RawConnection}, Timeout) -> Ref = make_ref(), - ok = esqlite3_nif:column_types(Conn, Stmt, Ref, self()), - receive_answer(Conn, Ref, Timeout). + ok = esqlite3_nif:column_types(RawConnection, RawStatement, Ref, self()), + receive_answer(RawConnection, Ref, Timeout). %% @doc Close the database -spec close(connection()) -> ok | {error, _}. @@ -494,10 +505,10 @@ close(Connection) -> %% @doc Close the database -spec close(connection(), timeout()) -> ok | {error, _}. -close({connection, _Ref, Connection}, Timeout) -> +close(#connection{raw_connection=RawConnection}, Timeout) -> Ref = make_ref(), - ok = esqlite3_nif:close(Connection, Ref, self()), - receive_answer(Connection, Ref, Timeout). + ok = esqlite3_nif:close(RawConnection, Ref, self()), + receive_answer(RawConnection, Ref, Timeout). %% @doc Flush any stale answers left in the mailbox of the current process. @@ -511,12 +522,12 @@ flush() -> %% Internal functions -receive_answer(Connection, Ref, Timeout) -> +receive_answer(RawConnection, Ref, Timeout) -> receive {esqlite3, Ref, Resp} -> Resp after Timeout -> - ok = esqlite3_nif:interrupt(Connection), + ok = esqlite3_nif:interrupt(RawConnection), throw({error, timeout, Ref}) end. diff --git a/src/esqlite3_nif.erl b/src/esqlite3_nif.erl index 3f2503a..d2592b9 100644 --- a/src/esqlite3_nif.erl +++ b/src/esqlite3_nif.erl @@ -39,6 +39,11 @@ close/3 ]). +-type raw_connection() :: reference(). +-type raw_statement() :: reference(). + +-export_type([raw_connection/0, raw_statement/0]). + -on_load(init/0). init() -> @@ -51,7 +56,7 @@ init() -> %% @doc Start a low level thread which will can handle sqlite3 calls. %% --spec start() -> {ok, esqlite:connection()} | {error, any()}. +-spec start() -> {ok, raw_connection()} | {error, _}. start() -> erlang:nif_error(nif_library_not_loaded). @@ -60,10 +65,11 @@ start() -> %% Sends an asynchronous open command over the connection and returns %% ok immediately. When the database is opened %% --spec open(esqlite:connection(), reference(), pid(), string()) -> ok | {error, any()}. +-spec open(raw_connection(), reference(), pid(), string()) -> ok | {error, _}. open(_Db, _Ref, _Dest, _Filename) -> erlang:nif_error(nif_library_not_loaded). +-spec set_update_hook(raw_connection(), reference(), pid(), pid()) -> ok | {error, _}. set_update_hook(_Db, _Ref, _Dest, _Pid) -> erlang:nif_error(nif_library_not_loaded). @@ -75,7 +81,7 @@ set_update_hook(_Db, _Ref, _Dest, _Pid) -> %% When the statement is executed Dest will receive message {Ref, answer()} %% with answer() integer | {error, reason()} %% --spec exec(esqlite:connection(), reference(), pid(), string()) -> ok | {error, any()}. +-spec exec(raw_connection(), reference(), pid(), string()) -> ok | {error, _}. exec(_Db, _Ref, _Dest, _Sql) -> erlang:nif_error(nif_library_not_loaded). @@ -83,70 +89,72 @@ exec(_Db, _Ref, _Dest, _Sql) -> %% %% When the statement is executed Dest will receive message {Ref, answer()} %% with answer() integer | {error, reason()} -%% +-spec changes(raw_connection(), reference(), pid()) -> ok | {error, _}. changes(_Db, _Ref, _Dest) -> erlang:nif_error(nif_library_not_loaded). %% @doc %% --spec prepare(esqlite:connection(), reference(), pid(), string()) -> ok | {error, any()}. +-spec prepare(raw_connection(), reference(), pid(), string()) -> ok | {error, _}. prepare(_Db, _Ref, _Dest, _Sql) -> erlang:nif_error(nif_library_not_loaded). %% @doc %% --spec multi_step(esqlite:connection(), esqlite:statement(), pos_integer(), reference(), pid()) -> ok | {error, any()}. +-spec multi_step(raw_connection(), raw_statement(), pos_integer(), reference(), pid()) -> ok | {error, _}. multi_step(_Db, _Stmt, _Chunk_Size, _Ref, _Dest) -> erlang:nif_error(nif_library_not_loaded). %% @doc %% --spec reset(esqlite:connection(), esqlite:statement(), reference(), pid()) -> ok | {error, any()}. +-spec reset(raw_connection(), raw_statement(), reference(), pid()) -> ok | {error, _}. reset(_Db, _Stmt, _Ref, _Dest) -> erlang:nif_error(nif_library_not_loaded). %% @doc %% --spec finalize(esqlite:connection(), esqlite:statement(), reference(), pid()) -> ok | {error, any()}. +-spec finalize(raw_connection(), raw_statement(), reference(), pid()) -> ok | {error, _}. finalize(_Db, _Stmt, _Ref, _Dest) -> erlang:nif_error(nif_library_not_loaded). %% @doc Bind parameters to a prepared statement. %% --spec bind(esqlite:connection(), esqlite:statement(), reference(), pid(), list(any())) -> ok | {error, any()}. +-spec bind(raw_connection(), raw_statement(), reference(), pid(), list(any())) -> ok | {error, _}. bind(_Db, _Stmt, _Ref, _Dest, _Args) -> erlang:nif_error(nif_library_not_loaded). %% @doc Retrieve the column names of the prepared statement %% --spec column_names(esqlite:connection(), esqlite:statement(), reference(), pid()) -> ok | {error, any()}. +-spec column_names(raw_connection(), raw_statement(), reference(), pid()) -> ok | {error, _}. column_names(_Db, _Stmt, _Ref, _Dest) -> erlang:nif_error(nif_library_not_loaded). %% @doc Retrieve the column types of the prepared statement %% --spec column_types(esqlite:connection(), esqlite:statement(), reference(), pid()) -> ok | {error, any()}. +-spec column_types(raw_connection(), raw_statement(), reference(), pid()) -> ok | {error, _}. column_types(_Db, _Stmt, _Ref, _Dest) -> erlang:nif_error(nif_library_not_loaded). %% @doc Interrupt all active queries. +-spec interrupt(raw_connection()) -> ok. interrupt(_Db) -> erlang:nif_error(nif_library_not_loaded). %% @doc Close the connection. %% --spec close(esqlite:connection(), reference(), pid()) -> ok | {error, any()}. +-spec close(raw_connection(), reference(), pid()) -> ok | {error, _}. close(_Db, _Ref, _Dest) -> erlang:nif_error(nif_library_not_loaded). %% @doc Insert record %% --spec insert(esqlite:connection(), reference(), pid(), esqlite:sql()) -> ok | {error, any()}. +-spec insert(raw_connection(), reference(), pid(), esqlite:sql()) -> ok | {error, _}. insert(_Db, _Ref, _Dest, _Sql) -> erlang:nif_error(nif_library_not_loaded). %% @doc Get automcommit %% --spec get_autocommit(esqlite:connection(), reference(), pid()) -> ok | {error, any()}. +-spec get_autocommit(raw_connection(), reference(), pid()) -> ok | {error, _}. get_autocommit(_Db, _Ref, _Dest) -> erlang:nif_error(nif_library_not_loaded). +