Added map and foreach support on queries
This commit is contained in:
@@ -28,6 +28,8 @@
|
|||||||
bind/2, bind/3,
|
bind/2, bind/3,
|
||||||
close/1, close/2]).
|
close/1, close/2]).
|
||||||
|
|
||||||
|
-export([q/2, map/3, foreach/3]).
|
||||||
|
|
||||||
-define(DEFAULT_TIMEOUT, infinity).
|
-define(DEFAULT_TIMEOUT, infinity).
|
||||||
|
|
||||||
%% @doc Opens a sqlite3 database mentioned in Filename.
|
%% @doc Opens a sqlite3 database mentioned in Filename.
|
||||||
@@ -40,43 +42,95 @@ open(Filename) ->
|
|||||||
%%
|
%%
|
||||||
%% @spec open(string(), timeout()) -> {ok, connection()} | {error, error_message()}
|
%% @spec open(string(), timeout()) -> {ok, connection()} | {error, error_message()}
|
||||||
open(Filename, Timeout) ->
|
open(Filename, Timeout) ->
|
||||||
{ok, Db} = esqlite3_nif:start(),
|
{ok, Connection} = esqlite3_nif:start(),
|
||||||
|
|
||||||
Ref = make_ref(),
|
Ref = make_ref(),
|
||||||
ok = esqlite3_nif:open(Db, Ref, self(), Filename),
|
ok = esqlite3_nif:open(Connection, Ref, self(), Filename),
|
||||||
case receive_answer(Ref, Timeout) of
|
case receive_answer(Ref, Timeout) of
|
||||||
ok ->
|
ok ->
|
||||||
{ok, Db};
|
{ok, Connection};
|
||||||
Other ->
|
Other ->
|
||||||
{error, Other}
|
{error, Other}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% @doc Execute Sql statement
|
%% @doc Execute a sql statement, returns a list with tuples.
|
||||||
|
q(Sql, Connection) ->
|
||||||
|
{ok, Statement} = prepare(Sql, Connection),
|
||||||
|
do_steps(Statement).
|
||||||
|
|
||||||
|
%%
|
||||||
|
map(F, Sql, Connection) ->
|
||||||
|
{ok, Statement} = prepare(Sql, Connection),
|
||||||
|
map_s(F, Statement).
|
||||||
|
|
||||||
|
%%
|
||||||
|
foreach(F, Sql, Connection) ->
|
||||||
|
{ok, Statement} = prepare(Sql, Connection),
|
||||||
|
foreach_s(F, Statement).
|
||||||
|
|
||||||
%%
|
%%
|
||||||
%% @spec exec(connection(), iolist()) -> integer() | {error, error_message()}
|
foreach_s(F, Statement) ->
|
||||||
exec(Db, Sql) ->
|
case try_step(Statement, 0) of
|
||||||
exec(Db, Sql, ?DEFAULT_TIMEOUT).
|
'$done' -> ok;
|
||||||
|
Row when is_tuple(Row) ->
|
||||||
|
F(Row),
|
||||||
|
foreach_s(F, Statement)
|
||||||
|
end.
|
||||||
|
|
||||||
|
%%
|
||||||
|
map_s(F, Statement) ->
|
||||||
|
case try_step(Statement, 0) of
|
||||||
|
'$done' ->
|
||||||
|
[];
|
||||||
|
Row ->
|
||||||
|
[F(Row) | map_s(F, Statement)]
|
||||||
|
end.
|
||||||
|
|
||||||
|
%%
|
||||||
|
do_steps(Statement) ->
|
||||||
|
case try_step(Statement, 0) of
|
||||||
|
'$done' ->
|
||||||
|
[];
|
||||||
|
Row when is_tuple(Row) ->
|
||||||
|
[Row | do_steps(Statement)]
|
||||||
|
end.
|
||||||
|
|
||||||
|
try_step(_Statement, Tries) when Tries > 5 ->
|
||||||
|
throw(too_many_tries);
|
||||||
|
try_step(Statement, Tries) ->
|
||||||
|
case esqlite3:step(Statement) of
|
||||||
|
'$busy' ->
|
||||||
|
try_step(Statement, Tries + 1);
|
||||||
|
Something ->
|
||||||
|
Something
|
||||||
|
end.
|
||||||
|
|
||||||
|
%% @doc Execute Sql statement, returns the number of affected rows.
|
||||||
|
%%
|
||||||
|
%% @spec exec(iolist(), connection()) -> integer() | {error, error_message()}
|
||||||
|
exec(Sql, Connection) ->
|
||||||
|
exec(Sql, Connection, ?DEFAULT_TIMEOUT).
|
||||||
|
|
||||||
%% @doc Execute
|
%% @doc Execute
|
||||||
%%
|
%%
|
||||||
%% @spec exec(connection(), iolist(), timeout()) -> integer() | {error, error_message()}
|
%% @spec exec(iolist(), connection(), timeout()) -> integer() | {error, error_message()}
|
||||||
exec(Db, Sql, Timeout) ->
|
exec(Sql, Connection, Timeout) ->
|
||||||
Ref = make_ref(),
|
Ref = make_ref(),
|
||||||
ok = esqlite3_nif:exec(Db, Ref, self(), add_eos(Sql)),
|
ok = esqlite3_nif:exec(Connection, Ref, self(), add_eos(Sql)),
|
||||||
receive_answer(Ref, Timeout).
|
receive_answer(Ref, Timeout).
|
||||||
|
|
||||||
%% @doc Prepare a statement
|
%% @doc Prepare a statement
|
||||||
%%
|
%%
|
||||||
%% @spec prepare(connection(), iolist()) -> {ok, prepared_statement()} | {error, error_message()}
|
%% @spec prepare(iolost(), connection()) -> {ok, prepared_statement()} | {error, error_message()}
|
||||||
prepare(Db, Sql) ->
|
prepare(Sql, Connection) ->
|
||||||
prepare(Db, Sql, ?DEFAULT_TIMEOUT).
|
prepare(Sql, Connection, ?DEFAULT_TIMEOUT).
|
||||||
|
|
||||||
%% @doc
|
%% @doc
|
||||||
%%
|
%%
|
||||||
%% @spec(connection(), iolist(), timeout()) -> {ok, prepared_statement()} | {error, error_message()}
|
%% @spec(iolist(), connection(), timeout()) -> {ok, prepared_statement()} | {error, error_message()}
|
||||||
prepare(Db, Sql, Timeout) ->
|
prepare(Sql, Connection, Timeout) ->
|
||||||
Ref = make_ref(),
|
Ref = make_ref(),
|
||||||
ok = esqlite3_nif:prepare(Db, Ref, self(), add_eos(Sql)),
|
ok = esqlite3_nif:prepare(Connection, Ref, self(), add_eos(Sql)),
|
||||||
receive_answer(Ref, Timeout).
|
receive_answer(Ref, Timeout).
|
||||||
|
|
||||||
%% @doc Step
|
%% @doc Step
|
||||||
@@ -110,21 +164,20 @@ bind(Stmt, Args, Timeout) ->
|
|||||||
%% @doc Close the database
|
%% @doc Close the database
|
||||||
%%
|
%%
|
||||||
%% @spec close(connection()) -> ok | {error, error_message()}
|
%% @spec close(connection()) -> ok | {error, error_message()}
|
||||||
close(Db) ->
|
close(Connection) ->
|
||||||
close(Db, ?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(), integer()) -> ok | {error, error_message()}
|
||||||
close(Db, Timeout) ->
|
close(Connection, Timeout) ->
|
||||||
Ref = make_ref(),
|
Ref = make_ref(),
|
||||||
ok = esqlite3_nif:close(Db, Ref, self()),
|
ok = esqlite3_nif:close(Connection, Ref, self()),
|
||||||
receive_answer(Ref, Timeout).
|
receive_answer(Ref, Timeout).
|
||||||
|
|
||||||
%% Internal functions
|
%% Internal functions
|
||||||
|
add_eos(IoList) ->
|
||||||
add_eos(String) when is_list(String) ->
|
[IoList, 0].
|
||||||
[String, 0].
|
|
||||||
|
|
||||||
receive_answer(Ref, Timeout) ->
|
receive_answer(Ref, Timeout) ->
|
||||||
receive
|
receive
|
||||||
|
|||||||
@@ -22,44 +22,45 @@ open_multiple_different_databases_test() ->
|
|||||||
|
|
||||||
simple_query_test() ->
|
simple_query_test() ->
|
||||||
{ok, Db} = esqlite3:open(":memory:"),
|
{ok, Db} = esqlite3:open(":memory:"),
|
||||||
ok = esqlite3:exec(Db, "begin;"),
|
ok = esqlite3:exec("begin;", Db),
|
||||||
ok = esqlite3:exec(Db, "create table test_table(one varchar(10), two int);"),
|
ok = esqlite3:exec("create table test_table(one varchar(10), two int);", Db),
|
||||||
ok = esqlite3:exec(Db, ["insert into test_table values(", "\"hello1\"", ",", "10" ");"]),
|
ok = esqlite3:exec(["insert into test_table values(", "\"hello1\"", ",", "10" ");"], Db),
|
||||||
ok = esqlite3:exec(Db, ["insert into test_table values(", "\"hello2\"", ",", "11" ");"]),
|
ok = esqlite3:exec(["insert into test_table values(", "\"hello2\"", ",", "11" ");"], Db),
|
||||||
ok = esqlite3:exec(Db, ["insert into test_table values(", "\"hello3\"", ",", "12" ");"]),
|
ok = esqlite3:exec(["insert into test_table values(", "\"hello3\"", ",", "12" ");"], Db),
|
||||||
ok = esqlite3:exec(Db, ["insert into test_table values(", "\"hello4\"", ",", "13" ");"]),
|
ok = esqlite3:exec(["insert into test_table values(", "\"hello4\"", ",", "13" ");"], Db),
|
||||||
ok = esqlite3:exec(Db, "commit;"),
|
ok = esqlite3:exec("commit;", Db),
|
||||||
ok = esqlite3:exec(Db, "select * from test_table;"),
|
ok = esqlite3:exec("select * from test_table;", Db),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
prepare_test() ->
|
prepare_test() ->
|
||||||
{ok, Db} = esqlite3:open(":memory:"),
|
{ok, Db} = esqlite3:open(":memory:"),
|
||||||
esqlite3:exec(Db, "begin;"),
|
esqlite3:exec("begin;", Db),
|
||||||
esqlite3:exec(Db, "create table test_table(one varchar(10), two int);"),
|
esqlite3:exec("create table test_table(one varchar(10), two int);", Db),
|
||||||
{ok, Statement} = esqlite3:prepare(Db, "insert into test_table values(\"one\", 2)"),
|
{ok, Statement} = esqlite3:prepare("insert into test_table values(\"one\", 2)", Db),
|
||||||
|
|
||||||
'$done' = esqlite3:step(Statement),
|
'$done' = esqlite3:step(Statement),
|
||||||
|
|
||||||
ok = esqlite3:exec(Db, ["insert into test_table values(", "\"hello4\"", ",", "13" ");"]),
|
ok = esqlite3:exec(["insert into test_table values(", "\"hello4\"", ",", "13" ");"], Db),
|
||||||
{ok, St2} = esqlite3:prepare(Db, "select * from test_table order by two"),
|
|
||||||
[{"one", 2}, {"hello4", 13}] = exec(St2),
|
%% Check if the values are there.
|
||||||
esqlite3:exec(Db, "commit;"),
|
[{"one", 2}, {"hello4", 13}] = esqlite3:q("select * from test_table order by two", Db),
|
||||||
|
esqlite3:exec("commit;", Db),
|
||||||
esqlite3:close(Db),
|
esqlite3:close(Db),
|
||||||
|
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
bind_test() ->
|
bind_test() ->
|
||||||
{ok, Db} = esqlite3:open(":memory:"),
|
{ok, Db} = esqlite3:open(":memory:"),
|
||||||
ok = esqlite3:exec(Db, "begin;"),
|
ok = esqlite3:exec("begin;", Db),
|
||||||
ok = esqlite3:exec(Db, "create table test_table(one varchar(10), two int);"),
|
ok = esqlite3:exec("create table test_table(one varchar(10), two int);", Db),
|
||||||
ok = esqlite3:exec(Db, ["insert into test_table values(", "\"hello1\"", ",", "10" ");"]),
|
ok = esqlite3:exec(["insert into test_table values(", "\"hello1\"", ",", "10" ");"], Db),
|
||||||
ok = esqlite3:exec(Db, ["insert into test_table values(", "\"hello2\"", ",", "11" ");"]),
|
ok = esqlite3:exec(["insert into test_table values(", "\"hello2\"", ",", "11" ");"], Db),
|
||||||
ok = esqlite3:exec(Db, ["insert into test_table values(", "\"hello3\"", ",", "12" ");"]),
|
ok = esqlite3:exec(["insert into test_table values(", "\"hello3\"", ",", "12" ");"], Db),
|
||||||
ok = esqlite3:exec(Db, ["insert into test_table values(", "\"hello4\"", ",", "13" ");"]),
|
ok = esqlite3:exec(["insert into test_table values(", "\"hello4\"", ",", "13" ");"], Db),
|
||||||
ok = esqlite3:exec(Db, "commit;"),
|
ok = esqlite3:exec("commit;", Db),
|
||||||
|
|
||||||
%% Create a prepared statement
|
%% Create a prepared statement
|
||||||
{ok, Statement} = esqlite3:prepare(Db, "insert into test_table values(?1, ?2)"),
|
{ok, Statement} = esqlite3:prepare("insert into test_table values(?1, ?2)", Db),
|
||||||
esqlite3:bind(Statement, [one, 2]),
|
esqlite3:bind(Statement, [one, 2]),
|
||||||
esqlite3:step(Statement),
|
esqlite3:step(Statement),
|
||||||
esqlite3:bind(Statement, ["three", 4]),
|
esqlite3:bind(Statement, ["three", 4]),
|
||||||
@@ -67,87 +68,20 @@ bind_test() ->
|
|||||||
esqlite3:bind(Statement, [<<"five">>, 6]),
|
esqlite3:bind(Statement, [<<"five">>, 6]),
|
||||||
esqlite3:step(Statement),
|
esqlite3:step(Statement),
|
||||||
|
|
||||||
[{"one", 2}] = q(Db, "select * from test_table where two = '2'"),
|
[{"one", 2}] = esqlite3:q("select * from test_table where two = '2'", Db),
|
||||||
[{"three", 4}] = q(Db, "select * from test_table where two = 4"),
|
[{"three", 4}] = esqlite3:q("select * from test_table where two = 4", Db),
|
||||||
[{<<"five">>, 6}] = q(Db, "select * from test_table where two = 6"),
|
[{<<"five">>, 6}] = esqlite3:q("select * from test_table where two = 6", Db),
|
||||||
|
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
gen_db_test() ->
|
%%gen_db_test() ->
|
||||||
{ok, Conn} = gen_db:open(sqlite, ":memory:"),
|
%% {ok, Conn} = gen_db:open(sqlite, ":memory:"),
|
||||||
[] = gen_db:execute("create table some_shit(hole_one varchar(10), hole_two int);", [], Conn),
|
%% [] = gen_db:execute("create table some_shit(hole_one varchar(10), hole_two int);", [], Conn),
|
||||||
[] = gen_db:execute("insert into some_shit values('dung', 100);", Conn),
|
%% [] = gen_db:execute("insert into some_shit values('dung', 100);", Conn),
|
||||||
[] = gen_db:execute("insert into some_shit values(?, ?);", ["manure", 1000], Conn),
|
%% [] = gen_db:execute("insert into some_shit values(?, ?);", ["manure", 1000], Conn),
|
||||||
ok.
|
%% ok.
|
||||||
|
|
||||||
|
|
||||||
%% Handy functions...
|
|
||||||
%%
|
|
||||||
q(Connection, Sql) ->
|
|
||||||
{ok, Statement} = esqlite3:prepare(Connection, Sql),
|
|
||||||
exec(Statement).
|
|
||||||
|
|
||||||
q(Connection, Sql, []) ->
|
|
||||||
q(Connection, Sql);
|
|
||||||
q(Connection, Sql, Args) ->
|
|
||||||
{ok, Statement} = esqlite3:prepare(Connection, Sql),
|
|
||||||
esqlite3:bind(Statement, Args),
|
|
||||||
exec(Statement).
|
|
||||||
|
|
||||||
|
|
||||||
exec(Statement) ->
|
|
||||||
exec(Statement, [], 0).
|
|
||||||
|
|
||||||
exec(_Statement, _Acc, Tries) when Tries > 5 ->
|
|
||||||
throw(too_many_tries);
|
|
||||||
exec(Statement, Acc, Tries) ->
|
|
||||||
case esqlite3:step(Statement) of
|
|
||||||
'$done' ->
|
|
||||||
lists:reverse(Acc);
|
|
||||||
'$busy' ->
|
|
||||||
timer:sleep(100),
|
|
||||||
exec(Statement, Acc, Tries + 1);
|
|
||||||
V when is_tuple(V) ->
|
|
||||||
exec(Statement, [V | Acc], 0)
|
|
||||||
end.
|
|
||||||
|
|
||||||
%% esqlite:bind(Statement, ":one", "hello"),
|
|
||||||
%% esqlite:bind(Statement, ":two", 11),
|
|
||||||
|
|
||||||
%% %% of
|
|
||||||
%% esqlite:bind(Statement, ["hello", 11]),
|
|
||||||
|
|
||||||
%% {ok, []} = esqlite:execute(Statement),
|
|
||||||
|
|
||||||
%% esqlite:exec(Db, "commit;"),
|
|
||||||
|
|
||||||
%%
|
|
||||||
|
|
||||||
% api...
|
|
||||||
|
|
||||||
%% q(Sql, Connection) ->
|
|
||||||
%% ok.
|
|
||||||
|
|
||||||
%% q(Sql, Args, Connection) ->
|
|
||||||
%% ok.
|
|
||||||
|
|
||||||
|
|
||||||
%%
|
|
||||||
%% options --
|
|
||||||
%% [{sync, bool()}
|
|
||||||
%% -- asynchronous or synchronous. Default true
|
|
||||||
%% {receiver, (pid()|function/1|{M, F, A}},
|
|
||||||
%% -- for asynchronous requests, to send the result to.
|
|
||||||
%% {stream, pid()},
|
|
||||||
%% -- instead of receiving all rows at once, send them one by one.
|
|
||||||
%% {..},
|
|
||||||
%% ]
|
|
||||||
%%
|
|
||||||
%% return
|
|
||||||
%% {ok, Result} -- result kan dan headers, rows of request-id (voor async)
|
|
||||||
%% {error, ...
|
|
||||||
%% q(Sql, Args, Options, Connection) ->
|
|
||||||
%% ok.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user