diff --git a/src/tsung/ts_pgsql.erl b/src/tsung/ts_pgsql.erl index ee5f798ec..c16e04bda 100644 --- a/src/tsung/ts_pgsql.erl +++ b/src/tsung/ts_pgsql.erl @@ -123,6 +123,15 @@ get_message(#pgsql_request{type=describe, name_portal=undefined,name_prepared=Na %% sync get_message(#pgsql_request{type=sync},#state_rcv{session=S}) -> {pgsql_proto:encode_message(sync,[]), S}; +%% copyfail +get_message(#pgsql_request{type=copyfail,equery=Msg},#state_rcv{session=S}) -> + {pgsql_proto:encode_message(copyfail,Msg), S}; +%% copydone +get_message(#pgsql_request{type=copydone},#state_rcv{session=S}) -> + {pgsql_proto:encode_message(copydone,<< >> ), S}; +%% copy +get_message(#pgsql_request{type=copy,equery=Data},#state_rcv{session=S}) -> + {pgsql_proto:encode_message(copy,Data), S}; %% flush get_message(#pgsql_request{type=flush},#state_rcv{session=S}) -> {pgsql_proto:encode_message(flush,[]), S}. @@ -176,6 +185,9 @@ parse(Data, State=#state_rcv{acc = [], dyndata=DynData}) -> NewDynData=DynData#dyndata{proto=#pgsql_dyndata{auth_method=AuthType}}, {State#state_rcv{ack_done = true, dyndata=NewDynData},[],false}; + {ok, {copy_response, {_Format,_ColsFormat}},_ } -> + ?LOG("PGSQL: Copy response ~n",?DEB), + {State#state_rcv{ack_done = true},[],false}; {ok, _Pair, Tail } -> parse(Tail, State); diff --git a/src/tsung/ts_utils.erl b/src/tsung/ts_utils.erl index bf8a50494..0b82b6df4 100644 --- a/src/tsung/ts_utils.erl +++ b/src/tsung/ts_utils.erl @@ -43,7 +43,7 @@ decode_base64/1, encode_base64/1, to_lower/1, release_is_newer_or_eq/1, randomstr/1,urandomstr/1,urandomstr_noflat/1, eval/1, list_to_number/1, time2sec/1, time2sec_hires/1, read_file_raw/1, init_seed/1, jsonpath/2, pmap/2, - concat_atoms/1, ceiling/1, accept_loop/3 + concat_atoms/1, ceiling/1, accept_loop/3, append_to_filename/3, splitchar/2 ]). level2int("debug") -> ?DEB; @@ -466,10 +466,23 @@ join(Sep, List) when is_list(List)-> end, string:join(lists:map(ToStr,List), Sep). -%% split a string (at first occurence of char) +%% split a string given a string (at first occurence of char) split(String,Chr) -> re:split(String,Chr,[{return,list}]). +%% split a string given a char (faster) +splitchar(String,Chr) -> + splitchar2(String,Chr,[],[]). +splitchar2([],_,[],Acc) -> + lists:reverse(Acc); +splitchar2([],_,AccChr,Acc) -> + lists:reverse([lists:reverse(AccChr)|Acc]); +splitchar2([Chr|String],Chr,AccChr,Acc) -> + splitchar2(String,Chr,[],[lists:reverse(AccChr)|Acc]); +splitchar2([Other|String],Chr,AccChr,Acc) -> + splitchar2(String,Chr,[Other|AccChr],Acc). + + %% split a string in 2 (at first occurence of char) split2(String,Chr) -> split2(String,Chr,nostrip). @@ -798,3 +811,11 @@ accept_loop(PPid, Tag, ServerSock)-> _-> normal end. + + +append_to_filename(Filename, From, To) -> + case re:replace(Filename,From,To, [{return,list},global] ) of + Filename -> Filename ++"." ++ To; + RealName -> RealName + end. + diff --git a/src/tsung_controller/ts_config_pgsql.erl b/src/tsung_controller/ts_config_pgsql.erl index 8f64e8724..1b50da729 100644 --- a/src/tsung_controller/ts_config_pgsql.erl +++ b/src/tsung_controller/ts_config_pgsql.erl @@ -52,59 +52,75 @@ parse_config(Element = #xmlElement{name=pgsql}, sessions = [CurS | _], dynvar=DynVar, subst = SubstFlag, match=MatchRegExp}) -> - {Ack,Request} = case ts_config:getAttr(atom, Element#xmlElement.attributes, type) of - sql -> - ValRaw = ts_config:getText(Element#xmlElement.content), - SQL = list_to_binary(ts_utils:clean_str(ValRaw)), - ?LOGF("Got SQL query: ~p~n",[SQL], ?NOTICE), - {parse,#pgsql_request{sql=SQL, type= sql}}; - close -> - {parse,#pgsql_request{type=close}}; - sync -> - {parse,#pgsql_request{type=sync}}; - flush -> - {parse,#pgsql_request{type=flush}}; - execute -> - Portal = ts_config:getAttr(Element#xmlElement.attributes, name_portal), - Limit = ts_config:getAttr(integer,Element#xmlElement.attributes, max_rows,0), - {no_ack,#pgsql_request{type=execute,name_portal=Portal,max_rows=Limit}}; - parse -> - Name = ts_config:getAttr(Element#xmlElement.attributes, name_prepared), - Query = list_to_binary(ts_config:getText(Element#xmlElement.content)), - Params=case ts_config:getAttr(Element#xmlElement.attributes, parameters) of - "" -> ""; - P -> - lists:map(fun(S)-> list_to_integer(S) end, ts_utils:split(P,",")) - end, - {no_ack,#pgsql_request{type=parse,name_prepared=Name,equery=Query,parameters=Params}}; - bind -> - Portal = ts_config:getAttr(Element#xmlElement.attributes, name_portal), - Prep = ts_config:getAttr(Element#xmlElement.attributes, name_prepared), - Formats = case ts_config:getAttr(Element#xmlElement.attributes, formats_results) of - "" -> ""; - FR -> - lists:map(fun(A)->list_to_atom(A) end,ts_utils:split(FR,",")) - end, - Params=case ts_config:getAttr(Element#xmlElement.attributes, parameters) of - "" -> ""; - P -> - ts_utils:split(P,",") - end, - ParamsFormat = ts_config:getAttr(atom,Element#xmlElement.attributes, formats, none), - {no_ack,#pgsql_request{type=bind,name_portal=Portal,name_prepared=Prep, - formats=ParamsFormat,formats_results=Formats,parameters=Params}}; - describe -> - Portal=ts_config:getAttr(string,Element#xmlElement.attributes, name_portal,undefined), - Prep = ts_config:getAttr(string,Element#xmlElement.attributes, name_prepared,undefined), - {no_ack,#pgsql_request{type=describe,name_portal=Portal,name_prepared=Prep}}; - authenticate -> - Passwd = ts_config:getAttr(Element#xmlElement.attributes, password), - {parse,#pgsql_request{passwd=Passwd, type= authenticate}}; - connect -> - Database = ts_config:getAttr(Element#xmlElement.attributes, database), - User = ts_config:getAttr(Element#xmlElement.attributes, username), - {parse,#pgsql_request{username=User, database=Database, type=connect}} - end, + {Ack,Request} = + case ts_config:getAttr(atom, Element#xmlElement.attributes, type) of + sql -> + ValRaw = ts_config:getText(Element#xmlElement.content), + SQL = list_to_binary(ts_utils:clean_str(ValRaw)), + ?LOGF("Got SQL query: ~p~n",[SQL], ?NOTICE), + {parse,#pgsql_request{sql=SQL, type= sql}}; + close -> + {parse,#pgsql_request{type=close}}; + sync -> + {parse,#pgsql_request{type=sync}}; + flush -> + {parse,#pgsql_request{type=flush}}; + copydone -> + {parse,#pgsql_request{type=copydone}}; + execute -> + Portal = ts_config:getAttr(Element#xmlElement.attributes, name_portal), + Limit = ts_config:getAttr(integer,Element#xmlElement.attributes, max_rows,0), + {no_ack,#pgsql_request{type=execute,name_portal=Portal,max_rows=Limit}}; + parse -> + Name = ts_config:getAttr(Element#xmlElement.attributes, name_prepared), + Query = list_to_binary(ts_config:getText(Element#xmlElement.content)), + Params=case ts_config:getAttr(Element#xmlElement.attributes, parameters) of + "" -> ""; + P -> + lists:map(fun(S)-> list_to_integer(S) end, ts_utils:splitchar(P,$,)) + end, + {no_ack,#pgsql_request{type=parse,name_prepared=Name,equery=Query,parameters=Params}}; + bind -> + Portal = ts_config:getAttr(Element#xmlElement.attributes, name_portal), + Prep = ts_config:getAttr(Element#xmlElement.attributes, name_prepared), + Formats = case ts_config:getAttr(Element#xmlElement.attributes, formats_results) of + "" -> ""; + FR -> + lists:map(fun(A)->list_to_atom(A) end,ts_utils:splitchar(FR,$,)) + end, + Params=case ts_config:getAttr(Element#xmlElement.attributes, parameters) of + "" -> ""; + P -> + ts_utils:split(P,",") + end, + ParamsFormat = ts_config:getAttr(atom,Element#xmlElement.attributes, formats, none), + {no_ack,#pgsql_request{type=bind,name_portal=Portal,name_prepared=Prep, + formats=ParamsFormat,formats_results=Formats,parameters=Params}}; + copy -> + Contents = case ts_config:getAttr(string, Element#xmlElement.attributes, contents_from_file) of + [] -> + P=ts_config:getText(Element#xmlElement.content), + list_to_binary(lists:map(fun(S)-> list_to_integer(S) end, ts_utils:splitchar(P,$,))); + FileName -> + {ok, FileContent} = file:read_file(FileName), + FileContent + end, + {no_ack,#pgsql_request{type=copy,equery=Contents}}; + copyfail -> + Str = ts_config:getAttr(string,Element#xmlElement.attributes, equery,undefined), + {parse,#pgsql_request{type=copyfail,equery=list_to_binary(Str)}}; + describe -> + Portal=ts_config:getAttr(string,Element#xmlElement.attributes, name_portal,undefined), + Prep = ts_config:getAttr(string,Element#xmlElement.attributes, name_prepared,undefined), + {no_ack,#pgsql_request{type=describe,name_portal=Portal,name_prepared=Prep}}; + authenticate -> + Passwd = ts_config:getAttr(Element#xmlElement.attributes, password), + {parse,#pgsql_request{passwd=Passwd, type= authenticate}}; + connect -> + Database = ts_config:getAttr(Element#xmlElement.attributes, database), + User = ts_config:getAttr(Element#xmlElement.attributes, username), + {parse,#pgsql_request{username=User, database=Database, type=connect}} + end, Msg= #ts_request{ack = Ack, endpage = true, dynvar_specs = DynVar, diff --git a/src/tsung_controller/ts_config_server.erl b/src/tsung_controller/ts_config_server.erl index ee0348cfe..9c52a2377 100644 --- a/src/tsung_controller/ts_config_server.erl +++ b/src/tsung_controller/ts_config_server.erl @@ -47,7 +47,7 @@ %%-------------------------------------------------------------------- %% External exports --export([start_link/1, read_config/1, get_req/2, get_next_session/1, +-export([start_link/1, read_config/1, read_config/2, get_req/2, get_next_session/1, get_client_config/1, newbeams/1, newbeam/2, get_monitor_hosts/0, encode_filename/1, decode_filename/1, endlaunching/1, status/0, start_file_server/1, get_user_agents/0, @@ -124,7 +124,9 @@ get_user_agents()-> %% Returns: ok | {error, Reason} %%-------------------------------------------------------------------- read_config(ConfigFile)-> - gen_server:call({global,?MODULE},{read_config, ConfigFile},?config_timeout). + read_config(ConfigFile,?config_timeout). +read_config(ConfigFile,Timeout)-> + gen_server:call({global,?MODULE},{read_config, ConfigFile},Timeout). %%-------------------------------------------------------------------- %% Function: get_client_config/1 diff --git a/src/tsung_controller/tsung_controller.erl b/src/tsung_controller/tsung_controller.erl index 89fb776f0..c984e5a5b 100644 --- a/src/tsung_controller/tsung_controller.erl +++ b/src/tsung_controller/tsung_controller.erl @@ -30,6 +30,7 @@ -behaviour(application). -include("ts_profile.hrl"). +-include_lib("kernel/include/file.hrl"). %%---------------------------------------------------------------------- %% Func: start/2 @@ -59,7 +60,18 @@ start(_Type, _StartArgs) -> end. start_phase(load_config, _StartType, _PhaseArgs) -> - case ts_config_server:read_config(?config(config_file)) of + Conf = ?config(config_file), + Timeout = case file:read_file_info(Conf) of + {ok, #file_info{size=Size}} when Size > 10000000 -> % > 10MB + erlang:display(["Can take up to 5mn to read config ",Size]), + 300000; % 10mn + {ok, #file_info{size=Size}} when Size > 1000000 -> % > 1MB + erlang:display(["Can take up to 3mn to read config ",Size]), + 180000; % 5mn + {ok, #file_info{size=Size}} -> + 120000 % 2mn + end, + case ts_config_server:read_config(Conf,Timeout) of {error,Reason}-> erlang:display(["Config Error, aborting ! ", Reason]), init:stop(); diff --git a/src/tsung_recorder/ts_proxy_http.erl b/src/tsung_recorder/ts_proxy_http.erl index 70f41f59c..01f532478 100644 --- a/src/tsung_recorder/ts_proxy_http.erl +++ b/src/tsung_recorder/ts_proxy_http.erl @@ -289,7 +289,7 @@ record_request(State=#state_rec{prev_host=Host, prev_port=Port, prev_scheme=Sche Id=State#state_rec.ext_file_id, case ts_utils:key1search(ParsedHeader,"content-type") of "multipart/form-data" ++ _Tail-> - FileName=append_to_filename(State#state_rec.log_file,".xml","-"++integer_to_list(Id)++".bin"), + FileName=ts_utils:append_to_filename(State#state_rec.log_file,".xml","-"++integer_to_list(Id)++".bin"), ?LOGF("multipart/form-data, write body data in external binary file ~s~n",[FileName],?NOTICE), ok = file:write_file(FileName,list_to_binary(Body)), io:format(Fd," contents_from_file='~s' ", [FileName]), @@ -349,10 +349,3 @@ record_header(Fd, Headers,HeaderName, Msg, Fun)-> Value -> io:format(Fd,Msg,[Fun(Value)]) end. -append_to_filename(Filename, From, To) -> - case re:replace(Filename,From,To, [{return,list},global] ) of - Filename -> Filename ++"." ++ To; - RealName -> RealName - end. - - diff --git a/src/tsung_recorder/ts_proxy_pgsql.erl b/src/tsung_recorder/ts_proxy_pgsql.erl index 3c8c8b3f6..186989de3 100644 --- a/src/tsung_recorder/ts_proxy_pgsql.erl +++ b/src/tsung_recorder/ts_proxy_pgsql.erl @@ -36,7 +36,7 @@ %%-------------------------------------------------------------------- %% Func: socket_opts/0 %%-------------------------------------------------------------------- -socket_opts() -> []. +socket_opts() -> [binary]. %%-------------------------------------------------------------------- %% Func: gettype/0 @@ -58,13 +58,12 @@ rewrite_ssl(Data)->{ok, Data}. %% Purpose: parse PGSQL request %% Returns: {ok, NewState} %%-------------------------------------------------------------------- -parse(State=#proxy{parse_status=Status},_,_SSocket,String=[0,0,0,8,4,210,22,47]) when Status==new -> +parse(State=#proxy{parse_status=Status},_,_SSocket,Data= << 0,0,0,8,4,210,22,47 >>) when Status==new -> ?LOG("SSL req: ~n",?DEB), Socket = connect(undefined), - ts_client_proxy:send(Socket, String, ?MODULE), - {ok, State#proxy{buffer=[],serversock = Socket }}; -parse(State=#proxy{parse_status=Status},_,ServerSocket,String) when Status==new -> - Data = list_to_binary(String), + ts_client_proxy:send(Socket, Data, ?MODULE), + {ok, State#proxy{buffer= << >>,serversock = Socket }}; +parse(State=#proxy{parse_status=Status},_,ServerSocket,Data) when Status==new -> <> = Data, ?LOGF("Received data from client: size=~p [~p]~n",[PacketSize, StartupPacket],?DEB), <> = StartupPacket, @@ -75,20 +74,19 @@ parse(State=#proxy{parse_status=Status},_,ServerSocket,String) when Status==new ?LOGF("Received data from client: split = ~p~n",[Res],?DEB), Socket = connect(ServerSocket), ts_client_proxy:send(Socket, Data, ?MODULE), - {ok, State#proxy{buffer=[], serversock = Socket} }; + {ok, State#proxy{buffer= <<>>, serversock = Socket} }; Req -> ?LOGF("Received data from client: split = ~p~n",[Res],?DEB), ts_proxy_recorder:dorecord({Req#pgsql_request{type=connect}}), Socket = connect(ServerSocket), ts_client_proxy:send(Socket, Data, ?MODULE), - {ok, State#proxy{parse_status=open, buffer=[], serversock = Socket} } + {ok, State#proxy{parse_status=open, buffer= <<>>, serversock = Socket} } end; -parse(State=#proxy{},_,ServerSocket,String) -> - NewString = lists:append(State#proxy.buffer, String), - Data = list_to_binary(NewString), - ?LOGF("Received data from client: ~p~n",[Data],?DEB), - NewState = process_data(State,Data), - ts_client_proxy:send(ServerSocket, String, ?MODULE), +parse(State=#proxy{},_,ServerSocket,Data) -> + NewData = << (State#proxy.buffer)/binary, Data/binary >>, + ?LOGF("Received data from client: ~p~n",[NewData],?DEB), + NewState = process_data(State,NewData), + ts_client_proxy:send(ServerSocket, Data, ?MODULE), {ok,NewState}. @@ -106,54 +104,66 @@ process_data(State,RawData = <>}; terminate -> ts_proxy_recorder:dorecord({#pgsql_request{type=close}}), - State#proxy{buffer=[]}; + State#proxy{buffer= <<>>}; {password, Password} -> PwdStr= binary_to_list(Password), ?LOGF("password = ~s~n",[PwdStr],?DEB), ts_proxy_recorder:dorecord({#pgsql_request{type=authenticate, passwd=PwdStr}}), - State#proxy{buffer=[]}; + State#proxy{buffer= <<>>}; {parse,{<< >>,StringQuery,Params} } -> %% TODO: handle Parameters if defined ts_proxy_recorder:dorecord({#pgsql_request{type=parse,equery=StringQuery, parameters=Params}}), - State#proxy{buffer=[]}; + State#proxy{buffer= <<>>}; {parse,{StringName,StringQuery,Params} } -> %% TODO: handle Parameters if defined ts_proxy_recorder:dorecord({#pgsql_request{type=parse,name_prepared=StringName, parameters=Params, equery=StringQuery}}), - State#proxy{buffer=[]}; + State#proxy{buffer= <<>>}; {bind,{Portal,StringQuery,Params, ParamsFormat,ResFormats} } -> R={#pgsql_request{type=bind, name_prepared=StringQuery, name_portal=Portal, parameters=Params,formats=ParamsFormat, formats_results=ResFormats}}, ts_proxy_recorder:dorecord(R), - State#proxy{buffer=[]}; - sync -> - ts_proxy_recorder:dorecord({#pgsql_request{type=sync}}), - State#proxy{buffer=[]}; - flush -> - ts_proxy_recorder:dorecord({#pgsql_request{type=flush}}), - State#proxy{buffer=[]}; + State#proxy{buffer= <<>>}; + {copy, CopyData} -> + ts_proxy_recorder:dorecord({#pgsql_request{type=copy,equery=CopyData}}), + State#proxy{buffer= <<>>}; + copydone -> + ts_proxy_recorder:dorecord({#pgsql_request{type=copydone}}), + State#proxy{buffer= <<>>}; + {copyfail,Msg} -> + ts_proxy_recorder:dorecord({#pgsql_request{type=copyfail,equery=Msg}}), + State#proxy{buffer= <<>>}; {describe,{<<"S">>,Name} } -> ts_proxy_recorder:dorecord({#pgsql_request{type=describe,name_prepared=Name}}), - State#proxy{buffer=[]}; + State#proxy{buffer= <<>>}; {describe,{<<"P">>,Name} } -> ts_proxy_recorder:dorecord({#pgsql_request{type=describe,name_portal=Name}}), - State#proxy{buffer=[]}; + State#proxy{buffer= <<>>}; {execute,{NamePortal,Max} } -> ts_proxy_recorder:dorecord({#pgsql_request{type=execute,name_portal=NamePortal,max_rows=Max}}), - State#proxy{buffer=[]} + State#proxy{buffer= <<>>}; + sync -> + ts_proxy_recorder:dorecord({#pgsql_request{type=sync}}), + State#proxy{buffer= <<>>}; + flush -> + ts_proxy_recorder:dorecord({#pgsql_request{type=flush}}), + State#proxy{buffer= <<>>} end, process_data(NewState,Data); false -> ?LOG("need more~n",?DEB), State#proxy{buffer=RawData} - end. + end; +process_data(State,RawData) -> + ?LOG("need more~n",?DEB), + State#proxy{buffer=RawData}. get_db_user(Arg) -> get_db_user(Arg,#pgsql_request{}). @@ -195,6 +205,15 @@ decode_packet($E, Data) -> %execute 0 -> {execute,{NamePortal,unlimited}}; Val -> {execute,{NamePortal,Val}} end; +decode_packet($d, Data) -> %copy + ?LOGF("Extended protocol: copy ~p~n",[Data], ?DEB), + {copy, Data}; +decode_packet($c, _) -> %copy-complete + ?LOG("Extended protocol: copydone~n", ?DEB), + copydone; +decode_packet($f, Data) -> %copy-fail + ?LOGF("Extended protocol: copy failure~p~n", [Data],?DEB), + {copyfail, Data}; decode_packet($B, Data) -> %bind [NamePortal, StringQuery | _] = split(Data,<<0>>,[global,trim]), Size = size(NamePortal)+size(StringQuery)+2, @@ -206,7 +225,7 @@ decode_packet($B, Data) -> %bind {1,<< 1:16/integer >> } -> binary; _ -> auto end, - {Params,<< NFormatRes:16/integer,FormatsResBin/binary >> }=get_params(NParams,Tail2,[]), + {Params,<< _NFormatRes:16/integer,FormatsResBin/binary >> }=get_params(NParams,Tail2,[]), ResFormats=get_params_format(FormatsResBin,[]), ?LOGF("Extended protocol: bind ~p ~p ~p ~p ~p~n",[NamePortal,StringQuery,Params,ParamsFormat,ResFormats ], ?DEB), {bind,{NamePortal,StringQuery,Params,ParamsFormat,ResFormats}}; @@ -231,7 +250,7 @@ get_params(0,Tail,Acc) -> get_params(N,<>,Acc) -> get_params(N-1,Tail,[S|Acc]). -get_params_int(0,Tail,Acc) -> +get_params_int(0,_,Acc) -> lists:reverse(Acc); get_params_int(N,<>,Acc) -> get_params_int(N-1,Tail,[Val|Acc]). @@ -277,6 +296,22 @@ record_request(State=#state_rec{logfd=Fd}, #pgsql_request{type=sync})-> record_request(State=#state_rec{logfd=Fd}, #pgsql_request{type=flush})-> io:format(Fd,"~n", []), {ok,State}; +record_request(State=#state_rec{logfd=Fd,ext_file_id=Id}, #pgsql_request{type=copy,equery=Bin}) when size(Bin) > 1024-> + FileName=ts_utils:append_to_filename(State#state_rec.log_file,".xml","-"++integer_to_list(Id)++".bin"), + ok = file:write_file(FileName,Bin), + io:format(Fd,"~n", [FileName]), + {ok,State#state_rec{ext_file_id=Id+1} }; +record_request(State=#state_rec{logfd=Fd}, #pgsql_request{type=copy,equery=Bin}) -> + Str=ts_utils:join(",",binary_to_list(Bin)), + io:format(Fd,"~s~n", [Str]), + {ok,State}; +record_request(State=#state_rec{logfd=Fd}, #pgsql_request{type=copyfail,equery=Bin}) -> + Str=binary_to_list(Bin), + io:format(Fd,"~n", [Str]), + {ok,State}; +record_request(State=#state_rec{logfd=Fd}, #pgsql_request{type=copydone}) -> + io:format(Fd,"~n", []), + {ok,State}; record_request(State=#state_rec{logfd=Fd}, #pgsql_request{type=describe,name_prepared=undefined,name_portal=Val}) -> io:format(Fd,"~n", [Val]), {ok,State}; @@ -338,7 +373,7 @@ connect(undefined) -> [{active, once}, {recbuf, ?tcp_buffer}, {sndbuf, ?tcp_buffer} - ]), + ]++ socket_opts()), ?LOGF("ok, connected ~p~n",[Socket],?DEB), Socket; connect(Socket) -> Socket. diff --git a/tsung-1.0.dtd b/tsung-1.0.dtd index 5fce3fc71..ebb4eebc5 100644 --- a/tsung-1.0.dtd +++ b/tsung-1.0.dtd @@ -270,6 +270,7 @@ repeat | if | change_type | foreach | set_option)*> max_rows CDATA "0" formats CDATA #IMPLIED formats_results CDATA #IMPLIED + contents_from_file CDATA #IMPLIED type (connect | authenticate | sql | close | bind | parse | cancel|call| sync | execute | describe | flush | copy | copydone| copyfail) #REQUIRED >