1/* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
2 Copyright (c) 2016, MariaDB
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; version 2 of the License.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
16
17
18#ifndef EMBEDDED_LIBRARY
19#include "sql_plugin.h"
20#include "session_tracker.h"
21
22#include "hash.h"
23#include "table.h"
24#include "rpl_gtid.h"
25#include "sql_class.h"
26#include "sql_show.h"
27#include "sql_plugin.h"
28#include "set_var.h"
29
30void State_tracker::mark_as_changed(THD *thd, LEX_CSTRING *tracked_item_name)
31{
32 m_changed= true;
33 thd->lex->safe_to_cache_query= 0;
34 thd->server_status|= SERVER_SESSION_STATE_CHANGED;
35}
36
37
38class Not_implemented_tracker : public State_tracker
39{
40public:
41 bool enable(THD *thd)
42 { return false; }
43 bool update(THD *, set_var *)
44 { return false; }
45 bool store(THD *, String *)
46 { return false; }
47 void mark_as_changed(THD *, LEX_CSTRING *tracked_item_name)
48 {}
49
50};
51
52/**
53 Session_sysvars_tracker
54
55 This is a tracker class that enables & manages the tracking of session
56 system variables. It internally maintains a hash of user supplied variable
57 references and a boolean field to store if the variable was changed by the
58 last statement.
59*/
60
61class Session_sysvars_tracker : public State_tracker
62{
63private:
64
65 struct sysvar_node_st {
66 sys_var *m_svar;
67 bool *test_load;
68 bool m_changed;
69 };
70
71 class vars_list
72 {
73 private:
74 /**
75 Registered system variables. (@@session_track_system_variables)
76 A hash to store the name of all the system variables specified by the
77 user.
78 */
79 HASH m_registered_sysvars;
80 /** Size of buffer for string representation */
81 size_t buffer_length;
82 myf m_mem_flag;
83 /**
84 If TRUE then we want to check all session variable.
85 */
86 bool track_all;
87 void init()
88 {
89 my_hash_init(&m_registered_sysvars,
90 &my_charset_bin,
91 4, 0, 0, (my_hash_get_key) sysvars_get_key,
92 my_free, MYF(HASH_UNIQUE |
93 ((m_mem_flag & MY_THREAD_SPECIFIC) ?
94 HASH_THREAD_SPECIFIC : 0)));
95 }
96 void free_hash()
97 {
98 if (my_hash_inited(&m_registered_sysvars))
99 {
100 my_hash_free(&m_registered_sysvars);
101 }
102 }
103
104 uchar* search(const sys_var *svar)
105 {
106 return (my_hash_search(&m_registered_sysvars, (const uchar *)&svar,
107 sizeof(sys_var *)));
108 }
109
110 public:
111 vars_list() :
112 buffer_length(0)
113 {
114 m_mem_flag= current_thd ? MY_THREAD_SPECIFIC : 0;
115 init();
116 }
117
118 size_t get_buffer_length()
119 {
120 DBUG_ASSERT(buffer_length != 0); // asked earlier then should
121 return buffer_length;
122 }
123 ~vars_list()
124 {
125 /* free the allocated hash. */
126 if (my_hash_inited(&m_registered_sysvars))
127 {
128 my_hash_free(&m_registered_sysvars);
129 }
130 }
131
132 uchar* insert_or_search(sysvar_node_st *node, const sys_var *svar)
133 {
134 uchar *res;
135 res= search(svar);
136 if (!res)
137 {
138 if (track_all)
139 {
140 insert(node, svar, m_mem_flag);
141 return search(svar);
142 }
143 }
144 return res;
145 }
146
147 bool insert(sysvar_node_st *node, const sys_var *svar, myf mem_flag);
148 void reinit();
149 void reset();
150 inline bool is_enabled()
151 {
152 return track_all || m_registered_sysvars.records;
153 }
154 void copy(vars_list* from, THD *thd);
155 bool parse_var_list(THD *thd, LEX_STRING var_list, bool throw_error,
156 CHARSET_INFO *char_set, bool take_mutex);
157 bool construct_var_list(char *buf, size_t buf_len);
158 bool store(THD *thd, String *buf);
159 };
160 /**
161 Two objects of vars_list type are maintained to manage
162 various operations.
163 */
164 vars_list *orig_list, *tool_list;
165
166public:
167 Session_sysvars_tracker()
168 {
169 orig_list= new (std::nothrow) vars_list();
170 tool_list= new (std::nothrow) vars_list();
171 }
172
173 ~Session_sysvars_tracker()
174 {
175 if (orig_list)
176 delete orig_list;
177 if (tool_list)
178 delete tool_list;
179 }
180
181 size_t get_buffer_length()
182 {
183 return orig_list->get_buffer_length();
184 }
185 bool construct_var_list(char *buf, size_t buf_len)
186 {
187 return orig_list->construct_var_list(buf, buf_len);
188 }
189
190 /**
191 Method used to check the validity of string provided
192 for session_track_system_variables during the server
193 startup.
194 */
195 static bool server_init_check(THD *thd, CHARSET_INFO *char_set,
196 LEX_STRING var_list)
197 {
198 return check_var_list(thd, var_list, false, char_set, false);
199 }
200
201 static bool server_init_process(THD *thd, CHARSET_INFO *char_set,
202 LEX_STRING var_list)
203 {
204 vars_list dummy;
205 bool result;
206 result= dummy.parse_var_list(thd, var_list, false, char_set, false);
207 if (!result)
208 dummy.construct_var_list(var_list.str, var_list.length + 1);
209 return result;
210 }
211
212 void reset();
213 bool enable(THD *thd);
214 bool check_str(THD *thd, LEX_STRING *val);
215 bool update(THD *thd, set_var *var);
216 bool store(THD *thd, String *buf);
217 void mark_as_changed(THD *thd, LEX_CSTRING *tracked_item_name);
218 /* callback */
219 static uchar *sysvars_get_key(const char *entry, size_t *length,
220 my_bool not_used __attribute__((unused)));
221
222 // hash iterators
223 static my_bool name_array_filler(void *ptr, void *data_ptr);
224 static my_bool store_variable(void *ptr, void *data_ptr);
225 static my_bool reset_variable(void *ptr, void *data_ptr);
226
227 static bool check_var_list(THD *thd, LEX_STRING var_list, bool throw_error,
228 CHARSET_INFO *char_set, bool take_mutex);
229};
230
231
232
233/**
234 Current_schema_tracker,
235
236 This is a tracker class that enables & manages the tracking of current
237 schema for a particular connection.
238*/
239
240class Current_schema_tracker : public State_tracker
241{
242private:
243 bool schema_track_inited;
244 void reset();
245
246public:
247
248 Current_schema_tracker()
249 {
250 schema_track_inited= false;
251 }
252
253 bool enable(THD *thd)
254 { return update(thd, NULL); }
255 bool update(THD *thd, set_var *var);
256 bool store(THD *thd, String *buf);
257};
258
259/*
260 Session_state_change_tracker
261
262 This is a boolean tracker class that will monitor any change that contributes
263 to a session state change.
264 Attributes that contribute to session state change include:
265 - Successful change to System variables
266 - User defined variables assignments
267 - temporary tables created, altered or deleted
268 - prepared statements added or removed
269 - change in current database
270 - change of current role
271*/
272
273class Session_state_change_tracker : public State_tracker
274{
275private:
276
277 void reset();
278
279public:
280 Session_state_change_tracker();
281 bool enable(THD *thd)
282 { return update(thd, NULL); };
283 bool update(THD *thd, set_var *var);
284 bool store(THD *thd, String *buf);
285 bool is_state_changed(THD*);
286};
287
288
289/* To be used in expanding the buffer. */
290static const unsigned int EXTRA_ALLOC= 1024;
291
292
293void Session_sysvars_tracker::vars_list::reinit()
294{
295 buffer_length= 0;
296 track_all= 0;
297 if (m_registered_sysvars.records)
298 my_hash_reset(&m_registered_sysvars);
299}
300
301/**
302 Copy the given list.
303
304 @param from Source vars_list object.
305 @param thd THD handle to retrive the charset in use.
306
307 @retval true there is something to track
308 @retval false nothing to track
309*/
310
311void Session_sysvars_tracker::vars_list::copy(vars_list* from, THD *thd)
312{
313 reinit();
314 track_all= from->track_all;
315 free_hash();
316 buffer_length= from->buffer_length;
317 m_registered_sysvars= from->m_registered_sysvars;
318 from->init();
319}
320
321/**
322 Inserts the variable to be tracked into m_registered_sysvars hash.
323
324 @param node Node to be inserted.
325 @param svar address of the system variable
326
327 @retval false success
328 @retval true error
329*/
330
331bool Session_sysvars_tracker::vars_list::insert(sysvar_node_st *node,
332 const sys_var *svar,
333 myf mem_flag)
334{
335 if (!node)
336 {
337 if (!(node= (sysvar_node_st *) my_malloc(sizeof(sysvar_node_st),
338 MYF(MY_WME | mem_flag))))
339 {
340 reinit();
341 return true;
342 }
343 }
344
345 node->m_svar= (sys_var *)svar;
346 node->test_load= node->m_svar->test_load;
347 node->m_changed= false;
348 if (my_hash_insert(&m_registered_sysvars, (uchar *) node))
349 {
350 my_free(node);
351 if (!search((sys_var *)svar))
352 {
353 //EOF (error is already reported)
354 reinit();
355 return true;
356 }
357 }
358 return false;
359}
360
361/**
362 Parse the specified system variables list.
363
364 @Note In case of invalid entry a warning is raised per invalid entry.
365 This is done in order to handle 'potentially' valid system
366 variables from uninstalled plugins which might get installed in
367 future.
368
369
370 @param thd [IN] The thd handle.
371 @param var_list [IN] System variable list.
372 @param throw_error [IN] bool when set to true, returns an error
373 in case of invalid/duplicate values.
374 @param char_set [IN] charecter set information used for string
375 manipulations.
376 @param take_mutex [IN] take LOCK_plugin
377
378 @return
379 true Error
380 false Success
381*/
382bool Session_sysvars_tracker::vars_list::parse_var_list(THD *thd,
383 LEX_STRING var_list,
384 bool throw_error,
385 CHARSET_INFO *char_set,
386 bool take_mutex)
387{
388 const char separator= ',';
389 char *token, *lasts= NULL;
390 size_t rest= var_list.length;
391 reinit();
392
393 if (!var_list.str || var_list.length == 0)
394 {
395 buffer_length= 1;
396 return false;
397 }
398
399 if(!strcmp(var_list.str, "*"))
400 {
401 track_all= true;
402 buffer_length= 2;
403 return false;
404 }
405
406 buffer_length= var_list.length + 1;
407 token= var_list.str;
408
409 track_all= false;
410 /*
411 If Lock to the plugin mutex is not acquired here itself, it results
412 in having to acquire it multiple times in find_sys_var_ex for each
413 token value. Hence the mutex is handled here to avoid a performance
414 overhead.
415 */
416 if (!thd || take_mutex)
417 mysql_mutex_lock(&LOCK_plugin);
418 for (;;)
419 {
420 sys_var *svar;
421 LEX_CSTRING var;
422
423 lasts= (char *) memchr(token, separator, rest);
424
425 var.str= token;
426 if (lasts)
427 {
428 var.length= (lasts - token);
429 rest-= var.length + 1;
430 }
431 else
432 var.length= rest;
433
434 /* Remove leading/trailing whitespace. */
435 trim_whitespace(char_set, &var);
436
437 if(!strcmp(var.str, "*"))
438 {
439 track_all= true;
440 }
441 else if ((svar=
442 find_sys_var_ex(thd, var.str, var.length, throw_error, true)))
443 {
444 if (insert(NULL, svar, m_mem_flag) == TRUE)
445 goto error;
446 }
447 else if (throw_error && thd)
448 {
449 push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
450 ER_WRONG_VALUE_FOR_VAR,
451 "%.*s is not a valid system variable and will"
452 "be ignored.", (int)var.length, token);
453 }
454 else
455 goto error;
456
457 if (lasts)
458 token= lasts + 1;
459 else
460 break;
461 }
462 if (!thd || take_mutex)
463 mysql_mutex_unlock(&LOCK_plugin);
464
465 return false;
466
467error:
468 if (!thd || take_mutex)
469 mysql_mutex_unlock(&LOCK_plugin);
470 return true;
471}
472
473
474bool Session_sysvars_tracker::check_var_list(THD *thd,
475 LEX_STRING var_list,
476 bool throw_error,
477 CHARSET_INFO *char_set,
478 bool take_mutex)
479{
480 const char separator= ',';
481 char *token, *lasts= NULL;
482 size_t rest= var_list.length;
483
484 if (!var_list.str || var_list.length == 0 ||
485 !strcmp(var_list.str, "*"))
486 {
487 return false;
488 }
489
490 token= var_list.str;
491
492 /*
493 If Lock to the plugin mutex is not acquired here itself, it results
494 in having to acquire it multiple times in find_sys_var_ex for each
495 token value. Hence the mutex is handled here to avoid a performance
496 overhead.
497 */
498 if (!thd || take_mutex)
499 mysql_mutex_lock(&LOCK_plugin);
500 for (;;)
501 {
502 LEX_CSTRING var;
503
504 lasts= (char *) memchr(token, separator, rest);
505
506 var.str= token;
507 if (lasts)
508 {
509 var.length= (lasts - token);
510 rest-= var.length + 1;
511 }
512 else
513 var.length= rest;
514
515 /* Remove leading/trailing whitespace. */
516 trim_whitespace(char_set, &var);
517
518 if(!strcmp(var.str, "*") &&
519 !find_sys_var_ex(thd, var.str, var.length, throw_error, true))
520 {
521 if (throw_error && take_mutex && thd)
522 {
523 push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
524 ER_WRONG_VALUE_FOR_VAR,
525 "%.*s is not a valid system variable and will"
526 "be ignored.", (int)var.length, token);
527 }
528 else
529 {
530 if (!thd || take_mutex)
531 mysql_mutex_unlock(&LOCK_plugin);
532 return true;
533 }
534 }
535
536 if (lasts)
537 token= lasts + 1;
538 else
539 break;
540 }
541 if (!thd || take_mutex)
542 mysql_mutex_unlock(&LOCK_plugin);
543
544 return false;
545}
546
547struct name_array_filler_data
548{
549 LEX_CSTRING **names;
550 uint idx;
551
552};
553
554/** Collects variable references into array */
555my_bool Session_sysvars_tracker::name_array_filler(void *ptr,
556 void *data_ptr)
557{
558 Session_sysvars_tracker::sysvar_node_st *node=
559 (Session_sysvars_tracker::sysvar_node_st *)ptr;
560 name_array_filler_data *data= (struct name_array_filler_data *)data_ptr;
561 if (*node->test_load)
562 data->names[data->idx++]= &node->m_svar->name;
563 return FALSE;
564}
565
566/* Sorts variable references array */
567static int name_array_sorter(const void *a, const void *b)
568{
569 LEX_CSTRING **an= (LEX_CSTRING **)a, **bn=(LEX_CSTRING **)b;
570 size_t min= MY_MIN((*an)->length, (*bn)->length);
571 int res= strncmp((*an)->str, (*bn)->str, min);
572 if (res == 0)
573 res= ((int)(*bn)->length)- ((int)(*an)->length);
574 return res;
575}
576
577/**
578 Construct variable list by internal hash with references
579*/
580
581bool Session_sysvars_tracker::vars_list::construct_var_list(char *buf,
582 size_t buf_len)
583{
584 struct name_array_filler_data data;
585 size_t left= buf_len;
586 size_t names_size= m_registered_sysvars.records * sizeof(LEX_CSTRING *);
587 const char separator= ',';
588
589 if (unlikely(buf_len < 1))
590 return true;
591
592 if (unlikely(track_all))
593 {
594 if (buf_len < 2)
595 return true;
596 buf[0]= '*';
597 buf[1]= '\0';
598 return false;
599 }
600
601 if (m_registered_sysvars.records == 0)
602 {
603 buf[0]= '\0';
604 return false;
605 }
606
607 data.names= (LEX_CSTRING**)my_safe_alloca(names_size);
608
609 if (unlikely(!data.names))
610 return true;
611
612 data.idx= 0;
613
614 mysql_mutex_lock(&LOCK_plugin);
615 my_hash_iterate(&m_registered_sysvars, &name_array_filler, &data);
616 DBUG_ASSERT(data.idx <= m_registered_sysvars.records);
617
618 /*
619 We check number of records again here because number of variables
620 could be reduced in case of plugin unload.
621 */
622 if (m_registered_sysvars.records == 0)
623 {
624 mysql_mutex_unlock(&LOCK_plugin);
625 buf[0]= '\0';
626 return false;
627 }
628
629 my_qsort(data.names, data.idx, sizeof(LEX_CSTRING *),
630 &name_array_sorter);
631
632 for(uint i= 0; i < data.idx; i++)
633 {
634 LEX_CSTRING *nm= data.names[i];
635 size_t ln= nm->length + 1;
636 if (ln > left)
637 {
638 mysql_mutex_unlock(&LOCK_plugin);
639 my_safe_afree(data.names, names_size);
640 return true;
641 }
642 memcpy(buf, nm->str, nm->length);
643 buf[nm->length]= separator;
644 buf+= ln;
645 left-= ln;
646 }
647 mysql_mutex_unlock(&LOCK_plugin);
648
649 buf--; buf[0]= '\0';
650 my_safe_afree(data.names, names_size);
651
652 return false;
653}
654
655/**
656 Enable session tracker by parsing global value of tracked variables.
657
658 @param thd [IN] The thd handle.
659
660 @retval true Error
661 @retval false Success
662*/
663
664bool Session_sysvars_tracker::enable(THD *thd)
665{
666 mysql_mutex_lock(&LOCK_plugin);
667 LEX_STRING tmp;
668 tmp.str= global_system_variables.session_track_system_variables;
669 tmp.length= safe_strlen(tmp.str);
670 if (tool_list->parse_var_list(thd, tmp,
671 true, thd->charset(), false) == true)
672 {
673 mysql_mutex_unlock(&LOCK_plugin);
674 return true;
675 }
676 mysql_mutex_unlock(&LOCK_plugin);
677 orig_list->copy(tool_list, thd);
678 m_enabled= true;
679
680 return false;
681}
682
683
684/**
685 Check system variable name(s).
686
687 @note This function is called from the ON_CHECK() function of the
688 session_track_system_variables' sys_var class.
689
690 @param thd [IN] The thd handle.
691 @param var [IN] A pointer to set_var holding the specified list of
692 system variable names.
693
694 @retval true Error
695 @retval false Success
696*/
697
698inline bool Session_sysvars_tracker::check_str(THD *thd, LEX_STRING *val)
699{
700 return Session_sysvars_tracker::check_var_list(thd, *val, true,
701 thd->charset(), true);
702}
703
704
705/**
706 Once the value of the @@session_track_system_variables has been
707 successfully updated, this function calls
708 Session_sysvars_tracker::vars_list::copy updating the hash in orig_list
709 which represents the system variables to be tracked.
710
711 @note This function is called from the ON_UPDATE() function of the
712 session_track_system_variables' sys_var class.
713
714 @param thd [IN] The thd handle.
715
716 @retval true Error
717 @retval false Success
718*/
719
720bool Session_sysvars_tracker::update(THD *thd, set_var *var)
721{
722 /*
723 We are doing via tool list because there possible errors with memory
724 in this case value will be unchanged.
725 */
726 tool_list->reinit();
727 if (tool_list->parse_var_list(thd, var->save_result.string_value, true,
728 thd->charset(), true))
729 return true;
730 orig_list->copy(tool_list, thd);
731 return false;
732}
733
734
735/*
736 Function and structure to support storing variables from hash to the buffer.
737*/
738
739struct st_store_variable_param
740{
741 THD *thd;
742 String *buf;
743};
744
745my_bool Session_sysvars_tracker::store_variable(void *ptr, void *data_ptr)
746{
747 Session_sysvars_tracker::sysvar_node_st *node=
748 (Session_sysvars_tracker::sysvar_node_st *)ptr;
749 if (node->m_changed)
750 {
751 THD *thd= ((st_store_variable_param *)data_ptr)->thd;
752 String *buf= ((st_store_variable_param *)data_ptr)->buf;
753 char val_buf[SHOW_VAR_FUNC_BUFF_SIZE];
754 SHOW_VAR show;
755 CHARSET_INFO *charset;
756 size_t val_length, length;
757 mysql_mutex_lock(&LOCK_plugin);
758 if (!*node->test_load)
759 {
760 mysql_mutex_unlock(&LOCK_plugin);
761 return false;
762 }
763 sys_var *svar= node->m_svar;
764 bool is_plugin= svar->cast_pluginvar();
765 if (!is_plugin)
766 mysql_mutex_unlock(&LOCK_plugin);
767
768 /* As its always system variable. */
769 show.type= SHOW_SYS;
770 show.name= svar->name.str;
771 show.value= (char *) svar;
772
773 const char *value= get_one_variable(thd, &show, OPT_SESSION, SHOW_SYS, NULL,
774 &charset, val_buf, &val_length);
775 if (is_plugin)
776 mysql_mutex_unlock(&LOCK_plugin);
777
778 length= net_length_size(svar->name.length) +
779 svar->name.length +
780 net_length_size(val_length) +
781 val_length;
782
783 compile_time_assert(SESSION_TRACK_SYSTEM_VARIABLES < 251);
784 if (unlikely((1 + net_length_size(length) + length + buf->length() >=
785 MAX_PACKET_LENGTH) ||
786 buf->reserve(1 + net_length_size(length) + length,
787 EXTRA_ALLOC)))
788 return true;
789
790
791 /* Session state type (SESSION_TRACK_SYSTEM_VARIABLES) */
792 buf->q_append((char)SESSION_TRACK_SYSTEM_VARIABLES);
793
794 /* Length of the overall entity. */
795 buf->q_net_store_length((ulonglong)length);
796
797 /* System variable's name (length-encoded string). */
798 buf->q_net_store_data((const uchar*)svar->name.str, svar->name.length);
799
800 /* System variable's value (length-encoded string). */
801 buf->q_net_store_data((const uchar*)value, val_length);
802 }
803 return false;
804}
805
806bool Session_sysvars_tracker::vars_list::store(THD *thd, String *buf)
807{
808 st_store_variable_param data= {thd, buf};
809 return my_hash_iterate(&m_registered_sysvars, &store_variable, &data);
810}
811
812/**
813 Store the data for changed system variables in the specified buffer.
814 Once the data is stored, we reset the flags related to state-change
815 (see reset()).
816
817 @param thd [IN] The thd handle.
818 @paran buf [INOUT] Buffer to store the information to.
819
820 @retval true Error
821 @retval false Success
822*/
823
824bool Session_sysvars_tracker::store(THD *thd, String *buf)
825{
826 if (!orig_list->is_enabled())
827 return false;
828
829 if (orig_list->store(thd, buf))
830 return true;
831
832 reset();
833
834 return false;
835}
836
837
838/**
839 Mark the system variable as changed.
840
841 @param [IN] pointer on a variable
842*/
843
844void Session_sysvars_tracker::mark_as_changed(THD *thd,
845 LEX_CSTRING *var)
846{
847 sysvar_node_st *node= NULL;
848 sys_var *svar= (sys_var *)var;
849 /*
850 Check if the specified system variable is being tracked, if so
851 mark it as changed and also set the class's m_changed flag.
852 */
853 if (orig_list->is_enabled() &&
854 (node= (sysvar_node_st *) (orig_list->insert_or_search(node, svar))))
855 {
856 node->m_changed= true;
857 State_tracker::mark_as_changed(thd, var);
858 }
859}
860
861
862/**
863 Supply key to a hash.
864
865 @param entry [IN] A single entry.
866 @param length [OUT] Length of the key.
867 @param not_used Unused.
868
869 @return Pointer to the key buffer.
870*/
871
872uchar *Session_sysvars_tracker::sysvars_get_key(const char *entry,
873 size_t *length,
874 my_bool not_used __attribute__((unused)))
875{
876 *length= sizeof(sys_var *);
877 return (uchar *) &(((sysvar_node_st *) entry)->m_svar);
878}
879
880
881/* Function to support resetting hash nodes for the variables */
882
883my_bool Session_sysvars_tracker::reset_variable(void *ptr,
884 void *data_ptr)
885{
886 ((Session_sysvars_tracker::sysvar_node_st *)ptr)->m_changed= false;
887 return false;
888}
889
890void Session_sysvars_tracker::vars_list::reset()
891{
892 my_hash_iterate(&m_registered_sysvars, &reset_variable, NULL);
893}
894
895/**
896 Prepare/reset the m_registered_sysvars hash for next statement.
897*/
898
899void Session_sysvars_tracker::reset()
900{
901
902 orig_list->reset();
903 m_changed= false;
904}
905
906static Session_sysvars_tracker* sysvar_tracker(THD *thd)
907{
908 return (Session_sysvars_tracker*)
909 thd->session_tracker.get_tracker(SESSION_SYSVARS_TRACKER);
910}
911
912bool sysvartrack_validate_value(THD *thd, const char *str, size_t len)
913{
914 LEX_STRING tmp= {(char *)str, len};
915 return Session_sysvars_tracker::server_init_check(thd, system_charset_info,
916 tmp);
917}
918bool sysvartrack_reprint_value(THD *thd, char *str, size_t len)
919{
920 LEX_STRING tmp= {str, len};
921 return Session_sysvars_tracker::server_init_process(thd,
922 system_charset_info,
923 tmp);
924}
925bool sysvartrack_update(THD *thd, set_var *var)
926{
927 return sysvar_tracker(thd)->update(thd, var);
928}
929size_t sysvartrack_value_len(THD *thd)
930{
931 return sysvar_tracker(thd)->get_buffer_length();
932}
933bool sysvartrack_value_construct(THD *thd, char *val, size_t len)
934{
935 return sysvar_tracker(thd)->construct_var_list(val, len);
936}
937
938///////////////////////////////////////////////////////////////////////////////
939
940/**
941 Enable/disable the tracker based on @@session_track_schema's value.
942
943 @param thd [IN] The thd handle.
944
945 @return
946 false (always)
947*/
948
949bool Current_schema_tracker::update(THD *thd, set_var *)
950{
951 m_enabled= thd->variables.session_track_schema;
952 return false;
953}
954
955
956/**
957 Store the schema name as length-encoded string in the specified buffer.
958
959 @param thd [IN] The thd handle.
960 @paran buf [INOUT] Buffer to store the information to.
961
962 @reval false Success
963 @retval true Error
964*/
965
966bool Current_schema_tracker::store(THD *thd, String *buf)
967{
968 size_t db_length, length;
969
970 /*
971 Protocol made (by unknown reasons) redundant:
972 It saves length of database name and name of database name +
973 length of saved length of database length.
974 */
975 length= db_length= thd->db.length;
976 length += net_length_size(length);
977
978 compile_time_assert(SESSION_TRACK_SCHEMA < 251);
979 compile_time_assert(NAME_LEN < 251);
980 DBUG_ASSERT(length < 251);
981 if (unlikely((1 + 1 + length + buf->length() >= MAX_PACKET_LENGTH) ||
982 buf->reserve(1 + 1 + length, EXTRA_ALLOC)))
983 return true;
984
985 /* Session state type (SESSION_TRACK_SCHEMA) */
986 buf->q_append((char)SESSION_TRACK_SCHEMA);
987
988 /* Length of the overall entity. */
989 buf->q_net_store_length(length);
990
991 /* Length and current schema name */
992 buf->q_net_store_data((const uchar *)thd->db.str, thd->db.length);
993
994 reset();
995
996 return false;
997}
998
999
1000/**
1001 Reset the m_changed flag for next statement.
1002
1003 @return void
1004*/
1005
1006void Current_schema_tracker::reset()
1007{
1008 m_changed= false;
1009}
1010
1011
1012///////////////////////////////////////////////////////////////////////////////
1013
1014
1015Transaction_state_tracker::Transaction_state_tracker()
1016{
1017 m_enabled = false;
1018 tx_changed = TX_CHG_NONE;
1019 tx_curr_state =
1020 tx_reported_state= TX_EMPTY;
1021 tx_read_flags = TX_READ_INHERIT;
1022 tx_isol_level = TX_ISOL_INHERIT;
1023}
1024
1025/**
1026 Enable/disable the tracker based on @@session_track_transaction_info.
1027
1028 @param thd [IN] The thd handle.
1029
1030 @retval true if updating the tracking level failed
1031 @retval false otherwise
1032*/
1033
1034bool Transaction_state_tracker::update(THD *thd, set_var *)
1035{
1036 if (thd->variables.session_track_transaction_info != TX_TRACK_NONE)
1037 {
1038 /*
1039 If we only just turned reporting on (rather than changing between
1040 state and characteristics reporting), start from a defined state.
1041 */
1042 if (!m_enabled)
1043 {
1044 tx_curr_state =
1045 tx_reported_state = TX_EMPTY;
1046 tx_changed |= TX_CHG_STATE;
1047 m_enabled= true;
1048 }
1049 if (thd->variables.session_track_transaction_info == TX_TRACK_CHISTICS)
1050 tx_changed |= TX_CHG_CHISTICS;
1051 mark_as_changed(thd, NULL);
1052 }
1053 else
1054 m_enabled= false;
1055
1056 return false;
1057}
1058
1059
1060/**
1061 Store the transaction state (and, optionally, characteristics)
1062 as length-encoded string in the specified buffer. Once the data
1063 is stored, we reset the flags related to state-change (see reset()).
1064
1065
1066 @param thd [IN] The thd handle.
1067 @paran buf [INOUT] Buffer to store the information to.
1068
1069 @retval false Success
1070 @retval true Error
1071*/
1072
1073static LEX_CSTRING isol[]= {
1074 { STRING_WITH_LEN("READ UNCOMMITTED") },
1075 { STRING_WITH_LEN("READ COMMITTED") },
1076 { STRING_WITH_LEN("REPEATABLE READ") },
1077 { STRING_WITH_LEN("SERIALIZABLE") }
1078};
1079
1080bool Transaction_state_tracker::store(THD *thd, String *buf)
1081{
1082 /* STATE */
1083 if (tx_changed & TX_CHG_STATE)
1084 {
1085 if (unlikely((11 + buf->length() >= MAX_PACKET_LENGTH) ||
1086 buf->reserve(11, EXTRA_ALLOC)))
1087 return true;
1088
1089 buf->q_append((char)SESSION_TRACK_TRANSACTION_STATE);
1090
1091 buf->q_append((char)9); // whole packet length
1092 buf->q_append((char)8); // results length
1093
1094 buf->q_append((char)((tx_curr_state & TX_EXPLICIT) ? 'T' :
1095 ((tx_curr_state & TX_IMPLICIT) ? 'I' : '_')));
1096 buf->q_append((char)((tx_curr_state & TX_READ_UNSAFE) ? 'r' : '_'));
1097 buf->q_append((char)(((tx_curr_state & TX_READ_TRX) ||
1098 (tx_curr_state & TX_WITH_SNAPSHOT)) ? 'R' : '_'));
1099 buf->q_append((char)((tx_curr_state & TX_WRITE_UNSAFE) ? 'w' : '_'));
1100 buf->q_append((char)((tx_curr_state & TX_WRITE_TRX) ? 'W' : '_'));
1101 buf->q_append((char)((tx_curr_state & TX_STMT_UNSAFE) ? 's' : '_'));
1102 buf->q_append((char)((tx_curr_state & TX_RESULT_SET) ? 'S' : '_'));
1103 buf->q_append((char)((tx_curr_state & TX_LOCKED_TABLES) ? 'L' : '_'));
1104 }
1105
1106 /* CHARACTERISTICS -- How to restart the transaction */
1107
1108 if ((thd->variables.session_track_transaction_info == TX_TRACK_CHISTICS) &&
1109 (tx_changed & TX_CHG_CHISTICS))
1110 {
1111 bool is_xa= (thd->transaction.xid_state.xa_state != XA_NOTR);
1112 size_t start;
1113
1114 /* 2 length by 1 byte and code */
1115 if (unlikely((1 + 1 + 1 + 110 + buf->length() >= MAX_PACKET_LENGTH) ||
1116 buf->reserve(1 + 1 + 1, EXTRA_ALLOC)))
1117 return true;
1118
1119 compile_time_assert(SESSION_TRACK_TRANSACTION_CHARACTERISTICS < 251);
1120 /* Session state type (SESSION_TRACK_TRANSACTION_CHARACTERISTICS) */
1121 buf->q_append((char)SESSION_TRACK_TRANSACTION_CHARACTERISTICS);
1122
1123 /* placeholders for lengths. will be filled in at the end */
1124 buf->q_append('\0');
1125 buf->q_append('\0');
1126
1127 start= buf->length();
1128
1129 {
1130 /*
1131 We have four basic replay scenarios:
1132
1133 a) SET TRANSACTION was used, but before an actual transaction
1134 was started, the load balancer moves the connection elsewhere.
1135 In that case, the same one-shots should be set up in the
1136 target session. (read-only/read-write; isolation-level)
1137
1138 b) The initial transaction has begun; the relevant characteristics
1139 are the session defaults, possibly overridden by previous
1140 SET TRANSACTION statements, possibly overridden or extended
1141 by options passed to the START TRANSACTION statement.
1142 If the load balancer wishes to move this transaction,
1143 it needs to be replayed with the correct characteristics.
1144 (read-only/read-write from SET or START;
1145 isolation-level from SET only, snapshot from START only)
1146
1147 c) A subsequent transaction started with START TRANSACTION
1148 (which is legal syntax in lieu of COMMIT AND CHAIN in MySQL)
1149 may add/modify the current one-shots:
1150
1151 - It may set up a read-only/read-write one-shot.
1152 This one-shot will override the value used in the previous
1153 transaction (whether that came from the default or a one-shot),
1154 and, like all one-shots currently do, it will carry over into
1155 any subsequent transactions that don't explicitly override them
1156 in turn. This behavior is not guaranteed in the docs and may
1157 change in the future, but the tracker item should correctly
1158 reflect whatever behavior a given version of mysqld implements.
1159
1160 - It may also set up a WITH CONSISTENT SNAPSHOT one-shot.
1161 This one-shot does not currently carry over into subsequent
1162 transactions (meaning that with "traditional syntax", WITH
1163 CONSISTENT SNAPSHOT can only be requested for the first part
1164 of a transaction chain). Again, the tracker item should reflect
1165 mysqld behavior.
1166
1167 d) A subsequent transaction started using COMMIT AND CHAIN
1168 (or, for that matter, BEGIN WORK, which is currently
1169 legal and equivalent syntax in MySQL, or START TRANSACTION
1170 sans options) will re-use any one-shots set up so far
1171 (with SET before the first transaction started, and with
1172 all subsequent STARTs), except for WITH CONSISTANT SNAPSHOT,
1173 which will never be chained and only applies when explicitly
1174 given.
1175
1176 It bears noting that if we switch sessions in a follow-up
1177 transaction, SET TRANSACTION would be illegal in the old
1178 session (as a transaction is active), whereas in the target
1179 session which is being prepared, it should be legal, as no
1180 transaction (chain) should have started yet.
1181
1182 Therefore, we are free to generate SET TRANSACTION as a replay
1183 statement even for a transaction that isn't the first in an
1184 ongoing chain. Consider
1185
1186 SET TRANSACTION ISOLATION LEVEL READ UNCOMMITED;
1187 START TRANSACTION READ ONLY, WITH CONSISTENT SNAPSHOT;
1188 # work
1189 COMMIT AND CHAIN;
1190
1191 If we switch away at this point, the replay in the new session
1192 needs to be
1193
1194 SET TRANSACTION ISOLATION LEVEL READ UNCOMMITED;
1195 START TRANSACTION READ ONLY;
1196
1197 When a transaction ends (COMMIT/ROLLBACK sans CHAIN), all
1198 per-transaction characteristics are reset to the session's
1199 defaults.
1200
1201 This also holds for a transaction ended implicitly! (transaction.cc)
1202 Once again, the aim is to have the tracker item reflect on a
1203 given mysqld's actual behavior.
1204 */
1205
1206 /*
1207 "ISOLATION LEVEL"
1208 Only legal in SET TRANSACTION, so will always be replayed as such.
1209 */
1210 if (tx_isol_level != TX_ISOL_INHERIT)
1211 {
1212 /*
1213 Unfortunately, we can't re-use tx_isolation_names /
1214 tx_isolation_typelib as it hyphenates its items.
1215 */
1216 buf->append(STRING_WITH_LEN("SET TRANSACTION ISOLATION LEVEL "));
1217 buf->append(&isol[tx_isol_level - 1]);
1218 buf->append(STRING_WITH_LEN("; "));
1219 }
1220
1221 /*
1222 Start transaction will usually result in TX_EXPLICIT (transaction
1223 started, but no data attached yet), except when WITH CONSISTENT
1224 SNAPSHOT, in which case we may have data pending.
1225 If it's an XA transaction, we don't go through here so we can
1226 first print the trx access mode ("SET TRANSACTION READ ...")
1227 separately before adding XA START (whereas with START TRANSACTION,
1228 we can merge the access mode into the same statement).
1229 */
1230 if ((tx_curr_state & TX_EXPLICIT) && !is_xa)
1231 {
1232 buf->append(STRING_WITH_LEN("START TRANSACTION"));
1233
1234 /*
1235 "WITH CONSISTENT SNAPSHOT"
1236 Defaults to no, can only be enabled.
1237 Only appears in START TRANSACTION.
1238 */
1239 if (tx_curr_state & TX_WITH_SNAPSHOT)
1240 {
1241 buf->append(STRING_WITH_LEN(" WITH CONSISTENT SNAPSHOT"));
1242 if (tx_read_flags != TX_READ_INHERIT)
1243 buf->append(STRING_WITH_LEN(","));
1244 }
1245
1246 /*
1247 "READ WRITE / READ ONLY" can be set globally, per-session,
1248 or just for one transaction.
1249
1250 The latter case can take the form of
1251 START TRANSACTION READ (WRITE|ONLY), or of
1252 SET TRANSACTION READ (ONLY|WRITE).
1253 (Both set thd->read_only for the upcoming transaction;
1254 it will ultimately be re-set to the session default.)
1255
1256 As the regular session-variable tracker does not monitor the one-shot,
1257 we'll have to do it here.
1258
1259 If READ is flagged as set explicitly (rather than just inherited
1260 from the session's default), we'll get the actual bool from the THD.
1261 */
1262 if (tx_read_flags != TX_READ_INHERIT)
1263 {
1264 if (tx_read_flags == TX_READ_ONLY)
1265 buf->append(STRING_WITH_LEN(" READ ONLY"));
1266 else
1267 buf->append(STRING_WITH_LEN(" READ WRITE"));
1268 }
1269 buf->append(STRING_WITH_LEN("; "));
1270 }
1271 else if (tx_read_flags != TX_READ_INHERIT)
1272 {
1273 /*
1274 "READ ONLY" / "READ WRITE"
1275 We could transform this to SET TRANSACTION even when it occurs
1276 in START TRANSACTION, but for now, we'll resysynthesize the original
1277 command as closely as possible.
1278 */
1279 buf->append(STRING_WITH_LEN("SET TRANSACTION "));
1280 if (tx_read_flags == TX_READ_ONLY)
1281 buf->append(STRING_WITH_LEN("READ ONLY; "));
1282 else
1283 buf->append(STRING_WITH_LEN("READ WRITE; "));
1284 }
1285
1286 if ((tx_curr_state & TX_EXPLICIT) && is_xa)
1287 {
1288 XID *xid= &thd->transaction.xid_state.xid;
1289 long glen, blen;
1290
1291 buf->append(STRING_WITH_LEN("XA START"));
1292
1293 if ((glen= xid->gtrid_length) > 0)
1294 {
1295 buf->append(STRING_WITH_LEN(" '"));
1296 buf->append(xid->data, glen);
1297
1298 if ((blen= xid->bqual_length) > 0)
1299 {
1300 buf->append(STRING_WITH_LEN("','"));
1301 buf->append(xid->data + glen, blen);
1302 }
1303 buf->append(STRING_WITH_LEN("'"));
1304
1305 if (xid->formatID != 1)
1306 {
1307 buf->append(STRING_WITH_LEN(","));
1308 buf->append_ulonglong(xid->formatID);
1309 }
1310 }
1311
1312 buf->append(STRING_WITH_LEN("; "));
1313 }
1314
1315 // discard trailing space
1316 if (buf->length() > start)
1317 buf->length(buf->length() - 1);
1318 }
1319
1320 {
1321 size_t length= buf->length() - start;
1322 uchar *place= (uchar *)(buf->ptr() + (start - 2));
1323 DBUG_ASSERT(length < 249); // in fact < 110
1324 DBUG_ASSERT(start >= 3);
1325
1326 DBUG_ASSERT((place - 1)[0] == SESSION_TRACK_TRANSACTION_CHARACTERISTICS);
1327 /* Length of the overall entity. */
1328 place[0]= (uchar)length + 1;
1329 /* Transaction characteristics (length-encoded string). */
1330 place[1]= (uchar)length;
1331 }
1332 }
1333
1334 reset();
1335
1336 return false;
1337}
1338
1339
1340/**
1341 Reset the m_changed flag for next statement.
1342*/
1343
1344void Transaction_state_tracker::reset()
1345{
1346 m_changed= false;
1347 tx_reported_state= tx_curr_state;
1348 tx_changed= TX_CHG_NONE;
1349}
1350
1351
1352/**
1353 Helper function: turn table info into table access flag.
1354 Accepts table lock type and engine type flag (transactional/
1355 non-transactional), and returns the corresponding access flag
1356 out of TX_READ_TRX, TX_READ_UNSAFE, TX_WRITE_TRX, TX_WRITE_UNSAFE.
1357
1358 @param thd [IN] The thd handle
1359 @param set [IN] The table's access/lock type
1360 @param set [IN] Whether the table's engine is transactional
1361
1362 @return The table access flag
1363*/
1364
1365enum_tx_state Transaction_state_tracker::calc_trx_state(THD *thd,
1366 thr_lock_type l,
1367 bool has_trx)
1368{
1369 enum_tx_state s;
1370 bool read= (l <= TL_READ_NO_INSERT);
1371
1372 if (read)
1373 s= has_trx ? TX_READ_TRX : TX_READ_UNSAFE;
1374 else
1375 s= has_trx ? TX_WRITE_TRX : TX_WRITE_UNSAFE;
1376
1377 return s;
1378}
1379
1380
1381/**
1382 Register the end of an (implicit or explicit) transaction.
1383
1384 @param thd [IN] The thd handle
1385*/
1386void Transaction_state_tracker::end_trx(THD *thd)
1387{
1388 DBUG_ASSERT(thd->variables.session_track_transaction_info > TX_TRACK_NONE);
1389
1390 if ((!m_enabled) || (thd->state_flags & Open_tables_state::BACKUPS_AVAIL))
1391 return;
1392
1393 if (tx_curr_state != TX_EMPTY)
1394 {
1395 if (tx_curr_state & TX_EXPLICIT)
1396 tx_changed |= TX_CHG_CHISTICS;
1397 tx_curr_state &= TX_LOCKED_TABLES;
1398 }
1399 update_change_flags(thd);
1400}
1401
1402
1403/**
1404 Clear flags pertaining to the current statement or transaction.
1405 May be called repeatedly within the same execution cycle.
1406
1407 @param thd [IN] The thd handle.
1408 @param set [IN] The flags to clear
1409*/
1410
1411void Transaction_state_tracker::clear_trx_state(THD *thd, uint clear)
1412{
1413 if ((!m_enabled) || (thd->state_flags & Open_tables_state::BACKUPS_AVAIL))
1414 return;
1415
1416 tx_curr_state &= ~clear;
1417 update_change_flags(thd);
1418}
1419
1420
1421/**
1422 Add flags pertaining to the current statement or transaction.
1423 May be called repeatedly within the same execution cycle,
1424 e.g. to add access info for more tables.
1425
1426 @param thd [IN] The thd handle.
1427 @param set [IN] The flags to add
1428*/
1429
1430void Transaction_state_tracker::add_trx_state(THD *thd, uint add)
1431{
1432 if ((!m_enabled) || (thd->state_flags & Open_tables_state::BACKUPS_AVAIL))
1433 return;
1434
1435 if (add == TX_EXPLICIT)
1436 {
1437 /* Always send characteristic item (if tracked), always replace state. */
1438 tx_changed |= TX_CHG_CHISTICS;
1439 tx_curr_state = TX_EXPLICIT;
1440 }
1441
1442 /*
1443 If we're not in an implicit or explicit transaction, but
1444 autocommit==0 and tables are accessed, we flag "implicit transaction."
1445 */
1446 else if (!(tx_curr_state & (TX_EXPLICIT|TX_IMPLICIT)) &&
1447 (thd->variables.option_bits & OPTION_NOT_AUTOCOMMIT) &&
1448 (add &
1449 (TX_READ_TRX | TX_READ_UNSAFE | TX_WRITE_TRX | TX_WRITE_UNSAFE)))
1450 tx_curr_state |= TX_IMPLICIT;
1451
1452 /*
1453 Only flag state when in transaction or LOCK TABLES is added.
1454 */
1455 if ((tx_curr_state & (TX_EXPLICIT | TX_IMPLICIT)) ||
1456 (add & TX_LOCKED_TABLES))
1457 tx_curr_state |= add;
1458
1459 update_change_flags(thd);
1460}
1461
1462
1463/**
1464 Add "unsafe statement" flag if applicable.
1465
1466 @param thd [IN] The thd handle.
1467 @param set [IN] The flags to add
1468*/
1469
1470void Transaction_state_tracker::add_trx_state_from_thd(THD *thd)
1471{
1472 if (m_enabled)
1473 {
1474 if (thd->lex->is_stmt_unsafe())
1475 add_trx_state(thd, TX_STMT_UNSAFE);
1476 }
1477}
1478
1479
1480/**
1481 Set read flags (read only/read write) pertaining to the next
1482 transaction.
1483
1484 @param thd [IN] The thd handle.
1485 @param set [IN] The flags to set
1486*/
1487
1488void Transaction_state_tracker::set_read_flags(THD *thd,
1489 enum enum_tx_read_flags flags)
1490{
1491 if (m_enabled && (tx_read_flags != flags))
1492 {
1493 tx_read_flags = flags;
1494 tx_changed |= TX_CHG_CHISTICS;
1495 mark_as_changed(thd, NULL);
1496 }
1497}
1498
1499
1500/**
1501 Set isolation level pertaining to the next transaction.
1502
1503 @param thd [IN] The thd handle.
1504 @param set [IN] The isolation level to set
1505*/
1506
1507void Transaction_state_tracker::set_isol_level(THD *thd,
1508 enum enum_tx_isol_level level)
1509{
1510 if (m_enabled && (tx_isol_level != level))
1511 {
1512 tx_isol_level = level;
1513 tx_changed |= TX_CHG_CHISTICS;
1514 mark_as_changed(thd, NULL);
1515 }
1516}
1517
1518
1519///////////////////////////////////////////////////////////////////////////////
1520
1521Session_state_change_tracker::Session_state_change_tracker()
1522{
1523 m_changed= false;
1524}
1525
1526/**
1527 @Enable/disable the tracker based on @@session_track_state_change value.
1528
1529 @param thd [IN] The thd handle.
1530 @return false (always)
1531
1532**/
1533
1534bool Session_state_change_tracker::update(THD *thd, set_var *)
1535{
1536 m_enabled= thd->variables.session_track_state_change;
1537 return false;
1538}
1539
1540/**
1541 Store the '1' in the specified buffer when state is changed.
1542
1543 @param thd [IN] The thd handle.
1544 @paran buf [INOUT] Buffer to store the information to.
1545
1546 @reval false Success
1547 @retval true Error
1548**/
1549
1550bool Session_state_change_tracker::store(THD *thd, String *buf)
1551{
1552 if (unlikely((1 + 1 + 1 + buf->length() >= MAX_PACKET_LENGTH) ||
1553 buf->reserve(1 + 1 + 1, EXTRA_ALLOC)))
1554 return true;
1555
1556 compile_time_assert(SESSION_TRACK_STATE_CHANGE < 251);
1557 /* Session state type (SESSION_TRACK_STATE_CHANGE) */
1558 buf->q_append((char)SESSION_TRACK_STATE_CHANGE);
1559
1560 /* Length of the overall entity (1 byte) */
1561 buf->q_append('\1');
1562
1563 DBUG_ASSERT(is_state_changed(thd));
1564 buf->q_append('1');
1565
1566 reset();
1567
1568 return false;
1569}
1570
1571
1572/**
1573 Reset the m_changed flag for next statement.
1574*/
1575
1576void Session_state_change_tracker::reset()
1577{
1578 m_changed= false;
1579}
1580
1581
1582/**
1583 Find if there is a session state change.
1584*/
1585
1586bool Session_state_change_tracker::is_state_changed(THD *)
1587{
1588 return m_changed;
1589}
1590
1591///////////////////////////////////////////////////////////////////////////////
1592
1593/**
1594 @brief Initialize session tracker objects.
1595*/
1596
1597Session_tracker::Session_tracker()
1598{
1599 /* track data ID fit into one byte in net coding */
1600 compile_time_assert(SESSION_TRACK_always_at_the_end < 251);
1601 /* one tracker could serv several tracking data */
1602 compile_time_assert((uint)SESSION_TRACK_always_at_the_end >=
1603 (uint)SESSION_TRACKER_END);
1604
1605 for (int i= 0; i < SESSION_TRACKER_END; i++)
1606 m_trackers[i]= NULL;
1607}
1608
1609
1610/**
1611 @brief Enables the tracker objects.
1612
1613 @param thd [IN] The thread handle.
1614
1615 @return void
1616*/
1617
1618void Session_tracker::enable(THD *thd)
1619{
1620 /*
1621 Originally and correctly this allocation was in the constructor and
1622 deallocation in the destructor, but in this case memory counting
1623 system works incorrectly (for example in INSERT DELAYED thread)
1624 */
1625 deinit();
1626 m_trackers[SESSION_SYSVARS_TRACKER]=
1627 new (std::nothrow) Session_sysvars_tracker();
1628 m_trackers[CURRENT_SCHEMA_TRACKER]=
1629 new (std::nothrow) Current_schema_tracker;
1630 m_trackers[SESSION_STATE_CHANGE_TRACKER]=
1631 new (std::nothrow) Session_state_change_tracker;
1632 m_trackers[SESSION_GTIDS_TRACKER]=
1633 new (std::nothrow) Not_implemented_tracker;
1634 m_trackers[TRANSACTION_INFO_TRACKER]=
1635 new (std::nothrow) Transaction_state_tracker;
1636
1637 for (int i= 0; i < SESSION_TRACKER_END; i++)
1638 m_trackers[i]->enable(thd);
1639}
1640
1641
1642/**
1643 Method called during the server startup to verify the contents
1644 of @@session_track_system_variables.
1645
1646 @retval false Success
1647 @retval true Failure
1648*/
1649
1650bool Session_tracker::server_boot_verify(CHARSET_INFO *char_set)
1651{
1652 bool result;
1653 LEX_STRING tmp;
1654 tmp.str= global_system_variables.session_track_system_variables;
1655 tmp.length= safe_strlen(tmp.str);
1656 result=
1657 Session_sysvars_tracker::server_init_check(NULL, char_set, tmp);
1658 return result;
1659}
1660
1661
1662/**
1663 @brief Store all change information in the specified buffer.
1664
1665 @param thd [IN] The thd handle.
1666 @param buf [OUT] Reference to the string buffer to which the state
1667 change data needs to be written.
1668*/
1669
1670void Session_tracker::store(THD *thd, String *buf)
1671{
1672 size_t start;
1673
1674 /*
1675 Probably most track result will fit in 251 byte so lets made it at
1676 least efficient. We allocate 1 byte for length and then will move
1677 string if there is more.
1678 */
1679 buf->append('\0');
1680 start= buf->length();
1681
1682 /* Get total length. */
1683 for (int i= 0; i < SESSION_TRACKER_END; i++)
1684 {
1685 if (m_trackers[i]->is_changed() &&
1686 m_trackers[i]->store(thd, buf))
1687 {
1688 buf->length(start); // it is safer to have 0-length block in case of error
1689 return;
1690 }
1691 }
1692
1693 size_t length= buf->length() - start;
1694 uchar *data= (uchar *)(buf->ptr() + start);
1695 uint size;
1696
1697 if ((size= net_length_size(length)) != 1)
1698 {
1699 if (buf->reserve(size - 1, EXTRA_ALLOC))
1700 {
1701 buf->length(start); // it is safer to have 0-length block in case of error
1702 return;
1703 }
1704 memmove(data + (size - 1), data, length);
1705 }
1706
1707 net_store_length(data - 1, length);
1708}
1709
1710#endif //EMBEDDED_LIBRARY
1711