diff --git a/web/api/flukso/Makefile b/web/api/flukso/Makefile
index 361dbd2..3e6720e 100644
--- a/web/api/flukso/Makefile
+++ b/web/api/flukso/Makefile
@@ -2,7 +2,7 @@ ERL ?= erl
EBIN_DIRS := $(wildcard deps/*/ebin)
APP := flukso
-all: erl ebin/$(APP).app erlrrd
+all: erl ebin/$(APP).app erlrrd mysql
erl:
@$(ERL) -pa $(EBIN_DIRS) -noinput +B \
@@ -21,3 +21,5 @@ ebin/$(APP).app: src/$(APP).app
erlrrd:
@(cd deps/erlrrd;$(MAKE))
+mysql:
+ @(cd deps/mysql;$(MAKE))
diff --git a/web/api/flukso/deps/erlrrd/ebin/erlrrd.app b/web/api/flukso/deps/erlrrd/ebin/erlrrd.app
deleted file mode 100644
index af306e2..0000000
--- a/web/api/flukso/deps/erlrrd/ebin/erlrrd.app
+++ /dev/null
@@ -1,12 +0,0 @@
-{application, erlrrd,
- [{description, "erlang rrdtool port"},
- {vsn, "1.0"},
- {modules, [
- erlrrd,
- erlrrd_app,
- erlrrd_sup
- ]},
- {registered, [erlrrd, erlrrd_sup]},
- {mod, {erlrrd_app, []}},
- {env, []},
- {applications, [kernel, stdlib]}]}.
diff --git a/web/api/flukso/deps/erlrrd/ebin/erlrrd.beam b/web/api/flukso/deps/erlrrd/ebin/erlrrd.beam
deleted file mode 100644
index f0d148d..0000000
Binary files a/web/api/flukso/deps/erlrrd/ebin/erlrrd.beam and /dev/null differ
diff --git a/web/api/flukso/deps/erlrrd/ebin/erlrrd_app.beam b/web/api/flukso/deps/erlrrd/ebin/erlrrd_app.beam
deleted file mode 100644
index bb2207a..0000000
Binary files a/web/api/flukso/deps/erlrrd/ebin/erlrrd_app.beam and /dev/null differ
diff --git a/web/api/flukso/deps/erlrrd/ebin/erlrrd_sup.beam b/web/api/flukso/deps/erlrrd/ebin/erlrrd_sup.beam
deleted file mode 100644
index 4cb24f6..0000000
Binary files a/web/api/flukso/deps/erlrrd/ebin/erlrrd_sup.beam and /dev/null differ
diff --git a/web/api/flukso/deps/mysql/Makefile b/web/api/flukso/deps/mysql/Makefile
new file mode 100644
index 0000000..2f6cace
--- /dev/null
+++ b/web/api/flukso/deps/mysql/Makefile
@@ -0,0 +1,11 @@
+all:
+ (cd src;$(MAKE) all)
+
+edoc:
+ (cd src;$(MAKE) edoc)
+
+test:
+ (cd src;$(MAKE) test)
+
+clean:
+ (cd src;$(MAKE) clean)
diff --git a/web/api/flukso/deps/mysql/doc/edoc-info b/web/api/flukso/deps/mysql/doc/edoc-info
new file mode 100644
index 0000000..db35f7c
--- /dev/null
+++ b/web/api/flukso/deps/mysql/doc/edoc-info
@@ -0,0 +1,3 @@
+{application,mysql}.
+{packages,[]}.
+{modules,[mysql,mysql_auth,mysql_conn,mysql_recv]}.
diff --git a/web/api/flukso/deps/mysql/doc/erlang.png b/web/api/flukso/deps/mysql/doc/erlang.png
new file mode 100644
index 0000000..987a618
Binary files /dev/null and b/web/api/flukso/deps/mysql/doc/erlang.png differ
diff --git a/web/api/flukso/deps/mysql/doc/index.html b/web/api/flukso/deps/mysql/doc/index.html
new file mode 100644
index 0000000..0626291
--- /dev/null
+++ b/web/api/flukso/deps/mysql/doc/index.html
@@ -0,0 +1,17 @@
+
+
+
+The mysql application
+
+
+
\ No newline at end of file
diff --git a/web/api/flukso/deps/mysql/doc/modules-frame.html b/web/api/flukso/deps/mysql/doc/modules-frame.html
new file mode 100644
index 0000000..9737a4f
--- /dev/null
+++ b/web/api/flukso/deps/mysql/doc/modules-frame.html
@@ -0,0 +1,15 @@
+
+
+
+The mysql application
+
+
+
+Modules
+
+
+
\ No newline at end of file
diff --git a/web/api/flukso/deps/mysql/doc/mysql.html b/web/api/flukso/deps/mysql/doc/mysql.html
new file mode 100644
index 0000000..5c4e284
--- /dev/null
+++ b/web/api/flukso/deps/mysql/doc/mysql.html
@@ -0,0 +1,145 @@
+
+
+
+Module mysql
+
+
+
+
+
+
+Module mysql
+
+
+Behaviours: gen_server.
+
+
+
+
+
+
+
+
+
asciz_binary() -> term()
+
+
+
+
+
code_change() -> term()
+
+
+
+
+
+
+
+
+
+
+
+
+
+
get_result_affected_rows() -> term()
+
+
+
+
+
get_result_field_info() -> term()
+
+
+
+
+
get_result_reason() -> term()
+
+
+
+
+
get_result_rows() -> term()
+
+
+
+
+
handle_call() -> term()
+
+
+
+
+
handle_cast() -> term()
+
+
+
+
+
handle_info() -> term()
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
start_link() -> term()
+
+
+
+
+
start_link() -> term()
+
+
+
+
+
start_link() -> term()
+
+
+
+
+
+
+
+Generated by EDoc, Nov 12 2009, 13:11:27.
+
+
diff --git a/web/api/flukso/deps/mysql/doc/mysql_auth.html b/web/api/flukso/deps/mysql/doc/mysql_auth.html
new file mode 100644
index 0000000..403930d
--- /dev/null
+++ b/web/api/flukso/deps/mysql/doc/mysql_auth.html
@@ -0,0 +1,36 @@
+
+
+
+Module mysql_auth
+
+
+
+
+
+
+Module mysql_auth
+
+
+
+
+
+
+
+
+
+
+
do_new_auth() -> term()
+
+
+
+
+
do_old_auth() -> term()
+
+
+
+
+Generated by EDoc, Nov 12 2009, 13:11:27.
+
+
diff --git a/web/api/flukso/deps/mysql/doc/mysql_conn.html b/web/api/flukso/deps/mysql/doc/mysql_conn.html
new file mode 100644
index 0000000..353089e
--- /dev/null
+++ b/web/api/flukso/deps/mysql/doc/mysql_conn.html
@@ -0,0 +1,60 @@
+
+
+
+Module mysql_conn
+
+
+
+
+
+
+Module mysql_conn
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
start_link() -> term()
+
+
+
+
+Generated by EDoc, Nov 12 2009, 13:11:27.
+
+
diff --git a/web/api/flukso/deps/mysql/doc/mysql_recv.html b/web/api/flukso/deps/mysql/doc/mysql_recv.html
new file mode 100644
index 0000000..e0d7943
--- /dev/null
+++ b/web/api/flukso/deps/mysql/doc/mysql_recv.html
@@ -0,0 +1,30 @@
+
+
+
+Module mysql_recv
+
+
+
+
+
+
+Module mysql_recv
+
+
+
+
+
+
+
+
+
+
+
start_link() -> term()
+
+
+
+
+Generated by EDoc, Nov 12 2009, 13:11:27.
+
+
diff --git a/web/api/flukso/deps/mysql/doc/overview-summary.html b/web/api/flukso/deps/mysql/doc/overview-summary.html
new file mode 100644
index 0000000..b2a1853
--- /dev/null
+++ b/web/api/flukso/deps/mysql/doc/overview-summary.html
@@ -0,0 +1,15 @@
+
+
+
+The mysql application
+
+
+
+
+The mysql application
+
+
+
+Generated by EDoc, Nov 12 2009, 13:11:27.
+
+
diff --git a/web/api/flukso/deps/mysql/doc/packages-frame.html b/web/api/flukso/deps/mysql/doc/packages-frame.html
new file mode 100644
index 0000000..3c63b4a
--- /dev/null
+++ b/web/api/flukso/deps/mysql/doc/packages-frame.html
@@ -0,0 +1,11 @@
+
+
+
+The mysql application
+
+
+
+Packages
+
+
+
\ No newline at end of file
diff --git a/web/api/flukso/deps/mysql/doc/stylesheet.css b/web/api/flukso/deps/mysql/doc/stylesheet.css
new file mode 100644
index 0000000..e426a90
--- /dev/null
+++ b/web/api/flukso/deps/mysql/doc/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
+}
diff --git a/web/api/flukso/deps/mysql/src/Makefile b/web/api/flukso/deps/mysql/src/Makefile
new file mode 100644
index 0000000..af7b8d9
--- /dev/null
+++ b/web/api/flukso/deps/mysql/src/Makefile
@@ -0,0 +1,20 @@
+include ../support/include.mk
+
+APPLICATION=mysql
+DOC_OPTS={dir,\"../doc\"}
+
+all: $(EBIN_FILES)
+
+debug:
+ $(MAKE) DEBUG=-DDEBUG
+
+clean:
+ rm -rf $(EBIN_FILES)
+
+edoc:
+ $(ERL) -noshell -pa ../ebin \
+ -eval "edoc:application($(APPLICATION), \".\", [$(DOC_OPTS)])" \
+ -s init stop
+
+test: all
+ $(ERL) -noshell -pa ../ebin -s $(APPLICATION) test -s init stop
diff --git a/web/api/flukso/deps/mysql/src/mysql.app b/web/api/flukso/deps/mysql/src/mysql.app
new file mode 100644
index 0000000..60c65d3
--- /dev/null
+++ b/web/api/flukso/deps/mysql/src/mysql.app
@@ -0,0 +1,12 @@
+{application, mysql,
+ [{description, "erlang mysql driver"},
+ {vsn, "1.0"},
+ {modules, [
+ mysql,
+ mysql_app,
+ mysql_sup
+ ]},
+ {registered, [mysql, mysql_sup]},
+ {mod, {mysql_app, []}},
+ {env, []},
+ {applications, [kernel, stdlib]}]}.
diff --git a/web/api/flukso/deps/mysql/src/mysql.erl b/web/api/flukso/deps/mysql/src/mysql.erl
new file mode 100644
index 0000000..7dde778
--- /dev/null
+++ b/web/api/flukso/deps/mysql/src/mysql.erl
@@ -0,0 +1,656 @@
+%%%-------------------------------------------------------------------
+%%% File : mysql.erl
+%%% Author : Magnus Ahltorp
+%%% Descrip.: MySQL client.
+%%%
+%%% Created : 4 Aug 2005 by Magnus Ahltorp
+%%%
+%%% Copyright (c) 2001-2004 Kungliga Tekniska Högskolan
+%%% See the file COPYING
+%%%
+%%% Usage:
+%%%
+%%%
+%%% Call one of the start-functions before any call to fetch/2
+%%%
+%%% 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 is a connection group identifier. If you want to have more
+%%% than one connection to a server (or a set of MySQL replicas),
+%%% add more with
+%%%
+%%% connect(Id, Host, Port, User, Password, Database, Reconnect)
+%%%
+%%% use 'undefined' as Port to get default MySQL port number (3306).
+%%% MySQL querys will be sent in a per-Id round-robin fashion.
+%%% Set Reconnect to 'true' if you want the dispatcher to try and
+%%% open a new connection, should this one die.
+%%%
+%%% When you have a mysql_dispatcher running, this is how you make a
+%%% query :
+%%%
+%%% fetch(Id, "select * from hello") -> Result
+%%% 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()
+%%%
+%%% If you just want a single MySQL connection, or want to manage your
+%%% connections yourself, you can use the mysql_conn module as a
+%%% stand-alone single MySQL connection. See the comment at the top of
+%%% mysql_conn.erl.
+%%%
+%%%-------------------------------------------------------------------
+-module(mysql).
+
+-behaviour(gen_server).
+
+%%--------------------------------------------------------------------
+%% External exports
+%%--------------------------------------------------------------------
+-export([start_link/5,
+ start_link/6,
+ start_link/7,
+
+ fetch/2,
+ fetch/3,
+
+ get_result_field_info/1,
+ get_result_rows/1,
+ get_result_affected_rows/1,
+ get_result_reason/1,
+
+ quote/1,
+ asciz_binary/2,
+
+ connect/7
+ ]).
+
+%%--------------------------------------------------------------------
+%% Internal exports - just for mysql_* modules
+%%--------------------------------------------------------------------
+-export([log/3,
+ log/4
+ ]).
+
+%%--------------------------------------------------------------------
+%% Internal exports - gen_server callbacks
+%%--------------------------------------------------------------------
+-export([init/1,
+ handle_call/3,
+ handle_cast/2,
+ handle_info/2,
+ terminate/2,
+ code_change/3
+ ]).
+
+%%--------------------------------------------------------------------
+%% Records
+%%--------------------------------------------------------------------
+-include("mysql.hrl").
+-record(state, {
+ conn_list, %% list() of mysql_connection record()
+ log_fun %% undefined | function for logging,
+ }).
+
+-record(mysql_connection, {
+ id, %% term(), user of 'mysql' modules id of this socket group
+ conn_pid, %% pid(), mysql_conn process
+ reconnect, %% true | false, should mysql_dispatcher try to reconnect if this connection dies?
+ host, %% undefined | string()
+ port, %% undefined | integer()
+ user, %% undefined | string()
+ password, %% undefined | string()
+ database %% undefined | string()
+ }).
+
+%%--------------------------------------------------------------------
+%% Macros
+%%--------------------------------------------------------------------
+-define(SERVER, mysql_dispatcher).
+-define(CONNECT_TIMEOUT, 5000).
+-define(LOCAL_FILES, 128).
+
+-define(PORT, 3306).
+
+
+%%====================================================================
+%% External functions
+%%====================================================================
+
+%%--------------------------------------------------------------------
+%% 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}
+%%--------------------------------------------------------------------
+start_link(Id, Host, User, Password, Database) when is_list(Host), is_list(User), is_list(Password),
+ is_list(Database) ->
+ start_link(Id, Host, ?PORT, User, Password, Database, undefined).
+
+start_link(Id, Host, Port, User, Password, Database) when is_list(Host), is_integer(Port), is_list(User),
+ is_list(Password), is_list(Database) ->
+ start_link(Id, Host, Port, User, Password, Database, undefined);
+
+start_link(Id, Host, User, Password, Database, LogFun) when is_list(Host), is_list(User), is_list(Password),
+ is_list(Database) ->
+ start_link(Id, Host, ?PORT, User, Password, Database, LogFun).
+
+start_link(Id, Host, Port, User, Password, Database, LogFun) when is_list(Host), is_integer(Port), is_list(User),
+ is_list(Password), is_list(Database) ->
+ crypto:start(),
+ gen_server:start_link({local, ?SERVER}, ?MODULE, [Id, Host, Port, User, Password, Database, LogFun], []).
+
+%%--------------------------------------------------------------------
+%% 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()
+%%--------------------------------------------------------------------
+fetch(Id, Query) when is_list(Query) ->
+ gen_server:call(?SERVER, {fetch, Id, Query}).
+fetch(Id, Query, Timeout) when is_list(Query) ->
+ gen_server:call(?SERVER, {fetch, Id, Query}, Timeout).
+
+%%--------------------------------------------------------------------
+%% 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()
+%%--------------------------------------------------------------------
+quote(String) when is_list(String) ->
+ [34 | lists:reverse([34 | quote(String, [])])]. %% 34 is $"
+
+quote([], Acc) ->
+ Acc;
+quote([0 | Rest], Acc) ->
+ quote(Rest, [$0, $\\ | Acc]);
+quote([10 | Rest], Acc) ->
+ quote(Rest, [$n, $\\ | Acc]);
+quote([13 | Rest], Acc) ->
+ quote(Rest, [$r, $\\ | Acc]);
+quote([$\\ | Rest], Acc) ->
+ quote(Rest, [$\\ , $\\ | Acc]);
+quote([39 | Rest], Acc) -> %% 39 is $'
+ quote(Rest, [39, $\\ | Acc]); %% 39 is $'
+quote([34 | Rest], Acc) -> %% 34 is $"
+ quote(Rest, [34, $\\ | Acc]); %% 34 is $"
+quote([26 | Rest], Acc) ->
+ quote(Rest, [$Z, $\\ | Acc]);
+quote([C | Rest], Acc) ->
+ quote(Rest, [C | Acc]).
+
+%%--------------------------------------------------------------------
+%% 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
+%%--------------------------------------------------------------------
+asciz_binary(<<>>, Acc) ->
+ {lists:reverse(Acc), <<>>};
+asciz_binary(<<0:8, Rest/binary>>, Acc) ->
+ {lists:reverse(Acc), Rest};
+asciz_binary(<>, Acc) ->
+ asciz_binary(Rest, [C | Acc]).
+
+%%--------------------------------------------------------------------
+%% 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}
+%%--------------------------------------------------------------------
+connect(Id, Host, undefined, User, Password, Database, Reconnect) ->
+ connect(Id, Host, ?PORT, User, Password, Database, Reconnect);
+connect(Id, Host, Port, User, Password, Database, Reconnect) ->
+ {ok, LogFun} = gen_server:call(?SERVER, get_logfun),
+ case mysql_conn:start(Host, Port, User, Password, Database, LogFun) of
+ {ok, ConnPid} ->
+ MysqlConn =
+ case Reconnect of
+ true ->
+ #mysql_connection{id = Id,
+ conn_pid = ConnPid,
+ reconnect = true,
+ host = Host,
+ port = Port,
+ user = User,
+ password = Password,
+ database = Database
+ };
+ false ->
+ #mysql_connection{id = Id,
+ conn_pid = ConnPid,
+ reconnect = false
+ }
+ end,
+ case gen_server:call(?SERVER, {add_mysql_connection, MysqlConn}) of
+ ok ->
+ {ok, ConnPid};
+ Res ->
+ Res
+ end;
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+%%--------------------------------------------------------------------
+%% 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()
+%%
+%% Note : Exported only for use by the mysql_* modules.
+%%
+%%--------------------------------------------------------------------
+log(LogFun, Level, Format) ->
+ log(LogFun, Level, Format, []).
+
+log(LogFun, Level, Format, Arguments) when is_function(LogFun) ->
+ LogFun(Level, Format, Arguments);
+log(undefined, _Level, Format, Arguments) ->
+ %% default is to log to console
+ io:format(Format, Arguments),
+ io:format("~n", []).
+
+
+%%====================================================================
+%% gen_server callbacks
+%%====================================================================
+
+%%--------------------------------------------------------------------
+%% 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).
+%%--------------------------------------------------------------------
+init([Id, Host, Port, User, Password, Database, LogFun]) ->
+ case mysql_conn:start(Host, Port, User, Password, Database, LogFun) of
+ {ok, ConnPid} ->
+ MysqlConn = #mysql_connection{id = Id,
+ conn_pid = ConnPid,
+ reconnect = true,
+ host = Host,
+ port = Port,
+ user = User,
+ password = Password,
+ database = Database
+ },
+ case add_mysql_conn(MysqlConn, []) of
+ {ok, ConnList} ->
+ {ok, #state{log_fun = LogFun,
+ conn_list = ConnList
+ }};
+ error ->
+ Msg = "mysql: Failed adding first MySQL connection handler to my list, exiting",
+ log(LogFun, error, Msg),
+ {error, Msg}
+ end;
+ {error, Reason} ->
+ log(LogFun, error, "mysql: Failed starting first MySQL connection handler, exiting"),
+ {stop, {error, Reason}}
+ end.
+
+%%--------------------------------------------------------------------
+%% 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)
+%%--------------------------------------------------------------------
+
+
+%%--------------------------------------------------------------------
+%% 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()
+%%--------------------------------------------------------------------
+handle_call({fetch, Id, Query}, From, State) ->
+ log(State#state.log_fun, debug, "mysql: fetch ~p (id ~p)", [Query, Id]),
+ case get_next_mysql_connection_for_id(Id, State#state.conn_list) of
+ {ok, MysqlConn, RestOfConnList} when is_record(MysqlConn, mysql_connection) ->
+ mysql_conn:fetch(MysqlConn#mysql_connection.conn_pid, Query, From),
+ %% move this mysql socket to the back of the list
+ NewConnList = RestOfConnList ++ [MysqlConn],
+ %% The ConnPid process does a gen_server:reply() when it has an answer
+ {noreply, State#state{conn_list = NewConnList}};
+ nomatch ->
+ %% we have no active connection matching Id
+ {reply, {error, no_connection}, State}
+ end;
+
+%%--------------------------------------------------------------------
+%% 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()
+%%--------------------------------------------------------------------
+handle_call({add_mysql_connection, Conn}, _From, State) when is_record(Conn, mysql_connection) ->
+ case add_mysql_conn(Conn, State#state.conn_list) of
+ {ok, NewConnList} ->
+ {Id, ConnPid} = {Conn#mysql_connection.id, Conn#mysql_connection.conn_pid},
+ log(State#state.log_fun, normal, "mysql: Added connection with id '~p' (pid ~p) to my list",
+ [Id, ConnPid]),
+ {reply, ok, State#state{conn_list = NewConnList}};
+ error ->
+ {reply, {error, "failed adding MySQL connection to my list"}, State}
+ end;
+
+%%--------------------------------------------------------------------
+%% Function: handle_call(get_logfun, From, State)
+%% Descrip.: Fetch our logfun.
+%% Returns : {reply, {ok, LogFun}, State}
+%% LogFun = undefined | function() with arity 3
+%%--------------------------------------------------------------------
+handle_call(get_logfun, _From, State) ->
+ {reply, {ok, State#state.log_fun}, State};
+
+handle_call(Unknown, _From, State) ->
+ log(State#state.log_fun, error, "mysql: Received unknown gen_server call : ~p", [Unknown]),
+ {reply, {error, "unknown gen_server call in mysql client"}, State}.
+
+
+%%--------------------------------------------------------------------
+%% Function: handle_cast(Msg, State)
+%% Descrip.: Handling cast messages
+%% Returns : {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State} (terminate/2 is called)
+%%--------------------------------------------------------------------
+handle_cast(Unknown, State) ->
+ log(State#state.log_fun, error, "mysql: Received unknown gen_server cast : ~p", [Unknown]),
+ {noreply, State}.
+
+
+%%--------------------------------------------------------------------
+%% 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)
+%%--------------------------------------------------------------------
+
+%%--------------------------------------------------------------------
+%% 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.
+%%--------------------------------------------------------------------
+handle_info({'DOWN', _MonitorRef, process, Pid, Info}, State) ->
+ LogFun = State#state.log_fun,
+ case remove_mysql_connection_using_pid(Pid, State#state.conn_list, []) of
+ {ok, Conn, NewConnList} ->
+ LogLevel = case Info of
+ normal -> normal;
+ _ -> error
+ end,
+ log(LogFun, LogLevel, "mysql: MySQL connection pid ~p exited : ~p", [Pid, Info]),
+ log(LogFun, normal, "mysql: Removed MySQL connection with pid ~p from list",
+ [Pid]),
+ case Conn#mysql_connection.reconnect of
+ true ->
+ start_reconnect(Conn, LogFun);
+ false ->
+ ok
+ end,
+ {noreply, State#state{conn_list = NewConnList}};
+ nomatch ->
+ log(LogFun, error, "mysql: Received 'DOWN' signal from pid ~p not in my list", [Pid]),
+ {noreply, State}
+ end;
+
+handle_info(Info, State) ->
+ log(State#state.log_fun, error, "mysql: Received unknown signal : ~p", [Info]),
+ {noreply, State}.
+
+%%--------------------------------------------------------------------
+%% Function: terminate(Reason, State)
+%% Descrip.: Shutdown the server
+%% Returns : Reason
+%%--------------------------------------------------------------------
+terminate(Reason, State) ->
+ LogFun = State#state.log_fun,
+ LogLevel = case Reason of
+ normal -> debug;
+ _ -> error
+ end,
+ log(LogFun, LogLevel, "mysql: Terminating with reason : ~p", [Reason]),
+ Reason.
+
+%%--------------------------------------------------------------------
+%% Function: code_change(_OldVsn, State, _Extra)
+%% Descrip.: Convert process state when code is changed
+%% Returns : {ok, State}
+%%--------------------------------------------------------------------
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+%%====================================================================
+%% Internal functions
+%%====================================================================
+
+%%--------------------------------------------------------------------
+%% 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()
+%%--------------------------------------------------------------------
+add_mysql_conn(Conn, ConnList) when is_record(Conn, mysql_connection), is_list(ConnList) ->
+ erlang:monitor(process, Conn#mysql_connection.conn_pid),
+ {ok, [Conn | ConnList]}.
+
+%%--------------------------------------------------------------------
+%% 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()
+%%--------------------------------------------------------------------
+remove_mysql_connection_using_pid(Pid, [#mysql_connection{conn_pid = Pid} = H | T], Res) ->
+ {ok, H, lists:reverse(Res) ++ T};
+remove_mysql_connection_using_pid(Pid, [H | T], Res) when is_record(H, mysql_connection) ->
+ remove_mysql_connection_using_pid(Pid, T, [H | Res]);
+remove_mysql_connection_using_pid(_Pid, [], _Res) ->
+ nomatch.
+
+%%--------------------------------------------------------------------
+%% 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
+%%--------------------------------------------------------------------
+get_next_mysql_connection_for_id(Id, ConnList) ->
+ get_next_mysql_connection_for_id(Id, ConnList, []).
+
+get_next_mysql_connection_for_id(Id, [#mysql_connection{id = Id} = H | T], Res) ->
+ {ok, H, lists:reverse(Res) ++ T};
+get_next_mysql_connection_for_id(Id, [H | T], Res) when is_record(H, mysql_connection) ->
+ get_next_mysql_connection_for_id(Id, T, [H | Res]);
+get_next_mysql_connection_for_id(_Id, [], _Res) ->
+ nomatch.
+
+%%--------------------------------------------------------------------
+%% 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
+%%--------------------------------------------------------------------
+start_reconnect(Conn, LogFun) when is_record(Conn, mysql_connection) ->
+ Pid = spawn(fun () ->
+ reconnect_loop(Conn#mysql_connection{conn_pid = undefined}, LogFun, 0)
+ end),
+ {Id, Host, Port} = {Conn#mysql_connection.id, Conn#mysql_connection.host, Conn#mysql_connection.port},
+ log(LogFun, debug, "mysql: Started pid ~p to try and reconnect to ~p:~s:~p (replacing "
+ "connection with pid ~p)", [Pid, Id, Host, Port, Conn#mysql_connection.conn_pid]),
+ ok.
+
+%%--------------------------------------------------------------------
+%% 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
+%%--------------------------------------------------------------------
+reconnect_loop(Conn, LogFun, N) when is_record(Conn, mysql_connection) ->
+ {Id, Host, Port} = {Conn#mysql_connection.id, Conn#mysql_connection.host, Conn#mysql_connection.port},
+ case connect(Id,
+ Host,
+ Port,
+ Conn#mysql_connection.user,
+ Conn#mysql_connection.password,
+ Conn#mysql_connection.database,
+ Conn#mysql_connection.reconnect) of
+ {ok, ConnPid} ->
+ log(LogFun, debug, "mysql_reconnect: Managed to reconnect to ~p:~s:~p (connection pid ~p)",
+ [Id, Host, Port, ConnPid]),
+ ok;
+ {error, Reason} ->
+ %% log every once in a while
+ NewN = case N of
+ 10 ->
+ log(LogFun, debug, "mysql_reconnect: Still unable to connect to ~p:~s:~p (~p)",
+ [Id, Host, Port, Reason]),
+ 0;
+ _ ->
+ N + 1
+ end,
+ %% sleep between every unsuccessfull attempt
+ timer:sleep(20 * 1000),
+ reconnect_loop(Conn, LogFun, NewN)
+ end.
diff --git a/web/api/flukso/deps/mysql/src/mysql.hrl b/web/api/flukso/deps/mysql/src/mysql.hrl
new file mode 100644
index 0000000..aee5611
--- /dev/null
+++ b/web/api/flukso/deps/mysql/src/mysql.hrl
@@ -0,0 +1,6 @@
+%% MySQL result record:
+-record(mysql_result,
+ {fieldinfo=[],
+ rows=[],
+ affectedrows=0,
+ error=""}).
diff --git a/web/api/flukso/deps/mysql/src/mysql_app.erl b/web/api/flukso/deps/mysql/src/mysql_app.erl
new file mode 100644
index 0000000..46e629e
--- /dev/null
+++ b/web/api/flukso/deps/mysql/src/mysql_app.erl
@@ -0,0 +1,18 @@
+-module(mysql_app).
+
+-export([start/0, stop/0]).
+-behavior(application).
+-export([start/2, stop/1]).
+
+
+start() ->
+ application:start(mysql).
+
+start(_Type, _Args) ->
+ mysql_sup:start_link().
+
+stop() ->
+ application:stop(mysql).
+
+stop(_State) ->
+ ok.
diff --git a/web/api/flukso/deps/mysql/src/mysql_auth.erl b/web/api/flukso/deps/mysql/src/mysql_auth.erl
new file mode 100644
index 0000000..bfa226d
--- /dev/null
+++ b/web/api/flukso/deps/mysql/src/mysql_auth.erl
@@ -0,0 +1,194 @@
+%%%-------------------------------------------------------------------
+%%% File : mysql_auth.erl
+%%% Author : Fredrik Thulin
+%%% Descrip.: MySQL client authentication functions.
+%%% Created : 4 Aug 2005 by Fredrik Thulin
+%%%
+%%% Note : All MySQL code was written by Magnus Ahltorp, originally
+%%% in the file mysql.erl - I just moved it here.
+%%%
+%%% Copyright (c) 2001-2004 Kungliga Tekniska Högskolan
+%%% See the file COPYING
+%%%
+%%%-------------------------------------------------------------------
+-module(mysql_auth).
+
+%%--------------------------------------------------------------------
+%% External exports (should only be used by the 'mysql_conn' module)
+%%--------------------------------------------------------------------
+-export([
+ do_old_auth/7,
+ do_new_auth/8
+ ]).
+
+%%--------------------------------------------------------------------
+%% Macros
+%%--------------------------------------------------------------------
+-define(LONG_PASSWORD, 1).
+-define(LONG_FLAG, 4).
+-define(PROTOCOL_41, 512).
+-define(TRANSACTIONS, 8192).
+-define(SECURE_CONNECTION, 32768).
+-define(CONNECT_WITH_DB, 8).
+
+-define(MAX_PACKET_SIZE, 1000000).
+
+%%====================================================================
+%% External functions
+%%====================================================================
+
+%%--------------------------------------------------------------------
+%% 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
+%%--------------------------------------------------------------------
+do_old_auth(Sock, RecvPid, SeqNum, User, Password, Salt1, LogFun) ->
+ Auth = password_old(Password, Salt1),
+ Packet2 = make_auth(User, Auth),
+ do_send(Sock, Packet2, SeqNum, LogFun),
+ mysql_conn:do_recv(LogFun, RecvPid, SeqNum).
+
+%%--------------------------------------------------------------------
+%% 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
+%%--------------------------------------------------------------------
+do_new_auth(Sock, RecvPid, SeqNum, User, Password, Salt1, Salt2, LogFun) ->
+ Auth = password_new(Password, Salt1 ++ Salt2),
+ Packet2 = make_new_auth(User, Auth, none),
+ do_send(Sock, Packet2, SeqNum, LogFun),
+ case mysql_conn:do_recv(LogFun, RecvPid, SeqNum) of
+ {ok, Packet3, SeqNum2} ->
+ case Packet3 of
+ <<254:8>> ->
+ AuthOld = password_old(Password, Salt1),
+ do_send(Sock, <>, SeqNum2 + 1, LogFun),
+ mysql_conn:do_recv(LogFun, RecvPid, SeqNum2 + 1);
+ _ ->
+ {ok, Packet3, SeqNum2}
+ end;
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+%%====================================================================
+%% Internal functions
+%%====================================================================
+
+password_old(Password, Salt) ->
+ {P1, P2} = hash(Password),
+ {S1, S2} = hash(Salt),
+ Seed1 = P1 bxor S1,
+ Seed2 = P2 bxor S2,
+ List = rnd(9, Seed1, Seed2),
+ {L, [Extra]} = lists:split(8, List),
+ list_to_binary(lists:map(fun (E) ->
+ E bxor (Extra - 64)
+ end, L)).
+
+%% part of do_old_auth/4, which is part of mysql_init/4
+make_auth(User, Password) ->
+ Caps = ?LONG_PASSWORD bor ?LONG_FLAG bor ?TRANSACTIONS,
+ Maxsize = 0,
+ UserB = list_to_binary(User),
+ PasswordB = Password,
+ <>.
+
+%% part of do_new_auth/4, which is part of mysql_init/4
+make_new_auth(User, Password, Database) ->
+ DBCaps = case Database of
+ none ->
+ 0;
+ _ ->
+ ?CONNECT_WITH_DB
+ end,
+ Caps = ?LONG_PASSWORD bor ?LONG_FLAG bor ?TRANSACTIONS bor
+ ?PROTOCOL_41 bor ?SECURE_CONNECTION bor DBCaps,
+ Maxsize = ?MAX_PACKET_SIZE,
+ UserB = list_to_binary(User),
+ PasswordL = size(Password),
+ DatabaseB = case Database of
+ none ->
+ <<>>;
+ _ ->
+ list_to_binary(Database)
+ end,
+ <>.
+
+hash(S) ->
+ hash(S, 1345345333, 305419889, 7).
+
+hash([C | S], N1, N2, Add) ->
+ N1_1 = N1 bxor (((N1 band 63) + Add) * C + N1 * 256),
+ N2_1 = N2 + ((N2 * 256) bxor N1_1),
+ Add_1 = Add + C,
+ hash(S, N1_1, N2_1, Add_1);
+hash([], N1, N2, _Add) ->
+ Mask = (1 bsl 31) - 1,
+ {N1 band Mask , N2 band Mask}.
+
+rnd(N, Seed1, Seed2) ->
+ Mod = (1 bsl 30) - 1,
+ rnd(N, [], Seed1 rem Mod, Seed2 rem Mod).
+
+rnd(0, List, _, _) ->
+ lists:reverse(List);
+rnd(N, List, Seed1, Seed2) ->
+ Mod = (1 bsl 30) - 1,
+ NSeed1 = (Seed1 * 3 + Seed2) rem Mod,
+ NSeed2 = (NSeed1 + Seed2 + 33) rem Mod,
+ Float = (float(NSeed1) / float(Mod))*31,
+ Val = trunc(Float)+64,
+ rnd(N - 1, [Val | List], NSeed1, NSeed2).
+
+
+
+dualmap(_F, [], []) ->
+ [];
+dualmap(F, [E1 | R1], [E2 | R2]) ->
+ [F(E1, E2) | dualmap(F, R1, R2)].
+
+bxor_binary(B1, B2) ->
+ list_to_binary(dualmap(fun (E1, E2) ->
+ E1 bxor E2
+ end, binary_to_list(B1), binary_to_list(B2))).
+
+password_new(Password, Salt) ->
+ %% Check for blank password
+ case length(Password) of
+ 0 ->
+ <<>>;
+ _ ->
+ Stage1 = crypto:sha(Password),
+ Stage2 = crypto:sha(Stage1),
+ Res = crypto:sha_final(
+ crypto:sha_update(
+ crypto:sha_update(crypto:sha_init(), Salt),
+ Stage2)
+ ),
+ bxor_binary(Res, Stage1)
+ end.
+
+do_send(Sock, Packet, Num, LogFun) ->
+ mysql:log(LogFun, debug, "mysql_auth send packet ~p: ~p", [Num, Packet]),
+ Data = <<(size(Packet)):24/little, Num:8, Packet/binary>>,
+ gen_tcp:send(Sock, Data).
diff --git a/web/api/flukso/deps/mysql/src/mysql_conn.erl b/web/api/flukso/deps/mysql/src/mysql_conn.erl
new file mode 100644
index 0000000..414890f
--- /dev/null
+++ b/web/api/flukso/deps/mysql/src/mysql_conn.erl
@@ -0,0 +1,656 @@
+%%%-------------------------------------------------------------------
+%%% File : mysql_conn.erl
+%%% Author : Fredrik Thulin
+%%% Descrip.: MySQL connection handler, handles de-framing of messages
+%%% received by the MySQL receiver process.
+%%% Created : 5 Aug 2005 by Fredrik Thulin
+%%% Modified: 11 Jan 2006 by Mickael Remond
+%%%
+%%% Note : All MySQL code was written by Magnus Ahltorp, originally
+%%% in the file mysql.erl - I just moved it here.
+%%%
+%%% Copyright (c) 2001-2004 Kungliga Tekniska Högskolan
+%%% See the file COPYING
+%%%
+%%%
+%%% This module handles a single connection to a single MySQL server.
+%%% You can use it stand-alone, or through the 'mysql' module if you
+%%% want to have more than one connection to the server, or
+%%% connections to different servers.
+%%%
+%%% To use it stand-alone, set up the connection with
+%%%
+%%% {ok, Pid} = mysql_conn:start(Host, Port, User, Password,
+%%% Database, LogFun)
+%%%
+%%% Host = string()
+%%% Port = integer()
+%%% User = string()
+%%% Password = string()
+%%% Database = string()
+%%% LogFun = undefined | (gives logging to console)
+%%% function() of arity 3 (Level, Fmt, Args)
+%%%
+%%% Note: In stand-alone mode you have to start Erlang crypto application by
+%%% yourself with crypto:start()
+%%%
+%%% and then make MySQL querys with
+%%%
+%%% Result = mysql_conn:fetch(Pid, Query, self())
+%%%
+%%% 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()
+%%%-------------------------------------------------------------------
+
+-module(mysql_conn).
+
+%%--------------------------------------------------------------------
+%% External exports
+%%--------------------------------------------------------------------
+-export([start/6,
+ start_link/6,
+ fetch/3,
+ fetch/4,
+ squery/4
+ ]).
+
+%%--------------------------------------------------------------------
+%% External exports (should only be used by the 'mysql_auth' module)
+%%--------------------------------------------------------------------
+-export([do_recv/3
+ ]).
+
+-include("mysql.hrl").
+-record(state, {
+ mysql_version,
+ log_fun,
+ recv_pid,
+ socket,
+ data
+ }).
+
+-define(SECURE_CONNECTION, 32768).
+-define(MYSQL_QUERY_OP, 3).
+-define(DEFAULT_STANDALONE_TIMEOUT, 5000).
+-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
+
+%%====================================================================
+%% External functions
+%%====================================================================
+
+%%--------------------------------------------------------------------
+%% 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()
+%%--------------------------------------------------------------------
+start(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(fun () ->
+ init(Host, Port, User, Password, Database, LogFun, ConnPid)
+ end),
+ 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,
+ receive
+ {mysql_conn, Pid, ok} ->
+ {ok, Pid};
+ {mysql_conn, Pid, {error, Reason}} ->
+ {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 ->
+ {error, "timed out"}
+ end.
+
+%%--------------------------------------------------------------------
+%% 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()
+%%--------------------------------------------------------------------
+
+fetch(Pid, Query, From) ->
+ squery(Pid, Query, From, []).
+fetch(Pid, Query, From, Timeout) ->
+ squery(Pid, Query, From, [{timeout, Timeout}]).
+
+squery(Pid, Query, From, Options) when is_pid(Pid), is_list(Query) ->
+ Self = self(),
+ Timeout = get_option(timeout, Options, ?DEFAULT_STANDALONE_TIMEOUT),
+ Pid ! {fetch, Query, From, Options},
+ case From of
+ Self ->
+ %% We are not using a mysql_dispatcher, await the response
+ receive
+ {fetch_result, Pid, Result} ->
+ Result
+ after Timeout ->
+ {error, "query timed out"}
+ end;
+ _ ->
+ %% From is gen_server From, Pid will do gen_server:reply() when it has an answer
+ ok
+ end.
+
+%%--------------------------------------------------------------------
+%% 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()
+%%
+%% Note : Only to be used externally by the 'mysql_auth' module.
+%%--------------------------------------------------------------------
+do_recv(LogFun, RecvPid, SeqNum) when is_function(LogFun); LogFun == undefined, SeqNum == undefined ->
+ receive
+ {mysql_recv, RecvPid, data, Packet, Num} ->
+ %%mysql:log(LogFun, debug, "mysql_conn: recv packet ~p: ~p", [Num, Packet]),
+ {ok, Packet, Num};
+ {mysql_recv, RecvPid, closed, _E} ->
+ {error, "mysql_recv: socket was closed"}
+ end;
+do_recv(LogFun, RecvPid, SeqNum) when is_function(LogFun); LogFun == undefined, is_integer(SeqNum) ->
+ ResponseNum = SeqNum + 1,
+ receive
+ {mysql_recv, RecvPid, data, Packet, ResponseNum} ->
+ %%mysql:log(LogFun, debug, "mysql_conn: recv packet ~p: ~p", [ResponseNum, Packet]),
+ {ok, Packet, ResponseNum};
+ {mysql_recv, RecvPid, closed, _E} ->
+ {error, "mysql_recv: socket was closed"}
+ end.
+
+
+%%====================================================================
+%% Internal functions
+%%====================================================================
+
+%%--------------------------------------------------------------------
+%% 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
+%%--------------------------------------------------------------------
+init(Host, Port, User, Password, Database, LogFun, Parent) ->
+ case mysql_recv:start_link(Host, Port, LogFun, self()) of
+ {ok, RecvPid, Sock} ->
+ case mysql_init(Sock, RecvPid, User, Password, LogFun) of
+ {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} ->
+ Parent ! {mysql_conn, self(), ok},
+ State = #state{mysql_version=Version,
+ recv_pid = RecvPid,
+ socket = Sock,
+ log_fun = LogFun,
+ data = <<>>
+ },
+ loop(State)
+ end;
+ {error, _Reason} ->
+ Parent ! {mysql_conn, self(), {error, login_failed}}
+ end;
+ E ->
+ mysql:log(LogFun, error, "mysql_conn: Failed connecting to ~p:~p : ~p",
+ [Host, Port, E]),
+ Parent ! {mysql_conn, self(), {error, connect_failed}}
+ end.
+
+%%--------------------------------------------------------------------
+%% 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
+%%--------------------------------------------------------------------
+loop(State) ->
+ RecvPid = State#state.recv_pid,
+ receive
+ {fetch, Query, GenSrvFrom, Options} ->
+ %% GenSrvFrom is either a gen_server:call/3 From term(), or a pid if no
+ %% gen_server was used to make the query
+ Res = do_query(State, Query, Options),
+ case is_pid(GenSrvFrom) of
+ true ->
+ %% The query was not sent using gen_server mechanisms
+ GenSrvFrom ! {fetch_result, self(), Res};
+ false ->
+ gen_server:reply(GenSrvFrom, Res)
+ end,
+ loop(State);
+ {mysql_recv, RecvPid, data, Packet, Num} ->
+ mysql:log(State#state.log_fun, error, "mysql_conn: Received MySQL data when not expecting any "
+ "(num ~p) - ignoring it", [Num]),
+ mysql:log(State#state.log_fun, error, "mysql_conn: Unexpected MySQL data (num ~p) :~n~p",
+ [Num, Packet]),
+ loop(State);
+ Unknown ->
+ 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]),
+ error
+ end.
+
+%%--------------------------------------------------------------------
+%% 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()
+%%--------------------------------------------------------------------
+mysql_init(Sock, RecvPid, User, Password, LogFun) ->
+ case do_recv(LogFun, RecvPid, undefined) of
+ {ok, Packet, InitSeqNum} ->
+ {Version, Salt1, Salt2, Caps} = greeting(Packet, LogFun),
+ AuthRes =
+ case Caps band ?SECURE_CONNECTION of
+ ?SECURE_CONNECTION ->
+ mysql_auth:do_new_auth(Sock, RecvPid, InitSeqNum + 1, User, Password, Salt1, Salt2, LogFun);
+ _ ->
+ mysql_auth:do_old_auth(Sock, RecvPid, InitSeqNum + 1, User, Password, Salt1, LogFun)
+ end,
+ case AuthRes of
+ {ok, <<0:8, _Rest/binary>>, _RecvNum} ->
+ {ok,Version};
+ {ok, <<255:8, Code:16/little, Message/binary>>, _RecvNum} ->
+ mysql:log(LogFun, error, "mysql_conn: init error ~p: ~p~n", [Code, binary_to_list(Message)]),
+ {error, binary_to_list(Message)};
+ {ok, RecvPacket, _RecvNum} ->
+ mysql:log(LogFun, error, "mysql_conn: init unknown error ~p~n", [binary_to_list(RecvPacket)]),
+ {error, binary_to_list(RecvPacket)};
+ {error, Reason} ->
+ mysql:log(LogFun, error, "mysql_conn: init failed receiving data : ~p~n", [Reason]),
+ {error, Reason}
+ end;
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+%% part of mysql_init/4
+greeting(Packet, LogFun) ->
+ <> = Packet,
+ {Version, Rest2} = asciz(Rest),
+ <<_TreadID:32/little, Rest3/binary>> = Rest2,
+ {Salt, Rest4} = asciz(Rest3),
+ <> = Rest4,
+ <> = Rest5,
+ {Salt2, _Rest7} = asciz(Rest6),
+ 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}.
+
+%% part of greeting/2
+asciz(Data) when binary(Data) ->
+ mysql:asciz_binary(Data, []);
+asciz(Data) when list(Data) ->
+ {String, [0 | Rest]} = lists:splitwith(fun (C) ->
+ C /= 0
+ end, Data),
+ {String, Rest}.
+
+%%--------------------------------------------------------------------
+%% 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()
+%%--------------------------------------------------------------------
+get_query_response(LogFun, RecvPid, Version, Options) ->
+ case do_recv(LogFun, RecvPid, undefined) of
+ {ok, <>, _} ->
+ case Fieldcount of
+ 0 ->
+ %% No Tabular data
+ <> = Rest,
+ {updated, #mysql_result{affectedrows=AffectedRows}};
+ 255 ->
+ <<_Code:16/little, Message/binary>> = Rest,
+ {error, #mysql_result{error=binary_to_list(Message)}};
+ _ ->
+ %% Tabular data received
+ case get_fields(LogFun, RecvPid, [], Version) of
+ {ok, Fields} ->
+ ResultType = get_option(result_type, Options, ?DEFAULT_RESULT_TYPE),
+ case get_rows(Fieldcount, LogFun, RecvPid, ResultType, []) of
+ {ok, Rows} ->
+ {data, #mysql_result{fieldinfo=Fields, rows=Rows}};
+ {error, Reason} ->
+ {error, #mysql_result{error=Reason}}
+ end;
+ {error, Reason} ->
+ {error, #mysql_result{error=Reason}}
+ end
+ end;
+ {error, Reason} ->
+ {error, #mysql_result{error=Reason}}
+ end.
+
+%%--------------------------------------------------------------------
+%% 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()
+%%--------------------------------------------------------------------
+%% Support for MySQL 4.0.x:
+get_fields(LogFun, RecvPid, Res, ?MYSQL_4_0) ->
+ 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)};
+ _ ->
+ {Table, Rest} = get_with_length(Packet),
+ {Field, Rest2} = get_with_length(Rest),
+ {LengthB, Rest3} = get_with_length(Rest2),
+ LengthL = size(LengthB) * 8,
+ <> = LengthB,
+ {Type, Rest4} = get_with_length(Rest3),
+ {_Flags, _Rest5} = get_with_length(Rest4),
+ This = {binary_to_list(Table),
+ binary_to_list(Field),
+ Length,
+ %% TODO: Check on MySQL 4.0 if types are specified
+ %% using the same 4.1 formalism and could
+ %% be expanded to atoms:
+ binary_to_list(Type)},
+ 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)
+ end;
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+%%--------------------------------------------------------------------
+%% 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()]
+%%--------------------------------------------------------------------
+get_rows(N, LogFun, RecvPid, ResultType, Res) ->
+ case do_recv(LogFun, RecvPid, undefined) of
+ {ok, Packet, _Num} ->
+ case Packet of
+ <<254:8, Rest/binary>> when size(Rest) < 8 ->
+ {ok, lists:reverse(Res)};
+ _ ->
+ {ok, This} = get_row(N, Packet, ResultType, []),
+ get_rows(N, LogFun, RecvPid, ResultType, [This | Res])
+ end;
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+
+%% part of get_rows/4
+get_row(0, _Data, _ResultType, Res) ->
+ {ok, lists:reverse(Res)};
+get_row(N, Data, ResultType, Res) ->
+ {Col, Rest} = get_with_length(Data),
+ This = case Col of
+ null ->
+ null;
+ _ ->
+ if
+ ResultType == list ->
+ binary_to_list(Col);
+ ResultType == binary ->
+ Col
+ end
+ end,
+ get_row(N - 1, Rest, ResultType, [This | Res]).
+
+get_with_length(<<251:8, Rest/binary>>) ->
+ {null, Rest};
+get_with_length(<<252:8, Length:16/little, Rest/binary>>) ->
+ split_binary(Rest, Length);
+get_with_length(<<253:8, Length:24/little, Rest/binary>>) ->
+ split_binary(Rest, Length);
+get_with_length(<<254:8, Length:64/little, Rest/binary>>) ->
+ split_binary(Rest, Length);
+get_with_length(<>) when Length < 251 ->
+ split_binary(Rest, Length).
+
+%%--------------------------------------------------------------------
+%% 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}
+%%--------------------------------------------------------------------
+do_query(State, Query, Options) when is_record(State, state) ->
+ do_query(State#state.socket,
+ State#state.recv_pid,
+ State#state.log_fun,
+ Query,
+ State#state.mysql_version,
+ Options
+ ).
+
+do_query(Sock, RecvPid, LogFun, Query, Version, Options) when is_pid(RecvPid),
+ is_list(Query) ->
+ Packet = list_to_binary([?MYSQL_QUERY_OP, Query]),
+ case do_send(Sock, Packet, 0, LogFun) of
+ ok ->
+ get_query_response(LogFun, RecvPid, Version, Options);
+ {error, Reason} ->
+ Msg = io_lib:format("Failed sending data on socket : ~p", [Reason]),
+ {error, Msg}
+ end.
+
+%%--------------------------------------------------------------------
+%% 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
+%%--------------------------------------------------------------------
+do_send(Sock, Packet, SeqNum, _LogFun) when is_binary(Packet), is_integer(SeqNum) ->
+ Data = <<(size(Packet)):24/little, SeqNum:8, Packet/binary>>,
+ %%mysql:log(LogFun, debug, "mysql_conn: send packet ~p: ~p", [SeqNum, Data]),
+ gen_tcp:send(Sock, Data).
+
+%%--------------------------------------------------------------------
+%% 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 --git a/web/api/flukso/deps/mysql/src/mysql_recv.erl b/web/api/flukso/deps/mysql/src/mysql_recv.erl
new file mode 100644
index 0000000..d770d9b
--- /dev/null
+++ b/web/api/flukso/deps/mysql/src/mysql_recv.erl
@@ -0,0 +1,161 @@
+%%%-------------------------------------------------------------------
+%%% File : mysql_recv.erl
+%%% Author : Fredrik Thulin
+%%% Descrip.: Handles data being received on a MySQL socket. Decodes
+%%% per-row framing and sends each row to parent.
+%%%
+%%% Created : 4 Aug 2005 by Fredrik Thulin
+%%%
+%%% Note : All MySQL code was written by Magnus Ahltorp, originally
+%%% in the file mysql.erl - I just moved it here.
+%%%
+%%% Copyright (c) 2001-2004 Kungliga Tekniska Högskolan
+%%% See the file COPYING
+%%%
+%%% Signals this receiver process can send to it's parent
+%%% (the parent is a mysql_conn connection handler) :
+%%%
+%%% {mysql_recv, self(), data, Packet, Num}
+%%% {mysql_recv, self(), closed, {error, Reason}}
+%%% {mysql_recv, self(), closed, normal}
+%%%
+%%% Internally (from inside init/4 to start_link/4) the
+%%% following signals may be sent to the parent process :
+%%%
+%%% {mysql_recv, self(), init, {ok, Sock}}
+%%% {mysql_recv, self(), init, {error, E}}
+%%%
+%%%-------------------------------------------------------------------
+-module(mysql_recv).
+
+%%--------------------------------------------------------------------
+%% External exports (should only be used by the 'mysql_conn' module)
+%%--------------------------------------------------------------------
+-export([start_link/4
+ ]).
+
+-record(state, {
+ socket,
+ parent,
+ log_fun,
+ data
+ }).
+
+-define(SECURE_CONNECTION, 32768).
+-define(CONNECT_TIMEOUT, 5000).
+
+%%====================================================================
+%% External functions
+%%====================================================================
+
+%%--------------------------------------------------------------------
+%% 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()
+%%--------------------------------------------------------------------
+start_link(Host, Port, LogFun, Parent) when is_list(Host), is_integer(Port) ->
+ RecvPid =
+ spawn_link(fun () ->
+ init(Host, Port, LogFun, Parent)
+ end),
+ %% wait for the socket from the spawned pid
+ receive
+ {mysql_recv, RecvPid, init, {error, E}} ->
+ {error, E};
+ {mysql_recv, RecvPid, init, {ok, Socket}} ->
+ {ok, RecvPid, Socket}
+ after ?CONNECT_TIMEOUT ->
+ catch exit(RecvPid, kill),
+ {error, "timeout"}
+ end.
+
+
+
+%%====================================================================
+%% Internal functions
+%%====================================================================
+
+%%--------------------------------------------------------------------
+%% 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
+%%--------------------------------------------------------------------
+init(Host, Port, LogFun, Parent) ->
+ case gen_tcp:connect(Host, Port, [binary, {packet, 0}]) of
+ {ok, Sock} ->
+ Parent ! {mysql_recv, self(), init, {ok, Sock}},
+ State = #state{socket = Sock,
+ parent = Parent,
+ log_fun = LogFun,
+ data = <<>>
+ },
+ loop(State);
+ E ->
+ mysql:log(LogFun, error, "mysql_recv: Failed connecting to ~p:~p : ~p",
+ [Host, Port, E]),
+ Msg = lists:flatten(io_lib:format("connect failed : ~p", [E])),
+ Parent ! {mysql_recv, self(), init, {error, Msg}}
+ end.
+
+%%--------------------------------------------------------------------
+%% 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
+%%--------------------------------------------------------------------
+loop(State) ->
+ Sock = State#state.socket,
+ receive
+ {tcp, Sock, InData} ->
+ NewData = list_to_binary([State#state.data, InData]),
+ %% send data to parent if we have enough data
+ Rest = sendpacket(State#state.parent, NewData),
+ loop(State#state{data = Rest});
+ {tcp_error, Sock, Reason} ->
+ mysql:log(State#state.log_fun, error, "mysql_recv: Socket ~p closed : ~p", [Sock, Reason]),
+ State#state.parent ! {mysql_recv, self(), closed, {error, Reason}},
+ error;
+ {tcp_closed, Sock} ->
+ mysql:log(State#state.log_fun, debug, "mysql_recv: Socket ~p closed", [Sock]),
+ State#state.parent ! {mysql_recv, self(), closed, normal},
+ error
+ end.
+
+%%--------------------------------------------------------------------
+%% 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()
+%%--------------------------------------------------------------------
+%% send data to parent if we have enough data
+sendpacket(Parent, Data) ->
+ case Data of
+ <> ->
+ if
+ Length =< size(D) ->
+ {Packet, Rest} = split_binary(D, Length),
+ Parent ! {mysql_recv, self(), data, Packet, Num},
+ sendpacket(Parent, Rest);
+ true ->
+ Data
+ end;
+ _ ->
+ Data
+ end.
diff --git a/web/api/flukso/deps/mysql/src/mysql_sup.erl b/web/api/flukso/deps/mysql/src/mysql_sup.erl
new file mode 100644
index 0000000..06c35a9
--- /dev/null
+++ b/web/api/flukso/deps/mysql/src/mysql_sup.erl
@@ -0,0 +1,22 @@
+-module(mysql_sup).
+
+-export([start_link/0]).
+
+-behaviour(supervisor).
+
+-export([init/1]).
+
+%% @spec start_link() -> Result
+%% Result = {ok,Pid} | ignore | {error,Error}
+%% Pid = pid()
+%% Error = {already_started,Pid} | shutdown | term()
+start_link() ->
+ supervisor:start_link(mysql_sup, []).
+
+init([]) ->
+ MysqlConfig = [pool, "localhost", "flukso", "your_mysql_password_here", "flukso"],
+ Mysql = {mysql,
+ {mysql, start_link, MysqlConfig},
+ permanent, 3000, worker, [mysql]},
+ Processes = [Mysql],
+ {ok, {{one_for_one, 5, 10}, Processes}}.
diff --git a/web/api/flukso/deps/mysql/support/include.mk b/web/api/flukso/deps/mysql/support/include.mk
new file mode 100644
index 0000000..065e409
--- /dev/null
+++ b/web/api/flukso/deps/mysql/support/include.mk
@@ -0,0 +1,39 @@
+## -*- makefile -*-
+
+######################################################################
+## Erlang
+
+ERL := erl
+ERLC := $(ERL)c
+
+INCLUDE_DIRS := ../include $(wildcard ../deps/*/include)
+EBIN_DIRS := $(wildcard ../deps/*/ebin)
+ERLC_FLAGS := -W $(INCLUDE_DIRS:../%=-I ../%) $(EBIN_DIRS:%=-pa %)
+
+ifndef no_debug_info
+ ERLC_FLAGS += +debug_info
+endif
+
+ifdef debug
+ ERLC_FLAGS += -Ddebug
+endif
+
+EBIN_DIR := ../ebin
+EMULATOR := beam
+
+ERL_SOURCES := $(wildcard *.erl)
+ERL_HEADERS := $(wildcard *.hrl) $(wildcard ../include/*.hrl)
+ERL_OBJECTS := $(ERL_SOURCES:%.erl=$(EBIN_DIR)/%.$(EMULATOR))
+ERL_OBJECTS_LOCAL := $(ERL_SOURCES:%.erl=./%.$(EMULATOR))
+APP_FILES := $(wildcard *.app)
+EBIN_FILES = $(ERL_OBJECTS) $(APP_FILES:%.app=../ebin/%.app)
+MODULES = $(ERL_SOURCES:%.erl=%)
+
+../ebin/%.app: %.app
+ cp $< $@
+
+$(EBIN_DIR)/%.$(EMULATOR): %.erl
+ $(ERLC) $(ERLC_FLAGS) -o $(EBIN_DIR) $<
+
+./%.$(EMULATOR): %.erl
+ $(ERLC) $(ERLC_FLAGS) -o . $<
diff --git a/web/api/flukso/src/flukso.app b/web/api/flukso/src/flukso.app
index b139907..f88de78 100644
--- a/web/api/flukso/src/flukso.app
+++ b/web/api/flukso/src/flukso.app
@@ -11,4 +11,4 @@
{registered, []},
{mod, {flukso_app, []}},
{env, []},
- {applications, [kernel, stdlib, crypto]}]}.
+ {applications, [kernel, stdlib, crypto, erlrrd, mysql, webmachine]}]}.
diff --git a/web/api/flukso/src/flukso.erl b/web/api/flukso/src/flukso.erl
index 698849e..701351b 100644
--- a/web/api/flukso/src/flukso.erl
+++ b/web/api/flukso/src/flukso.erl
@@ -19,8 +19,9 @@ ensure_started(App) ->
%% @doc Starts the app for inclusion in a supervisor tree
start_link() ->
flukso_deps:ensure(),
- ensure_started(erlrrd),
ensure_started(crypto),
+ ensure_started(erlrrd),
+ ensure_started(mysql),
ensure_started(webmachine),
flukso_sup:start_link().
@@ -28,17 +29,18 @@ start_link() ->
%% @doc Start the flukso server.
start() ->
flukso_deps:ensure(),
- ensure_started(erlrrd),
ensure_started(crypto),
+ ensure_started(erlrrd),
+ ensure_started(mysql),
ensure_started(webmachine),
- application:start(flukso).
+ application:start(flukso).
%% @spec stop() -> ok
%% @doc Stop the flukso server.
stop() ->
Res = application:stop(flukso),
- application:stop(erlrrd),
application:stop(webmachine),
+ application:stop(mysql),
+ application:stop(erlrrd),
application:stop(crypto),
Res.
-