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