From 2dd67031bae99fd150600ee90ff7ede1d9d12fb1 Mon Sep 17 00:00:00 2001 From: "H. Diedrich" Date: Thu, 10 Feb 2011 07:44:59 +0100 Subject: [PATCH] auto doc update --- LICENSE | 59 +- changes.edoc | 16 + diff-ejabberd-yxa-2.txt | 567 +++++++++++++ diff-ejabberd-yxa.txt | 1515 +++++++++++++++++++++++++++++++++++ edoc-info | 5 + emysql-footer.png | Bin 0 -> 4335 bytes emysql-style.css | 180 +++++ emysql.html | 339 ++++++++ emysql_app.html | 61 ++ emysql_auth.html | 30 + emysql_conn.html | 84 ++ emysql_conn_mgr.html | 133 +++ emysql_statements.html | 103 +++ emysql_sup.html | 37 + emysql_tcp.html | 36 + emysql_tracer.html | 30 + emysql_util.html | 125 +++ emysql_worker.html | 49 ++ erlang.png | Bin 0 -> 2109 bytes index.html | 17 + markedoc.sed | 380 +++++++++ modules-frame.html | 22 + overview-summary.html | 426 ++++++++++ overview.edoc | 7 + packages-frame.html | 11 + powered-by-mysql-125x64.png | Bin 0 -> 1443 bytes stylesheet.css | 55 ++ 27 files changed, 4280 insertions(+), 7 deletions(-) create mode 100644 changes.edoc create mode 100644 diff-ejabberd-yxa-2.txt create mode 100644 diff-ejabberd-yxa.txt create mode 100644 edoc-info create mode 100644 emysql-footer.png create mode 100644 emysql-style.css create mode 100644 emysql.html create mode 100644 emysql_app.html create mode 100644 emysql_auth.html create mode 100644 emysql_conn.html create mode 100644 emysql_conn_mgr.html create mode 100644 emysql_statements.html create mode 100644 emysql_sup.html create mode 100644 emysql_tcp.html create mode 100644 emysql_tracer.html create mode 100644 emysql_util.html create mode 100644 emysql_worker.html create mode 100644 erlang.png create mode 100644 index.html create mode 100644 markedoc.sed create mode 100644 modules-frame.html create mode 100644 overview-summary.html create mode 100644 overview.edoc create mode 100644 packages-frame.html create mode 100644 powered-by-mysql-125x64.png create mode 100644 stylesheet.css diff --git a/LICENSE b/LICENSE index bae765bd..bab2c113 100644 --- a/LICENSE +++ b/LICENSE @@ -2,18 +2,18 @@ Copyright (c) 2009-2011 Bill Warnecke , Jacob Vorreuter , Henning Diedrich , -Eonblast Corporation +Eonblast Corporation [http://www.eonblast.com]. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation -files (the "Software"),to deal in the Software without restric- -tion, including without limitation the rights to use, copy, -modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom +the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, @@ -24,3 +24,48 @@ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +Use of Oracle's MySQL Logo in the documentation of this package according +to Oracle's Logo Usage Guidelines + +As retrieved on 01/22/11 from http://www.mysql.de/about/legal/trademark.html: + +Use of MySQL Conditional Use Logos + + Oracle has a family of MySQL Conditional Use Logos for those who + wish to show their support for MySQL products and services by + displaying such a logo in the user interface of a website. The MySQL + Conditional Use Logos may be used without specific written + permission from Oracle under these terms of the Third Party Usage + Guidelines for Oracle Trademarks, Third Party Usage Guidelines for + Oracle Logos, and the following conditions: + + * The use must not be detrimental, i.e., harmful or damaging, to + the value of any of the MySQL Marks, or to Oracle, its brand + integrity, reputation or goodwill, as determined by Oracle in its + sole discretion; + + * The MySQL Conditional Use Logo must be a "clickable" link that + leads directly to http://www.mysql.com/; + + * If the website has password-protected areas, the MySQL + Conditional Use Logo must be placed only in areas that are not + password-protected; + + * If used in an application released under the GPL license, the + application must be able to connect directly to a MySQL server; + + * The limited license given herein does not include the right to + use any MySQL Conditional Use Logo or any other Oracle Logo as a + trademark to promote your own products or services. For example, no + Oracle or MySQL Conditional Use Logo may be used on any product + packaging or documentation, such as (without limitation) on CD-ROM + or diskette labels or packaging, books or other publications. Any + such use must be expressly authorized by Oracle in a signed, written + agreement; + + * The use of any MySQL Conditional Use Logo under these terms + must also comply with applicable provisions of the Third Party Usage + Guidelines for Oracle Trademarks and Third Party Usage Guidelines + for Oracle Logos. diff --git a/changes.edoc b/changes.edoc new file mode 100644 index 00000000..09fc8188 --- /dev/null +++ b/changes.edoc @@ -0,0 +1,16 @@ +@doc +== Changes == + +=== 0.2 Stored Procedures and Docs === + +
  • Correct handling of stored procedures result packages
  • +
  • Module emysql_tcp extended to handle the flag SERVER_MORE_RESULTS_EXIST correctly
  • +
  • Error packages parse additional sql status flag of mysql 4.1 format.
  • +
  • Additional example for stored procedures
  • +
  • Renaming of samples
  • +
  • Much extended docs
  • + diff --git a/diff-ejabberd-yxa-2.txt b/diff-ejabberd-yxa-2.txt new file mode 100644 index 00000000..90077f25 --- /dev/null +++ b/diff-ejabberd-yxa-2.txt @@ -0,0 +1,567 @@ +Diff of ejabberd mysql driver vs yxa mysql driver (comment changes ignored) +--------------------------------------------------------------------------- + +Both depend on Magnus Ahltorp' code. Half of the difference are simply changed +comments, as Yxa went edoc. The remainder are mostly result inspection extensions +on the part of the ejabberd source. + +This diff ignores changes to comments but lists added comments. Find one listing all +changes here: https://github.com/Eonblast/Emysql/blob/master/doc/diff-ejabberd-yxa.txt + +Yxa: https://github.com/fredrikt/yxa/tree/master/src/mysql/ +Ejabberd: http://svn.process-one.net/ejabberd-modules/mysql/trunk/ +Diff: Jan 2011 + +Only in ejabberd-modules/mysql/trunk/: .svn +Only in yxa-1.0/src/mysql/: Makefile.in +diff -I '^%.*$' ejabberd-modules/mysql/trunk/mysql.erl yxa-1.0/src/mysql/mysql.erl +73,77d60 +< get_result_field_info/1, +< get_result_rows/1, +< get_result_affected_rows/1, +< get_result_reason/1, +< +81,84c64 +< connect/7, +< stop/0, +< +< gc_each/1 +--- +> connect/7 +108c88,89 +< -include("mysql.hrl"). +--- +> %% @type state() = #state{}. +> %% no description +111,112c92 +< log_fun, %% undefined | function for logging, +< gc_tref %% undefined | timer:TRef +--- +> log_fun %% undefined | function for logging, +173,178d154 +< stop() -> +< gen_server:call(?SERVER, stop). +< +< gc_each(Millisec) -> +< gen_server:call(?SERVER, {gc_each, Millisec}). +< +197,241c177,186 +< %% Function: get_result_field_info(MySQLRes) +< %% MySQLRes = term(), result of fetch function on "data" +< %% Descrip.: Extract the FieldInfo from MySQL Result on data received +< %% Returns : FieldInfo +< %% FieldInfo = list() of {Table, Field, Length, Name} +< %%-------------------------------------------------------------------- +< get_result_field_info(#mysql_result{fieldinfo = FieldInfo}) -> +< FieldInfo. +< +< %%-------------------------------------------------------------------- +< %% Function: get_result_rows(MySQLRes) +< %% MySQLRes = term(), result of fetch function on "data" +< %% Descrip.: Extract the Rows from MySQL Result on data received +< %% Returns : Rows +< %% Rows = list() of list() representing records +< %%-------------------------------------------------------------------- +< get_result_rows(#mysql_result{rows=AllRows}) -> +< AllRows. +< +< %%-------------------------------------------------------------------- +< %% Function: get_result_affected_rows(MySQLRes) +< %% MySQLRes = term(), result of fetch function on "updated" +< %% Descrip.: Extract the Rows from MySQL Result on update +< %% Returns : AffectedRows +< %% AffectedRows = integer() +< %%-------------------------------------------------------------------- +< get_result_affected_rows(#mysql_result{affectedrows=AffectedRows}) -> +< AffectedRows. +< +< %%-------------------------------------------------------------------- +< %% Function: get_result_reason(MySQLRes) +< %% MySQLRes = term(), result of fetch function on "error" +< %% Descrip.: Extract the error Reason from MySQL Result on error +< %% Returns : Reason +< %% Reason = string() +< %%-------------------------------------------------------------------- +< get_result_reason(#mysql_result{error=Reason}) -> +< Reason. +< +< %%-------------------------------------------------------------------- +< %% Function: quote(String) +< %% String = string() +< %% Descrip.: Quote a string so that it can be included safely in a +< %% MySQL query. +< %% Returns : Quoted = string() +--- +> %% @spec (String) -> +> %% Quoted +> %% +> %% String = string() +> %% +> %% Quoted = string() +> %% +> %% @doc Quote a string so that it can be included safely in a +> %% MySQL query. +> %% @end +391,392c344 +< conn_list = ConnList, +< gc_tref = undefined +--- +> conn_list = ConnList +414a370,371 +> %% @clear +> +472,487d442 +< handle_call(stop, _From, State) -> +< {stop, normal, State}; +< +< handle_call({gc_each, Millisec}, _From, State) -> +< case State#state.gc_tref of +< undefined -> ok; +< TRef -> +< timer:cancel(TRef) +< end, +< case timer:send_interval(Millisec, gc) of +< {ok, NewTRef} -> +< {reply, ok, State#state{gc_tref = NewTRef}}; +< {error, Reason} -> +< {reply, {error, Reason}, State} +< end; +< +499a458,460 +> +> %% @clear +> +512a477,478 +> %% @clear +> +551,556d519 +< handle_info(gc, #state{conn_list = Connections} = State) -> +< [erlang:garbage_collect(C#mysql_connection.conn_pid) || C <- Connections], +< erlang:garbage_collect(self()), +< {noreply, State}; +< +< +573,575d537 +< lists:foreach(fun(MysqlConn) -> +< MysqlConn#mysql_connection.conn_pid ! close +< end, State#state.conn_list), +Only in ejabberd-modules/mysql/trunk/: mysql.hrl +diff -I '^%.*$' ejabberd-modules/mysql/trunk/mysql_auth.erl yxa-1.0/src/mysql/mysql_auth.erl +28d28 +< -define(FOUND_ROWS, 2). +109,110c113 +< Caps = ?LONG_PASSWORD bor ?LONG_FLAG +< bor ?TRANSACTIONS bor ?FOUND_ROWS, +--- +> Caps = ?LONG_PASSWORD bor ?LONG_FLAG bor ?TRANSACTIONS, +126,127c129 +< ?PROTOCOL_41 bor ?SECURE_CONNECTION bor DBCaps +< bor ?FOUND_ROWS, +--- +> ?PROTOCOL_41 bor ?SECURE_CONNECTION bor DBCaps, +diff -I '^%.*$' ejabberd-modules/mysql/trunk/mysql_conn.erl yxa-1.0/src/mysql/mysql_conn.erl +67d48 +< start_link/6, +69,71c50 +< fetch/4, +< squery/4, +< stop/1 +--- +> fetch/4 +80c59,60 +< -include("mysql.hrl"). +--- +> %% @type state() = #state{}. +> %% no description +82d61 +< mysql_version, +92,94d70 +< -define(DEFAULT_RESULT_TYPE, list). +< -define(MYSQL_4_0, 40). %% Support for MySQL 4.0.x +< -define(MYSQL_4_1, 41). %% Support for MySQL 4.1.x et 5.0.x +121,135d99 +< post_start(Pid, LogFun). +< +< start_link(Host, Port, User, Password, Database, LogFun) when is_list(Host), is_integer(Port), is_list(User), +< is_list(Password), is_list(Database) -> +< ConnPid = self(), +< Pid = spawn_link(fun () -> +< init(Host, Port, User, Password, Database, LogFun, ConnPid) +< end), +< post_start(Pid, LogFun). +< +< %% part of start/6 or start_link/6: +< post_start(Pid, _LogFun) -> +< %%Timeout = get_option(timeout, Options, ?DEFAULT_STANDALONE_TIMEOUT), +< %%TODO find a way to get configured Options here +< Timeout= ?DEFAULT_STANDALONE_TIMEOUT, +140,147c104,109 +< stop(Pid), +< {error, Reason} +< % Unknown -> +< % mysql:log(_LogFun, error, "mysql_conn: Received unknown signal, exiting"), +< % mysql:log(_LogFun, debug, "mysql_conn: Unknown signal : ~p", [Unknown]), +< % {error, "unknown signal received"} +< after Timeout -> +< stop(Pid), +--- +> {error, Reason}; +> Unknown -> +> mysql:log(LogFun, error, "mysql_conn: Received unknown signal, exiting"), +> mysql:log(LogFun, debug, "mysql_conn: Unknown signal : ~p", [Unknown]), +> {error, "unknown signal received"} +> after 5000 -> +173d132 +< +175,177c134 +< squery(Pid, Query, From, []). +< fetch(Pid, Query, From, Timeout) -> +< squery(Pid, Query, From, [{timeout, Timeout}]). +--- +> fetch(Pid, Query, From, ?DEFAULT_STANDALONE_TIMEOUT). +179c136 +< squery(Pid, Query, From, Options) when is_pid(Pid), is_list(Query) -> +--- +> fetch(Pid, Query, From, Timeout) when is_pid(Pid), is_list(Query), is_integer(Timeout) -> +181,183c138 +< Timeout = get_option(timeout, Options, ?DEFAULT_STANDALONE_TIMEOUT), +< TRef = erlang:start_timer(Timeout, self(), timeout), +< Pid ! {fetch, TRef, Query, From, Options}, +--- +> Pid ! {fetch, Query, From}, +187c142,147 +< wait_fetch_result(TRef, Pid); +--- +> receive +> {fetch_result, Pid, Result} -> +> Result +> after Timeout -> +> {error, "query timed out"} +> end; +193,218d152 +< wait_fetch_result(TRef, Pid) -> +< receive +< {fetch_result, TRef, Pid, Result} -> +< case erlang:cancel_timer(TRef) of +< false -> +< receive +< {timeout, TRef, _} -> +< ok +< after 0 -> +< ok +< end; +< _ -> +< ok +< end, +< Result; +< {fetch_result, _BadRef, Pid, _Result} -> +< wait_fetch_result(TRef, Pid); +< {timeout, TRef, _} -> +< stop(Pid), +< {error, "query timed out"} +< end. +< +< stop(Pid) -> +< Pid ! close. +< +< +275,282c215,217 +< {ok, Version} -> +< case do_query(Sock, RecvPid, LogFun, "use " ++ Database, Version, [{result_type, binary}]) of +< {error, MySQLRes} -> +< mysql:log(LogFun, error, "mysql_conn: Failed changing to database ~p : ~p", +< [Database, mysql:get_result_reason(MySQLRes)]), +< Parent ! {mysql_conn, self(), {error, failed_changing_database}}; +< %% ResultType: data | updated +< {_ResultType, _MySQLRes} -> +--- +> ok -> +> case do_query(Sock, RecvPid, LogFun, "use " ++ Database) of +> {ok, _Fields, _Rows} -> +284,285c219 +< State = #state{mysql_version=Version, +< recv_pid = RecvPid, +--- +> State = #state{recv_pid = RecvPid, +290c224,228 +< loop(State) +--- +> loop(State); +> {error, Reason} -> +> mysql:log(LogFun, error, "mysql_conn: Failed changing to database ~p : ~p", +> [Database, Reason]), +> Parent ! {mysql_conn, self(), {error, failed_changing_database}} +311c251 +< {fetch, Ref, Query, GenSrvFrom, Options} -> +--- +> {fetch, Query, GenSrvFrom} -> +314c254 +< Res = do_query(State, Query, Options), +--- +> Res = do_query(State, Query), +318c258 +< GenSrvFrom ! {fetch_result, Ref, self(), Res}; +--- +> GenSrvFrom ! {fetch_result, self(), Res}; +320,323d259 +< %% the timer is canceled in wait_fetch_result/2, but we wait on that funtion only if the query +< %% was not sent using the mysql gen_server. So we at least should try to cancel the timer here +< %% (no warranty, the gen_server can still receive timeout messages) +< erlang:cancel_timer(Ref), +333,334d268 +< close -> +< close_connection(State); +336,337c270,271 +< mysql:log(State#state.log_fun, error, "mysql_conn: Received unknown signal, exiting : ~p", [Unknown]), +< close_connection(State), +--- +> mysql:log(State#state.log_fun, error, "mysql_conn: Received unknown signal, exiting"), +> mysql:log(State#state.log_fun, debug, "mysql_conn: Unknown signal : ~p", [Unknown]), +355c293 +< {Version, Salt1, Salt2, Caps} = greeting(Packet, LogFun), +--- +> {Salt1, Salt2, Caps} = greeting(Packet, LogFun), +365c303 +< {ok,Version}; +--- +> ok; +382c320 +< <> = Packet, +--- +> <<_Protocol:8, Rest/binary>> = Packet, +389,391c327,329 +< mysql:log(LogFun, debug, "mysql_conn: greeting version ~p (protocol ~p) salt ~p caps ~p serverchar ~p salt2 ~p", +< [Version, Protocol, Salt, Caps, ServerChar, Salt2]), +< {normalize_version(Version, LogFun), Salt, Salt2, Caps}. +--- +> mysql:log(LogFun, debug, "mysql_conn: greeting version ~p salt ~p caps ~p serverchar ~p salt2 ~p", +> [Version, Salt, Caps, ServerChar, Salt2]), +> {Salt, Salt2, Caps}. +394c332 +< asciz(Data) when is_binary(Data) -> +--- +> asciz(Data) when binary(Data) -> +396c334 +< asciz(Data) when is_list(Data) -> +--- +> asciz(Data) when list(Data) -> +416c355 +< get_query_response(LogFun, RecvPid, Version, Options) -> +--- +> get_query_response(LogFun, RecvPid) -> +421,423c360 +< %% No Tabular data +< <> = Rest, +< {updated, #mysql_result{affectedrows=AffectedRows}}; +--- +> {ok, [], []}; +426c363 +< {error, #mysql_result{error=binary_to_list(Message)}}; +--- +> {error, binary_to_list(Message)}; +428,429c365 +< %% Tabular data received +< case get_fields(LogFun, RecvPid, [], Version) of +--- +> case get_fields(LogFun, RecvPid, []) of +431,432c367 +< ResultType = get_option(result_type, Options, ?DEFAULT_RESULT_TYPE), +< case get_rows(Fieldcount, LogFun, RecvPid, ResultType, []) of +--- +> case get_rows(Fieldcount, LogFun, RecvPid, []) of +434c369 +< {data, #mysql_result{fieldinfo=Fields, rows=Rows}}; +--- +> {ok, Fields, Rows}; +436c371 +< {error, #mysql_result{error=Reason}} +--- +> {error, Reason} +439c374 +< {error, #mysql_result{error=Reason}} +--- +> {error, Reason} +443c378 +< {error, #mysql_result{error=Reason}} +--- +> {error, Reason} +457,458c395 +< %% Support for MySQL 4.0.x: +< get_fields(LogFun, RecvPid, Res, ?MYSQL_4_0) -> +--- +> get_fields(LogFun, RecvPid, Res) -> +477,479d413 +< %% TODO: Check on MySQL 4.0 if types are specified +< %% using the same 4.1 formalism and could +< %% be expanded to atoms: +481,514c415 +< get_fields(LogFun, RecvPid, [This | Res], ?MYSQL_4_0) +< end; +< {error, Reason} -> +< {error, Reason} +< end; +< %% Support for MySQL 4.1.x and 5.x: +< get_fields(LogFun, RecvPid, Res, ?MYSQL_4_1) -> +< case do_recv(LogFun, RecvPid, undefined) of +< {ok, Packet, _Num} -> +< case Packet of +< <<254:8>> -> +< {ok, lists:reverse(Res)}; +< <<254:8, Rest/binary>> when size(Rest) < 8 -> +< {ok, lists:reverse(Res)}; +< _ -> +< {_Catalog, Rest} = get_with_length(Packet), +< {_Database, Rest2} = get_with_length(Rest), +< {Table, Rest3} = get_with_length(Rest2), +< %% OrgTable is the real table name if Table is an alias +< {_OrgTable, Rest4} = get_with_length(Rest3), +< {Field, Rest5} = get_with_length(Rest4), +< %% OrgField is the real field name if Field is an alias +< {_OrgField, Rest6} = get_with_length(Rest5), +< +< <<_Metadata:8/little, _Charset:16/little, +< Length:32/little, Type:8/little, +< _Flags:16/little, _Decimals:8/little, +< _Rest7/binary>> = Rest6, +< +< This = {binary_to_list(Table), +< binary_to_list(Field), +< Length, +< get_field_datatype(Type)}, +< get_fields(LogFun, RecvPid, [This | Res], ?MYSQL_4_1) +--- +> get_fields(LogFun, RecvPid, [This | Res]) +530c435 +< get_rows(N, LogFun, RecvPid, ResultType, Res) -> +--- +> get_rows(N, LogFun, RecvPid, Res) -> +537,538c442,443 +< {ok, This} = get_row(N, Packet, ResultType, []), +< get_rows(N, LogFun, RecvPid, ResultType, [This | Res]) +--- +> {ok, This} = get_row(N, Packet, []), +> get_rows(N, LogFun, RecvPid, [This | Res]) +544d448 +< +546c450 +< get_row(0, _Data, _ResultType, Res) -> +--- +> get_row(0, _Data, Res) -> +548c452 +< get_row(N, Data, ResultType, Res) -> +--- +> get_row(N, Data, Res) -> +554,559c458 +< if +< ResultType == list -> +< binary_to_list(Col); +< ResultType == binary -> +< Col +< end +--- +> binary_to_list(Col) +561c460,461 +< get_row(N - 1, Rest, ResultType, [This | Res]). +--- +> get_row(N - 1, Rest, [This | Res]). +> +574,579d473 +< close_connection(State) -> +< Result = gen_tcp:close(State#state.socket), +< mysql:log(State#state.log_fun, normal, "Closing connection ~p: ~p~n", [State#state.socket, Result]), +< Result. +< +< +590c486 +< do_query(State, Query, Options) when is_record(State, state) -> +--- +> do_query(State, Query) when is_record(State, state) -> +594,596c490 +< Query, +< State#state.mysql_version, +< Options +--- +> Query +599,600c493 +< do_query(Sock, RecvPid, LogFun, Query, Version, Options) when is_pid(RecvPid), +< is_list(Query) -> +--- +> do_query(Sock, RecvPid, LogFun, Query) when is_pid(RecvPid), is_list(Query) -> +604c497 +< get_query_response(LogFun, RecvPid, Version, Options); +--- +> get_query_response(LogFun, RecvPid); +623,693d517 +< +< %%-------------------------------------------------------------------- +< %% Function: normalize_version(Version, LogFun) +< %% Version = string() +< %% LogFun = undefined | function() with arity 3 +< %% Descrip.: Return a flag corresponding to the MySQL version used. +< %% The protocol used depends on this flag. +< %% Returns : Version = string() +< %%-------------------------------------------------------------------- +< normalize_version([$4,$.,$0|_T], LogFun) -> +< mysql:log(LogFun, debug, "Switching to MySQL 4.0.x protocol.~n"), +< ?MYSQL_4_0; +< normalize_version([$4,$.,$1|_T], _LogFun) -> +< ?MYSQL_4_1; +< normalize_version([$5|_T], _LogFun) -> +< %% MySQL version 5.x protocol is compliant with MySQL 4.1.x: +< ?MYSQL_4_1; +< normalize_version(_Other, LogFun) -> +< mysql:log(LogFun, error, "MySQL version not supported: MySQL Erlang module might not work correctly.~n"), +< %% Error, but trying the oldest protocol anyway: +< ?MYSQL_4_0. +< +< %%-------------------------------------------------------------------- +< %% Function: get_field_datatype(DataType) +< %% DataType = integer(), MySQL datatype +< %% Descrip.: Return MySQL field datatype as description string +< %% Returns : String, MySQL datatype +< %%-------------------------------------------------------------------- +< get_field_datatype(0) -> 'DECIMAL'; +< get_field_datatype(1) -> 'TINY'; +< get_field_datatype(2) -> 'SHORT'; +< get_field_datatype(3) -> 'LONG'; +< get_field_datatype(4) -> 'FLOAT'; +< get_field_datatype(5) -> 'DOUBLE'; +< get_field_datatype(6) -> 'NULL'; +< get_field_datatype(7) -> 'TIMESTAMP'; +< get_field_datatype(8) -> 'LONGLONG'; +< get_field_datatype(9) -> 'INT24'; +< get_field_datatype(10) -> 'DATE'; +< get_field_datatype(11) -> 'TIME'; +< get_field_datatype(12) -> 'DATETIME'; +< get_field_datatype(13) -> 'YEAR'; +< get_field_datatype(14) -> 'NEWDATE'; +< get_field_datatype(16) -> 'BIT'; +< get_field_datatype(246) -> 'DECIMAL'; +< get_field_datatype(247) -> 'ENUM'; +< get_field_datatype(248) -> 'SET'; +< get_field_datatype(249) -> 'TINYBLOB'; +< get_field_datatype(250) -> 'MEDIUM_BLOG'; +< get_field_datatype(251) -> 'LONG_BLOG'; +< get_field_datatype(252) -> 'BLOB'; +< get_field_datatype(253) -> 'VAR_STRING'; +< get_field_datatype(254) -> 'STRING'; +< get_field_datatype(255) -> 'GEOMETRY'. +< +< %%-------------------------------------------------------------------- +< %% Function: get_option(Key1, Options, Default) -> Value1 +< %% Options = [Option] +< %% Option = {Key2, Value2} +< %% Key1 = Key2 = atom() +< %% Value1 = Value2 = Default = term() +< %% Descrip.: Return the option associated with Key passed to squery/4 +< %%-------------------------------------------------------------------- +< +< get_option(Key, Options, Default) -> +< case lists:keysearch(Key, 1, Options) of +< {value, {_, Value}} -> +< Value; +< false -> +< Default +< end. +Only in yxa-1.0/src/mysql/: overview.edoc +cube:~ hd$ diff --git a/diff-ejabberd-yxa.txt b/diff-ejabberd-yxa.txt new file mode 100644 index 00000000..67e051af --- /dev/null +++ b/diff-ejabberd-yxa.txt @@ -0,0 +1,1515 @@ +Diff of ejabberd mysql driver vs yxa mysql driver +------------------------------------------------- + +Both depend on Magnus Ahltorp's code. Half of the difference are simply changed +comments, as Yxa went edoc. The remainder are mostly result inspection extensions +on the part of the ejabberd source. + +This diff includes changes to comments. Find one ignoring comments here: +https://github.com/Eonblast/Emysql/blob/master/doc/diff-ejabberd-yxa-2.txt + +Yxa: https://github.com/fredrikt/yxa/tree/master/src/mysql/ +Ejabberd: http://svn.process-one.net/ejabberd-modules/mysql/trunk/ +Diff: Jan 2011 + +$ diff ejabberd-modules/mysql/trunk/ yxa-1.0/src/mysql/ +Only in ejabberd-modules/mysql/trunk/: .svn +Only in yxa-1.0/src/mysql/: Makefile.in +diff ejabberd-modules/mysql/trunk/mysql.erl yxa-1.0/src/mysql/mysql.erl +3,4c3,4 +< %%% Author : Magnus Ahltorp +< %%% Descrip.: MySQL client. +--- +> %%% @author Magnus Ahltorp +> %%% @doc MySQL client. +6c6,7 +< %%% Created : 4 Aug 2005 by Magnus Ahltorp +--- +> %%% @since 4 Aug 2005 by Magnus Ahltorp +> %%% @end +36,51c37,39 +< %%% Result = {data, MySQLRes} | {updated, MySQLRes} | +< %%% {error, MySQLRes} +< %%% +< %%% Actual data can be extracted from MySQLRes by calling the following API +< %%% functions: +< %%% - on data received: +< %%% FieldInfo = mysql:get_result_field_info(MysqlRes) +< %%% AllRows = mysql:get_result_rows(MysqlRes) +< %%% with FieldInfo = list() of {Table, Field, Length, Name} +< %%% and AllRows = list() of list() representing records +< %%% - on update: +< %%% Affected = mysql:get_result_affected_rows(MysqlRes) +< %%% with Affected = integer() +< %%% - on error: +< %%% Reason = mysql:get_result_reason(MysqlRes) +< %%% with Reason = string() +--- +> %%% Result = {ok, Fieldinfo, Rows} +> %%% Rows = list() of [Row] +> %%% Row = [string()] +73,77d60 +< get_result_field_info/1, +< get_result_rows/1, +< get_result_affected_rows/1, +< get_result_reason/1, +< +81,84c64 +< connect/7, +< stop/0, +< +< gc_each/1 +--- +> connect/7 +108c88,89 +< -include("mysql.hrl"). +--- +> %% @type state() = #state{}. +> %% no description +111,112c92 +< log_fun, %% undefined | function for logging, +< gc_tref %% undefined | timer:TRef +--- +> log_fun %% undefined | function for logging, +141,154c121,136 +< %% Function: start_link(Id, Host, User, Password, Database) +< %% start_link(Id, Host, Port, User, Password, Database) +< %% start_link(Id, Host, User, Password, Database, LogFun) +< %% start_link(Id, Host, Port, User, Password, Database, +< %% LogFun) +< %% Id = term(), first connection-group Id +< %% Host = string() +< %% Port = integer() +< %% User = string() +< %% Password = string() +< %% Database = string() +< %% LogFun = undefined | function() of arity 3 +< %% Descrip.: Starts the MySQL client gen_server process. +< %% Returns : {ok, Pid} | ignore | {error, Error} +--- +> %% @spec (Id, Host, User, Password, Database) start_link(Id, Host, +> %% Port, User, Password, Database) start_link(Id, Host, +> %% User, Password, Database, LogFun) start_link(Id, Host, +> %% Port, User, Password, Database, LogFun) -> +> %% {ok, Pid} | ignore | {error, Error} +> %% +> %% Id = term() "first connection-group Id" +> %% Host = string() +> %% Port = integer() +> %% User = string() +> %% Password = string() +> %% Database = string() +> %% LogFun = undefined | function() of arity 3 +> %% +> %% @doc Starts the MySQL client gen_server process. +> %% @end +173,178d154 +< stop() -> +< gen_server:call(?SERVER, stop). +< +< gc_each(Millisec) -> +< gen_server:call(?SERVER, {gc_each, Millisec}). +< +180,189c156,169 +< %% Function: fetch(Id, Query) +< %% fetch(Id, Query, Timeout) +< %% Id = term(), connection-group Id +< %% Query = string(), MySQL query in verbatim +< %% Timeout = integer() | infinity, gen_server timeout value +< %% Descrip.: Send a query and wait for the result. +< %% Returns : {data, MySQLRes} | +< %% {updated, MySQLRes} | +< %% {error, MySQLRes} +< %% MySQLRes = term() +--- +> %% @spec (Id, Query) fetch(Id, Query, Timeout) -> +> %% {ok, FieldInfo, Rows} | +> %% {error, Reason} +> %% +> %% Id = term() "connection-group Id" +> %% Query = string() "MySQL query in verbatim" +> %% Timeout = integer() | infinity "gen_server timeout value" +> %% +> %% FieldInfo = term() +> %% Rows = [[string()]] +> %% Reason = term() +> %% +> %% @doc Send a query and wait for the result. +> %% @end +197,241c177,186 +< %% Function: get_result_field_info(MySQLRes) +< %% MySQLRes = term(), result of fetch function on "data" +< %% Descrip.: Extract the FieldInfo from MySQL Result on data received +< %% Returns : FieldInfo +< %% FieldInfo = list() of {Table, Field, Length, Name} +< %%-------------------------------------------------------------------- +< get_result_field_info(#mysql_result{fieldinfo = FieldInfo}) -> +< FieldInfo. +< +< %%-------------------------------------------------------------------- +< %% Function: get_result_rows(MySQLRes) +< %% MySQLRes = term(), result of fetch function on "data" +< %% Descrip.: Extract the Rows from MySQL Result on data received +< %% Returns : Rows +< %% Rows = list() of list() representing records +< %%-------------------------------------------------------------------- +< get_result_rows(#mysql_result{rows=AllRows}) -> +< AllRows. +< +< %%-------------------------------------------------------------------- +< %% Function: get_result_affected_rows(MySQLRes) +< %% MySQLRes = term(), result of fetch function on "updated" +< %% Descrip.: Extract the Rows from MySQL Result on update +< %% Returns : AffectedRows +< %% AffectedRows = integer() +< %%-------------------------------------------------------------------- +< get_result_affected_rows(#mysql_result{affectedrows=AffectedRows}) -> +< AffectedRows. +< +< %%-------------------------------------------------------------------- +< %% Function: get_result_reason(MySQLRes) +< %% MySQLRes = term(), result of fetch function on "error" +< %% Descrip.: Extract the error Reason from MySQL Result on error +< %% Returns : Reason +< %% Reason = string() +< %%-------------------------------------------------------------------- +< get_result_reason(#mysql_result{error=Reason}) -> +< Reason. +< +< %%-------------------------------------------------------------------- +< %% Function: quote(String) +< %% String = string() +< %% Descrip.: Quote a string so that it can be included safely in a +< %% MySQL query. +< %% Returns : Quoted = string() +--- +> %% @spec (String) -> +> %% Quoted +> %% +> %% String = string() +> %% +> %% Quoted = string() +> %% +> %% @doc Quote a string so that it can be included safely in a +> %% MySQL query. +> %% @end +266,274c211,222 +< %% Function: asciz_binary(Data, Acc) +< %% Data = binary() +< %% Acc = list(), input accumulator +< %% Descrip.: Find the first zero-byte in Data and add everything +< %% before it to Acc, as a string. +< %% Returns : {NewList, Rest} +< %% NewList = list(), Acc plus what we extracted from Data +< %% Rest = binary(), whatever was left of Data, not +< %% including the zero-byte +--- +> %% @spec (Data, Acc) -> +> %% {NewList, Rest} +> %% +> %% Data = binary() +> %% Acc = list() "input accumulator" +> %% +> %% NewList = list() "Acc plus what we extracted from Data" +> %% Rest = binary() "whatever was left of Data, not including the zero-byte" +> %% +> %% @doc Find the first zero-byte in Data and add everything before +> %% it to Acc, as a string. +> %% @end +284,295c232,245 +< %% Function: connect(Id, Host, Port, User, Password, Database, +< %% Reconnect) +< %% Id = term(), connection-group Id +< %% Host = string() +< %% Port = undefined | integer() +< %% User = string() +< %% Password = string() +< %% Database = string() +< %% Reconnect = true | false +< %% Descrip.: Starts a MySQL connection and, if successfull, registers +< %% it with the mysql_dispatcher. +< %% Returns : {ok, ConnPid} | {error, Reason} +--- +> %% @spec (Id, Host, Port, User, Password, Database, Reconnect) -> +> %% {ok, ConnPid} | {error, Reason} +> %% +> %% Id = term() "connection-group Id" +> %% Host = string() +> %% Port = undefined | integer() +> %% User = string() +> %% Password = string() +> %% Database = string() +> %% Reconnect = true | false +> %% +> %% @doc Starts a MySQL connection and, if successfull, registers +> %% it with the mysql_dispatcher. +> %% @end +332,341c282,288 +< %% Function: log(LogFun, Level, Format) +< %% log(LogFun, Level, Format, Arguments) +< %% LogFun = undefined | function() with arity 3 +< %% Level = debug | normal | error +< %% Format = string() +< %% Arguments = list() of term() +< %% Descrip.: Either call the function LogFun with the Level, Format +< %% and Arguments as parameters or log it to the console if +< %% LogFun is undefined. +< %% Returns : void() +--- +> %% @spec (LogFun, Level, Format) log(LogFun, Level, Format, +> %% Arguments) -> void() +> %% +> %% LogFun = undefined | function() with arity 3 +> %% Level = debug | normal | error +> %% Format = string() +> %% Arguments = [term()] +343c290,293 +< %% Note : Exported only for use by the mysql_* modules. +--- +> %% @doc Either call the function LogFun with the Level, Format and +> %% Arguments as parameters or log it to the console if +> %% LogFun is undefined. Note : Exported only for use by the +> %% mysql_* modules. +344a295 +> %% @end +362,374c313,327 +< %% Function: init(Args) -> {ok, State} | +< %% {ok, State, Timeout} | +< %% ignore | +< %% {stop, Reason} +< %% Args = [Id, Host, Port, User, Password, Database, LogFun] +< %% Id = term(), connection-group Id +< %% Host = string() +< %% Port = integer() +< %% User = string() +< %% Password = string() +< %% Database = string() +< %% LogFun = undefined | function() with arity 3 +< %% Descrip.: Initiates the gen_server (MySQL dispatcher). +--- +> %% @spec (Args) -> {ok, State} | {ok, State, Timeout} | ignore | +> %% {stop, Reason} -> term() +> %% +> %% Args = [Id, Host, Port, User, Password, Database, LogFun] +> %% Id = term() "connection-group Id" +> %% Host = string() +> %% Port = integer() +> %% User = string() +> %% Password = string() +> %% Database = string() +> %% LogFun = undefined | function() with arity 3 +> %% +> %% @doc Initiates the gen_server (MySQL dispatcher). +> %% @hidden +> %% @end +391,392c344 +< conn_list = ConnList, +< gc_tref = undefined +--- +> conn_list = ConnList +405,412c357,367 +< %% Function: handle_call(Msg, From, State) +< %% Descrip.: 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) +--- +> %% @spec handle_call(Msg, From, State) -> +> %% {reply, Reply, State} | +> %% {reply, Reply, State, Timeout} | +> %% {noreply, State} | +> %% {noreply, State, Timeout} | +> %% {stop, Reason, Reply, State} | +> %% {stop, Reason, State} +> %% +> %% @doc Handling call messages. +> %% @hidden +> %% @end +414a370,371 +> %% @clear +> +417,427c374,389 +< %% Function: handle_call({fetch, Id, Query}, From, State) +< %% Id = term(), connection-group id +< %% Query = string(), MySQL query +< %% Descrip.: Make a MySQL query. Use the first connection matching Id +< %% in our connection-list. Don't block the mysql_dispatcher +< %% by returning {noreply, ...} here and let the mysql_conn +< %% do gen_server:reply(...) when it has an answer. +< %% Returns : {noreply, NewState} | +< %% {reply, {error, Reason}, State} +< %% NewState = state record() +< %% Reason = atom() | string() +--- +> %% @spec ({fetch, Id, Query}, From, State) -> +> %% {noreply, NewState} | +> %% {reply, {error, Reason}, State} +> %% +> %% Id = term() "connection-group id" +> %% Query = string() "MySQL query" +> %% +> %% NewState = #state{} +> %% Reason = atom() | string() +> %% +> %% @doc Make a MySQL query. Use the first connection matching Id +> %% in our connection-list. Don't block the mysql_dispatcher +> %% by returning {noreply, ...} here and let the mysql_conn +> %% do gen_server:reply(...) when it has an answer. +> %% @hidden +> %% @end +444,450c406,417 +< %% Function: handle_call({add_mysql_connection, Conn}, From, State) +< %% Conn = mysql_connection record() +< %% Descrip.: Add Conn to our list of connections. +< %% Returns : {reply, Reply, NewState} +< %% Reply = ok | {error, Reason} +< %% NewState = state record() +< %% Reason = string() +--- +> %% @spec ({add_mysql_connection, Conn}, From, State) -> +> %% {reply, Reply, NewState} +> %% +> %% Conn = #mysql_connection{} +> %% +> %% Reply = ok | {error, Reason} +> %% NewState = #state{} +> %% Reason = string() +> %% +> %% @doc Add Conn to our list of connections. +> %% @hidden +> %% @end +464,467c431,438 +< %% Function: handle_call(get_logfun, From, State) +< %% Descrip.: Fetch our logfun. +< %% Returns : {reply, {ok, LogFun}, State} +< %% LogFun = undefined | function() with arity 3 +--- +> %% @spec (get_logfun, From, State) -> +> %% {reply, {ok, LogFun}, State} +> %% +> %% LogFun = undefined | function() with arity 3 +> %% +> %% @doc Fetch our logfun. +> %% @hidden +> %% @end +472,487d442 +< handle_call(stop, _From, State) -> +< {stop, normal, State}; +< +< handle_call({gc_each, Millisec}, _From, State) -> +< case State#state.gc_tref of +< undefined -> ok; +< TRef -> +< timer:cancel(TRef) +< end, +< case timer:send_interval(Millisec, gc) of +< {ok, NewTRef} -> +< {reply, ok, State#state{gc_tref = NewTRef}}; +< {error, Reason} -> +< {reply, {error, Reason}, State} +< end; +< +494,498c449,456 +< %% Function: handle_cast(Msg, State) +< %% Descrip.: Handling cast messages +< %% Returns : {noreply, State} | +< %% {noreply, State, Timeout} | +< %% {stop, Reason, State} (terminate/2 is called) +--- +> %% @spec handle_cast(Msg, State) -> +> %% {noreply, State} | +> %% {noreply, State, Timeout} | +> %% {stop, Reason, State} +> %% +> %% @doc Handling cast messages +> %% @hidden +> %% @end +499a458,460 +> +> %% @clear +> +506,510c467,474 +< %% Function: handle_info(Msg, State) +< %% Descrip.: Handling all non call/cast messages +< %% Returns : {noreply, State} | +< %% {noreply, State, Timeout} | +< %% {stop, Reason, State} (terminate/2 is called) +--- +> %% @spec handle_info(Msg, State) -> +> %% {noreply, State} | +> %% {noreply, State, Timeout} | +> %% {stop, Reason, State} +> %% +> %% @doc Handling all non call/cast messages +> %% @hidden +> %% @end +512a477,478 +> %% @clear +> +514,526c480,495 +< %% Function: handle_info({'DOWN', ...}, State) +< %% Descrip.: Handle a message that one of our monitored processes +< %% (mysql_conn processes in our connection list) has exited. +< %% Remove the entry from our list. +< %% Returns : {noreply, NewState} | +< %% {stop, normal, State} +< %% NewState = state record() +< %% +< %% Note : For now, we stop if our connection list becomes empty. +< %% We should try to reconnect for a while first, to not +< %% eventually stop the whole OTP application if the MySQL- +< %% server is shut down and the mysql_dispatcher was super- +< %% vised by an OTP supervisor. +--- +> %% @spec ({'DOWN', ...}, State) -> +> %% {noreply, NewState} | +> %% {stop, normal, State} +> %% +> %% NewState = #state{} +> %% +> %% @doc Handle a message that one of our monitored processes +> %% (mysql_conn processes in our connection list) has exited. +> %% Remove the entry from our list. Note : For now, we stop +> %% if our connection list becomes empty. We should try to +> %% reconnect for a while first, to not eventually stop the +> %% whole OTP application if the MySQL- server is shut down +> %% and the mysql_dispatcher was super- vised by an OTP +> %% supervisor. +> %% @hidden +> %% @end +551,556d519 +< handle_info(gc, #state{conn_list = Connections} = State) -> +< [erlang:garbage_collect(C#mysql_connection.conn_pid) || C <- Connections], +< erlang:garbage_collect(self()), +< {noreply, State}; +< +< +562,564c525,529 +< %% Function: terminate(Reason, State) +< %% Descrip.: Shutdown the server +< %% Returns : Reason +--- +> %% @spec (Reason, State) -> Reason +> %% +> %% @doc Shutdown the server +> %% @hidden +> %% @end +573,575d537 +< lists:foreach(fun(MysqlConn) -> +< MysqlConn#mysql_connection.conn_pid ! close +< end, State#state.conn_list), +579,581c541,545 +< %% Function: code_change(_OldVsn, State, _Extra) +< %% Descrip.: Convert process state when code is changed +< %% Returns : {ok, State} +--- +> %% @spec (_OldVsn, State, _Extra) -> {ok, State} +> %% +> %% @doc Convert process state when code is changed +> %% @hidden +> %% @end +591,596c555,565 +< %% Function: add_mysql_conn(Conn, ConnList) +< %% Conn = mysql_connection record() +< %% ConnList = list() of mysql_connection record() +< %% Descrip.: Set up process monitoring of the mysql_conn process and +< %% then add it (first) to ConnList. +< %% Returns : NewConnList = list() of mysql_connection record() +--- +> %% @spec (Conn, ConnList) -> +> %% NewConnList +> %% +> %% Conn = #mysql_connection{} +> %% ConnList = [#mysql_connection{}] +> %% +> %% NewConnList = [#mysql_connection{}] +> %% +> %% @doc Set up process monitoring of the mysql_conn process and +> %% then add it (first) to ConnList. +> %% @end +603,610c572,583 +< %% Function: remove_mysql_connection_using_pid(Pid, ConnList) +< %% Pid = pid() +< %% ConnList = list() of mysql_connection record() +< %% Descrip.: Removes the first mysql_connection in ConnList that has +< %% a pid matching Pid. +< %% Returns : {ok, Conn, NewConnList} | nomatch +< %% Conn = mysql_connection record() +< %% NewConnList = list() of mysql_connection record() +--- +> %% @spec (Pid, ConnList) -> +> %% {ok, Conn, NewConnList} | nomatch +> %% +> %% Pid = pid() +> %% ConnList = [#mysql_connection{}] +> %% +> %% Conn = #mysql_connection{} +> %% NewConnList = [#mysql_connection{}] +> %% +> %% @doc Removes the first mysql_connection in ConnList that has a +> %% pid matching Pid. +> %% @end +620,628c593,604 +< %% Function: get_next_mysql_connection_for_id(Id, ConnList) +< %% Id = term(), connection-group id +< %% ConnList = list() of mysql_connection record() +< %% Descrip.: Find the first mysql_connection in ConnList that has an +< %% id matching Id. +< %% Returns : {ok, Conn, NewConnList} | nomatch +< %% Conn = mysql_connection record() +< %% NewConnList = list() of mysql_connection record(), same +< %% as ConnList but without Conn +--- +> %% @spec (Id, ConnList) -> +> %% {ok, Conn, NewConnList} | nomatch +> %% +> %% Id = term() "connection-group id" +> %% ConnList = [#mysql_connection{}] +> %% +> %% Conn = #mysql_connection{} +> %% NewConnList = [#mysql_connection{}] "same as ConnList but without Conn" +> %% +> %% @doc Find the first mysql_connection in ConnList that has an id +> %% matching Id. +> %% @end +641,647c617,625 +< %% Function: start_reconnect(Conn, LogFun) +< %% Conn = mysql_connection record() +< %% LogFun = undefined | function() with arity 3 +< %% Descrip.: Spawns a process that will try to re-establish a new +< %% connection instead of the one in Conn which has just +< %% died. +< %% Returns : ok +--- +> %% @spec (Conn, LogFun) -> ok +> %% +> %% Conn = #mysql_connection{} +> %% LogFun = undefined | function() with arity 3 +> %% +> %% @doc Spawns a process that will try to re-establish a new +> %% connection instead of the one in Conn which has just +> %% died. +> %% @end +659,664c637,644 +< %% Function: reconnect_loop(Conn, LogFun, 0) +< %% Conn = mysql_connection record() +< %% LogFun = undefined | function() with arity 3 +< %% Descrip.: Loop indefinately until we are able to reconnect to the +< %% server specified in the now dead connection Conn. +< %% Returns : ok +--- +> %% @spec (Conn, LogFun, 0) -> ok +> %% +> %% Conn = #mysql_connection{} +> %% LogFun = undefined | function() with arity 3 +> %% +> %% @doc Loop indefinately until we are able to reconnect to the +> %% server specified in the now dead connection Conn. +> %% @end +Only in ejabberd-modules/mysql/trunk/: mysql.hrl +diff ejabberd-modules/mysql/trunk/mysql_auth.erl yxa-1.0/src/mysql/mysql_auth.erl +3,5c3,6 +< %%% Author : Fredrik Thulin +< %%% Descrip.: MySQL client authentication functions. +< %%% Created : 4 Aug 2005 by Fredrik Thulin +--- +> %%% @author Fredrik Thulin +> %%% @doc MySQL client authentication functions. +> %%% @since 4 Aug 2005 by Fredrik Thulin +> %%% @end +10c11 +< %%% Copyright (c) 2001-2004 Kungliga Tekniska Högskolan +--- +> %%% Copyright (c) 2001-2004 Kungliga Tekniska H?gskolan +28d28 +< -define(FOUND_ROWS, 2). +42,52c42,54 +< %% Function: do_old_auth(Sock, RecvPid, SeqNum, User, Password, Salt1, +< %% LogFun) +< %% Sock = term(), gen_tcp socket +< %% RecvPid = pid(), receiver process pid +< %% SeqNum = integer(), first sequence number we should use +< %% User = string(), MySQL username +< %% Password = string(), MySQL password +< %% Salt1 = string(), salt 1 from server greeting +< %% LogFun = undefined | function() of arity 3 +< %% Descrip.: Perform old-style MySQL authentication. +< %% Returns : result of mysql_conn:do_recv/3 +--- +> %% @spec (Sock, RecvPid, SeqNum, User, Password, Salt1, LogFun) -> +> %% result of mysql_conn:do_recv/3 +> %% +> %% Sock = term() "gen_tcp socket" +> %% RecvPid = pid() "receiver process pid" +> %% SeqNum = integer() "first sequence number we should use" +> %% User = string() "MySQL username" +> %% Password = string() "MySQL password" +> %% Salt1 = string() "salt 1 from server greeting" +> %% LogFun = undefined | function() of arity 3 +> %% +> %% @doc Perform old-style MySQL authentication. +> %% @end +61,72c63,76 +< %% Function: do_new_auth(Sock, RecvPid, SeqNum, User, Password, Salt1, +< %% Salt2, LogFun) +< %% Sock = term(), gen_tcp socket +< %% RecvPid = pid(), receiver process pid +< %% SeqNum = integer(), first sequence number we should use +< %% User = string(), MySQL username +< %% Password = string(), MySQL password +< %% Salt1 = string(), salt 1 from server greeting +< %% Salt2 = string(), salt 2 from server greeting +< %% LogFun = undefined | function() of arity 3 +< %% Descrip.: Perform MySQL authentication. +< %% Returns : result of mysql_conn:do_recv/3 +--- +> %% @spec (Sock, RecvPid, SeqNum, User, Password, Salt1, Salt2, +> %% LogFun) -> result of mysql_conn:do_recv/3 +> %% +> %% Sock = term() "gen_tcp socket" +> %% RecvPid = pid() "receiver process pid" +> %% SeqNum = integer() "first sequence number we should use" +> %% User = string() "MySQL username" +> %% Password = string() "MySQL password" +> %% Salt1 = string() "salt 1 from server greeting" +> %% Salt2 = string() "salt 2 from server greeting" +> %% LogFun = undefined | function() of arity 3 +> %% +> %% @doc Perform MySQL authentication. +> %% @end +109,110c113 +< Caps = ?LONG_PASSWORD bor ?LONG_FLAG +< bor ?TRANSACTIONS bor ?FOUND_ROWS, +--- +> Caps = ?LONG_PASSWORD bor ?LONG_FLAG bor ?TRANSACTIONS, +126,127c129 +< ?PROTOCOL_41 bor ?SECURE_CONNECTION bor DBCaps +< bor ?FOUND_ROWS, +--- +> ?PROTOCOL_41 bor ?SECURE_CONNECTION bor DBCaps, +diff ejabberd-modules/mysql/trunk/mysql_conn.erl yxa-1.0/src/mysql/mysql_conn.erl +3,4c3,4 +< %%% Author : Fredrik Thulin +< %%% Descrip.: MySQL connection handler, handles de-framing of messages +--- +> %%% @author Fredrik Thulin +> %%% @doc MySQL connection handler, handles de-framing of messages +6,7c6,7 +< %%% Created : 5 Aug 2005 by Fredrik Thulin +< %%% Modified: 11 Jan 2006 by Mickael Remond +--- +> %%% @since 5 Aug 2005 by Fredrik Thulin +> %%% @end +34,36d33 +< %%% Note: In stand-alone mode you have to start Erlang crypto application by +< %%% yourself with crypto:start() +< %%% +41,58c38,40 +< %%% Result = {data, MySQLRes} | +< %%% {updated, MySQLRes} | +< %%% {error, MySQLRes} +< %%% Where: MySQLRes = #mysql_result +< %%% +< %%% Actual data can be extracted from MySQLRes by calling the following API +< %%% functions: +< %%% - on data received: +< %%% FieldInfo = mysql:get_result_field_info(MysqlRes) +< %%% AllRows = mysql:get_result_rows(MysqlRes) +< %%% with FieldInfo = list() of {Table, Field, Length, Name} +< %%% and AllRows = list() of list() representing records +< %%% - on update: +< %%% Affected= mysql:get_result_affected_rows(MysqlRes) +< %%% with Affected = integer() +< %%% - on error: +< %%% Reason = mysql:get_result_reason(MysqlRes) +< %%% with Reason = string() +--- +> %%% Result = {ok, Fields, Rows} | +> %%% {error, Reason} +> %%% +67d48 +< start_link/6, +69,71c50 +< fetch/4, +< squery/4, +< stop/1 +--- +> fetch/4 +80c59,60 +< -include("mysql.hrl"). +--- +> %% @type state() = #state{}. +> %% no description +82d61 +< mysql_version, +92,94d70 +< -define(DEFAULT_RESULT_TYPE, list). +< -define(MYSQL_4_0, 40). %% Support for MySQL 4.0.x +< -define(MYSQL_4_1, 41). %% Support for MySQL 4.1.x et 5.0.x +101,113c77,92 +< %% Function: start(Host, Port, User, Password, Database, LogFun) +< %% Function: start_link(Host, Port, User, Password, Database, LogFun) +< %% Host = string() +< %% Port = integer() +< %% User = string() +< %% Password = string() +< %% Database = string() +< %% LogFun = undefined | function() of arity 3 +< %% Descrip.: Starts a mysql_conn process that connects to a MySQL +< %% server, logs in and chooses a database. +< %% Returns : {ok, Pid} | {error, Reason} +< %% Pid = pid() +< %% Reason = string() +--- +> %% @spec (Host, Port, User, Password, Database, LogFun) -> +> %% {ok, Pid} | {error, Reason} +> %% +> %% Host = string() +> %% Port = integer() +> %% User = string() +> %% Password = string() +> %% Database = string() +> %% LogFun = undefined | function() of arity 3 +> %% +> %% Pid = pid() +> %% Reason = string() +> %% +> %% @doc Starts a mysql_conn process that connects to a MySQL +> %% server, logs in and chooses a database. +> %% @end +121,135d99 +< post_start(Pid, LogFun). +< +< start_link(Host, Port, User, Password, Database, LogFun) when is_list(Host), is_integer(Port), is_list(User), +< is_list(Password), is_list(Database) -> +< ConnPid = self(), +< Pid = spawn_link(fun () -> +< init(Host, Port, User, Password, Database, LogFun, ConnPid) +< end), +< post_start(Pid, LogFun). +< +< %% part of start/6 or start_link/6: +< post_start(Pid, _LogFun) -> +< %%Timeout = get_option(timeout, Options, ?DEFAULT_STANDALONE_TIMEOUT), +< %%TODO find a way to get configured Options here +< Timeout= ?DEFAULT_STANDALONE_TIMEOUT, +140,147c104,109 +< stop(Pid), +< {error, Reason} +< % Unknown -> +< % mysql:log(_LogFun, error, "mysql_conn: Received unknown signal, exiting"), +< % mysql:log(_LogFun, debug, "mysql_conn: Unknown signal : ~p", [Unknown]), +< % {error, "unknown signal received"} +< after Timeout -> +< stop(Pid), +--- +> {error, Reason}; +> Unknown -> +> mysql:log(LogFun, error, "mysql_conn: Received unknown signal, exiting"), +> mysql:log(LogFun, debug, "mysql_conn: Unknown signal : ~p", [Unknown]), +> {error, "unknown signal received"} +> after 5000 -> +152,171c114,131 +< %% Function: fetch(Pid, Query, From) +< %% fetch(Pid, Query, From, Timeout) +< %% Pid = pid(), mysql_conn to send fetch-request to +< %% Query = string(), MySQL query in verbatim +< %% From = pid() or term(), use a From of self() when +< %% using this module for a single connection, +< %% or pass the gen_server:call/3 From argument if +< %% using a gen_server to do the querys (e.g. the +< %% mysql_dispatcher) +< %% Timeout = integer() | infinity, gen_server timeout value +< %% Descrip.: Send a query and wait for the result if running stand- +< %% alone (From = self()), but don't block the caller if we +< %% are not running stand-alone (From = gen_server From). +< %% Returns : ok | (non-stand-alone mode) +< %% {data, #mysql_result} | (stand-alone mode) +< %% {updated, #mysql_result} | (stand-alone mode) +< %% {error, #mysql_result} (stand-alone mode) +< %% FieldInfo = term() +< %% Rows = list() of [string()] +< %% Reason = term() +--- +> %% @spec (Pid, Query, From) fetch(Pid, Query, From, Timeout) -> +> %% ok | +> %% {ok, FieldInfo, Rows} | +> %% {error, Reason} (stand-alone mode) +> %% +> %% Pid = pid() "mysql_conn to send fetch-request to" +> %% Query = string() "MySQL query in verbatim" +> %% From = pid() or term() "use a From of self() when using this module for a single connection, or pass the gen_server:call/3 From argument if using a gen_server to do the querys (e.g. the mysql_dispatcher)" +> %% Timeout = integer() | infinity "gen_server timeout value" +> %% +> %% FieldInfo = term() +> %% Rows = [[string()]] +> %% Reason = term() +> %% +> %% @doc Send a query and wait for the result if running stand- +> %% alone (From = self()), but don't block the caller if we +> %% are not running stand-alone (From = gen_server From). +> %% @end +173d132 +< +175,177c134 +< squery(Pid, Query, From, []). +< fetch(Pid, Query, From, Timeout) -> +< squery(Pid, Query, From, [{timeout, Timeout}]). +--- +> fetch(Pid, Query, From, ?DEFAULT_STANDALONE_TIMEOUT). +179c136 +< squery(Pid, Query, From, Options) when is_pid(Pid), is_list(Query) -> +--- +> fetch(Pid, Query, From, Timeout) when is_pid(Pid), is_list(Query), is_integer(Timeout) -> +181,183c138 +< Timeout = get_option(timeout, Options, ?DEFAULT_STANDALONE_TIMEOUT), +< TRef = erlang:start_timer(Timeout, self(), timeout), +< Pid ! {fetch, TRef, Query, From, Options}, +--- +> Pid ! {fetch, Query, From}, +187c142,147 +< wait_fetch_result(TRef, Pid); +--- +> receive +> {fetch_result, Pid, Result} -> +> Result +> after Timeout -> +> {error, "query timed out"} +> end; +193,218d152 +< wait_fetch_result(TRef, Pid) -> +< receive +< {fetch_result, TRef, Pid, Result} -> +< case erlang:cancel_timer(TRef) of +< false -> +< receive +< {timeout, TRef, _} -> +< ok +< after 0 -> +< ok +< end; +< _ -> +< ok +< end, +< Result; +< {fetch_result, _BadRef, Pid, _Result} -> +< wait_fetch_result(TRef, Pid); +< {timeout, TRef, _} -> +< stop(Pid), +< {error, "query timed out"} +< end. +< +< stop(Pid) -> +< Pid ! close. +< +< +220,229c154,160 +< %% Function: do_recv(LogFun, RecvPid, SeqNum) +< %% LogFun = undefined | function() with arity 3 +< %% RecvPid = pid(), mysql_recv process +< %% SeqNum = undefined | integer() +< %% Descrip.: Wait for a frame decoded and sent to us by RecvPid. +< %% Either wait for a specific frame if SeqNum is an integer, +< %% or just any frame if SeqNum is undefined. +< %% Returns : {ok, Packet, Num} | +< %% {error, Reason} +< %% Reason = term() +--- +> %% @spec (LogFun, RecvPid, SeqNum) -> +> %% {ok, Packet, Num} | +> %% {error, Reason} +> %% +> %% LogFun = undefined | function() with arity 3 +> %% RecvPid = pid() "mysql_recv process" +> %% SeqNum = undefined | integer() +231c162,168 +< %% Note : Only to be used externally by the 'mysql_auth' module. +--- +> %% Reason = term() +> %% +> %% @doc Wait for a frame decoded and sent to us by RecvPid. Either +> %% wait for a specific frame if SeqNum is an integer, or +> %% just any frame if SeqNum is undefined. Note : Only to be +> %% used externally by the 'mysql_auth' module. +> %% @end +257,269c194,209 +< %% Function: init(Host, Port, User, Password, Database, LogFun, +< %% Parent) +< %% Host = string() +< %% Port = integer() +< %% User = string() +< %% Password = string() +< %% Database = string() +< %% LogFun = undefined | function() of arity 3 +< %% Parent = pid() of process starting this mysql_conn +< %% Descrip.: Connect to a MySQL server, log in and chooses a database. +< %% Report result of this to Parent, and then enter loop() if +< %% we were successfull. +< %% Returns : void() | does not return +--- +> %% @spec (Host, Port, User, Password, Database, LogFun, Parent) -> +> %% void() | does not return +> %% +> %% Host = string() +> %% Port = integer() +> %% User = string() +> %% Password = string() +> %% Database = string() +> %% LogFun = undefined | function() of arity 3 +> %% Parent = pid() of process starting this mysql_conn +> %% +> %% @doc Connect to a MySQL server, log in and chooses a database. +> %% Report result of this to Parent, and then enter loop() if +> %% we were successfull. +> %% @hidden +> %% @end +275,282c215,217 +< {ok, Version} -> +< case do_query(Sock, RecvPid, LogFun, "use " ++ Database, Version, [{result_type, binary}]) of +< {error, MySQLRes} -> +< mysql:log(LogFun, error, "mysql_conn: Failed changing to database ~p : ~p", +< [Database, mysql:get_result_reason(MySQLRes)]), +< Parent ! {mysql_conn, self(), {error, failed_changing_database}}; +< %% ResultType: data | updated +< {_ResultType, _MySQLRes} -> +--- +> ok -> +> case do_query(Sock, RecvPid, LogFun, "use " ++ Database) of +> {ok, _Fields, _Rows} -> +284,285c219 +< State = #state{mysql_version=Version, +< recv_pid = RecvPid, +--- +> State = #state{recv_pid = RecvPid, +290c224,228 +< loop(State) +--- +> loop(State); +> {error, Reason} -> +> mysql:log(LogFun, error, "mysql_conn: Failed changing to database ~p : ~p", +> [Database, Reason]), +> Parent ! {mysql_conn, self(), {error, failed_changing_database}} +302,306c240,246 +< %% Function: loop(State) +< %% State = state record() +< %% Descrip.: Wait for signals asking us to perform a MySQL query, or +< %% signals that the socket was closed. +< %% Returns : error | does not return +--- +> %% @spec (State) -> error | does not return +> %% +> %% State = #state{} +> %% +> %% @doc Wait for signals asking us to perform a MySQL query, or +> %% signals that the socket was closed. +> %% @end +311c251 +< {fetch, Ref, Query, GenSrvFrom, Options} -> +--- +> {fetch, Query, GenSrvFrom} -> +314c254 +< Res = do_query(State, Query, Options), +--- +> Res = do_query(State, Query), +318c258 +< GenSrvFrom ! {fetch_result, Ref, self(), Res}; +--- +> GenSrvFrom ! {fetch_result, self(), Res}; +320,323d259 +< %% the timer is canceled in wait_fetch_result/2, but we wait on that funtion only if the query +< %% was not sent using the mysql gen_server. So we at least should try to cancel the timer here +< %% (no warranty, the gen_server can still receive timeout messages) +< erlang:cancel_timer(Ref), +333,334d268 +< close -> +< close_connection(State); +336,337c270,271 +< mysql:log(State#state.log_fun, error, "mysql_conn: Received unknown signal, exiting : ~p", [Unknown]), +< close_connection(State), +--- +> mysql:log(State#state.log_fun, error, "mysql_conn: Received unknown signal, exiting"), +> mysql:log(State#state.log_fun, debug, "mysql_conn: Unknown signal : ~p", [Unknown]), +342,350c276,288 +< %% Function: mysql_init(Sock, RecvPid, User, Password, LogFun) +< %% Sock = term(), gen_tcp socket +< %% RecvPid = pid(), mysql_recv process +< %% User = string() +< %% Password = string() +< %% LogFun = undefined | function() with arity 3 +< %% Descrip.: Try to authenticate on our new socket. +< %% Returns : ok | {error, Reason} +< %% Reason = string() +--- +> %% @spec (Sock, RecvPid, User, Password, LogFun) -> +> %% ok | {error, Reason} +> %% +> %% Sock = term() "gen_tcp socket" +> %% RecvPid = pid() "mysql_recv process" +> %% User = string() +> %% Password = string() +> %% LogFun = undefined | function() with arity 3 +> %% +> %% Reason = string() +> %% +> %% @doc Try to authenticate on our new socket. +> %% @end +355c293 +< {Version, Salt1, Salt2, Caps} = greeting(Packet, LogFun), +--- +> {Salt1, Salt2, Caps} = greeting(Packet, LogFun), +365c303 +< {ok,Version}; +--- +> ok; +382c320 +< <> = Packet, +--- +> <<_Protocol:8, Rest/binary>> = Packet, +389,391c327,329 +< mysql:log(LogFun, debug, "mysql_conn: greeting version ~p (protocol ~p) salt ~p caps ~p serverchar ~p salt2 ~p", +< [Version, Protocol, Salt, Caps, ServerChar, Salt2]), +< {normalize_version(Version, LogFun), Salt, Salt2, Caps}. +--- +> mysql:log(LogFun, debug, "mysql_conn: greeting version ~p salt ~p caps ~p serverchar ~p salt2 ~p", +> [Version, Salt, Caps, ServerChar, Salt2]), +> {Salt, Salt2, Caps}. +394c332 +< asciz(Data) when is_binary(Data) -> +--- +> asciz(Data) when binary(Data) -> +396c334 +< asciz(Data) when is_list(Data) -> +--- +> asciz(Data) when list(Data) -> +403,414c341,353 +< %% Function: get_query_response(LogFun, RecvPid) +< %% LogFun = undefined | function() with arity 3 +< %% RecvPid = pid(), mysql_recv process +< %% Version = integer(), Representing MySQL version used +< %% Descrip.: Wait for frames until we have a complete query response. +< %% Returns : {data, #mysql_result} +< %% {updated, #mysql_result} +< %% {error, #mysql_result} +< %% FieldInfo = list() of term() +< %% Rows = list() of [string()] +< %% AffectedRows = int() +< %% Reason = term() +--- +> %% @spec (LogFun, RecvPid) -> +> %% {ok, FieldInfo, Rows} | +> %% {error, Reason} +> %% +> %% LogFun = undefined | function() with arity 3 +> %% RecvPid = pid() "mysql_recv process" +> %% +> %% FieldInfo = [term()] +> %% Rows = [[string()]] +> %% Reason = term() +> %% +> %% @doc Wait for frames until we have a complete query response. +> %% @end +416c355 +< get_query_response(LogFun, RecvPid, Version, Options) -> +--- +> get_query_response(LogFun, RecvPid) -> +421,423c360 +< %% No Tabular data +< <> = Rest, +< {updated, #mysql_result{affectedrows=AffectedRows}}; +--- +> {ok, [], []}; +426c363 +< {error, #mysql_result{error=binary_to_list(Message)}}; +--- +> {error, binary_to_list(Message)}; +428,429c365 +< %% Tabular data received +< case get_fields(LogFun, RecvPid, [], Version) of +--- +> case get_fields(LogFun, RecvPid, []) of +431,432c367 +< ResultType = get_option(result_type, Options, ?DEFAULT_RESULT_TYPE), +< case get_rows(Fieldcount, LogFun, RecvPid, ResultType, []) of +--- +> case get_rows(Fieldcount, LogFun, RecvPid, []) of +434c369 +< {data, #mysql_result{fieldinfo=Fields, rows=Rows}}; +--- +> {ok, Fields, Rows}; +436c371 +< {error, #mysql_result{error=Reason}} +--- +> {error, Reason} +439c374 +< {error, #mysql_result{error=Reason}} +--- +> {error, Reason} +443c378 +< {error, #mysql_result{error=Reason}} +--- +> {error, Reason} +447,455c382,393 +< %% Function: get_fields(LogFun, RecvPid, [], Version) +< %% LogFun = undefined | function() with arity 3 +< %% RecvPid = pid(), mysql_recv process +< %% Version = integer(), Representing MySQL version used +< %% Descrip.: Received and decode field information. +< %% Returns : {ok, FieldInfo} | +< %% {error, Reason} +< %% FieldInfo = list() of term() +< %% Reason = term() +--- +> %% @spec (LogFun, RecvPid, []) -> +> %% {ok, FieldInfo} | +> %% {error, Reason} +> %% +> %% LogFun = undefined | function() with arity 3 +> %% RecvPid = pid() "mysql_recv process" +> %% +> %% FieldInfo = [term()] +> %% Reason = term() +> %% +> %% @doc Received and decode field information. +> %% @end +457,458c395 +< %% Support for MySQL 4.0.x: +< get_fields(LogFun, RecvPid, Res, ?MYSQL_4_0) -> +--- +> get_fields(LogFun, RecvPid, Res) -> +477,479d413 +< %% TODO: Check on MySQL 4.0 if types are specified +< %% using the same 4.1 formalism and could +< %% be expanded to atoms: +481,514c415 +< get_fields(LogFun, RecvPid, [This | Res], ?MYSQL_4_0) +< end; +< {error, Reason} -> +< {error, Reason} +< end; +< %% Support for MySQL 4.1.x and 5.x: +< get_fields(LogFun, RecvPid, Res, ?MYSQL_4_1) -> +< case do_recv(LogFun, RecvPid, undefined) of +< {ok, Packet, _Num} -> +< case Packet of +< <<254:8>> -> +< {ok, lists:reverse(Res)}; +< <<254:8, Rest/binary>> when size(Rest) < 8 -> +< {ok, lists:reverse(Res)}; +< _ -> +< {_Catalog, Rest} = get_with_length(Packet), +< {_Database, Rest2} = get_with_length(Rest), +< {Table, Rest3} = get_with_length(Rest2), +< %% OrgTable is the real table name if Table is an alias +< {_OrgTable, Rest4} = get_with_length(Rest3), +< {Field, Rest5} = get_with_length(Rest4), +< %% OrgField is the real field name if Field is an alias +< {_OrgField, Rest6} = get_with_length(Rest5), +< +< <<_Metadata:8/little, _Charset:16/little, +< Length:32/little, Type:8/little, +< _Flags:16/little, _Decimals:8/little, +< _Rest7/binary>> = Rest6, +< +< This = {binary_to_list(Table), +< binary_to_list(Field), +< Length, +< get_field_datatype(Type)}, +< get_fields(LogFun, RecvPid, [This | Res], ?MYSQL_4_1) +--- +> get_fields(LogFun, RecvPid, [This | Res]) +521,528c422,433 +< %% Function: get_rows(N, LogFun, RecvPid, []) +< %% N = integer(), number of rows to get +< %% LogFun = undefined | function() with arity 3 +< %% RecvPid = pid(), mysql_recv process +< %% Descrip.: Receive and decode a number of rows. +< %% Returns : {ok, Rows} | +< %% {error, Reason} +< %% Rows = list() of [string()] +--- +> %% @spec (N, LogFun, RecvPid, []) -> +> %% {ok, Rows} | +> %% {error, Reason} +> %% +> %% N = integer() "number of rows to get" +> %% LogFun = undefined | function() with arity 3 +> %% RecvPid = pid() "mysql_recv process" +> %% +> %% Rows = [[string()]] +> %% +> %% @doc Receive and decode a number of rows. +> %% @end +530c435 +< get_rows(N, LogFun, RecvPid, ResultType, Res) -> +--- +> get_rows(N, LogFun, RecvPid, Res) -> +537,538c442,443 +< {ok, This} = get_row(N, Packet, ResultType, []), +< get_rows(N, LogFun, RecvPid, ResultType, [This | Res]) +--- +> {ok, This} = get_row(N, Packet, []), +> get_rows(N, LogFun, RecvPid, [This | Res]) +544d448 +< +546c450 +< get_row(0, _Data, _ResultType, Res) -> +--- +> get_row(0, _Data, Res) -> +548c452 +< get_row(N, Data, ResultType, Res) -> +--- +> get_row(N, Data, Res) -> +554,559c458 +< if +< ResultType == list -> +< binary_to_list(Col); +< ResultType == binary -> +< Col +< end +--- +> binary_to_list(Col) +561c460,461 +< get_row(N - 1, Rest, ResultType, [This | Res]). +--- +> get_row(N - 1, Rest, [This | Res]). +> +574,579d473 +< close_connection(State) -> +< Result = gen_tcp:close(State#state.socket), +< mysql:log(State#state.log_fun, normal, "Closing connection ~p: ~p~n", [State#state.socket, Result]), +< Result. +< +< +581,588c475,484 +< %% Function: do_query(State, Query) +< %% do_query(Sock, RecvPid, LogFun, Query) +< %% Sock = term(), gen_tcp socket +< %% RecvPid = pid(), mysql_recv process +< %% LogFun = undefined | function() with arity 3 +< %% Query = string() +< %% Descrip.: Send a MySQL query and block awaiting it's response. +< %% Returns : result of get_query_response/2 | {error, Reason} +--- +> %% @spec (State, Query) do_query(Sock, RecvPid, LogFun, Query) -> +> %% result of get_query_response/2 | {error, Reason} +> %% +> %% Sock = term() "gen_tcp socket" +> %% RecvPid = pid() "mysql_recv process" +> %% LogFun = undefined | function() with arity 3 +> %% Query = string() +> %% +> %% @doc Send a MySQL query and block awaiting it's response. +> %% @end +590c486 +< do_query(State, Query, Options) when is_record(State, state) -> +--- +> do_query(State, Query) when is_record(State, state) -> +594,596c490 +< Query, +< State#state.mysql_version, +< Options +--- +> Query +599,600c493 +< do_query(Sock, RecvPid, LogFun, Query, Version, Options) when is_pid(RecvPid), +< is_list(Query) -> +--- +> do_query(Sock, RecvPid, LogFun, Query) when is_pid(RecvPid), is_list(Query) -> +604c497 +< get_query_response(LogFun, RecvPid, Version, Options); +--- +> get_query_response(LogFun, RecvPid); +611,617c504,512 +< %% Function: do_send(Sock, Packet, SeqNum, LogFun) +< %% Sock = term(), gen_tcp socket +< %% Packet = binary() +< %% SeqNum = integer(), packet sequence number +< %% LogFun = undefined | function() with arity 3 +< %% Descrip.: Send a packet to the MySQL server. +< %% Returns : result of gen_tcp:send/2 +--- +> %% @spec (Sock, Packet, SeqNum, LogFun) -> result of gen_tcp:send/2 +> %% +> %% Sock = term() "gen_tcp socket" +> %% Packet = binary() +> %% SeqNum = integer() "packet sequence number" +> %% LogFun = undefined | function() with arity 3 +> %% +> %% @doc Send a packet to the MySQL server. +> %% @end +623,693d517 +< +< %%-------------------------------------------------------------------- +< %% Function: normalize_version(Version, LogFun) +< %% Version = string() +< %% LogFun = undefined | function() with arity 3 +< %% Descrip.: Return a flag corresponding to the MySQL version used. +< %% The protocol used depends on this flag. +< %% Returns : Version = string() +< %%-------------------------------------------------------------------- +< normalize_version([$4,$.,$0|_T], LogFun) -> +< mysql:log(LogFun, debug, "Switching to MySQL 4.0.x protocol.~n"), +< ?MYSQL_4_0; +< normalize_version([$4,$.,$1|_T], _LogFun) -> +< ?MYSQL_4_1; +< normalize_version([$5|_T], _LogFun) -> +< %% MySQL version 5.x protocol is compliant with MySQL 4.1.x: +< ?MYSQL_4_1; +< normalize_version(_Other, LogFun) -> +< mysql:log(LogFun, error, "MySQL version not supported: MySQL Erlang module might not work correctly.~n"), +< %% Error, but trying the oldest protocol anyway: +< ?MYSQL_4_0. +< +< %%-------------------------------------------------------------------- +< %% Function: get_field_datatype(DataType) +< %% DataType = integer(), MySQL datatype +< %% Descrip.: Return MySQL field datatype as description string +< %% Returns : String, MySQL datatype +< %%-------------------------------------------------------------------- +< get_field_datatype(0) -> 'DECIMAL'; +< get_field_datatype(1) -> 'TINY'; +< get_field_datatype(2) -> 'SHORT'; +< get_field_datatype(3) -> 'LONG'; +< get_field_datatype(4) -> 'FLOAT'; +< get_field_datatype(5) -> 'DOUBLE'; +< get_field_datatype(6) -> 'NULL'; +< get_field_datatype(7) -> 'TIMESTAMP'; +< get_field_datatype(8) -> 'LONGLONG'; +< get_field_datatype(9) -> 'INT24'; +< get_field_datatype(10) -> 'DATE'; +< get_field_datatype(11) -> 'TIME'; +< get_field_datatype(12) -> 'DATETIME'; +< get_field_datatype(13) -> 'YEAR'; +< get_field_datatype(14) -> 'NEWDATE'; +< get_field_datatype(16) -> 'BIT'; +< get_field_datatype(246) -> 'DECIMAL'; +< get_field_datatype(247) -> 'ENUM'; +< get_field_datatype(248) -> 'SET'; +< get_field_datatype(249) -> 'TINYBLOB'; +< get_field_datatype(250) -> 'MEDIUM_BLOG'; +< get_field_datatype(251) -> 'LONG_BLOG'; +< get_field_datatype(252) -> 'BLOB'; +< get_field_datatype(253) -> 'VAR_STRING'; +< get_field_datatype(254) -> 'STRING'; +< get_field_datatype(255) -> 'GEOMETRY'. +< +< %%-------------------------------------------------------------------- +< %% Function: get_option(Key1, Options, Default) -> Value1 +< %% Options = [Option] +< %% Option = {Key2, Value2} +< %% Key1 = Key2 = atom() +< %% Value1 = Value2 = Default = term() +< %% Descrip.: Return the option associated with Key passed to squery/4 +< %%-------------------------------------------------------------------- +< +< get_option(Key, Options, Default) -> +< case lists:keysearch(Key, 1, Options) of +< {value, {_, Value}} -> +< Value; +< false -> +< Default +< end. +diff ejabberd-modules/mysql/trunk/mysql_recv.erl yxa-1.0/src/mysql/mysql_recv.erl +3,4c3,4 +< %%% Author : Fredrik Thulin +< %%% Descrip.: Handles data being received on a MySQL socket. Decodes +--- +> %%% @author Fredrik Thulin +> %%% @doc Handles data being received on a MySQL socket. Decodes +7c7,8 +< %%% Created : 4 Aug 2005 by Fredrik Thulin +--- +> %%% @since 4 Aug 2005 by Fredrik Thulin +> %%% @end +36a38,39 +> %% @type state() = #state{}. +> %% no description +52,64c55,71 +< %% Function: start_link(Host, Port, LogFun, Parent) +< %% Host = string() +< %% Port = integer() +< %% LogFun = undefined | function() of arity 3 +< %% Parent = pid(), process that should get received frames +< %% Descrip.: Start a process that connects to Host:Port and waits for +< %% data. When it has received a MySQL frame, it sends it to +< %% Parent and waits for the next frame. +< %% Returns : {ok, RecvPid, Socket} | +< %% {error, Reason} +< %% RecvPid = pid(), receiver process pid +< %% Socket = term(), gen_tcp socket +< %% Reason = atom() | string() +--- +> %% @spec (Host, Port, LogFun, Parent) -> +> %% {ok, RecvPid, Socket} | +> %% {error, Reason} +> %% +> %% Host = string() +> %% Port = integer() +> %% LogFun = undefined | function() of arity 3 +> %% Parent = pid() "process that should get received frames" +> %% +> %% RecvPid = pid() "receiver process pid" +> %% Socket = term() "gen_tcp socket" +> %% Reason = atom() | string() +> %% +> %% @doc Start a process that connects to Host:Port and waits for +> %% data. When it has received a MySQL frame, it sends it to +> %% Parent and waits for the next frame. +> %% @end +89,95c96,105 +< %% Function: init((Host, Port, LogFun, Parent) +< %% Host = string() +< %% Port = integer() +< %% LogFun = undefined | function() of arity 3 +< %% Parent = pid(), process that should get received frames +< %% Descrip.: Connect to Host:Port and then enter receive-loop. +< %% Returns : error | never returns +--- +> %% @spec ((Host, Port, LogFun, Parent) -> error | never returns +> %% +> %% Host = string() +> %% Port = integer() +> %% LogFun = undefined | function() of arity 3 +> %% Parent = pid() "process that should get received frames" +> %% +> %% @doc Connect to Host:Port and then enter receive-loop. +> %% @hidden +> %% @end +115,119c125,131 +< %% Function: loop(State) +< %% State = state record() +< %% Descrip.: The main loop. Wait for data from our TCP socket and act +< %% on received data or signals that our socket was closed. +< %% Returns : error | never returns +--- +> %% @spec (State) -> error | never returns +> %% +> %% State = #state{} +> %% +> %% @doc The main loop. Wait for data from our TCP socket and act +> %% on received data or signals that our socket was closed. +> %% @end +140,145c152,162 +< %% Function: sendpacket(Parent, Data) +< %% Parent = pid() +< %% Data = binary() +< %% Descrip.: Check if we have received one or more complete frames by +< %% now, and if so - send them to Parent. +< %% Returns : Rest = binary() +--- +> %% @spec (Parent, Data) -> +> %% Rest +> %% +> %% Parent = pid() +> %% Data = binary() +> %% +> %% Rest = binary() +> %% +> %% @doc Check if we have received one or more complete frames by +> %% now, and if so - send them to Parent. +> %% @end +Only in yxa-1.0/src/mysql/: overview.edoc +cube:~ hd$ diff --git a/edoc-info b/edoc-info new file mode 100644 index 00000000..cf1f0486 --- /dev/null +++ b/edoc-info @@ -0,0 +1,5 @@ +{application,emysql}. +{packages,[]}. +{modules,[emysql,emysql_app,emysql_auth,emysql_conn,emysql_conn_mgr, + emysql_statements,emysql_sup,emysql_tcp,emysql_tracer,emysql_util, + emysql_worker]}. diff --git a/emysql-footer.png b/emysql-footer.png new file mode 100644 index 0000000000000000000000000000000000000000..27414c13e98703d1d74defe18317aa9ac60c5d97 GIT binary patch literal 4335 zcmXX}c|6nqA6I=#LMnt%By)unmZOw=LZaL;yxqjRF{qy;J9*@`S^L{^Huh;W+#aUaL@E<#KjEjqlA7pB1%f)q+%KElB zdWiK@2M?~WUSe>gJ8(Pyhj7FLm>1VgPk(nWDUja-Z!cS~2cDsUWG`KolBo49dm~1! z>Gha0;x)rQUt>5j{bS0D&P?xo;hPU0-vDxPo$v!0-n4%_w3rbGo46{(rLhc}*x!HL ztvq;{dGu{5t>OykwVEeNaf{fy!@mA%BG4Erra`i>DhIf4d#5?K2(5=mHhuqxB~$s~ z+@3=Hr^;q&2_TVg34aFxzF-6Y&!kLe3J?vx75|<2q%BPv9<(JMx9Ra#oF!w*7k`)S zq66rkQ8iN(WJdVwJwx|n_yb&A&wODrR#SjKiSa(+)DkKw@AFRbc@|=>6+mH7f2jAi zN2HC;iS6W3egqi`&)>sP>ahOz>UEU9G~oe?_N`up}eT z{f2s4pd``PPCroA5jV>+M2;7bc@+N&Z{!tsujiWU6w!?x%oglc|20yo9`*U%zj*bp z9Sd=0=dtw6kE5?)pXyRy9WP5iy&6hgEfUG4NwJZY@`@;VxjO`yTMfp3JYTU++A@G< zwj=+dhCMnfUHJS1qI{1`y`~)E;k=(5@klkSZ1xDp+IxL*n&)jBFYnFWdN6lhb9)L< z8!r!IyId7;`!IJ}zLE+Maj(4*O6@_Ntxj-JuDJB??_!1an$ z$}UW>0~Lc4Vu@=d98V(zU>*RC=E;}-_by-;Gvmh@kx}fh)Sg>FA{ZvOxA`M~Zikee zbNNb`I)QX5wz(9E!x+s@=TqA{)7e3KHhT8psvtr>fj5%joG2}ho{%cfOC)P(?v5;b z%}$q9<0t}bq$?&zUr2Ra9u^gjV?@lvcKw3=CduGW=4X3k zrlHo=S z+~dsS*KCaJHZ6*~quOC4C?~|8@%Lxed7x@AVQ34XvIH_kq`EEUy`} z$rh>Ft@2s%<#hQ(Ro|)D?udkROvL2QqWdMcqr+`DJ&161QgWN(l5JS2G$)0bbY|^~ zhJiL@yw*rc#9B^A>DZ8RMbL+=g=0|Tn@QaK)=fwC$mKz}VR4R2mY_0$b@n4~i}FKc z<(mqcG>L98QW?X#s#|4P{>g$RE3I`L?LY+#6AOSxc&c#%74j%h-1*2KFgP!AV>+g` z>C3Tp=3o!BF})+D7*=rjnBU5M@T>I1>_BjX;gG!R%WaAIt?N<->^kuu_k`x$@#Qte zm6{>=&pOp$%ylu1?M@Xd=ucAdhuo;)WW;qzw|BXM{T(s>&Zn(*=9+~#cZyY=6vPS* z{BHIu=@V7??L_MQvG}!O*mH6&K28H1pI)ouzPJUAubX7JpQmxjv8^0(DV3v)M5hVdP=kZQTXYz^{5hmAB05 zC=d>p1dO2AyO8IE{I{!;|C=q&0n)? z(GEYg@T`}5)&RMy9ts~_yrPpSMkhJQ&THpl&%|mRWeE2>W-efCdXKR-i2D6ug1Bw@>TIhosTzZH@Q=34|xD2?6hxrB#3M@TY0aOFZIs^JSK6Db(^@pOUz{~#!+8; zBO!wPq<#ewkOo%xf>^@GMGKsAwEBSlONc!ph54Z3dHwmT&I^}K=3P6zqyQ?-^XGNS z#mwfigLw?IB>VvOK!{dnUlI-W1)`x>s+4l?yl#OIbd{V}=X zWneumXk;W5DQYn*C+z*x=IhBU1t{Z^T#j6yNh`RGO^s*%SmCj-pko+C|C&P}pC`T= zYfVapZgTAgDCwF=cs(uoTh5RB$%gu!Kh;S6e(T9KJJXFk#MhJW(tX3EfDg@~L%NgP zGQMvH&Y1JKOS9VgO8vi7_Z;+>XtAI`uby42P-}C_jaKG!l3MT(b{UI&d&>PvHb(1* zaDz{6)Iw3XgT=P$<4J87YFc5;8-#GqRCCQnvtBSlJO z&U8ogSKG*R)Tyl07I{PH=NS1O^rW(OhefDyig@Zd^8u7|VH5w%Vq$IcYs=yvXkF`N zO3IO%`XhsKJnD%$RkCVALWl<#n^!1BG7|?;54#5)637@0XZ0i~9@KE!!X{Oy7{4!h zd}`b0cxAM>Ke07^2N&?ABncB>YP(dp?bu^WdLKgH?r5%G;3u>pkqH+V7eztiXBmY& z;Y;EQyfUl1aqVl^y*A(4tqM3+DR=O{G{P0TV8@xw-6eR!*2IJQVo z&&7ngCt1krq?y1w^Cy|t%$X{rn+Y3JgmQF#wkMQT9D zXEf29wNziR4ca$_!mCrwh$7gb(aJV$B@_PfzSnUJy|yYN3_ld61@%c{`Vt6AvZa?= zC>(x@b1sRv1-*MS@F>q*_KOKm-=UfI^aHpz3d4HRo@49Let9E=m4wP8fg*?WQNBU; z!&_sv3ALFQEAwIRc=Om5nECL@QFOE!A7(j1&FFJ}O7l#3Ay#!_HRM8;m z@J4hLWF9S-n(qxJQ<8&vb#&J1Tzn2EwzQSq(B4bB4x41$$fB&$0K>`ON_>-D%lo?Q z*W7!!!`7YGf-y{)hPe&4qWWn?OK2O4GMA^lUy_9uj9mjL&weX* z9C`0~<=Y>n-UCakbe`Z>St*w~M_d(BI~6yg(}a}MtY&?aFj|B3M2kg!zp?`Ekz}#hJFLE!^1#<$CY#R1h|7p!; zPjCawGc@Wu(llqvNEV?g>_Ko&%u|S1M_GuwcDs)`K>(%g#SRR2Lr*PZUk zEqX;f)h9`OX$n9ubiabmw-^i(=>8NZBQ|B+nxJX-rDecdDV)6JswnkdLuUq}7HQ%Ox4QPj4dVP3Ri z4_1+W=I^c6Y?X7oJHh!Hvc2JDPR0xZCi98v0v8`Vw!89`rNY!Jy;Cn0QGjaBIST9a zi#KQ^26^lgR2BlKY)QTwYn*NAiNhmoLQ7|N+$kJ)A|AO}vc2PZ@AQQN{2iOx$c>9u zI{-E&9>V)LV8UphES;u^T|H<*cOPKj48fjGRu@HNfWZUc%t_9&^5kTBbwx+Y{A>|# zuW9b}gtUlq)exC&Lr+tEbi7!QhH6fMD$|H{B2y7R$Gr!Q2|R*N`($Cyy0O|AxzX<^?i@U!*J(2s+j)BW9p7B0_4;L!yGHU!khS+W{Khj+w`={n~K?~V{Ee^t;|p)nw6Yx z8Zsa+6b07gAz$r9GP0BiX%an>S7K{+&=*T0izHnbvV2Yo?2RdIWP1Gl-E;5sMbBt| zoEGPSCOX#+kZ}GY$?s#m;r@*5hmr%BNNcIT+EEt=6ohr&j_5Zh!zN0lgAAaXn~21{ z?8GJG(<->&d)L&ZyeIEo?#L74ICAfJ*NOh;sF>Y8uDE{8UML=X@S}jAVb-5`b!UQ5 lbvGR$O>12ldwP(M%gH0uzZ;72WVwk8WMpYrtbhN-{{Sm7QZWDk literal 0 HcmV?d00001 diff --git a/emysql-style.css b/emysql-style.css new file mode 100644 index 00000000..a44a7e19 --- /dev/null +++ b/emysql-style.css @@ -0,0 +1,180 @@ +/* standard EDoc style sheet */ +body { + font-family: Verdana, Arial, Helvetica, sans-serif; + font-size: .9em; + margin-left: .25in; + margin-right: .2in; + margin-top: 0.2in; + margin-bottom: 0.2in; + color: #000000; + background-color: #ffffff; +} +h1 { + margin-left: 0; +} +h2 { + margin-left: -.3em; +} + +h2.indextitle { + + font-size: 1em; +} + +h2.indextitle table { + border-collapse: separate; + border-bottom-right-radius: 1em; + border: 1px solid #3070a0; + padding: 1em; + + font-size: 0.9em; +} + +h2.indextitle table tr td { + background-color: #deeefe; + border: 0; + border-bottom: 1px solid white; +} + + + + +div.navbar { + color: white; + font-weight: bold; + text-size: 0.9em; + background-color: #3070a0; + padding: 0.2em; + border-bottom: 1em solid #f09840; + border-top-left-radius: 1em; +} +h2.indextitle { + color: white; + padding: 0.4em; + background-color: #3070a0; + border-bottom: 10px solid #f09840; + border-top-left-radius: 1em; +} + +h3.function,h3.typedecl { + color: white; + background-color: #3070a0; + padding-left: 1em; + border-bottom: 5px solid #f09840; + border-top-left-radius: 1em; +} + +div.spec { + font-weight: bold; + margin-left: 0em; + padding: .2em 1.1em .2em; + background-color: #eeeeee; + border-bottom-right-radius: 1em; + +} +a { color: inherit; padding: 2px; text-decoration: none; } + +a.module,a.package { + text-decoration:none +} +a:hover { + background-color: #deeefe; + color: navy; +} + +a.module:hover,a.package:hover { + background-color: #deeefe; +} +ul.definitions { + list-style-type: none; +} + +ul.index { + list-style-type: none; + border-collapse: separate; + border-bottom-right-radius: 1em; + border: 1px solid #3070a0; + padding: 1em; + + font-size: 1.1em; +} + +ul.index li { + background-color: #feeede; + border: 0; + border-bottom: 1px solid white; + padding: 2px; +} + +ul { + list-style-type: square; +} +table { + border-collapse: collapse; +} + +table.index { + border-collapse: separate; + border-bottom-right-radius: 1em; + border: 1px solid #3070a0; + padding: 1em; + + font-size: 0.9em; +} + +table.index td { + background-color: #deeefe; + border: 0; + border-bottom: 1px solid white; +} + + +table.index td a:hover { + background-color: #feeede; +} + +td { + padding: 2px; +} + +code { + font-size: 1.1em; +} + +div.navbar { + background:url("emysql-footer.png") no-repeat right top; +} + + +div.navbar a { + color: lightblue; + font-size: 0.9em; + font-weight: bold; + margin-left:1em; + line-height: 1em; +} + +img { + margin-right: 10px; + + } + +a { + color: navy; +} + +h1 a, h2 a, h3 a, h4 a, h5 a { + color: black; + } + +h3.function a { + color: white; + } + +pre { margin: 1em; + padding: 1em; + background-color: #f0f0f0; + border-radius: 1em; } +span.comment { + color: dimgrey; + } \ No newline at end of file diff --git a/emysql.html b/emysql.html new file mode 100644 index 00000000..2f3fe495 --- /dev/null +++ b/emysql.html @@ -0,0 +1,339 @@ + + + +Module emysql + + + + +
    + +

    Module emysql

    +The main Emysql module. + + +

    Description

    The main Emysql module.

    + +

    Emysql is implemented as an Erlang + application. The term has a special meaning in Erlang, see + http://www.erlang.org/doc/design_principles/applications.html

    + +

    This module exports functions to: +

  • start and stop the driver (the 'application'),
  • +
  • execute queries or prepared statements,
  • +
  • prepare such statements,
  • +
  • change the connection pool size.
  • + +

    Sample

    +
      	-module(sample).
    + 	-export([run/0]).
    + 	
    + 	run() ->
    + 	
    + 		crypto:start(),
    + 		emysql:start(),
    + 	
    + 		emysql:add_pool(hello_pool, 1,
    + 			"hello_username", "hello_password", "localhost", 3306,
    + 			"hello_database", utf8),
    + 	
    + 		emysql:execute(hello_pool,
    + 			<<"INSERT INTO hello_table SET hello_text = 'Hello World!'">>),
    + 
    + 		emysql:prepare(my_stmt, <<"SELECT * from mytable WHERE id = ?">>),
    + 
    + 		Result = emysql:execute(mypoolname, my_stmt, [1]).
    + +

    Implementation

    + +

    Under this headline, Implementation, you will find details about the +inner workings of Emysql. If you are new to Emysql, you can safely ignore +them.

    + + start(), stop(), modules() and default_timeout() are one-line 'fascades': +
      	start() -> application:start(emysql).
    +  	stop() -> application:stop(emysql).
    +  	modules() -> emysql_app:modules().
    +  	default_timeout() -> emysql_app:default_timeout().
    + + execute() and prepare() are the bulk of the source + of this module. A lot gets repeated for default values in lesser arities. + The quintessential execute however is this, in execute/2: +
      	execute(PoolId, Query, Args, Timeout)
    + 		when is_atom(PoolId) andalso (is_list(Query) orelse is_binary(Query)) andalso is_list(Args) andalso is_integer(Timeout) ->
    + 		
    + 			Connection =
    + 				emysql_conn_mgr:wait_for_connection(PoolId),
    + 				monitor_work(Connection, Timeout, {emysql_conn, execute, [Connection, Query, Args]});

    +As all executions, it uses the monitor_work/3 function to create a process to +asynchronously handle the execution.

    + + The pool-related functions execute brief operations using the primitive + functions exported by emysql_conn_mgr and emysql_conn_mgr. +

    Function Index

    + + + + + + + + + + + + +
    add_pool/8
    decrement_pool_size/2
    default_timeout/0Returns the default timeout in milliseconds.
    execute/2Execute a query, prepared statement or a stored procedure.
    execute/3Execute a query, prepared statement or a stored procedure.
    execute/4Execute a query, prepared statement or a stored procedure.
    execute/5Execute a query, prepared statement or a stored procedure - but return immediately, returning the atom 'unavailable', when no connection in the pool is readily available without wait.
    increment_pool_size/2
    prepare/2Prepare a statement.
    remove_pool/1
    start/0Start the Emysql application.
    stop/0Stop the Emysql application.
    + +

    Function Details

    + +

    add_pool/8

    +
    +

    add_pool(PoolId, Size, User, Password, Host, Port, Database, Encoding) -> any()

    +
    + +

    decrement_pool_size/2

    +
    +

    decrement_pool_size(PoolId, Num) -> any()

    +
    + +

    default_timeout/0

    +
    +

    default_timeout() -> Timeout +

    • Timeout = integer()
    • +

    +

    Returns the default timeout in milliseconds. As set in emysql.app.src, +or if not set, the value ?TIMEOUT as defined in include/emysql.hrl (8000ms).

    + +

    Implementation

    + + src/emysql.app.src is a template for the emysql app file from which + ebin/emysql.app is created during building, by a sed command in 'Makefile'. +

    + +

    execute/2

    +
    +

    execute(PoolId, Query::Query | StmtName) -> Result | [Result] +

    +

    Execute a query, prepared statement or a stored procedure.

    + +

    Same as execute(PoolId, Query, [], default_timeout()).

    + + The result is a list for stored procedure execution >= MySQL 4.1 +

    +

    See also: execute/3, execute/4, execute/5, prepare/2.

    + +

    execute/3

    +
    +

    execute(PoolId, Query::Query | StmtName, Args::Args | Timeout) -> Result | [Result] +

    +

    Execute a query, prepared statement or a stored procedure.

    + +

    Same as execute(PoolId, Query, Args, default_timeout()) + or execute(PoolId, Query, [], Timeout).

    + +

    Timeout is the query timeout in milliseconds.

    + + The result is a list for stored procedure execution >= MySQL 4.1 +

    +

    See also: execute/2, execute/4, execute/5, prepare/2.

    + +

    execute/4

    +
    +

    execute(PoolId, Query::Query | StmtName, Args, Timeout) -> Result | [Result] +

    +

    Execute a query, prepared statement or a stored procedure.

    + +

    +

  • Opens a connection,
  • +
  • sends the query string, or statement atom, and
  • +
  • returns the result packet.
  • +

    + + Basically: +
      Connection = emysql_conn_mgr:wait_for_connection(PoolId),
    +  monitor_work(Connection, Timeout, {emysql_conn, execute, [Connection, Query_or_StmtName, Args]}).

    +Timeout is the query timeout in milliseconds.

    + + All other execute function eventually call this function. +

    +

    See also: execute/2, execute/3, execute/5, prepare/2.

    + +

    execute/5

    +
    +

    execute(PoolId, Query::Query | StmtName, Args, Timeout, X5::nonblocking) -> Result | [Result] +

    +

    Execute a query, prepared statement or a stored procedure - but return immediately, returning the atom 'unavailable', when no connection in the pool is readily available without wait.

    + +

    +

  • Checks if a connection is available,
  • +
  • returns 'unavailable' if not,
  • +
  • else as the other exception functions(): sends the query string, or statement atom, and
  • +
  • returns the result packet.
  • +

    + +

    Timeout is the query timeout in milliseconds.

    + +
    Implementation
    + + Basically: +
      {Connection, connection} = case emysql_conn_mgr:lock_connection(PoolId),
    +  		monitor_work(Connection, Timeout, {emysql_conn, execute, [Connection, Query_or_StmtName, Args]}).
    + +

    The result is a list for stored procedure execution >= MySQL 4.1

    + + All other execute function eventually call this function. +

    +

    See also: execute/2, execute/3, execute/4, prepare/2.

    + +

    increment_pool_size/2

    +
    +

    increment_pool_size(PoolId, Num) -> any()

    +
    + +

    prepare/2

    +
    +

    prepare(StmtName, Statement) -> ok +

    • StmtName = atom()
    • +
    • Statement = binary() | string()
    • +

    +

    Prepare a statement.

    + +

    The atom given by parameter 'StmtName' is bound to the SQL string + 'Statement'. Calling execute(<Pool>, StmtName, <ParamList>) executes the + statement with parameters from <ParamList>.

    + +

    This is not a mySQL prepared statement, but an implementation on the side of +Emysql.

    + +

    Sample

    +
      -module(sample).
    +  -export([run/0]).
    + 
    +  run() ->
    + 
    +  	application:start(sasl),
    +  	crypto:start(),
    +  	application:start(emysql),
    + 
    +  	emysql:add_pool(hello_pool, 1,
    +  		"hello_username", "hello_password", "localhost", 3306,
    +  		"hello_database", utf8),
    + 
    +  	emysql:execute(hello_pool,
    +  		<<"INSERT INTO hello_table SET hello_text = 'Hello World!'">>),
    + 
    +  	emysql:prepare(hello_stmt,
    +  		<<"SELECT * from hello_table WHERE hello_text like ?">>),
    + 
    +  	Result = emysql:execute(hello_pool, hello_stmt, ["Hello%"]),
    + 
    +  	io:format("~n~s~n", [string:chars($-,72)]),
    +  	io:format("~p~n", [Result]),
    + 
    +      ok.
    + Output: +
      {result_packet,32,
    +                [{field,2,<<"def">>,<<"hello_database">>,
    +                         <<"hello_table">>,<<"hello_table">>,
    +                         <<"hello_text">>,<<"hello_text">>,254,<<>>,33,
    +                         60,0,0}],
    +                 [[<<"Hello World!">>]],
    +                 <<>>}
    +

    Implementation

    + +

    Hands parameters over to emysql_statements:add/2: + emysql_statements:add(StmtName, Statement)., which calls + handle_call({add, StmtName, Statement}, _From, State).

    + + The statement is there added to the Emysql statement GB tree: + ...
     				State#state{
    + 					statements = gb_trees:enter(StmtName, {1, Statement},
    + 						State#state.statements)
    + Execution is called like this: +
      execute(Connection, StmtName, Args) when is_atom(StmtName), is_list(Args) ->
    +  	prepare_statement(Connection, StmtName),
    +  	case set_params(Connection, 1, Args, undefined) of
    +  		OK when is_record(OK, ok_packet) ->
    +  			ParamNamesBin = list_to_binary(string:join([[$@ | integer_to_list(I)] || I <- lists:seq(1, length(Args))], ", ")),
    +  			StmtNameBin = atom_to_binary(StmtName, utf8),
    +  			Packet = <<?COM_QUERY, "EXECUTE ", StmtNameBin/binary, " USING ", ParamNamesBin/binary>>,
    +  			emysql_tcp:send_and_recv_packet(Connection#connection.socket, Packet, 0);
    +  		Error ->
    +  			Error
    +  	end.
    +

    +

    See also: emysql_conn:execute/3, emysql_statements:add/2, emysql_statements:handle/3.

    + +

    remove_pool/1

    +
    +

    remove_pool(PoolId) -> any()

    +
    + +

    start/0

    +
    +

    start() -> ok

    +

    Start the Emysql application.

    + +

    Simply calls application:start(emysql).

    + +

    From the Erlang Manual

    +If the application is not already loaded, the application controller will +first load it using application:load/1. It will check the value of the +applications key, to ensure that all applications that should be started +before this application are running. The application controller then +creates an application master for the application. The application master +is the group leader of all the processes in the application. The +application master starts the application by calling the application +callback function start/2 in the module, and with the start argument, +defined by the mod key in the .app file.

    + +

    application:start(Application) is the same as calling +application:start(Application, temporary). If a temporary application +terminates, this is reported but no other applications are terminated.

    + + See http://www.erlang.org/doc/design_principles/applications.html +

    + +

    stop/0

    +
    +

    stop() -> ok

    +

    Stop the Emysql application.

    + +

    Simply calls application:stop(emysql).

    + +

    From the Erlang Manual

    +It is always possible to stop an application explicitly by calling +application:stop/1. Regardless of the mode, no other applications will be +affected.

    + + See http://www.erlang.org/doc/design_principles/applications.html +

    +
    + + +

    Generated by EDoc, Feb 10 2011, 07:38:23.

    + + diff --git a/emysql_app.html b/emysql_app.html new file mode 100644 index 00000000..89b2b708 --- /dev/null +++ b/emysql_app.html @@ -0,0 +1,61 @@ + + + +Module emysql_app + + + + +
    + +

    Module emysql_app

    + + +

    Behaviours: application.

    + +

    Function Index

    + + + + + + +
    default_timeout/0
    lock_timeout/0
    modules/0
    pools/0
    start/2
    stop/1
    + +

    Function Details

    + +

    default_timeout/0

    +
    +

    default_timeout() -> any()

    +
    + +

    lock_timeout/0

    +
    +

    lock_timeout() -> any()

    +
    + +

    modules/0

    +
    +

    modules() -> any()

    +
    + +

    pools/0

    +
    +

    pools() -> any()

    +
    + +

    start/2

    +
    +

    start(Type, StartArgs) -> any()

    +
    + +

    stop/1

    +
    +

    stop(State) -> any()

    +
    +
    + + +

    Generated by EDoc, Feb 10 2011, 07:38:23.

    + + diff --git a/emysql_auth.html b/emysql_auth.html new file mode 100644 index 00000000..76d2195c --- /dev/null +++ b/emysql_auth.html @@ -0,0 +1,30 @@ + + + +Module emysql_auth + + + + +
    + +

    Module emysql_auth

    + + + +

    Function Index

    + +
    do_handshake/3
    + +

    Function Details

    + +

    do_handshake/3

    +
    +

    do_handshake(Sock, User, Password) -> any()

    +
    +
    + + +

    Generated by EDoc, Feb 10 2011, 07:38:23.

    + + diff --git a/emysql_conn.html b/emysql_conn.html new file mode 100644 index 00000000..3267af2f --- /dev/null +++ b/emysql_conn.html @@ -0,0 +1,84 @@ + + + +Module emysql_conn + + + + +
    + +

    Module emysql_conn

    + + + +

    Function Index

    + + + + + + + + + + +
    close_connection/1
    execute/3
    open_connection/1
    open_connections/1
    open_n_connections/2
    prepare/3
    reset_connection/2
    set_database/2
    set_encoding/2
    unprepare/2
    + +

    Function Details

    + +

    close_connection/1

    +
    +

    close_connection(Conn) -> any()

    +
    + +

    execute/3

    +
    +

    execute(Connection, Query, Args) -> any()

    +
    + +

    open_connection/1

    +
    +

    open_connection(Pool) -> any()

    +
    + +

    open_connections/1

    +
    +

    open_connections(Pool) -> any()

    +
    + +

    open_n_connections/2

    +
    +

    open_n_connections(PoolId, N) -> any()

    +
    + +

    prepare/3

    +
    +

    prepare(Connection, Name, Statement) -> any()

    +
    + +

    reset_connection/2

    +
    +

    reset_connection(Pools, Conn) -> any()

    +
    + +

    set_database/2

    +
    +

    set_database(Connection, Database) -> any()

    +
    + +

    set_encoding/2

    +
    +

    set_encoding(Connection, Encoding) -> any()

    +
    + +

    unprepare/2

    +
    +

    unprepare(Connection, Name) -> any()

    +
    +
    + + +

    Generated by EDoc, Feb 10 2011, 07:38:23.

    + + diff --git a/emysql_conn_mgr.html b/emysql_conn_mgr.html new file mode 100644 index 00000000..c35040f4 --- /dev/null +++ b/emysql_conn_mgr.html @@ -0,0 +1,133 @@ + + + +Module emysql_conn_mgr + + + + +
    + +

    Module emysql_conn_mgr

    + + +

    Behaviours: gen_server.

    + +

    Function Index

    + + + + + + + + + + + + + + + + + + +
    add_connections/2
    add_pool/1
    code_change/3
    find_pool/3
    handle_call/3
    handle_cast/2
    handle_info/2
    init/1
    lock_connection/1
    pools/0
    remove_connections/2
    remove_pool/1
    replace_connection/2
    start_link/0
    terminate/2
    unlock_connection/1
    wait_for_connection/1
    waiting/0
    + +

    Function Details

    + +

    add_connections/2

    +
    +

    add_connections(PoolId, Conns) -> any()

    +
    + +

    add_pool/1

    +
    +

    add_pool(Pool) -> any()

    +
    + +

    code_change/3

    +
    +

    code_change(OldVsn, State, Extra) -> any()

    +
    + +

    find_pool/3

    +
    +

    find_pool(PoolId, Tail, OtherPools) -> any()

    +
    + +

    handle_call/3

    +
    +

    handle_call(X1, From, State) -> any()

    +
    + +

    handle_cast/2

    +
    +

    handle_cast(Msg, State) -> any()

    +
    + +

    handle_info/2

    +
    +

    handle_info(Info, State) -> any()

    +
    + +

    init/1

    +
    +

    init(X1) -> any()

    +
    + +

    lock_connection/1

    +
    +

    lock_connection(PoolId) -> any()

    +
    + +

    pools/0

    +
    +

    pools() -> any()

    +
    + +

    remove_connections/2

    +
    +

    remove_connections(PoolId, Num) -> any()

    +
    + +

    remove_pool/1

    +
    +

    remove_pool(PoolId) -> any()

    +
    + +

    replace_connection/2

    +
    +

    replace_connection(OldConn, NewConn) -> any()

    +
    + +

    start_link/0

    +
    +

    start_link() -> any()

    +
    + +

    terminate/2

    +
    +

    terminate(Reason, State) -> any()

    +
    + +

    unlock_connection/1

    +
    +

    unlock_connection(Connection) -> any()

    +
    + +

    wait_for_connection/1

    +
    +

    wait_for_connection(PoolId) -> any()

    +
    + +

    waiting/0

    +
    +

    waiting() -> any()

    +
    +
    + + +

    Generated by EDoc, Feb 10 2011, 07:38:23.

    + + diff --git a/emysql_statements.html b/emysql_statements.html new file mode 100644 index 00000000..19450a8b --- /dev/null +++ b/emysql_statements.html @@ -0,0 +1,103 @@ + + + +Module emysql_statements + + + + +
    + +

    Module emysql_statements

    + + +

    Behaviours: gen_server.

    + +

    Function Index

    + + + + + + + + + + + + + +
    add/2
    all/0
    code_change/3
    fetch/1
    handle_call/3
    handle_cast/2
    handle_info/2
    init/1
    prepare/3
    remove/1
    start_link/0
    terminate/2
    version/2
    + +

    Function Details

    + +

    add/2

    +
    +

    add(StmtName, Statement) -> any()

    +
    + +

    all/0

    +
    +

    all() -> any()

    +
    + +

    code_change/3

    +
    +

    code_change(OldVsn, State, Extra) -> any()

    +
    + +

    fetch/1

    +
    +

    fetch(StmtName) -> any()

    +
    + +

    handle_call/3

    +
    +

    handle_call(X1, From, State) -> any()

    +
    + +

    handle_cast/2

    +
    +

    handle_cast(Msg, State) -> any()

    +
    + +

    handle_info/2

    +
    +

    handle_info(Info, State) -> any()

    +
    + +

    init/1

    +
    +

    init(X1) -> any()

    +
    + +

    prepare/3

    +
    +

    prepare(ConnId, StmtName, Version) -> any()

    +
    + +

    remove/1

    +
    +

    remove(ConnId) -> any()

    +
    + +

    start_link/0

    +
    +

    start_link() -> any()

    +
    + +

    terminate/2

    +
    +

    terminate(Reason, State) -> any()

    +
    + +

    version/2

    +
    +

    version(ConnId, StmtName) -> any()

    +
    +
    + + +

    Generated by EDoc, Feb 10 2011, 07:38:23.

    + + diff --git a/emysql_sup.html b/emysql_sup.html new file mode 100644 index 00000000..694395ef --- /dev/null +++ b/emysql_sup.html @@ -0,0 +1,37 @@ + + + +Module emysql_sup + + + + +
    + +

    Module emysql_sup

    + + +

    Behaviours: supervisor.

    + +

    Function Index

    + + +
    init/1
    start_link/0
    + +

    Function Details

    + +

    init/1

    +
    +

    init(X1) -> any()

    +
    + +

    start_link/0

    +
    +

    start_link() -> any()

    +
    +
    + + +

    Generated by EDoc, Feb 10 2011, 07:38:23.

    + + diff --git a/emysql_tcp.html b/emysql_tcp.html new file mode 100644 index 00000000..87775f08 --- /dev/null +++ b/emysql_tcp.html @@ -0,0 +1,36 @@ + + + +Module emysql_tcp + + + + +
    + +

    Module emysql_tcp

    + + + +

    Function Index

    + + +
    recv_packet/1
    send_and_recv_packet/3
    + +

    Function Details

    + +

    recv_packet/1

    +
    +

    recv_packet(Sock) -> any()

    +
    + +

    send_and_recv_packet/3

    +
    +

    send_and_recv_packet(Sock, Packet, SeqNum) -> any()

    +
    +
    + + +

    Generated by EDoc, Feb 10 2011, 07:38:23.

    + + diff --git a/emysql_tracer.html b/emysql_tracer.html new file mode 100644 index 00000000..324c72cc --- /dev/null +++ b/emysql_tracer.html @@ -0,0 +1,30 @@ + + + +Module emysql_tracer + + + + +
    + +

    Module emysql_tracer

    + + + +

    Function Index

    + +
    trace_module/1
    + +

    Function Details

    + +

    trace_module/1

    +
    +

    trace_module(Fun) -> any()

    +
    +
    + + +

    Generated by EDoc, Feb 10 2011, 07:38:23.

    + + diff --git a/emysql_util.html b/emysql_util.html new file mode 100644 index 00000000..abb5e63d --- /dev/null +++ b/emysql_util.html @@ -0,0 +1,125 @@ + + + +Module emysql_util + + + + +
    + +

    Module emysql_util

    + + + +

    Function Index

    + + + + + + + + + + + + + + +
    as_record/3
    as_record/4package row data as records.
    asciz/1
    bxor_binary/2
    dualmap/3
    encode/1Encode a value so that it can be included safely in a MySQL query.
    encode/2
    field_names/1
    hash/1
    length_coded_binary/1
    length_coded_string/1
    null_terminated_string/2
    quote/1
    rnd/3
    + +

    Function Details

    + +

    as_record/3

    +
    +

    as_record(Result, RecordName, Fields) -> any()

    +
    + +

    as_record/4

    +
    +

    as_record(Result, RecordName, Fields, Fun) -> Result +

    • Result = #result_packet{}
    • +
    • RecordName = atom()
    • +
    • Fields = [atom()]
    • +
    • Fun = 'fun'()
    • +
    • Result = [Row]
    • +
    • Row = [record()]
    • +

    +

    package row data as records

    + +

    RecordName is the name of the record to generate. +Fields are the field names to generate for each record.

    + +

    -module(fetch_example).

    + + fetch_foo() -> + Res = emysql:execute(pool1, "select * from foo"), + Res:as_record(foo, record_info(fields, foo)).

    + +

    asciz/1

    +
    +

    asciz(Data) -> any()

    +
    + +

    bxor_binary/2

    +
    +

    bxor_binary(B1, B2) -> any()

    +
    + +

    dualmap/3

    +
    +

    dualmap(F, R1, R2) -> any()

    +
    + +

    encode/1

    +
    +

    encode(Val::term()) -> string() | binary() | {error, Error}

    +

    Encode a value so that it can be included safely in a MySQL query. +

    + +

    encode/2

    +
    +

    encode(Val, AsBinary) -> any()

    +
    + +

    field_names/1

    +
    +

    field_names(Result) -> any()

    +
    + +

    hash/1

    +
    +

    hash(S) -> any()

    +
    + +

    length_coded_binary/1

    +
    +

    length_coded_binary(X1) -> any()

    +
    + +

    length_coded_string/1

    +
    +

    length_coded_string(Bin) -> any()

    +
    + +

    null_terminated_string/2

    +
    +

    null_terminated_string(X1, Acc) -> any()

    +
    + +

    quote/1

    +
    +

    quote(String) -> any()

    +
    + +

    rnd/3

    +
    +

    rnd(N, Seed1, Seed2) -> any()

    +
    +
    + + +

    Generated by EDoc, Feb 10 2011, 07:38:23.

    + + diff --git a/emysql_worker.html b/emysql_worker.html new file mode 100644 index 00000000..80188a9e --- /dev/null +++ b/emysql_worker.html @@ -0,0 +1,49 @@ + + + +Module emysql_worker + + + + +
    + +

    Module emysql_worker

    + + + +

    Function Index

    + + + +
    behaviour_info/1
    execute/2
    start/1
    + +

    Function Details

    + +

    behaviour_info/1

    +
    +

    behaviour_info(X1) -> any()

    +
    + +

    execute/2

    +
    +

    execute(To::Pid, Message) -> Result +

    • Pid = pid()
    • +
    • Message = term()
    • +
    • Result = term()
    • +

    +
    + +

    start/1

    +
    +

    start(Module) -> Result +

    • Module = atom()
    • +
    • Result = {ok, pid()} | {error, term()}
    • +

    +
    +
    + + +

    Generated by EDoc, Feb 10 2011, 07:38:23.

    + + diff --git a/erlang.png b/erlang.png new file mode 100644 index 0000000000000000000000000000000000000000..987a618e2403af895bfaf8c2f929e3a4f3746659 GIT binary patch literal 2109 zcmV-D2*US?P)rez_nr%N ze)-p~%6|a|LA_bA=l=$|3jjqS$tjbGG?@TN0w$Azq7Z{YeQxKcpLO55vno1^u23DP&V=i9-KAAsU*ECy^#OtaDC!lVSo!+|-%T+LhTHP^Oqwx8m)b4r3V28JmV&6M#iG)&0;P`j>XGfomEIEK6wPkhI{{K?3#uAGq$!`N_F)TNX zAvuspF?^;c9h%CPWyTDc_03%r4N8+Yzzo_VSfa!zo_7F6D?<+-+KkHwXiWQR=Mr(9|K@{{xEjfDvAbS9uNCP&{)NNCoC?XA$aRe>R8-> z5N<#S_)$d|EYpJfPC?{`$Y~f4yjH&dxHXIGG8wiaLBD6usC87cg+dd&3WLJd4_TcmEeAOz8R>ikgW(9821 z{34Se09Y?KoG<_Y;DDSoyTk>fUN0YO5)3^Za{&s1JbidC9}56{px+f|K_0;YuL5h} z_9J3y%7ucwM)E4K#=Cn7tCjjRkKjnQuiFcM6{17Jt#5F}7z8~RYqW24xV?kAU6xQN zh+h4|SmO1;TdsVOaOeD*kKf}6I7=6ZNig_rtqV?Ov1HrU(P%Hi#6npSe>%qGaNK1w zW$v+r`r0>#p~AN^8b)#7Yesu(ys(>3SCYb4sF9%A9=kMHrLmzk}E&WPG~Jx z9!r{qo5M184t;<7I`t1AsNjv912EeKkHKtOSl%wbcjFh7L6|G?Q+{?radOvuEW$>1 zoc+c&F+u$^0f}1_2dN&lS#I#p3e&+|YGHlMzRC)%&8TnGt+p*;Oz z`0=D=n|qcN+f@07;QjB@ktLhZ`+qz;(xYDli^Pex&&wwU2V4N-a3b@veqHg2cvCRb zoi=ZerLk!4t5!s3?|ARuWx_4-VCgl|TY2qa@$Dr~5QdiT8?$oPpZhaF5UOZ&x=+I9 zt((`6wBPM((BS{;2lmSB;o%z{>=mg*1k2oLjI=+zcf5$4BIZmkOrjrE z*VY(<@FO?zBVDc+Q~Lh;LnlYodZ$J3tmWJBN4j~wVOWelzexhft2nY6A3PZAcm!q} z931CL#1Ki6;HM{agTbKF>3(R-yuF1&Apn3Nh@PGvv)K$mkVqu*^z@vaFgQ3kFfg!s z^=f26@{Ny=_w@7x1qHF$bEk5X$)wR}0s{l>V!TCGM=R5Ei1Ll8u7Z*N0G1CPgB zyLPP|0H{-FRUDJv`Ea=9fX zC63D4+FBlumz$eAJv~j5q*|@^_xC?_>XiL0K@bH61$;i=&CLx(QGb8`8#iu{BnjJW zHUvSgUcK7T&~W(h;koN8t5vB~Ha0dgnane1&RA#87dVcaOpEMM)6>)E&YiPZEXBpe zlarHk89g;+G#U#E3hL_W002xT6UTApOeR%UR_5g7q^73!_4PG2Hi|@|ii(Pfi3vIY z0ES^?Mx1IOizO0?e0_a!9483k`PtCk-rm~Unwpw=?b@~O?(WdgP^bMMAYlLg{dIM9 zOy}OcxVTs%k(@q#n$PF+`TXkYYA%;cr_*5ofWcr$PEL-Ai772Db)3`L*|~G)&eqn} zq@*OrbXim`UAiO`3XdK=%H#1=D%HHV>FMbqAtCAM=@!e}C6Cc))ai5zg~H3rYjkup zD=RBMKR+`wv!kN}1^{3fR#a3}RaLcP#}20|H!^bT)~%G3lp{xu!0_{Wr2hW?>({UQ z`T1F`)|D$)*3{IP&1UDKhLn_)sMYHH{QRkzV=$M?#W2idGFh!wf*`b7ZGC-xVPT=c zV1Vs&!otFoN~M>VQ$G_G6}5No-m0pqwzjr;?W@INu~;m#k*%qz(P%VUt#;3zJ^lUt zU0q%G?%kVzvF7cqQmLw|tA~e&XIqun*x2Ug=9-!s48ty7ycil9Di(|7aybkD7#y?%lgQ z9`Ewy%eDpgxlvJ3Cr+GTFc>(F+cg;(8TPc>y?b|jeEgLwR}LLIBoqp1+1c4_HrvO? z$J5g@G&D3gIC$2ITrQ7`iwh4AfA;K|OePZu1oriTVVG1Zl}e@S)~)mK@UU1cI-Ty| z!Gj8gg2UmUD2ibif*{e+(R4bU#bU|j@{Joe^7(uSf+8X!q*7@_M1;L=AqbM3oXp{H nT3T9A6wS=c+_!HZolgHhw9g$%O4Wbp00000NkvXXu0mjf3HKBY literal 0 HcmV?d00001 diff --git a/index.html b/index.html new file mode 100644 index 00000000..10d5fa52 --- /dev/null +++ b/index.html @@ -0,0 +1,17 @@ + + + +The emysql application + + + + + + +<h2>This page uses frames</h2> +<p>Your browser does not accept frames. +<br>You should go to the <a href="overview-summary.html">non-frame version</a> instead. +</p> + + + diff --git a/markedoc.sed b/markedoc.sed new file mode 100644 index 00000000..8027e316 --- /dev/null +++ b/markedoc.sed @@ -0,0 +1,380 @@ +# markedoc 0.3.1 - 02/05/11 - H. Diedrich +# ---------------------------------------------------------- +# sed command file to convert markdown format to edoc format +# Linux, FreeBSD and Mac OS X. -- Windows must install sed. +# ---------------------------------------------------------- +# Use it to make a markdown readme file part of an edoc file: +# FrBSD: sed -E -f > +# MacOS: sed -E -f > +# Linux: sed -r -f > +# Only difference, Linux uses -r where the others use -E. +# ---------------------------------------------------------- +# SAMPLE for FreeBSD / Mac OS X: +# sed -E -f markedoc.sed README.markdown > overview.edoc +# SAMPLE for Linux: +# sed -r -f markedoc.sed README.markdown > overview.edoc +# ---------------------------------------------------------- +# SAMPLE FILES: +# https://github.com/hdiedrich/markedoc/tree/master/samples +# SAMPLE RESULTS: +# samples/what-you-should-see/ & samples/what-you-could-see/ +# ---------------------------------------------------------- +# SAMPLE WORKFLOW (change -r to -E for FreeBSD / Mac OS X): +# sed -r -f markedoc.sed README.md > doc/README.edoc +# erl -noshell -run edoc_run application "'myapp'" '"."' '[]' +# ---------------------------------------------------------- +# REQUIREMENTS: sed, Erlang. sed is in all *x distros. +# Windows: http://gnuwin32.sourceforge.net/packages/sed.htm +# ---------------------------------------------------------- +# STATUS: Pre-Beta. +# It can reliably do nice things but likes to trip up EDoc. +# With a bit of patience, and mostly with pretty clean md +# markup, and some blank lines sometimes, most things work. +# ---------------------------------------------------------- +# LICENSE: Free software, no warranties. +# ---------------------------------------------------------- +# edown: http://www.erlang.org/doc/apps/edoc/ +# Markdown: http://daringfireball.net/projects/markdown/ +# Edoc: http://www.erlang.org/doc/apps/edoc/ +# sed: http://www.gnu.org/software/sed/manual/sed.html +# ---------------------------------------------------------- +# Repository: https://github.com/hdiedrich/markedoc/ +# Issues: https://github.com/hdiedrich/markedoc/issues +# Please experiment and push your fixes. - Thanks! +# ---------------------------------------------------------- + +# ********************************************************** +# SCRIPT +# ********************************************************** +# Ach, da kommt der Meister! Herr, die Not ist gross! ~~~ +# ~~~ Die ich rief, die Geister, Werd ich nun nicht los. +# ---------------------------------------------------------- +# This is a sed script for use with -E/-r regexes & NOT -n. +# s/// is the basic sed regex replace +# command. sed normally works strictly line by line. 'N' +# is used to join lines. 't' is a conditional branch, ':' +# is a label. The order of replacement functions matters. +# There are tabs in some patterns that may look like spaces. +# See 'man sed' for more info. If you are a sed master, +# your help making this better is much appreciated. +# ********************************************************** + +:start + +# as first line, make the @doc tag +# -------------------------------- +1 i\ +@doc\ + +# code sample blocks, trying to get them into one
     block
    +# -----------------------------------------------------------
    +# tabs are consumed for 'navigation'. sed is Turing complete.
    +# inserted space is needed by edocs.
    +# There are tabs in this pattern.
    +/^	/ {
    +	# break ... on last line ('N' would exit)
    +	$ b end_collect_with_last_line_hit
    +	s/^	(.*)$/ \1/
    +	# do ...
    +	: do_collect
    +		# append next line
    +		N
    +		# break ... if we are now into the last line
    +		# (or the test below will eat the tab away.)
    +		$ b end_collect_with_last_line_hit
    +		# does the current last line start with a tab, too?
    +		s/(\n)	(.*)$/\1 \2/
    +		# while: ... yes, then loop
    +		t do_collect
    +	# normal end of collect: got all indendet lines, plus one too many.
    +	# -----------------------------------------------------------------
    +	b normal_course
    +	#
    +	# Run into file end while looping
    +	# -------------------------------
    +	: end_collect_with_last_line_hit
    +	# and does that last line start with a tab, too?
    +	s/(\n)	(.*)$/\1 \2/
    +	s/^	(.*)$/ \1/
    +	# yes, then we're done actually
    +	t wrap_rest_and_done
    +	# else, cut it off and such, as normal
    +	# debug i\
    +	# debug normal
    +	#
    +	: normal_course
    +	# ... ok, we have multiple lines, and we have one line too much, back it all up.
    +	h
    +	# Handle the 
     block to be (*):
    +	# ---------------------------------
    +	# cut off the last line, that doesn't belong and insert newlines
    +	s/^(.*)(\n)(.*)$/\2\1\2/
    +	# wrap all in the docs code tags ```...'''
    +	s/^(.*)$/```\1'''/
    +	# protect @ (for edoc related texts that explain @-tags). There is a tab in [].
    +	s/([ 	\"\'\`]+@)/\1@/g
    +	# send result to stdout  
    +	p
    +	# Now make sure that that last line is not lost:
    +	# ----------------------------------------------
    +	# get stored back
    +	g
    +	# this time discard all but the last line, which is processed further
    +	s/^.*\n(.*)$/\1/
    +	# jump to end
    +	b end_of_code_blocks_handling
    +	#
    +	# File End Remedy: wrap all to end and done.
    +	# ------------------------------------------
    +	: wrap_rest_and_done
    +	# debug i\
    +	# debug rest and done
    + 	# wrap all in the docs code tags ```...'''
    +	s/^(.*)$/```\1'''/
    +	# protect @ (for edoc related texts that explain @-tags). There is a tab in [].
    +	s/([ 	\"\'\`]+@)/\1@/g
    +	b end
    +	#
    +} 
    +
    +:end_of_code_blocks_handling
    +
    +
    +# robust alternate for code blocks: each tabbed line
    +# --------------------------------------------------
    +# If the above keeps being difficult, use this more robust 
    +# version. The main difference is merely that it will tag each 
    +# line separately. If you work with very small margins and 
    +# paddings for 
     in your css file, that might give just as
    +# nice a result as the above. There are tabs in this pattern.
    +# (Really, delete all of the above from '# code sample blocks ...' 
    +# to # :end_of_code_blocks_handling)
    +# s/^	(.+)$/```	\1'''/
    +
    +# edoc sugar
    +# -----------
    +# won't help the markdown source but make the edoc prettier
    +
    +# footnote signs
    +# ..............
    +# superscript 1
    +s/\(\*1\)/\¹/g
    +# superscript 2
    +s/\(\*2\)/\²/g
    +# superscript 3
    +s/\(\*3\)/\³/g
    +# dagger
    +s/\(\+\)/\†/g
    +# double dagger
    +s/\(\+\+\)/\‡/g
    +# star
    +s/\(\*\)/\*/g
    +# double star
    +s/\(\*\*\)/\*\*/g
    +# triple star
    +s/\(\*\*\*\)/\*\*\*/g
    +
    +# special chars
    +# .............
    +# middle dot
    +s/::/\·/g
    +# guillemot
    +s/<>/\»/g
    +
    +# copyright
    +# .........
    +s/\(c)/\©/g
    +s/\(C)/\©/g
    +s/\(R)/\®/g
    +s/\(r)/\®/g
    +s/\(tm)/\™/g
    +s/\(TM)/\™/g
    +
    +# atone for markdown \_
    +# ---------------------
    +s/\\_/_/g
    +
    +
    +# links
    +# -----
    +# external links
    +s/\[([^]]+)\]\(([^)]+)\)/\1<\/a>/
    +
    +
    +# references, '[..]:...'-style
    +# ----------------------------
    +# take out markdown anchor relay tricks (see README)
    +# ..................................................
    +s/\[[^]]+\]:[ 	]*#.*//
    +
    +# real urls (normal case)
    +# .......................
    +s/(\[([^]]+)\]): +\[?(http[s]?:\/\/[^.>" ]+\.[^>" ]+)\]? *	*("([^"]+)") *	*$/
  • \5:\3<\/a><\/li>/ +# check next line "..." description +/(\[([^]]+)\]): +\[?(http[s]?:\/\/[^.>" ]+\.[^>" ]+)\]? *$/ { + # get next line, if the current is not the last + $!N + # try two line spanning, or single (last) line + s/(\[([^]]+)\]): +\[?(http[s]?:\/\/[^.>" ]+\.[^>" ]+)\]? * *\n * *("([^"]*)") * *$/
  • \5:\3<\/a><\/li>/ + t double_line_url_done + # try one line only, rest to be saved + s/(\[([^]]+)\]): +\[?(http[s]?:\/\/[^.>" ]+\.[^>" ]+)\]? * *(\n)/
  • \3<\/a><\/li>\4/ + t double_line_url_done + # case of last line, single, no "..." description + s/(\[([^]]+)\]): +\[?(http[s]?:\/\/[^.>" ]+\.[^>" ]+)\]? * *$/
  • \3<\/a><\/li>/ + : double_line_url_done + # print out up to first \n, delete, start from top with the rest + P + D +} + +# email addresses +# ............... +s/(\[([^]]+)\]): +" ]+@[^.>" ]+\.[^>" ]+)>? * *("([^"]+)") * *$/
  • +s/^\*(.+)$/
  • \1<\/li>/ + + +# emails, urls +# ------------ +s/<([^aA][^@>]+@[^.>]+.[^>]+)>/\1<\/a>/ +s/<(http[s]?:\/\/[^.>]+.[^>]+)>/\1<\/a>/ + + +# line breaks +# ----------- +s/ $/
    / + + +# single backticks +# ---------------- +# make code quotes +s/`([^`]+)`/\1<\/code>/g + + +# protect @ +# --------- +# leading space or tab indicates use as code sample for, well, edoc +# itself most likely, so escape it. +s/([ \"\'\`]+@)/\1@/g + +# protect & +# --------- +# still edoc won't understand code names like → +s/&#/:::AMPERSAND:::#/g +s/&([a-z]{1,7});/:::AMPERSAND:::\1;/g +s/&/\&/g +s/:::AMPERSAND:::/\&/g + +# headlines by underline === or --- +# --------------------------------- +# demoted to h2 and h3, as h1 is reserved in edoc +{ + # don't check this for the last line ('N' would exit) + $ b skip_alt_headlines + # get next line + N + # contract === with previous to headline h2 + # and in passing weed out explicit anchors and formatting + s/^([^<]+)(<.*>)?[ ]*\n=+ *$/== \1 ==/ + # if substitution took place, goto ... + t substi + # contract --- with previous to headline h2 + # and in passing weed out explicit anchors and formatting + s/^([^<]+)(<.*>)?[ ]*\n-+ *$/=== \1 ===/ + # if substitution took place, goto ... + t substi + # no substitution: print the previous line and start with latest from top + # ----------------------------------------------------------------------- + # store the two lines we have now, one is the one formatting is done with + # the next is the fresh one we just pulled. + h + # cut off the last line, print the ready formatted one + P + D + # and this is the goto for successful headline substitutions above: + :substi +} + +:skip_alt_headlines + +:end + +# at the bottom, add JS for the 'smart' direct jump +# ------------------------------------------------- +# to a reference url in trailing '[]:...'-notation +$ a\ + + +# debugger stable +# --------------- +# i\ +# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +# p +# i\ +# <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + +# ----------------------------------------------------------------- +# t,b: "In most cases, use of these commands indicates that you are +# probably better off programming in something like awk or Perl." +# sed manual: http://www.gnu.org/software/sed/manual/sed.html +# ----------------------------------------------------------------- +# 'powered by Eonblast' http://www.eonblast.com - all the new tech \ No newline at end of file diff --git a/modules-frame.html b/modules-frame.html new file mode 100644 index 00000000..ff6fa78f --- /dev/null +++ b/modules-frame.html @@ -0,0 +1,22 @@ + + + +The emysql application + + + +

    Modules

    + + + + + + + + + + + +
    emysql
    emysql_app
    emysql_auth
    emysql_conn
    emysql_conn_mgr
    emysql_statements
    emysql_sup
    emysql_tcp
    emysql_tracer
    emysql_util
    emysql_worker
    + + diff --git a/overview-summary.html b/overview-summary.html new file mode 100644 index 00000000..8030396d --- /dev/null +++ b/overview-summary.html @@ -0,0 +1,426 @@ + + + + Emysql - Erlang MySQL driver + + + + +
    +

    Emysql - Erlang MySQL driver +

    +

    Version: 0.2 +

    +

    Authors: Bill Warnecke, Jacob Vorreuter, Henning Diedrich.

    + +

    Emysql

    + +

    Erlang MySQL driver, based on a rewrite at Electronic Arts. Easy to use, Strong connection pooling, prepared statements & stored procedures. Optimized for a central node architecture and OLTP.

    + +

    While you can use mysql via ODBC, using a driver, like Emysql, should perform better. For samples and docs see below. Read the brief on choosing a package and about the history of the various MySQL drivers.

    + +

    This package is a direct continuation of the original emysql with fixes, updates, more documentation and samples. Emysql is a cleaner rewrite of erlang-mysql-driver, see History.

    + +
    + +

    Which fork/package should I use? Likely this one, but see Choosing.
    + Why are there so many? See History.
    + Who used this fork? Electronic Arts.
    + How do I ...? See Samples.
    + Hello ...? See Samples.

    + +

    Download: https://github.com/Eonblast/Emysql/archives/master
    + Docs: http://eonblast.github.com/Emysql/
    + Issues: https://github.com/Eonblast/Emysql/issues
    + Repository: https://github.com/Eonblast/Emysql

    + +
    + +

    Contents

    + +

  • Choosing
  • +
  • Samples
  • +
  • Usage
  • +
  • History
  • +
  • Links
  • +
  • Todo
  • +
  • License
  • + +
    + +

    Choosing

    + +
    Best

    +In most cases, especially for high performance and stability, this package, Emysql, will be the best choice. It was rewritten from the ground up to overcome fundamental issues of 'erlang-mysql-driver'. It also has some usable docs meanwhile.

    + +
    Simple

    +If you are looking for the plain necessities, you should use the ejabberd mysql driver. It is simple, battle tested and stable. There are comprehensive instructions in the source comments.

    + +
    Transaction

    +For mnesia-style transactions, one of the multiple 'erlang-mysql-drivers' may suite you best. There are quite many branches of it out there, and they are based on the same project as the ejabberd driver. To learn more about out the differences between the drivers, see the mysql driver history.

    + +

    Getting Emysql

    + +
     $ git clone git://github.com/Eonblast/Emysql.git Emysql
    + + +

    Samples

    + +

    Hello World

    + +This is a hello world program. Follow the three steps below to try it out. +
     -module(a_hello).
    + -export([run/0]).
    + 
    + run() ->
    + 
    + 	crypto:start(),
    + 	application:start(emysql),
    + 
    + 	emysql:add_pool(hello_pool, 1,
    + 		"hello_username", "hello_password", "localhost", 3306,
    + 		"hello_database", utf8),
    + 
    + 	emysql:execute(hello_pool,
    + 		<<"INSERT INTO hello_table SET hello_text = 'Hello World!'">>),
    + 
    +     Result = emysql:execute(hello_pool,
    + 		<<"select hello_text from hello_table">>),
    + 
    + 	io:format("~n~p~n", [Result]).
    + + +

    We'll be coming back to this source to make it run on your machine in a minute. But let's look at the basic building blocks first:

    + +

    Executing an SQL Statement

    + +
     emysql:execute(my_pool, <<"SELECT * from mytable">>).
    + +

    For the exact spec, see below, Usage. Regarding the 'pool', also see below.

    + +

    Executing a Prepared Statement

    + +
     emysql:prepare(my_stmt, <<"SELECT * from mytable WHERE id = ?">>).
    + 
    + emysql:execute(my_pool, my_stmt, [1]).
    + +

    Executing Stored Procedures

    + +
     emysql:execute(my_pool, <<"create procedure my_sp() begin select * from mytable; end">>).
    + 
    + emysql:execute(my_pool, <<"call my_sp();">>).
    + +

    Result Record

    + +
     -record(result_packet, {seq_num, field_list, rows, extra}).
    +

    Converting Row Data To Records

    + +
     -record(foo, {bar, baz}).
    + +
     Result = emysql:execute(pool1, <<"select bar, baz from foo">>).
    + Recs = emysql_util:as_record(Result, foo, record_info(fields, foo)).
    + Bars = [Foo#foo.bar || Foo <- Recs].
    + +

    Adding a Connection to the Connection Pool

    + +

    Emysql uses a sophisticated connection pooling mechanism.

    + +
     emysql:add_pool(my_pool, 1, "myuser", "mypass", "myhost", 3306, "mydatabase", utf8).
    + +

    Running Hello World

    + +

    Let's run the hello world sample from above:

    + +
    1. Build Emysql
    + +

    Build emysql.app, using make:

    + +
     $ cd Emysql
    + $ make
    + + +
    2. Make a Sample Database
    + +For use in the above sample (and all of those below, too), create a local mysql database. You should have a mysql server installed and running: +
     $ mysql [-u<user> -p]
    + mysql> create database hello_database;
    + mysql> use hello_database;
    + mysql> create table hello_table (hello_text char(20));
    + mysql> grant all privileges on hello_database.* to hello_username@localhost identified by 'hello_password';
    + +
    3. Paste & Run Hello
    + +

    Be sure to have ./ebin in your Erlang path. The hello-world source as shown above already waits in the Emysql directory, as hello.erl. Just compile and run it:

    + +
     $ erlc hello.erl
    + $ erl -pa ./ebin -s hello run -s init stop -noshell
    + +

    That's it.

    + +

    There are more sample programs:

    + +

    More Samples

    +Sample programs are in ./samples.

    + +

  • a_hello - Hello World
  • +
  • b_raw - Hello World, raw output
  • +
  • c_rows_as_records - Using Erlang records to access result rows
  • +
  • d_prepared_statement - Using prepared statements
  • +
  • e_stored_procedure - Using stored procedures
  • + +

    To run the samples, create the database as listed above at localhost, and simply run the compile & run batches:

    + +
     $ cd samples
    + $ ./a_hello
    + $ ./b_raw
    + $ ./c_rows_as_records
    + $ ./d_prepared_statement
    + $ ./e_stored_procedure

    +or (after building emysql.app and the database, as explained above), start a_hello etc. manually along these lines:

    + +
     $ make
    + $ cd samples
    + $ erlc a_hello.erl
    + $ erl -pa ../ebin -s a_hello run -s init stop -noshell
    + +

    Usage

    + +

    General Notes on using Emysql, including the actual specs:

    + +
    Starting an Application
    + +

    The Emysql driver is an Erlang gen-server, and, application.

    + +
     crypto:start(),
    + application:start(emysql).
    + +
    Adding a Pool
    + +
     % emysql:add_pool(PoolName, PoolSize, Username, Password, Host, Port, Database, Encoding) ->
    + %	 ok | {error, pool_already_exists}  
    + % PoolName = atom()  
    + % PoolSize = integer()  
    + % Username = string()  
    + % Password = string()  
    + % Host = string()  
    + % Port = integer()  
    + % Database = string()  
    + % Encoding = atom()  
    + 
    + emysql:add_pool(mypoolname, 1, "username", "mypassword", "localhost", 3306, "mydatabase", utf8).
    + +
    More Record Types
    + +
     -record(result_packet, {seq_num, field_list, rows, extra}).
    + 
    + -record(ok_packet, {seq_num, affected_rows, insert_id, status, warning_count, msg}).
    + 
    + -record(error_packet, {seq_num, code, msg}).
    + +

    For other record types, see include/emysql.hrl.

    + +
    Executing SQL Statements
    + +
     % emysql:execute(PoolName, Statement) -> result_packet() | ok_packet() | error_packet()  
    + % PoolName = atom()  
    + % Statement = string() | binary()  
    + 
    + emysql:execute(mypoolname, <<"SELECT * from mytable">>).
    + # result_packet{field_list=[...], rows=[...]}
    + 
    + emysql:execute(mypoolname, <<"UPDATE mytable SET bar = 'baz' WHERE id = 1">>).
    + # ok_packet{affected_rows=1}
    + +
    Executing Prepared Statements
    + +
     % emysql:prepare(StmtName, Statement) -> ok  
    + % StmtName = atom()  
    + % Statement = binary() | string()  
    + 
    + emysql:prepare(my_stmt, <<"SELECT * from mytable WHERE id = ?">>).
    + # ok
    + +
     % emysql:execute(PoolName, StmtName, Args) -> result_packet() | ok_packet() | error_packet()  
    + % StmtName = atom()  
    + % Args = [term()]  
    + 
    + emysql:execute(mypoolname, my_stmt, [1]).
    + #result_packet{field_list=[...], rows=[...]}
    + +
    Executing Stored Procedures
    + +
     % emysql:execute(PoolName, StmtName, Args) -> result_packet() | ok_packet() | error_packet()  
    + % StmtName = atom()  
    + % Args = [term()]  
    + 
    + emysql:execute(hello_pool,
    + 	<<"create procedure sp_hello() begin select * from hello_table; end">>).
    + {ok_packet,1,0,0,2,0,[]}
    + 
    + emysql:execute(hello_pool, <<"call sp_hello();">>).
    + [{result_packet,6,
    +                 [{field,2,<<"def">>,<<"hello_database">>,<<"hello_table">>,
    +                         <<"hello_table">>,<<"hello_text">>,<<"hello_text">>,
    +                         254,<<>>,33,60,0,0}],
    +                 [[<<"Hello World!">>],[<<"Hello World!">>]],
    +                 <<>>},
    + {ok_packet,7,0,0,34,0,[]}]
    + +

    Note that you are getting back a list of results here.

    + +
    Converting Row Data To Records
    + +
     % emysql_util:as_record(ResultPacket, RecordName, Fields) -> Result  
    + % ResultPacket = result_packet()  
    + % RecordName = atom() (the name of the record to generate)  
    + % Fields = [atom()] (the field names to generate for each record)  
    + % Result = [record()]  
    + 
    + -module(fetch_example).
    + -record(foo, {bar, baz, bat}).
    + 
    + fetch_foo() ->
    +    Result = emysql:execute(pool1, <<"select bar, baz, bat from foo">>),
    +    Recs = emysql_util:as_record(Result, foo, record_info(fields, foo)),
    +    [begin
    + 	  io:format("foo: ~p, ~p, ~p~n", [Foo#foo.bar, Foo#foo.baz, Foo#foo.bat])
    +     end || Foo <- Recs].
    + + +

    History

    + +

    Open Source Erlang MySQL driver efforts are a fractured matter. You may find yourself digging in the source to find out about their relationship with each other - and which one to pick. Here is a brief history.

    + +

    Yxa: The first Erlang MySQL driver, in ~270 lines of code, seems to have been written between 2001 and 2004 by Magnus Ahltorp at the Swedish Royal Institute of Technology. It exposes low level, blocking functions to talk 4.0 protocol with a MySQL server. In 2005 Fredrik Thulin brought the driver into its current modular form to use it for the the SIP proxy Yxa while working at the Stockholm University. It has three process layers: a high level, round-robin connection pooling module; then, middle-man, single-connection, blocking processes that do the bit-level wrangling with the MySQL protocol. This module, mysql_conn, can also be used as a single-connection, stand-alone driver. And below this, there are small, protocol-agnostic receiver processes that handle the socket communication with the server, no matter the contents. Fredrik implemented gen-server behavior, added logging, error messages, upgraded authentication, and thoroughly commented the source. This mysql driver is working, complete and stable since at least 2007, it is available as part of Yxa 1.0 (hosted on github). It has no support for transactions or stored procedures. It is the basis for the following two packages. Its basic modular division and general functionality were not changed but only enhanced and it had originally been agreed upon that the Yxa branch should receive and merge the contributions of the later forks upstream. Unfortunately, that did not come to pass.

    + + +

    ejabberd: In 2006, a fork of the Yxa driver was created by Mickael Remond at Process One to become part of the successful instant messaging server ejabberd (also hosted at github). It can be assumed to be as stable as the Yxa branch, didn't change a byte in the lowest level, and only enhanced the higher levels. The difference to the original Yxa branch mainly consists of added inspection functions that help using the query results, and of an independent adoption to the MySQL 4.1 client-server protocol. The original Yxa branch has meanwhile adopted edoc comment format, which makes the sources look more different than they actually are. Find a Jan 2011 diff between Yxa and the ejabberd version here, and one ignoring comments here. These two branches could, be merged quiet easily, probably without any change in functionality.

    + +

    erlang-mysql-driver: The storied life of this branch began in 2006 when Yariv Sadan created a fork from the ejabberd branch, made it a standalone project, gave it the maximally generic name that stuck, and hosted it at Google Code. Before he moved on to work at Facebook, he added high-level handling of prepared statements and transactions, and at long last completed some loose ends with the connection pooling that had been known to be lagging since the Yxa version. There were changes both in the original Yxa and the ejabberd branch after the forking off that never made their way into this fork, but in all likelihood they must be minor. It is not always obvious if the changes in erlang-mysql-driver had reached their intended final form. The separation of the three process layers seems to have suffered and complicated enhancements as the highest layer module, mysql.erl, started to become entangled with the second, mysql_conn.erl. Maybe that had a part in why the upstream merge never happened. The main repository of this fork lay dormant since Oct '07 when in Feb '10, Dave Smith started making some updates and put them on github. The driver is now enjoying a couple of active forks that make a convincing case for the github Network graphs.

    + +

    A parallel fork from Yariv's branch, not entangled with Dave's tree, is the one by Nick Gerakines. It may technically be the strongest of the erlang-mysql-driver forks, with a lot of clean up work by some very smart guys put in. It is generally less well known. And much less forked. In the end, the branch was abandoned in favor of Emysql. In all branches, docs beyond the source comments remain lacking.

    + +

    Emysql was created from scratch in 2009, specifically to achieve better stability and throughput. It was proposed and written by Jacob Vorreuter at Electronic Arts and deployed at Shawn Fanning's Rupture.com, a social network site for gamers. Initially, Nick Gerakines, Jacob's boss at EA, rewrote large parts of erlang-mysql-server to clean it up. But some fundamental problems remained and when half a year in, they could still not overcome performance and stability issues, Nick gave Jacob green light to rewrite it from the ground up because they felt that, in Jacob's words, the Yxa branch had been touched by so many people that it had become more complicated than necessary. According to Jacob, Bill Warnecke helped in the early design and testing. They abandoned the separation into three process layers and pulled the socket handling and bit-parsing into one module, coupling the functionality in direct function calls. It looks like they borrowed some chore lines from Magnus but generally created a new, straight forward architecture focussed on providing a high performance node. Not only can Emysql have multiple connections, but multiple pools of multiple connections each, which makes for a strong central OLTP manager. Jacob says that Emysql is pretty stable and ran without issues in production at EA. Nick remembers: "The primary way we used it was to have a single Erlang node be the MySQL communication point and allow a number of connected nodes to connect through it to MySQL. We wanted very high throughput with many pids across a grid connecting to it and needed the ability to have a number of MySQL connections open for connection pooling." Rupture was killed in the consolidations of 2008. But Shawn could probably keep the money and we the fond memory of Napster and now, the glistening Emysql.

    + +

    Eonblast Emysql is a continuation fork of Jacob's work, including all his commits and adding docs, samples, fixes and extensions. Henning Diedrich, Vitaliy Batichko and Chris Rempel have contributed updates to this branch. Support for stored procedures has been added, but the fork is otherwise still very close to the original, which currently lays dormant.

    + +

    Fredrik, Nick and Jacob helped shedding light on the matter. Thank you very much! Errors and omissions are mine. Please let me know about typos and errors you may be spotting. Thanks.

    + + +

    Links and References

    + +

  • emysql:http://github.com/JacobVorreuter/emysql
  • +
  • erlang-mysql-driver:http://github.com/dizzyd/erlang-mysql-driver
  • +
  • Royal Institure of Technology:http://www.kth.se/
  • +
  • Yxa mysql driver:https://github.com/fredrikt/yxa/tree/master/src/mysql
  • +
  • Yxa Home:http://www.stacken.kth.se/project/yxa/index.html
  • +
  • Yxa repository at github:https://github.com/fredrikt/yxa
  • +
  • ejabberd mysql driver:http://svn.process-one.net/ejabberd-modules/mysql/trunk/
  • +
  • Process One Home:https://support.process-one.net
  • +
  • ejabberd Home:http://www.process-one.net/en/ejabberd/
  • +
  • ejabberd repository at github:https://github.com/processone/ejabberd/
  • +
  • ejabberd MySQL 4.1. patch:https://support.process-one.net/doc/display/CONTRIBS/Yxa
  • +
  • Diff of Yxa and ejabberd mysql drivers:https://github.com/Eonblast/Emysql/tree/master/doc/diff-ejabberd-yxa.txt
  • +
  • Diff of Yxa and ejabberd mysql drivers ignoring comment changes:https://github.com/Eonblast/Emysql/tree/master/doc/diff-ejabberd-yxa-2.txt
  • +
  • original erlang-mysql-driver:http://code.google.com/p/erlang-mysql-driver/
  • +
  • Dave Smith's erlang-mysql-driver at github, currently not maintained:http://github.com/dizzyd/erlang-mysql-driver
  • +
  • Fork graph of erlang-mysql-driver at github:https://github.com/dizzyd/erlang-mysql-driver/network
  • +
  • Earliest Yxa mysql driver:http://www.stacken.kth.se/projekt/yxa/mysql-0.1.tar.gz
  • +
  • MySQL protocol:http://forge.mysql.com/wiki/MySQL_Internals_ClientServer_Protocol
  • +
  • Stockholm University:http://www.su.se/english/
  • +
  • Network graph of Nick's erlang-mysql-driver fork:https://github.com/ngerakines/erlang_mysql/network
  • +
  • Commits in Nick Gerakines' erlang-mysql-driver fork:https://github.com/ngerakines/erlang_mysql/commits/master
  • +
  • Nick Gerakines' erlang-mysql-driver fork:https://github.com/ngerakines/erlang_mysql
  • +
  • About Rupture:http://method.com/detail/CaseStudy/65
  • +
  • Commits in the current Eonblast Emysql branch:https://github.com/Eonblast/Emysql/commits/master
  • + +


    +

    + + +
  • Yariv Sadan:http://yarivsblog.blogspot.com
  • + +
  • Nick Gerakines:https://github.com/ngerakines
  • +
  • Jacob Vorreuter:https://github.com/JacobVorreuter
  • + + +
  • Vitaliy Batichko:https://github.com/bva
  • +
  • Chris Rempel:https://github.com/csrl
  • + +

  • Eonblast Emysql Repository:https://github.com/Eonblast/Emysql
  • +
  • Emysql fixes:https://github.com/Eonblast/Emysql/issues/closed
  • +
  • Emysql updates:https://github.com/Eonblast/Emysql/commits/master
  • +
  • Emysql online docs:http://eonblast.github.com/Emysql/
  • + +

    Links

    + +

  • Emysql on Github
  • +
  • Original Yxa mysql driver
  • +
  • ejabberd fork
  • +
  • 'erlang-mysql-driver'
  • +
  • Dave Smith's erlang-mysql-driver fork
  • +
  • A maintained† erlang-mysql-driver fork
  • +
  • Another maintained† erlang-mysql-driver fork
  • +
  • MySQL Client Server Protocol
  • +
  • MySQL 5.5 Source
  • + +

    †maintained at the time of writing, Feb 2011.

    + + + +

    TODO

    +

  • decrementing pool size could close sockets that are in use
  • +
  • spawn individual conn_mgr gen_server processes for each pool
  • +
  • allow row results to be returned as binary
  • + +

    License

    + +

    Copyright © 2009-2011 +Bill Warnecke bill@rupture.com, +Jacob Vorreuter jacob.vorreuter@gmail.com, +Henning Diedrich hd2010@eonblast.com, +Eonblast Corporation http://www.eonblast.com.

    + +

    Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom +the Software is furnished to do so, subject to the following +conditions:

    + +

    The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software.

    + +

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE.

    + + + + + + + + + + + + + + +
    + +

    Generated by EDoc, Feb 10 2011, 07:38:24.

    + + diff --git a/overview.edoc b/overview.edoc new file mode 100644 index 00000000..116afbbb --- /dev/null +++ b/overview.edoc @@ -0,0 +1,7 @@ +@author Bill Warnecke, Jacob Vorreuter, Henning Diedrich + +@title Emysql - Erlang MySQL driver + +@version 0.2 + +@docfile "doc/readme.edoc" diff --git a/packages-frame.html b/packages-frame.html new file mode 100644 index 00000000..504e9860 --- /dev/null +++ b/packages-frame.html @@ -0,0 +1,11 @@ + + + +The emysql application + + + +

    Packages

    +
    + + diff --git a/powered-by-mysql-125x64.png b/powered-by-mysql-125x64.png new file mode 100644 index 0000000000000000000000000000000000000000..86b3460d505d1be6baaab2a3f5d63d9ab01856d0 GIT binary patch literal 1443 zcmV;U1zh@xP)M(?)`|aAv$;$A+e}903u+ZG)jv4@H zkN^MwZ=(B)0000PbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$$w@>( zRA}DCnTfWeAPhiJaDCRgYpaF-|I0~ORFtW&)7HtE<3f4d5E2p`B0efLxC7aeB{ zuL3^HPo%?|%OP_G&0z(y6X}9*1h*Pm7SF8zdHTc)8Tn@9`bqqKo-UY-x_UPF#Quu^ z0=?k*))P57jXuGKx7q#w=6YYCYt!3-!1J@{*?y*Ey$yVqUNTl~nkuFu;RHHNe&^(F z+kb^_lFcUbpGV(K6#TwAiQY3;*^`|^uUszHa&`{AUXuJ;C0?K8^Oe&X*eUduyVqkk z`LEE4q_@-I!uu^fkTNze(yzh#w)OcM zS9zCd^KGUDI%*#VH@t>>9YU(^G&C1=U+8rAR~784K-wo8vN`2xI$m#RgGfXH6*L%e z;IQ#WKkz9$UV{Ks^v4l;H$HYW^ax&V!pGMr(0cT|f=pBIR=&*el+l(ex_`Z1{eaRS zYGAM%+))uK{82=(AfN#>k6}nr&l`}UAQa&^(?tUcqw#2PolRe~Fn`APXQ{4WON|fo z(FZ|OqT;@f)G-<(983N-2J{NbKnLsyH27z)djnlj*PvOM7?!wm)^lYxdxWvP(DV@} zehb3uh$}GqG9VhsfR#X@*k@0N$xE$-DdS0rHgMK+T`zxK(~ad}ELe3K1ip|qrtlyc zvO}aTPdAnay>YenUlS^4^#duVv*^a_kO@~Gqd@wX^t!o&_PWuxy$*kkTqkDJHEU{uN6~g0V0m*qVdvgxc@s{ zn4pttOviMb|3yl2mwF8E&?U7}G$wvU-@4lybm?r_;(o8tg-w@fB{`r&r(vPXahVFe z@(nsrn^-?Qq2C{IoDK_J?Ktzcv!=H%SRQv;h4=JMn<%faWJvEn@jYJXa@u^}at*yu zJD-t4HLugSXS_~lnFKsBo@1d=&2~l-m%A4~hR2FtX?P*l^s?tW(X+MWF3ss|-X~sh ziM=Ep(iL58v0PW+hOW6qUa xVce>Mj__vN`DZ%Wd3;}rPsz*lzW(6J;tzqcVSM7`Lf-%Y002ovPDHLkV1iWD*RB8n literal 0 HcmV?d00001 diff --git a/stylesheet.css b/stylesheet.css new file mode 100644 index 00000000..e426a904 --- /dev/null +++ b/stylesheet.css @@ -0,0 +1,55 @@ +/* standard EDoc style sheet */ +body { + font-family: Verdana, Arial, Helvetica, sans-serif; + margin-left: .25in; + margin-right: .2in; + margin-top: 0.2in; + margin-bottom: 0.2in; + color: #000000; + background-color: #ffffff; +} +h1,h2 { + margin-left: -0.2in; +} +div.navbar { + background-color: #add8e6; + padding: 0.2em; +} +h2.indextitle { + padding: 0.4em; + background-color: #add8e6; +} +h3.function,h3.typedecl { + background-color: #add8e6; + padding-left: 1em; +} +div.spec { + margin-left: 2em; + background-color: #eeeeee; +} +a.module,a.package { + text-decoration:none +} +a.module:hover,a.package:hover { + background-color: #eeeeee; +} +ul.definitions { + list-style-type: none; +} +ul.index { + list-style-type: none; + background-color: #eeeeee; +} + +/* + * Minor style tweaks + */ +ul { + list-style-type: square; +} +table { + border-collapse: collapse; +} +td { + padding: 3 +}