Better interface for bulk inserts

This commit is contained in:
Alexey Romanov
2010-10-22 10:26:34 +04:00
parent 3bfd35418c
commit c53f0de508
2 changed files with 93 additions and 27 deletions

View File

@@ -19,11 +19,12 @@
-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]).
-export([write/2, write/3]). -export([write/2, write/3, write_many/2, write_many/3]).
-export([update/4, update/5]). -export([update/4, update/5]).
-export([read_all/2, read_all/3, read/2, read/3, read/4]). -export([read_all/2, read_all/3, read/2, read/3, read/4]).
-export([delete/2, delete/3]). -export([delete/2, delete/3]).
-export([drop_table/1, drop_table/2]). -export([drop_table/1, drop_table/2]).
-export([begin_transaction/1, commit_transaction/1, rollback_transaction/1]).
-export([create_function/3]). -export([create_function/3]).
@@ -261,6 +262,30 @@ write(Tbl, Data) ->
write(Db, Tbl, Data) -> write(Db, Tbl, Data) ->
gen_server:call(Db, {write, Tbl, Data}). gen_server:call(Db, {write, Tbl, Data}).
%%--------------------------------------------------------------------
%% @spec write_many(Tbl :: atom(), Data) -> any()
%% Data = [[{Column :: atom(), Value :: sql_value()}]]
%% @doc
%% Write all records in Data into table Tbl. Value must be of the
%% same type as determined from table_info/2.
%% @end
%%--------------------------------------------------------------------
-spec write_many(atom(), [[{atom(), sql_value()}]]) -> any().
write_many(Tbl, Data) ->
write_many(?MODULE, Tbl, Data).
%%--------------------------------------------------------------------
%% @spec write_many(Db :: atom(), Tbl :: atom(), Data) -> term()
%% Data = [[{Column :: atom(), Value :: sql_value()}]]
%% @doc
%% Write all records in Data into table Tbl in database Db. Value
%% must be of the same type as determined from table_info/3.
%% @end
%%--------------------------------------------------------------------
-spec write_many(atom(), atom(), [[{atom(), sql_value()}]]) -> any().
write_many(Db, Tbl, Data) ->
gen_server:call(Db, {write_many, Tbl, Data}).
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% @spec update(Tbl :: atom(), Key :: atom(), Value, Data) -> Result %% @spec update(Tbl :: atom(), Key :: atom(), Value, Data) -> Result
%% Value = any() %% Value = any()
@@ -410,6 +435,39 @@ drop_table(Db, Tbl) ->
create_function(Db, FunctionName, Function) -> create_function(Db, FunctionName, Function) ->
gen_server:call(Db, {create_function, FunctionName, Function}). gen_server:call(Db, {create_function, FunctionName, Function}).
%%--------------------------------------------------------------------
%% @spec begin_transaction(Db :: atom()) -> term()
%% @doc
%% Begins a transaction in Db.
%% @end
%%--------------------------------------------------------------------
-spec begin_transaction(atom()) -> any().
begin_transaction(Db) ->
SQL = "BEGIN;",
sql_exec(Db, SQL).
%%--------------------------------------------------------------------
%% @spec commit_transaction(Db :: atom()) -> term()
%% @doc
%% Commits the current transaction in Db.
%% @end
%%--------------------------------------------------------------------
-spec commit_transaction(atom()) -> any().
commit_transaction(Db) ->
SQL = "COMMIT;",
sql_exec(Db, SQL).
%%--------------------------------------------------------------------
%% @spec rollback_transaction(Db :: atom()) -> term()
%% @doc
%% Rolls back the current transaction in Db.
%% @end
%%--------------------------------------------------------------------
-spec rollback_transaction(atom()) -> any().
rollback_transaction(Db) ->
SQL = "ROLLBACK;",
sql_exec(Db, SQL).
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% @spec value_to_sql_unsafe(Value :: sql_value()) -> iolist() %% @spec value_to_sql_unsafe(Value :: sql_value()) -> iolist()
%% @doc %% @doc
@@ -493,17 +551,17 @@ init(Options) ->
handle_call(close, _From, State) -> handle_call(close, _From, State) ->
Reply = ok, Reply = ok,
{stop, normal, Reply, State}; {stop, normal, Reply, State};
handle_call(list_tables, _From, #state{port = Port} = State) -> handle_call(list_tables, _From, State) ->
SQL = "select name from sqlite_master where type='table';", SQL = "select name from sqlite_master where type='table';",
Reply = exec(Port, {sql_exec, SQL}), Data = do_sql_exec(SQL, State),
TableList = proplists:get_value(rows, Reply), TableList = proplists:get_value(rows, Data),
TableNames = [erlang:list_to_atom(erlang:binary_to_list(Name)) || {Name} <- TableList], TableNames = [erlang:list_to_atom(erlang:binary_to_list(Name)) || {Name} <- TableList],
{reply, TableNames, State}; {reply, TableNames, State};
handle_call({table_info, Tbl}, _From, #state{port = Port} = State) -> handle_call({table_info, Tbl}, _From, State) ->
% make sure we only get table info. % make sure we only get table info.
% SQL Injection warning % SQL Injection warning
SQL = io_lib:format("select sql from sqlite_master where tbl_name = '~p' and type='table';", [Tbl]), SQL = io_lib:format("select sql from sqlite_master where tbl_name = '~p' and type='table';", [Tbl]),
Data = exec(Port, {sql_exec, SQL}), Data = do_sql_exec(SQL, State),
TableSql = proplists:get_value(rows, Data), TableSql = proplists:get_value(rows, Data),
case TableSql of case TableSql of
[{Info}] -> [{Info}] ->
@@ -518,41 +576,45 @@ handle_call({create_function, FunctionName, Function}, _From, #state{port = Port
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_sql_exec(SQL, State); do_handle_call_sql_exec(SQL, 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_sql_exec(SQL, State); do_handle_call_sql_exec(SQL, State);
handle_call({create_table, Tbl, Columns, Constraints}, _From, State) -> handle_call({create_table, Tbl, Columns, Constraints}, _From, State) ->
SQL = sqlite3_lib:create_table_sql(Tbl, Columns, Constraints), SQL = sqlite3_lib:create_table_sql(Tbl, Columns, Constraints),
do_handle_sql_exec(SQL, State); do_handle_call_sql_exec(SQL, State);
handle_call({update, Tbl, Key, Value, Data}, _From, State) -> handle_call({update, Tbl, Key, Value, Data}, _From, State) ->
SQL = sqlite3_lib:update_sql(Tbl, Key, Value, Data), SQL = sqlite3_lib:update_sql(Tbl, Key, Value, Data),
do_handle_sql_exec(SQL, State); do_handle_call_sql_exec(SQL, State);
handle_call({write, Tbl, Data}, _From, State) -> handle_call({write, Tbl, Data}, _From, State) ->
% insert into t1 (data,num) values ('This is sample data',3); % insert into t1 (data,num) values ('This is sample data',3);
SQL = sqlite3_lib:write_sql(Tbl, Data), SQL = sqlite3_lib:write_sql(Tbl, Data),
do_handle_sql_exec(SQL, State); do_handle_call_sql_exec(SQL, State);
handle_call({write_many, Tbl, DataList}, _From, State) ->
do_sql_exec("BEGIN;", State),
[do_sql_exec(sqlite3_lib:write_sql(Tbl, Data), State) || Data <- DataList],
do_handle_call_sql_exec("COMMIT;", State);
handle_call({read, Tbl}, _From, State) -> handle_call({read, Tbl}, _From, State) ->
% select * from Tbl where Key = Value; % select * from Tbl where Key = Value;
SQL = sqlite3_lib:read_sql(Tbl), SQL = sqlite3_lib:read_sql(Tbl),
do_handle_sql_exec(SQL, State); do_handle_call_sql_exec(SQL, State);
handle_call({read, Tbl, Columns}, _From, State) -> handle_call({read, Tbl, Columns}, _From, State) ->
SQL = sqlite3_lib:read_sql(Tbl, Columns), SQL = sqlite3_lib:read_sql(Tbl, Columns),
do_handle_sql_exec(SQL, State); do_handle_call_sql_exec(SQL, State);
handle_call({read, Tbl, Key, Value}, _From, State) -> handle_call({read, Tbl, Key, Value}, _From, State) ->
% select * from Tbl where Key = Value; % select * from Tbl where Key = Value;
SQL = sqlite3_lib:read_sql(Tbl, Key, Value), SQL = sqlite3_lib:read_sql(Tbl, Key, Value),
do_handle_sql_exec(SQL, State); do_handle_call_sql_exec(SQL, State);
handle_call({read, Tbl, Key, Value, Columns}, _From, State) -> handle_call({read, Tbl, Key, Value, Columns}, _From, State) ->
SQL = sqlite3_lib:read_sql(Tbl, Key, Value, Columns), SQL = sqlite3_lib:read_sql(Tbl, Key, Value, Columns),
do_handle_sql_exec(SQL, State); do_handle_call_sql_exec(SQL, State);
handle_call({delete, Tbl, {Key, Value}}, _From, State) -> handle_call({delete, Tbl, {Key, Value}}, _From, State) ->
% delete from Tbl where Key = Value; % delete from Tbl where Key = Value;
SQL = sqlite3_lib:delete_sql(Tbl, Key, Value), SQL = sqlite3_lib:delete_sql(Tbl, Key, Value),
do_handle_sql_exec(SQL, State); do_handle_call_sql_exec(SQL, State);
handle_call({drop_table, Tbl}, _From, State) -> handle_call({drop_table, Tbl}, _From, State) ->
SQL = sqlite3_lib:drop_table_sql(Tbl), SQL = sqlite3_lib:drop_table_sql(Tbl),
do_handle_sql_exec(SQL, State); do_handle_call_sql_exec(SQL, State);
handle_call(_Request, _From, State) -> handle_call(_Request, _From, State) ->
Reply = ok, Reply = ok,
{reply, Reply, State}. {reply, Reply, State}.
@@ -621,11 +683,15 @@ code_change(_OldVsn, State, _Extra) ->
create_port_cmd(Dbase) -> create_port_cmd(Dbase) ->
atom_to_list(?DRIVER_NAME) ++ " " ++ Dbase. atom_to_list(?DRIVER_NAME) ++ " " ++ Dbase.
do_handle_sql_exec(SQL, #state{port = Port} = State) -> do_handle_call_sql_exec(SQL, State) ->
?dbg("SQL: ~s~n", [SQL]), ?dbg("SQL: ~s~n", [SQL]),
Reply = exec(Port, {sql_exec, SQL}), Reply = do_sql_exec(SQL, State),
{reply, Reply, State}. {reply, Reply, State}.
do_sql_exec(SQL, #state{port = Port}) ->
?dbg("SQL: ~s~n", [SQL]),
exec(Port, {sql_exec, SQL}).
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)),

View File

@@ -7,14 +7,13 @@
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
-module(sqlite3_test). -module(sqlite3_test).
%%==================================================================== %% ====================================================================
%% API %% API
%%==================================================================== %% ====================================================================
%%-------------------------------------------------------------------- %% --------------------------------------------------------------------
%% Function: %% Function:
%% Description: %% Description:
%%-------------------------------------------------------------------- %% --------------------------------------------------------------------
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl"). -include_lib("eunit/include/eunit.hrl").
drop_all_tables(Db) -> drop_all_tables(Db) ->
@@ -84,7 +83,10 @@ select_many_records_test() ->
sqlite3:open(ct), sqlite3:open(ct),
drop_table_if_exists(ct, many_records), drop_table_if_exists(ct, many_records),
sqlite3:create_table(ct, many_records, [{id, integer}, {name, text}]), sqlite3:create_table(ct, many_records, [{id, integer}, {name, text}]),
[sqlite3:write(ct, many_records, [{id, X}, {name, "bar"}]) || X <- lists:seq(1, 1024)], %% takes very long :( sqlite3:write_many(ct, many_records, [[{id, X}, {name, "bar"}] || X <- lists:seq(1, 1024)]),
%% sqlite3:begin_transaction(ct),
%% [sqlite3:write(ct, many_records, [{id, X}, {name, "bar"}]) || X <- lists:seq(1, 1024)], %% takes very long :(
%% sqlite3:commit_transaction(ct),
Columns = ["id", "name"], Columns = ["id", "name"],
?assertEqual( ?assertEqual(
[{columns, Columns}, {rows, [{1, <<"bar">>}]}], [{columns, Columns}, {rows, [{1, <<"bar">>}]}],
@@ -107,8 +109,6 @@ nonexistent_table_info_test() ->
sqlite3:open(ct), sqlite3:open(ct),
?assertEqual(table_does_not_exist, sqlite3:table_info(ct, nonexistent)), ?assertEqual(table_does_not_exist, sqlite3:table_info(ct, nonexistent)),
sqlite3:close(ct). sqlite3:close(ct).
-endif.
% create, read, update, delete % create, read, update, delete
%%==================================================================== %%====================================================================