Added first test for parametrized statements
This commit is contained in:
2
Makefile
2
Makefile
@@ -8,7 +8,7 @@ compile:
|
|||||||
$(REBAR_COMPILE)
|
$(REBAR_COMPILE)
|
||||||
|
|
||||||
test:
|
test:
|
||||||
$(REBAR) eunit
|
$(REBAR_COMPILE) eunit
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
-rm -rf deps ebin priv doc/* .eunit c_src/*.o
|
-rm -rf deps ebin priv doc/* .eunit c_src/*.o
|
||||||
|
|||||||
@@ -103,7 +103,6 @@ static void stop(ErlDrvData handle) {
|
|||||||
static int control(ErlDrvData drv_data, unsigned int command, char *buf,
|
static int control(ErlDrvData drv_data, unsigned int command, char *buf,
|
||||||
int len, char **rbuf, int rlen) {
|
int len, char **rbuf, int rlen) {
|
||||||
sqlite3_drv_t* driver_data = (sqlite3_drv_t*) drv_data;
|
sqlite3_drv_t* driver_data = (sqlite3_drv_t*) drv_data;
|
||||||
|
|
||||||
switch (command) {
|
switch (command) {
|
||||||
case CMD_SQL_EXEC:
|
case CMD_SQL_EXEC:
|
||||||
sql_exec(driver_data, buf, len);
|
sql_exec(driver_data, buf, len);
|
||||||
@@ -422,11 +421,12 @@ static void ready_async(ErlDrvData drv_data, ErlDrvThreadData thread_data) {
|
|||||||
|
|
||||||
// Unknown Command
|
// Unknown Command
|
||||||
static int unknown(sqlite3_drv_t *drv, char *command, int command_size) {
|
static int unknown(sqlite3_drv_t *drv, char *command, int command_size) {
|
||||||
// Return {error, unknown_command}
|
// Return {Port, error, unknown_command}
|
||||||
ErlDrvTermData spec[] = {
|
ErlDrvTermData spec[] = {
|
||||||
|
ERL_DRV_PORT, driver_mk_port(drv->port),
|
||||||
ERL_DRV_ATOM, drv->atom_error,
|
ERL_DRV_ATOM, drv->atom_error,
|
||||||
ERL_DRV_ATOM, drv->atom_unknown_cmd,
|
ERL_DRV_ATOM, drv->atom_unknown_cmd,
|
||||||
ERL_DRV_TUPLE, 2
|
ERL_DRV_TUPLE, 3
|
||||||
};
|
};
|
||||||
return driver_output_term(drv->port, spec, sizeof(spec) / sizeof(spec[0]));
|
return driver_output_term(drv->port, spec, sizeof(spec) / sizeof(spec[0]));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
-export([open/1, open/2]).
|
-export([open/1, open/2]).
|
||||||
-export([start_link/1, start_link/2]).
|
-export([start_link/1, start_link/2]).
|
||||||
-export([stop/0, close/1]).
|
-export([stop/0, close/1]).
|
||||||
-export([sql_exec/1, sql_exec/2]).
|
-export([sql_exec/1, sql_exec/2, sql_exec/3]).
|
||||||
|
|
||||||
-export([create_table/2, create_table/3, create_table/4]).
|
-export([create_table/2, create_table/3, create_table/4]).
|
||||||
-export([list_tables/0, list_tables/1, table_info/1, table_info/2]).
|
-export([list_tables/0, list_tables/1, table_info/1, table_info/2]).
|
||||||
@@ -157,6 +157,18 @@ sql_exec(SQL) ->
|
|||||||
sql_exec(Db, SQL) ->
|
sql_exec(Db, SQL) ->
|
||||||
gen_server:call(Db, {sql_exec, SQL}).
|
gen_server:call(Db, {sql_exec, SQL}).
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% @spec sql_exec(Db :: atom(), Sql :: iodata(), Params) -> any()
|
||||||
|
%% Params = [sql_value() | {atom() | string() | integer(), sql_value()}]
|
||||||
|
%% @doc
|
||||||
|
%% Executes the Sql statement with parameters Params directly on the Db
|
||||||
|
%% database. Returns the result of the Sql call.
|
||||||
|
%% @end
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
-spec sql_exec(atom(), iodata(), [sql_value() | {atom() | string() | integer(), sql_value()}]) -> any().
|
||||||
|
sql_exec(Db, SQL, Params) ->
|
||||||
|
gen_server:call(Db, {sql_bind_and_exec, SQL, Params}).
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
%% @spec create_table(Tbl :: atom(), TblInfo :: [{atom(), atom()}]) -> any()
|
%% @spec create_table(Tbl :: atom(), TblInfo :: [{atom(), atom()}]) -> any()
|
||||||
%% @doc
|
%% @doc
|
||||||
@@ -575,12 +587,13 @@ handle_call({table_info, Tbl}, _From, State) ->
|
|||||||
{reply, table_does_not_exist, State}
|
{reply, table_does_not_exist, State}
|
||||||
end;
|
end;
|
||||||
handle_call({create_function, FunctionName, Function}, _From, #state{port = Port} = State) ->
|
handle_call({create_function, FunctionName, Function}, _From, #state{port = Port} = State) ->
|
||||||
% make sure we only get table info.
|
|
||||||
% SQL Injection warning
|
|
||||||
Reply = exec(Port, {create_function, FunctionName, Function}),
|
Reply = exec(Port, {create_function, FunctionName, Function}),
|
||||||
{reply, Reply, State};
|
{reply, Reply, State};
|
||||||
handle_call({sql_exec, SQL}, _From, State) ->
|
handle_call({sql_exec, SQL}, _From, State) ->
|
||||||
do_handle_call_sql_exec(SQL, State);
|
do_handle_call_sql_exec(SQL, State);
|
||||||
|
handle_call({sql_bind_and_exec, SQL, Params}, _From, State) ->
|
||||||
|
Reply = do_sql_bind_and_exec(SQL, Params, State),
|
||||||
|
{reply, Reply, State};
|
||||||
handle_call({create_table, Tbl, Columns}, _From, State) ->
|
handle_call({create_table, Tbl, Columns}, _From, State) ->
|
||||||
SQL = sqlite3_lib:create_table_sql(Tbl, Columns),
|
SQL = sqlite3_lib:create_table_sql(Tbl, Columns),
|
||||||
do_handle_call_sql_exec(SQL, State);
|
do_handle_call_sql_exec(SQL, State);
|
||||||
@@ -693,6 +706,7 @@ get_priv_dir() ->
|
|||||||
|
|
||||||
-define(SQL_EXEC_COMMAND, 2).
|
-define(SQL_EXEC_COMMAND, 2).
|
||||||
-define(SQL_CREATE_FUNCTION, 3).
|
-define(SQL_CREATE_FUNCTION, 3).
|
||||||
|
-define(SQL_BIND_AND_EXEC_COMMAND, 4).
|
||||||
|
|
||||||
create_port_cmd(DbFile) ->
|
create_port_cmd(DbFile) ->
|
||||||
atom_to_list(?DRIVER_NAME) ++ " " ++ DbFile.
|
atom_to_list(?DRIVER_NAME) ++ " " ++ DbFile.
|
||||||
@@ -705,17 +719,25 @@ do_sql_exec(SQL, #state{port = Port}) ->
|
|||||||
?dbg("SQL: ~s~n", [SQL]),
|
?dbg("SQL: ~s~n", [SQL]),
|
||||||
exec(Port, {sql_exec, SQL}).
|
exec(Port, {sql_exec, SQL}).
|
||||||
|
|
||||||
|
do_sql_bind_and_exec(SQL, Params, #state{port = Port}) ->
|
||||||
|
?dbg("SQL: ~s; Parameters: ~p~n", [SQL, Params]),
|
||||||
|
exec(Port, {sql_bind_and_exec, SQL, Params}).
|
||||||
|
|
||||||
exec(_Port, {create_function, _FunctionName, _Function}) ->
|
exec(_Port, {create_function, _FunctionName, _Function}) ->
|
||||||
error_logger:error_report([{application, sqlite3}, "NOT IMPL YET"]);
|
error_logger:error_report([{application, sqlite3}, "NOT IMPL YET"]);
|
||||||
%port_control(Port, ?SQL_CREATE_FUNCTION, list_to_binary(Cmd)),
|
%port_control(Port, ?SQL_CREATE_FUNCTION, list_to_binary(Cmd)),
|
||||||
%wait_result(Port);
|
%wait_result(Port);
|
||||||
exec(Port, {sql_exec, Cmd}) ->
|
exec(Port, {sql_exec, Cmd}) ->
|
||||||
port_control(Port, ?SQL_EXEC_COMMAND, Cmd),
|
port_control(Port, ?SQL_EXEC_COMMAND, Cmd),
|
||||||
|
wait_result(Port);
|
||||||
|
exec(Port, {sql_bind_and_exec, SQL, Params}) ->
|
||||||
|
Bin = term_to_binary({iolist_to_binary(SQL), Params}),
|
||||||
|
port_control(Port, ?SQL_BIND_AND_EXEC_COMMAND, Bin),
|
||||||
|
io:format(user, "Sending sql_bind_and_exec to port", []),
|
||||||
wait_result(Port).
|
wait_result(Port).
|
||||||
|
|
||||||
wait_result(Port) ->
|
wait_result(Port) ->
|
||||||
receive
|
receive
|
||||||
%% Messages given at http://www.erlang.org/doc/reference_manual/ports.html
|
|
||||||
{Port, error, Reason} ->
|
{Port, error, Reason} ->
|
||||||
error_logger:error_msg("sqlite3 driver error: ~s~n", [Reason]),
|
error_logger:error_msg("sqlite3 driver error: ~s~n", [Reason]),
|
||||||
% ?dbg("Error: ~p~n", [Reason]),
|
% ?dbg("Error: ~p~n", [Reason]),
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ all_test_() ->
|
|||||||
fun open_db/0,
|
fun open_db/0,
|
||||||
fun close_db/1,
|
fun close_db/1,
|
||||||
[?FuncTest(basic_functionality),
|
[?FuncTest(basic_functionality),
|
||||||
|
?FuncTest(parametrized),
|
||||||
?FuncTest(blob),
|
?FuncTest(blob),
|
||||||
?FuncTest(escaping),
|
?FuncTest(escaping),
|
||||||
?FuncTest(select_many_records),
|
?FuncTest(select_many_records),
|
||||||
@@ -102,6 +103,14 @@ basic_functionality() ->
|
|||||||
ok,
|
ok,
|
||||||
sqlite3:drop_table(ct, user)).
|
sqlite3:drop_table(ct, user)).
|
||||||
|
|
||||||
|
parametrized() ->
|
||||||
|
drop_table_if_exists(ct, user1),
|
||||||
|
sqlite3:create_table(ct, user1, [{id, integer}, {name, text}]),
|
||||||
|
sqlite3:sql_exec(ct, "INSERT INTO user1 (id, name) VALUES (?, ?)", [{1, 1}, {2, "john"}]),
|
||||||
|
?assertEqual(
|
||||||
|
[{columns, ["id", "name"]}, {rows, [{1, <<"john">>}]}],
|
||||||
|
sqlite3:sql_exec(ct, "select * from user1;")).
|
||||||
|
|
||||||
blob() ->
|
blob() ->
|
||||||
drop_table_if_exists(ct, blobs),
|
drop_table_if_exists(ct, blobs),
|
||||||
sqlite3:create_table(ct, blobs, [{blob_col, blob}]),
|
sqlite3:create_table(ct, blobs, [{blob_col, blob}]),
|
||||||
@@ -139,21 +148,21 @@ select_many_records() ->
|
|||||||
N,
|
N,
|
||||||
length(rows(sqlite3:sql_exec(ct, "select * from many_records;")))).
|
length(rows(sqlite3:sql_exec(ct, "select * from many_records;")))).
|
||||||
|
|
||||||
%% note that inserts are actually serialized by gen_server
|
%% %% note that inserts are actually serialized by gen_server
|
||||||
concurrent_inserts_test() ->
|
%% concurrent_inserts_test() ->
|
||||||
N = 1024,
|
%% N = 1024,
|
||||||
sqlite3:open(concurrent, [in_memory]), %% doing this test not in memory is much slower!
|
%% sqlite3:open(concurrent, [in_memory]), %% doing this test not in memory is much slower!
|
||||||
drop_table_if_exists(concurrent, t),
|
%% drop_table_if_exists(concurrent, t),
|
||||||
sqlite3:create_table(concurrent, t, [{id0, integer}]),
|
%% sqlite3:create_table(concurrent, t, [{id0, integer}]),
|
||||||
Self = self(),
|
%% Self = self(),
|
||||||
[spawn(fun () ->
|
%% [spawn(fun () ->
|
||||||
sqlite3:write(concurrent, t, [{id0, X}]),
|
%% sqlite3:write(concurrent, t, [{id0, X}]),
|
||||||
Self ! {finished, N}
|
%% Self ! {finished, N}
|
||||||
end) || X <- lists:seq(1, N)],
|
%% end) || X <- lists:seq(1, N)],
|
||||||
loop_concurrent_inserts(N),
|
%% loop_concurrent_inserts(N),
|
||||||
?assertEqual(
|
%% ?assertEqual(
|
||||||
N, length(rows(sqlite3:read_all(concurrent, t)))),
|
%% N, length(rows(sqlite3:read_all(concurrent, t)))),
|
||||||
sqlite3:close(concurrent).
|
%% sqlite3:close(concurrent).
|
||||||
|
|
||||||
loop_concurrent_inserts(0) ->
|
loop_concurrent_inserts(0) ->
|
||||||
ok;
|
ok;
|
||||||
|
|||||||
Reference in New Issue
Block a user