|
@@ -16,74 +16,90 @@
|
|
%% You should have received a copy of the GNU Lesser General Public License
|
|
%% You should have received a copy of the GNU Lesser General Public License
|
|
%% along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
%% along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
|
|
-%% @doc MySQL/OTP. FIXME: Documentation of value representation, examples
|
|
|
|
-%% and more.
|
|
|
|
|
|
+%% @doc API for communicating with MySQL databases.
|
|
%%
|
|
%%
|
|
-%% This documentation is written with `edoc' as part of the source code and thus
|
|
|
|
-%% is covered by GPL 3 or later. See the LICENSE file or find GPL 3 on
|
|
|
|
-%% <a href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>.
|
|
|
|
|
|
+%% Most of the functions are wrappers for `gen_server' calls. The
|
|
|
|
+%% `connection()' type is the same as returned by `gen_server:start_link/2,3'.
|
|
-module(mysql).
|
|
-module(mysql).
|
|
|
|
|
|
-export([start_link/1, query/2, execute/3, prepare/2, warning_count/1,
|
|
-export([start_link/1, query/2, execute/3, prepare/2, warning_count/1,
|
|
affected_rows/1, insert_id/1, transaction/2, transaction/3]).
|
|
affected_rows/1, insert_id/1, transaction/2, transaction/3]).
|
|
|
|
|
|
|
|
+-export_type([connection/0]).
|
|
|
|
+
|
|
|
|
+%% A connection is a ServerRef as in gen_server:call/2,3.
|
|
|
|
+-type connection() :: Name :: atom() |
|
|
|
|
+ {Name :: atom(), Node :: atom()} |
|
|
|
|
+ {global, GlobalName :: term()} |
|
|
|
|
+ {via, Module :: atom(), ViaName :: term()} |
|
|
|
|
+ pid().
|
|
|
|
+
|
|
%% MySQL error with the codes and message returned from the server.
|
|
%% MySQL error with the codes and message returned from the server.
|
|
-type reason() :: {Code :: integer(), SQLState :: binary(),
|
|
-type reason() :: {Code :: integer(), SQLState :: binary(),
|
|
Message :: binary()}.
|
|
Message :: binary()}.
|
|
|
|
|
|
-%% @doc Starts a connection process and connects to a database. To disconnect
|
|
|
|
-%% do `exit(Pid, normal)'.
|
|
|
|
|
|
+%% @doc Starts a connection gen_server process and connects to a database. To
|
|
|
|
+%% disconnect just do `exit(Pid, normal)'.
|
|
%%
|
|
%%
|
|
%% This is just a wrapper for `gen_server:start_link(mysql_connection, Options,
|
|
%% This is just a wrapper for `gen_server:start_link(mysql_connection, Options,
|
|
%% [])'. If you need to specify gen_server options, use gen_server:start_link/3
|
|
%% [])'. If you need to specify gen_server options, use gen_server:start_link/3
|
|
%% directly.
|
|
%% directly.
|
|
|
|
+start_link(Opts) ->
|
|
|
|
+ gen_server:start_link(mysql_connection, Opts, []).
|
|
-spec start_link(Options) -> {ok, pid()} | ignore | {error, term()}
|
|
-spec start_link(Options) -> {ok, pid()} | ignore | {error, term()}
|
|
when Options :: [Option],
|
|
when Options :: [Option],
|
|
Option :: {host, iodata()} | {port, integer()} | {user, iodata()} |
|
|
Option :: {host, iodata()} | {port, integer()} | {user, iodata()} |
|
|
{password, iodata()} | {database, iodata()}.
|
|
{password, iodata()} | {database, iodata()}.
|
|
-start_link(Opts) ->
|
|
|
|
- gen_server:start_link(mysql_connection, Opts, []).
|
|
|
|
|
|
|
|
--spec query(Conn, Query) -> ok | {ok, Fields, Rows} | {error, Reason}
|
|
|
|
- when Conn :: pid(),
|
|
|
|
|
|
+%% @doc Executes a query.
|
|
|
|
+-spec query(Conn, Query) -> ok | {ok, ColumnNames, Rows} | {error, Reason}
|
|
|
|
+ when Conn :: connection(),
|
|
Query :: iodata(),
|
|
Query :: iodata(),
|
|
- Fields :: [binary()],
|
|
|
|
|
|
+ ColumnNames :: [binary()],
|
|
Rows :: [[term()]],
|
|
Rows :: [[term()]],
|
|
Reason :: reason().
|
|
Reason :: reason().
|
|
query(Conn, Query) ->
|
|
query(Conn, Query) ->
|
|
gen_server:call(Conn, {query, Query}).
|
|
gen_server:call(Conn, {query, Query}).
|
|
|
|
|
|
%% @doc Executes a prepared statement.
|
|
%% @doc Executes a prepared statement.
|
|
|
|
+%% @see prepare/2
|
|
execute(Conn, StatementId, Args) ->
|
|
execute(Conn, StatementId, Args) ->
|
|
gen_server:call(Conn, {execute, StatementId, Args}).
|
|
gen_server:call(Conn, {execute, StatementId, Args}).
|
|
|
|
|
|
--spec prepare(Conn :: pid(), Query :: iodata()) ->
|
|
|
|
|
|
+%% @doc Creates a prepared statement from the passed query.
|
|
|
|
+%% @see execute/3
|
|
|
|
+-spec prepare(Conn :: connection(), Query :: iodata()) ->
|
|
{ok, StatementId :: integer()} | {error, Reason :: reason()}.
|
|
{ok, StatementId :: integer()} | {error, Reason :: reason()}.
|
|
prepare(Conn, Query) ->
|
|
prepare(Conn, Query) ->
|
|
gen_server:call(Conn, {prepare, Query}).
|
|
gen_server:call(Conn, {prepare, Query}).
|
|
|
|
|
|
--spec warning_count(pid()) -> integer().
|
|
|
|
|
|
+%% @doc Returns the number of warnings generated by the last query/2 or
|
|
|
|
+%% execute/3 calls.
|
|
|
|
+-spec warning_count(connection()) -> integer().
|
|
warning_count(Conn) ->
|
|
warning_count(Conn) ->
|
|
gen_server:call(Conn, warning_count).
|
|
gen_server:call(Conn, warning_count).
|
|
|
|
|
|
--spec affected_rows(pid()) -> integer().
|
|
|
|
|
|
+%% @doc Returns the number of inserted, updated and deleted rows of the last
|
|
|
|
+%% executed query or prepared statement.
|
|
|
|
+-spec affected_rows(connection()) -> integer().
|
|
affected_rows(Conn) ->
|
|
affected_rows(Conn) ->
|
|
gen_server:call(Conn, affected_rows).
|
|
gen_server:call(Conn, affected_rows).
|
|
|
|
|
|
--spec insert_id(pid()) -> integer().
|
|
|
|
|
|
+%% @doc Returns the last insert-id.
|
|
|
|
+-spec insert_id(connection()) -> integer().
|
|
insert_id(Conn) ->
|
|
insert_id(Conn) ->
|
|
gen_server:call(Conn, insert_id).
|
|
gen_server:call(Conn, insert_id).
|
|
|
|
|
|
%% @doc This function executes the functional object Fun as a transaction.
|
|
%% @doc This function executes the functional object Fun as a transaction.
|
|
-%% @see transaction/2
|
|
|
|
--spec transaction(pid(), fun()) -> {atomic, term()} | {aborted, term()}.
|
|
|
|
|
|
+%% @see transaction/3
|
|
|
|
+-spec transaction(connection(), fun()) -> {atomic, term()} | {aborted, term()}.
|
|
transaction(Conn, Fun) ->
|
|
transaction(Conn, Fun) ->
|
|
transaction(Conn, Fun, []).
|
|
transaction(Conn, Fun, []).
|
|
|
|
|
|
%% @doc This function executes the functional object Fun with arguments Args as
|
|
%% @doc This function executes the functional object Fun with arguments Args as
|
|
%% a transaction.
|
|
%% a transaction.
|
|
%%
|
|
%%
|
|
-%% The semantics are the sames as for mnesia's transactions.
|
|
|
|
|
|
+%% The semantics are the same as for mnesia's transactions.
|
|
%%
|
|
%%
|
|
%% The Fun must be a function and Args must be a list with the same length
|
|
%% The Fun must be a function and Args must be a list with the same length
|
|
%% as the arity of Fun.
|
|
%% as the arity of Fun.
|
|
@@ -95,9 +111,28 @@ transaction(Conn, Fun) ->
|
|
%% <li>They are not automatically restarted when deadlocks are detected.</li>
|
|
%% <li>They are not automatically restarted when deadlocks are detected.</li>
|
|
%% </ul>
|
|
%% </ul>
|
|
%%
|
|
%%
|
|
|
|
+%% If an exception occurs within Fun, the exception is caught and `{aborted,
|
|
|
|
+%% Reason}' is returned. The value of `Reason' depends on the class of the
|
|
|
|
+%% exception.
|
|
|
|
+%%
|
|
|
|
+%% <table>
|
|
|
|
+%% <thead>
|
|
|
|
+%% <tr><th>Class of exception</th><th>Return value</th></tr>
|
|
|
|
+%% </thead>
|
|
|
|
+%% <tbody>
|
|
|
|
+%% <tr>
|
|
|
|
+%% <td>`error' with ErrorReason</td>
|
|
|
|
+%% <td>`{aborted, {ErrorReason, Stack}}'</td>
|
|
|
|
+%% </tr>
|
|
|
|
+%% <tr><td>`exit(Term)'</td><td>`{aborted, Term}'</td></tr>
|
|
|
|
+%% <tr><td>`throw(Term)'</td><td>`{aborted, {throw, Term}}'</td></tr>
|
|
|
|
+%% </tbody>
|
|
|
|
+%% </table>
|
|
|
|
+%%
|
|
%% TODO: Implement nested transactions
|
|
%% TODO: Implement nested transactions
|
|
%% TODO: Automatic restart on deadlocks
|
|
%% TODO: Automatic restart on deadlocks
|
|
--spec transaction(pid(), fun(), list()) -> {atomic, term()} | {aborted, term()}.
|
|
|
|
|
|
+-spec transaction(connection(), fun(), list()) -> {atomic, term()} |
|
|
|
|
+ {aborted, term()}.
|
|
transaction(Conn, Fun, Args) when is_list(Args),
|
|
transaction(Conn, Fun, Args) when is_list(Args),
|
|
is_function(Fun, length(Args)) ->
|
|
is_function(Fun, length(Args)) ->
|
|
%% The guard makes sure that we can apply Fun to Args. Any error we catch
|
|
%% The guard makes sure that we can apply Fun to Args. Any error we catch
|