1 #ifndef SESSION_TRACKER_INCLUDED
2 #define SESSION_TRACKER_INCLUDED
3 
4 /* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
5    Copyright (c) 2016, 2017, MariaDB Corporation.
6 
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; version 2 of the License.
10 
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15 
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
19 
20 #include "m_string.h"
21 #include "thr_lock.h"
22 
23 #ifndef EMBEDDED_LIBRARY
24 /* forward declarations */
25 class THD;
26 class set_var;
27 class String;
28 
29 
30 enum enum_session_tracker
31 {
32   SESSION_SYSVARS_TRACKER,                       /* Session system variables */
33   CURRENT_SCHEMA_TRACKER,                        /* Current schema */
34   SESSION_STATE_CHANGE_TRACKER,
35   TRANSACTION_INFO_TRACKER,                      /* Transaction state */
36   SESSION_TRACKER_END                            /* must be the last */
37 };
38 
39 /**
40   State_tracker
41 
42   An abstract class that defines the interface for any of the server's
43   'session state change tracker'. A tracker, however, is a sub- class of
44   this class which takes care of tracking the change in value of a part-
45   icular session state type and thus defines various methods listed in this
46   interface. The change information is later serialized and transmitted to
47   the client through protocol's OK packet.
48 
49   Tracker system variables :-
50   A tracker is normally mapped to a system variable. So in order to enable,
51   disable or modify the sub-entities of a tracker, the user needs to modify
52   the respective system variable either through SET command or via command
53   line option. As required in system variable handling, this interface also
54   includes two functions to help in the verification of the supplied value
55   (ON_UPDATE) of the tracker system variable, namely - update().
56 */
57 
58 class State_tracker
59 {
60 protected:
61   /**
62     Is tracking enabled for a particular session state type ?
63 
64     @note: it is a cache of the corresponding thd->variables.session_track_xxx
65     variable
66   */
67   bool m_enabled;
68 
69 private:
70   /** Has the session state type changed ? */
71   bool m_changed;
72 
73 public:
~State_tracker()74   virtual ~State_tracker() {}
75 
76   /** Getters */
is_enabled()77   bool is_enabled() const
78   { return m_enabled; }
79 
is_changed()80   bool is_changed() const
81   { return m_changed; }
82 
reset_changed()83   void reset_changed() { m_changed= false; }
84 
85   /**
86     Called by THD::init() when new connection is being created
87 
88     We may inherit m_changed from previous connection served by this THD if
89     connection was broken or client didn't have session tracking capability.
90     Thus we have to reset it here.
91   */
enable(THD * thd)92   virtual bool enable(THD *thd)
93   {
94     reset_changed();
95     return update(thd, 0);
96   }
97 
98   /** To be invoked when the tracker's system variable is updated (ON_UPDATE).*/
99   virtual bool update(THD *thd, set_var *var)= 0;
100 
101   /** Store changed data into the given buffer. */
102   virtual bool store(THD *thd, String *buf)= 0;
103 
104   /** Mark the entity as changed. */
105   virtual void mark_as_changed(THD *thd, LEX_CSTRING *name);
106 };
107 
108 
109 /**
110   Session_sysvars_tracker
111 
112   This is a tracker class that enables & manages the tracking of session
113   system variables. It internally maintains a hash of user supplied variable
114   references and a boolean field to store if the variable was changed by the
115   last statement.
116 */
117 
118 class Session_sysvars_tracker: public State_tracker
119 {
120   struct sysvar_node_st {
121     sys_var *m_svar;
122     bool *test_load;
123     bool m_changed;
124   };
125 
126   class vars_list
127   {
128     /**
129       Registered system variables. (@@session_track_system_variables)
130       A hash to store the name of all the system variables specified by the
131       user.
132     */
133     HASH m_registered_sysvars;
134     /**
135       If TRUE then we want to check all session variable.
136     */
137     bool track_all;
init()138     void init()
139     {
140       my_hash_init(&m_registered_sysvars, &my_charset_bin, 0, 0, 0,
141                    (my_hash_get_key) sysvars_get_key, my_free,
142                    HASH_UNIQUE | (mysqld_server_initialized ?
143                                   HASH_THREAD_SPECIFIC : 0));
144     }
free_hash()145     void free_hash()
146     {
147       DBUG_ASSERT(my_hash_inited(&m_registered_sysvars));
148       my_hash_free(&m_registered_sysvars);
149     }
150 
search(const sys_var * svar)151     sysvar_node_st *search(const sys_var *svar)
152     {
153       return reinterpret_cast<sysvar_node_st*>(
154                my_hash_search(&m_registered_sysvars,
155                              reinterpret_cast<const uchar*>(&svar),
156                              sizeof(sys_var*)));
157     }
158 
at(ulong i)159     sysvar_node_st *at(ulong i)
160     {
161       DBUG_ASSERT(i < m_registered_sysvars.records);
162       return reinterpret_cast<sysvar_node_st*>(
163                my_hash_element(&m_registered_sysvars, i));
164     }
165   public:
vars_list()166     vars_list(): track_all(false) { init(); }
~vars_list()167     ~vars_list() { if (my_hash_inited(&m_registered_sysvars)) free_hash(); }
deinit()168     void deinit() { free_hash(); }
169 
insert_or_search(const sys_var * svar)170     sysvar_node_st *insert_or_search(const sys_var *svar)
171     {
172       sysvar_node_st *res= search(svar);
173       if (!res)
174       {
175         if (track_all)
176         {
177           insert(svar);
178           return search(svar);
179         }
180       }
181       return res;
182     }
183 
184     bool insert(const sys_var *svar);
185     void reinit();
186     void reset();
is_enabled()187     inline bool is_enabled()
188     {
189       return track_all || m_registered_sysvars.records;
190     }
191     void copy(vars_list* from, THD *thd);
192     bool parse_var_list(THD *thd, LEX_STRING var_list, bool throw_error,
193                         CHARSET_INFO *char_set);
194     bool construct_var_list(char *buf, size_t buf_len);
195     bool store(THD *thd, String *buf);
196   };
197   /**
198     Two objects of vars_list type are maintained to manage
199     various operations.
200   */
201   vars_list orig_list;
202   bool m_parsed;
203 
204 public:
205   void init(THD *thd);
206   void deinit(THD *thd);
207   bool enable(THD *thd);
208   bool update(THD *thd, set_var *var);
209   bool store(THD *thd, String *buf);
210   void mark_as_changed(THD *thd, LEX_CSTRING *tracked_item_name);
deinit()211   void deinit() { orig_list.deinit(); }
212   /* callback */
213   static uchar *sysvars_get_key(const char *entry, size_t *length,
214                                 my_bool not_used __attribute__((unused)));
215 
216   friend bool sysvartrack_global_update(THD *thd, char *str, size_t len);
217 };
218 
219 
220 bool sysvartrack_validate_value(THD *thd, const char *str, size_t len);
221 bool sysvartrack_global_update(THD *thd, char *str, size_t len);
222 
223 
224 /**
225   Current_schema_tracker,
226 
227   This is a tracker class that enables & manages the tracking of current
228   schema for a particular connection.
229 */
230 
231 class Current_schema_tracker: public State_tracker
232 {
233 public:
234   bool update(THD *thd, set_var *var);
235   bool store(THD *thd, String *buf);
236 };
237 
238 
239 /*
240   Session_state_change_tracker
241 
242   This is a boolean tracker class that will monitor any change that contributes
243   to a session state change.
244   Attributes that contribute to session state change include:
245      - Successful change to System variables
246      - User defined variables assignments
247      - temporary tables created, altered or deleted
248      - prepared statements added or removed
249      - change in current database
250      - change of current role
251 */
252 
253 class Session_state_change_tracker: public State_tracker
254 {
255 public:
256   bool update(THD *thd, set_var *var);
257   bool store(THD *thd, String *buf);
258 };
259 
260 
261 /*
262   Transaction_state_tracker
263 */
264 
265 /**
266   Transaction state (no transaction, transaction active, work attached, etc.)
267 */
268 enum enum_tx_state {
269   TX_EMPTY        =   0,  ///< "none of the below"
270   TX_EXPLICIT     =   1,  ///< an explicit transaction is active
271   TX_IMPLICIT     =   2,  ///< an implicit transaction is active
272   TX_READ_TRX     =   4,  ///<     transactional reads  were done
273   TX_READ_UNSAFE  =   8,  ///< non-transaction   reads  were done
274   TX_WRITE_TRX    =  16,  ///<     transactional writes were done
275   TX_WRITE_UNSAFE =  32,  ///< non-transactional writes were done
276   TX_STMT_UNSAFE  =  64,  ///< "unsafe" (non-deterministic like UUID()) stmts
277   TX_RESULT_SET   = 128,  ///< result set was sent
278   TX_WITH_SNAPSHOT= 256,  ///< WITH CONSISTENT SNAPSHOT was used
279   TX_LOCKED_TABLES= 512   ///< LOCK TABLES is active
280 };
281 
282 
283 /**
284   Transaction access mode
285 */
286 enum enum_tx_read_flags {
287   TX_READ_INHERIT =   0,  ///< not explicitly set, inherit session.tx_read_only
288   TX_READ_ONLY    =   1,  ///< START TRANSACTION READ ONLY,  or tx_read_only=1
289   TX_READ_WRITE   =   2,  ///< START TRANSACTION READ WRITE, or tx_read_only=0
290 };
291 
292 
293 /**
294   Transaction isolation level
295 */
296 enum enum_tx_isol_level {
297   TX_ISOL_INHERIT     = 0, ///< not explicitly set, inherit session.tx_isolation
298   TX_ISOL_UNCOMMITTED = 1,
299   TX_ISOL_COMMITTED   = 2,
300   TX_ISOL_REPEATABLE  = 3,
301   TX_ISOL_SERIALIZABLE= 4
302 };
303 
304 
305 /**
306   Transaction tracking level
307 */
308 enum enum_session_track_transaction_info {
309   TX_TRACK_NONE      = 0,  ///< do not send tracker items on transaction info
310   TX_TRACK_STATE     = 1,  ///< track transaction status
311   TX_TRACK_CHISTICS  = 2   ///< track status and characteristics
312 };
313 
314 
315 /**
316   This is a tracker class that enables & manages the tracking of
317   current transaction info for a particular connection.
318 */
319 
320 class Transaction_state_tracker : public State_tracker
321 {
322   /** Helper function: turn table info into table access flag */
323   enum_tx_state calc_trx_state(THD *thd, thr_lock_type l, bool has_trx);
324 public:
325 
enable(THD * thd)326   bool enable(THD *thd)
327   {
328     m_enabled= false;
329     tx_changed= TX_CHG_NONE;
330     tx_curr_state= TX_EMPTY;
331     tx_reported_state= TX_EMPTY;
332     tx_read_flags= TX_READ_INHERIT;
333     tx_isol_level= TX_ISOL_INHERIT;
334     return State_tracker::enable(thd);
335   }
336 
337   bool update(THD *thd, set_var *var);
338   bool store(THD *thd, String *buf);
339 
340   /** Change transaction characteristics */
341   void set_read_flags(THD *thd, enum enum_tx_read_flags flags);
342   void set_isol_level(THD *thd, enum enum_tx_isol_level level);
343 
344   /** Change transaction state */
345   void clear_trx_state(THD *thd, uint clear);
346   void add_trx_state(THD *thd, uint add);
add_trx_state(THD * thd,thr_lock_type l,bool has_trx)347   void inline add_trx_state(THD *thd, thr_lock_type l, bool has_trx)
348   {
349     add_trx_state(thd, calc_trx_state(thd, l, has_trx));
350   }
351   void add_trx_state_from_thd(THD *thd);
352   void end_trx(THD *thd);
353 
354 
355 private:
356   enum enum_tx_changed {
357     TX_CHG_NONE     = 0,  ///< no changes from previous stmt
358     TX_CHG_STATE    = 1,  ///< state has changed from previous stmt
359     TX_CHG_CHISTICS = 2   ///< characteristics have changed from previous stmt
360   };
361 
362   /** any trackable changes caused by this statement? */
363   uint                     tx_changed;
364 
365   /** transaction state */
366   uint                     tx_curr_state,  tx_reported_state;
367 
368   /** r/w or r/o set? session default? */
369   enum enum_tx_read_flags  tx_read_flags;
370 
371   /**  isolation level */
372   enum enum_tx_isol_level  tx_isol_level;
373 
update_change_flags(THD * thd)374   inline void update_change_flags(THD *thd)
375   {
376     tx_changed &= uint(~TX_CHG_STATE);
377     tx_changed |= (tx_curr_state != tx_reported_state) ? TX_CHG_STATE : 0;
378     if (tx_changed != TX_CHG_NONE)
379       mark_as_changed(thd, NULL);
380   }
381 };
382 
383 #define TRANSACT_TRACKER(X) \
384  do { if (thd->variables.session_track_transaction_info > TX_TRACK_NONE) \
385         thd->session_tracker.transaction_info.X; } while(0)
386 #define SESSION_TRACKER_CHANGED(A,B,C) \
387   thd->session_tracker.mark_as_changed(A,B,C)
388 
389 
390 /**
391   Session_tracker
392 
393   This class holds an object each for all tracker classes and provides
394   methods necessary for systematic detection and generation of session
395   state change information.
396 */
397 
398 class Session_tracker
399 {
400   State_tracker *m_trackers[SESSION_TRACKER_END];
401 
402   /* The following two functions are private to disable copying. */
Session_tracker(Session_tracker const & other)403   Session_tracker(Session_tracker const &other)
404   {
405     DBUG_ASSERT(FALSE);
406   }
407   Session_tracker& operator= (Session_tracker const &rhs)
408   {
409     DBUG_ASSERT(FALSE);
410     return *this;
411   }
412 
413 public:
414   Current_schema_tracker current_schema;
415   Session_state_change_tracker state_change;
416   Transaction_state_tracker transaction_info;
417   Session_sysvars_tracker sysvars;
418 
Session_tracker()419   Session_tracker()
420   {
421     m_trackers[SESSION_SYSVARS_TRACKER]= &sysvars;
422     m_trackers[CURRENT_SCHEMA_TRACKER]= &current_schema;
423     m_trackers[SESSION_STATE_CHANGE_TRACKER]= &state_change;
424     m_trackers[TRANSACTION_INFO_TRACKER]= &transaction_info;
425   }
426 
enable(THD * thd)427   void enable(THD *thd)
428   {
429     for (int i= 0; i < SESSION_TRACKER_END; i++)
430       m_trackers[i]->enable(thd);
431   }
432 
mark_as_changed(THD * thd,enum enum_session_tracker tracker,LEX_CSTRING * data)433   inline void mark_as_changed(THD *thd, enum enum_session_tracker tracker,
434                               LEX_CSTRING *data)
435   {
436     if (m_trackers[tracker]->is_enabled())
437       m_trackers[tracker]->mark_as_changed(thd, data);
438   }
439 
440 
441   void store(THD *thd, String *main_buf);
442 };
443 
444 
445 int session_tracker_init();
446 #else
447 
448 #define TRANSACT_TRACKER(X) do{}while(0)
449 #define SESSION_TRACKER_CHANGED(A,B,C) do{}while(0)
450 
451 #endif //EMBEDDED_LIBRARY
452 
453 #endif /* SESSION_TRACKER_INCLUDED */
454