1 /* Copyright (c) 2014, 2021, Oracle and/or its affiliates.
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License, version 2.0,
5 as published by the Free Software Foundation.
6
7 This program is also distributed with certain software (including
8 but not limited to OpenSSL) that is licensed under separate terms,
9 as designated in a particular file or component or in included license
10 documentation. The authors of MySQL hereby grant you an additional
11 permission to link the program and your derivative works with the
12 separately licensed software that they have included with MySQL.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License, version 2.0, for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22 02110-1301 USA */
23
24 #include "rpl_context.h"
25
26 #include "rpl_gtid.h" // Gtid_set
27 #include "sql_class.h" // THD
28
29
Session_consistency_gtids_ctx()30 Session_consistency_gtids_ctx::Session_consistency_gtids_ctx() :
31 m_sid_map(NULL), m_gtid_set(NULL), m_listener(NULL),
32 m_curr_session_track_gtids(OFF)
33 { }
34
~Session_consistency_gtids_ctx()35 Session_consistency_gtids_ctx::~Session_consistency_gtids_ctx()
36 {
37 if (m_gtid_set)
38 {
39 delete m_gtid_set;
40 m_gtid_set= NULL;
41 }
42
43 if (m_sid_map)
44 {
45 delete m_sid_map;
46 m_sid_map= NULL;
47 }
48 }
49
shall_collect(const THD * thd)50 inline bool Session_consistency_gtids_ctx::shall_collect(const THD* thd)
51 {
52 return /* Do not track OWN_GTID if session does not own a
53 (non-anonymous) GTID. */
54 (thd->owned_gtid.sidno > 0 ||
55 m_curr_session_track_gtids == ALL_GTIDS) &&
56 /* if there is no listener/tracker, then there is no reason to collect */
57 m_listener != NULL &&
58 /* ROLLBACK statements may end up calling trans_commit_stmt */
59 thd->lex->sql_command != SQLCOM_ROLLBACK &&
60 thd->lex->sql_command != SQLCOM_ROLLBACK_TO_SAVEPOINT;
61 }
62
notify_after_transaction_commit(const THD * thd)63 bool Session_consistency_gtids_ctx::notify_after_transaction_commit(const THD* thd)
64 {
65 DBUG_ENTER("Rpl_consistency_ctx::notify_after_transaction_commit");
66 assert(thd);
67 bool res= false;
68
69 if (!shall_collect(thd))
70 DBUG_RETURN(res);
71
72 if (m_curr_session_track_gtids == ALL_GTIDS)
73 {
74 /*
75 If one is configured to read all writes, we always collect
76 the GTID_EXECUTED.
77
78 NOTE: in the future optimize to collect deltas instead maybe.
79 */
80 global_sid_lock->wrlock();
81 res= m_gtid_set->add_gtid_set(gtid_state->get_executed_gtids()) != RETURN_STATUS_OK;
82 global_sid_lock->unlock();
83
84 if (!res)
85 notify_ctx_change_listener();
86 }
87
88 DBUG_RETURN(res);
89 }
90
notify_after_gtid_executed_update(const THD * thd)91 bool Session_consistency_gtids_ctx::notify_after_gtid_executed_update(const THD *thd)
92 {
93 DBUG_ENTER("Rpl_consistency_ctx::notify_after_gtid_executed_update");
94 assert(thd);
95 bool res= false;
96
97 if (!shall_collect(thd))
98 DBUG_RETURN(res);
99
100 if (m_curr_session_track_gtids == OWN_GTID)
101 {
102 assert(get_gtid_mode(GTID_MODE_LOCK_SID) != GTID_MODE_OFF);
103 assert(thd->owned_gtid.sidno > 0);
104 const Gtid& gtid= thd->owned_gtid;
105 if (gtid.sidno == -1) // we need to add thd->owned_gtid_set
106 {
107 /* Caller must only call this function if the set was not empty. */
108 #ifdef HAVE_GTID_NEXT_LIST
109 assert(!thd->owned_gtid_set.is_empty());
110 res= m_gtid_set->add_gtid_set(&thd->owned_gtid_set) != RETURN_STATUS_OK;
111 #else
112 assert(0);
113 #endif
114 }
115 else if (gtid.sidno > 0) // only one gtid
116 {
117 /*
118 Note that the interface is such that m_sid_map must contain
119 sidno before we add the gtid to m_gtid_set.
120
121 Thus, to avoid relying on global_sid_map and thus contributing
122 to increased contention, we arrange for sidnos on the local
123 sid map.
124 */
125 rpl_sidno local_set_sidno= m_sid_map->add_sid(thd->owned_sid);
126
127 assert(!m_gtid_set->contains_gtid(local_set_sidno, gtid.gno));
128 res= m_gtid_set->ensure_sidno(local_set_sidno) != RETURN_STATUS_OK;
129 if (!res)
130 m_gtid_set->_add_gtid(local_set_sidno, gtid.gno);
131 }
132
133 if (!res)
134 notify_ctx_change_listener();
135 }
136 DBUG_RETURN(res);
137 }
138
139 void Session_consistency_gtids_ctx::
update_tracking_activeness_from_session_variable(const THD * thd)140 update_tracking_activeness_from_session_variable(const THD* thd)
141 {
142 m_curr_session_track_gtids= thd->variables.session_track_gtids;
143 }
144
notify_after_response_packet(const THD * thd)145 bool Session_consistency_gtids_ctx::notify_after_response_packet(const THD *thd)
146 {
147 int res= false;
148 DBUG_ENTER("Rpl_consistency_ctx::notify_after_response_packet");
149
150 if (m_gtid_set && !m_gtid_set->is_empty())
151 m_gtid_set->clear();
152
153 /*
154 Every time we get a notification that a packet was sent, we update
155 this value. It may have changed (the previous command may have been
156 a SET SESSION session_track_gtids=...;).
157 */
158 update_tracking_activeness_from_session_variable(thd);
159 DBUG_RETURN(res);
160 }
161
162 void
register_ctx_change_listener(Session_consistency_gtids_ctx::Ctx_change_listener * listener,THD * thd)163 Session_consistency_gtids_ctx::register_ctx_change_listener(
164 Session_consistency_gtids_ctx::Ctx_change_listener* listener,
165 THD* thd)
166 {
167 assert(m_listener == NULL || m_listener == listener);
168 if (m_listener == NULL)
169 {
170 assert(m_sid_map == NULL && m_gtid_set == NULL);
171 m_listener= listener;
172 m_sid_map= new Sid_map(NULL);
173 m_gtid_set= new Gtid_set(m_sid_map);
174
175 /*
176 Caches the value at startup if needed. This is called during THD::init,
177 if the session_track_gtids value is set at startup time to anything
178 different than OFF.
179 */
180 update_tracking_activeness_from_session_variable(thd);
181 }
182 }
183
unregister_ctx_change_listener(Session_consistency_gtids_ctx::Ctx_change_listener * listener)184 void Session_consistency_gtids_ctx::unregister_ctx_change_listener(
185 Session_consistency_gtids_ctx::Ctx_change_listener* listener)
186 {
187 assert(m_listener == listener || m_listener == NULL);
188
189 if (m_gtid_set)
190 delete m_gtid_set;
191
192 if (m_sid_map)
193 delete m_sid_map;
194
195 m_listener= NULL;
196 m_gtid_set= NULL;
197 m_sid_map= NULL;
198 }
199