mysql.erl 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. %% MySQL/OTP – MySQL client library for Erlang/OTP
  2. %% Copyright (C) 2014 Viktor Söderqvist
  3. %%
  4. %% This file is part of MySQL/OTP.
  5. %%
  6. %% MySQL/OTP is free software: you can redistribute it and/or modify it under
  7. %% the terms of the GNU Lesser General Public License as published by the Free
  8. %% Software Foundation, either version 3 of the License, or (at your option)
  9. %% any later version.
  10. %%
  11. %% This program is distributed in the hope that it will be useful, but WITHOUT
  12. %% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13. %% FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  14. %% more details.
  15. %%
  16. %% You should have received a copy of the GNU Lesser General Public License
  17. %% along with this program. If not, see <https://www.gnu.org/licenses/>.
  18. %% @doc API for communicating with MySQL databases.
  19. %%
  20. %% Most of the functions are wrappers for `gen_server' calls. The
  21. %% `connection()' type is the same as returned by `gen_server:start_link/2,3'.
  22. -module(mysql).
  23. -export([start_link/1, query/2, execute/3, prepare/2, prepare/3, unprepare/2,
  24. warning_count/1, affected_rows/1, autocommit/1, insert_id/1,
  25. in_transaction/1,
  26. transaction/2, transaction/3]).
  27. -export_type([connection/0]).
  28. %% A connection is a ServerRef as in gen_server:call/2,3.
  29. -type connection() :: Name :: atom() |
  30. {Name :: atom(), Node :: atom()} |
  31. {global, GlobalName :: term()} |
  32. {via, Module :: atom(), ViaName :: term()} |
  33. pid().
  34. %% MySQL error with the codes and message returned from the server.
  35. -type reason() :: {Code :: integer(), SQLState :: binary(),
  36. Message :: binary()}.
  37. %% @doc Starts a connection gen_server process and connects to a database. To
  38. %% disconnect just do `exit(Pid, normal)'.
  39. %%
  40. %% This is just a wrapper for `gen_server:start_link(mysql_connection, Options,
  41. %% [])'. If you need to specify gen_server options, use gen_server:start_link/3
  42. %% directly.
  43. -spec start_link(Options) -> {ok, pid()} | ignore | {error, term()}
  44. when Options :: [Option],
  45. Option :: {host, iodata()} | {port, integer()} | {user, iodata()} |
  46. {password, iodata()} | {database, iodata()}.
  47. start_link(Opts) ->
  48. gen_server:start_link(mysql_connection, Opts, []).
  49. %% @doc Executes a query.
  50. -spec query(Conn, Query) -> ok | {ok, ColumnNames, Rows} | {error, Reason}
  51. when Conn :: connection(),
  52. Query :: iodata(),
  53. ColumnNames :: [binary()],
  54. Rows :: [[term()]],
  55. Reason :: reason().
  56. query(Conn, Query) ->
  57. gen_server:call(Conn, {query, Query}).
  58. %% @doc Executes a prepared statement.
  59. %% @see prepare/2
  60. execute(Conn, StatementId, Args) ->
  61. gen_server:call(Conn, {execute, StatementId, Args}).
  62. %% @doc Creates a prepared statement from the passed query.
  63. %% @see execute/3
  64. -spec prepare(Conn :: connection(), Query :: iodata()) ->
  65. {ok, StatementId :: integer()} | {error, Reason :: reason()}.
  66. prepare(Conn, Query) ->
  67. gen_server:call(Conn, {prepare, Query}).
  68. %% @doc Creates a prepared statement from the passed query and associates it
  69. %% with the given name.
  70. %% @see execute/3
  71. -spec prepare(Conn :: connection(), Name :: term(), Query :: iodata()) ->
  72. {ok, Name :: term()} | {error, Reason :: reason()}.
  73. prepare(Conn, Name, Query) ->
  74. gen_server:call(Conn, {prepare, Name, Query}).
  75. %% @doc Deallocates a prepared statement.
  76. %% @see prepare/3
  77. -spec unprepare(Conn :: connection(), StatementRef :: term()) ->
  78. ok | {error, not_prepared} | {error, Reason :: reason()}.
  79. unprepare(Conn, StatementRef) ->
  80. gen_server:call(Conn, {unprepare, StatementRef}).
  81. %% @doc Returns the number of warnings generated by the last query/2 or
  82. %% execute/3 calls.
  83. -spec warning_count(connection()) -> integer().
  84. warning_count(Conn) ->
  85. gen_server:call(Conn, warning_count).
  86. %% @doc Returns the number of inserted, updated and deleted rows of the last
  87. %% executed query or prepared statement.
  88. -spec affected_rows(connection()) -> integer().
  89. affected_rows(Conn) ->
  90. gen_server:call(Conn, affected_rows).
  91. %% @doc Returns true if auto-commit is enabled and false otherwise.
  92. -spec autocommit(connection()) -> boolean().
  93. autocommit(Conn) ->
  94. gen_server:call(Conn, autocommit).
  95. %% @doc Returns the last insert-id.
  96. -spec insert_id(connection()) -> integer().
  97. insert_id(Conn) ->
  98. gen_server:call(Conn, insert_id).
  99. %% @doc Returns true if the connection is in a transaction and false otherwise.
  100. %% This works regardless of whether the transaction has been started using
  101. %% transaction/2,3 or using a plain `mysql:query(Connection, "START
  102. %% TRANSACTION")'.
  103. %% @see transaction/2
  104. %% @see transaction/3
  105. -spec in_transaction(connection()) -> boolean().
  106. in_transaction(Conn) ->
  107. gen_server:call(Conn, in_transaction).
  108. %% @doc This function executes the functional object Fun as a transaction.
  109. %% @see transaction/3
  110. %% @see in_transaction/1
  111. -spec transaction(connection(), fun()) -> {atomic, term()} | {aborted, term()}.
  112. transaction(Conn, Fun) ->
  113. transaction(Conn, Fun, []).
  114. %% @doc This function executes the functional object Fun with arguments Args as
  115. %% a transaction.
  116. %%
  117. %% The semantics are the same as for mnesia's transactions.
  118. %%
  119. %% The Fun must be a function and Args must be a list with the same length
  120. %% as the arity of Fun.
  121. %%
  122. %% Current limitations:
  123. %%
  124. %% <ul>
  125. %% <li>Transactions cannot be nested</li>
  126. %% <li>They are not automatically restarted when deadlocks are detected.</li>
  127. %% </ul>
  128. %%
  129. %% If an exception occurs within Fun, the exception is caught and `{aborted,
  130. %% Reason}' is returned. The value of `Reason' depends on the class of the
  131. %% exception.
  132. %%
  133. %% <table>
  134. %% <thead>
  135. %% <tr><th>Class of exception</th><th>Return value</th></tr>
  136. %% </thead>
  137. %% <tbody>
  138. %% <tr>
  139. %% <td>`error' with reason `ErrorReason'</td>
  140. %% <td>`{aborted, {ErrorReason, Stack}}'</td>
  141. %% </tr>
  142. %% <tr><td>`exit(Term)'</td><td>`{aborted, Term}'</td></tr>
  143. %% <tr><td>`throw(Term)'</td><td>`{aborted, {throw, Term}}'</td></tr>
  144. %% </tbody>
  145. %% </table>
  146. %%
  147. %% TODO: Implement nested transactions
  148. %% TODO: Automatic restart on deadlocks
  149. %% @see in_transaction/1
  150. -spec transaction(connection(), fun(), list()) -> {atomic, term()} |
  151. {aborted, term()}.
  152. transaction(Conn, Fun, Args) when is_list(Args),
  153. is_function(Fun, length(Args)) ->
  154. %% The guard makes sure that we can apply Fun to Args. Any error we catch
  155. %% in the try-catch are actual errors that occurred in Fun.
  156. ok = query(Conn, <<"BEGIN">>),
  157. try apply(Fun, Args) of
  158. ResultOfFun ->
  159. %% We must be able to rollback. Otherwise let's go mad.
  160. ok = query(Conn, <<"COMMIT">>),
  161. {atomic, ResultOfFun}
  162. catch
  163. Class:Reason ->
  164. %% We must be able to rollback. Otherwise let's go mad.
  165. ok = query(Conn, <<"ROLLBACK">>),
  166. %% These forms for throw, error and exit mirror Mnesia's behaviour.
  167. Aborted = case Class of
  168. throw -> {throw, Reason};
  169. error -> {Reason, erlang:get_stacktrace()};
  170. exit -> Reason
  171. end,
  172. {aborted, Aborted}
  173. end.