Added interface for prepared statements on the Erlang side

This commit is contained in:
Alexey Romanov
2010-12-07 18:58:15 +03:00
parent 51e9481aa7
commit 75f5544f10
2 changed files with 91 additions and 11 deletions

View File

@@ -23,5 +23,6 @@
-type(sql_value() :: number() | ?NULL_ATOM | iodata() | {blob, binary()}). -type(sql_value() :: number() | ?NULL_ATOM | iodata() | {blob, binary()}).
-type(sqlite_error() :: {error, integer(), string()}). -type(sqlite_error() :: {error, integer(), string()}).
-type(sql_params() :: [sql_value() | {atom() | string() | integer(), sql_value()}]).
-type(sql_non_query_result() :: ok | sqlite_error() | {rowid, integer()}). -type(sql_non_query_result() :: ok | sqlite_error() | {rowid, integer()}).
-type(sql_result() :: sql_non_query_result() | [{columns, [string()]} | {rows, [tuple()]}]). -type(sql_result() :: sql_non_query_result() | [{columns, [string()]} | {rows, [tuple()]}]).

View File

@@ -16,7 +16,7 @@
-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, sql_exec/3]). -export([sql_exec/1, sql_exec/2, sql_exec/3]).
-export([prepare/2, bind/3, next/2, reset/2, clear_bindings/2, finalize/2]).
-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, write_many/2, write_many/3]). -export([write/2, write/3, write_many/2, write_many/3]).
@@ -35,7 +35,7 @@
terminate/2, code_change/3]). terminate/2, code_change/3]).
-define('DRIVER_NAME', 'sqlite3_drv'). -define('DRIVER_NAME', 'sqlite3_drv').
-record(state, {port, ops = []}). -record(state, {port, ops = [], refs = dict:new()}).
%%==================================================================== %%====================================================================
%% API %% API
@@ -168,6 +168,30 @@ sql_exec(Db, SQL) ->
sql_exec(Db, SQL, Params) -> sql_exec(Db, SQL, Params) ->
gen_server:call(Db, {sql_bind_and_exec, SQL, Params}). gen_server:call(Db, {sql_bind_and_exec, SQL, Params}).
-spec prepare(atom(), iodata()) -> {ok, reference()} | sqlite_error().
prepare(Db, SQL) ->
gen_server:call(Db, {prepare, SQL}).
-spec bind(atom(), reference(), sql_params()) -> sql_non_query_result().
bind(Db, Ref, Params) ->
gen_server:call(Db, {bind, Ref, Params}).
-spec next(atom(), reference()) -> tuple() | done | sqlite_error().
next(Db, Ref) ->
gen_server:call(Db, {next, Ref}).
-spec reset(atom(), reference()) -> sql_non_query_result().
reset(Db, Ref) ->
gen_server:call(Db, {reset, Ref}).
-spec clear_bindings(atom(), reference()) -> sql_non_query_result().
clear_bindings(Db, Ref) ->
gen_server:call(Db, {clear_bindings, Ref}).
-spec finalize(atom(), reference()) -> sql_non_query_result().
finalize(Db, Ref) ->
gen_server:call(Db, {finalize, Ref}).
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% @spec create_table(Tbl :: atom(), TblInfo :: [{atom(), atom()}]) -> sql_non_query_result() %% @spec create_table(Tbl :: atom(), TblInfo :: [{atom(), atom()}]) -> sql_non_query_result()
%% @doc %% @doc
@@ -632,8 +656,46 @@ handle_call({delete, Tbl, {Key, Value}}, _From, 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_call_sql_exec(SQL, State); do_handle_call_sql_exec(SQL, State);
handle_call({prepare, SQL}, _From, State = #state{port = Port, refs = Refs}) ->
case exec(Port, {prepare, SQL}) of
Index when is_integer(Index) ->
Ref = erlang:make_ref(),
Reply = {ok, Ref},
NewState = State#state{refs = dict:append(Ref, Index, Refs)};
Error ->
Reply = Error,
NewState = State
end,
{reply, Reply, NewState};
handle_call({bind, Ref, Params}, _From, State = #state{port = Port, refs = Refs}) ->
Index = dict:fetch(Ref, Refs),
Reply = exec(Port, {bind, Index, Params}),
{reply, Reply, State};
handle_call({next, Ref}, _From, State = #state{port = Port, refs = Refs}) ->
Index = dict:fetch(Ref, Refs),
Reply = exec(Port, {next, Index}),
{reply, Reply, State};
handle_call({reset, Ref}, _From, State = #state{port = Port, refs = Refs}) ->
Index = dict:fetch(Ref, Refs),
Reply = exec(Port, {reset, Index}),
{reply, Reply, State};
handle_call({clear_bindings, Ref}, _From, State = #state{port = Port, refs = Refs}) ->
Index = dict:fetch(Ref, Refs),
Reply = exec(Port, {clear_bindings, Index}),
{reply, Reply, State};
handle_call({finalize, Ref}, _From, State = #state{port = Port, refs = Refs}) ->
Index = dict:fetch(Ref, Refs),
case exec(Port, {finalize, Index}) of
ok ->
Reply = ok,
NewState = dict:erase(Ref, Refs);
Error ->
Reply = Error,
NewState = State
end,
{reply, Reply, NewState};
handle_call(_Request, _From, State) -> handle_call(_Request, _From, State) ->
Reply = ok, Reply = unknown_request,
{reply, Reply, State}. {reply, Reply, State}.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
@@ -719,12 +781,12 @@ 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). -define(SQL_BIND_AND_EXEC_COMMAND, 4).
-define(CMD_PREPARE, 5). -define(PREPARE, 5).
-define(CMD_PREPARED_BIND, 6). -define(PREPARED_BIND, 6).
-define(CMD_PREPARED_STEP, 7). -define(PREPARED_STEP, 7).
-define(CMD_PREPARED_RESET, 8). -define(PREPARED_RESET, 8).
-define(CMD_PREPARED_CLEAR_BINDINGS, 9). -define(PREPARED_CLEAR_BINDINGS, 9).
-define(CMD_PREPARED_FINALIZE, 10). -define(PREPARED_FINALIZE, 10).
create_port_cmd(DbFile) -> create_port_cmd(DbFile) ->
atom_to_list(?DRIVER_NAME) ++ " " ++ DbFile. atom_to_list(?DRIVER_NAME) ++ " " ++ DbFile.
@@ -745,12 +807,29 @@ 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, SQL}) ->
port_control(Port, ?SQL_EXEC_COMMAND, Cmd), port_control(Port, ?SQL_EXEC_COMMAND, SQL),
wait_result(Port); wait_result(Port);
exec(Port, {sql_bind_and_exec, SQL, Params}) -> exec(Port, {sql_bind_and_exec, SQL, Params}) ->
Bin = term_to_binary({iolist_to_binary(SQL), Params}), Bin = term_to_binary({iolist_to_binary(SQL), Params}),
port_control(Port, ?SQL_BIND_AND_EXEC_COMMAND, Bin), port_control(Port, ?SQL_BIND_AND_EXEC_COMMAND, Bin),
wait_result(Port);
exec(Port, {prepare, SQL}) ->
port_control(Port, ?PREPARE, SQL),
wait_result(Port);
exec(Port, {bind, Index, Params}) ->
Bin = term_to_binary({Index, Params}),
port_control(Port, ?SQL_BIND_AND_EXEC_COMMAND, Bin),
wait_result(Port);
exec(Port, {Cmd, Index}) when is_integer(Index) ->
CmdCode = case Cmd of
next -> ?PREPARED_STEP;
reset -> ?PREPARED_RESET;
clear_bindings -> ?PREPARED_CLEAR_BINDINGS;
finalize -> ?PREPARED_FINALIZE
end,
Bin = term_to_binary(Index),
port_control(Port, CmdCode, Bin),
wait_result(Port). wait_result(Port).
wait_result(Port) -> wait_result(Port) ->