Port drivers

Danie Schutte <>
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 <>
%%% Description : 
%%%
%%% Created : 27 Apr 2004 by Henk Bijker <>
%%%-------------------------------------------------------------------
-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 <>
%%% Description : 
%%%
%%% Created : 27 Apr 2004 by Henk Bijker <>
%%%-------------------------------------------------------------------
-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 <>
%%% Description : 
%%%
%%% Created : 27 Dec 2004 by Henk Bijker <>
%%%-------------------------------------------------------------------
-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 ()
%%% 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