cover/mysql.COVER.html

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