mysql.erl 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758
  1. %% MySQL/OTP – MySQL client library for Erlang/OTP
  2. %% Copyright (C) 2014-2015, 2018 Viktor Söderqvist,
  3. %% 2016 Johan Lövdahl
  4. %% 2017 Piotr Nosek, Michal Slaski
  5. %%
  6. %% This file is part of MySQL/OTP.
  7. %%
  8. %% MySQL/OTP is free software: you can redistribute it and/or modify it under
  9. %% the terms of the GNU Lesser General Public License as published by the Free
  10. %% Software Foundation, either version 3 of the License, or (at your option)
  11. %% any later version.
  12. %%
  13. %% This program is distributed in the hope that it will be useful, but WITHOUT
  14. %% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  15. %% FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  16. %% more details.
  17. %%
  18. %% You should have received a copy of the GNU Lesser General Public License
  19. %% along with this program. If not, see <https://www.gnu.org/licenses/>.
  20. %% @doc MySQL client.
  21. %%
  22. %% The `connection()' type is a gen_server reference as described in the
  23. %% documentation for `gen_server:call/2,3', e.g. the pid or the name if the
  24. %% gen_server is locally registered.
  25. -module(mysql).
  26. -export([start_link/1, stop/1, stop/2,
  27. query/2, query/3, query/4, query/5,
  28. execute/3, execute/4, execute/5,
  29. prepare/2, prepare/3, unprepare/2,
  30. warning_count/1, affected_rows/1, autocommit/1, insert_id/1,
  31. encode/2, in_transaction/1,
  32. transaction/2, transaction/3, transaction/4,
  33. change_user/3, change_user/4]).
  34. -export_type([connection/0, server_reason/0, query_result/0]).
  35. %% A connection is a ServerRef as in gen_server:call/2,3.
  36. -type connection() :: Name :: atom() |
  37. {Name :: atom(), Node :: atom()} |
  38. {global, GlobalName :: term()} |
  39. {via, Module :: atom(), ViaName :: term()} |
  40. pid().
  41. %% MySQL error with the codes and message returned from the server.
  42. -type server_reason() :: {Code :: integer(), SQLState :: binary() | undefined,
  43. Message :: binary()}.
  44. -type column_names() :: [binary()].
  45. -type row() :: [term()].
  46. -type rows() :: [row()].
  47. -type query_filtermap_fun() :: fun((row()) -> query_filtermap_res())
  48. | fun((column_names(), row()) -> query_filtermap_res()).
  49. -type query_filtermap_res() :: boolean()
  50. | {true, term()}.
  51. -type query_result() :: ok
  52. | {ok, column_names(), rows()}
  53. | {ok, [{column_names(), rows()}, ...]}
  54. | {error, server_reason()}.
  55. -define(default_connect_timeout, 5000).
  56. -include("exception.hrl").
  57. %% @doc Starts a connection gen_server process and connects to a database. To
  58. %% disconnect just do `exit(Pid, normal)'.
  59. %%
  60. %% Options:
  61. %%
  62. %% <dl>
  63. %% <dt>`{name, ServerName}'</dt>
  64. %% <dd>If a name is provided, the gen_server will be registered with this
  65. %% name. For details see the documentation for the first argument of
  66. %% gen_server:start_link/4.</dd>
  67. %% <dt>`{host, Host}'</dt>
  68. %% <dd>Hostname of the MySQL database. Since OTP version 19, it is also
  69. %% possible to specify a local (Unix) Socket by specifying
  70. %% `{local, SocketFile}'. Default `"localhost"'.</dd>
  71. %% <dt>`{port, Port}'</dt>
  72. %% <dd>Port; default 3306 for non-local or 0 for local (Unix) sockets.</dd>
  73. %% <dt>`{user, User}'</dt>
  74. %% <dd>Username.</dd>
  75. %% <dt>`{password, Password}'</dt>
  76. %% <dd>Password.</dd>
  77. %% <dt>`{database, Database}'</dt>
  78. %% <dd>The name of the database AKA schema to use. This can be changed later
  79. %% using the query `USE <database>'.</dd>
  80. %% <dt>`{connect_timeout, Timeout}'</dt>
  81. %% <dd>The maximum time to spend for start_link/1.</dd>
  82. %% <dt>`{log_warnings, boolean()}'</dt>
  83. %% <dd>Whether to fetch warnings and log them using error_logger; default
  84. %% true.</dd>
  85. %% <dt>`{keep_alive, boolean() | timeout()}'</dt>
  86. %% <dd>Send ping when unused for a certain time. Possible values are `true',
  87. %% `false' and `integer() > 0' for an explicit interval in milliseconds.
  88. %% The default is `false'. For `true' a default ping timeout is used.
  89. %% </dd>
  90. %% <dt>`{prepare, NamedStatements}'</dt>
  91. %% <dd>Named prepared statements to be created as soon as the connection is
  92. %% ready.</dd>
  93. %% <dt>`{queries, Queries}'</dt>
  94. %% <dd>Queries to be executed as soon as the connection is ready. Any results
  95. %% are discarded. Typically, this is used for setting time zone and other
  96. %% session variables.</dd>
  97. %% <dt>`{query_timeout, Timeout}'</dt>
  98. %% <dd>The default time to wait for a response when executing a query or a
  99. %% prepared statement. This can be given per query using `query/3,4' and
  100. %% `execute/4'. The default is `infinity'.</dd>
  101. %% <dt>`{found_rows, boolean()}'</dt>
  102. %% <dd>If set to true, the connection will be established with
  103. %% CLIENT_FOUND_ROWS capability. affected_rows/1 will now return the
  104. %% number of found rows, not the number of rows changed by the
  105. %% query.</dd>
  106. %% <dt>`{query_cache_time, Timeout}'</dt>
  107. %% <dd>The minimum number of milliseconds to cache prepared statements used
  108. %% for parametrized queries with query/3.</dd>
  109. %% <dt>`{tcp_options, Options}'</dt>
  110. %% <dd>Additional options for `gen_tcp:connect/3'. You may want to set
  111. %% `{recbuf, Size}' and `{sndbuf, Size}' if you send or receive more than
  112. %% the default (typically 8K) per query.</dd>
  113. %% <dt>`{ssl, Options}'</dt>
  114. %% <dd>Additional options for `ssl:connect/3'.</dd>
  115. %% </dl>
  116. -spec start_link(Options) -> {ok, pid()} | ignore | {error, term()}
  117. when Options :: [Option],
  118. Option :: {name, ServerName} |
  119. {host, inet:socket_address() | inet:hostname()} | {port, integer()} |
  120. {user, iodata()} | {password, iodata()} |
  121. {database, iodata()} |
  122. {connect_timeout, timeout()} |
  123. {log_warnings, boolean()} |
  124. {keep_alive, boolean() | timeout()} |
  125. {prepare, NamedStatements} |
  126. {queries, [iodata()]} |
  127. {query_timeout, timeout()} |
  128. {found_rows, boolean()} |
  129. {query_cache_time, non_neg_integer()} |
  130. {tcp_options, [gen_tcp:connect_option()]} |
  131. {ssl, term()},
  132. ServerName :: {local, Name :: atom()} |
  133. {global, GlobalName :: term()} |
  134. {via, Module :: atom(), ViaName :: term()},
  135. NamedStatements :: [{StatementName :: atom(), Statement :: iodata()}].
  136. start_link(Options) ->
  137. GenSrvOpts = [{timeout, proplists:get_value(connect_timeout, Options,
  138. ?default_connect_timeout)}],
  139. Ret = case proplists:get_value(name, Options) of
  140. undefined ->
  141. gen_server:start_link(mysql_conn, Options, GenSrvOpts);
  142. ServerName ->
  143. gen_server:start_link(ServerName, mysql_conn, Options, GenSrvOpts)
  144. end,
  145. case Ret of
  146. {ok, Pid} ->
  147. execute_after_connect(Pid,
  148. proplists:get_value(queries, Options, []),
  149. proplists:get_value(prepare, Options, []));
  150. _ -> ok
  151. end,
  152. Ret.
  153. %% @see stop/2.
  154. -spec stop(Conn) -> ok
  155. when Conn :: connection().
  156. stop(Conn) ->
  157. stop(Conn, infinity).
  158. %% @doc Stops a connection process and closes the connection. The
  159. %% process calling `stop' will be blocked until the connection
  160. %% process stops or the given timeout expires.
  161. %%
  162. %% If the connection is not stopped within the given timeout,
  163. %% an exit exception is raised with reason `timeout'.
  164. %%
  165. %% If the connection process exits with any other reason than `normal',
  166. %% an exit exception is raised with that reason.
  167. -spec stop(Conn, Timeout) -> ok
  168. when Conn :: connection(),
  169. Timeout :: timeout().
  170. stop(Conn, Timeout) ->
  171. case erlang:function_exported(gen_server, stop, 3) of
  172. true -> gen_server:stop(Conn, normal, Timeout); %% OTP >= 18
  173. false -> backported_gen_server_stop(Conn, normal, Timeout) %% OTP < 18
  174. end.
  175. -spec backported_gen_server_stop(Conn, Reason, Timeout) -> ok
  176. when Conn :: connection(),
  177. Reason :: term(),
  178. Timeout :: timeout().
  179. backported_gen_server_stop(Conn, Reason, Timeout) ->
  180. Monitor=monitor(process, Conn),
  181. exit(Conn, Reason),
  182. receive
  183. {'DOWN', Monitor, process, Conn, Reason} ->
  184. ok;
  185. {'DOWN', Monitor, process, Conn, UnexpectedReason} ->
  186. exit(UnexpectedReason)
  187. after Timeout ->
  188. exit(Conn, kill),
  189. receive
  190. {'DOWN', Monitor, process, Conn, killed} ->
  191. exit(timeout)
  192. end
  193. end.
  194. %% @see query/5.
  195. -spec query(Conn, Query) -> Result
  196. when Conn :: connection(),
  197. Query :: iodata(),
  198. Result :: query_result().
  199. query(Conn, Query) ->
  200. query(Conn, Query, no_params, no_filtermap_fun, default_timeout).
  201. %% @see query/5.
  202. -spec query(Conn, Query, Params | FilterMap | Timeout) -> Result
  203. when Conn :: connection(),
  204. Query :: iodata(),
  205. Timeout :: default_timeout | timeout(),
  206. Params :: no_params | [term()],
  207. FilterMap :: no_filtermap_fun | query_filtermap_fun(),
  208. Result :: query_result().
  209. query(Conn, Query, Params) when Params == no_params;
  210. is_list(Params) ->
  211. query(Conn, Query, Params, no_filtermap_fun, default_timeout);
  212. query(Conn, Query, FilterMap) when FilterMap == no_filtermap_fun;
  213. is_function(FilterMap, 1);
  214. is_function(FilterMap, 2) ->
  215. query(Conn, Query, no_params, FilterMap, default_timeout);
  216. query(Conn, Query, Timeout) when Timeout == default_timeout;
  217. is_integer(Timeout);
  218. Timeout == infinity ->
  219. query(Conn, Query, no_params, no_filtermap_fun, Timeout).
  220. %% @see query/5.
  221. -spec query(Conn, Query, Params, Timeout) -> Result
  222. when Conn :: connection(),
  223. Query :: iodata(),
  224. Timeout :: default_timeout | timeout(),
  225. Params :: no_params | [term()],
  226. Result :: query_result();
  227. (Conn, Query, FilterMap, Timeout) -> Result
  228. when Conn :: connection(),
  229. Query :: iodata(),
  230. Timeout :: default_timeout | timeout(),
  231. FilterMap :: no_filtermap_fun | query_filtermap_fun(),
  232. Result :: query_result();
  233. (Conn, Query, Params, FilterMap) -> Result
  234. when Conn :: connection(),
  235. Query :: iodata(),
  236. Params :: no_params | [term()],
  237. FilterMap :: no_filtermap_fun | query_filtermap_fun(),
  238. Result :: query_result().
  239. query(Conn, Query, Params, Timeout) when (Params == no_params orelse
  240. is_list(Params)) andalso
  241. (Timeout == default_timeout orelse
  242. is_integer(Timeout) orelse
  243. Timeout == infinity) ->
  244. query(Conn, Query, Params, no_filtermap_fun, Timeout);
  245. query(Conn, Query, FilterMap, Timeout) when (FilterMap == no_filtermap_fun orelse
  246. is_function(FilterMap, 1) orelse
  247. is_function(FilterMap, 2)) andalso
  248. (Timeout == default_timeout orelse
  249. is_integer(Timeout) orelse
  250. Timeout=:=infinity) ->
  251. query(Conn, Query, no_params, FilterMap, Timeout);
  252. query(Conn, Query, Params, FilterMap) when (Params == no_params orelse
  253. is_list(Params)) andalso
  254. (FilterMap == no_filtermap_fun orelse
  255. is_function(FilterMap, 1) orelse
  256. is_function(FilterMap, 2)) ->
  257. query(Conn, Query, Params, FilterMap, default_timeout).
  258. %% @doc Executes a parameterized query with a timeout and applies a filter/map
  259. %% function to the result rows.
  260. %%
  261. %% A prepared statement is created, executed and then cached for a certain
  262. %% time. If the same query is executed again when it is already cached, it does
  263. %% not need to be prepared again.
  264. %%
  265. %% The minimum time the prepared statement is cached can be specified using the
  266. %% option `{query_cache_time, Milliseconds}' to start_link/1.
  267. %%
  268. %% Results are returned in the form `{ok, ColumnNames, Rows}' if there is one
  269. %% result set. If there are more than one result sets, they are returned in the
  270. %% form `{ok, [{ColumnNames, Rows}, ...]}'.
  271. %%
  272. %% For queries that don't return any rows (INSERT, UPDATE, etc.) only the atom
  273. %% `ok' is returned.
  274. %%
  275. %% The `Params', `FilterMap' and `Timeout' arguments are optional.
  276. %% <ul>
  277. %% <li>If the `Params' argument is the atom `no_params' or is omitted, a plain
  278. %% query will be executed instead of a parameterized one.</li>
  279. %% <li>If the `FilterMap' argument is the atom `no_filtermap_fun' or is
  280. %% omitted, no row filtering/mapping will be applied and all result rows
  281. %% will be returned unchanged.</li>
  282. %% <li>If the `Timeout' argument is the atom `default_timeout' or is omitted,
  283. %% the timeout given in `start_link/1' is used.</li>
  284. %% </ul>
  285. %%
  286. %% If the `FilterMap' argument is used, it must be a function of arity 1 or 2
  287. %% that returns either `true', `false', or `{true, Value}'.
  288. %%
  289. %% Each result row is handed to the given function as soon as it is received
  290. %% from the server, and only when the function has returned, the next row is
  291. %% fetched. This provides the ability to prevent memory exhaustion; on the
  292. %% other hand, it can cause the server to time out on sending if your function
  293. %% is doing something slow (see the MySQL documentation on `NET_WRITE_TIMEOUT').
  294. %%
  295. %% If the function is of arity 1, only the row is passed to it as the single
  296. %% argument, while if the function is of arity 2, the column names are passed
  297. %% in as the first argument and the row as the second.
  298. %%
  299. %% The value returned is then used to decide if the row is to be included in
  300. %% the result(s) returned from the `query' call (filtering), or if something
  301. %% else is to be included in the result instead (mapping). You may also use
  302. %% this function for side effects, like writing rows to disk or sending them
  303. %% to another process etc.
  304. %%
  305. %% Here is an example showing some of the things that are possible:
  306. %% ```
  307. %% Query = "SELECT a, b, c FROM foo",
  308. %% FilterMap = fun
  309. %% %% Include all rows where the first column is < 10.
  310. %% ([A|_]) when A < 10 ->
  311. %% true;
  312. %% %% Exclude all rows where the first column is >= 10 and < 20.
  313. %% ([A|_]) when A < 20 ->
  314. %% false;
  315. %% %% For rows where the first column is >= 20 and < 30, include
  316. %% %% the atom 'foo' in place of the row instead.
  317. %% ([A|_]) when A < 30 ->
  318. %% {true, foo}};
  319. %% %% For rows where the first row is >= 30 and < 40, send the
  320. %% %% row to a gen_server via call (ie, wait for a response),
  321. %% %% and do not include the row in the result.
  322. %% (R=[A|_]) when A < 40 ->
  323. %% gen_server:call(Pid, R),
  324. %% false;
  325. %% %% For rows where the first column is >= 40 and < 50, send the
  326. %% %% row to a gen_server via cast (ie, do not wait for a reply),
  327. %% %% and include the row in the result, also.
  328. %% (R=[A|_]) when A < 50 ->
  329. %% gen_server:cast(Pid, R),
  330. %% true;
  331. %% %% Exclude all other rows from the result.
  332. %% (_) ->
  333. %% false
  334. %% end,
  335. %% query(Conn, Query, no_params, FilterMap, default_timeout).
  336. %% '''
  337. -spec query(Conn, Query, Params, FilterMap, Timeout) -> Result
  338. when Conn :: connection(),
  339. Query :: iodata(),
  340. Timeout :: default_timeout | timeout(),
  341. Params :: no_params | [term()],
  342. FilterMap :: no_filtermap_fun | query_filtermap_fun(),
  343. Result :: query_result().
  344. query(Conn, Query, no_params, FilterMap, Timeout) ->
  345. query_call(Conn, {query, Query, FilterMap, Timeout});
  346. query(Conn, Query, Params, FilterMap, Timeout) ->
  347. case mysql_protocol:valid_params(Params) of
  348. true ->
  349. query_call(Conn,
  350. {param_query, Query, Params, FilterMap, Timeout});
  351. false ->
  352. error(badarg)
  353. end.
  354. %% @doc Executes a prepared statement with the default query timeout as given
  355. %% to start_link/1.
  356. %% @see prepare/2
  357. %% @see prepare/3
  358. %% @see prepare/4
  359. %% @see execute/5
  360. -spec execute(Conn, StatementRef, Params) -> Result | {error, not_prepared}
  361. when Conn :: connection(),
  362. StatementRef :: atom() | integer(),
  363. Params :: [term()],
  364. Result :: query_result().
  365. execute(Conn, StatementRef, Params) ->
  366. execute(Conn, StatementRef, Params, no_filtermap_fun, default_timeout).
  367. %% @doc Executes a prepared statement.
  368. %% @see prepare/2
  369. %% @see prepare/3
  370. %% @see prepare/4
  371. %% @see execute/5
  372. -spec execute(Conn, StatementRef, Params, FilterMap | Timeout) ->
  373. Result | {error, not_prepared}
  374. when Conn :: connection(),
  375. StatementRef :: atom() | integer(),
  376. Params :: [term()],
  377. FilterMap :: no_filtermap_fun | query_filtermap_fun(),
  378. Timeout :: default_timeout | timeout(),
  379. Result :: query_result().
  380. execute(Conn, StatementRef, Params, Timeout) when Timeout == default_timeout;
  381. is_integer(Timeout);
  382. Timeout=:=infinity ->
  383. execute(Conn, StatementRef, Params, no_filtermap_fun, Timeout);
  384. execute(Conn, StatementRef, Params, FilterMap) when FilterMap == no_filtermap_fun;
  385. is_function(FilterMap, 1);
  386. is_function(FilterMap, 2) ->
  387. execute(Conn, StatementRef, Params, FilterMap, default_timeout).
  388. %% @doc Executes a prepared statement.
  389. %%
  390. %% The `FilterMap' and `Timeout' arguments are optional.
  391. %% <ul>
  392. %% <li>If the `FilterMap' argument is the atom `no_filtermap_fun' or is
  393. %% omitted, no row filtering/mapping will be applied and all result rows
  394. %% will be returned unchanged.</li>
  395. %% <li>If the `Timeout' argument is the atom `default_timeout' or is omitted,
  396. %% the timeout given in `start_link/1' is used.</li>
  397. %% </ul>
  398. %%
  399. %% See `query/5' for an explanation of the `FilterMap' argument.
  400. %%
  401. %% @see prepare/2
  402. %% @see prepare/3
  403. %% @see prepare/4
  404. %% @see query/5
  405. -spec execute(Conn, StatementRef, Params, FilterMap, Timeout) ->
  406. Result | {error, not_prepared}
  407. when Conn :: connection(),
  408. StatementRef :: atom() | integer(),
  409. Params :: [term()],
  410. FilterMap :: no_filtermap_fun | query_filtermap_fun(),
  411. Timeout :: default_timeout | timeout(),
  412. Result :: query_result().
  413. execute(Conn, StatementRef, Params, FilterMap, Timeout) ->
  414. case mysql_protocol:valid_params(Params) of
  415. true ->
  416. query_call(Conn,
  417. {execute, StatementRef, Params, FilterMap, Timeout});
  418. false ->
  419. error(badarg)
  420. end.
  421. %% @doc Creates a prepared statement from the passed query.
  422. %% @see prepare/3
  423. -spec prepare(Conn, Query) -> {ok, StatementId} | {error, Reason}
  424. when Conn :: connection(),
  425. Query :: iodata(),
  426. StatementId :: integer(),
  427. Reason :: server_reason().
  428. prepare(Conn, Query) ->
  429. gen_server:call(Conn, {prepare, Query}).
  430. %% @doc Creates a prepared statement from the passed query and associates it
  431. %% with the given name.
  432. %% @see prepare/2
  433. -spec prepare(Conn, Name, Query) -> {ok, Name} | {error, Reason}
  434. when Conn :: connection(),
  435. Name :: atom(),
  436. Query :: iodata(),
  437. Reason :: server_reason().
  438. prepare(Conn, Name, Query) ->
  439. gen_server:call(Conn, {prepare, Name, Query}).
  440. %% @doc Deallocates a prepared statement.
  441. -spec unprepare(Conn, StatementRef) -> ok | {error, Reason}
  442. when Conn :: connection(),
  443. StatementRef :: atom() | integer(),
  444. Reason :: server_reason() | not_prepared.
  445. unprepare(Conn, StatementRef) ->
  446. gen_server:call(Conn, {unprepare, StatementRef}).
  447. %% @doc Returns the number of warnings generated by the last query/2 or
  448. %% execute/3 calls.
  449. -spec warning_count(connection()) -> integer().
  450. warning_count(Conn) ->
  451. gen_server:call(Conn, warning_count).
  452. %% @doc Returns the number of inserted, updated and deleted rows of the last
  453. %% executed query or prepared statement. If found_rows is set on the
  454. %% connection, for update operation the return value will equal to the number
  455. %% of rows matched by the query.
  456. -spec affected_rows(connection()) -> integer().
  457. affected_rows(Conn) ->
  458. gen_server:call(Conn, affected_rows).
  459. %% @doc Returns true if auto-commit is enabled and false otherwise.
  460. -spec autocommit(connection()) -> boolean().
  461. autocommit(Conn) ->
  462. gen_server:call(Conn, autocommit).
  463. %% @doc Returns the last insert-id.
  464. -spec insert_id(connection()) -> integer().
  465. insert_id(Conn) ->
  466. gen_server:call(Conn, insert_id).
  467. %% @doc Returns true if the connection is in a transaction and false otherwise.
  468. %% This works regardless of whether the transaction has been started using
  469. %% transaction/2,3 or using a plain `mysql:query(Connection, "BEGIN")'.
  470. %% @see transaction/2
  471. %% @see transaction/4
  472. -spec in_transaction(connection()) -> boolean().
  473. in_transaction(Conn) ->
  474. gen_server:call(Conn, in_transaction).
  475. %% @doc This function executes the functional object Fun as a transaction.
  476. %% @see transaction/4
  477. -spec transaction(connection(), fun()) -> {atomic, term()} | {aborted, term()}.
  478. transaction(Conn, Fun) ->
  479. transaction(Conn, Fun, [], infinity).
  480. %% @doc This function executes the functional object Fun as a transaction.
  481. %% @see transaction/4
  482. -spec transaction(connection(), fun(), Retries) -> {atomic, term()} |
  483. {aborted, term()}
  484. when Retries :: non_neg_integer() | infinity.
  485. transaction(Conn, Fun, Retries) ->
  486. transaction(Conn, Fun, [], Retries).
  487. %% @doc This function executes the functional object Fun with arguments Args as
  488. %% a transaction.
  489. %%
  490. %% The semantics are as close as possible to mnesia's transactions. Transactions
  491. %% can be nested and are restarted automatically when deadlocks are detected.
  492. %% MySQL's savepoints are used to implement nested transactions.
  493. %%
  494. %% Fun must be a function and Args must be a list of the same length as the
  495. %% arity of Fun.
  496. %%
  497. %% If an exception occurs within Fun, the exception is caught and `{aborted,
  498. %% Reason}' is returned. The value of `Reason' depends on the class of the
  499. %% exception.
  500. %%
  501. %% Note that an error response from a query does not cause a transaction to be
  502. %% rollbacked. To force a rollback on a MySQL error you can trigger a `badmatch'
  503. %% using e.g. `ok = mysql:query(Pid, "SELECT some_non_existent_value")'. An
  504. %% exception to this is the error 1213 "Deadlock", after the specified number
  505. %% of retries, all failed. In this case, the transaction is aborted and the
  506. %% error is retured as the reason for the aborted transaction, along with a
  507. %% stacktrace pointing to where the last deadlock was detected. (In earlier
  508. %% versions, up to and including 1.3.2, transactions where automatically
  509. %% restarted also for the error 1205 "Lock wait timeout". This is no longer the
  510. %% case.)
  511. %%
  512. %% Some queries such as ALTER TABLE cause an *implicit commit* on the server.
  513. %% If such a query is executed within a transaction, an error on the form
  514. %% `{implicit_commit, Query}' is raised. This means that the transaction has
  515. %% been committed prematurely. This also happens if an explicit COMMIT is
  516. %% executed as a plain query within a managed transaction. (Don't do that!)
  517. %%
  518. %% <table>
  519. %% <thead>
  520. %% <tr><th>Class of exception</th><th>Return value</th></tr>
  521. %% </thead>
  522. %% <tbody>
  523. %% <tr>
  524. %% <td>`error' with reason `ErrorReason'</td>
  525. %% <td>`{aborted, {ErrorReason, Stack}}'</td>
  526. %% </tr>
  527. %% <tr><td>`exit(Term)'</td><td>`{aborted, Term}'</td></tr>
  528. %% <tr><td>`throw(Term)'</td><td>`{aborted, {throw, Term}}'</td></tr>
  529. %% </tbody>
  530. %% </table>
  531. -spec transaction(connection(), fun(), list(), Retries) -> {atomic, term()} |
  532. {aborted, term()}
  533. when Retries :: non_neg_integer() | infinity.
  534. transaction(Conn, Fun, Args, Retries) when is_list(Args),
  535. is_function(Fun, length(Args)) ->
  536. %% The guard makes sure that we can apply Fun to Args. Any error we catch
  537. %% in the try-catch are actual errors that occurred in Fun.
  538. ok = gen_server:call(Conn, start_transaction, infinity),
  539. execute_transaction(Conn, Fun, Args, Retries).
  540. %% @private
  541. %% @doc This is a helper for transaction/2,3,4. It performs everything except
  542. %% executing the BEGIN statement. It is called recursively when a transaction
  543. %% is retried.
  544. %%
  545. %% "When a transaction rollback occurs due to a deadlock or lock wait timeout,
  546. %% it cancels the effect of the statements within the transaction. But if the
  547. %% start-transaction statement was START TRANSACTION or BEGIN statement,
  548. %% rollback does not cancel that statement."
  549. %% (https://dev.mysql.com/doc/refman/5.6/en/innodb-error-handling.html)
  550. %%
  551. %% Lock Wait Timeout:
  552. %% "InnoDB rolls back only the last statement on a transaction timeout by
  553. %% default. If --innodb_rollback_on_timeout is specified, a transaction timeout
  554. %% causes InnoDB to abort and roll back the entire transaction (the same
  555. %% behavior as in MySQL 4.1)."
  556. %% (https://dev.mysql.com/doc/refman/5.6/en/innodb-parameters.html)
  557. execute_transaction(Conn, Fun, Args, Retries) ->
  558. try apply(Fun, Args) of
  559. ResultOfFun ->
  560. ok = gen_server:call(Conn, commit, infinity),
  561. {atomic, ResultOfFun}
  562. catch
  563. %% We are at the top level, try to restart the transaction if there are
  564. %% retries left
  565. ?EXCEPTION(throw, {implicit_rollback, 1, _}, _Stacktrace)
  566. when Retries == infinity ->
  567. execute_transaction(Conn, Fun, Args, infinity);
  568. ?EXCEPTION(throw, {implicit_rollback, 1, _}, _Stacktrace)
  569. when Retries > 0 ->
  570. execute_transaction(Conn, Fun, Args, Retries - 1);
  571. ?EXCEPTION(throw, {implicit_rollback, 1, Reason}, Stacktrace)
  572. when Retries == 0 ->
  573. %% No more retries. Return 'aborted' along with the deadlock error
  574. %% and a the trace to the line where the deadlock occured.
  575. Trace = ?GET_STACK(Stacktrace),
  576. ok = gen_server:call(Conn, rollback, infinity),
  577. {aborted, {Reason, Trace}};
  578. ?EXCEPTION(throw, {implicit_rollback, N, Reason}, Stacktrace)
  579. when N > 1 ->
  580. %% Nested transaction. Bubble out to the outermost level.
  581. erlang:raise(throw, {implicit_rollback, N - 1, Reason},
  582. ?GET_STACK(Stacktrace));
  583. ?EXCEPTION(error, {implicit_commit, _Query} = E, Stacktrace) ->
  584. %% The called did something like ALTER TABLE which resulted in an
  585. %% implicit commit. The server has already committed. We need to
  586. %% jump out of N levels of transactions.
  587. %%
  588. %% Returning 'atomic' or 'aborted' would both be wrong. Raise an
  589. %% exception is the best we can do.
  590. erlang:raise(error, E, ?GET_STACK(Stacktrace));
  591. ?EXCEPTION(error, change_user_in_transaction = E, Stacktrace) ->
  592. %% The called tried to change user inside the transaction, which
  593. %% is not allowed and a serious mistake. We roll back and raise
  594. %% an error.
  595. ok = gen_server:call(Conn, rollback, infinity),
  596. erlang:raise(error, E, ?GET_STACK(Stacktrace));
  597. ?EXCEPTION(Class, Reason, Stacktrace) ->
  598. %% We must be able to rollback. Otherwise let's crash.
  599. ok = gen_server:call(Conn, rollback, infinity),
  600. %% These forms for throw, error and exit mirror Mnesia's behaviour.
  601. Aborted = case Class of
  602. throw -> {throw, Reason};
  603. error -> {Reason, ?GET_STACK(Stacktrace)};
  604. exit -> Reason
  605. end,
  606. {aborted, Aborted}
  607. end.
  608. %% @doc Equivalent to `change_user(Conn, Username, Password, [])'.
  609. %% @see change_user/4
  610. -spec change_user(Conn, Username, Password) -> Result
  611. when Conn :: connection(),
  612. Username :: iodata(),
  613. Password :: iodata(),
  614. Result :: ok.
  615. change_user(Conn, Username, Password) ->
  616. change_user(Conn, Username, Password, []).
  617. %% @doc Changes the user of the active connection without closing and
  618. %% and re-opening it. The currently active session will be reset (ie,
  619. %% user variables, temporary tables, prepared statements, etc will
  620. %% be lost) independent of whether the operation succeeds or fails.
  621. %%
  622. %% If change user is called when a transaction is active (ie, neither
  623. %% committed nor rolled back), calling `change_user' will fail with
  624. %% an error exception and `change_user_in_transaction' as the error
  625. %% message.
  626. %%
  627. %% If the change user operation fails for other reasons (eg authentication
  628. %% failure), an error exception occurs, and the connection process
  629. %% exits with reason `change_user_failed'. The connection can not be used
  630. %% any longer if this happens.
  631. %%
  632. %% For a description of the `database', `queries' and `prepare'
  633. %% options, see `start_link/1'.
  634. %%
  635. %% @see start_link/1
  636. -spec change_user(Conn, Username, Password, Options) -> Result
  637. when Conn :: connection(),
  638. Username :: iodata(),
  639. Password :: iodata(),
  640. Options :: [Option],
  641. Result :: ok,
  642. Option :: {database, iodata()}
  643. | {queries, [iodata()]}
  644. | {prepare, [NamedStatement]},
  645. NamedStatement :: {StatementName :: atom(), Statement :: iodata()}.
  646. change_user(Conn, Username, Password, Options) ->
  647. case in_transaction(Conn) of
  648. true -> error(change_user_in_transaction);
  649. false -> ok
  650. end,
  651. Database = proplists:get_value(database, Options, undefined),
  652. Ret = gen_server:call(Conn, {change_user, Username, Password, Database}),
  653. case Ret of
  654. ok ->
  655. execute_after_connect(Conn,
  656. proplists:get_value(queries, Options, []),
  657. proplists:get_value(prepare, Options, [])),
  658. ok;
  659. {error, Reason} ->
  660. error(Reason)
  661. end.
  662. %% @doc Encodes a term as a MySQL literal so that it can be used to inside a
  663. %% query. If backslash escapes are enabled, backslashes and single quotes in
  664. %% strings and binaries are escaped. Otherwise only single quotes are escaped.
  665. %%
  666. %% Note that the preferred way of sending values is by prepared statements or
  667. %% parametrized queries with placeholders.
  668. %%
  669. %% @see query/3
  670. %% @see execute/3
  671. -spec encode(connection(), term()) -> iodata().
  672. encode(Conn, Term) ->
  673. Term1 = case (is_list(Term) orelse is_binary(Term)) andalso
  674. gen_server:call(Conn, backslash_escapes_enabled) of
  675. true -> mysql_encode:backslash_escape(Term);
  676. false -> Term
  677. end,
  678. mysql_encode:encode(Term1).
  679. %% --- Helpers ---
  680. %% @doc Executes the given queries and prepares the given statements after a
  681. %% connection has been made.
  682. %%
  683. %% If any of the queries or prepares fails, the connection is closed and an
  684. %% exception is raised.
  685. -spec execute_after_connect(connection(), [iodata()], [{atom(), iodata()}])
  686. -> ok.
  687. execute_after_connect(Conn, Queries, Prepares) ->
  688. try
  689. lists:foreach(fun (Query) ->
  690. case query(Conn, Query) of
  691. ok -> ok;
  692. {ok, _} -> ok;
  693. {ok, _, _} -> ok
  694. end
  695. end,
  696. Queries),
  697. lists:foreach(fun ({Name, Stmt}) ->
  698. {ok, Name} = prepare(Conn, Name, Stmt)
  699. end,
  700. Prepares),
  701. ok
  702. catch
  703. ?EXCEPTION(Class, Reason, Stacktrace) ->
  704. catch stop(Conn, ?default_connect_timeout),
  705. erlang:raise(Class, Reason, ?GET_STACK(Stacktrace))
  706. end.
  707. %% @doc Makes a gen_server call for a query (plain, parametrized or prepared),
  708. %% checks the reply and sometimes throws an exception when we need to jump out
  709. %% of a transaction.
  710. query_call(Conn, CallReq) ->
  711. case gen_server:call(Conn, CallReq, infinity) of
  712. {implicit_commit, _NestingLevel, Query} ->
  713. error({implicit_commit, Query});
  714. {implicit_rollback, _NestingLevel, _ServerReason} = ImplicitRollback ->
  715. throw(ImplicitRollback);
  716. Result ->
  717. Result
  718. end.