1 /* Copyright (c) 2002, 2020, Oracle and/or its affiliates. All rights reserved.
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 02110-1301  USA */
22 
23 #include "sql/set_var.h"
24 
25 #include <string.h>
26 #include <sys/types.h>
27 #include <cstdlib>
28 #include <utility>
29 
30 #include "m_ctype.h"
31 #include "m_string.h"
32 #include "map_helpers.h"
33 #include "my_dbug.h"
34 #include "my_io.h"
35 #include "my_loglevel.h"
36 #include "my_sys.h"
37 #include "mysql/components/services/log_builtins.h"
38 #include "mysql/components/services/log_shared.h"
39 #include "mysql/plugin_audit.h"
40 #include "mysql/psi/mysql_mutex.h"
41 #include "mysql/psi/mysql_rwlock.h"
42 #include "mysql/psi/psi_base.h"
43 #include "mysqld_error.h"
44 #include "sql/auth/auth_acls.h"
45 #include "sql/auth/auth_common.h"  // SUPER_ACL, generate_password
46 #include "sql/auth/sql_security_ctx.h"
47 #include "sql/derror.h"  // ER_THD
48 #include "sql/enum_query_type.h"
49 #include "sql/item.h"
50 #include "sql/item_func.h"
51 #include "sql/log.h"
52 #include "sql/mysqld.h"  // system_charset_info
53 #include "sql/persisted_variable.h"
54 #include "sql/protocol_classic.h"
55 #include "sql/session_tracker.h"
56 #include "sql/sql_audit.h"  // mysql_audit
57 #include "sql/sql_base.h"   // lock_tables
58 #include "sql/sql_class.h"  // THD
59 #include "sql/sql_error.h"
60 #include "sql/sql_lex.h"
61 #include "sql/sql_list.h"
62 #include "sql/sql_parse.h"        // is_supported_parser_charset
63 #include "sql/sql_select.h"       // free_underlaid_joins
64 #include "sql/sql_show.h"         // append_identifier
65 #include "sql/sys_vars_shared.h"  // PolyLock_mutex
66 #include "sql/system_variables.h"
67 #include "sql/table.h"
68 #include "sql_string.h"
69 
70 using std::min;
71 using std::string;
72 
73 static collation_unordered_map<string, sys_var *> *system_variable_hash;
74 static PolyLock_mutex PLock_global_system_variables(
75     &LOCK_global_system_variables);
76 ulonglong system_variable_hash_version = 0;
77 
get_system_variable_hash(void)78 collation_unordered_map<string, sys_var *> *get_system_variable_hash(void) {
79   return system_variable_hash;
80 }
81 
82 /** list of variables that shouldn't be persisted in all cases */
83 static collation_unordered_set<string> *never_persistable_vars;
84 
85 /**
86   Get source of a given system variable given its name and name length.
87 */
get_sysvar_source(const char * name,uint length,enum enum_variable_source * source)88 bool get_sysvar_source(const char *name, uint length,
89                        enum enum_variable_source *source) {
90   DBUG_TRACE;
91 
92   bool ret = false;
93   sys_var *sysvar = nullptr;
94 
95   mysql_rwlock_wrlock(&LOCK_system_variables_hash);
96 
97   /* system_variable_hash should have been initialized. */
98   DBUG_ASSERT(get_system_variable_hash() != nullptr);
99   std::string str(name, length);
100   sysvar = find_or_nullptr(*get_system_variable_hash(), str);
101 
102   if (sysvar == nullptr) {
103     ret = true;
104   } else {
105     *source = sysvar->get_source();
106   }
107 
108   mysql_rwlock_unlock(&LOCK_system_variables_hash);
109   return ret;
110 }
111 
112 sys_var_chain all_sys_vars = {nullptr, nullptr};
113 
sys_var_init()114 int sys_var_init() {
115   DBUG_TRACE;
116 
117   /* Must be already initialized. */
118   DBUG_ASSERT(system_charset_info != nullptr);
119 
120   system_variable_hash = new collation_unordered_map<string, sys_var *>(
121       system_charset_info, PSI_INSTRUMENT_ME);
122 
123   never_persistable_vars = new collation_unordered_set<string>(
124       {PERSIST_ONLY_ADMIN_X509_SUBJECT, PERSISTED_GLOBALS_LOAD},
125       system_charset_info, PSI_INSTRUMENT_ME);
126 
127   if (mysql_add_sys_var_chain(all_sys_vars.first)) goto error;
128 
129   return 0;
130 
131 error:
132   LogErr(ERROR_LEVEL, ER_FAILED_TO_INIT_SYS_VAR);
133   return 1;
134 }
135 
sys_var_add_options(std::vector<my_option> * long_options,int parse_flags)136 int sys_var_add_options(std::vector<my_option> *long_options, int parse_flags) {
137   DBUG_TRACE;
138 
139   for (sys_var *var = all_sys_vars.first; var; var = var->next) {
140     if (var->register_option(long_options, parse_flags)) goto error;
141   }
142 
143   return 0;
144 
145 error:
146   LogErr(ERROR_LEVEL, ER_FAILED_TO_INIT_SYS_VAR);
147   return 1;
148 }
149 
sys_var_end()150 void sys_var_end() {
151   DBUG_TRACE;
152 
153   delete system_variable_hash;
154   delete never_persistable_vars;
155   system_variable_hash = nullptr;
156 
157   for (sys_var *var = all_sys_vars.first; var; var = var->next) var->cleanup();
158 }
159 
160 /**
161   This function will check for necessary privileges needed to perform RESET
162   PERSIST or SET PERSIST[_ONLY] operation.
163 
164   @param [in] thd                     Pointer to connection handle.
165   @param [in] static_variable         describes if variable is static or dynamic
166 
167   @return 0 Success
168   @return 1 Failure
169 */
check_priv(THD * thd,bool static_variable)170 bool check_priv(THD *thd, bool static_variable) {
171   Security_context *sctx = thd->security_context();
172   /* for dynamic variables user needs SUPER_ACL or SYSTEM_VARIABLES_ADMIN */
173   if (!static_variable) {
174     if (!sctx->check_access(SUPER_ACL) &&
175         !(sctx->has_global_grant(STRING_WITH_LEN("SYSTEM_VARIABLES_ADMIN"))
176               .first)) {
177       my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0),
178                "SUPER or SYSTEM_VARIABLES_ADMIN");
179       return true;
180     }
181   } else {
182     /*
183      for static variables user needs both SYSTEM_VARIABLES_ADMIN and
184      PERSIST_RO_VARIABLES_ADMIN
185     */
186     if (!(sctx->has_global_grant(STRING_WITH_LEN("SYSTEM_VARIABLES_ADMIN"))
187               .first &&
188           sctx->has_global_grant(STRING_WITH_LEN("PERSIST_RO_VARIABLES_ADMIN"))
189               .first)) {
190       my_error(ER_PERSIST_ONLY_ACCESS_DENIED_ERROR, MYF(0),
191                "SYSTEM_VARIABLES_ADMIN and PERSIST_RO_VARIABLES_ADMIN");
192       return true;
193     }
194   }
195   return false;
196 }
197 
198 /**
199   sys_var constructor
200 
201   @param chain     variables are linked into chain for mysql_add_sys_var_chain()
202   @param name_arg  the name of the variable. Must be 0-terminated and exist
203                    for the liftime of the sys_var object. @sa my_option::name
204   @param comment   shown in mysqld --help, @sa my_option::comment
205   @param flags_arg or'ed flag_enum values
206   @param off       offset of the global variable value from the
207                    &global_system_variables.
208   @param getopt_id -1 for no command-line option, otherwise @sa my_option::id
209   @param getopt_arg_type no|optional|required value @sa my_option::arg_type
210   @param show_val_type_arg what value_ptr() returns for sql_show.cc
211   @param def_val   default value, @sa my_option::def_value
212   @param lock      mutex or rw_lock that protects the global variable
213                    *in addition* to LOCK_global_system_variables.
214   @param binlog_status_arg if the sysvar will be written to binlog or not @sa
215   binlog_status_enum
216   @param on_check_func a function to be called at the end of sys_var::check,
217                    put your additional checks here
218   @param on_update_func a function to be called at the end of sys_var::update,
219                    any post-update activity should happen here
220   @param substitute If non-NULL, this variable is deprecated and the
221   string describes what one should use instead. If an empty string,
222   the variable is deprecated but no replacement is offered.
223   @param parse_flag either PARSE_EARLY or PARSE_NORMAL
224 */
sys_var(sys_var_chain * chain,const char * name_arg,const char * comment,int flags_arg,ptrdiff_t off,int getopt_id,enum get_opt_arg_type getopt_arg_type,SHOW_TYPE show_val_type_arg,longlong def_val,PolyLock * lock,enum binlog_status_enum binlog_status_arg,on_check_function on_check_func,on_update_function on_update_func,const char * substitute,int parse_flag)225 sys_var::sys_var(sys_var_chain *chain, const char *name_arg,
226                  const char *comment, int flags_arg, ptrdiff_t off,
227                  int getopt_id, enum get_opt_arg_type getopt_arg_type,
228                  SHOW_TYPE show_val_type_arg, longlong def_val, PolyLock *lock,
229                  enum binlog_status_enum binlog_status_arg,
230                  on_check_function on_check_func,
231                  on_update_function on_update_func, const char *substitute,
232                  int parse_flag)
233     : next(nullptr),
234       binlog_status(binlog_status_arg),
235       flags(flags_arg),
236       m_parse_flag(parse_flag),
237       show_val_type(show_val_type_arg),
238       guard(lock),
239       offset(off),
240       on_check(on_check_func),
241       on_update(on_update_func),
242       deprecation_substitute(substitute),
243       is_os_charset(false) {
244   /*
245     There is a limitation in handle_options() related to short options:
246     - either all short options should be declared when parsing in multiple
247     stages,
248     - or none should be declared.
249     Because a lot of short options are used in the normal parsing phase
250     for mysqld, we enforce here that no short option is present
251     in the first (PARSE_EARLY) stage.
252     See handle_options() for details.
253   */
254   DBUG_ASSERT(parse_flag == PARSE_NORMAL || getopt_id <= 0 || getopt_id >= 255);
255 
256   name.str = name_arg;  // ER_NO_DEFAULT relies on 0-termination of name_arg
257   name.length = strlen(name_arg);  // and so does this.
258   DBUG_ASSERT(name.length <= NAME_CHAR_LEN);
259 
260   memset(&option, 0, sizeof(option));
261   option.name = name_arg;
262   option.id = getopt_id;
263   option.comment = comment;
264   option.arg_type = getopt_arg_type;
265   option.value = (uchar **)global_var_ptr();
266   option.def_value = def_val;
267 
268   /* set default values */
269   source.m_source = enum_variable_source::COMPILED;
270 
271   timestamp = 0;
272   user[0] = '\0';
273   host[0] = '\0';
274 
275   memset(source.m_path_name, 0, FN_REFLEN);
276   option.arg_source = &source;
277 
278   if (chain->last)
279     chain->last->next = this;
280   else
281     chain->first = this;
282   chain->last = this;
283 }
284 
update(THD * thd,set_var * var)285 bool sys_var::update(THD *thd, set_var *var) {
286   enum_var_type type = var->type;
287   if (type == OPT_GLOBAL || type == OPT_PERSIST || scope() == GLOBAL) {
288     /*
289       Yes, both locks need to be taken before an update, just as
290       both are taken to get a value. If we'll take only 'guard' here,
291       then value_ptr() for strings won't be safe in SHOW VARIABLES anymore,
292       to make it safe we'll need value_ptr_unlock().
293     */
294     AutoWLock lock1(&PLock_global_system_variables);
295     AutoWLock lock2(guard);
296     return global_update(thd, var) ||
297            (on_update && on_update(this, thd, OPT_GLOBAL));
298   } else {
299     /* Block reads from other threads. */
300     mysql_mutex_lock(&thd->LOCK_thd_sysvar);
301 
302     bool ret = session_update(thd, var) ||
303                (on_update && on_update(this, thd, OPT_SESSION));
304 
305     mysql_mutex_unlock(&thd->LOCK_thd_sysvar);
306 
307     /*
308       Make sure we don't session-track variables that are not actually
309       part of the session. tx_isolation and and tx_read_only for example
310       exist as GLOBAL, SESSION, and one-shot ("for next transaction only").
311     */
312     if ((var->type == OPT_SESSION) || !is_trilevel()) {
313       if ((!ret) && thd->session_tracker.get_tracker(SESSION_SYSVARS_TRACKER)
314                         ->is_enabled())
315         thd->session_tracker.get_tracker(SESSION_SYSVARS_TRACKER)
316             ->mark_as_changed(thd, &(var->var->name));
317 
318       if ((!ret) &&
319           thd->session_tracker.get_tracker(SESSION_STATE_CHANGE_TRACKER)
320               ->is_enabled())
321         thd->session_tracker.get_tracker(SESSION_STATE_CHANGE_TRACKER)
322             ->mark_as_changed(thd, &var->var->name);
323     }
324 
325     return ret;
326   }
327 }
328 
session_value_ptr(THD *,THD * target_thd,LEX_STRING *)329 const uchar *sys_var::session_value_ptr(THD *, THD *target_thd, LEX_STRING *) {
330   return session_var_ptr(target_thd);
331 }
332 
global_value_ptr(THD *,LEX_STRING *)333 const uchar *sys_var::global_value_ptr(THD *, LEX_STRING *) {
334   return global_var_ptr();
335 }
336 
session_var_ptr(THD * thd)337 uchar *sys_var::session_var_ptr(THD *thd) {
338   return ((uchar *)&(thd->variables)) + offset;
339 }
340 
global_var_ptr()341 uchar *sys_var::global_var_ptr() {
342   return ((uchar *)&global_system_variables) + offset;
343 }
344 
check(THD * thd,set_var * var)345 bool sys_var::check(THD *thd, set_var *var) {
346   if ((var->value && do_check(thd, var)) ||
347       (on_check && on_check(this, thd, var))) {
348     if (!thd->is_error()) {
349       char buff[STRING_BUFFER_USUAL_SIZE];
350       String str(buff, sizeof(buff), system_charset_info), *res;
351 
352       if (!var->value) {
353         str.set(STRING_WITH_LEN("DEFAULT"), &my_charset_latin1);
354         res = &str;
355       } else if (!(res = var->value->val_str(&str))) {
356         str.set(STRING_WITH_LEN("NULL"), &my_charset_latin1);
357         res = &str;
358       }
359       ErrConvString err(res);
360       my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), name.str, err.ptr());
361     }
362     return true;
363   }
364   return false;
365 }
366 
value_ptr(THD * running_thd,THD * target_thd,enum_var_type type,LEX_STRING * base)367 const uchar *sys_var::value_ptr(THD *running_thd, THD *target_thd,
368                                 enum_var_type type, LEX_STRING *base) {
369   if (type == OPT_GLOBAL || type == OPT_PERSIST || scope() == GLOBAL) {
370     mysql_mutex_assert_owner(&LOCK_global_system_variables);
371     AutoRLock lock(guard);
372     return global_value_ptr(running_thd, base);
373   } else
374     return session_value_ptr(running_thd, target_thd, base);
375 }
376 
value_ptr(THD * thd,enum_var_type type,LEX_STRING * base)377 const uchar *sys_var::value_ptr(THD *thd, enum_var_type type,
378                                 LEX_STRING *base) {
379   return value_ptr(thd, thd, type, base);
380 }
381 
set_default(THD * thd,set_var * var)382 bool sys_var::set_default(THD *thd, set_var *var) {
383   DBUG_TRACE;
384   if (var->is_global_persist() || scope() == GLOBAL)
385     global_save_default(thd, var);
386   else
387     session_save_default(thd, var);
388 
389   bool ret = check(thd, var) || update(thd, var);
390   return ret;
391 }
392 
set_user_host(THD * thd)393 void sys_var::set_user_host(THD *thd) {
394   memset(user, 0, sizeof(user));
395   memset(host, 0, sizeof(host));
396   Security_context *sctx = thd->security_context();
397   bool truncated = false;
398   if (sctx->user().length > 0) {
399     truncated = set_and_truncate(user, thd->security_context()->user().str,
400                                  sizeof(user));
401     if (truncated) {
402       LogErr(WARNING_LEVEL, ER_USERNAME_TRUNKATED, sctx->user().str,
403              USERNAME_CHAR_LENGTH);
404     }
405   }
406   if (sctx->host().length > 0) {
407     truncated = set_and_truncate(host, thd->security_context()->host().str,
408                                  sizeof(host));
409     if (truncated) {
410       LogErr(WARNING_LEVEL, ER_HOSTNAME_TRUNKATED, sctx->host().str,
411              HOSTNAME_LENGTH);
412     }
413   }
414 }
415 
do_deprecated_warning(THD * thd)416 void sys_var::do_deprecated_warning(THD *thd) {
417   if (deprecation_substitute != nullptr) {
418     char buf1[NAME_CHAR_LEN + 3];
419     strxnmov(buf1, sizeof(buf1) - 1, "@@", name.str, 0);
420 
421     /*
422        if deprecation_substitute is an empty string,
423        there is no replacement for the syntax
424     */
425     uint errmsg = deprecation_substitute[0] == '\0'
426                       ? ER_DEPRECATE_MSG_NO_REPLACEMENT
427                       : ER_DEPRECATE_MSG_WITH_REPLACEMENT;
428     if (thd)
429       push_warning_printf(
430           thd, Sql_condition::SL_WARNING, ER_WARN_DEPRECATED_SYNTAX,
431           ER_THD_NONCONST(thd, errmsg), buf1, deprecation_substitute);
432     else
433       LogErr(WARNING_LEVEL, errmsg, buf1, deprecation_substitute);
434   }
435 }
436 
copy_value(THD * thd)437 Item *sys_var::copy_value(THD *thd) {
438   LEX_STRING str;
439   const uchar *val_ptr = session_value_ptr(thd, thd, &str);
440   switch (get_var_type()) {
441     case GET_INT:
442       return new Item_int(*pointer_cast<const int *>(val_ptr));
443     case GET_UINT:
444       return new Item_int(
445           static_cast<ulonglong>(*pointer_cast<const uint *>(val_ptr)));
446     case GET_LONG:
447       return new Item_int(
448           static_cast<longlong>(*pointer_cast<const long *>(val_ptr)));
449     case GET_ULONG:
450       return new Item_int(
451           static_cast<ulonglong>(*pointer_cast<const ulong *>(val_ptr)));
452     case GET_LL:
453       return new Item_int(*pointer_cast<const longlong *>(val_ptr));
454     case GET_ULL:
455       return new Item_int(*pointer_cast<const ulonglong *>(val_ptr));
456     case GET_BOOL:
457       return new Item_int(*pointer_cast<const bool *>(val_ptr));
458     case GET_ENUM:
459     case GET_SET:
460     case GET_FLAGSET:
461     case GET_STR_ALLOC:
462     case GET_STR:
463     case GET_NO_ARG:
464     case GET_PASSWORD: {
465       const char *tmp_str_val = pointer_cast<const char *>(val_ptr);
466       return new Item_string(tmp_str_val, strlen(tmp_str_val),
467                              system_charset_info);
468     }
469     case GET_DOUBLE:
470       return new Item_float(*pointer_cast<const double *>(val_ptr),
471                             DECIMAL_NOT_SPECIFIED);
472     default:
473       DBUG_ASSERT(0);
474   }
475   return nullptr;
476 }
477 
478 /**
479   Throw warning (error in STRICT mode) if value for variable needed bounding.
480   Plug-in interface also uses this.
481 
482   @param thd         thread handle
483   @param name        variable's name
484   @param fixed       did we have to correct the value? (throw warn/err if so)
485   @param is_unsigned is value's type unsigned?
486   @param v           variable's value
487 
488   @retval         true on error, false otherwise (warning or ok)
489  */
throw_bounds_warning(THD * thd,const char * name,bool fixed,bool is_unsigned,longlong v)490 bool throw_bounds_warning(THD *thd, const char *name, bool fixed,
491                           bool is_unsigned, longlong v) {
492   if (fixed) {
493     char buf[22];
494 
495     if (is_unsigned)
496       ullstr((ulonglong)v, buf);
497     else
498       llstr(v, buf);
499 
500     if (thd->variables.sql_mode & MODE_STRICT_ALL_TABLES) {
501       my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), name, buf);
502       return true;
503     }
504     push_warning_printf(thd, Sql_condition::SL_WARNING,
505                         ER_TRUNCATED_WRONG_VALUE,
506                         ER_THD(thd, ER_TRUNCATED_WRONG_VALUE), name, buf);
507   }
508   return false;
509 }
510 
throw_bounds_warning(THD * thd,const char * name,bool fixed,double v)511 bool throw_bounds_warning(THD *thd, const char *name, bool fixed, double v) {
512   if (fixed) {
513     char buf[64];
514 
515     my_gcvt(v, MY_GCVT_ARG_DOUBLE, static_cast<int>(sizeof(buf)) - 1, buf,
516             nullptr);
517 
518     if (thd->variables.sql_mode & MODE_STRICT_ALL_TABLES) {
519       my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), name, buf);
520       return true;
521     }
522     push_warning_printf(thd, Sql_condition::SL_WARNING,
523                         ER_TRUNCATED_WRONG_VALUE,
524                         ER_THD(thd, ER_TRUNCATED_WRONG_VALUE), name, buf);
525   }
526   return false;
527 }
528 
charset(THD * thd)529 const CHARSET_INFO *sys_var::charset(THD *thd) {
530   return is_os_charset ? thd->variables.character_set_filesystem
531                        : system_charset_info;
532 }
533 
534 struct my_old_conv {
535   const char *old_name;
536   const char *new_name;
537 };
538 
539 static my_old_conv old_conv[] = {{"cp1251_koi8", "cp1251"},
540                                  {"cp1250_latin2", "cp1250"},
541                                  {"kam_latin2", "keybcs2"},
542                                  {"mac_latin2", "MacRoman"},
543                                  {"macce_latin2", "MacCE"},
544                                  {"pc2_latin2", "pclatin2"},
545                                  {"vga_latin2", "pclatin1"},
546                                  {"koi8_cp1251", "koi8r"},
547                                  {"win1251ukr_koi8_ukr", "win1251ukr"},
548                                  {"koi8_ukr_win1251ukr", "koi8u"},
549                                  {nullptr, nullptr}};
550 
get_old_charset_by_name(const char * name)551 const CHARSET_INFO *get_old_charset_by_name(const char *name) {
552   my_old_conv *conv;
553 
554   for (conv = old_conv; conv->old_name; conv++) {
555     if (!my_strcasecmp(&my_charset_latin1, name, conv->old_name))
556       return get_charset_by_csname(conv->new_name, MY_CS_PRIMARY, MYF(0));
557   }
558   return nullptr;
559 }
560 
561 /****************************************************************************
562   Main handling of variables:
563   - Initialisation
564   - Searching during parsing
565   - Update loop
566 ****************************************************************************/
567 
568 /**
569   Add variables to the dynamic hash of system variables
570 
571   @param first       Pointer to first system variable to add
572 
573   @retval
574     0           SUCCESS
575   @retval
576     otherwise   FAILURE
577 */
578 
mysql_add_sys_var_chain(sys_var * first)579 int mysql_add_sys_var_chain(sys_var *first) {
580   sys_var *var;
581 
582   /* A write lock should be held on LOCK_system_variables_hash */
583 
584   for (var = first; var; var = var->next) {
585     /* this fails if there is a conflicting variable name. */
586     if (!system_variable_hash->emplace(to_string(var->name), var).second) {
587       LogErr(ERROR_LEVEL, ER_DUPLICATE_SYS_VAR, var->name.str);
588       goto error;
589     }
590   }
591 
592   /* Update system_variable_hash version. */
593   system_variable_hash_version++;
594   return 0;
595 
596 error:
597   for (; first != var; first = first->next)
598     system_variable_hash->erase(to_string(var->name));
599   return 1;
600 }
601 
602 /*
603   Remove variables to the dynamic hash of system variables
604 
605   SYNOPSIS
606     mysql_del_sys_var_chain()
607     first       Pointer to first system variable to remove
608 
609   RETURN VALUES
610     0           SUCCESS
611     otherwise   FAILURE
612 */
613 
mysql_del_sys_var_chain(sys_var * first)614 int mysql_del_sys_var_chain(sys_var *first) {
615   int result = 0;
616 
617   /* A write lock should be held on LOCK_system_variables_hash */
618 
619   for (sys_var *var = first; var; var = var->next)
620     result |= !system_variable_hash->erase(to_string(var->name));
621 
622   /* Update system_variable_hash version. */
623   system_variable_hash_version++;
624 
625   return result;
626 }
627 
628 /*
629   Comparison function for std::sort.
630   @param a  SHOW_VAR element
631   @param b  SHOW_VAR element
632 
633   @retval
634     True if a < b.
635   @retval
636     False if a >= b.
637 */
show_cmp(const void * a,const void * b)638 static int show_cmp(const void *a, const void *b) {
639   return strcmp(static_cast<const SHOW_VAR *>(a)->name,
640                 static_cast<const SHOW_VAR *>(b)->name);
641 }
642 
643 /*
644   Number of records in the system_variable_hash.
645   Requires lock on LOCK_system_variables_hash.
646 */
get_system_variable_hash_records(void)647 ulong get_system_variable_hash_records(void) {
648   return (system_variable_hash->size());
649 }
650 
651 /*
652   Current version of the system_variable_hash.
653   Requires lock on LOCK_system_variables_hash.
654 */
get_system_variable_hash_version(void)655 ulonglong get_system_variable_hash_version(void) {
656   return (system_variable_hash_version);
657 }
658 
659 /**
660   Constructs an array of system variables for display to the user.
661 
662   @param show_var_array Prealloced_array of SHOW_VAR elements for display
663   @param sort           If true, the system variables should be sorted
664   @param query_scope    OPT_GLOBAL or OPT_SESSION for SHOW GLOBAL|SESSION
665   VARIABLES
666   @param strict         Use strict scope checking
667   @retval               True on error, false otherwise
668 */
enumerate_sys_vars(Show_var_array * show_var_array,bool sort,enum enum_var_type query_scope,bool strict)669 bool enumerate_sys_vars(Show_var_array *show_var_array, bool sort,
670                         enum enum_var_type query_scope, bool strict) {
671   DBUG_ASSERT(show_var_array != nullptr);
672   DBUG_ASSERT(query_scope == OPT_SESSION || query_scope == OPT_GLOBAL);
673   int count = system_variable_hash->size();
674 
675   /* Resize array if necessary. */
676   if (show_var_array->reserve(count + 1)) return true;
677 
678   if (show_var_array) {
679     for (const auto &key_and_value : *system_variable_hash) {
680       sys_var *sysvar = key_and_value.second;
681 
682       if (strict) {
683         /*
684           Strict scope match (5.7). Success if this is a:
685             - global query and the variable scope is GLOBAL or SESSION, OR
686             - session query and the variable scope is SESSION or ONLY_SESSION.
687         */
688         if (!sysvar->check_scope(query_scope)) continue;
689       } else {
690         /*
691           Non-strict scope match (5.6). Success if this is a:
692             - global query and the variable scope is GLOBAL or SESSION, OR
693             - session query and the variable scope is GLOBAL, SESSION or
694           ONLY_SESSION.
695         */
696         if (query_scope == OPT_GLOBAL && !sysvar->check_scope(query_scope))
697           continue;
698       }
699 
700       /* Don't show non-visible variables. */
701       if (sysvar->not_visible()) continue;
702 
703       SHOW_VAR show_var;
704       show_var.name = sysvar->name.str;
705       show_var.value = (char *)sysvar;
706       show_var.type = SHOW_SYS;
707       show_var.scope = SHOW_SCOPE_UNDEF; /* not used for sys vars */
708       show_var_array->push_back(show_var);
709     }
710 
711     if (sort)
712       std::qsort(show_var_array->begin(), show_var_array->size(),
713                  show_var_array->element_size(), show_cmp);
714 
715     /* Make last element empty. */
716     show_var_array->push_back(SHOW_VAR());
717   }
718 
719   return false;
720 }
721 
722 /**
723   Find a user set-table variable.
724 
725   @param str       Name of system variable to find
726   @param length    Length of variable.  zero means that we should use strlen()
727                    on the variable
728 
729   @retval
730     pointer     pointer to variable definitions
731   @retval
732     0           Unknown variable (error message is given)
733 */
734 
intern_find_sys_var(const char * str,size_t length)735 sys_var *intern_find_sys_var(const char *str, size_t length) {
736   sys_var *var;
737 
738   /*
739     This function is only called from the sql_plugin.cc.
740     A lock on LOCK_system_variable_hash should be held
741   */
742   var = find_or_nullptr(*system_variable_hash,
743                         string(str, length ? length : strlen(str)));
744 
745   /* Don't show non-visible variables. */
746   if (var && var->not_visible()) return nullptr;
747 
748   return var;
749 }
750 
751 /**
752   Execute update of all variables.
753 
754   First run a check of all variables that all updates will go ok.
755   If yes, then execute all updates, returning an error if any one failed.
756 
757   This should ensure that in all normal cases none all or variables are
758   updated.
759 
760   @param thd            Thread id
761   @param var_list       List of variables to update
762   @param opened         True means tables are open and this function will lock
763                         them.
764 
765   @retval
766     0   ok
767   @retval
768     1   ERROR, message sent (normally no variables was updated)
769   @retval
770     -1  ERROR, message not sent
771 */
772 
sql_set_variables(THD * thd,List<set_var_base> * var_list,bool opened)773 int sql_set_variables(THD *thd, List<set_var_base> *var_list, bool opened) {
774   int error;
775   List_iterator_fast<set_var_base> it(*var_list);
776   DBUG_TRACE;
777 
778   LEX *lex = thd->lex;
779   set_var_base *var;
780   while ((var = it++)) {
781     if ((error = var->resolve(thd))) goto err;
782   }
783   if ((error = thd->is_error())) goto err;
784 
785   if (opened && lock_tables(thd, lex->query_tables, lex->table_count, 0)) {
786     error = 1;
787     goto err;
788   }
789   it.rewind();
790   while ((var = it++)) {
791     if ((error = var->check(thd))) goto err;
792   }
793   if ((error = thd->is_error())) goto err;
794 
795   it.rewind();
796   while ((var = it++)) {
797     if ((error = var->update(thd)))  // Returns 0, -1 or 1
798       goto err;
799   }
800   if (!error) {
801     /* At this point SET statement is considered a success. */
802     Persisted_variables_cache *pv = nullptr;
803     it.rewind();
804     while ((var = it++)) {
805       set_var *setvar = dynamic_cast<set_var *>(var);
806       if (setvar &&
807           (setvar->type == OPT_PERSIST || setvar->type == OPT_PERSIST_ONLY)) {
808         pv = Persisted_variables_cache::get_instance();
809         /* update in-memory copy of persistent options */
810         if (pv->set_variable(thd, setvar)) return 1;
811       }
812     }
813     /* flush all persistent options to a file */
814     if (pv && pv->flush_to_file()) {
815       my_error(ER_VARIABLE_NOT_PERSISTED, MYF(0));
816       return 1;
817     }
818   }
819 err:
820   free_underlaid_joins(thd, thd->lex->select_lex);
821   return error;
822 }
823 
824 /**
825   This function is used to check if key management UDFs like
826   keying_key_generate/store/remove should proceed or not. If global
827   variable @@keyring_operations is OFF then above said udfs will fail.
828 
829   @return Operation status
830     @retval 0 OK
831     @retval 1 ERROR, keyring operations are not allowed
832 
833   @sa Sys_keyring_operations
834 */
keyring_access_test()835 bool keyring_access_test() {
836   bool keyring_operations;
837   mysql_mutex_lock(&LOCK_keyring_operations);
838   keyring_operations = !opt_keyring_operations;
839   mysql_mutex_unlock(&LOCK_keyring_operations);
840   return keyring_operations;
841 }
842 
843 /*****************************************************************************
844   Functions to handle SET mysql_internal_variable=const_expr
845 *****************************************************************************/
846 
set_var(enum_var_type type_arg,sys_var * var_arg,LEX_CSTRING base_name_arg,Item * value_arg)847 set_var::set_var(enum_var_type type_arg, sys_var *var_arg,
848                  LEX_CSTRING base_name_arg, Item *value_arg)
849     : var(var_arg), type(type_arg), base(base_name_arg) {
850   /*
851     If the set value is a field, change it to a string to allow things like
852     SET table_type=MYISAM;
853   */
854   if (value_arg && value_arg->type() == Item::FIELD_ITEM) {
855     Item_field *item = (Item_field *)value_arg;
856     if (item->field_name) {
857       if (!(value = new Item_string(item->field_name, strlen(item->field_name),
858                                     system_charset_info)))  // names are utf8
859         value = value_arg; /* Give error message later */
860     } else {
861       /* Both Item_field and Item_insert_value will return the type as
862          Item::FIELD_ITEM. If the item->field_name is NULL, we assume the
863          object to be Item_insert_value. */
864       value = value_arg;
865     }
866   } else
867     value = value_arg;
868 }
869 
870 /**
871   global X509 subject name to require from the client session
872   to allow SET PERSIST[_ONLY] on sys_var::NOTPERSIST variables
873 
874   @sa set_var::resolve
875 */
876 char *sys_var_persist_only_admin_x509_subject = nullptr;
877 
878 /**
879   Checks if a THD can set non-persist variables
880 
881   Requires that:
882   * the session uses SSL
883   * the peer has presented a valid certificate
884   * the certificate has a certain subject name
885 
886   The format checked is deliberately kept the same as the
887   other SSL system and status variables representing names.
888   Hence X509_NAME_oneline is used.
889 
890   @retval true the THD can set NON_PERSIST variables
891   @retval false usual restrictions apply
892   @param thd the THD handle
893   @param var the variable to be set
894   @param setvar_type  the operation to check against.
895 
896   @sa sys_variables_admin_dn
897 */
can_persist_non_persistent_var(THD * thd,sys_var * var,enum_var_type setvar_type)898 static bool can_persist_non_persistent_var(THD *thd, sys_var *var,
899                                            enum_var_type setvar_type) {
900   SSL *ssl = nullptr;
901   X509 *cert = nullptr;
902   char *ptr = nullptr;
903   bool result = false;
904 
905   /* Bail off if no subject is set */
906   if (likely(!sys_var_persist_only_admin_x509_subject ||
907              !sys_var_persist_only_admin_x509_subject[0]))
908     return false;
909 
910   /* Can't persist read only variables without command line support */
911   if (unlikely(setvar_type == OPT_PERSIST_ONLY &&
912                !var->is_settable_at_command_line() &&
913                (var->is_readonly() || var->is_persist_readonly())))
914     return false;
915 
916   /* do not allow setting the controlling variables */
917   if (never_persistable_vars->find(var->name.str) !=
918       never_persistable_vars->end())
919     return false;
920 
921   ssl = thd->get_ssl();
922   if (!ssl) return false;
923 
924   cert = SSL_get_peer_certificate(ssl);
925   if (!cert) goto done;
926 
927   ptr = X509_NAME_oneline(X509_get_subject_name(cert), nullptr, 0);
928   if (!ptr) goto done;
929 
930   result = !strcmp(sys_var_persist_only_admin_x509_subject, ptr);
931 done:
932   if (ptr) OPENSSL_free(ptr);
933   if (cert) X509_free(cert);
934   return result;
935 }
936 
937 /**
938 Resolve the variable assignment
939 
940 @param thd Thread handler
941 
942 @return status code
943 @retval -1 Failure
944 @retval 0 Success
945 */
946 
resolve(THD * thd)947 int set_var::resolve(THD *thd) {
948   DBUG_TRACE;
949   var->do_deprecated_warning(thd);
950   if (var->is_readonly()) {
951     if (type != OPT_PERSIST_ONLY) {
952       my_error(ER_INCORRECT_GLOBAL_LOCAL_VAR, MYF(0), var->name.str,
953                "read only");
954       return -1;
955     }
956     if (type == OPT_PERSIST_ONLY && var->is_non_persistent() &&
957         !can_persist_non_persistent_var(thd, var, type)) {
958       my_error(ER_INCORRECT_GLOBAL_LOCAL_VAR, MYF(0), var->name.str,
959                "non persistent read only");
960       return -1;
961     }
962   }
963   if (!var->check_scope(type)) {
964     int err = (is_global_persist()) ? ER_LOCAL_VARIABLE : ER_GLOBAL_VARIABLE;
965     my_error(err, MYF(0), var->name.str);
966     return -1;
967   }
968   if (type == OPT_GLOBAL || type == OPT_PERSIST) {
969     /* Either the user has SUPER_ACL or she has SYSTEM_VARIABLES_ADMIN */
970     if (check_priv(thd, false)) return 1;
971   }
972   if (type == OPT_PERSIST_ONLY) {
973     if (check_priv(thd, true)) return 1;
974   }
975 
976   /* check if read/write non-persistent variables can be persisted */
977   if ((type == OPT_PERSIST || type == OPT_PERSIST_ONLY) &&
978       var->is_non_persistent() &&
979       !can_persist_non_persistent_var(thd, var, type)) {
980     my_error(ER_INCORRECT_GLOBAL_LOCAL_VAR, MYF(0), var->name.str,
981              "non persistent");
982     return -1;
983   }
984 
985   /* value is a NULL pointer if we are using SET ... = DEFAULT */
986   if (!value) return 0;
987 
988   if ((!value->fixed && value->fix_fields(thd, &value)) || value->check_cols(1))
989     return -1;
990 
991   return 0;
992 }
993 
994 /**
995   Verify that the supplied value is correct.
996 
997   @param thd Thread handler
998 
999   @return status code
1000    @retval -1 Failure
1001    @retval 0 Success
1002 */
1003 
check(THD * thd)1004 int set_var::check(THD *thd) {
1005   DBUG_TRACE;
1006 
1007   /* value is a NULL pointer if we are using SET ... = DEFAULT */
1008   if (!value) return 0;
1009 
1010   if (var->check_update_type(value->result_type())) {
1011     my_error(ER_WRONG_TYPE_FOR_VAR, MYF(0), var->name.str);
1012     return -1;
1013   }
1014   int ret = (type != OPT_PERSIST_ONLY && var->check(thd, this)) ? -1 : 0;
1015 
1016   if (!ret && (is_global_persist())) {
1017     ret = mysql_audit_notify(thd, AUDIT_EVENT(MYSQL_AUDIT_GLOBAL_VARIABLE_SET),
1018                              var->name.str, value->item_name.ptr(),
1019                              value->item_name.length());
1020   }
1021 
1022   return ret;
1023 }
1024 
1025 /**
1026   Check variable, but without assigning value (used by PS).
1027 
1028   @param thd            thread handler
1029 
1030   @retval
1031     0   ok
1032   @retval
1033     1   ERROR, message sent (normally no variables was updated)
1034   @retval
1035     -1   ERROR, message not sent
1036 */
light_check(THD * thd)1037 int set_var::light_check(THD *thd) {
1038   if (!var->check_scope(type)) {
1039     int err = (is_global_persist()) ? ER_LOCAL_VARIABLE : ER_GLOBAL_VARIABLE;
1040     my_error(err, MYF(0), var->name.str);
1041     return -1;
1042   }
1043   Security_context *sctx = thd->security_context();
1044   if ((type == OPT_GLOBAL || type == OPT_PERSIST) &&
1045       !(sctx->check_access(SUPER_ACL) ||
1046         sctx->has_global_grant(STRING_WITH_LEN("SYSTEM_VARIABLES_ADMIN"))
1047             .first))
1048     return 1;
1049 
1050   if ((type == OPT_PERSIST_ONLY) &&
1051       !(sctx->has_global_grant(STRING_WITH_LEN("PERSIST_RO_VARIABLES_ADMIN"))
1052             .first &&
1053         sctx->has_global_grant(STRING_WITH_LEN("SYSTEM_VARIABLES_ADMIN"))
1054             .first))
1055     return 1;
1056 
1057   if (value && ((!value->fixed && value->fix_fields(thd, &value)) ||
1058                 value->check_cols(1)))
1059     return -1;
1060   return 0;
1061 }
1062 
1063 /**
1064   Update variable source, user, host and timestamp values.
1065 */
1066 
update_source_user_host_timestamp(THD * thd)1067 void set_var::update_source_user_host_timestamp(THD *thd) {
1068   var->set_source(enum_variable_source::DYNAMIC);
1069   var->set_source_name(EMPTY_CSTR.str);
1070   var->set_user_host(thd);
1071   var->set_timestamp();
1072 }
1073 
1074 /**
1075   Update variable
1076 
1077   @param   thd    thread handler
1078   @returns 0|1    ok or ERROR
1079 
1080   @note ERROR can be only due to abnormal operations involving
1081   the server's execution evironment such as
1082   out of memory, hard disk failure or the computer blows up.
1083   Consider set_var::check() method if there is a need to return
1084   an error due to logics.
1085 */
update(THD * thd)1086 int set_var::update(THD *thd) {
1087   int ret = 0;
1088   /* for persist only syntax do not update the value */
1089   if (type != OPT_PERSIST_ONLY) {
1090     if (value)
1091       ret = (int)var->update(thd, this);
1092     else
1093       ret = (int)var->set_default(thd, this);
1094   }
1095   /*
1096    For PERSIST_ONLY syntax we dont change the value of the variable
1097    for the current session, thus we should not change variables
1098    source/timestamp/user/host.
1099   */
1100   if (ret == 0 && type != OPT_PERSIST_ONLY) {
1101     update_source_user_host_timestamp(thd);
1102   }
1103   return ret;
1104 }
1105 
print_short(const THD * thd,String * str)1106 void set_var::print_short(const THD *thd, String *str) {
1107   str->append(var->name.str, var->name.length);
1108   str->append(STRING_WITH_LEN("="));
1109   if (value)
1110     value->print(thd, str, QT_ORDINARY);
1111   else
1112     str->append(STRING_WITH_LEN("DEFAULT"));
1113 }
1114 
1115 /**
1116   Self-print assignment
1117 
1118   @param thd Thread handle
1119   @param str String buffer to append the partial assignment to.
1120 */
print(const THD * thd,String * str)1121 void set_var::print(const THD *thd, String *str) {
1122   switch (type) {
1123     case OPT_PERSIST:
1124       str->append("PERSIST ");
1125       break;
1126     case OPT_PERSIST_ONLY:
1127       str->append("PERSIST_ONLY ");
1128       break;
1129     case OPT_GLOBAL:
1130       str->append("GLOBAL ");
1131       break;
1132     default:
1133       str->append("SESSION ");
1134   }
1135   if (base.length) {
1136     str->append(base.str, base.length);
1137     str->append(STRING_WITH_LEN("."));
1138   }
1139   print_short(thd, str);
1140 }
1141 
1142 /*****************************************************************************
1143   Functions to handle SET @user_variable=const_expr
1144 *****************************************************************************/
1145 
resolve(THD * thd)1146 int set_var_user::resolve(THD *thd) {
1147   /*
1148     Item_func_set_user_var can't substitute something else on its place =>
1149     0 can be passed as last argument (reference on item)
1150   */
1151   return user_var_item->fix_fields(thd, nullptr) ? -1 : 0;
1152 }
1153 
check(THD *)1154 int set_var_user::check(THD *) {
1155   /*
1156     Item_func_set_user_var can't substitute something else on its place =>
1157     0 can be passed as last argument (reference on item)
1158   */
1159   return user_var_item->check(false) ? -1 : 0;
1160 }
1161 
1162 /**
1163   Check variable, but without assigning value (used by PS).
1164 
1165   @param thd            thread handler
1166 
1167   @retval
1168     0   ok
1169   @retval
1170     1   ERROR, message sent (normally no variables was updated)
1171   @retval
1172     -1   ERROR, message not sent
1173 */
light_check(THD * thd)1174 int set_var_user::light_check(THD *thd) {
1175   /*
1176     Item_func_set_user_var can't substitute something else on its place =>
1177     0 can be passed as last argument (reference on item)
1178   */
1179   return (user_var_item->fix_fields(thd, (Item **)nullptr));
1180 }
1181 
update(THD * thd)1182 int set_var_user::update(THD *thd) {
1183   if (user_var_item->update()) {
1184     /* Give an error if it's not given already */
1185     my_error(ER_SET_CONSTANTS_ONLY, MYF(0));
1186     return -1;
1187   }
1188   if (thd->session_tracker.get_tracker(SESSION_STATE_CHANGE_TRACKER)
1189           ->is_enabled())
1190     thd->session_tracker.get_tracker(SESSION_STATE_CHANGE_TRACKER)
1191         ->mark_as_changed(thd, nullptr);
1192   return 0;
1193 }
1194 
print(const THD * thd,String * str)1195 void set_var_user::print(const THD *thd, String *str) {
1196   user_var_item->print_assignment(thd, str, QT_ORDINARY);
1197 }
1198 
1199 /*****************************************************************************
1200   Functions to handle SET PASSWORD
1201 *****************************************************************************/
1202 
set_var_password(LEX_USER * user_arg,char * password_arg,char * current_password_arg,bool retain_current,bool gen_pass)1203 set_var_password::set_var_password(LEX_USER *user_arg, char *password_arg,
1204                                    char *current_password_arg,
1205                                    bool retain_current, bool gen_pass)
1206     : user(user_arg),
1207       password(password_arg),
1208       current_password(current_password_arg),
1209       retain_current_password(retain_current),
1210       generate_password(gen_pass) {
1211   if (current_password != nullptr) {
1212     user_arg->uses_replace_clause = true;
1213     user_arg->current_auth.str = current_password_arg;
1214     user_arg->current_auth.length = strlen(current_password_arg);
1215   }
1216   user_arg->retain_current_password = retain_current_password;
1217 }
1218 
~set_var_password()1219 set_var_password::~set_var_password() {
1220   // We copied the generated password buffer to circumvent
1221   // the password nullification code in change_password()
1222   if (generate_password) my_free(password);
1223 }
1224 
1225 /**
1226   Check the validity of the SET PASSWORD request
1227 
1228   @param  thd  The current thread
1229   @return      status code
1230   @retval 0    failure
1231   @retval 1    success
1232 */
check(THD * thd)1233 int set_var_password::check(THD *thd) {
1234   /* Returns 1 as the function sends error to client */
1235   return check_change_password(thd, user->host.str, user->user.str,
1236                                retain_current_password)
1237              ? 1
1238              : 0;
1239 }
1240 
update(THD * thd)1241 int set_var_password::update(THD *thd) {
1242   if (generate_password) {
1243     thd->m_disable_password_validation = true;
1244     std::string generated_password;
1245     generate_random_password(&generated_password,
1246                              thd->variables.generated_random_password_length);
1247     /*
1248       We need to copy the password buffer here because it will be set to \0
1249       later by change_password() and since we're generated a random password
1250       we need to retain it until it can be sent to the client.
1251       Because set_var_password never will get its destructor called we also
1252       need to move the string allocated memory to the THD mem root.
1253     */
1254     password = thd->mem_strdup(generated_password.c_str());
1255     str_generated_password = thd->mem_strdup(generated_password.c_str());
1256   }
1257   /* Returns 1 as the function sends error to client */
1258   auto res = change_password(thd, user, password, current_password,
1259                              retain_current_password)
1260                  ? 1
1261                  : 0;
1262   return res;
1263 }
1264 
print(const THD * thd,String * str)1265 void set_var_password::print(const THD *thd, String *str) {
1266   if (user->user.str != nullptr && user->user.length > 0) {
1267     str->append(STRING_WITH_LEN("PASSWORD FOR "));
1268     append_identifier(thd, str, user->user.str, user->user.length);
1269     if (user->host.str != nullptr && user->host.length > 0) {
1270       str->append(STRING_WITH_LEN("@"));
1271       append_identifier(thd, str, user->host.str, user->host.length);
1272     }
1273     str->append(STRING_WITH_LEN("="));
1274   } else
1275     str->append(STRING_WITH_LEN("PASSWORD FOR CURRENT_USER()="));
1276   str->append(STRING_WITH_LEN("<secret>"));
1277   if (user->uses_replace_clause) {
1278     str->append(STRING_WITH_LEN(" REPLACE <secret>"));
1279   }
1280   if (user->retain_current_password) {
1281     str->append(STRING_WITH_LEN(" RETAIN CURRENT PASSWORD"));
1282   }
1283 }
1284 
1285 /*****************************************************************************
1286   Functions to handle SET NAMES and SET CHARACTER SET
1287 *****************************************************************************/
1288 
check(THD *)1289 int set_var_collation_client::check(THD *) {
1290   /* Currently, UCS-2 cannot be used as a client character set */
1291   if (!is_supported_parser_charset(character_set_client)) {
1292     my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), "character_set_client",
1293              character_set_client->csname);
1294     return 1;
1295   }
1296   return 0;
1297 }
1298 
update(THD * thd)1299 int set_var_collation_client::update(THD *thd) {
1300   thd->variables.character_set_client = character_set_client;
1301   thd->variables.character_set_results = character_set_results;
1302   thd->variables.collation_connection = collation_connection;
1303   thd->update_charset();
1304 
1305   /* Mark client collation variables as changed */
1306   if (thd->session_tracker.get_tracker(SESSION_SYSVARS_TRACKER)->is_enabled()) {
1307     LEX_CSTRING cs_client = {"character_set_client",
1308                              sizeof("character_set_client") - 1};
1309     thd->session_tracker.get_tracker(SESSION_SYSVARS_TRACKER)
1310         ->mark_as_changed(thd, &cs_client);
1311     LEX_CSTRING cs_results = {"character_set_results",
1312                               sizeof("character_set_results") - 1};
1313     thd->session_tracker.get_tracker(SESSION_SYSVARS_TRACKER)
1314         ->mark_as_changed(thd, &cs_results);
1315     LEX_CSTRING cs_connection = {"character_set_connection",
1316                                  sizeof("character_set_connection") - 1};
1317     thd->session_tracker.get_tracker(SESSION_SYSVARS_TRACKER)
1318         ->mark_as_changed(thd, &cs_connection);
1319   }
1320   if (thd->session_tracker.get_tracker(SESSION_STATE_CHANGE_TRACKER)
1321           ->is_enabled())
1322     thd->session_tracker.get_tracker(SESSION_STATE_CHANGE_TRACKER)
1323         ->mark_as_changed(thd, nullptr);
1324   thd->protocol_text->init(thd);
1325   thd->protocol_binary->init(thd);
1326   return 0;
1327 }
1328 
print(const THD *,String * str)1329 void set_var_collation_client::print(const THD *, String *str) {
1330   str->append((set_cs_flags & SET_CS_NAMES) ? "NAMES " : "CHARACTER SET ");
1331   if (set_cs_flags & SET_CS_DEFAULT)
1332     str->append("DEFAULT");
1333   else {
1334     str->append("'");
1335     str->append(character_set_client->csname);
1336     str->append("'");
1337     if (set_cs_flags & SET_CS_COLLATE) {
1338       str->append(" COLLATE '");
1339       str->append(collation_connection->name);
1340       str->append("'");
1341     }
1342   }
1343 }
1344