1/* Copyright (c) 2002, 2013, Oracle and/or its affiliates.
2 Copyright (c) 2008, 2014, SkySQL Ab.
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/* variable declarations are in sys_vars.cc now !!! */
18
19#include "sql_plugin.h"
20#include "sql_class.h" // set_var.h: session_var_ptr
21#include "set_var.h"
22#include "sql_priv.h"
23#include "unireg.h"
24#include "mysqld.h" // lc_messages_dir
25#include "sys_vars_shared.h"
26#include "transaction.h"
27#include "sql_locale.h" // my_locale_by_number,
28 // my_locale_by_name
29#include "strfunc.h" // find_set_from_flags, find_set
30#include "sql_parse.h" // check_global_access
31#include "sql_table.h" // reassign_keycache_tables
32#include "sql_time.h" // date_time_format_copy,
33 // date_time_format_make
34#include "derror.h"
35#include "tztime.h" // my_tz_find, my_tz_SYSTEM, struct Time_zone
36#include "sql_acl.h" // SUPER_ACL
37#include "sql_select.h" // free_underlaid_joins
38#include "sql_show.h"
39#include "sql_view.h" // updatable_views_with_limit_typelib
40#include "lock.h" // lock_global_read_lock,
41 // make_global_read_lock_block_commit,
42 // unlock_global_read_lock
43
44static HASH system_variable_hash;
45static PolyLock_mutex PLock_global_system_variables(&LOCK_global_system_variables);
46
47/**
48 Return variable name and length for hashing of variables.
49*/
50
51static uchar *get_sys_var_length(const sys_var *var, size_t *length,
52 my_bool first)
53{
54 *length= var->name.length;
55 return (uchar*) var->name.str;
56}
57
58sys_var_chain all_sys_vars = { NULL, NULL };
59
60int sys_var_init()
61{
62 DBUG_ENTER("sys_var_init");
63
64 /* Must be already initialized. */
65 DBUG_ASSERT(system_charset_info != NULL);
66
67 if (my_hash_init(&system_variable_hash, system_charset_info, 700, 0,
68 0, (my_hash_get_key) get_sys_var_length, 0, HASH_UNIQUE))
69 goto error;
70
71 if (mysql_add_sys_var_chain(all_sys_vars.first))
72 goto error;
73
74 DBUG_RETURN(0);
75
76error:
77 fprintf(stderr, "failed to initialize System variables");
78 DBUG_RETURN(1);
79}
80
81uint sys_var_elements()
82{
83 return system_variable_hash.records;
84}
85
86int sys_var_add_options(DYNAMIC_ARRAY *long_options, int parse_flags)
87{
88 uint saved_elements= long_options->elements;
89
90 DBUG_ENTER("sys_var_add_options");
91
92 for (sys_var *var=all_sys_vars.first; var; var= var->next)
93 {
94 if (var->register_option(long_options, parse_flags))
95 goto error;
96 }
97
98 DBUG_RETURN(0);
99
100error:
101 fprintf(stderr, "failed to initialize System variables");
102 long_options->elements= saved_elements;
103 DBUG_RETURN(1);
104}
105
106void sys_var_end()
107{
108 DBUG_ENTER("sys_var_end");
109
110 my_hash_free(&system_variable_hash);
111
112 for (sys_var *var=all_sys_vars.first; var; var= var->next)
113 var->cleanup();
114
115 DBUG_VOID_RETURN;
116}
117
118
119static bool static_test_load= TRUE;
120
121/**
122 sys_var constructor
123
124 @param chain variables are linked into chain for mysql_add_sys_var_chain()
125 @param name_arg the name of the variable. Must be 0-terminated and exist
126 for the liftime of the sys_var object. @sa my_option::name
127 @param comment shown in mysqld --help, @sa my_option::comment
128 @param flags_arg or'ed flag_enum values
129 @param off offset of the global variable value from the
130 &global_system_variables.
131 @param getopt_id -1 for no command-line option, otherwise @sa my_option::id
132 @param getopt_arg_type @sa my_option::arg_type
133 @param show_val_type_arg what value_ptr() returns for sql_show.cc
134 @param def_val default value, @sa my_option::def_value
135 @param lock mutex or rw_lock that protects the global variable
136 *in addition* to LOCK_global_system_variables.
137 @param binlog_status_enum @sa binlog_status_enum
138 @param on_check_func a function to be called at the end of sys_var::check,
139 put your additional checks here
140 @param on_update_func a function to be called at the end of sys_var::update,
141 any post-update activity should happen here
142 @param substitute If non-NULL, this variable is deprecated and the
143 string describes what one should use instead. If an empty string,
144 the variable is deprecated but no replacement is offered.
145*/
146sys_var::sys_var(sys_var_chain *chain, const char *name_arg,
147 const char *comment, int flags_arg, ptrdiff_t off,
148 int getopt_id, enum get_opt_arg_type getopt_arg_type,
149 SHOW_TYPE show_val_type_arg, longlong def_val,
150 PolyLock *lock, enum binlog_status_enum binlog_status_arg,
151 on_check_function on_check_func,
152 on_update_function on_update_func,
153 const char *substitute) :
154 next(0), binlog_status(binlog_status_arg), value_origin(COMPILE_TIME),
155 flags(flags_arg), show_val_type(show_val_type_arg),
156 guard(lock), offset(off), on_check(on_check_func), on_update(on_update_func),
157 deprecation_substitute(substitute),
158 is_os_charset(FALSE)
159{
160 /*
161 There is a limitation in handle_options() related to short options:
162 - either all short options should be declared when parsing in multiple stages,
163 - or none should be declared.
164 Because a lot of short options are used in the normal parsing phase
165 for mysqld, we enforce here that no short option is present
166 in the first (PARSE_EARLY) stage.
167 See handle_options() for details.
168 */
169 DBUG_ASSERT(!(flags & PARSE_EARLY) || getopt_id <= 0 || getopt_id >= 255);
170
171 name.str= name_arg; // ER_NO_DEFAULT relies on 0-termination of name_arg
172 name.length= strlen(name_arg); // and so does this.
173 DBUG_ASSERT(name.length <= NAME_CHAR_LEN);
174
175 bzero(&option, sizeof(option));
176 option.name= name_arg;
177 option.id= getopt_id;
178 option.comment= comment;
179 option.arg_type= getopt_arg_type;
180 option.value= (uchar **)global_var_ptr();
181 option.def_value= def_val;
182 option.app_type= this;
183 option.var_type= flags & AUTO_SET ? GET_AUTO : 0;
184
185 if (chain->last)
186 chain->last->next= this;
187 else
188 chain->first= this;
189 chain->last= this;
190
191 test_load= &static_test_load;
192}
193
194bool sys_var::update(THD *thd, set_var *var)
195{
196 enum_var_type type= var->type;
197 if (type == OPT_GLOBAL || scope() == GLOBAL)
198 {
199 /*
200 Yes, both locks need to be taken before an update, just as
201 both are taken to get a value. If we'll take only 'guard' here,
202 then value_ptr() for strings won't be safe in SHOW VARIABLES anymore,
203 to make it safe we'll need value_ptr_unlock().
204 */
205 AutoWLock lock1(&PLock_global_system_variables);
206 AutoWLock lock2(guard);
207 value_origin= SQL;
208 return global_update(thd, var) ||
209 (on_update && on_update(this, thd, OPT_GLOBAL));
210 }
211 else
212 {
213 bool ret= session_update(thd, var) ||
214 (on_update && on_update(this, thd, OPT_SESSION));
215
216 /*
217 Make sure we don't session-track variables that are not actually
218 part of the session. tx_isolation and and tx_read_only for example
219 exist as GLOBAL, SESSION, and one-shot ("for next transaction only").
220 */
221 if ((var->type == OPT_SESSION) && (!ret))
222 {
223 SESSION_TRACKER_CHANGED(thd, SESSION_SYSVARS_TRACKER,
224 (LEX_CSTRING*)var->var);
225 /*
226 Here MySQL sends variable name to avoid reporting change of
227 the tracker itself, but we decided that it is not needed
228 */
229 SESSION_TRACKER_CHANGED(thd, SESSION_STATE_CHANGE_TRACKER, NULL);
230 }
231
232 return ret;
233 }
234}
235
236uchar *sys_var::session_value_ptr(THD *thd, const LEX_CSTRING *base)
237{
238 return session_var_ptr(thd);
239}
240
241uchar *sys_var::global_value_ptr(THD *thd, const LEX_CSTRING *base)
242{
243 return global_var_ptr();
244}
245
246bool sys_var::check(THD *thd, set_var *var)
247{
248 if (unlikely((var->value && do_check(thd, var)) ||
249 (on_check && on_check(this, thd, var))))
250 {
251 if (likely(!thd->is_error()))
252 {
253 char buff[STRING_BUFFER_USUAL_SIZE];
254 String str(buff, sizeof(buff), system_charset_info), *res;
255
256 if (!var->value)
257 {
258 str.set(STRING_WITH_LEN("DEFAULT"), &my_charset_latin1);
259 res= &str;
260 }
261 else if (!(res=var->value->val_str(&str)))
262 {
263 str.set(STRING_WITH_LEN("NULL"), &my_charset_latin1);
264 res= &str;
265 }
266 ErrConvString err(res);
267 my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), name.str, err.ptr());
268 }
269 return true;
270 }
271 return false;
272}
273
274uchar *sys_var::value_ptr(THD *thd, enum_var_type type,
275 const LEX_CSTRING *base)
276{
277 DBUG_ASSERT(base);
278 if (type == OPT_GLOBAL || scope() == GLOBAL)
279 {
280 mysql_mutex_assert_owner(&LOCK_global_system_variables);
281 AutoRLock lock(guard);
282 return global_value_ptr(thd, base);
283 }
284 else
285 return session_value_ptr(thd, base);
286}
287
288bool sys_var::set_default(THD *thd, set_var* var)
289{
290 if (var->type == OPT_GLOBAL || scope() == GLOBAL)
291 global_save_default(thd, var);
292 else
293 session_save_default(thd, var);
294
295 return check(thd, var) || update(thd, var);
296}
297
298
299#define do_num_val(T,CMD) \
300do { \
301 T val= *(T*) value; \
302 CMD; \
303} while (0)
304
305#define case_for_integers(CMD) \
306 case SHOW_SINT: do_num_val (int,CMD); \
307 case SHOW_SLONG: do_num_val (long,CMD); \
308 case SHOW_SLONGLONG:do_num_val (longlong,CMD); \
309 case SHOW_UINT: do_num_val (uint,CMD); \
310 case SHOW_ULONG: do_num_val (ulong,CMD); \
311 case SHOW_ULONGLONG:do_num_val (ulonglong,CMD); \
312 case SHOW_HA_ROWS: do_num_val (ha_rows,CMD);
313
314#define case_for_double(CMD) \
315 case SHOW_DOUBLE: do_num_val (double,CMD)
316
317#define case_get_string_as_lex_string \
318 case SHOW_CHAR: \
319 sval.str= (char*) value; \
320 sval.length= sval.str ? strlen(sval.str) : 0; \
321 break; \
322 case SHOW_CHAR_PTR: \
323 sval.str= *(char**) value; \
324 sval.length= sval.str ? strlen(sval.str) : 0; \
325 break; \
326 case SHOW_LEX_STRING: \
327 sval= *(LEX_CSTRING *) value; \
328 break
329
330longlong sys_var::val_int(bool *is_null,
331 THD *thd, enum_var_type type,
332 const LEX_CSTRING *base)
333{
334 LEX_CSTRING sval;
335 AutoWLock lock(&PLock_global_system_variables);
336 const uchar *value= value_ptr(thd, type, base);
337 *is_null= false;
338
339 switch (show_type())
340 {
341 case_get_string_as_lex_string;
342 case_for_integers(return val);
343 case_for_double(return (longlong) val);
344 case SHOW_MY_BOOL: return *(my_bool*)value;
345 default:
346 my_error(ER_VAR_CANT_BE_READ, MYF(0), name.str);
347 return 0;
348 }
349
350 longlong ret= 0;
351 if (!(*is_null= !sval.str))
352 ret= longlong_from_string_with_check(charset(thd),
353 sval.str, sval.str + sval.length);
354 return ret;
355}
356
357
358String *sys_var::val_str_nolock(String *str, THD *thd, const uchar *value)
359{
360 static LEX_CSTRING bools[]=
361 {
362 { STRING_WITH_LEN("OFF") },
363 { STRING_WITH_LEN("ON") }
364 };
365
366 LEX_CSTRING sval;
367 switch (show_type())
368 {
369 case_get_string_as_lex_string;
370 case_for_integers(return str->set(val, system_charset_info) ? 0 : str);
371 case_for_double(return str->set_real(val, 6, system_charset_info) ? 0 : str);
372 case SHOW_MY_BOOL:
373 sval= bools[(int)*(my_bool*)value];
374 break;
375 default:
376 my_error(ER_VAR_CANT_BE_READ, MYF(0), name.str);
377 return 0;
378 }
379
380 if (!sval.str || str->copy(sval.str, sval.length, charset(thd)))
381 str= NULL;
382 return str;
383}
384
385
386String *sys_var::val_str(String *str,
387 THD *thd, enum_var_type type, const LEX_CSTRING *base)
388{
389 AutoWLock lock(&PLock_global_system_variables);
390 const uchar *value= value_ptr(thd, type, base);
391 return val_str_nolock(str, thd, value);
392}
393
394
395double sys_var::val_real(bool *is_null,
396 THD *thd, enum_var_type type, const LEX_CSTRING *base)
397{
398 LEX_CSTRING sval;
399 AutoWLock lock(&PLock_global_system_variables);
400 const uchar *value= value_ptr(thd, type, base);
401 *is_null= false;
402
403 switch (show_type())
404 {
405 case_get_string_as_lex_string;
406 case_for_integers(return (double)val);
407 case_for_double(return val);
408 case SHOW_MY_BOOL: return *(my_bool*)value;
409 default:
410 my_error(ER_VAR_CANT_BE_READ, MYF(0), name.str);
411 return 0;
412 }
413
414 double ret= 0;
415 if (!(*is_null= !sval.str))
416 ret= double_from_string_with_check(charset(thd),
417 sval.str, sval.str + sval.length);
418 return ret;
419}
420
421
422void sys_var::do_deprecated_warning(THD *thd)
423{
424 if (deprecation_substitute != NULL)
425 {
426 char buf1[NAME_CHAR_LEN + 3];
427 strxnmov(buf1, sizeof(buf1)-1, "@@", name.str, 0);
428
429 /*
430 if deprecation_substitute is an empty string,
431 there is no replacement for the syntax
432 */
433 uint errmsg= deprecation_substitute[0] == '\0'
434 ? ER_WARN_DEPRECATED_SYNTAX_NO_REPLACEMENT
435 : ER_WARN_DEPRECATED_SYNTAX;
436 if (thd)
437 push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
438 ER_WARN_DEPRECATED_SYNTAX, ER_THD(thd, errmsg),
439 buf1, deprecation_substitute);
440 else
441 sql_print_warning(ER_DEFAULT(errmsg), buf1, deprecation_substitute);
442 }
443}
444
445/**
446 Throw warning (error in STRICT mode) if value for variable needed bounding.
447 Plug-in interface also uses this.
448
449 @param thd thread handle
450 @param name variable's name
451 @param fixed did we have to correct the value? (throw warn/err if so)
452 @param is_unsigned is value's type unsigned?
453 @param v variable's value
454
455 @retval true on error, false otherwise (warning or ok)
456 */
457bool throw_bounds_warning(THD *thd, const char *name,
458 bool fixed, bool is_unsigned, longlong v)
459{
460 if (fixed)
461 {
462 char buf[22];
463
464 if (is_unsigned)
465 ullstr((ulonglong) v, buf);
466 else
467 llstr(v, buf);
468
469 if (thd->variables.sql_mode & MODE_STRICT_ALL_TABLES)
470 {
471 my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), name, buf);
472 return true;
473 }
474 push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
475 ER_TRUNCATED_WRONG_VALUE,
476 ER_THD(thd, ER_TRUNCATED_WRONG_VALUE), name, buf);
477 }
478 return false;
479}
480
481bool throw_bounds_warning(THD *thd, const char *name, bool fixed, double v)
482{
483 if (fixed)
484 {
485 char buf[64];
486
487 my_gcvt(v, MY_GCVT_ARG_DOUBLE, sizeof(buf) - 1, buf, NULL);
488
489 if (thd->variables.sql_mode & MODE_STRICT_ALL_TABLES)
490 {
491 my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), name, buf);
492 return true;
493 }
494 push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
495 ER_TRUNCATED_WRONG_VALUE,
496 ER_THD(thd, ER_TRUNCATED_WRONG_VALUE), name, buf);
497 }
498 return false;
499}
500
501CHARSET_INFO *sys_var::charset(THD *thd)
502{
503 return is_os_charset ? thd->variables.character_set_filesystem :
504 system_charset_info;
505}
506
507
508typedef struct old_names_map_st
509{
510 const char *old_name;
511 const char *new_name;
512} my_old_conv;
513
514static my_old_conv old_conv[]=
515{
516 { "cp1251_koi8" , "cp1251" },
517 { "cp1250_latin2" , "cp1250" },
518 { "kam_latin2" , "keybcs2" },
519 { "mac_latin2" , "MacRoman" },
520 { "macce_latin2" , "MacCE" },
521 { "pc2_latin2" , "pclatin2" },
522 { "vga_latin2" , "pclatin1" },
523 { "koi8_cp1251" , "koi8r" },
524 { "win1251ukr_koi8_ukr" , "win1251ukr" },
525 { "koi8_ukr_win1251ukr" , "koi8u" },
526 { NULL , NULL }
527};
528
529CHARSET_INFO *get_old_charset_by_name(const char *name)
530{
531 my_old_conv *conv;
532
533 for (conv= old_conv; conv->old_name; conv++)
534 {
535 if (!my_strcasecmp(&my_charset_latin1, name, conv->old_name))
536 return get_charset_by_csname(conv->new_name, MY_CS_PRIMARY, MYF(0));
537 }
538 return NULL;
539}
540
541/****************************************************************************
542 Main handling of variables:
543 - Initialisation
544 - Searching during parsing
545 - Update loop
546****************************************************************************/
547
548/**
549 Add variables to the dynamic hash of system variables
550
551 @param first Pointer to first system variable to add
552
553 @retval
554 0 SUCCESS
555 @retval
556 otherwise FAILURE
557*/
558
559
560int mysql_add_sys_var_chain(sys_var *first)
561{
562 sys_var *var;
563
564 /* A write lock should be held on LOCK_system_variables_hash */
565
566 for (var= first; var; var= var->next)
567 {
568 /* this fails if there is a conflicting variable name. see HASH_UNIQUE */
569 if (my_hash_insert(&system_variable_hash, (uchar*) var))
570 {
571 fprintf(stderr, "*** duplicate variable name '%s' ?\n", var->name.str);
572 goto error;
573 }
574 }
575 return 0;
576
577error:
578 for (; first != var; first= first->next)
579 my_hash_delete(&system_variable_hash, (uchar*) first);
580 return 1;
581}
582
583
584/*
585 Remove variables to the dynamic hash of system variables
586
587 SYNOPSIS
588 mysql_del_sys_var_chain()
589 first Pointer to first system variable to remove
590
591 RETURN VALUES
592 0 SUCCESS
593 otherwise FAILURE
594*/
595
596int mysql_del_sys_var_chain(sys_var *first)
597{
598 int result= 0;
599
600 mysql_prlock_wrlock(&LOCK_system_variables_hash);
601 for (sys_var *var= first; var; var= var->next)
602 result|= my_hash_delete(&system_variable_hash, (uchar*) var);
603 mysql_prlock_unlock(&LOCK_system_variables_hash);
604
605 return result;
606}
607
608
609static int show_cmp(SHOW_VAR *a, SHOW_VAR *b)
610{
611 return strcmp(a->name, b->name);
612}
613
614
615/**
616 Constructs an array of system variables for display to the user.
617
618 @param thd current thread
619 @param sorted If TRUE, the system variables should be sorted
620 @param scope OPT_GLOBAL or OPT_SESSION for SHOW GLOBAL|SESSION VARIABLES
621
622 @retval
623 pointer Array of SHOW_VAR elements for display
624 @retval
625 NULL FAILURE
626*/
627
628SHOW_VAR* enumerate_sys_vars(THD *thd, bool sorted, enum enum_var_type scope)
629{
630 int count= system_variable_hash.records, i;
631 int size= sizeof(SHOW_VAR) * (count + 1);
632 SHOW_VAR *result= (SHOW_VAR*) thd->alloc(size);
633
634 if (result)
635 {
636 SHOW_VAR *show= result;
637
638 for (i= 0; i < count; i++)
639 {
640 sys_var *var= (sys_var*) my_hash_element(&system_variable_hash, i);
641
642 // don't show session-only variables in SHOW GLOBAL VARIABLES
643 if (scope == OPT_GLOBAL && var->check_type(scope))
644 continue;
645
646 show->name= var->name.str;
647 show->value= (char*) var;
648 show->type= SHOW_SYS;
649 show++;
650 }
651
652 /* sort into order */
653 if (sorted)
654 my_qsort(result, show-result, sizeof(SHOW_VAR),
655 (qsort_cmp) show_cmp);
656
657 /* make last element empty */
658 bzero(show, sizeof(SHOW_VAR));
659 }
660 return result;
661}
662
663/**
664 Find a user set-table variable.
665
666 @param str Name of system variable to find
667 @param length Length of variable. zero means that we should use strlen()
668 on the variable
669
670 @retval
671 pointer pointer to variable definitions
672 @retval
673 0 Unknown variable (error message is given)
674*/
675
676sys_var *intern_find_sys_var(const char *str, size_t length)
677{
678 sys_var *var;
679
680 /*
681 This function is only called from the sql_plugin.cc.
682 A lock on LOCK_system_variable_hash should be held
683 */
684 var= (sys_var*) my_hash_search(&system_variable_hash,
685 (uchar*) str, length ? length : strlen(str));
686
687 return var;
688}
689
690
691/**
692 Execute update of all variables.
693
694 First run a check of all variables that all updates will go ok.
695 If yes, then execute all updates, returning an error if any one failed.
696
697 This should ensure that in all normal cases none all or variables are
698 updated.
699
700 @param THD Thread id
701 @param var_list List of variables to update
702
703 @retval
704 0 ok
705 @retval
706 1 ERROR, message sent (normally no variables was updated)
707 @retval
708 -1 ERROR, message not sent
709*/
710
711int sql_set_variables(THD *thd, List<set_var_base> *var_list, bool free)
712{
713 int error= 0;
714 bool was_error= thd->is_error();
715 List_iterator_fast<set_var_base> it(*var_list);
716 DBUG_ENTER("sql_set_variables");
717
718 set_var_base *var;
719 while ((var=it++))
720 {
721 if (unlikely((error= var->check(thd))))
722 goto err;
723 }
724 if (unlikely(was_error) || likely(!(error= MY_TEST(thd->is_error()))))
725 {
726 it.rewind();
727 while ((var= it++))
728 error|= var->update(thd); // Returns 0, -1 or 1
729 }
730
731err:
732 if (free)
733 free_underlaid_joins(thd, &thd->lex->select_lex);
734 DBUG_RETURN(error);
735}
736
737/*****************************************************************************
738 Functions to handle SET mysql_internal_variable=const_expr
739*****************************************************************************/
740
741/**
742 Verify that the supplied value is correct.
743
744 @param thd Thread handler
745
746 @return status code
747 @retval -1 Failure
748 @retval 0 Success
749 */
750
751int set_var::check(THD *thd)
752{
753 var->do_deprecated_warning(thd);
754 if (var->is_readonly())
755 {
756 my_error(ER_INCORRECT_GLOBAL_LOCAL_VAR, MYF(0), var->name.str, "read only");
757 return -1;
758 }
759 if (var->check_type(type))
760 {
761 int err= type == OPT_GLOBAL ? ER_LOCAL_VARIABLE : ER_GLOBAL_VARIABLE;
762 my_error(err, MYF(0), var->name.str);
763 return -1;
764 }
765 if ((type == OPT_GLOBAL && check_global_access(thd, SUPER_ACL)))
766 return 1;
767 /* value is a NULL pointer if we are using SET ... = DEFAULT */
768 if (!value)
769 return 0;
770
771 if ((!value->fixed &&
772 value->fix_fields(thd, &value)) || value->check_cols(1))
773 return -1;
774 if (var->check_update_type(value))
775 {
776 my_error(ER_WRONG_TYPE_FOR_VAR, MYF(0), var->name.str);
777 return -1;
778 }
779 return var->check(thd, this) ? -1 : 0;
780}
781
782
783/**
784 Check variable, but without assigning value (used by PS).
785
786 @param thd thread handler
787
788 @retval
789 0 ok
790 @retval
791 1 ERROR, message sent (normally no variables was updated)
792 @retval
793 -1 ERROR, message not sent
794*/
795int set_var::light_check(THD *thd)
796{
797 if (var->check_type(type))
798 {
799 int err= type == OPT_GLOBAL ? ER_LOCAL_VARIABLE : ER_GLOBAL_VARIABLE;
800 my_error(err, MYF(0), var->name);
801 return -1;
802 }
803 if (type == OPT_GLOBAL && check_global_access(thd, SUPER_ACL))
804 return 1;
805
806 if (value && ((!value->fixed && value->fix_fields(thd, &value)) ||
807 value->check_cols(1)))
808 return -1;
809 return 0;
810}
811
812/**
813 Update variable
814
815 @param thd thread handler
816 @returns 0|1 ok or ERROR
817
818 @note ERROR can be only due to abnormal operations involving
819 the server's execution evironment such as
820 out of memory, hard disk failure or the computer blows up.
821 Consider set_var::check() method if there is a need to return
822 an error due to logics.
823*/
824
825int set_var::update(THD *thd)
826{
827 return value ? var->update(thd, this) : var->set_default(thd, this);
828}
829
830
831set_var::set_var(THD *thd, enum_var_type type_arg, sys_var *var_arg,
832 const LEX_CSTRING *base_name_arg, Item *value_arg)
833 :var(var_arg), type(type_arg), base(*base_name_arg)
834{
835 /*
836 If the set value is a field, change it to a string to allow things like
837 SET table_type=MYISAM;
838 */
839 if (value_arg && value_arg->type() == Item::FIELD_ITEM)
840 {
841 Item_field *item= (Item_field*) value_arg;
842 // names are utf8
843 if (!(value= new (thd->mem_root) Item_string_sys(thd,
844 item->field_name.str,
845 (uint)item->field_name.length)))
846 value=value_arg; /* Give error message later */
847 }
848 else
849 value=value_arg;
850}
851
852
853/*****************************************************************************
854 Functions to handle SET @user_variable=const_expr
855*****************************************************************************/
856
857int set_var_user::check(THD *thd)
858{
859 /*
860 Item_func_set_user_var can't substitute something else on its place =>
861 0 can be passed as last argument (reference on item)
862 */
863 return (user_var_item->fix_fields(thd, (Item**) 0) ||
864 user_var_item->check(0)) ? -1 : 0;
865}
866
867
868/**
869 Check variable, but without assigning value (used by PS).
870
871 @param thd thread handler
872
873 @retval
874 0 ok
875 @retval
876 1 ERROR, message sent (normally no variables was updated)
877 @retval
878 -1 ERROR, message not sent
879*/
880int set_var_user::light_check(THD *thd)
881{
882 /*
883 Item_func_set_user_var can't substitute something else on its place =>
884 0 can be passed as last argument (reference on item)
885 */
886 return (user_var_item->fix_fields(thd, (Item**) 0));
887}
888
889
890int set_var_user::update(THD *thd)
891{
892 if (user_var_item->update())
893 {
894 /* Give an error if it's not given already */
895 my_message(ER_SET_CONSTANTS_ONLY, ER_THD(thd, ER_SET_CONSTANTS_ONLY),
896 MYF(0));
897 return -1;
898 }
899
900 SESSION_TRACKER_CHANGED(thd, SESSION_STATE_CHANGE_TRACKER, NULL);
901 return 0;
902}
903
904
905/*****************************************************************************
906 Functions to handle SET PASSWORD
907*****************************************************************************/
908
909int set_var_password::check(THD *thd)
910{
911#ifndef NO_EMBEDDED_ACCESS_CHECKS
912 return check_change_password(thd, user);
913#else
914 return 0;
915#endif
916}
917
918int set_var_password::update(THD *thd)
919{
920#ifndef NO_EMBEDDED_ACCESS_CHECKS
921 Reprepare_observer *save_reprepare_observer= thd->m_reprepare_observer;
922 thd->m_reprepare_observer= 0;
923 int res= change_password(thd, user);
924 thd->m_reprepare_observer= save_reprepare_observer;
925 return res;
926#else
927 return 0;
928#endif
929}
930
931/*****************************************************************************
932 Functions to handle SET ROLE
933*****************************************************************************/
934
935int set_var_role::check(THD *thd)
936{
937#ifndef NO_EMBEDDED_ACCESS_CHECKS
938 int status= acl_check_setrole(thd, role.str, &access);
939 return status;
940#else
941 return 0;
942#endif
943}
944
945int set_var_role::update(THD *thd)
946{
947#ifndef NO_EMBEDDED_ACCESS_CHECKS
948 int res= acl_setrole(thd, role.str, access);
949 if (!res)
950 thd->session_tracker.mark_as_changed(thd, SESSION_STATE_CHANGE_TRACKER,
951 NULL);
952 return res;
953#else
954 return 0;
955#endif
956}
957
958/*****************************************************************************
959 Functions to handle SET DEFAULT ROLE
960*****************************************************************************/
961
962int set_var_default_role::check(THD *thd)
963{
964#ifndef NO_EMBEDDED_ACCESS_CHECKS
965 real_user= get_current_user(thd, user);
966 int status= acl_check_set_default_role(thd, real_user->host.str, real_user->user.str);
967 return status;
968#else
969 return 0;
970#endif
971}
972
973int set_var_default_role::update(THD *thd)
974{
975#ifndef NO_EMBEDDED_ACCESS_CHECKS
976 Reprepare_observer *save_reprepare_observer= thd->m_reprepare_observer;
977 thd->m_reprepare_observer= 0;
978 int res= acl_set_default_role(thd, real_user->host.str, real_user->user.str, role.str);
979 thd->m_reprepare_observer= save_reprepare_observer;
980 return res;
981#else
982 return 0;
983#endif
984}
985
986/*****************************************************************************
987 Functions to handle SET NAMES and SET CHARACTER SET
988*****************************************************************************/
989
990int set_var_collation_client::check(THD *thd)
991{
992 /* Currently, UCS-2 cannot be used as a client character set */
993 if (!is_supported_parser_charset(character_set_client))
994 {
995 my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), "character_set_client",
996 character_set_client->csname);
997 return 1;
998 }
999 return 0;
1000}
1001
1002int set_var_collation_client::update(THD *thd)
1003{
1004 thd->update_charset(character_set_client, collation_connection,
1005 character_set_results);
1006
1007 /* Mark client collation variables as changed */
1008#ifndef EMBEDDED_LIBRARY
1009 if (thd->session_tracker.get_tracker(SESSION_SYSVARS_TRACKER)->is_enabled())
1010 {
1011 sys_var *svar;
1012 mysql_mutex_lock(&LOCK_plugin);
1013 if ((svar= find_sys_var_ex(thd, "character_set_client",
1014 sizeof("character_set_client") - 1,
1015 false, true)))
1016 thd->session_tracker.get_tracker(SESSION_SYSVARS_TRACKER)->
1017 mark_as_changed(thd, (LEX_CSTRING*)svar);
1018 if ((svar= find_sys_var_ex(thd, "character_set_results",
1019 sizeof("character_set_results") - 1,
1020 false, true)))
1021 thd->session_tracker.get_tracker(SESSION_SYSVARS_TRACKER)->
1022 mark_as_changed(thd, (LEX_CSTRING*)svar);
1023 if ((svar= find_sys_var_ex(thd, "character_set_connection",
1024 sizeof("character_set_connection") - 1,
1025 false, true)))
1026 thd->session_tracker.get_tracker(SESSION_SYSVARS_TRACKER)->
1027 mark_as_changed(thd, (LEX_CSTRING*)svar);
1028 mysql_mutex_unlock(&LOCK_plugin);
1029 }
1030 thd->session_tracker.mark_as_changed(thd, SESSION_STATE_CHANGE_TRACKER, NULL);
1031#endif //EMBEDDED_LIBRARY
1032
1033 thd->protocol_text.init(thd);
1034 thd->protocol_binary.init(thd);
1035 return 0;
1036}
1037
1038/*****************************************************************************
1039 INFORMATION_SCHEMA.SYSTEM_VARIABLES
1040*****************************************************************************/
1041static void store_value_ptr(Field *field, sys_var *var, String *str,
1042 uchar *value_ptr)
1043{
1044 field->set_notnull();
1045 str= var->val_str_nolock(str, field->table->in_use, value_ptr);
1046 if (str)
1047 field->store(str->ptr(), str->length(), str->charset());
1048}
1049
1050static void store_var(Field *field, sys_var *var, enum_var_type scope,
1051 String *str)
1052{
1053 if (var->check_type(scope))
1054 return;
1055
1056 store_value_ptr(field, var, str,
1057 var->value_ptr(field->table->in_use, scope, &null_clex_str));
1058}
1059
1060int fill_sysvars(THD *thd, TABLE_LIST *tables, COND *cond)
1061{
1062 char name_buffer[NAME_CHAR_LEN];
1063 enum_check_fields save_count_cuted_fields= thd->count_cuted_fields;
1064 bool res= 1;
1065 CHARSET_INFO *scs= system_charset_info;
1066 StringBuffer<STRING_BUFFER_USUAL_SIZE> strbuf(scs);
1067 const char *wild= thd->lex->wild ? thd->lex->wild->ptr() : 0;
1068 Field **fields=tables->table->field;
1069
1070 DBUG_ASSERT(tables->table->in_use == thd);
1071
1072 cond= make_cond_for_info_schema(thd, cond, tables);
1073 thd->count_cuted_fields= CHECK_FIELD_WARN;
1074 mysql_prlock_rdlock(&LOCK_system_variables_hash);
1075
1076 for (uint i= 0; i < system_variable_hash.records; i++)
1077 {
1078 sys_var *var= (sys_var*) my_hash_element(&system_variable_hash, i);
1079
1080 strmake_buf(name_buffer, var->name.str);
1081 my_caseup_str(system_charset_info, name_buffer);
1082
1083 /* this must be done before evaluating cond */
1084 restore_record(tables->table, s->default_values);
1085 fields[0]->store(name_buffer, strlen(name_buffer), scs);
1086
1087 if ((wild && wild_case_compare(system_charset_info, name_buffer, wild))
1088 || (cond && !cond->val_int()))
1089 continue;
1090
1091 mysql_mutex_lock(&LOCK_global_system_variables);
1092
1093 // SESSION_VALUE
1094 store_var(fields[1], var, OPT_SESSION, &strbuf);
1095
1096 // GLOBAL_VALUE
1097 store_var(fields[2], var, OPT_GLOBAL, &strbuf);
1098
1099 // GLOBAL_VALUE_ORIGIN
1100 static const LEX_CSTRING origins[]=
1101 {
1102 { STRING_WITH_LEN("CONFIG") },
1103 { STRING_WITH_LEN("AUTO") },
1104 { STRING_WITH_LEN("SQL") },
1105 { STRING_WITH_LEN("COMPILE-TIME") },
1106 { STRING_WITH_LEN("ENVIRONMENT") }
1107 };
1108 const LEX_CSTRING *origin= origins + var->value_origin;
1109 fields[3]->store(origin->str, origin->length, scs);
1110
1111 // DEFAULT_VALUE
1112 uchar *def= var->is_readonly() && var->option.id < 0
1113 ? 0 : var->default_value_ptr(thd);
1114 if (def)
1115 store_value_ptr(fields[4], var, &strbuf, def);
1116
1117 mysql_mutex_unlock(&LOCK_global_system_variables);
1118
1119 // VARIABLE_SCOPE
1120 static const LEX_CSTRING scopes[]=
1121 {
1122 { STRING_WITH_LEN("GLOBAL") },
1123 { STRING_WITH_LEN("SESSION") },
1124 { STRING_WITH_LEN("SESSION ONLY") }
1125 };
1126 const LEX_CSTRING *scope= scopes + var->scope();
1127 fields[5]->store(scope->str, scope->length, scs);
1128
1129 // VARIABLE_TYPE
1130#if SIZEOF_LONG == SIZEOF_INT
1131#define LONG_TYPE "INT"
1132#else
1133#define LONG_TYPE "BIGINT"
1134#endif
1135
1136 static const LEX_CSTRING types[]=
1137 {
1138 { 0, 0 }, // unused 0
1139 { 0, 0 }, // GET_NO_ARG 1
1140 { STRING_WITH_LEN("BOOLEAN") }, // GET_BOOL 2
1141 { STRING_WITH_LEN("INT") }, // GET_INT 3
1142 { STRING_WITH_LEN("INT UNSIGNED") }, // GET_UINT 4
1143 { STRING_WITH_LEN(LONG_TYPE) }, // GET_LONG 5
1144 { STRING_WITH_LEN(LONG_TYPE " UNSIGNED") }, // GET_ULONG 6
1145 { STRING_WITH_LEN("BIGINT") }, // GET_LL 7
1146 { STRING_WITH_LEN("BIGINT UNSIGNED") }, // GET_ULL 8
1147 { STRING_WITH_LEN("VARCHAR") }, // GET_STR 9
1148 { STRING_WITH_LEN("VARCHAR") }, // GET_STR_ALLOC 10
1149 { 0, 0 }, // GET_DISABLED 11
1150 { STRING_WITH_LEN("ENUM") }, // GET_ENUM 12
1151 { STRING_WITH_LEN("SET") }, // GET_SET 13
1152 { STRING_WITH_LEN("DOUBLE") }, // GET_DOUBLE 14
1153 { STRING_WITH_LEN("FLAGSET") }, // GET_FLAGSET 15
1154 { STRING_WITH_LEN("BOOLEAN") }, // GET_BIT 16
1155 };
1156 const ulong vartype= (var->option.var_type & GET_TYPE_MASK);
1157 const LEX_CSTRING *type= types + vartype;
1158 fields[6]->store(type->str, type->length, scs);
1159
1160 // VARIABLE_COMMENT
1161 fields[7]->store(var->option.comment, strlen(var->option.comment),
1162 scs);
1163
1164 // NUMERIC_MIN_VALUE
1165 // NUMERIC_MAX_VALUE
1166 // NUMERIC_BLOCK_SIZE
1167 bool is_unsigned= true;
1168 switch (vartype)
1169 {
1170 case GET_INT:
1171 case GET_LONG:
1172 case GET_LL:
1173 is_unsigned= false;
1174 /* fall through */
1175 case GET_UINT:
1176 case GET_ULONG:
1177 case GET_ULL:
1178 fields[8]->set_notnull();
1179 fields[9]->set_notnull();
1180 fields[10]->set_notnull();
1181 fields[8]->store(var->option.min_value, is_unsigned);
1182 fields[9]->store(var->option.max_value, is_unsigned);
1183 fields[10]->store(var->option.block_size, is_unsigned);
1184 break;
1185 case GET_DOUBLE:
1186 fields[8]->set_notnull();
1187 fields[9]->set_notnull();
1188 fields[8]->store(getopt_ulonglong2double(var->option.min_value));
1189 fields[9]->store(getopt_ulonglong2double(var->option.max_value));
1190 }
1191
1192 // ENUM_VALUE_LIST
1193 TYPELIB *tl= var->option.typelib;
1194 if (tl)
1195 {
1196 uint i;
1197 strbuf.length(0);
1198 for (i=0; i + 1 < tl->count; i++)
1199 {
1200 strbuf.append(tl->type_names[i]);
1201 strbuf.append(',');
1202 }
1203 strbuf.append(tl->type_names[i]);
1204 fields[11]->set_notnull();
1205 fields[11]->store(strbuf.ptr(), strbuf.length(), scs);
1206 }
1207
1208 // READ_ONLY
1209 static const LEX_CSTRING yesno[]=
1210 {
1211 { STRING_WITH_LEN("NO") },
1212 { STRING_WITH_LEN("YES") }
1213 };
1214 const LEX_CSTRING *yn = yesno + var->is_readonly();
1215 fields[12]->store(yn->str, yn->length, scs);
1216
1217 // COMMAND_LINE_ARGUMENT
1218 if (var->option.id >= 0)
1219 {
1220 static const LEX_CSTRING args[]=
1221 {
1222 { STRING_WITH_LEN("NONE") }, // NO_ARG
1223 { STRING_WITH_LEN("OPTIONAL") }, // OPT_ARG
1224 { STRING_WITH_LEN("REQUIRED") } // REQUIRED_ARG
1225 };
1226 const LEX_CSTRING *arg= args + var->option.arg_type;
1227 fields[13]->set_notnull();
1228 fields[13]->store(arg->str, arg->length, scs);
1229 }
1230
1231 if (schema_table_store_record(thd, tables->table))
1232 goto end;
1233 thd->get_stmt_da()->inc_current_row_for_warning();
1234 }
1235 res= 0;
1236end:
1237 mysql_prlock_unlock(&LOCK_system_variables_hash);
1238 thd->count_cuted_fields= save_count_cuted_fields;
1239 return res;
1240}
1241
1242/*
1243 This is a simple and inefficient helper that sets sys_var::value_origin
1244 for a specific sysvar.
1245 It should *only* be used on server startup, if you need to do this later,
1246 get yourself a pointer to your sysvar (see e.g. Sys_autocommit_ptr)
1247 and update it directly.
1248*/
1249
1250void set_sys_var_value_origin(void *ptr, enum sys_var::where here)
1251{
1252 bool found __attribute__((unused))= false;
1253 DBUG_ASSERT(!mysqld_server_started); // only to be used during startup
1254
1255 for (uint i= 0; i < system_variable_hash.records; i++)
1256 {
1257 sys_var *var= (sys_var*) my_hash_element(&system_variable_hash, i);
1258 if (var->option.value == ptr)
1259 {
1260 found= true;
1261 var->value_origin= here;
1262 /* don't break early, search for all matches */
1263 }
1264 }
1265
1266 DBUG_ASSERT(found); // variable must have been found
1267}
1268
1269enum sys_var::where get_sys_var_value_origin(void *ptr)
1270{
1271 DBUG_ASSERT(!mysqld_server_started); // only to be used during startup
1272
1273 for (uint i= 0; i < system_variable_hash.records; i++)
1274 {
1275 sys_var *var= (sys_var*) my_hash_element(&system_variable_hash, i);
1276 if (var->option.value == ptr)
1277 {
1278 return var->value_origin; //first match
1279 }
1280 }
1281
1282 DBUG_ASSERT(0); // variable must have been found
1283 return sys_var::CONFIG;
1284}
1285
1286
1287/*
1288 Find the next item in string of comma-separated items.
1289 END_POS points at the end of the string.
1290 ITEM_START and ITEM_END return the limits of the next item.
1291 Returns true while items are available, false at the end.
1292*/
1293static bool
1294engine_list_next_item(const char **pos, const char *end_pos,
1295 const char **item_start, const char **item_end)
1296{
1297 if (*pos >= end_pos)
1298 return false;
1299 *item_start= *pos;
1300 while (*pos < end_pos && **pos != ',')
1301 ++*pos;
1302 *item_end= *pos;
1303 ++*pos;
1304 return true;
1305}
1306
1307
1308static bool
1309resolve_engine_list_item(THD *thd, plugin_ref *list, uint32 *idx,
1310 const char *pos, const char *pos_end,
1311 bool error_on_unknown_engine, bool temp_copy)
1312{
1313 LEX_CSTRING item_str;
1314 plugin_ref ref;
1315 uint32 i;
1316 THD *thd_or_null = (temp_copy ? thd : NULL);
1317
1318 item_str.str= pos;
1319 item_str.length= pos_end-pos;
1320 ref= ha_resolve_by_name(thd_or_null, &item_str, false);
1321 if (!ref)
1322 {
1323 if (error_on_unknown_engine)
1324 {
1325 ErrConvString err(pos, pos_end-pos, system_charset_info);
1326 my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), err.ptr());
1327 return true;
1328 }
1329 return false;
1330 }
1331 /* Ignore duplicates, like --plugin-load does. */
1332 for (i= 0; i < *idx; ++i)
1333 {
1334 if (plugin_hton(list[i]) == plugin_hton(ref))
1335 {
1336 if (!temp_copy)
1337 plugin_unlock(NULL, ref);
1338 return false;
1339 }
1340 }
1341 list[*idx]= ref;
1342 ++*idx;
1343 return false;
1344}
1345
1346
1347/*
1348 Helper for class Sys_var_pluginlist.
1349 Resolve a comma-separated list of storage engine names to a null-terminated
1350 array of plugin_ref.
1351
1352 If TEMP_COPY is true, a THD must be given as well. In this case, the
1353 allocated memory and locked plugins are registered in the THD and will
1354 be freed / unlocked automatically. If TEMP_COPY is true, THD can be
1355 passed as NULL, and resources must be freed explicitly later with
1356 free_engine_list().
1357*/
1358plugin_ref *
1359resolve_engine_list(THD *thd, const char *str_arg, size_t str_arg_len,
1360 bool error_on_unknown_engine, bool temp_copy)
1361{
1362 uint32 count, idx;
1363 const char *pos, *item_start, *item_end;
1364 const char *str_arg_end= str_arg + str_arg_len;
1365 plugin_ref *res;
1366
1367 count= 0;
1368 pos= str_arg;
1369 for (;;)
1370 {
1371 if (!engine_list_next_item(&pos, str_arg_end, &item_start, &item_end))
1372 break;
1373 ++count;
1374 }
1375
1376 if (temp_copy)
1377 res= (plugin_ref *)thd->calloc((count+1)*sizeof(*res));
1378 else
1379 res= (plugin_ref *)my_malloc((count+1)*sizeof(*res), MYF(MY_ZEROFILL|MY_WME));
1380 if (!res)
1381 {
1382 my_error(ER_OUTOFMEMORY, MYF(0), (int)((count+1)*sizeof(*res)));
1383 goto err;
1384 }
1385
1386 idx= 0;
1387 pos= str_arg;
1388 for (;;)
1389 {
1390 if (!engine_list_next_item(&pos, str_arg_end, &item_start, &item_end))
1391 break;
1392 DBUG_ASSERT(idx < count);
1393 if (idx >= count)
1394 break;
1395 if (resolve_engine_list_item(thd, res, &idx, item_start, item_end,
1396 error_on_unknown_engine, temp_copy))
1397 goto err;
1398 }
1399
1400 return res;
1401
1402err:
1403 if (!temp_copy)
1404 free_engine_list(res);
1405 return NULL;
1406}
1407
1408
1409void
1410free_engine_list(plugin_ref *list)
1411{
1412 plugin_ref *p;
1413
1414 if (!list)
1415 return;
1416 for (p= list; *p; ++p)
1417 plugin_unlock(NULL, *p);
1418 my_free(list);
1419}
1420
1421
1422plugin_ref *
1423copy_engine_list(plugin_ref *list)
1424{
1425 plugin_ref *p;
1426 uint32 count, i;
1427
1428 for (p= list, count= 0; *p; ++p, ++count)
1429 ;
1430 p= (plugin_ref *)my_malloc((count+1)*sizeof(*p), MYF(0));
1431 if (!p)
1432 {
1433 my_error(ER_OUTOFMEMORY, MYF(0), (int)((count+1)*sizeof(*p)));
1434 return NULL;
1435 }
1436 for (i= 0; i < count; ++i)
1437 p[i]= my_plugin_lock(NULL, list[i]);
1438 p[i] = NULL;
1439 return p;
1440}
1441
1442
1443/*
1444 Create a temporary copy of an engine list. The memory will be freed
1445 (and the plugins unlocked) automatically, on the passed THD.
1446*/
1447plugin_ref *
1448temp_copy_engine_list(THD *thd, plugin_ref *list)
1449{
1450 plugin_ref *p;
1451 uint32 count, i;
1452
1453 for (p= list, count= 0; *p; ++p, ++count)
1454 ;
1455 p= (plugin_ref *)thd->alloc((count+1)*sizeof(*p));
1456 if (!p)
1457 {
1458 my_error(ER_OUTOFMEMORY, MYF(0), (int)((count+1)*sizeof(*p)));
1459 return NULL;
1460 }
1461 for (i= 0; i < count; ++i)
1462 p[i]= my_plugin_lock(thd, list[i]);
1463 p[i] = NULL;
1464 return p;
1465}
1466
1467
1468char *
1469pretty_print_engine_list(THD *thd, plugin_ref *list)
1470{
1471 plugin_ref *p;
1472 size_t size;
1473 char *buf, *pos;
1474
1475 if (!list || !*list)
1476 return thd->strmake("", 0);
1477
1478 size= 0;
1479 for (p= list; *p; ++p)
1480 size+= plugin_name(*p)->length + 1;
1481 buf= static_cast<char *>(thd->alloc(size));
1482 if (!buf)
1483 return NULL;
1484 pos= buf;
1485 for (p= list; *p; ++p)
1486 {
1487 LEX_CSTRING *name;
1488 size_t remain;
1489
1490 remain= buf + size - pos;
1491 DBUG_ASSERT(remain > 0);
1492 if (remain <= 1)
1493 break;
1494 if (pos != buf)
1495 {
1496 pos= strmake(pos, ",", remain-1);
1497 --remain;
1498 }
1499 name= plugin_name(*p);
1500 pos= strmake(pos, name->str, MY_MIN(name->length, remain-1));
1501 }
1502 *pos= '\0';
1503 return buf;
1504}
1505