|
@@ -483,6 +483,7 @@ encode(Conn, Term) ->
|
|
query_timeout, query_cache_time,
|
|
query_timeout, query_cache_time,
|
|
affected_rows = 0, status = 0, warning_count = 0, insert_id = 0,
|
|
affected_rows = 0, status = 0, warning_count = 0, insert_id = 0,
|
|
transaction_level = 0, ping_ref = undefined,
|
|
transaction_level = 0, ping_ref = undefined,
|
|
|
|
+ monitors = [],
|
|
stmts = dict:new(), query_cache = empty, cap_found_rows = false}).
|
|
stmts = dict:new(), query_cache = empty, cap_found_rows = false}).
|
|
|
|
|
|
%% @private
|
|
%% @private
|
|
@@ -715,11 +716,14 @@ handle_call(backslash_escapes_enabled, _From, State = #state{status = S}) ->
|
|
{reply, S band ?SERVER_STATUS_NO_BACKSLASH_ESCAPES == 0, State};
|
|
{reply, S band ?SERVER_STATUS_NO_BACKSLASH_ESCAPES == 0, State};
|
|
handle_call(in_transaction, _From, State) ->
|
|
handle_call(in_transaction, _From, State) ->
|
|
{reply, State#state.status band ?SERVER_STATUS_IN_TRANS /= 0, State};
|
|
{reply, State#state.status band ?SERVER_STATUS_IN_TRANS /= 0, State};
|
|
-handle_call(start_transaction, _From,
|
|
|
|
|
|
+handle_call(start_transaction, {FromPid, _},
|
|
State = #state{socket = Socket, sockmod = SockMod,
|
|
State = #state{socket = Socket, sockmod = SockMod,
|
|
- transaction_level = L, status = Status})
|
|
|
|
|
|
+ transaction_level = L, status = Status, monitors = Monitors})
|
|
when Status band ?SERVER_STATUS_IN_TRANS == 0, L == 0;
|
|
when Status band ?SERVER_STATUS_IN_TRANS == 0, L == 0;
|
|
Status band ?SERVER_STATUS_IN_TRANS /= 0, L > 0 ->
|
|
Status band ?SERVER_STATUS_IN_TRANS /= 0, L > 0 ->
|
|
|
|
+
|
|
|
|
+ MRef = erlang:monitor(process, FromPid),
|
|
|
|
+
|
|
Query = case L of
|
|
Query = case L of
|
|
0 -> <<"BEGIN">>;
|
|
0 -> <<"BEGIN">>;
|
|
_ -> <<"SAVEPOINT s", (integer_to_binary(L))/binary>>
|
|
_ -> <<"SAVEPOINT s", (integer_to_binary(L))/binary>>
|
|
@@ -730,10 +734,13 @@ handle_call(start_transaction, _From,
|
|
?cmd_timeout),
|
|
?cmd_timeout),
|
|
SockMod:setopts(Socket, [{active, once}]),
|
|
SockMod:setopts(Socket, [{active, once}]),
|
|
State1 = update_state(Res, State),
|
|
State1 = update_state(Res, State),
|
|
- {reply, ok, State1#state{transaction_level = L + 1}};
|
|
|
|
-handle_call(rollback, _From, State = #state{socket = Socket, sockmod = SockMod,
|
|
|
|
- status = Status, transaction_level = L})
|
|
|
|
|
|
+ {reply, ok, State1#state{transaction_level = L + 1, monitors = [{FromPid, MRef} | Monitors]}};
|
|
|
|
+handle_call(rollback, {FromPid, _}, State = #state{socket = Socket, sockmod = SockMod,
|
|
|
|
+ status = Status, transaction_level = L,
|
|
|
|
+ monitors = [{FromPid, MRef}|NewMonitors]})
|
|
when Status band ?SERVER_STATUS_IN_TRANS /= 0, L >= 1 ->
|
|
when Status band ?SERVER_STATUS_IN_TRANS /= 0, L >= 1 ->
|
|
|
|
+ erlang:demonitor(MRef),
|
|
|
|
+
|
|
Query = case L of
|
|
Query = case L of
|
|
1 -> <<"ROLLBACK">>;
|
|
1 -> <<"ROLLBACK">>;
|
|
_ -> <<"ROLLBACK TO s", (integer_to_binary(L - 1))/binary>>
|
|
_ -> <<"ROLLBACK TO s", (integer_to_binary(L - 1))/binary>>
|
|
@@ -744,10 +751,13 @@ handle_call(rollback, _From, State = #state{socket = Socket, sockmod = SockMod,
|
|
?cmd_timeout),
|
|
?cmd_timeout),
|
|
SockMod:setopts(Socket, [{active, once}]),
|
|
SockMod:setopts(Socket, [{active, once}]),
|
|
State1 = update_state(Res, State),
|
|
State1 = update_state(Res, State),
|
|
- {reply, ok, State1#state{transaction_level = L - 1}};
|
|
|
|
-handle_call(commit, _From, State = #state{socket = Socket, sockmod = SockMod,
|
|
|
|
- status = Status, transaction_level = L})
|
|
|
|
|
|
+ {reply, ok, State1#state{transaction_level = L - 1, monitors = NewMonitors}};
|
|
|
|
+handle_call(commit, {FromPid, _}, State = #state{socket = Socket, sockmod = SockMod,
|
|
|
|
+ status = Status, transaction_level = L,
|
|
|
|
+ monitors = [{FromPid, MRef}|NewMonitors]})
|
|
when Status band ?SERVER_STATUS_IN_TRANS /= 0, L >= 1 ->
|
|
when Status band ?SERVER_STATUS_IN_TRANS /= 0, L >= 1 ->
|
|
|
|
+ erlang:demonitor(MRef),
|
|
|
|
+
|
|
Query = case L of
|
|
Query = case L of
|
|
1 -> <<"COMMIT">>;
|
|
1 -> <<"COMMIT">>;
|
|
_ -> <<"RELEASE SAVEPOINT s", (integer_to_binary(L - 1))/binary>>
|
|
_ -> <<"RELEASE SAVEPOINT s", (integer_to_binary(L - 1))/binary>>
|
|
@@ -758,7 +768,7 @@ handle_call(commit, _From, State = #state{socket = Socket, sockmod = SockMod,
|
|
?cmd_timeout),
|
|
?cmd_timeout),
|
|
SockMod:setopts(Socket, [{active, once}]),
|
|
SockMod:setopts(Socket, [{active, once}]),
|
|
State1 = update_state(Res, State),
|
|
State1 = update_state(Res, State),
|
|
- {reply, ok, State1#state{transaction_level = L - 1}}.
|
|
|
|
|
|
+ {reply, ok, State1#state{transaction_level = L - 1, monitors = NewMonitors}}.
|
|
|
|
|
|
%% @private
|
|
%% @private
|
|
handle_cast(_Msg, State) ->
|
|
handle_cast(_Msg, State) ->
|
|
@@ -782,6 +792,8 @@ handle_info(query_cache, #state{query_cache = Cache,
|
|
mysql_cache:size(Cache1) > 0 andalso
|
|
mysql_cache:size(Cache1) > 0 andalso
|
|
erlang:send_after(CacheTime, self(), query_cache),
|
|
erlang:send_after(CacheTime, self(), query_cache),
|
|
{noreply, State#state{query_cache = Cache1}};
|
|
{noreply, State#state{query_cache = Cache1}};
|
|
|
|
+handle_info({'DOWN', _MRef, _, Pid, _Info}, State) ->
|
|
|
|
+ stop_server({application_process_died, Pid}, State);
|
|
handle_info(ping, #state{socket = Socket, sockmod = SockMod} = State) ->
|
|
handle_info(ping, #state{socket = Socket, sockmod = SockMod} = State) ->
|
|
SockMod:setopts(Socket, [{active, false}]),
|
|
SockMod:setopts(Socket, [{active, false}]),
|
|
SockMod = State#state.sockmod,
|
|
SockMod = State#state.sockmod,
|
|
@@ -880,14 +892,15 @@ handle_query_call_reply([], _Query, State, ResultSetsAcc) ->
|
|
[_|_] -> {ok, lists:reverse(ResultSetsAcc)}
|
|
[_|_] -> {ok, lists:reverse(ResultSetsAcc)}
|
|
end,
|
|
end,
|
|
{reply, Reply, State};
|
|
{reply, Reply, State};
|
|
-handle_query_call_reply([Rec|Recs], Query, State, ResultSetsAcc) ->
|
|
|
|
|
|
+handle_query_call_reply([Rec|Recs], Query, #state{monitors = Monitors} = State, ResultSetsAcc) ->
|
|
case Rec of
|
|
case Rec of
|
|
#ok{status = Status} when Status band ?SERVER_STATUS_IN_TRANS == 0,
|
|
#ok{status = Status} when Status band ?SERVER_STATUS_IN_TRANS == 0,
|
|
State#state.transaction_level > 0 ->
|
|
State#state.transaction_level > 0 ->
|
|
%% DDL statements (e.g. CREATE TABLE, ALTER TABLE, etc.) result in
|
|
%% DDL statements (e.g. CREATE TABLE, ALTER TABLE, etc.) result in
|
|
%% an implicit commit.
|
|
%% an implicit commit.
|
|
Reply = {implicit_commit, State#state.transaction_level, Query},
|
|
Reply = {implicit_commit, State#state.transaction_level, Query},
|
|
- {reply, Reply, State#state{transaction_level = 0}};
|
|
|
|
|
|
+ NewMonitors = demonitor_processes(Monitors, length(Monitors)),
|
|
|
|
+ {reply, Reply, State#state{transaction_level = 0, monitors = NewMonitors}};
|
|
#ok{} ->
|
|
#ok{} ->
|
|
handle_query_call_reply(Recs, Query, State, ResultSetsAcc);
|
|
handle_query_call_reply(Recs, Query, State, ResultSetsAcc);
|
|
#resultset{cols = ColDefs, rows = Rows} ->
|
|
#resultset{cols = ColDefs, rows = Rows} ->
|
|
@@ -900,7 +913,8 @@ handle_query_call_reply([Rec|Recs], Query, State, ResultSetsAcc) ->
|
|
error_to_reason(Rec)},
|
|
error_to_reason(Rec)},
|
|
%% Everything in the transaction is rolled back, except the BEGIN
|
|
%% Everything in the transaction is rolled back, except the BEGIN
|
|
%% statement itself. Thus, we are in transaction level 1.
|
|
%% statement itself. Thus, we are in transaction level 1.
|
|
- {reply, Reply, State#state{transaction_level = 1}};
|
|
|
|
|
|
+ NewMonitors = demonitor_processes(Monitors, length(Monitors) -1),
|
|
|
|
+ {reply, Reply, State#state{transaction_level = 1, monitors = NewMonitors}};
|
|
#error{} ->
|
|
#error{} ->
|
|
{reply, {error, error_to_reason(Rec)}, State}
|
|
{reply, {error, error_to_reason(Rec)}, State}
|
|
end.
|
|
end.
|
|
@@ -954,3 +968,10 @@ stop_server(Reason,
|
|
[ConnId, Reason]),
|
|
[ConnId, Reason]),
|
|
ok = gen_tcp:close(Socket),
|
|
ok = gen_tcp:close(Socket),
|
|
{stop, Reason, State#state{socket = undefined, connection_id = undefined}}.
|
|
{stop, Reason, State#state{socket = undefined, connection_id = undefined}}.
|
|
|
|
+
|
|
|
|
+demonitor_processes(List, 0) ->
|
|
|
|
+ List;
|
|
|
|
+demonitor_processes([{_FromPid, MRef}|T], Count) ->
|
|
|
|
+ erlang:demonitor(MRef),
|
|
|
|
+ demonitor_processes(T, Count -1).
|
|
|
|
+
|