%%% The MIT License (MIT) %%% Copyright (c) 2016-2018 Hajime Nakagami -module(efirebirdsql_protocol). -export([connect/5, connect/6, detach/1, begin_transaction/2]). -export([allocate_statement/1, prepare_statement/2, free_statement/1]). -export([execute/2, execute2/2, fetchrows/1, description/2]). -export([commit/1, rollback/1]). -include("efirebirdsql.hrl"). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Utility functions in module connect_database(TcpMod, Sock, Username, Password, Database, PageSize, IsCreateDB, State) -> case IsCreateDB of true -> TcpMod:send(Sock, efirebirdsql_op:op_create( Username, Password, Database, PageSize, State#state.accept_version)); false -> TcpMod:send(Sock, efirebirdsql_op:op_attach( Username, Password, Database, State#state.accept_version)) end, case efirebirdsql_op:get_response(TcpMod, Sock) of {op_response, {ok, Handle, _}} -> State2 = State#state{db_handle=Handle}, allocate_statement(State2); {op_response, {error, Msg}} -> {{error, Msg}, State} end. connect(Host, Username, Password, Database, IsCreateDB, PageSize, State) -> TcpMod = State#state.mod, Sock = State#state.sock, TcpMod:send(Sock, efirebirdsql_op:op_connect(Host, Username, Password, Database, State#state.public_key, State#state.wire_crypt)), case efirebirdsql_op:get_response(TcpMod, Sock) of {op_accept, {AcceptVersion, _AcceptType}} -> connect_database(TcpMod, Sock, Username, Password, Database, PageSize, IsCreateDB, State#state{accept_version=AcceptVersion}); {op_cond_accept, {_AcceptVersion, _AcceptType}} -> io:format("op_cond_accept"); {op_accept_data, {_AcceptVersion, _AcceptType}} -> io:format("op_accept_data"); {op_reject, _} -> {{error, "Connection Rejected"}, State} end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % public functions connect(Host, Username, Password, Database, Options) -> connect(Host, Username, Password, Database, Options, #state{}). connect(Host, Username, Password, Database, Options, State) -> SockOptions = [{active, false}, {packet, raw}, binary], Port = proplists:get_value(port, Options, 3050), IsCreateDB = proplists:get_value(createdb, Options, false), PageSize = proplists:get_value(pagesize, Options, 4096), {Pub ,Private} = efirebirdsql_srp:client_seed(), case (State#state.mod):connect(Host, Port, SockOptions) of {ok, Sock} -> State2 = State#state{ sock=Sock, public_key=Pub, private_key=Private, wire_crypt=proplists:get_value(wire_crypt, Options, false) }, {R, NewState} = connect(Host, Username, Password, Database, IsCreateDB, PageSize, State2), {ok, R, NewState}; {error, Reason} -> {error, Reason, State} end. detach(State) -> TcpMod = State#state.mod, Sock = State#state.sock, DbHandle = State#state.db_handle, TcpMod:send(Sock, efirebirdsql_op:op_detach(DbHandle)), case efirebirdsql_op:get_response(TcpMod, Sock) of {op_response, {ok, _, _}} -> ok; {op_response, {error, Msg}} -> {error, Msg} end. %% Transaction begin_transaction(Tpb, State) -> TcpMod = State#state.mod, Sock = State#state.sock, DbHandle = State#state.db_handle, TcpMod:send(Sock, efirebirdsql_op:op_transaction(DbHandle, Tpb)), case efirebirdsql_op:get_response(TcpMod, Sock) of {op_response, {ok, Handle, _}} -> {ok, Handle}; {op_response, {error, Msg}} -> {error, Msg} end. %% allocate, prepare and free statement allocate_statement(State) -> TcpMod = State#state.mod, Sock = State#state.sock, DbHandle = State#state.db_handle, TcpMod:send(Sock, efirebirdsql_op:op_allocate_statement(DbHandle)), case efirebirdsql_op:get_response(TcpMod, Sock) of {op_response, {ok, Handle, _}} -> {ok, State#state{stmt_handle=Handle}}; {op_response, {error, Msg}} ->{error, Msg} end. prepare_statement(Sql, State) -> TcpMod = State#state.mod, Sock = State#state.sock, TransHandle = State#state.trans_handle, StmtHandle = State#state.stmt_handle, TcpMod:send(Sock, efirebirdsql_op:op_prepare_statement(TransHandle, StmtHandle, Sql)), efirebirdsql_op:get_prepare_statement_response(TcpMod, Sock, StmtHandle). free_statement(State) -> TcpMod = State#state.mod, Sock = State#state.sock, StmtHandle = State#state.stmt_handle, TcpMod:send(Sock, efirebirdsql_op:op_free_statement(StmtHandle)), case efirebirdsql_op:get_response(TcpMod, Sock) of {op_response, {ok, _, _}} -> ok; {op_response, {error, Msg}} -> {error, Msg} end. %% Execute, Fetch and Description execute(Params, State) -> TcpMod = State#state.mod, Sock = State#state.sock, TransHandle = State#state.trans_handle, StmtHandle = State#state.stmt_handle, TcpMod:send(Sock, efirebirdsql_op:op_execute(TransHandle, StmtHandle, Params)), case efirebirdsql_op:get_response(TcpMod, Sock) of {op_response, {ok, _, _}} -> ok; {op_response, {error, Msg}} -> {error, Msg} end. execute2(Param, State) -> TcpMod = State#state.mod, Sock = State#state.sock, TransHandle = State#state.trans_handle, StmtHandle = State#state.stmt_handle, XSqlVars = State#state.xsqlvars, TcpMod:send(Sock, efirebirdsql_op:op_execute2(TransHandle, StmtHandle, Param, XSqlVars)), Row = efirebirdsql_op:get_sql_response(TcpMod, Sock, XSqlVars), case efirebirdsql_op:get_response(TcpMod, Sock) of {op_response, {ok, _, _}} -> {ok, Row}; {op_response, {error, Msg}} -> {error, Msg} end. fetchrows(Results, State) -> TcpMod = State#state.mod, Sock = State#state.sock, StmtHandle = State#state.stmt_handle, XSqlVars = State#state.xsqlvars, TcpMod:send(Sock, efirebirdsql_op:op_fetch(StmtHandle, XSqlVars)), {op_fetch_response, {NewResults, MoreData}} = efirebirdsql_op:get_fetch_response(TcpMod, Sock, XSqlVars), case MoreData of true -> fetchrows(lists:flatten([Results, NewResults]), State); false -> {ok, Results ++ NewResults} end. fetchrows(State) -> fetchrows([], State). description([], XSqlVar) -> lists:reverse(XSqlVar); description(InXSqlVars, XSqlVar) -> [H | T] = InXSqlVars, description(T, [{H#column.name, H#column.type, H#column.scale, H#column.length, H#column.null_ind} | XSqlVar]). %% Commit and rollback commit(State) -> TcpMod = State#state.mod, Sock = State#state.sock, TransHandle = State#state.trans_handle, TcpMod:send(Sock, efirebirdsql_op:op_commit_retaining(TransHandle)), case efirebirdsql_op:get_response(TcpMod, Sock) of {op_response, {ok, _, _}} -> ok; {op_response, {error, Msg}} -> {error, Msg} end. rollback(State) -> TcpMod = State#state.mod, Sock = State#state.sock, TransHandle = State#state.trans_handle, TcpMod:send(Sock, efirebirdsql_op:op_rollback_retaining(TransHandle)), case efirebirdsql_op:get_response(TcpMod, Sock) of {op_response, {ok, _, _}} -> ok; {op_response, {error, Msg}} -> {error, Msg} end.