Port drivers
Danie Schutte
danie@REDACTED
Fri Jun 16 15:27:53 CEST 2006
I may misunderstand but just to clarify
port driver = linked in driver
port = the erl_interface stdin stdout type communication
1. Linked in drivers, use binaries to send data, we experienced a 270%
performance increase over our traditional port interface. Side effect - if
something goes wrong :) it goes wrong spectacularly (but this rarely
happened)
2. Significantly faster
3. no windows :)
4. Attached to this mail is some sample (production driver) code.
5. We have the same thing, the attached code is sybase linked in drivers, and
each one makes a connection to the database. We run about 50 of these
drivers simultaneously. The answer might also lie in what you do with the
driver.
On Friday 16 June 2006 14:44, Sebastian Bello wrote:
> Hello list,
>
> a few questions regarding port drivers:
>
> 1- when should a port driver be used instead of a port?
> 2- is a port driver faster than a port?
> 3- is it easy/possible to debug a port driver under Windows?
> 4- has anybody written an MFC based dll as a port driver? Is there any
> sample code available? 5- when having to deal with a big number of requests
> for a port driver, may it be possible/desirable to start multiple port
> drivers to handle them?
>
> Thanks,
> Sebastian-
--
Managing Director
Erlang Financial Systems (Pty) Ltd
Mobile: +27 84 468 3138
Phone : +11 235 6500 ext 6801
Fax : +11 235 6690
-------------- next part --------------
A non-text attachment was scrubbed...
Name: port_driver1.c
Type: text/x-csrc
Size: 1599 bytes
Desc: not available
URL: <http://erlang.org/pipermail/erlang-questions/attachments/20060616/7840e1c6/attachment.bin>
-------------- next part --------------
-module(sybase_api).
-revision('$Revision: 1958 $ ').
-vsn('$Revision: 1958 $ ').
-behaviour(gen_server).
%% touch for recompile
%%--------------------------------------------------------------------
%% Include files
%%--------------------------------------------------------------------
-include("sybase_api.hrl").
%%--------------------------------------------------------------------
%% External exports
-export([start_link/0, transaction/3, get_state/0]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_info/2, terminate/2,
handle_cast/2, code_change/3]).
-export([spawn_workers/2]).
%%====================================================================
%% External functions
%%====================================================================
%%--------------------------------------------------------------------
%% Function: start_link/0 -> {ok, pid()}
%% Description: Starts the sybase DB server
%%--------------------------------------------------------------------
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). %% local / global?
%%--------------------------------------------------------------------
%% Function: transaction/2 -> {ok, ref()}
%% Description: Forwards a sybase transaction to a Database running
%% on Node.
%% Parameters: Node - Node on which the sybase_api server is running
%% Cmd - {Function, Param, Type}
%% Function - Cmd to be executed in the databse
%% Param - Parameters to the Sybase Transaction
%% Type - Type list of the Param List
%% Returns: {ok, Ref()}
%%--------------------------------------------------------------------
transaction(Node, Cmd, ReplyPID) ->
gen_server:call({?MODULE, Node}, {transaction, Cmd, ReplyPID}).
%%--------------------------------------------------------------------
%% Function: get_state/0 -> #state
%% Description: Retrieves the state of the server. Note, this function
%% should be used for testing and debugging only.
get_state() ->
gen_server:call(?MODULE, get_state).
%%====================================================================
%% Server functions
%%====================================================================
%%--------------------------------------------------------------------
%% Function: init/1
%% Description: Initiates the server by starting all the C Nodes.
%% Number of Nodes and host name defined in app file.
%% Returns: {ok, State}
%%--------------------------------------------------------------------
init([]) ->
process_flag(trap_exit, true),
ThreadNum = case application:get_env(sybase_threads) of
undefined -> ?SYBASE_THREADS;
{ok, Num} -> Num
end,
SybaseHost = case application:get_env(sybase_host) of
undefined -> ?SYBASE_HOST;
{ok, Name} -> Name
end,
alarm({{sybase_api, max_threshold}, requests_queued}),
%% timer:apply_after(60000, gen_server, cast, [self(), stats]),
%% open logfile.
FileName = "log/sybase_api_" ++ format_util:format_date_out(date(), yyyymmdd) ++ "_" ++ format_util:format_time_out(time(), hhmmss) ++ ".log",
case error_logger:logfile({open, FileName}) of
FileName ->
%%error_logger:info_msg("Logging to ~s", [FileName]),
error_logger:tty(true),
ok;
ok ->
%%error_logger:info_msg("Logging to ~s", [FileName]),
error_logger:tty(true),
ok;
ErrorReason ->
error_logger:error_msg("Could NOT log, reason ~p~n", [ErrorReason])
end,
SybSettings = sybase_settings:read(),
timer:apply_after(1000, gen_server, cast, [?MODULE, {spawn_workers, ThreadNum, SybSettings}]),
{ok, #state{hostname = SybaseHost, settings = SybSettings}}.
%%--------------------------------------------------------------------
%% Function: handle_call({transaction, Cmd, Pid}, From, State)
%% Description: Handling a request for a sybase transaction from a
%% client. If C nodes are available, it will execute
%% immideately, else it is queued for next available
%% node. Reposnses are send directly to clients,
%% sybase_api gets ack that transaction has completed.
%% Parameters: Cmd - Command passed on to sybase
%% Pid - Cliend pid who will receive the response
%% Returns: {reply, {ok, ref(()}, #state}
%%--------------------------------------------------------------------
handle_call({transaction, Cmd, Pid}, _From, State) ->
#state{avail=Avail, reqlist=ReqList, working=Working} = State,
Ref = make_ref(),
Req = #req{pid = Pid,
ref = Ref,
cmd = Cmd},
NewState =
case Avail of
[] ->
case ReqList of
[] -> alarm({{sybase_api, max_threshold}, requests_queued});
_ -> ok
end,
State#state{reqlist = ReqList ++ [Req]};
[H|T] ->
gen_server:cast(H#worker.num, {rpc, self(), Ref, Cmd}),
%error_logger:info_msg("~p command ~p", [H#worker.num, Cmd]),
NewWorking = [H#worker{req=Req}|Working],
State#state{avail=T, working = NewWorking}
end,
{reply, {ok, Ref}, NewState};
%%--------------------------------------------------------------------
%% Function: handle_call(get_state, From, State)
%% Description: retrieves the state for testing purposes
%% Returns: {reply, #state, #state}
%%--------------------------------------------------------------------
handle_call(get_state, _From, State) ->
{reply, State, State};
%%handle_call({Ref, {row, RowData}}, From, State) ->
%% %%error_logger:info_msg("Returning data for reference ~p", [Ref]),
%% {reply, gen_server:call(syb_db_result, {returndata, Ref, RowData}), State};
handle_call(OtherCall, _from, _state) ->
error_logger:error_msg("!! unknown handle_call received !! (sybase_api) : ~p", [OtherCall]),
{reply, OtherCall, _state}.
handle_cast(What, State) -> %% wrapper for handle cast
%% %%error_logger:info_msg("handle_cast(~p, ~p)", [What, State]),
hc(What, State).
hc(stats, State) ->
%%error_logger:info_msg("~nsnapshot reports ~s% capacity used", [hd(io_lib:format("~.3f", [(length(State#state.working)/(length(State#state.avail)+length(State#state.working))*100)]))]),
timer:apply_after(60000, gen_server, cast, [self(), stats]),
{noreply, State};
hc({spawn_workers, Num, Settings}, State)->
NumList = spawn_workers(Num, Settings),
NewState = State#state{started = NumList, avail = NumList},
{noreply, NewState};
hc(What, State) ->
%% %%error_logger:info_msg("~nUnhandled cast (sybase_api) - ~p", [What]),
{noreply, State}.
%%--------------------------------------------------------------------
%% Function: handle_info({nodedown, Node}, State)
%% Description: Handling of C Nodes terminating. Raises alarm and
%% tries to create a new one. If there was an ongoing
%% transaction on the node, the client is informed.
%% Parameters: Node - C Node that terminated.
%% Returns: {noreply, #state} |
%%--------------------------------------------------------------------
%% handle_info({nodedown, Node}, State) ->
%% case remove(Node, State#state.started) of
%% {ok, #node{num = Num}, Started} ->
%% clear({sybase_api, {node_down, Node}}),
%% NewStarted = [c_node(Num, State#state.settings)|Started],
%% {noreply,State#state{started = NewStarted}};
%% undefined -> %% Node was running
%% error_logger:error_msg("!! nodedown !!"),
%% {noreply, handle_nodedown(Node, State)}
%% end;
%%--------------------------------------------------------------------
%% Function: handle_info({pong, Node}, State)
%% Description: Handling of C Nodes Startup. Acknowledges that the
%% node has started and schedules a task for it if there
%% is a queue.
%% Parameters: Node - C Node that started.
%% Returns: {noreply, #state} |
%%--------------------------------------------------------------------
%% handle_info({pong, Node}, State) ->
%% clear({sybase_api, {node_down, Node}}),
%% case remove(Node, State#state.started) of
%% undefined -> %% Should not happen
%% info("~nUnknown C Node sent pong:~p", [Node]),
%% {noreply, State};
%% {ok , NodeRec, NewStarted} ->
%% NewState = State#state{started = NewStarted},
%% {noreply, handle_reqlist(NodeRec, NewState)}
%% end;
%%--------------------------------------------------------------------
%% Function: handle_info({pong, Node}, State)
%% Description: Handling of C Nodes Startup. Acknowledges that the
%% node has started and schedules a task for it if there
%% is a transaction queue.
%% Parameters: Node - C Node that started.
%% Returns: {noreply, #state} |
%%--------------------------------------------------------------------
handle_info({completed, Worker}, State) ->
case remove(Worker, State#state.working) of
undefined -> %% Should not happen
info("~nUnknown Worker Completed:~p",[Worker]),
self()! {completed, Worker},
{noreply, State};
{ok, WorkerRec, NewWorking} ->
%%error_logger:info_msg("Worker ~p complete", [Worker]),
NewState = State#state{working = NewWorking},
{noreply, handle_reqlist(WorkerRec, NewState)}
end;
handle_info({Reference, {rows, RowData}, {status, Status}}, State) ->
%% get_reference_pid(Reference, State#state.working) ! {Reference, RowData},
ok = gen_server:cast(get_reference_pid(Reference, State#state.working), {Reference, {rows, RowData}, {status, Status}}),
{noreply, State};
handle_info({'EXIT',Port,normal}, State) ->
error_logger:error_msg("Sybase driver exit received! port ~p", [Port]),
{noreply, State};
handle_info(_Unknown, State) ->
info("!! Unknown Msg in handle_info !! (sybase_api) : ~p",[_Unknown]),
{noreply, State}.
code_change(OldVsn, State, Extra) ->
{ok, State}.
%%--------------------------------------------------------------------
%% get_refernce_pid
%%--------------------------------------------------------------------
get_reference_pid(Reference, []) ->
error_logger:error_msg("Unable to find reply PID for Reference ~p !!", [Reference]),
{error, no_pid};
get_reference_pid(Reference, [Worker|Next]) ->
%% %%error_logger:info_msg("Node = ~p", [Node]),
Request = Worker#worker.req,
Pid = Request#req.pid,
Ref = Request#req.ref,
%% %%error_logger:info_msg("~p == ~p", [Reference, Ref]),
case (Ref == Reference) of
true ->
Pid;
false ->
get_reference_pid(Reference, Next)
end.
%%--------------------------------------------------------------------
%% Function: terminate/2
%% Description: Shutdown the server. Terminates all C nodes
%% Returns: any (ignored by gen_server)
%%--------------------------------------------------------------------
terminate(Reason, State) ->
%% #state{started=Started, avail=Avail, working=Working} = State,
%% Fun = fun(#worker{name = Name}) -> {db, Name} ! {self(), stop} end,
%% lists:foreach(Fun, Started ++ Avail ++ Working).
bla.
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
%%--------------------------------------------------------------------
%% Spawns Count C Nodes with connections towards the Sybase DB
spawn_workers(Count, Settings) ->
lists:map(fun(Num)-> worker(Num, Settings) end,lists:seq(1,Count)).
%%--------------------------------------------------------------------
%% Creates a C Node
worker(Num, Settings) ->
#setup{hostname = HostName,
servername = ServerName,
databasename = DatabaseName,
username = UserName,
password = Password
} = Settings,
{ok, Num1} = sybase_api_sup:start_worker(Num, ["sybasedrv" ++ integer_to_list(Num), 0,DatabaseName, 0,ServerName, 0,UserName, 0,Password,0]),
#worker{num = Num1}.
%%--------------------------------------------------------------------
%% When a node goes down, removes it from the available and working
%% set and if it was involved in a transaction, informs the client
%% handle_nodedown(Node, State) ->
%% #state{started=Started, avail=Avail, working=Working} = State,
%% case remove(Node, Avail) of
%% {ok, #node{num = Num}, NewAvail} ->
%% NewStarted = [worker(Num, State#state.settings)|Started],
%% State#state{started=NewStarted, avail = NewAvail};
%% undefined ->
%% case remove(Node, Working) of
%% {ok, #node{num=Num, req = Req}, NewWorking} ->
%% #req{pid = Pid, ref = Ref} = Req,
%% gen_server:cast(Pid, {trx_fail, Pid, Ref, {error, node_down}}),
%% %% syncro_worker:trx_failed(Pid, Ref, {error,node_down}),
%% NewStarted=[worker(Num,State#state.settings)|Started],
%% State#state{started=NewStarted, working=NewWorking};
%% undefined ->
%% info("~nUnknown Node sending node down:~p", [Node]),
%% State
%% end
%% end.
%%--------------------------------------------------------------------
% When a node comes up or has completed a transaction, this function
% checks if there are jobs queued up which need executing.
handle_reqlist(WorkerRec, #state{reqlist=[],avail=Avail} = State) ->
case Avail of
[] -> clear({sybase_api, max_threshold});
_ -> ok
end,
State#state{avail = [WorkerRec#worker{req = undefined}|Avail]};
handle_reqlist(WorkerRec, State) ->
#state{reqlist = [Req|ReqList], working = Working} = State,
#req{pid = Pid,
ref = Ref,
cmd = Cmd} = Req,
gen_server:cast(WorkerRec#worker.num, {rpc, self(), Ref, Cmd}),
State#state{working = [WorkerRec#worker{req = Req}|Working],
reqlist = ReqList}.
%%--------------------------------------------------------------------
% Removes a node from a list of nodes, returning the nodeRec and the
% new list. Used when moving nodes among lists.
remove(Worker, Workers) ->
case lists:keysearch(Worker, #worker.num, Workers) of
{value, Rec} -> {ok,Rec,lists:keydelete(Worker,#worker.num,Workers)};
false -> undefined
end.
%%--------------------------------------------------------------------
% Short cut to the error logger
info(Msg, Args) ->
%%error_logger:info_msg(Msg, Args),
ok.
alarm({Id, Desc}) ->
alarm_handler:set_alarm({Id, Desc}).
clear(Id) ->
alarm_handler:clear_alarm(Id).
-------------- next part --------------
%%%-------------------------------------------------------------------
%%% File : sybase_api_app.erl
%%% Author : Henk Bijker <henkb.bijker@REDACTED>
%%% Description :
%%%
%%% Created : 27 Apr 2004 by Henk Bijker <henkb.bijker@REDACTED>
%%%-------------------------------------------------------------------
-module(sybase_api_app).
-behaviour(application).
%%--------------------------------------------------------------------
%% Include files
%%--------------------------------------------------------------------
%%--------------------------------------------------------------------
%% External exports
%%--------------------------------------------------------------------
-export([
start/2,
stop/1
]).
%%--------------------------------------------------------------------
%% Internal exports
%%--------------------------------------------------------------------
-export([
]).
%%--------------------------------------------------------------------
%% Macros
%%--------------------------------------------------------------------
%%--------------------------------------------------------------------
%% Records
%%--------------------------------------------------------------------
%%====================================================================
%% External functions
%%====================================================================
%%--------------------------------------------------------------------
%% Func: start/2
%% Returns: {ok, Pid} |
%% {ok, Pid, State} |
%% {error, Reason}
%%--------------------------------------------------------------------
start(Type, StartArgs) ->
case sybase_api_sup:start_link() of
{ok, Pid} ->
{ok, Pid};
Error ->
Error
end.
%%--------------------------------------------------------------------
%% Func: stop/1
%% Returns: any
%%--------------------------------------------------------------------
stop(State) ->
ok.
%%====================================================================
%% Internal functions
%%====================================================================
-------------- next part --------------
%%%-------------------------------------------------------------------
%%% File : sybase_api_sup.erl
%%% Author : Henk Bijker <henkb.bijker@REDACTED>
%%% Description :
%%%
%%% Created : 27 Apr 2004 by Henk Bijker <henkb.bijker@REDACTED>
%%%-------------------------------------------------------------------
-module(sybase_api_sup).
-behaviour(supervisor).
%%--------------------------------------------------------------------
%% Include files
%%--------------------------------------------------------------------
%%--------------------------------------------------------------------
%% External exports
%%--------------------------------------------------------------------
-export([
start_link/0
]).
%%--------------------------------------------------------------------
%% Internal exports
%%--------------------------------------------------------------------
-export([
init/1, start_worker/2
]).
%%--------------------------------------------------------------------
%% Macros
%%--------------------------------------------------------------------
-define(SERVER, ?MODULE).
%%--------------------------------------------------------------------
%% Records
%%--------------------------------------------------------------------
%%====================================================================
%% External functions
%%====================================================================
%%--------------------------------------------------------------------
%% Function: start_link/0
%% Description: Starts the supervisor
%%--------------------------------------------------------------------
start_link() ->
supervisor:start_link({local, ?SERVER}, ?MODULE, []).
%%====================================================================
%% Server functions
%%====================================================================
%%--------------------------------------------------------------------
%% Func: init/1
%% Returns: {ok, {SupFlags, [ChildSpec]}} |
%% ignore |
%% {error, Reason}
%%--------------------------------------------------------------------
init([]) ->
AChild = {sybase_api, %% name db_sybase
{sybase_api, %% module
start_link,[]},
permanent,2000,worker,[sybase_api]},
{ok,{{one_for_all,0,1}, [AChild]}}.
%%====================================================================
%% Internal functions
%%====================================================================
start_worker(Num, Settings) ->
Child = {{'sybase_worker', Num}, {sybase_worker, start_link, [[Settings, Num]]},
permanent, 2000, worker,
[sybase_worker]},
supervisor:start_child(?SERVER, Child).
-------------- next part --------------
%%%-------------------------------------------------------------------
%%% File : sybase_settings.erl
%%% Author : Henk Bijker <henk@REDACTED>
%%% Description :
%%%
%%% Created : 27 Dec 2004 by Henk Bijker <henk@REDACTED>
%%%-------------------------------------------------------------------
-module(sybase_settings).
%%--------------------------------------------------------------------
%% Include files
%%--------------------------------------------------------------------
%%--------------------------------------------------------------------
%% External exports
%%--------------------------------------------------------------------
-export([
read/0
]).
%%--------------------------------------------------------------------
%% Internal exports
%%--------------------------------------------------------------------
-export([
]).
%%--------------------------------------------------------------------
%% Macros
%%--------------------------------------------------------------------
%%--------------------------------------------------------------------
%% Records
%%--------------------------------------------------------------------
-include("sybase_api.hrl").
%%====================================================================
%% External functions
%%====================================================================
%%--------------------------------------------------------------------
%% Function:
%% Description:
%%--------------------------------------------------------------------
read() ->
mnesia:start(),
timer:sleep(2000),
case catch mnesia:transaction(fun() -> mnesia:read({setup, setup}) end)
of
{aborted,{no_exists,setup}} -> execute_once();
{atomic, []} ->
error_logger:error_msg("!! Error !! initial setup complete, no entries found!"),
timer:sleep(10000),
execute_once();
{'EXIT',{aborted,{no_exists,[setup,setup]}}} -> execute_once();
{atomic, [Settings]} ->
Settings
end.
%%====================================================================
%% Internal functions
%%====================================================================
initial_entry() ->
{ok, [HostName]} = io:fread('Hostname?', "~s"),
{ok, [ServerName]} = io:fread('Servername?', "~s"),
{ok, [DatabaseName]} = io:fread('Database name?', "~s"),
{ok, [UserName]} = io:fread('Username?', "~s"),
{ok, [Password]} = io:fread('Password?', "~s"),
Object = #setup{
description = setup,
hostname = HostName,
servername = ServerName,
databasename = DatabaseName,
username = UserName,
password = Password},
Write = mnesia:transaction(fun() -> mnesia:write(Object) end),
error_logger:info_msg("~nWrite is ~p", [Write]),
Object.
%%%---------------------------------------------------------------------
%% create the table
%%%---------------------------------------------------------------------
create() ->
io:format("Done.~nCreating table ..."),
io:format("~nsetup ..."),
mnesia:create_table(setup,
[{type, bag}, {disc_copies,[node()]},
{attributes, record_info(fields, setup)}]).
%% initial setup
execute_once() ->
io:format("please switch to terminal 1, (^G, c1)"),
io:format("~nConfirm first sybase execution, (initial setup) [y/n]"),
Confirm = io:fread("", "~s"),
case Confirm of
{ok, ["y"]} ->
io:format("~nOk, creating database, please wait ..."),
schema([node()]),
startm(),
create(),
mnesia:change_table_copy_type(setup, node(), disc_copies),
Data = initial_entry(),
io:format("done.~n"),
Data;
_ ->
io:format("~nKeypressed: setup cancelled, please check nodename, database setup, directory access or user rights."),
#setup{}
end.
%%%---------------------------------------------------------------------
%% create mnesia schema if not exist
%%%---------------------------------------------------------------------
schema(Nodes) ->
io:format("~nStopping existing instances, please wait ..."),
mnesia:stop(),
io:format("Creating schema on local node & trx node, please wait ..."),
mnesia:create_schema(Nodes).
%%%---------------------------------------------------------------------
%% start mnesia
%%%---------------------------------------------------------------------
startm() ->
io:format("done.~nStarting mnesia ..."),
mnesia:start() .
-------------- next part --------------
%%%-------------------------------------------------------------------
%%% File : sybase_worker.erl
%%% Author : MP Scholtz (michael@REDACTED)
%%% Description :
%%%
%%% Created : 2005/08/16
%%%-------------------------------------------------------------------
-module(sybase_worker).
-behaviour(gen_server).
%%--------------------------------------------------------------------
%% Include files
%%--------------------------------------------------------------------
%%--------------------------------------------------------------------
%% External exports
-export([start_link/1]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
-record(state, {port, ref, rows, status, lastrow}).
-define(SERVER, ?MODULE).
%%====================================================================
%% External functions
%%====================================================================
%%--------------------------------------------------------------------
%% Function: start_link/0
%% Description: Starts the server
%%--------------------------------------------------------------------
start_link([Settings, Num]) ->
gen_server:start_link(?MODULE, [Settings, Num], []).
%%====================================================================
%% Server functions
%%====================================================================
%%--------------------------------------------------------------------
%% Function: init/1
%% Description: Initiates the server
%% Returns: {ok, State} |
%% {ok, State, Timeout} |
%% ignore |
%% {stop, Reason}
%%--------------------------------------------------------------------
init([Settings, Num]) ->
DriverName = "sybasedrv" ++ integer_to_list(Num),
error_logger:error_msg("DRV NAME ~p", [DriverName]),
case erl_ddll:load_driver(".", DriverName) of
ok -> ok;
{error, already_loaded} -> ok;
_ -> exit({error, could_not_load_driver})
end,
Port = open_port({spawn, DriverName}, []),
Port ! {self(), {command, [1, Settings]}}, %%Send connect to sybase_driver
{ok, #state{port = Port, rows = []}}.
%%--------------------------------------------------------------------
%% Function: handle_call/3
%% Description: Handling call messages
%% Returns: {reply, Reply, State} |
%% {reply, Reply, State, Timeout} |
%% {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, Reply, State} | (terminate/2 is called)
%% {stop, Reason, State} (terminate/2 is called)
%%--------------------------------------------------------------------
handle_call(Request, From, State) ->
io:format("~n Unhandled Request - ~p~n", [Request]),
{reply, ok, State}.
%%--------------------------------------------------------------------
%% Function: handle_cast/2
%% Description: Handling cast messages
%% Returns: {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State} (terminate/2 is called)
%%--------------------------------------------------------------------
handle_cast({rpc, From, Ref, Cmd}, State) ->
{Proc, ArgList} = Cmd,
X = create_string(ArgList, atom_to_list(Proc)),
State#state.port!{self(), {command, [2, X]}},
{noreply, State#state{ref = Ref}};
handle_cast(Msg, State) ->
{noreply, State}.
%%--------------------------------------------------------------------
%% Function: handle_infox/2
%% Description: Handling all non call/cast messages
%% Returns: {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State} (terminate/2 is called)
%%--------------------------------------------------------------------
handle_info({_, {data, [0]}}, State) ->
case State#state.lastrow of
undefined ->
NewState = State;
_ ->
Rows = State#state.rows -- [State#state.lastrow],
sybase_api!{State#state.ref, {rows, Rows} , {status, State#state.lastrow}},
sybase_api!{completed, self()},
NewState = State#state{lastrow = undefined,
status = State#state.lastrow,
rows = []}
end,
{noreply, NewState};
handle_info({_, {data, Data}}, State) ->
Cols = string:tokens(Data, [173]),
[H|T] = lists:reverse(Cols),
Row = lists:reverse(T),
NewState = State#state{lastrow = Row, rows = State#state.rows ++ [Row]},
{noreply, NewState};
handle_info(Info, State) ->
{noreply, State}.
%%--------------------------------------------------------------------
%% Function: terminate/2
%% Description: Shutdown the server
%% Returns: any (ignored by gen_server)
%%--------------------------------------------------------------------
terminate(Reason, State) ->
ok.
%%--------------------------------------------------------------------
%% Func: code_change/3
%% Purpose: Convert process state when code is changed
%% Returns: {ok, NewState}
%%--------------------------------------------------------------------
code_change(OldVsn, State, Extra) ->
{ok, State}.
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
create_string([], String) ->
[String|[0]];
create_string([{Arg, Type}|T], String) when is_list(Arg) ->
create_string(T, String ++ [0] ++ Arg ++ [0] ++ [Type]);
create_string([{Arg, Type}|T], String) when is_integer(Arg) ->
create_string(T, String ++ [0] ++ integer_to_list(Arg) ++ [0] ++ [Type]).
More information about the erlang-questions
mailing list