1 /*
2 Copyright (c) 2005, 2019, Oracle and/or its affiliates. All rights reserved.
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, version 2.0,
6 as published by the Free Software Foundation.
7
8 This program is also distributed with certain software (including
9 but not limited to OpenSSL) that is licensed under separate terms,
10 as designated in a particular file or component or in included license
11 documentation. The authors of MySQL hereby grant you an additional
12 permission to link the program and your derivative works with the
13 separately licensed software that they have included with MySQL.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License, version 2.0, for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software Foundation,
22 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
23
24 #include "sql_priv.h" // SHOW_MY_BOOL
25 #include "unireg.h"
26 #include "my_global.h" // REQUIRED by m_string.h
27 #include "sql_class.h" // set_var.h: THD
28 #include "sys_vars_shared.h"
29 #include "sql_locale.h"
30 #include "sql_plugin.h"
31 #include "sql_parse.h" // check_table_access
32 #include "sql_base.h" // close_mysql_tables
33 #include "key.h" // key_copy
34 #include "sql_show.h" // remove_status_vars, add_status_vars
35 #include "strfunc.h" // find_set
36 #include "sql_acl.h" // *_ACL
37 #include "records.h" // init_read_record, end_read_record
38 #include <my_pthread.h>
39 #include <my_getopt.h>
40 #include "sql_audit.h"
41 #include <mysql/plugin_auth.h>
42 #include "lock.h" // MYSQL_LOCK_IGNORE_TIMEOUT
43 #include <mysql/plugin_validate_password.h>
44 #include "my_default.h"
45 #include "debug_sync.h"
46
47 #include <algorithm>
48
49 using std::min;
50 using std::max;
51
52 #define REPORT_TO_LOG 1
53 #define REPORT_TO_USER 2
54
55 extern struct st_mysql_plugin *mysql_optional_plugins[];
56 extern struct st_mysql_plugin *mysql_mandatory_plugins[];
57
58 /**
59 @note The order of the enumeration is critical.
60 @see construct_options
61 */
62 const char *global_plugin_typelib_names[]=
63 { "OFF", "ON", "FORCE", "FORCE_PLUS_PERMANENT", NULL };
64 static TYPELIB global_plugin_typelib=
65 { array_elements(global_plugin_typelib_names)-1,
66 "", global_plugin_typelib_names, NULL };
67
68 static I_List<i_string> opt_plugin_load_list;
69 I_List<i_string> *opt_plugin_load_list_ptr= &opt_plugin_load_list;
70 char *opt_plugin_dir_ptr;
71 char opt_plugin_dir[FN_REFLEN];
72 /*
73 When you ad a new plugin type, add both a string and make sure that the
74 init and deinit array are correctly updated.
75 */
76 const LEX_STRING plugin_type_names[MYSQL_MAX_PLUGIN_TYPE_NUM]=
77 {
78 { C_STRING_WITH_LEN("UDF") },
79 { C_STRING_WITH_LEN("STORAGE ENGINE") },
80 { C_STRING_WITH_LEN("FTPARSER") },
81 { C_STRING_WITH_LEN("DAEMON") },
82 { C_STRING_WITH_LEN("INFORMATION SCHEMA") },
83 { C_STRING_WITH_LEN("AUDIT") },
84 { C_STRING_WITH_LEN("REPLICATION") },
85 { C_STRING_WITH_LEN("AUTHENTICATION") },
86 { C_STRING_WITH_LEN("VALIDATE PASSWORD") }
87 };
88
89 extern int initialize_schema_table(st_plugin_int *plugin);
90 extern int finalize_schema_table(st_plugin_int *plugin);
91
92 extern int initialize_audit_plugin(st_plugin_int *plugin);
93 extern int finalize_audit_plugin(st_plugin_int *plugin);
94
95 /*
96 The number of elements in both plugin_type_initialize and
97 plugin_type_deinitialize should equal to the number of plugins
98 defined.
99 */
100 plugin_type_init plugin_type_initialize[MYSQL_MAX_PLUGIN_TYPE_NUM]=
101 {
102 0,ha_initialize_handlerton,0,0,initialize_schema_table,
103 initialize_audit_plugin,0,0,0
104 };
105
106 plugin_type_init plugin_type_deinitialize[MYSQL_MAX_PLUGIN_TYPE_NUM]=
107 {
108 0,ha_finalize_handlerton,0,0,finalize_schema_table,
109 finalize_audit_plugin,0,0,0
110 };
111
112 #ifdef HAVE_DLOPEN
113 static const char *plugin_interface_version_sym=
114 "_mysql_plugin_interface_version_";
115 static const char *sizeof_st_plugin_sym=
116 "_mysql_sizeof_struct_st_plugin_";
117 static const char *plugin_declarations_sym= "_mysql_plugin_declarations_";
118 static int min_plugin_interface_version= MYSQL_PLUGIN_INTERFACE_VERSION & ~0xFF;
119 #endif
120
121 static void* innodb_callback_data;
122
123 /* Note that 'int version' must be the first field of every plugin
124 sub-structure (plugin->info).
125 */
126 static int min_plugin_info_interface_version[MYSQL_MAX_PLUGIN_TYPE_NUM]=
127 {
128 0x0000,
129 MYSQL_HANDLERTON_INTERFACE_VERSION,
130 MYSQL_FTPARSER_INTERFACE_VERSION,
131 MYSQL_DAEMON_INTERFACE_VERSION,
132 MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION,
133 MYSQL_AUDIT_INTERFACE_VERSION,
134 MYSQL_REPLICATION_INTERFACE_VERSION,
135 MYSQL_AUTHENTICATION_INTERFACE_VERSION,
136 MYSQL_VALIDATE_PASSWORD_INTERFACE_VERSION
137 };
138 static int cur_plugin_info_interface_version[MYSQL_MAX_PLUGIN_TYPE_NUM]=
139 {
140 0x0000, /* UDF: not implemented */
141 MYSQL_HANDLERTON_INTERFACE_VERSION,
142 MYSQL_FTPARSER_INTERFACE_VERSION,
143 MYSQL_DAEMON_INTERFACE_VERSION,
144 MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION,
145 MYSQL_AUDIT_INTERFACE_VERSION,
146 MYSQL_REPLICATION_INTERFACE_VERSION,
147 MYSQL_AUTHENTICATION_INTERFACE_VERSION,
148 MYSQL_VALIDATE_PASSWORD_INTERFACE_VERSION
149 };
150
151 /* support for Services */
152
153 #include "sql_plugin_services.h"
154
155 /*
156 A mutex LOCK_plugin_delete must be acquired before calling plugin_del
157 function.
158 */
159 mysql_mutex_t LOCK_plugin_delete;
160
161 /*
162 A mutex LOCK_plugin must be acquired before accessing the
163 following variables/structures.
164 We are always manipulating ref count, so a rwlock here is unneccessary.
165 */
166 mysql_mutex_t LOCK_plugin;
167 static DYNAMIC_ARRAY plugin_dl_array;
168 static DYNAMIC_ARRAY plugin_array;
169 static HASH plugin_hash[MYSQL_MAX_PLUGIN_TYPE_NUM];
170 static bool reap_needed= false;
171 static int plugin_array_version=0;
172
173 static bool initialized= 0;
174
175 /*
176 write-lock on LOCK_system_variables_hash is required before modifying
177 the following variables/structures
178 */
179 static MEM_ROOT plugin_mem_root;
180 static uint global_variables_dynamic_size= 0;
181 static HASH bookmark_hash;
182 /** Hash for system variables of string type with MEMALLOC flag. */
183 static HASH malloced_string_type_sysvars_bookmark_hash;
184
185
186 /*
187 hidden part of opaque value passed to variable check functions.
188 Used to provide a object-like structure to non C++ consumers.
189 */
190 struct st_item_value_holder : public st_mysql_value
191 {
192 Item *item;
193 };
194
195
196 /*
197 stored in bookmark_hash, this structure is never removed from the
198 hash and is used to mark a single offset for a thd local variable
199 even if plugins have been uninstalled and reinstalled, repeatedly.
200 This structure is allocated from plugin_mem_root.
201
202 The key format is as follows:
203 1 byte - variable type code
204 name_len bytes - variable name
205 '\0' - end of key
206 */
207 struct st_bookmark
208 {
209 uint name_len;
210 int offset;
211 uint version;
212 char key[1];
213 };
214
215
216 /*
217 skeleton of a plugin variable - portion of structure common to all.
218 */
219 struct st_mysql_sys_var
220 {
221 MYSQL_PLUGIN_VAR_HEADER;
222 };
223
224 static SHOW_TYPE pluginvar_show_type(st_mysql_sys_var *plugin_var);
225
226
227 /*
228 sys_var class for access to all plugin variables visible to the user
229 */
230 class sys_var_pluginvar: public sys_var
231 {
232 public:
233 struct st_plugin_int *plugin;
234 struct st_mysql_sys_var *plugin_var;
235 /**
236 variable name from whatever is hard-coded in the plugin source
237 and doesn't have pluginname- prefix is replaced by an allocated name
238 with a plugin prefix. When plugin is uninstalled we need to restore the
239 pointer to point to the hard-coded value, because plugin may be
240 installed/uninstalled many times without reloading the shared object.
241 */
242 const char *orig_pluginvar_name;
243
operator new(size_t size,MEM_ROOT * mem_root)244 static void *operator new(size_t size, MEM_ROOT *mem_root)
245 { return (void*) alloc_root(mem_root, size); }
operator delete(void * ptr_arg,size_t size)246 static void operator delete(void *ptr_arg,size_t size)
247 { TRASH(ptr_arg, size); }
248
sys_var_pluginvar(sys_var_chain * chain,const char * name_arg,struct st_mysql_sys_var * plugin_var_arg)249 sys_var_pluginvar(sys_var_chain *chain, const char *name_arg,
250 struct st_mysql_sys_var *plugin_var_arg)
251 :sys_var(chain, name_arg, plugin_var_arg->comment,
252 (plugin_var_arg->flags & PLUGIN_VAR_THDLOCAL ? SESSION : GLOBAL) |
253 (plugin_var_arg->flags & PLUGIN_VAR_READONLY ? READONLY : 0),
254 0, -1, NO_ARG, pluginvar_show_type(plugin_var_arg), 0, 0,
255 VARIABLE_NOT_IN_BINLOG, NULL, NULL, NULL, PARSE_NORMAL),
256 plugin_var(plugin_var_arg), orig_pluginvar_name(plugin_var_arg->name)
257 { plugin_var->name= name_arg; }
cast_pluginvar()258 sys_var_pluginvar *cast_pluginvar() { return this; }
259 bool check_update_type(Item_result type);
260 SHOW_TYPE show_type();
261 uchar* real_value_ptr(THD *thd, enum_var_type type);
262 TYPELIB* plugin_var_typelib(void);
263 uchar* do_value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
session_value_ptr(THD * thd,LEX_STRING * base)264 uchar* session_value_ptr(THD *thd, LEX_STRING *base)
265 { return do_value_ptr(thd, OPT_SESSION, base); }
global_value_ptr(THD * thd,LEX_STRING * base)266 uchar* global_value_ptr(THD *thd, LEX_STRING *base)
267 { return do_value_ptr(thd, OPT_GLOBAL, base); }
268 bool do_check(THD *thd, set_var *var);
session_save_default(THD * thd,set_var * var)269 virtual void session_save_default(THD *thd, set_var *var) {}
global_save_default(THD * thd,set_var * var)270 virtual void global_save_default(THD *thd, set_var *var) {}
271 bool session_update(THD *thd, set_var *var);
272 bool global_update(THD *thd, set_var *var);
273 };
274
275
276 /* prototypes */
277 static void plugin_load(MEM_ROOT *tmp_root, int *argc, char **argv);
278 static bool plugin_load_list(MEM_ROOT *tmp_root, int *argc, char **argv,
279 const char *list);
280 static my_bool check_if_option_is_deprecated(int optid,
281 const struct my_option *opt,
282 char *argument);
283 static int test_plugin_options(MEM_ROOT *, struct st_plugin_int *,
284 int *, char **);
285 static bool register_builtin(struct st_mysql_plugin *, struct st_plugin_int *,
286 struct st_plugin_int **);
287 static void unlock_variables(THD *thd, struct system_variables *vars);
288 static void cleanup_variables(THD *thd, struct system_variables *vars);
289 static void plugin_vars_free_values(sys_var *vars);
290 static bool plugin_var_memalloc_session_update(THD *thd,
291 struct st_mysql_sys_var *var,
292 char **dest, const char *value);
293 static bool plugin_var_memalloc_global_update(THD *thd,
294 struct st_mysql_sys_var *var,
295 char **dest, const char *value);
296 static void plugin_var_memalloc_free(struct system_variables *vars);
297 static void restore_pluginvar_names(sys_var *first);
298 static void plugin_opt_set_limits(struct my_option *,
299 const struct st_mysql_sys_var *);
300 #define my_intern_plugin_lock(A,B) intern_plugin_lock(A,B)
301 #define my_intern_plugin_lock_ci(A,B) intern_plugin_lock(A,B)
302 static plugin_ref intern_plugin_lock(LEX *lex, plugin_ref plugin);
303 static void intern_plugin_unlock(LEX *lex, plugin_ref plugin);
304 static void reap_plugins(void);
305
report_error(int where_to,uint error,...)306 static void report_error(int where_to, uint error, ...)
307 {
308 va_list args;
309 if (where_to & REPORT_TO_USER)
310 {
311 va_start(args, error);
312 my_printv_error(error, ER(error), MYF(0), args);
313 va_end(args);
314 }
315 if (where_to & REPORT_TO_LOG)
316 {
317 va_start(args, error);
318 error_log_print(ERROR_LEVEL, ER_DEFAULT(error), args);
319 va_end(args);
320 }
321 }
322
323 /**
324 Check if the provided path is valid in the sense that it does cause
325 a relative reference outside the directory.
326
327 @note Currently, this function only check if there are any
328 characters in FN_DIRSEP in the string, but it might change in the
329 future.
330
331 @code
332 check_valid_path("../foo.so") -> true
333 check_valid_path("foo.so") -> false
334 @endcode
335 */
check_valid_path(const char * path,size_t len)336 bool check_valid_path(const char *path, size_t len)
337 {
338 size_t prefix= my_strcspn(files_charset_info, path, path + len, FN_DIRSEP,
339 strlen(FN_DIRSEP));
340 return prefix < len;
341 }
342
343
344 /****************************************************************************
345 Value type thunks, allows the C world to play in the C++ world
346 ****************************************************************************/
347
item_value_type(struct st_mysql_value * value)348 static int item_value_type(struct st_mysql_value *value)
349 {
350 switch (((st_item_value_holder*)value)->item->result_type()) {
351 case INT_RESULT:
352 return MYSQL_VALUE_TYPE_INT;
353 case REAL_RESULT:
354 return MYSQL_VALUE_TYPE_REAL;
355 default:
356 return MYSQL_VALUE_TYPE_STRING;
357 }
358 }
359
item_val_str(struct st_mysql_value * value,char * buffer,int * length)360 static const char *item_val_str(struct st_mysql_value *value,
361 char *buffer, int *length)
362 {
363 String str(buffer, *length, system_charset_info), *res;
364 if (!(res= ((st_item_value_holder*)value)->item->val_str(&str)))
365 return NULL;
366 *length= res->length();
367 if (res->c_ptr_quick() == buffer)
368 return buffer;
369
370 /*
371 Lets be nice and create a temporary string since the
372 buffer was too small
373 */
374 return current_thd->strmake(res->c_ptr_quick(), res->length());
375 }
376
377
item_val_int(struct st_mysql_value * value,long long * buf)378 static int item_val_int(struct st_mysql_value *value, long long *buf)
379 {
380 Item *item= ((st_item_value_holder*)value)->item;
381 *buf= item->val_int();
382 if (item->is_null())
383 return 1;
384 return 0;
385 }
386
item_is_unsigned(struct st_mysql_value * value)387 static int item_is_unsigned(struct st_mysql_value *value)
388 {
389 Item *item= ((st_item_value_holder*)value)->item;
390 return item->unsigned_flag;
391 }
392
item_val_real(struct st_mysql_value * value,double * buf)393 static int item_val_real(struct st_mysql_value *value, double *buf)
394 {
395 Item *item= ((st_item_value_holder*)value)->item;
396 *buf= item->val_real();
397 if (item->is_null())
398 return 1;
399 return 0;
400 }
401
402
403 /****************************************************************************
404 Plugin support code
405 ****************************************************************************/
406
407 #ifdef HAVE_DLOPEN
408
plugin_dl_find(const LEX_STRING * dl)409 static struct st_plugin_dl *plugin_dl_find(const LEX_STRING *dl)
410 {
411 uint i;
412 struct st_plugin_dl *tmp;
413 DBUG_ENTER("plugin_dl_find");
414 for (i= 0; i < plugin_dl_array.elements; i++)
415 {
416 tmp= *dynamic_element(&plugin_dl_array, i, struct st_plugin_dl **);
417 if (tmp->ref_count &&
418 ! my_strnncoll(files_charset_info,
419 (const uchar *)dl->str, dl->length,
420 (const uchar *)tmp->dl.str, tmp->dl.length))
421 DBUG_RETURN(tmp);
422 }
423 DBUG_RETURN(0);
424 }
425
426
plugin_dl_insert_or_reuse(struct st_plugin_dl * plugin_dl)427 static st_plugin_dl *plugin_dl_insert_or_reuse(struct st_plugin_dl *plugin_dl)
428 {
429 uint i;
430 struct st_plugin_dl *tmp;
431 DBUG_ENTER("plugin_dl_insert_or_reuse");
432 for (i= 0; i < plugin_dl_array.elements; i++)
433 {
434 tmp= *dynamic_element(&plugin_dl_array, i, struct st_plugin_dl **);
435 if (! tmp->ref_count)
436 {
437 memcpy(tmp, plugin_dl, sizeof(struct st_plugin_dl));
438 DBUG_RETURN(tmp);
439 }
440 }
441 if (insert_dynamic(&plugin_dl_array, &plugin_dl))
442 DBUG_RETURN(0);
443 tmp= *dynamic_element(&plugin_dl_array, plugin_dl_array.elements - 1,
444 struct st_plugin_dl **)=
445 (struct st_plugin_dl *) memdup_root(&plugin_mem_root, (uchar*)plugin_dl,
446 sizeof(struct st_plugin_dl));
447 DBUG_RETURN(tmp);
448 }
449 #endif /* HAVE_DLOPEN */
450
451
free_plugin_mem(struct st_plugin_dl * p)452 static inline void free_plugin_mem(struct st_plugin_dl *p)
453 {
454 #ifdef HAVE_DLOPEN
455 if (p->handle)
456 dlclose(p->handle);
457 #endif
458 my_free(p->dl.str);
459 if (p->version != MYSQL_PLUGIN_INTERFACE_VERSION)
460 my_free(p->plugins);
461 }
462
463
plugin_dl_add(const LEX_STRING * dl,int report,bool allow_dl_path)464 static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl,
465 int report,
466 bool allow_dl_path)
467 {
468 #ifdef HAVE_DLOPEN
469 char dlpath[FN_REFLEN];
470 uint dummy_errors, dlpathlen, i;
471 struct st_plugin_dl *tmp, plugin_dl;
472 size_t dl_fname_pos= 0;
473 void *sym;
474 DBUG_ENTER("plugin_dl_add");
475 DBUG_PRINT("enter", ("dl->str: '%s', dl->length: %d",
476 dl->str, (int) dl->length));
477
478 if (allow_dl_path)
479 dl_fname_pos = dirname_length(dl->str);
480
481 if (!dl_fname_pos) {
482 size_t plugin_dir_len= strlen(opt_plugin_dir);
483 /*
484 Ensure that the dll doesn't have a path.
485 This is done to ensure that only approved libraries from the
486 plugin directory are used (to make this even remotely secure).
487 */
488 if (check_valid_path(dl->str, dl->length) ||
489 check_string_char_length((LEX_STRING *) dl, "", NAME_CHAR_LEN,
490 system_charset_info, 1) ||
491 plugin_dir_len + dl->length + 1 >= FN_REFLEN)
492 {
493 report_error(report, ER_UDF_NO_PATHS);
494 DBUG_RETURN(0);
495 }
496 /* Compile dll path */
497 dlpathlen=
498 strxnmov(dlpath,
499 sizeof(dlpath) - 1,
500 opt_plugin_dir,
501 "/",
502 dl->str,
503 NullS) -
504 dlpath;
505 }
506 else
507 {
508 if (dl->length + 1 >= sizeof(dlpath))
509 {
510 report_error(report, ER_UDF_NO_PATHS);
511 DBUG_RETURN(0);
512 }
513 strncpy(dlpath, dl->str, sizeof(dlpath) - 1);
514 dlpathlen= dl->length;
515 }
516
517 (void) unpack_filename(dlpath, dlpath);
518
519 /* If this dll is already loaded just increase ref_count. */
520 if ((tmp= plugin_dl_find(dl)))
521 {
522 tmp->ref_count++;
523 DBUG_RETURN(tmp);
524 }
525 memset(&plugin_dl, 0, sizeof(plugin_dl));
526 plugin_dl.ref_count= 1;
527 /* Open new dll handle */
528 if (!(plugin_dl.handle= dlopen(dlpath, RTLD_NOW)))
529 {
530 const char *errmsg;
531 int error_number= dlopen_errno;
532 DLERROR_GENERATE(errmsg, error_number);
533
534 if (!strncmp(dlpath, errmsg, dlpathlen))
535 { // if errmsg starts from dlpath, trim this prefix.
536 errmsg+=dlpathlen;
537 if (*errmsg == ':') errmsg++;
538 if (*errmsg == ' ') errmsg++;
539 }
540 report_error(report, ER_CANT_OPEN_LIBRARY, dlpath, error_number, errmsg);
541 DBUG_RETURN(0);
542 }
543 /* Determine interface version */
544 if (!(sym= dlsym(plugin_dl.handle, plugin_interface_version_sym)))
545 {
546 free_plugin_mem(&plugin_dl);
547 report_error(report, ER_CANT_FIND_DL_ENTRY, plugin_interface_version_sym);
548 DBUG_RETURN(0);
549 }
550 plugin_dl.version= *(int *)sym;
551 /* Versioning */
552 if (plugin_dl.version < min_plugin_interface_version ||
553 (plugin_dl.version >> 8) > (MYSQL_PLUGIN_INTERFACE_VERSION >> 8))
554 {
555 free_plugin_mem(&plugin_dl);
556 report_error(report, ER_CANT_OPEN_LIBRARY, dlpath, 0,
557 "plugin interface version mismatch");
558 DBUG_RETURN(0);
559 }
560
561 /* link the services in */
562 for (i= 0; i < array_elements(list_of_services); i++)
563 {
564 if ((sym= dlsym(plugin_dl.handle, list_of_services[i].name)))
565 {
566 uint ver= (uint)(intptr)*(void**)sym;
567 if ((*(void**)sym) != list_of_services[i].service && /* already replaced */
568 (ver > list_of_services[i].version ||
569 (ver >> 8) < (list_of_services[i].version >> 8)))
570 {
571 char buf[MYSQL_ERRMSG_SIZE];
572 my_snprintf(buf, sizeof(buf),
573 "service '%s' interface version mismatch",
574 list_of_services[i].name);
575 report_error(report, ER_CANT_OPEN_LIBRARY, dlpath, 0, buf);
576 DBUG_RETURN(0);
577 }
578 *(void**)sym= list_of_services[i].service;
579 }
580 }
581
582 /* Find plugin declarations */
583 if (!(sym= dlsym(plugin_dl.handle, plugin_declarations_sym)))
584 {
585 free_plugin_mem(&plugin_dl);
586 report_error(report, ER_CANT_FIND_DL_ENTRY, plugin_declarations_sym);
587 DBUG_RETURN(0);
588 }
589
590 if (plugin_dl.version != MYSQL_PLUGIN_INTERFACE_VERSION)
591 {
592 uint sizeof_st_plugin;
593 struct st_mysql_plugin *old, *cur;
594 char *ptr= (char *)sym;
595
596 if ((sym= dlsym(plugin_dl.handle, sizeof_st_plugin_sym)))
597 sizeof_st_plugin= *(int *)sym;
598 else
599 {
600 #ifdef ERROR_ON_NO_SIZEOF_PLUGIN_SYMBOL
601 report_error(report, ER_CANT_FIND_DL_ENTRY, sizeof_st_plugin_sym);
602 DBUG_RETURN(0);
603 #else
604 /*
605 When the following assert starts failing, we'll have to switch
606 to the upper branch of the #ifdef
607 */
608 DBUG_ASSERT(min_plugin_interface_version == 0);
609 sizeof_st_plugin= (int)offsetof(struct st_mysql_plugin, version);
610 #endif
611 }
612
613 /*
614 What's the purpose of this loop? If the goal is to catch a
615 missing 0 record at the end of a list, it will fail miserably
616 since the compiler is likely to optimize this away. /Matz
617 */
618 for (i= 0;
619 ((struct st_mysql_plugin *)(ptr+i*sizeof_st_plugin))->info;
620 i++)
621 /* no op */;
622
623 cur= (struct st_mysql_plugin*)
624 my_malloc((i+1)*sizeof(struct st_mysql_plugin), MYF(MY_ZEROFILL|MY_WME));
625 if (!cur)
626 {
627 free_plugin_mem(&plugin_dl);
628 report_error(report, ER_OUTOFMEMORY,
629 static_cast<int>(plugin_dl.dl.length));
630 DBUG_RETURN(0);
631 }
632 /*
633 All st_plugin fields not initialized in the plugin explicitly, are
634 set to 0. It matches C standard behaviour for struct initializers that
635 have less values than the struct definition.
636 */
637 for (i=0;
638 (old=(struct st_mysql_plugin *)(ptr+i*sizeof_st_plugin))->info;
639 i++)
640 memcpy(cur+i, old, min<size_t>(sizeof(cur[i]), sizeof_st_plugin));
641
642 sym= cur;
643 }
644 plugin_dl.plugins= (struct st_mysql_plugin *)sym;
645
646 /*
647 If report is REPORT_TO_USER, we were called from
648 mysql_install_plugin. Otherwise, we are called directly or
649 indirectly from plugin_init.
650 */
651 if (report == REPORT_TO_USER)
652 {
653 st_mysql_plugin *plugin= plugin_dl.plugins;
654 for ( ; plugin->info ; ++plugin)
655 if (plugin->flags & PLUGIN_OPT_NO_INSTALL)
656 {
657 report_error(report, ER_PLUGIN_NO_INSTALL, plugin->name);
658 free_plugin_mem(&plugin_dl);
659 DBUG_RETURN(0);
660 }
661 }
662
663 /* Duplicate and convert dll name */
664 plugin_dl.dl.length= dl->length * files_charset_info->mbmaxlen + 1;
665 if (! (plugin_dl.dl.str= (char*) my_malloc(plugin_dl.dl.length, MYF(0))))
666 {
667 free_plugin_mem(&plugin_dl);
668 report_error(report, ER_OUTOFMEMORY,
669 static_cast<int>(plugin_dl.dl.length));
670 DBUG_RETURN(0);
671 }
672 plugin_dl.dl.length= copy_and_convert(plugin_dl.dl.str, plugin_dl.dl.length,
673 files_charset_info, dl->str, dl->length, system_charset_info,
674 &dummy_errors);
675 plugin_dl.dl.str[plugin_dl.dl.length]= 0;
676 /* Add this dll to array */
677 if (! (tmp= plugin_dl_insert_or_reuse(&plugin_dl)))
678 {
679 free_plugin_mem(&plugin_dl);
680 report_error(report, ER_OUTOFMEMORY,
681 static_cast<int>(sizeof(struct st_plugin_dl)));
682 DBUG_RETURN(0);
683 }
684 DBUG_RETURN(tmp);
685 #else
686 DBUG_ENTER("plugin_dl_add");
687 report_error(report, ER_FEATURE_DISABLED, "plugin", "HAVE_DLOPEN");
688 DBUG_RETURN(0);
689 #endif
690 }
691
692
plugin_dl_del(const LEX_STRING * dl)693 static void plugin_dl_del(const LEX_STRING *dl)
694 {
695 #ifdef HAVE_DLOPEN
696 uint i;
697 DBUG_ENTER("plugin_dl_del");
698
699 mysql_mutex_assert_owner(&LOCK_plugin);
700
701 for (i= 0; i < plugin_dl_array.elements; i++)
702 {
703 struct st_plugin_dl *tmp= *dynamic_element(&plugin_dl_array, i,
704 struct st_plugin_dl **);
705 if (tmp->ref_count &&
706 ! my_strnncoll(files_charset_info,
707 (const uchar *)dl->str, dl->length,
708 (const uchar *)tmp->dl.str, tmp->dl.length))
709 {
710 /* Do not remove this element, unless no other plugin uses this dll. */
711 if (! --tmp->ref_count)
712 {
713 free_plugin_mem(tmp);
714 memset(tmp, 0, sizeof(struct st_plugin_dl));
715 }
716 break;
717 }
718 }
719 DBUG_VOID_RETURN;
720 #endif
721 }
722
723
plugin_find_internal(const LEX_STRING * name,int type)724 static struct st_plugin_int *plugin_find_internal(const LEX_STRING *name, int type)
725 {
726 uint i;
727 DBUG_ENTER("plugin_find_internal");
728 if (!initialized || !name->str)
729 DBUG_RETURN(0);
730
731 mysql_mutex_assert_owner(&LOCK_plugin);
732
733 if (type == MYSQL_ANY_PLUGIN)
734 {
735 for (i= 0; i < MYSQL_MAX_PLUGIN_TYPE_NUM; i++)
736 {
737 struct st_plugin_int *plugin= (st_plugin_int *)
738 my_hash_search(&plugin_hash[i], (const uchar *)name->str, name->length);
739 if (plugin)
740 DBUG_RETURN(plugin);
741 }
742 }
743 else
744 DBUG_RETURN((st_plugin_int *)
745 my_hash_search(&plugin_hash[type], (const uchar *)name->str,
746 name->length));
747 DBUG_RETURN(0);
748 }
749
750
plugin_status(const LEX_STRING * name,int type)751 static SHOW_COMP_OPTION plugin_status(const LEX_STRING *name, int type)
752 {
753 SHOW_COMP_OPTION rc= SHOW_OPTION_NO;
754 struct st_plugin_int *plugin;
755 DBUG_ENTER("plugin_is_ready");
756 mysql_mutex_lock(&LOCK_plugin);
757 if ((plugin= plugin_find_internal(name, type)))
758 {
759 rc= SHOW_OPTION_DISABLED;
760 if (plugin->state == PLUGIN_IS_READY)
761 rc= SHOW_OPTION_YES;
762 }
763 mysql_mutex_unlock(&LOCK_plugin);
764 DBUG_RETURN(rc);
765 }
766
767
plugin_is_ready(const LEX_STRING * name,int type)768 bool plugin_is_ready(const LEX_STRING *name, int type)
769 {
770 bool rc= FALSE;
771 if (plugin_status(name, type) == SHOW_OPTION_YES)
772 rc= TRUE;
773 return rc;
774 }
775
776
plugin_status(const char * name,size_t len,int type)777 SHOW_COMP_OPTION plugin_status(const char *name, size_t len, int type)
778 {
779 LEX_STRING plugin_name= { (char *) name, len };
780 return plugin_status(&plugin_name, type);
781 }
782
783
intern_plugin_lock(LEX * lex,plugin_ref rc)784 static plugin_ref intern_plugin_lock(LEX *lex, plugin_ref rc)
785 {
786 st_plugin_int *pi= plugin_ref_to_int(rc);
787 DBUG_ENTER("intern_plugin_lock");
788
789 mysql_mutex_assert_owner(&LOCK_plugin);
790
791 if (pi->state & (PLUGIN_IS_READY | PLUGIN_IS_UNINITIALIZED))
792 {
793 plugin_ref plugin;
794 #ifdef DBUG_OFF
795 /* built-in plugins don't need ref counting */
796 if (!pi->plugin_dl)
797 DBUG_RETURN(pi);
798
799 plugin= pi;
800 #else
801 /*
802 For debugging, we do an additional malloc which allows the
803 memory manager and/or valgrind to track locked references and
804 double unlocks to aid resolving reference counting problems.
805 */
806 if (!(plugin= (plugin_ref) my_malloc(sizeof(pi), MYF(MY_WME))))
807 DBUG_RETURN(NULL);
808
809 *plugin= pi;
810 #endif
811 pi->ref_count++;
812 DBUG_PRINT("info",("thd: %p, plugin: \"%s\", ref_count: %d",
813 current_thd, pi->name.str, pi->ref_count));
814 if (lex)
815 insert_dynamic(&lex->plugins, &plugin);
816 DBUG_RETURN(plugin);
817 }
818 DBUG_RETURN(NULL);
819 }
820
821
plugin_lock(THD * thd,plugin_ref * ptr)822 plugin_ref plugin_lock(THD *thd, plugin_ref *ptr)
823 {
824 LEX *lex= thd ? thd->lex : 0;
825 plugin_ref rc;
826 DBUG_ENTER("plugin_lock");
827 mysql_mutex_lock(&LOCK_plugin);
828 rc= my_intern_plugin_lock_ci(lex, *ptr);
829 mysql_mutex_unlock(&LOCK_plugin);
830 DBUG_RETURN(rc);
831 }
832
833
plugin_lock_by_name(THD * thd,const LEX_STRING * name,int type)834 plugin_ref plugin_lock_by_name(THD *thd, const LEX_STRING *name, int type)
835 {
836 LEX *lex= thd ? thd->lex : 0;
837 plugin_ref rc= NULL;
838 st_plugin_int *plugin;
839 DBUG_ENTER("plugin_lock_by_name");
840 mysql_mutex_lock(&LOCK_plugin);
841 if ((plugin= plugin_find_internal(name, type)))
842 rc= my_intern_plugin_lock_ci(lex, plugin_int_to_ref(plugin));
843 mysql_mutex_unlock(&LOCK_plugin);
844 DBUG_RETURN(rc);
845 }
846
847
plugin_insert_or_reuse(struct st_plugin_int * plugin)848 static st_plugin_int *plugin_insert_or_reuse(struct st_plugin_int *plugin)
849 {
850 uint i;
851 struct st_plugin_int *tmp;
852 DBUG_ENTER("plugin_insert_or_reuse");
853 for (i= 0; i < plugin_array.elements; i++)
854 {
855 tmp= *dynamic_element(&plugin_array, i, struct st_plugin_int **);
856 if (tmp->state == PLUGIN_IS_FREED)
857 {
858 memcpy(tmp, plugin, sizeof(struct st_plugin_int));
859 DBUG_RETURN(tmp);
860 }
861 }
862 if (insert_dynamic(&plugin_array, &plugin))
863 DBUG_RETURN(0);
864 tmp= *dynamic_element(&plugin_array, plugin_array.elements - 1,
865 struct st_plugin_int **)=
866 (struct st_plugin_int *) memdup_root(&plugin_mem_root, (uchar*)plugin,
867 sizeof(struct st_plugin_int));
868 DBUG_RETURN(tmp);
869 }
870
871
872 /*
873 NOTE
874 Requires that a write-lock is held on LOCK_system_variables_hash
875 */
plugin_add(MEM_ROOT * tmp_root,const LEX_STRING * name,const LEX_STRING * dl,int * argc,char ** argv,int report,bool allow_path)876 static bool plugin_add(MEM_ROOT *tmp_root,
877 const LEX_STRING *name, const LEX_STRING *dl,
878 int *argc, char **argv, int report, bool allow_path)
879 {
880 struct st_plugin_int tmp;
881 struct st_mysql_plugin *plugin;
882 DBUG_ENTER("plugin_add");
883 if (plugin_find_internal(name, MYSQL_ANY_PLUGIN))
884 {
885 report_error(report, ER_UDF_EXISTS, name->str);
886 DBUG_RETURN(TRUE);
887 }
888 /* Clear the whole struct to catch future extensions. */
889 memset(&tmp, 0, sizeof(tmp));
890 if (! (tmp.plugin_dl= plugin_dl_add(dl, report, allow_path)))
891 DBUG_RETURN(TRUE);
892 /* Find plugin by name */
893 for (plugin= tmp.plugin_dl->plugins; plugin->info; plugin++)
894 {
895 uint name_len= strlen(plugin->name);
896 if (plugin->type >= 0 && plugin->type < MYSQL_MAX_PLUGIN_TYPE_NUM &&
897 ! my_strnncoll(system_charset_info,
898 (const uchar *)name->str, name->length,
899 (const uchar *)plugin->name,
900 name_len))
901 {
902 struct st_plugin_int *tmp_plugin_ptr;
903 if (*(int*)plugin->info <
904 min_plugin_info_interface_version[plugin->type] ||
905 ((*(int*)plugin->info) >> 8) >
906 (cur_plugin_info_interface_version[plugin->type] >> 8))
907 {
908 char buf[256];
909 strxnmov(buf, sizeof(buf) - 1, "API version for ",
910 plugin_type_names[plugin->type].str,
911 " plugin is too different", NullS);
912 report_error(report, ER_CANT_OPEN_LIBRARY, dl->str, 0, buf);
913 goto err;
914 }
915 tmp.plugin= plugin;
916 tmp.name.str= (char *)plugin->name;
917 tmp.name.length= name_len;
918 tmp.ref_count= 0;
919 tmp.state= PLUGIN_IS_UNINITIALIZED;
920 tmp.load_option= PLUGIN_ON;
921 if (test_plugin_options(tmp_root, &tmp, argc, argv))
922 tmp.state= PLUGIN_IS_DISABLED;
923
924 if ((tmp_plugin_ptr= plugin_insert_or_reuse(&tmp)))
925 {
926 plugin_array_version++;
927 if (!my_hash_insert(&plugin_hash[plugin->type], (uchar*)tmp_plugin_ptr))
928 {
929 init_alloc_root(&tmp_plugin_ptr->mem_root, 4096, 4096);
930 DBUG_RETURN(FALSE);
931 }
932 tmp_plugin_ptr->state= PLUGIN_IS_FREED;
933 }
934 mysql_del_sys_var_chain(tmp.system_vars);
935 restore_pluginvar_names(tmp.system_vars);
936 goto err;
937
938 /* plugin was disabled */
939 plugin_dl_del(dl);
940 DBUG_RETURN(FALSE);
941 }
942 }
943 report_error(report, ER_CANT_FIND_DL_ENTRY, name->str);
944 err:
945 plugin_dl_del(dl);
946 DBUG_RETURN(TRUE);
947 }
948
949
plugin_deinitialize(struct st_plugin_int * plugin,bool ref_check)950 static void plugin_deinitialize(struct st_plugin_int *plugin, bool ref_check)
951 {
952 /*
953 we don't want to hold the LOCK_plugin mutex as it may cause
954 deinitialization to deadlock if plugins have worker threads
955 with plugin locks
956 */
957 mysql_mutex_assert_not_owner(&LOCK_plugin);
958
959 if (plugin->plugin->status_vars)
960 {
961 #ifdef FIX_LATER
962 /**
963 @todo
964 unfortunately, status variables were introduced without a
965 pluginname_ namespace, that is pluginname_ was not added automatically
966 to status variable names. It should be fixed together with the next
967 incompatible API change.
968 */
969 SHOW_VAR array[2]= {
970 {plugin->plugin->name, (char*)plugin->plugin->status_vars, SHOW_ARRAY},
971 {0, 0, SHOW_UNDEF}
972 };
973 remove_status_vars(array);
974 #else
975 remove_status_vars(plugin->plugin->status_vars);
976 #endif /* FIX_LATER */
977 }
978
979 if (plugin_type_deinitialize[plugin->plugin->type])
980 {
981 if ((*plugin_type_deinitialize[plugin->plugin->type])(plugin))
982 {
983 sql_print_error("Plugin '%s' of type %s failed deinitialization",
984 plugin->name.str, plugin_type_names[plugin->plugin->type].str);
985 }
986 }
987 else if (plugin->plugin->deinit)
988 {
989 DBUG_PRINT("info", ("Deinitializing plugin: '%s'", plugin->name.str));
990 if (plugin->plugin->deinit(plugin))
991 {
992 DBUG_PRINT("warning", ("Plugin '%s' deinit function returned error.",
993 plugin->name.str));
994 }
995 }
996 plugin->state= PLUGIN_IS_UNINITIALIZED;
997
998 /*
999 We do the check here because NDB has a worker THD which doesn't
1000 exit until NDB is shut down.
1001 */
1002 if (ref_check && plugin->ref_count)
1003 sql_print_error("Plugin '%s' has ref_count=%d after deinitialization.",
1004 plugin->name.str, plugin->ref_count);
1005 }
1006
plugin_del(struct st_plugin_int * plugin)1007 static void plugin_del(struct st_plugin_int *plugin)
1008 {
1009 DBUG_ENTER("plugin_del(plugin)");
1010 mysql_mutex_assert_owner(&LOCK_plugin);
1011 mysql_mutex_assert_owner(&LOCK_plugin_delete);
1012 /* Free allocated strings before deleting the plugin. */
1013 mysql_rwlock_wrlock(&LOCK_system_variables_hash);
1014 mysql_del_sys_var_chain(plugin->system_vars);
1015 mysql_rwlock_unlock(&LOCK_system_variables_hash);
1016 restore_pluginvar_names(plugin->system_vars);
1017 plugin_vars_free_values(plugin->system_vars);
1018 my_hash_delete(&plugin_hash[plugin->plugin->type], (uchar*)plugin);
1019 if (plugin->plugin_dl)
1020 plugin_dl_del(&plugin->plugin_dl->dl);
1021 plugin->state= PLUGIN_IS_FREED;
1022 plugin_array_version++;
1023 free_root(&plugin->mem_root, MYF(0));
1024 DBUG_VOID_RETURN;
1025 }
1026
reap_plugins(void)1027 static void reap_plugins(void)
1028 {
1029 uint count, idx;
1030 struct st_plugin_int *plugin, **reap, **list;
1031
1032 mysql_mutex_assert_owner(&LOCK_plugin);
1033
1034 if (!reap_needed)
1035 return;
1036
1037 reap_needed= false;
1038 count= plugin_array.elements;
1039 reap= (struct st_plugin_int **)my_alloca(sizeof(plugin)*(count+1));
1040 *(reap++)= NULL;
1041
1042 for (idx= 0; idx < count; idx++)
1043 {
1044 plugin= *dynamic_element(&plugin_array, idx, struct st_plugin_int **);
1045 if (plugin->state == PLUGIN_IS_DELETED && !plugin->ref_count)
1046 {
1047 /* change the status flag to prevent reaping by another thread */
1048 plugin->state= PLUGIN_IS_DYING;
1049 *(reap++)= plugin;
1050 }
1051 }
1052
1053 mysql_mutex_unlock(&LOCK_plugin);
1054
1055 list= reap;
1056 while ((plugin= *(--list)))
1057 {
1058 if (!opt_bootstrap)
1059 sql_print_information("Shutting down plugin '%s'", plugin->name.str);
1060 plugin_deinitialize(plugin, true);
1061 }
1062
1063 mysql_mutex_lock(&LOCK_plugin_delete);
1064 mysql_mutex_lock(&LOCK_plugin);
1065
1066 while ((plugin= *(--reap)))
1067 plugin_del(plugin);
1068
1069 mysql_mutex_unlock(&LOCK_plugin_delete);
1070
1071 my_afree(reap);
1072 }
1073
intern_plugin_unlock(LEX * lex,plugin_ref plugin)1074 static void intern_plugin_unlock(LEX *lex, plugin_ref plugin)
1075 {
1076 int i;
1077 st_plugin_int *pi;
1078 DBUG_ENTER("intern_plugin_unlock");
1079
1080 mysql_mutex_assert_owner(&LOCK_plugin);
1081
1082 if (!plugin)
1083 DBUG_VOID_RETURN;
1084
1085 pi= plugin_ref_to_int(plugin);
1086
1087 #ifdef DBUG_OFF
1088 if (!pi->plugin_dl)
1089 DBUG_VOID_RETURN;
1090 #else
1091 my_free(plugin);
1092 #endif
1093
1094 DBUG_PRINT("info",("unlocking plugin, name= %s, ref_count= %d",
1095 pi->name.str, pi->ref_count));
1096 if (lex)
1097 {
1098 /*
1099 Remove one instance of this plugin from the use list.
1100 We are searching backwards so that plugins locked last
1101 could be unlocked faster - optimizing for LIFO semantics.
1102 */
1103 for (i= lex->plugins.elements - 1; i >= 0; i--)
1104 if (plugin == *dynamic_element(&lex->plugins, i, plugin_ref*))
1105 {
1106 delete_dynamic_element(&lex->plugins, i);
1107 break;
1108 }
1109 DBUG_ASSERT(i >= 0);
1110 }
1111
1112 DBUG_ASSERT(pi->ref_count);
1113 pi->ref_count--;
1114
1115 if (pi->state == PLUGIN_IS_DELETED && !pi->ref_count)
1116 reap_needed= true;
1117
1118 DBUG_VOID_RETURN;
1119 }
1120
1121
plugin_unlock(THD * thd,plugin_ref plugin)1122 void plugin_unlock(THD *thd, plugin_ref plugin)
1123 {
1124 LEX *lex= thd ? thd->lex : 0;
1125 DBUG_ENTER("plugin_unlock");
1126 if (!plugin)
1127 DBUG_VOID_RETURN;
1128 #ifdef DBUG_OFF
1129 /* built-in plugins don't need ref counting */
1130 if (!plugin_dlib(plugin))
1131 DBUG_VOID_RETURN;
1132 #endif
1133 mysql_mutex_lock(&LOCK_plugin);
1134 intern_plugin_unlock(lex, plugin);
1135 reap_plugins();
1136 mysql_mutex_unlock(&LOCK_plugin);
1137 DBUG_VOID_RETURN;
1138 }
1139
1140
plugin_unlock_list(THD * thd,plugin_ref * list,uint count)1141 void plugin_unlock_list(THD *thd, plugin_ref *list, uint count)
1142 {
1143 LEX *lex= thd ? thd->lex : 0;
1144 DBUG_ENTER("plugin_unlock_list");
1145 DBUG_ASSERT(list);
1146
1147 /*
1148 In unit tests, LOCK_plugin may be uninitialized, so do not lock it.
1149 Besides: there's no point in locking it, if there are no plugins to unlock.
1150 */
1151 if (count == 0)
1152 DBUG_VOID_RETURN;
1153
1154 mysql_mutex_lock(&LOCK_plugin);
1155 while (count--)
1156 intern_plugin_unlock(lex, *list++);
1157 reap_plugins();
1158 mysql_mutex_unlock(&LOCK_plugin);
1159 DBUG_VOID_RETURN;
1160 }
1161
plugin_initialize(struct st_plugin_int * plugin)1162 static int plugin_initialize(struct st_plugin_int *plugin)
1163 {
1164 int ret= 1;
1165 DBUG_ENTER("plugin_initialize");
1166
1167 mysql_mutex_assert_owner(&LOCK_plugin);
1168 uint state= plugin->state;
1169 DBUG_ASSERT(state == PLUGIN_IS_UNINITIALIZED);
1170
1171 mysql_mutex_unlock(&LOCK_plugin);
1172 if (plugin_type_initialize[plugin->plugin->type])
1173 {
1174 if ((*plugin_type_initialize[plugin->plugin->type])(plugin))
1175 {
1176 sql_print_error("Plugin '%s' registration as a %s failed.",
1177 plugin->name.str, plugin_type_names[plugin->plugin->type].str);
1178 goto err;
1179 }
1180
1181 /* FIXME: Need better solution to transfer the callback function
1182 array to memcached */
1183 if (strcmp(plugin->name.str, "InnoDB") == 0) {
1184 innodb_callback_data = ((handlerton*)plugin->data)->data;
1185 }
1186 }
1187 else if (plugin->plugin->init)
1188 {
1189 if (strcmp(plugin->name.str, "daemon_memcached") == 0) {
1190 plugin->data = (void*)innodb_callback_data;
1191 }
1192
1193 if (plugin->plugin->init(plugin))
1194 {
1195 sql_print_error("Plugin '%s' init function returned error.",
1196 plugin->name.str);
1197 goto err;
1198 }
1199 }
1200 state= PLUGIN_IS_READY; // plugin->init() succeeded
1201
1202 if (plugin->plugin->status_vars)
1203 {
1204 #ifdef FIX_LATER
1205 /*
1206 We have a problem right now where we can not prepend without
1207 breaking backwards compatibility. We will fix this shortly so
1208 that engines have "use names" and we wil use those for
1209 CREATE TABLE, and use the plugin name then for adding automatic
1210 variable names.
1211 */
1212 SHOW_VAR array[2]= {
1213 {plugin->plugin->name, (char*)plugin->plugin->status_vars, SHOW_ARRAY},
1214 {0, 0, SHOW_UNDEF}
1215 };
1216 if (add_status_vars(array)) // add_status_vars makes a copy
1217 goto err;
1218 #else
1219 if (add_status_vars(plugin->plugin->status_vars))
1220 goto err;
1221 #endif /* FIX_LATER */
1222 }
1223
1224 /*
1225 set the plugin attribute of plugin's sys vars so they are pointing
1226 to the active plugin
1227 */
1228 if (plugin->system_vars)
1229 {
1230 sys_var_pluginvar *var= plugin->system_vars->cast_pluginvar();
1231 for (;;)
1232 {
1233 var->plugin= plugin;
1234 if (!var->next)
1235 break;
1236 var= var->next->cast_pluginvar();
1237 }
1238 }
1239
1240 ret= 0;
1241
1242 err:
1243 mysql_mutex_lock(&LOCK_plugin);
1244 plugin->state= state;
1245
1246 DBUG_RETURN(ret);
1247 }
1248
1249
1250 extern "C" uchar *get_plugin_hash_key(const uchar *, size_t *, my_bool);
1251 extern "C" uchar *get_bookmark_hash_key(const uchar *, size_t *, my_bool);
1252
1253
get_plugin_hash_key(const uchar * buff,size_t * length,my_bool not_used MY_ATTRIBUTE ((unused)))1254 uchar *get_plugin_hash_key(const uchar *buff, size_t *length,
1255 my_bool not_used MY_ATTRIBUTE((unused)))
1256 {
1257 struct st_plugin_int *plugin= (st_plugin_int *)buff;
1258 *length= (uint)plugin->name.length;
1259 return((uchar *)plugin->name.str);
1260 }
1261
1262
get_bookmark_hash_key(const uchar * buff,size_t * length,my_bool not_used MY_ATTRIBUTE ((unused)))1263 uchar *get_bookmark_hash_key(const uchar *buff, size_t *length,
1264 my_bool not_used MY_ATTRIBUTE((unused)))
1265 {
1266 struct st_bookmark *var= (st_bookmark *)buff;
1267 *length= var->name_len + 1;
1268 return (uchar*) var->key;
1269 }
1270
convert_dash_to_underscore(char * str,int len)1271 static inline void convert_dash_to_underscore(char *str, int len)
1272 {
1273 for (char *p= str; p <= str+len; p++)
1274 if (*p == '-')
1275 *p= '_';
1276 }
1277
convert_underscore_to_dash(char * str,int len)1278 static inline void convert_underscore_to_dash(char *str, int len)
1279 {
1280 for (char *p= str; p <= str+len; p++)
1281 if (*p == '_')
1282 *p= '-';
1283 }
1284
1285 #ifdef HAVE_PSI_INTERFACE
1286 static PSI_mutex_key key_LOCK_plugin;
1287 static PSI_mutex_key key_LOCK_plugin_delete;
1288
1289 static PSI_mutex_info all_plugin_mutexes[]=
1290 {
1291 { &key_LOCK_plugin, "LOCK_plugin", PSI_FLAG_GLOBAL},
1292 { &key_LOCK_plugin_delete, "LOCK_plugin_delete", PSI_FLAG_GLOBAL}
1293 };
1294
init_plugin_psi_keys(void)1295 static void init_plugin_psi_keys(void)
1296 {
1297 const char* category= "sql";
1298 int count;
1299
1300 count= array_elements(all_plugin_mutexes);
1301 mysql_mutex_register(category, all_plugin_mutexes, count);
1302 }
1303 #endif /* HAVE_PSI_INTERFACE */
1304
1305 /*
1306 The logic is that we first load and initialize all compiled in plugins.
1307 From there we load up the dynamic types (assuming we have not been told to
1308 skip this part).
1309
1310 Finally we initialize everything, aka the dynamic that have yet to initialize.
1311 */
plugin_init(int * argc,char ** argv,int flags)1312 int plugin_init(int *argc, char **argv, int flags)
1313 {
1314 uint i;
1315 bool is_myisam;
1316 struct st_mysql_plugin **builtins;
1317 struct st_mysql_plugin *plugin;
1318 struct st_plugin_int tmp, *plugin_ptr, **reap;
1319 MEM_ROOT tmp_root;
1320 bool reaped_mandatory_plugin= false;
1321 bool mandatory= true;
1322 DBUG_ENTER("plugin_init");
1323
1324 if (initialized)
1325 DBUG_RETURN(0);
1326
1327 #ifdef HAVE_PSI_INTERFACE
1328 init_plugin_psi_keys();
1329 #endif
1330
1331 init_alloc_root(&plugin_mem_root, 4096, 4096);
1332 init_alloc_root(&tmp_root, 4096, 4096);
1333
1334 if (my_hash_init(&bookmark_hash, &my_charset_bin, 16, 0, 0,
1335 get_bookmark_hash_key, NULL, HASH_UNIQUE))
1336 goto err;
1337
1338 if (my_hash_init(&malloced_string_type_sysvars_bookmark_hash, &my_charset_bin,
1339 16, 0, 0, get_bookmark_hash_key, NULL, HASH_UNIQUE))
1340 goto err;
1341
1342 mysql_mutex_init(key_LOCK_plugin, &LOCK_plugin, MY_MUTEX_INIT_FAST);
1343 mysql_mutex_init(key_LOCK_plugin_delete, &LOCK_plugin_delete, MY_MUTEX_INIT_FAST);
1344
1345 if (my_init_dynamic_array(&plugin_dl_array,
1346 sizeof(struct st_plugin_dl *),16,16) ||
1347 my_init_dynamic_array(&plugin_array,
1348 sizeof(struct st_plugin_int *),16,16))
1349 goto err;
1350
1351 for (i= 0; i < MYSQL_MAX_PLUGIN_TYPE_NUM; i++)
1352 {
1353 if (my_hash_init(&plugin_hash[i], system_charset_info, 16, 0, 0,
1354 get_plugin_hash_key, NULL, HASH_UNIQUE))
1355 goto err;
1356 }
1357
1358 mysql_mutex_lock(&LOCK_plugin);
1359
1360 initialized= 1;
1361
1362 /*
1363 First we register builtin plugins
1364 */
1365 for (builtins= mysql_mandatory_plugins; *builtins || mandatory; builtins++)
1366 {
1367 if (!*builtins)
1368 {
1369 builtins= mysql_optional_plugins;
1370 mandatory= false;
1371 if (!*builtins)
1372 break;
1373 }
1374 for (plugin= *builtins; plugin->info; plugin++)
1375 {
1376 memset(&tmp, 0, sizeof(tmp));
1377 tmp.plugin= plugin;
1378 tmp.name.str= (char *)plugin->name;
1379 tmp.name.length= strlen(plugin->name);
1380 tmp.state= 0;
1381 tmp.load_option= mandatory ? PLUGIN_FORCE : PLUGIN_ON;
1382
1383 /*
1384 If the performance schema is compiled in,
1385 treat the storage engine plugin as 'mandatory',
1386 to suppress any plugin-level options such as '--performance-schema'.
1387 This is specific to the performance schema, and is done on purpose:
1388 the server-level option '--performance-schema' controls the overall
1389 performance schema initialization, which consists of much more that
1390 the underlying storage engine initialization.
1391 See mysqld.cc, set_vars.cc.
1392 Suppressing ways to interfere directly with the storage engine alone
1393 prevents awkward situations where:
1394 - the user wants the performance schema functionality, by using
1395 '--enable-performance-schema' (the server option),
1396 - yet disable explicitly a component needed for the functionality
1397 to work, by using '--skip-performance-schema' (the plugin)
1398 */
1399 if (!my_strcasecmp(&my_charset_latin1, plugin->name, "PERFORMANCE_SCHEMA"))
1400 tmp.load_option= PLUGIN_FORCE;
1401
1402 free_root(&tmp_root, MYF(MY_MARK_BLOCKS_FREE));
1403 if (test_plugin_options(&tmp_root, &tmp, argc, argv))
1404 tmp.state= PLUGIN_IS_DISABLED;
1405 else
1406 tmp.state= PLUGIN_IS_UNINITIALIZED;
1407 if (register_builtin(plugin, &tmp, &plugin_ptr))
1408 goto err_unlock;
1409
1410 /* only initialize MyISAM and CSV at this stage */
1411 if (!(is_myisam=
1412 !my_strcasecmp(&my_charset_latin1, plugin->name, "MyISAM")) &&
1413 my_strcasecmp(&my_charset_latin1, plugin->name, "CSV"))
1414 continue;
1415
1416 if (plugin_ptr->state != PLUGIN_IS_UNINITIALIZED ||
1417 plugin_initialize(plugin_ptr))
1418 goto err_unlock;
1419
1420 /*
1421 initialize the global default storage engine so that it may
1422 not be null in any child thread.
1423 */
1424 if (is_myisam)
1425 {
1426 DBUG_ASSERT(!global_system_variables.table_plugin);
1427 DBUG_ASSERT(!global_system_variables.temp_table_plugin);
1428 global_system_variables.table_plugin=
1429 my_intern_plugin_lock(NULL, plugin_int_to_ref(plugin_ptr));
1430 global_system_variables.temp_table_plugin=
1431 my_intern_plugin_lock(NULL, plugin_int_to_ref(plugin_ptr));
1432 DBUG_ASSERT(plugin_ptr->ref_count == 2);
1433 }
1434 }
1435 }
1436
1437 /* should now be set to MyISAM storage engine */
1438 DBUG_ASSERT(global_system_variables.table_plugin);
1439 DBUG_ASSERT(global_system_variables.temp_table_plugin);
1440
1441 mysql_mutex_unlock(&LOCK_plugin);
1442
1443 /* Register all dynamic plugins */
1444 if (!(flags & PLUGIN_INIT_SKIP_DYNAMIC_LOADING))
1445 {
1446 I_List_iterator<i_string> iter(opt_plugin_load_list);
1447 i_string *item;
1448 while (NULL != (item= iter++))
1449 plugin_load_list(&tmp_root, argc, argv, item->ptr);
1450
1451 if (!(flags & PLUGIN_INIT_SKIP_PLUGIN_TABLE))
1452 plugin_load(&tmp_root, argc, argv);
1453 }
1454
1455 if (flags & PLUGIN_INIT_SKIP_INITIALIZATION)
1456 goto end;
1457
1458 /*
1459 Now we initialize all remaining plugins
1460 */
1461
1462 mysql_mutex_lock(&LOCK_plugin);
1463 reap= (st_plugin_int **) my_alloca((plugin_array.elements+1) * sizeof(void*));
1464 *(reap++)= NULL;
1465
1466 for (i= 0; i < plugin_array.elements; i++)
1467 {
1468 plugin_ptr= *dynamic_element(&plugin_array, i, struct st_plugin_int **);
1469 if (plugin_ptr->state == PLUGIN_IS_UNINITIALIZED)
1470 {
1471 if (plugin_initialize(plugin_ptr))
1472 {
1473 plugin_ptr->state= PLUGIN_IS_DYING;
1474 *(reap++)= plugin_ptr;
1475 }
1476 }
1477 }
1478
1479 /*
1480 Check if any plugins have to be reaped
1481 */
1482 while ((plugin_ptr= *(--reap)))
1483 {
1484 mysql_mutex_unlock(&LOCK_plugin);
1485 if (plugin_ptr->load_option == PLUGIN_FORCE ||
1486 plugin_ptr->load_option == PLUGIN_FORCE_PLUS_PERMANENT)
1487 reaped_mandatory_plugin= TRUE;
1488 plugin_deinitialize(plugin_ptr, true);
1489 mysql_mutex_lock(&LOCK_plugin_delete);
1490 mysql_mutex_lock(&LOCK_plugin);
1491 plugin_del(plugin_ptr);
1492 mysql_mutex_unlock(&LOCK_plugin_delete);
1493 }
1494
1495 mysql_mutex_unlock(&LOCK_plugin);
1496 my_afree(reap);
1497 if (reaped_mandatory_plugin)
1498 goto err;
1499
1500 end:
1501 free_root(&tmp_root, MYF(0));
1502
1503 DBUG_RETURN(0);
1504
1505 err_unlock:
1506 mysql_mutex_unlock(&LOCK_plugin);
1507 err:
1508 free_root(&tmp_root, MYF(0));
1509 DBUG_RETURN(1);
1510 }
1511
1512
register_builtin(struct st_mysql_plugin * plugin,struct st_plugin_int * tmp,struct st_plugin_int ** ptr)1513 static bool register_builtin(struct st_mysql_plugin *plugin,
1514 struct st_plugin_int *tmp,
1515 struct st_plugin_int **ptr)
1516 {
1517 DBUG_ENTER("register_builtin");
1518 tmp->ref_count= 0;
1519 tmp->plugin_dl= 0;
1520
1521 if (insert_dynamic(&plugin_array, &tmp))
1522 DBUG_RETURN(1);
1523
1524 *ptr= *dynamic_element(&plugin_array, plugin_array.elements - 1,
1525 struct st_plugin_int **)=
1526 (struct st_plugin_int *) memdup_root(&plugin_mem_root, (uchar*)tmp,
1527 sizeof(struct st_plugin_int));
1528
1529 if (my_hash_insert(&plugin_hash[plugin->type],(uchar*) *ptr))
1530 DBUG_RETURN(1);
1531
1532 DBUG_RETURN(0);
1533 }
1534
1535
1536 /*
1537 called only by plugin_init()
1538 */
plugin_load(MEM_ROOT * tmp_root,int * argc,char ** argv)1539 static void plugin_load(MEM_ROOT *tmp_root, int *argc, char **argv)
1540 {
1541 THD thd;
1542 TABLE_LIST tables;
1543 TABLE *table;
1544 READ_RECORD read_record_info;
1545 int error;
1546 THD *new_thd= &thd;
1547 bool result;
1548 #ifdef EMBEDDED_LIBRARY
1549 No_such_table_error_handler error_handler;
1550 #endif /* EMBEDDED_LIBRARY */
1551 DBUG_ENTER("plugin_load");
1552
1553 new_thd->thread_stack= (char*) &tables;
1554 new_thd->store_globals();
1555 new_thd->db= my_strdup("mysql", MYF(0));
1556 new_thd->db_length= 5;
1557 memset(&thd.net, 0, sizeof(thd.net));
1558 tables.init_one_table("mysql", 5, "plugin", 6, "plugin", TL_READ);
1559
1560 #ifdef EMBEDDED_LIBRARY
1561 /*
1562 When building an embedded library, if the mysql.plugin table
1563 does not exist, we silently ignore the missing table
1564 */
1565 new_thd->push_internal_handler(&error_handler);
1566 #endif /* EMBEDDED_LIBRARY */
1567
1568 result= open_and_lock_tables(new_thd, &tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT);
1569
1570 #ifdef EMBEDDED_LIBRARY
1571 new_thd->pop_internal_handler();
1572 if (error_handler.safely_trapped_errors())
1573 goto end;
1574 #endif /* EMBEDDED_LIBRARY */
1575
1576 if (result)
1577 {
1578 DBUG_PRINT("error",("Can't open plugin table"));
1579 sql_print_error("Can't open the mysql.plugin table. Please "
1580 "run mysql_upgrade to create it.");
1581 goto end;
1582 }
1583 table= tables.table;
1584 if (init_read_record(&read_record_info, new_thd, table, NULL, 1, 1, FALSE))
1585 goto end;
1586 table->use_all_columns();
1587 /*
1588 there're no other threads running yet, so we don't need a mutex.
1589 but plugin_add() before is designed to work in multi-threaded
1590 environment, and it uses mysql_mutex_assert_owner(), so we lock
1591 the mutex here to satisfy the assert
1592 */
1593 mysql_mutex_lock(&LOCK_plugin);
1594 while (!(error= read_record_info.read_record(&read_record_info)))
1595 {
1596 DBUG_PRINT("info", ("init plugin record"));
1597 String str_name, str_dl;
1598 get_field(tmp_root, table->field[0], &str_name);
1599 get_field(tmp_root, table->field[1], &str_dl);
1600
1601 LEX_STRING name= {(char *)str_name.ptr(), str_name.length()};
1602 LEX_STRING dl= {(char *)str_dl.ptr(), str_dl.length()};
1603
1604 if (plugin_add(tmp_root, &name, &dl, argc, argv, REPORT_TO_LOG, false))
1605 sql_print_warning("Couldn't load plugin named '%s' with soname '%s'.",
1606 str_name.c_ptr(), str_dl.c_ptr());
1607 free_root(tmp_root, MYF(MY_MARK_BLOCKS_FREE));
1608 }
1609 mysql_mutex_unlock(&LOCK_plugin);
1610 if (error > 0)
1611 sql_print_error(ER(ER_GET_ERRNO), my_errno);
1612 end_read_record(&read_record_info);
1613 table->m_needs_reopen= TRUE; // Force close to free memory
1614 close_mysql_tables(new_thd);
1615 end:
1616 /* Remember that we don't have a THD */
1617 my_pthread_setspecific_ptr(THR_THD, 0);
1618 DBUG_VOID_RETURN;
1619 }
1620
1621
1622 /*
1623 called only by plugin_init()
1624 */
plugin_load_list(MEM_ROOT * tmp_root,int * argc,char ** argv,const char * list)1625 static bool plugin_load_list(MEM_ROOT *tmp_root, int *argc, char **argv,
1626 const char *list)
1627 {
1628 char buffer[FN_REFLEN];
1629 LEX_STRING name= {buffer, 0}, dl= {NULL, 0}, *str= &name;
1630 struct st_plugin_dl *plugin_dl;
1631 struct st_mysql_plugin *plugin;
1632 char *p= buffer;
1633 DBUG_ENTER("plugin_load_list");
1634 while (list)
1635 {
1636 if (p == buffer + sizeof(buffer) - 1)
1637 {
1638 sql_print_error("plugin-load parameter too long");
1639 DBUG_RETURN(TRUE);
1640 }
1641
1642 switch ((*(p++)= *(list++))) {
1643 case '\0':
1644 list= NULL; /* terminate the loop */
1645 // fallthrough - the later 2 comments are required by GCC
1646 #ifndef __WIN__
1647 // fallthrough
1648 case ':': /* can't use this as delimiter as it may be drive letter */
1649 #endif
1650 // fallthrough
1651 case ';':
1652 str->str[str->length]= '\0';
1653 if (str == &name) // load all plugins in named module
1654 {
1655 if (!name.length)
1656 {
1657 p--; /* reset pointer */
1658 continue;
1659 }
1660
1661 dl= name;
1662 mysql_mutex_lock(&LOCK_plugin);
1663 if ((plugin_dl= plugin_dl_add(&dl, REPORT_TO_LOG, true)))
1664 {
1665 for (plugin= plugin_dl->plugins; plugin->info; plugin++)
1666 {
1667 name.str= (char *) plugin->name;
1668 name.length= strlen(name.str);
1669
1670 free_root(tmp_root, MYF(MY_MARK_BLOCKS_FREE));
1671 if (plugin_add(tmp_root,
1672 &name,
1673 &dl,
1674 argc,
1675 argv,
1676 REPORT_TO_LOG,
1677 true))
1678 goto error;
1679 }
1680 plugin_dl_del(&dl); // reduce ref count
1681 }
1682 }
1683 else
1684 {
1685 free_root(tmp_root, MYF(MY_MARK_BLOCKS_FREE));
1686 mysql_mutex_lock(&LOCK_plugin);
1687 if (plugin_add(tmp_root, &name, &dl, argc, argv, REPORT_TO_LOG, true))
1688 goto error;
1689 }
1690 mysql_mutex_unlock(&LOCK_plugin);
1691 name.length= dl.length= 0;
1692 dl.str= NULL; name.str= p= buffer;
1693 str= &name;
1694 continue;
1695 case '=':
1696 case '#':
1697 if (str == &name)
1698 {
1699 name.str[name.length]= '\0';
1700 str= &dl;
1701 str->str= p;
1702 continue;
1703 }
1704 // fallthrough
1705 default:
1706 str->length++;
1707 continue;
1708 }
1709 }
1710 DBUG_RETURN(FALSE);
1711 error:
1712 mysql_mutex_unlock(&LOCK_plugin);
1713 sql_print_error("Couldn't load plugin named '%s' with soname '%s'.",
1714 name.str, dl.str);
1715 DBUG_RETURN(TRUE);
1716 }
1717
1718 /*
1719 Shutdown memcached plugin before binlog shuts down
1720 */
memcached_shutdown(void)1721 void memcached_shutdown(void)
1722 {
1723 struct st_plugin_int *plugin;
1724 if (initialized)
1725 {
1726
1727 for (uint i= 0; i < plugin_array.elements; i++)
1728 {
1729 plugin= *dynamic_element(&plugin_array, i, struct st_plugin_int **);
1730
1731 if (plugin->state == PLUGIN_IS_READY
1732 && strcmp(plugin->name.str, "daemon_memcached") == 0)
1733 {
1734 plugin_deinitialize(plugin, true);
1735
1736 mysql_mutex_lock(&LOCK_plugin_delete);
1737 mysql_mutex_lock(&LOCK_plugin);
1738 plugin->state= PLUGIN_IS_DYING;
1739 plugin_del(plugin);
1740 mysql_mutex_unlock(&LOCK_plugin);
1741 mysql_mutex_unlock(&LOCK_plugin_delete);
1742 }
1743 }
1744
1745 }
1746 }
1747
plugin_shutdown(void)1748 void plugin_shutdown(void)
1749 {
1750 uint i, count= plugin_array.elements;
1751 struct st_plugin_int **plugins, *plugin;
1752 struct st_plugin_dl **dl;
1753 bool skip_binlog = true;
1754
1755 DBUG_ENTER("plugin_shutdown");
1756
1757 if (initialized)
1758 {
1759 mysql_mutex_lock(&LOCK_plugin);
1760
1761 reap_needed= true;
1762
1763 /*
1764 We want to shut down plugins in a reasonable order, this will
1765 become important when we have plugins which depend upon each other.
1766 Circular references cannot be reaped so they are forced afterwards.
1767 TODO: Have an additional step here to notify all active plugins that
1768 shutdown is requested to allow plugins to deinitialize in parallel.
1769 */
1770 while (reap_needed && (count= plugin_array.elements))
1771 {
1772 reap_plugins();
1773 for (i= 0; i < count; i++)
1774 {
1775 plugin= *dynamic_element(&plugin_array, i, struct st_plugin_int **);
1776
1777 if (plugin->state == PLUGIN_IS_READY
1778 && strcmp(plugin->name.str, "binlog") == 0 && skip_binlog)
1779 {
1780 skip_binlog = false;
1781
1782 } else if (plugin->state == PLUGIN_IS_READY)
1783 {
1784 plugin->state= PLUGIN_IS_DELETED;
1785 reap_needed= true;
1786 }
1787 }
1788 if (!reap_needed)
1789 {
1790 /*
1791 release any plugin references held.
1792 */
1793 unlock_variables(NULL, &global_system_variables);
1794 unlock_variables(NULL, &max_system_variables);
1795 }
1796 }
1797
1798 plugins= (struct st_plugin_int **) my_alloca(sizeof(void*) * (count+1));
1799
1800 /*
1801 If we have any plugins which did not die cleanly, we force shutdown
1802 */
1803 for (i= 0; i < count; i++)
1804 {
1805 plugins[i]= *dynamic_element(&plugin_array, i, struct st_plugin_int **);
1806 /* change the state to ensure no reaping races */
1807 if (plugins[i]->state == PLUGIN_IS_DELETED)
1808 plugins[i]->state= PLUGIN_IS_DYING;
1809 }
1810 mysql_mutex_unlock(&LOCK_plugin);
1811
1812 /*
1813 We loop through all plugins and call deinit() if they have one.
1814 */
1815 for (i= 0; i < count; i++)
1816 if (!(plugins[i]->state & (PLUGIN_IS_UNINITIALIZED | PLUGIN_IS_FREED |
1817 PLUGIN_IS_DISABLED)))
1818 {
1819 sql_print_warning("Plugin '%s' will be forced to shutdown",
1820 plugins[i]->name.str);
1821 /*
1822 We are forcing deinit on plugins so we don't want to do a ref_count
1823 check until we have processed all the plugins.
1824 */
1825 plugin_deinitialize(plugins[i], false);
1826 }
1827
1828 /*
1829 It's perfectly safe not to lock LOCK_plugin, LOCK_plugin_delete, as
1830 there're no concurrent threads anymore. But some functions called from
1831 here use mysql_mutex_assert_owner(), so we lock the mutex to satisfy it
1832 */
1833 mysql_mutex_lock(&LOCK_plugin_delete);
1834 mysql_mutex_lock(&LOCK_plugin);
1835
1836 /*
1837 We defer checking ref_counts until after all plugins are deinitialized
1838 as some may have worker threads holding on to plugin references.
1839 */
1840 for (i= 0; i < count; i++)
1841 {
1842 if (plugins[i]->ref_count)
1843 sql_print_error("Plugin '%s' has ref_count=%d after shutdown.",
1844 plugins[i]->name.str, plugins[i]->ref_count);
1845 if (plugins[i]->state & PLUGIN_IS_UNINITIALIZED)
1846 plugin_del(plugins[i]);
1847 }
1848
1849 /*
1850 Now we can deallocate all memory.
1851 */
1852
1853 cleanup_variables(NULL, &global_system_variables);
1854 cleanup_variables(NULL, &max_system_variables);
1855 mysql_mutex_unlock(&LOCK_plugin);
1856 mysql_mutex_unlock(&LOCK_plugin_delete);
1857
1858 initialized= 0;
1859 mysql_mutex_destroy(&LOCK_plugin);
1860 mysql_mutex_destroy(&LOCK_plugin_delete);
1861
1862 my_afree(plugins);
1863 }
1864
1865 /* Dispose of the memory */
1866
1867 for (i= 0; i < MYSQL_MAX_PLUGIN_TYPE_NUM; i++)
1868 my_hash_free(&plugin_hash[i]);
1869 delete_dynamic(&plugin_array);
1870
1871 count= plugin_dl_array.elements;
1872 dl= (struct st_plugin_dl **)my_alloca(sizeof(void*) * count);
1873 for (i= 0; i < count; i++)
1874 dl[i]= *dynamic_element(&plugin_dl_array, i, struct st_plugin_dl **);
1875 for (i= 0; i < plugin_dl_array.elements; i++)
1876 free_plugin_mem(dl[i]);
1877 my_afree(dl);
1878 delete_dynamic(&plugin_dl_array);
1879
1880 my_hash_free(&bookmark_hash);
1881 my_hash_free(&malloced_string_type_sysvars_bookmark_hash);
1882 free_root(&plugin_mem_root, MYF(0));
1883
1884 global_variables_dynamic_size= 0;
1885
1886 DBUG_VOID_RETURN;
1887 }
1888
1889
mysql_install_plugin(THD * thd,const LEX_STRING * name,const LEX_STRING * dl)1890 bool mysql_install_plugin(THD *thd, const LEX_STRING *name, const LEX_STRING *dl)
1891 {
1892 TABLE_LIST tables;
1893 TABLE *table;
1894 int error, argc=orig_argc;
1895 char **argv=orig_argv;
1896 struct st_plugin_int *tmp;
1897 DBUG_ENTER("mysql_install_plugin");
1898
1899 if (opt_noacl)
1900 {
1901 my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
1902 DBUG_RETURN(TRUE);
1903 }
1904
1905 tables.init_one_table("mysql", 5, "plugin", 6, "plugin", TL_WRITE);
1906 if (check_table_access(thd, INSERT_ACL, &tables, FALSE, 1, FALSE))
1907 DBUG_RETURN(TRUE);
1908
1909 /* need to open before acquiring LOCK_plugin or it will deadlock */
1910 if (! (table = open_ltable(thd, &tables, TL_WRITE,
1911 MYSQL_LOCK_IGNORE_TIMEOUT)))
1912 DBUG_RETURN(TRUE);
1913
1914 /*
1915 Pre-acquire audit plugins for events that may potentially occur
1916 during [UN]INSTALL PLUGIN.
1917
1918 When audit event is triggered, audit subsystem acquires interested
1919 plugins by walking through plugin list. Evidently plugin list
1920 iterator protects plugin list by acquiring LOCK_plugin, see
1921 plugin_foreach_with_mask().
1922
1923 On the other hand [UN]INSTALL PLUGIN is acquiring LOCK_plugin
1924 rather for a long time.
1925
1926 When audit event is triggered during [UN]INSTALL PLUGIN, plugin
1927 list iterator acquires the same lock (within the same thread)
1928 second time.
1929
1930 This hack should be removed when LOCK_plugin is fixed so it
1931 protects only what it supposed to protect.
1932 */
1933 mysql_audit_acquire_plugins(thd, MYSQL_AUDIT_GENERAL_CLASS);
1934
1935 mysql_mutex_lock(&LOCK_plugin);
1936 DEBUG_SYNC(thd, "acquired_LOCK_plugin");
1937 mysql_rwlock_wrlock(&LOCK_system_variables_hash);
1938
1939 if (my_load_defaults(MYSQL_CONFIG_NAME, load_default_groups, &argc, &argv, NULL))
1940 {
1941 mysql_rwlock_unlock(&LOCK_system_variables_hash);
1942 report_error(REPORT_TO_USER, ER_PLUGIN_IS_NOT_LOADED, name->str);
1943 goto err;
1944 }
1945 error= plugin_add(thd->mem_root,
1946 name,
1947 dl,
1948 &argc,
1949 argv,
1950 REPORT_TO_USER,
1951 false);
1952 if (argv)
1953 free_defaults(argv);
1954 mysql_rwlock_unlock(&LOCK_system_variables_hash);
1955
1956 if (error || !(tmp= plugin_find_internal(name, MYSQL_ANY_PLUGIN)))
1957 goto err;
1958
1959 if (tmp->state == PLUGIN_IS_DISABLED)
1960 {
1961 push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
1962 ER_CANT_INITIALIZE_UDF, ER(ER_CANT_INITIALIZE_UDF),
1963 name->str, "Plugin is disabled");
1964 }
1965 else
1966 {
1967 if (plugin_initialize(tmp))
1968 {
1969 mysql_mutex_unlock(&LOCK_plugin);
1970 my_error(ER_CANT_INITIALIZE_UDF, MYF(0), name->str,
1971 "Plugin initialization function failed.");
1972 goto deinit;
1973 }
1974 }
1975
1976 /*
1977 We do not replicate the INSTALL PLUGIN statement. Disable binlogging
1978 of the insert into the plugin table, so that it is not replicated in
1979 row based mode.
1980 */
1981 mysql_mutex_unlock(&LOCK_plugin);
1982 tmp_disable_binlog(thd);
1983 table->use_all_columns();
1984 restore_record(table, s->default_values);
1985 table->field[0]->store(name->str, name->length, system_charset_info);
1986 table->field[1]->store(dl->str, dl->length, files_charset_info);
1987 error= table->file->ha_write_row(table->record[0]);
1988 reenable_binlog(thd);
1989 if (error)
1990 {
1991 table->file->print_error(error, MYF(0));
1992 goto deinit;
1993 }
1994 DBUG_RETURN(FALSE);
1995 deinit:
1996 mysql_mutex_lock(&LOCK_plugin);
1997 tmp->state= PLUGIN_IS_DELETED;
1998 reap_needed= true;
1999 reap_plugins();
2000 err:
2001 mysql_mutex_unlock(&LOCK_plugin);
2002 DBUG_RETURN(TRUE);
2003 }
2004
2005
mysql_uninstall_plugin(THD * thd,const LEX_STRING * name)2006 bool mysql_uninstall_plugin(THD *thd, const LEX_STRING *name)
2007 {
2008 TABLE *table;
2009 TABLE_LIST tables;
2010 struct st_plugin_int *plugin;
2011 DBUG_ENTER("mysql_uninstall_plugin");
2012
2013 if (opt_noacl)
2014 {
2015 my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
2016 DBUG_RETURN(TRUE);
2017 }
2018
2019 tables.init_one_table("mysql", 5, "plugin", 6, "plugin", TL_WRITE);
2020
2021 if (check_table_access(thd, DELETE_ACL, &tables, FALSE, 1, FALSE))
2022 DBUG_RETURN(TRUE);
2023
2024 /* need to open before acquiring LOCK_plugin or it will deadlock */
2025 if (! (table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT)))
2026 DBUG_RETURN(TRUE);
2027
2028 if (!table->key_info)
2029 {
2030 my_error(ER_MISSING_KEY, MYF(0), table->s->db.str,
2031 table->s->table_name.str);
2032 DBUG_RETURN(TRUE);
2033 }
2034
2035 /*
2036 Pre-acquire audit plugins for events that may potentially occur
2037 during [UN]INSTALL PLUGIN.
2038
2039 When audit event is triggered, audit subsystem acquires interested
2040 plugins by walking through plugin list. Evidently plugin list
2041 iterator protects plugin list by acquiring LOCK_plugin, see
2042 plugin_foreach_with_mask().
2043
2044 On the other hand [UN]INSTALL PLUGIN is acquiring LOCK_plugin
2045 rather for a long time.
2046
2047 When audit event is triggered during [UN]INSTALL PLUGIN, plugin
2048 list iterator acquires the same lock (within the same thread)
2049 second time.
2050
2051 This hack should be removed when LOCK_plugin is fixed so it
2052 protects only what it supposed to protect.
2053 */
2054 mysql_audit_acquire_plugins(thd, MYSQL_AUDIT_GENERAL_CLASS);
2055
2056 mysql_mutex_lock(&LOCK_plugin);
2057 if (!(plugin= plugin_find_internal(name, MYSQL_ANY_PLUGIN)) ||
2058 plugin->state & (PLUGIN_IS_UNINITIALIZED | PLUGIN_IS_DYING))
2059 {
2060 my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "PLUGIN", name->str);
2061 goto err;
2062 }
2063 if (!plugin->plugin_dl)
2064 {
2065 push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
2066 WARN_PLUGIN_DELETE_BUILTIN, ER(WARN_PLUGIN_DELETE_BUILTIN));
2067 my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "PLUGIN", name->str);
2068 goto err;
2069 }
2070 if (plugin->load_option == PLUGIN_FORCE_PLUS_PERMANENT)
2071 {
2072 my_error(ER_PLUGIN_IS_PERMANENT, MYF(0), name->str);
2073 goto err;
2074 }
2075 /*
2076 Error message for ER_PLUGIN_IS_PERMANENT is not suitable for
2077 plugins marked as not dynamically uninstallable, so we have a
2078 separate one instead of changing the old one.
2079 */
2080 if (plugin->plugin->flags & PLUGIN_OPT_NO_UNINSTALL)
2081 {
2082 my_error(ER_PLUGIN_NO_UNINSTALL, MYF(0), plugin->plugin->name);
2083 goto err;
2084 }
2085
2086 #ifdef HAVE_REPLICATION
2087 /* Block Uninstallation of semi_sync plugins (Master/Slave)
2088 when they are busy
2089 */
2090 char buff[20];
2091 /*
2092 Master: If there are active semi sync slaves for this Master,
2093 then that means it is busy and rpl_semi_sync_master plugin
2094 cannot be uninstalled. To check whether the master
2095 has any semi sync slaves or not, check Rpl_semi_sync_master_cliens
2096 status variable value, if it is not 0, that means it is busy.
2097 */
2098 if (!strcmp(name->str, "rpl_semi_sync_master") &&
2099 get_status_var(thd,
2100 plugin->plugin->status_vars,
2101 "Rpl_semi_sync_master_clients",buff) &&
2102 strcmp(buff,"0") )
2103 {
2104 my_error(ER_PLUGIN_CANNOT_BE_UNINSTALLED, MYF(0), name->str,
2105 "Stop any active semisynchronous slaves of this master first.");
2106 goto err;
2107 }
2108 /* Slave: If there is semi sync enabled IO thread active on this Slave,
2109 then that means plugin is busy and rpl_semi_sync_slave plugin
2110 cannot be uninstalled. To check whether semi sync
2111 IO thread is active or not, check Rpl_semi_sync_slave_status status
2112 variable value, if it is ON, that means it is busy.
2113 */
2114 if (!strcmp(name->str, "rpl_semi_sync_slave") &&
2115 get_status_var(thd, plugin->plugin->status_vars,
2116 "Rpl_semi_sync_slave_status", buff) &&
2117 !strcmp(buff,"ON") )
2118 {
2119 my_error(ER_PLUGIN_CANNOT_BE_UNINSTALLED, MYF(0), name->str,
2120 "Stop any active semisynchronous I/O threads on this slave first.");
2121 goto err;
2122 }
2123 #endif
2124
2125 plugin->state= PLUGIN_IS_DELETED;
2126 if (plugin->ref_count)
2127 push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
2128 WARN_PLUGIN_BUSY, ER(WARN_PLUGIN_BUSY));
2129 else
2130 reap_needed= true;
2131 reap_plugins();
2132 mysql_mutex_unlock(&LOCK_plugin);
2133
2134 uchar user_key[MAX_KEY_LENGTH];
2135 table->use_all_columns();
2136 table->field[0]->store(name->str, name->length, system_charset_info);
2137 key_copy(user_key, table->record[0], table->key_info,
2138 table->key_info->key_length);
2139 if (! table->file->ha_index_read_idx_map(table->record[0], 0, user_key,
2140 HA_WHOLE_KEY, HA_READ_KEY_EXACT))
2141 {
2142 int error;
2143 /*
2144 We do not replicate the UNINSTALL PLUGIN statement. Disable binlogging
2145 of the delete from the plugin table, so that it is not replicated in
2146 row based mode.
2147 */
2148 tmp_disable_binlog(thd);
2149 error= table->file->ha_delete_row(table->record[0]);
2150 reenable_binlog(thd);
2151 if (error)
2152 {
2153 table->file->print_error(error, MYF(0));
2154 DBUG_RETURN(TRUE);
2155 }
2156 }
2157 DBUG_RETURN(FALSE);
2158 err:
2159 mysql_mutex_unlock(&LOCK_plugin);
2160 DBUG_RETURN(TRUE);
2161 }
2162
2163
plugin_foreach_with_mask(THD * thd,plugin_foreach_func * func,int type,uint state_mask,void * arg)2164 bool plugin_foreach_with_mask(THD *thd, plugin_foreach_func *func,
2165 int type, uint state_mask, void *arg)
2166 {
2167 uint idx, total;
2168 struct st_plugin_int *plugin, **plugins;
2169 int version=plugin_array_version;
2170 DBUG_ENTER("plugin_foreach_with_mask");
2171
2172 if (!initialized)
2173 DBUG_RETURN(FALSE);
2174
2175 state_mask= ~state_mask; // do it only once
2176
2177 mysql_mutex_lock(&LOCK_plugin);
2178 total= type == MYSQL_ANY_PLUGIN ? plugin_array.elements
2179 : plugin_hash[type].records;
2180 /*
2181 Do the alloca out here in case we do have a working alloca:
2182 leaving the nested stack frame invalidates alloca allocation.
2183 */
2184 plugins=(struct st_plugin_int **)my_alloca(total*sizeof(plugin));
2185 if (type == MYSQL_ANY_PLUGIN)
2186 {
2187 for (idx= 0; idx < total; idx++)
2188 {
2189 plugin= *dynamic_element(&plugin_array, idx, struct st_plugin_int **);
2190 plugins[idx]= !(plugin->state & state_mask) ? plugin : NULL;
2191 }
2192 }
2193 else
2194 {
2195 HASH *hash= plugin_hash + type;
2196 for (idx= 0; idx < total; idx++)
2197 {
2198 plugin= (struct st_plugin_int *) my_hash_element(hash, idx);
2199 plugins[idx]= !(plugin->state & state_mask) ? plugin : NULL;
2200 }
2201 }
2202 mysql_mutex_unlock(&LOCK_plugin);
2203
2204 for (idx= 0; idx < total; idx++)
2205 {
2206 if (unlikely(version != plugin_array_version))
2207 {
2208 mysql_mutex_lock(&LOCK_plugin);
2209 for (uint i=idx; i < total; i++)
2210 if (plugins[i] && plugins[i]->state & state_mask)
2211 plugins[i]=0;
2212 mysql_mutex_unlock(&LOCK_plugin);
2213 }
2214 plugin= plugins[idx];
2215 /* It will stop iterating on first engine error when "func" returns TRUE */
2216 if (plugin && func(thd, plugin_int_to_ref(plugin), arg))
2217 goto err;
2218 }
2219
2220 my_afree(plugins);
2221 DBUG_RETURN(FALSE);
2222 err:
2223 my_afree(plugins);
2224 DBUG_RETURN(TRUE);
2225 }
2226
2227
2228 /****************************************************************************
2229 Internal type declarations for variables support
2230 ****************************************************************************/
2231
2232 #undef MYSQL_SYSVAR_NAME
2233 #define MYSQL_SYSVAR_NAME(name) name
2234 #define PLUGIN_VAR_TYPEMASK 0x007f
2235
2236 #define EXTRA_OPTIONS 3 /* options for: 'foo', 'plugin-foo' and NULL */
2237
2238 typedef DECLARE_MYSQL_SYSVAR_BASIC(sysvar_bool_t, my_bool);
2239 typedef DECLARE_MYSQL_THDVAR_BASIC(thdvar_bool_t, my_bool);
2240 typedef DECLARE_MYSQL_SYSVAR_BASIC(sysvar_str_t, char *);
2241 typedef DECLARE_MYSQL_THDVAR_BASIC(thdvar_str_t, char *);
2242
2243 typedef DECLARE_MYSQL_SYSVAR_TYPELIB(sysvar_enum_t, unsigned long);
2244 typedef DECLARE_MYSQL_THDVAR_TYPELIB(thdvar_enum_t, unsigned long);
2245 typedef DECLARE_MYSQL_SYSVAR_TYPELIB(sysvar_set_t, ulonglong);
2246 typedef DECLARE_MYSQL_THDVAR_TYPELIB(thdvar_set_t, ulonglong);
2247
2248 typedef DECLARE_MYSQL_SYSVAR_SIMPLE(sysvar_int_t, int);
2249 typedef DECLARE_MYSQL_SYSVAR_SIMPLE(sysvar_long_t, long);
2250 typedef DECLARE_MYSQL_SYSVAR_SIMPLE(sysvar_longlong_t, longlong);
2251 typedef DECLARE_MYSQL_SYSVAR_SIMPLE(sysvar_uint_t, uint);
2252 typedef DECLARE_MYSQL_SYSVAR_SIMPLE(sysvar_ulong_t, ulong);
2253 typedef DECLARE_MYSQL_SYSVAR_SIMPLE(sysvar_ulonglong_t, ulonglong);
2254 typedef DECLARE_MYSQL_SYSVAR_SIMPLE(sysvar_double_t, double);
2255
2256 typedef DECLARE_MYSQL_THDVAR_SIMPLE(thdvar_int_t, int);
2257 typedef DECLARE_MYSQL_THDVAR_SIMPLE(thdvar_long_t, long);
2258 typedef DECLARE_MYSQL_THDVAR_SIMPLE(thdvar_longlong_t, longlong);
2259 typedef DECLARE_MYSQL_THDVAR_SIMPLE(thdvar_uint_t, uint);
2260 typedef DECLARE_MYSQL_THDVAR_SIMPLE(thdvar_ulong_t, ulong);
2261 typedef DECLARE_MYSQL_THDVAR_SIMPLE(thdvar_ulonglong_t, ulonglong);
2262 typedef DECLARE_MYSQL_THDVAR_SIMPLE(thdvar_double_t, double);
2263
2264
2265 /****************************************************************************
2266 default variable data check and update functions
2267 ****************************************************************************/
2268
check_func_bool(THD * thd,struct st_mysql_sys_var * var,void * save,st_mysql_value * value)2269 static int check_func_bool(THD *thd, struct st_mysql_sys_var *var,
2270 void *save, st_mysql_value *value)
2271 {
2272 char buff[STRING_BUFFER_USUAL_SIZE];
2273 const char *str;
2274 int result, length;
2275 long long tmp;
2276
2277 if (value->value_type(value) == MYSQL_VALUE_TYPE_STRING)
2278 {
2279 length= sizeof(buff);
2280 if (!(str= value->val_str(value, buff, &length)) ||
2281 (result= find_type(&bool_typelib, str, length, 1)-1) < 0)
2282 goto err;
2283 }
2284 else
2285 {
2286 if (value->val_int(value, &tmp) < 0)
2287 goto err;
2288 if (tmp > 1)
2289 goto err;
2290 result= (int) tmp;
2291 }
2292 *(my_bool *) save= result ? TRUE : FALSE;
2293 return 0;
2294 err:
2295 return 1;
2296 }
2297
2298 #define CHECK_FUNC_RANGE(cfr_ctype, cfr_vmin, cfr_vmax, cfr_save, cfr_res) \
2299 do { \
2300 if (cfr_vmin && *(cfr_ctype *) cfr_save < *(cfr_ctype *) cfr_vmin) { \
2301 *(cfr_ctype *)cfr_save= *(cfr_ctype *) cfr_vmin; \
2302 cfr_res= TRUE; \
2303 } \
2304 if (cfr_vmax && *(cfr_ctype *) cfr_save > *(cfr_ctype *) cfr_vmax) { \
2305 *(cfr_ctype *) cfr_save= *(cfr_ctype *) cfr_vmax; \
2306 cfr_res= TRUE; \
2307 } \
2308 } while(0)
2309
check_func_int(THD * thd,struct st_mysql_sys_var * var,void * save,st_mysql_value * value)2310 static int check_func_int(THD *thd, struct st_mysql_sys_var *var,
2311 void *save, st_mysql_value *value)
2312 {
2313 my_bool fixed1, fixed2;
2314 long long orig, val;
2315 struct my_option options;
2316 const void *vmin, *vmax;
2317 value->val_int(value, &orig);
2318 val= orig;
2319 plugin_opt_set_limits(&options, var);
2320
2321 vmin= getopt_constraint_get_min_value(var->name, 0, FALSE);
2322 vmax= getopt_constraint_get_max_value(var->name, 0, FALSE);
2323
2324 if (var->flags & PLUGIN_VAR_UNSIGNED)
2325 {
2326 if ((fixed1= (!value->is_unsigned(value) && val < 0)))
2327 val=0;
2328 *(uint *)save= (uint) getopt_ull_limit_value((ulonglong) val, &options,
2329 &fixed2);
2330
2331 CHECK_FUNC_RANGE(uint, vmin, vmax, save, fixed1);
2332 }
2333 else
2334 {
2335 if ((fixed1= (value->is_unsigned(value) && val < 0)))
2336 val=LONGLONG_MAX;
2337 *(int *)save= (int) getopt_ll_limit_value(val, &options, &fixed2);
2338
2339 CHECK_FUNC_RANGE(int, vmin, vmax, save, fixed1);
2340 }
2341
2342 return throw_bounds_warning(thd, var->name, fixed1 || fixed2,
2343 value->is_unsigned(value), (longlong) orig);
2344 }
2345
2346
check_func_long(THD * thd,struct st_mysql_sys_var * var,void * save,st_mysql_value * value)2347 static int check_func_long(THD *thd, struct st_mysql_sys_var *var,
2348 void *save, st_mysql_value *value)
2349 {
2350 my_bool fixed1, fixed2;
2351 long long orig, val;
2352 struct my_option options;
2353 const void *vmin, *vmax;
2354 value->val_int(value, &orig);
2355 val= orig;
2356 plugin_opt_set_limits(&options, var);
2357
2358 vmin= getopt_constraint_get_min_value(var->name, 0, FALSE);
2359 vmax= getopt_constraint_get_max_value(var->name, 0, FALSE);
2360
2361 if (var->flags & PLUGIN_VAR_UNSIGNED)
2362 {
2363 if ((fixed1= (!value->is_unsigned(value) && val < 0)))
2364 val=0;
2365 *(ulong *)save= (ulong) getopt_ull_limit_value((ulonglong) val, &options,
2366 &fixed2);
2367
2368 CHECK_FUNC_RANGE(ulong, vmin, vmax, save, fixed1);
2369 }
2370 else
2371 {
2372 if ((fixed1= (value->is_unsigned(value) && val < 0)))
2373 val=LONGLONG_MAX;
2374 *(long *)save= (long) getopt_ll_limit_value(val, &options, &fixed2);
2375
2376 CHECK_FUNC_RANGE(long, vmin, vmax, save, fixed1);
2377 }
2378
2379 return throw_bounds_warning(thd, var->name, fixed1 || fixed2,
2380 value->is_unsigned(value), (longlong) orig);
2381 }
2382
2383
check_func_longlong(THD * thd,struct st_mysql_sys_var * var,void * save,st_mysql_value * value)2384 static int check_func_longlong(THD *thd, struct st_mysql_sys_var *var,
2385 void *save, st_mysql_value *value)
2386 {
2387 my_bool fixed1, fixed2;
2388 long long orig, val;
2389 struct my_option options;
2390 const void *vmin, *vmax;
2391 value->val_int(value, &orig);
2392 val= orig;
2393 plugin_opt_set_limits(&options, var);
2394
2395 vmin= getopt_constraint_get_min_value(var->name, 0, FALSE);
2396 vmax= getopt_constraint_get_max_value(var->name, 0, FALSE);
2397
2398 if (var->flags & PLUGIN_VAR_UNSIGNED)
2399 {
2400 if ((fixed1= (!value->is_unsigned(value) && val < 0)))
2401 val=0;
2402 *(ulonglong *)save= getopt_ull_limit_value((ulonglong) val, &options,
2403 &fixed2);
2404
2405 CHECK_FUNC_RANGE(ulonglong, vmin, vmax, save, fixed1);
2406 }
2407 else
2408 {
2409 if ((fixed1= (value->is_unsigned(value) && val < 0)))
2410 val=LONGLONG_MAX;
2411 *(longlong *)save= getopt_ll_limit_value(val, &options, &fixed2);
2412
2413 CHECK_FUNC_RANGE(longlong, vmin, vmax, save, fixed1);
2414 }
2415
2416 return throw_bounds_warning(thd, var->name, fixed1 || fixed2,
2417 value->is_unsigned(value), (longlong) orig);
2418 }
2419
check_func_str(THD * thd,struct st_mysql_sys_var * var,void * save,st_mysql_value * value)2420 static int check_func_str(THD *thd, struct st_mysql_sys_var *var,
2421 void *save, st_mysql_value *value)
2422 {
2423 char buff[STRING_BUFFER_USUAL_SIZE];
2424 const char *str;
2425 int length;
2426
2427 length= sizeof(buff);
2428 if ((str= value->val_str(value, buff, &length)))
2429 str= thd->strmake(str, length);
2430 *(const char**)save= str;
2431 return 0;
2432 }
2433
2434
check_func_enum(THD * thd,struct st_mysql_sys_var * var,void * save,st_mysql_value * value)2435 static int check_func_enum(THD *thd, struct st_mysql_sys_var *var,
2436 void *save, st_mysql_value *value)
2437 {
2438 char buff[STRING_BUFFER_USUAL_SIZE];
2439 const char *str;
2440 TYPELIB *typelib;
2441 long long tmp;
2442 long result;
2443 int length;
2444
2445 if (var->flags & PLUGIN_VAR_THDLOCAL)
2446 typelib= ((thdvar_enum_t*) var)->typelib;
2447 else
2448 typelib= ((sysvar_enum_t*) var)->typelib;
2449
2450 if (value->value_type(value) == MYSQL_VALUE_TYPE_STRING)
2451 {
2452 length= sizeof(buff);
2453 if (!(str= value->val_str(value, buff, &length)))
2454 goto err;
2455 if ((result= (long)find_type(typelib, str, length, 0) - 1) < 0)
2456 goto err;
2457 }
2458 else
2459 {
2460 if (value->val_int(value, &tmp))
2461 goto err;
2462 if (tmp < 0 || tmp >= typelib->count)
2463 goto err;
2464 result= (long) tmp;
2465 }
2466 *(long*)save= result;
2467 return 0;
2468 err:
2469 return 1;
2470 }
2471
2472
check_func_set(THD * thd,struct st_mysql_sys_var * var,void * save,st_mysql_value * value)2473 static int check_func_set(THD *thd, struct st_mysql_sys_var *var,
2474 void *save, st_mysql_value *value)
2475 {
2476 char buff[STRING_BUFFER_USUAL_SIZE], *error= 0;
2477 const char *str;
2478 TYPELIB *typelib;
2479 ulonglong result;
2480 uint error_len= 0; // init as only set on error
2481 bool not_used;
2482 int length;
2483
2484 if (var->flags & PLUGIN_VAR_THDLOCAL)
2485 typelib= ((thdvar_set_t*) var)->typelib;
2486 else
2487 typelib= ((sysvar_set_t*)var)->typelib;
2488
2489 if (value->value_type(value) == MYSQL_VALUE_TYPE_STRING)
2490 {
2491 length= sizeof(buff);
2492 if (!(str= value->val_str(value, buff, &length)))
2493 goto err;
2494 result= find_set(typelib, str, length, NULL,
2495 &error, &error_len, ¬_used);
2496 if (error_len)
2497 goto err;
2498 }
2499 else
2500 {
2501 if (value->val_int(value, (long long *)&result))
2502 goto err;
2503 if (unlikely((result >= (1ULL << typelib->count)) &&
2504 (typelib->count < sizeof(long)*8)))
2505 goto err;
2506 }
2507 *(ulonglong*)save= result;
2508 return 0;
2509 err:
2510 return 1;
2511 }
2512
check_func_double(THD * thd,struct st_mysql_sys_var * var,void * save,st_mysql_value * value)2513 static int check_func_double(THD *thd, struct st_mysql_sys_var *var,
2514 void *save, st_mysql_value *value)
2515 {
2516 double v;
2517 my_bool fixed;
2518 struct my_option option;
2519
2520 value->val_real(value, &v);
2521 plugin_opt_set_limits(&option, var);
2522 *(double *) save= getopt_double_limit_value(v, &option, &fixed);
2523
2524 return throw_bounds_warning(thd, var->name, fixed, v);
2525 }
2526
2527
update_func_bool(THD * thd,struct st_mysql_sys_var * var,void * tgt,const void * save)2528 static void update_func_bool(THD *thd, struct st_mysql_sys_var *var,
2529 void *tgt, const void *save)
2530 {
2531 *(my_bool *) tgt= *(my_bool *) save ? TRUE : FALSE;
2532 }
2533
2534
update_func_int(THD * thd,struct st_mysql_sys_var * var,void * tgt,const void * save)2535 static void update_func_int(THD *thd, struct st_mysql_sys_var *var,
2536 void *tgt, const void *save)
2537 {
2538 *(int *)tgt= *(int *) save;
2539 }
2540
2541
update_func_long(THD * thd,struct st_mysql_sys_var * var,void * tgt,const void * save)2542 static void update_func_long(THD *thd, struct st_mysql_sys_var *var,
2543 void *tgt, const void *save)
2544 {
2545 *(long *)tgt= *(long *) save;
2546 }
2547
2548
update_func_longlong(THD * thd,struct st_mysql_sys_var * var,void * tgt,const void * save)2549 static void update_func_longlong(THD *thd, struct st_mysql_sys_var *var,
2550 void *tgt, const void *save)
2551 {
2552 *(longlong *)tgt= *(ulonglong *) save;
2553 }
2554
2555
update_func_str(THD * thd,struct st_mysql_sys_var * var,void * tgt,const void * save)2556 static void update_func_str(THD *thd, struct st_mysql_sys_var *var,
2557 void *tgt, const void *save)
2558 {
2559 *(char **) tgt= *(char **) save;
2560 }
2561
update_func_double(THD * thd,struct st_mysql_sys_var * var,void * tgt,const void * save)2562 static void update_func_double(THD *thd, struct st_mysql_sys_var *var,
2563 void *tgt, const void *save)
2564 {
2565 *(double *) tgt= *(double *) save;
2566 }
2567
2568 /****************************************************************************
2569 System Variables support
2570 ****************************************************************************/
2571
2572
find_sys_var(THD * thd,const char * str,uint length)2573 sys_var *find_sys_var(THD *thd, const char *str, uint length)
2574 {
2575 sys_var *var;
2576 sys_var_pluginvar *pi= NULL;
2577 plugin_ref plugin;
2578 DBUG_ENTER("find_sys_var");
2579
2580 const my_bool *hidden= getopt_constraint_get_hidden_value(str, 0, FALSE);
2581 if (hidden && *hidden)
2582 {
2583 var= NULL;
2584 goto exit;
2585 }
2586
2587 mysql_mutex_lock(&LOCK_plugin);
2588 mysql_rwlock_rdlock(&LOCK_system_variables_hash);
2589 if ((var= intern_find_sys_var(str, length)) &&
2590 (pi= var->cast_pluginvar()))
2591 {
2592 mysql_rwlock_unlock(&LOCK_system_variables_hash);
2593 LEX *lex= thd ? thd->lex : 0;
2594 if (!(plugin= my_intern_plugin_lock(lex, plugin_int_to_ref(pi->plugin))))
2595 var= NULL; /* failed to lock it, it must be uninstalling */
2596 else
2597 if (!(plugin_state(plugin) & PLUGIN_IS_READY))
2598 {
2599 /* initialization not completed */
2600 var= NULL;
2601 intern_plugin_unlock(lex, plugin);
2602 }
2603 }
2604 else
2605 mysql_rwlock_unlock(&LOCK_system_variables_hash);
2606 mysql_mutex_unlock(&LOCK_plugin);
2607
2608 exit:
2609 if (!var)
2610 my_error(ER_UNKNOWN_SYSTEM_VARIABLE, MYF(0), (char*) str);
2611 DBUG_RETURN(var);
2612 }
2613
2614
2615 /*
2616 called by register_var, construct_options and test_plugin_options.
2617 Returns the 'bookmark' for the named variable.
2618 LOCK_system_variables_hash should be at least read locked
2619 */
find_bookmark(const char * plugin,const char * name,int flags)2620 static st_bookmark *find_bookmark(const char *plugin, const char *name,
2621 int flags)
2622 {
2623 st_bookmark *result= NULL;
2624 uint namelen, length, pluginlen= 0;
2625 char *varname, *p;
2626
2627 if (!(flags & PLUGIN_VAR_THDLOCAL))
2628 return NULL;
2629
2630 namelen= strlen(name);
2631 if (plugin)
2632 pluginlen= strlen(plugin) + 1;
2633 length= namelen + pluginlen + 2;
2634 varname= (char*) my_alloca(length);
2635
2636 if (plugin)
2637 {
2638 strxmov(varname + 1, plugin, "_", name, NullS);
2639 for (p= varname + 1; *p; p++)
2640 if (*p == '-')
2641 *p= '_';
2642 }
2643 else
2644 memcpy(varname + 1, name, namelen + 1);
2645
2646 varname[0]= flags & PLUGIN_VAR_TYPEMASK;
2647
2648 result= (st_bookmark*) my_hash_search(&bookmark_hash,
2649 (const uchar*) varname, length - 1);
2650
2651 my_afree(varname);
2652 return result;
2653 }
2654
2655
2656 /*
2657 returns a bookmark for thd-local variables, creating if neccessary.
2658 returns null for non thd-local variables.
2659 Requires that a write lock is obtained on LOCK_system_variables_hash
2660 */
register_var(const char * plugin,const char * name,int flags)2661 static st_bookmark *register_var(const char *plugin, const char *name,
2662 int flags)
2663 {
2664 uint length= strlen(plugin) + strlen(name) + 3, size= 0, offset, new_size;
2665 st_bookmark *result;
2666 char *varname, *p;
2667
2668 if (!(flags & PLUGIN_VAR_THDLOCAL))
2669 return NULL;
2670
2671 switch (flags & PLUGIN_VAR_TYPEMASK) {
2672 case PLUGIN_VAR_BOOL:
2673 size= sizeof(my_bool);
2674 break;
2675 case PLUGIN_VAR_INT:
2676 size= sizeof(int);
2677 break;
2678 case PLUGIN_VAR_LONG:
2679 case PLUGIN_VAR_ENUM:
2680 size= sizeof(long);
2681 break;
2682 case PLUGIN_VAR_LONGLONG:
2683 case PLUGIN_VAR_SET:
2684 size= sizeof(ulonglong);
2685 break;
2686 case PLUGIN_VAR_STR:
2687 size= sizeof(char*);
2688 break;
2689 case PLUGIN_VAR_DOUBLE:
2690 size= sizeof(double);
2691 break;
2692 default:
2693 DBUG_ASSERT(0);
2694 return NULL;
2695 };
2696
2697 varname= ((char*) my_alloca(length));
2698 strxmov(varname + 1, plugin, "_", name, NullS);
2699 for (p= varname + 1; *p; p++)
2700 if (*p == '-')
2701 *p= '_';
2702
2703 if (!(result= find_bookmark(NULL, varname + 1, flags)))
2704 {
2705 result= (st_bookmark*) alloc_root(&plugin_mem_root,
2706 sizeof(struct st_bookmark) + length-1);
2707 varname[0]= flags & PLUGIN_VAR_TYPEMASK;
2708 memcpy(result->key, varname, length);
2709 result->name_len= length - 2;
2710 result->offset= -1;
2711
2712 DBUG_ASSERT(size && !(size & (size-1))); /* must be power of 2 */
2713
2714 offset= global_system_variables.dynamic_variables_size;
2715 offset= (offset + size - 1) & ~(size - 1);
2716 result->offset= (int) offset;
2717
2718 new_size= (offset + size + 63) & ~63;
2719
2720 if (new_size > global_variables_dynamic_size)
2721 {
2722 global_system_variables.dynamic_variables_ptr= (char*)
2723 my_realloc(global_system_variables.dynamic_variables_ptr, new_size,
2724 MYF(MY_WME | MY_FAE | MY_ALLOW_ZERO_PTR));
2725 max_system_variables.dynamic_variables_ptr= (char*)
2726 my_realloc(max_system_variables.dynamic_variables_ptr, new_size,
2727 MYF(MY_WME | MY_FAE | MY_ALLOW_ZERO_PTR));
2728 /*
2729 Clear the new variable value space. This is required for string
2730 variables. If their value is non-NULL, it must point to a valid
2731 string.
2732 */
2733 memset(global_system_variables.dynamic_variables_ptr +
2734 global_variables_dynamic_size, 0,
2735 new_size - global_variables_dynamic_size);
2736 memset(max_system_variables.dynamic_variables_ptr +
2737 global_variables_dynamic_size, 0,
2738 new_size - global_variables_dynamic_size);
2739 global_variables_dynamic_size= new_size;
2740 }
2741
2742 global_system_variables.dynamic_variables_head= offset;
2743 max_system_variables.dynamic_variables_head= offset;
2744 global_system_variables.dynamic_variables_size= offset + size;
2745 max_system_variables.dynamic_variables_size= offset + size;
2746 global_system_variables.dynamic_variables_version++;
2747 max_system_variables.dynamic_variables_version++;
2748
2749 result->version= global_system_variables.dynamic_variables_version;
2750
2751 /* this should succeed because we have already checked if a dup exists */
2752 if (my_hash_insert(&bookmark_hash, (uchar*) result))
2753 {
2754 fprintf(stderr, "failed to add placeholder to hash");
2755 DBUG_ASSERT(0);
2756 }
2757
2758 /*
2759 Hashing vars of string type with MEMALLOC flag.
2760 */
2761 if (((flags & PLUGIN_VAR_TYPEMASK) == PLUGIN_VAR_STR) &&
2762 (flags & PLUGIN_VAR_MEMALLOC) &&
2763 (my_hash_insert(&malloced_string_type_sysvars_bookmark_hash,
2764 (uchar *)result)))
2765 {
2766 fprintf(stderr, "failed to add placeholder to"
2767 " hash of malloced string type sysvars");
2768 DBUG_ASSERT(0);
2769 }
2770 }
2771 my_afree(varname);
2772 return result;
2773 }
2774
restore_pluginvar_names(sys_var * first)2775 static void restore_pluginvar_names(sys_var *first)
2776 {
2777 for (sys_var *var= first; var; var= var->next)
2778 {
2779 sys_var_pluginvar *pv= var->cast_pluginvar();
2780 pv->plugin_var->name= pv->orig_pluginvar_name;
2781 }
2782 }
2783
2784
2785 /*
2786 returns a pointer to the memory which holds the thd-local variable or
2787 a pointer to the global variable if thd==null.
2788 If required, will sync with global variables if the requested variable
2789 has not yet been allocated in the current thread.
2790 */
intern_sys_var_ptr(THD * thd,int offset,bool global_lock)2791 static uchar *intern_sys_var_ptr(THD* thd, int offset, bool global_lock)
2792 {
2793 DBUG_ASSERT(offset >= 0);
2794 DBUG_ASSERT((uint)offset <= global_system_variables.dynamic_variables_head);
2795
2796 if (!thd)
2797 return (uchar*) global_system_variables.dynamic_variables_ptr + offset;
2798
2799 /*
2800 dynamic_variables_head points to the largest valid offset
2801 */
2802 if (!thd->variables.dynamic_variables_ptr ||
2803 (uint)offset > thd->variables.dynamic_variables_head)
2804 {
2805 uint idx;
2806
2807 mysql_rwlock_rdlock(&LOCK_system_variables_hash);
2808
2809 thd->variables.dynamic_variables_ptr= (char*)
2810 my_realloc(thd->variables.dynamic_variables_ptr,
2811 global_variables_dynamic_size,
2812 MYF(MY_WME | MY_FAE | MY_ALLOW_ZERO_PTR));
2813
2814 if (global_lock)
2815 mysql_mutex_lock(&LOCK_global_system_variables);
2816
2817 mysql_mutex_assert_owner(&LOCK_global_system_variables);
2818
2819 memcpy(thd->variables.dynamic_variables_ptr +
2820 thd->variables.dynamic_variables_size,
2821 global_system_variables.dynamic_variables_ptr +
2822 thd->variables.dynamic_variables_size,
2823 global_system_variables.dynamic_variables_size -
2824 thd->variables.dynamic_variables_size);
2825
2826 /*
2827 now we need to iterate through any newly copied 'defaults'
2828 and if it is a string type with MEMALLOC flag, we need to strdup
2829 */
2830 for (idx= 0; idx < bookmark_hash.records; idx++)
2831 {
2832 sys_var_pluginvar *pi;
2833 sys_var *var;
2834 st_bookmark *v= (st_bookmark*) my_hash_element(&bookmark_hash,idx);
2835
2836 if (v->version <= thd->variables.dynamic_variables_version ||
2837 !(var= intern_find_sys_var(v->key + 1, v->name_len)) ||
2838 !(pi= var->cast_pluginvar()) ||
2839 v->key[0] != (pi->plugin_var->flags & PLUGIN_VAR_TYPEMASK))
2840 continue;
2841
2842 /* Here we do anything special that may be required of the data types */
2843
2844 if ((pi->plugin_var->flags & PLUGIN_VAR_TYPEMASK) == PLUGIN_VAR_STR &&
2845 pi->plugin_var->flags & PLUGIN_VAR_MEMALLOC)
2846 {
2847 int varoff= *(int *) (pi->plugin_var + 1);
2848 char **thdvar= (char **) (thd->variables.
2849 dynamic_variables_ptr + varoff);
2850 char **sysvar= (char **) (global_system_variables.
2851 dynamic_variables_ptr + varoff);
2852 *thdvar= NULL;
2853 plugin_var_memalloc_session_update(thd, NULL, thdvar, *sysvar);
2854 }
2855 }
2856
2857 if (global_lock)
2858 mysql_mutex_unlock(&LOCK_global_system_variables);
2859
2860 thd->variables.dynamic_variables_version=
2861 global_system_variables.dynamic_variables_version;
2862 thd->variables.dynamic_variables_head=
2863 global_system_variables.dynamic_variables_head;
2864 thd->variables.dynamic_variables_size=
2865 global_system_variables.dynamic_variables_size;
2866
2867 mysql_rwlock_unlock(&LOCK_system_variables_hash);
2868 }
2869 return (uchar*)thd->variables.dynamic_variables_ptr + offset;
2870 }
2871
2872
2873 /**
2874 For correctness and simplicity's sake, a pointer to a function
2875 must be compatible with pointed-to type, that is, the return and
2876 parameters types must be the same. Thus, a callback function is
2877 defined for each scalar type. The functions are assigned in
2878 construct_options to their respective types.
2879 */
2880
mysql_sys_var_char(THD * thd,int offset)2881 static char *mysql_sys_var_char(THD* thd, int offset)
2882 {
2883 return (char *) intern_sys_var_ptr(thd, offset, true);
2884 }
2885
mysql_sys_var_int(THD * thd,int offset)2886 static int *mysql_sys_var_int(THD* thd, int offset)
2887 {
2888 return (int *) intern_sys_var_ptr(thd, offset, true);
2889 }
2890
mysql_sys_var_long(THD * thd,int offset)2891 static long *mysql_sys_var_long(THD* thd, int offset)
2892 {
2893 return (long *) intern_sys_var_ptr(thd, offset, true);
2894 }
2895
mysql_sys_var_ulong(THD * thd,int offset)2896 static unsigned long *mysql_sys_var_ulong(THD* thd, int offset)
2897 {
2898 return (unsigned long *) intern_sys_var_ptr(thd, offset, true);
2899 }
2900
mysql_sys_var_longlong(THD * thd,int offset)2901 static long long *mysql_sys_var_longlong(THD* thd, int offset)
2902 {
2903 return (long long *) intern_sys_var_ptr(thd, offset, true);
2904 }
2905
mysql_sys_var_ulonglong(THD * thd,int offset)2906 static unsigned long long *mysql_sys_var_ulonglong(THD* thd, int offset)
2907 {
2908 return (unsigned long long *) intern_sys_var_ptr(thd, offset, true);
2909 }
2910
mysql_sys_var_str(THD * thd,int offset)2911 static char **mysql_sys_var_str(THD* thd, int offset)
2912 {
2913 return (char **) intern_sys_var_ptr(thd, offset, true);
2914 }
2915
mysql_sys_var_double(THD * thd,int offset)2916 static double *mysql_sys_var_double(THD* thd, int offset)
2917 {
2918 return (double *) intern_sys_var_ptr(thd, offset, true);
2919 }
2920
plugin_thdvar_init(THD * thd,bool enable_plugins)2921 void plugin_thdvar_init(THD *thd, bool enable_plugins)
2922 {
2923 plugin_ref old_table_plugin= thd->variables.table_plugin;
2924 plugin_ref old_temp_table_plugin= thd->variables.temp_table_plugin;
2925 DBUG_ENTER("plugin_thdvar_init");
2926
2927 thd->variables.table_plugin= NULL;
2928 thd->variables.temp_table_plugin= NULL;
2929 cleanup_variables(thd, &thd->variables);
2930
2931 thd->variables= global_system_variables;
2932 thd->variables.table_plugin= NULL;
2933 thd->variables.temp_table_plugin= NULL;
2934
2935 /* we are going to allocate these lazily */
2936 thd->variables.dynamic_variables_version= 0;
2937 thd->variables.dynamic_variables_size= 0;
2938 thd->variables.dynamic_variables_ptr= 0;
2939
2940 if (enable_plugins)
2941 {
2942 mysql_mutex_lock(&LOCK_plugin);
2943 thd->variables.table_plugin=
2944 my_intern_plugin_lock(NULL, global_system_variables.table_plugin);
2945 intern_plugin_unlock(NULL, old_table_plugin);
2946 thd->variables.temp_table_plugin=
2947 my_intern_plugin_lock(NULL, global_system_variables.temp_table_plugin);
2948 intern_plugin_unlock(NULL, old_temp_table_plugin);
2949 mysql_mutex_unlock(&LOCK_plugin);
2950 }
2951 DBUG_VOID_RETURN;
2952 }
2953
2954
2955 /*
2956 Unlocks all system variables which hold a reference
2957 */
unlock_variables(THD * thd,struct system_variables * vars)2958 static void unlock_variables(THD *thd, struct system_variables *vars)
2959 {
2960 intern_plugin_unlock(NULL, vars->table_plugin);
2961 intern_plugin_unlock(NULL, vars->temp_table_plugin);
2962 vars->table_plugin= NULL;
2963 vars->temp_table_plugin= NULL;
2964 }
2965
2966
2967 /*
2968 Frees memory used by system variables
2969
2970 Unlike plugin_vars_free_values() it frees all variables of all plugins,
2971 it's used on shutdown.
2972 */
cleanup_variables(THD * thd,struct system_variables * vars)2973 static void cleanup_variables(THD *thd, struct system_variables *vars)
2974 {
2975 if (thd)
2976 plugin_var_memalloc_free(&thd->variables);
2977
2978 DBUG_ASSERT(vars->table_plugin == NULL);
2979 DBUG_ASSERT(vars->temp_table_plugin == NULL);
2980
2981 my_free(vars->dynamic_variables_ptr);
2982 vars->dynamic_variables_ptr= NULL;
2983 vars->dynamic_variables_size= 0;
2984 vars->dynamic_variables_version= 0;
2985 }
2986
2987
plugin_thdvar_cleanup(THD * thd)2988 void plugin_thdvar_cleanup(THD *thd)
2989 {
2990 uint idx;
2991 plugin_ref *list;
2992 DBUG_ENTER("plugin_thdvar_cleanup");
2993
2994 mysql_mutex_lock(&LOCK_plugin);
2995
2996 unlock_variables(thd, &thd->variables);
2997 cleanup_variables(thd, &thd->variables);
2998
2999 if ((idx= thd->lex->plugins.elements))
3000 {
3001 list= ((plugin_ref*) thd->lex->plugins.buffer) + idx - 1;
3002 DBUG_PRINT("info",("unlocking %d plugins", idx));
3003 while ((uchar*) list >= thd->lex->plugins.buffer)
3004 intern_plugin_unlock(thd->lex, *list--);
3005 }
3006
3007 reap_plugins();
3008 mysql_mutex_unlock(&LOCK_plugin);
3009
3010 reset_dynamic(&thd->lex->plugins);
3011
3012 DBUG_VOID_RETURN;
3013 }
3014
3015
3016 /**
3017 @brief Free values of thread variables of a plugin.
3018
3019 This must be called before a plugin is deleted. Otherwise its
3020 variables are no longer accessible and the value space is lost. Note
3021 that only string values with PLUGIN_VAR_MEMALLOC are allocated and
3022 must be freed.
3023
3024 @param[in] vars Chain of system variables of a plugin
3025 */
3026
plugin_vars_free_values(sys_var * vars)3027 static void plugin_vars_free_values(sys_var *vars)
3028 {
3029 DBUG_ENTER("plugin_vars_free_values");
3030
3031 for (sys_var *var= vars; var; var= var->next)
3032 {
3033 sys_var_pluginvar *piv= var->cast_pluginvar();
3034 if (piv &&
3035 ((piv->plugin_var->flags & PLUGIN_VAR_TYPEMASK) == PLUGIN_VAR_STR) &&
3036 (piv->plugin_var->flags & PLUGIN_VAR_MEMALLOC))
3037 {
3038 /* Free the string from global_system_variables. */
3039 char **valptr= (char**) piv->real_value_ptr(NULL, OPT_GLOBAL);
3040 DBUG_PRINT("plugin", ("freeing value for: '%s' addr: 0x%lx",
3041 var->name.str, (long) valptr));
3042 my_free(*valptr);
3043 *valptr= NULL;
3044 }
3045 }
3046 DBUG_VOID_RETURN;
3047 }
3048
pluginvar_show_type(st_mysql_sys_var * plugin_var)3049 static SHOW_TYPE pluginvar_show_type(st_mysql_sys_var *plugin_var)
3050 {
3051 switch (plugin_var->flags & PLUGIN_VAR_TYPEMASK) {
3052 case PLUGIN_VAR_BOOL:
3053 return SHOW_MY_BOOL;
3054 case PLUGIN_VAR_INT:
3055 return SHOW_INT;
3056 case PLUGIN_VAR_LONG:
3057 return SHOW_LONG;
3058 case PLUGIN_VAR_LONGLONG:
3059 return SHOW_LONGLONG;
3060 case PLUGIN_VAR_STR:
3061 return SHOW_CHAR_PTR;
3062 case PLUGIN_VAR_ENUM:
3063 case PLUGIN_VAR_SET:
3064 return SHOW_CHAR;
3065 case PLUGIN_VAR_DOUBLE:
3066 return SHOW_DOUBLE;
3067 default:
3068 DBUG_ASSERT(0);
3069 return SHOW_UNDEF;
3070 }
3071 }
3072
3073
3074 /**
3075 Set value for thread local variable with PLUGIN_VAR_MEMALLOC flag.
3076
3077 @param[in] thd Thread context.
3078 @param[in] var Plugin variable.
3079 @param[in,out] dest Destination memory pointer.
3080 @param[in] value '\0'-terminated new value.
3081
3082 Most plugin variable values are stored on dynamic_variables_ptr.
3083 Releasing memory occupied by these values is as simple as freeing
3084 dynamic_variables_ptr.
3085
3086 An exception to the rule are PLUGIN_VAR_MEMALLOC variables, which
3087 are stored on individual memory hunks. All of these hunks has to
3088 be freed when it comes to cleanup.
3089
3090 It may happen that a plugin was uninstalled and descriptors of
3091 it's variables are lost. In this case it is impossible to locate
3092 corresponding values.
3093
3094 In addition to allocating and setting variable value, new element
3095 is added to dynamic_variables_allocs list. When thread is done, it
3096 has to call plugin_var_memalloc_free() to release memory used by
3097 PLUGIN_VAR_MEMALLOC variables.
3098
3099 If var is NULL, variable update function is not called. This is
3100 needed when we take snapshot of system variables during thread
3101 initialization.
3102
3103 @note List element and variable value are stored on the same memory
3104 hunk. List element is followed by variable value.
3105
3106 @return Completion status
3107 @retval false Success
3108 @retval true Failure
3109 */
3110
plugin_var_memalloc_session_update(THD * thd,struct st_mysql_sys_var * var,char ** dest,const char * value)3111 static bool plugin_var_memalloc_session_update(THD *thd,
3112 struct st_mysql_sys_var *var,
3113 char **dest, const char *value)
3114
3115 {
3116 LIST *old_element= NULL;
3117 struct system_variables *vars= &thd->variables;
3118 DBUG_ENTER("plugin_var_memalloc_session_update");
3119
3120 if (value)
3121 {
3122 size_t length= strlen(value) + 1;
3123 LIST *element;
3124 if (!(element= (LIST *) my_malloc(sizeof(LIST) + length, MYF(MY_WME))))
3125 DBUG_RETURN(true);
3126 memcpy(element + 1, value, length);
3127 value= (const char *) (element + 1);
3128 vars->dynamic_variables_allocs= list_add(vars->dynamic_variables_allocs,
3129 element);
3130 }
3131
3132 if (*dest)
3133 old_element= (LIST *) (*dest - sizeof(LIST));
3134
3135 if (var)
3136 var->update(thd, var, (void **) dest, (const void *) &value);
3137 else
3138 *dest= (char *) value;
3139
3140 if (old_element)
3141 {
3142 vars->dynamic_variables_allocs= list_delete(vars->dynamic_variables_allocs,
3143 old_element);
3144 my_free(old_element);
3145 }
3146 DBUG_RETURN(false);
3147 }
3148
3149
3150 /**
3151 Set value for a thread local variable.
3152
3153 @param[in] thd Thread context.
3154 @param[in] var Plugin variable.
3155 @param[in,out] dest Destination memory pointer.
3156 @param[in] value New value.
3157
3158 Note: new value should be '\0'-terminated for string variables.
3159
3160 Used in plugin.h:THDVAR_SET(thd, name, value) macro.
3161 */
3162
plugin_thdvar_safe_update(THD * thd,st_mysql_sys_var * var,char ** dest,const char * value)3163 void plugin_thdvar_safe_update(THD *thd, st_mysql_sys_var *var, char **dest, const char *value)
3164 {
3165 DBUG_ASSERT(current_thd == NULL || thd == current_thd);
3166
3167 if (var->flags & PLUGIN_VAR_THDLOCAL)
3168 {
3169 if ((var->flags & PLUGIN_VAR_TYPEMASK) == PLUGIN_VAR_STR &&
3170 var->flags & PLUGIN_VAR_MEMALLOC)
3171 plugin_var_memalloc_session_update(thd, var, dest, value);
3172 else
3173 var->update(thd, var, dest, value);
3174 }
3175 }
3176
3177
3178 /**
3179 Free all elements allocated by plugin_var_memalloc_session_update().
3180
3181 @param[in] vars system variables structure
3182
3183 @see plugin_var_memalloc_session_update
3184 */
3185
plugin_var_memalloc_free(struct system_variables * vars)3186 static void plugin_var_memalloc_free(struct system_variables *vars)
3187 {
3188 LIST *next, *root;
3189 DBUG_ENTER("plugin_var_memalloc_free");
3190 for (root= vars->dynamic_variables_allocs; root; root= next)
3191 {
3192 next= root->next;
3193 my_free(root);
3194 }
3195 vars->dynamic_variables_allocs= NULL;
3196 DBUG_VOID_RETURN;
3197 }
3198
3199
3200 /**
3201 Set value for global variable with PLUGIN_VAR_MEMALLOC flag.
3202
3203 @param[in] thd Thread context.
3204 @param[in] var Plugin variable.
3205 @param[in,out] dest Destination memory pointer.
3206 @param[in] value '\0'-terminated new value.
3207
3208 @return Completion status
3209 @retval false Success
3210 @retval true Failure
3211 */
3212
plugin_var_memalloc_global_update(THD * thd,struct st_mysql_sys_var * var,char ** dest,const char * value)3213 static bool plugin_var_memalloc_global_update(THD *thd,
3214 struct st_mysql_sys_var *var,
3215 char **dest, const char *value)
3216 {
3217 char *old_value= *dest;
3218 DBUG_ENTER("plugin_var_memalloc_global_update");
3219
3220 if (value && !(value= my_strdup(value, MYF(MY_WME))))
3221 DBUG_RETURN(true);
3222
3223 var->update(thd, var, (void **) dest, (const void *) &value);
3224
3225 if (old_value)
3226 my_free(old_value);
3227
3228 DBUG_RETURN(false);
3229 }
3230
3231
check_update_type(Item_result type)3232 bool sys_var_pluginvar::check_update_type(Item_result type)
3233 {
3234 switch (plugin_var->flags & PLUGIN_VAR_TYPEMASK) {
3235 case PLUGIN_VAR_INT:
3236 case PLUGIN_VAR_LONG:
3237 case PLUGIN_VAR_LONGLONG:
3238 return type != INT_RESULT;
3239 case PLUGIN_VAR_STR:
3240 return type != STRING_RESULT;
3241 case PLUGIN_VAR_ENUM:
3242 case PLUGIN_VAR_BOOL:
3243 case PLUGIN_VAR_SET:
3244 return type != STRING_RESULT && type != INT_RESULT;
3245 case PLUGIN_VAR_DOUBLE:
3246 return type != INT_RESULT && type != REAL_RESULT && type != DECIMAL_RESULT;
3247 default:
3248 return true;
3249 }
3250 }
3251
3252
real_value_ptr(THD * thd,enum_var_type type)3253 uchar* sys_var_pluginvar::real_value_ptr(THD *thd, enum_var_type type)
3254 {
3255 DBUG_ASSERT(thd || (type == OPT_GLOBAL));
3256 if (plugin_var->flags & PLUGIN_VAR_THDLOCAL)
3257 {
3258 if (type == OPT_GLOBAL)
3259 thd= NULL;
3260
3261 return intern_sys_var_ptr(thd, *(int*) (plugin_var+1), false);
3262 }
3263 return *(uchar**) (plugin_var+1);
3264 }
3265
3266
plugin_var_typelib(void)3267 TYPELIB* sys_var_pluginvar::plugin_var_typelib(void)
3268 {
3269 switch (plugin_var->flags & (PLUGIN_VAR_TYPEMASK | PLUGIN_VAR_THDLOCAL)) {
3270 case PLUGIN_VAR_ENUM:
3271 return ((sysvar_enum_t *)plugin_var)->typelib;
3272 case PLUGIN_VAR_SET:
3273 return ((sysvar_set_t *)plugin_var)->typelib;
3274 case PLUGIN_VAR_ENUM | PLUGIN_VAR_THDLOCAL:
3275 return ((thdvar_enum_t *)plugin_var)->typelib;
3276 case PLUGIN_VAR_SET | PLUGIN_VAR_THDLOCAL:
3277 return ((thdvar_set_t *)plugin_var)->typelib;
3278 default:
3279 return NULL;
3280 }
3281 return NULL; /* Keep compiler happy */
3282 }
3283
3284
do_value_ptr(THD * thd,enum_var_type type,LEX_STRING * base)3285 uchar* sys_var_pluginvar::do_value_ptr(THD *thd, enum_var_type type,
3286 LEX_STRING *base)
3287 {
3288 uchar* result;
3289
3290 result= real_value_ptr(thd, type);
3291
3292 if ((plugin_var->flags & PLUGIN_VAR_TYPEMASK) == PLUGIN_VAR_ENUM)
3293 result= (uchar*) get_type(plugin_var_typelib(), *(ulong*)result);
3294 else if ((plugin_var->flags & PLUGIN_VAR_TYPEMASK) == PLUGIN_VAR_SET)
3295 result= (uchar*) set_to_string(thd, 0, *(ulonglong*) result,
3296 plugin_var_typelib()->type_names);
3297 return result;
3298 }
3299
do_check(THD * thd,set_var * var)3300 bool sys_var_pluginvar::do_check(THD *thd, set_var *var)
3301 {
3302 st_item_value_holder value;
3303 DBUG_ASSERT(!is_readonly());
3304 DBUG_ASSERT(plugin_var->check);
3305
3306 value.value_type= item_value_type;
3307 value.val_str= item_val_str;
3308 value.val_int= item_val_int;
3309 value.val_real= item_val_real;
3310 value.is_unsigned= item_is_unsigned;
3311 value.item= var->value;
3312
3313 return plugin_var->check(thd, plugin_var, &var->save_result, &value);
3314 }
3315
session_update(THD * thd,set_var * var)3316 bool sys_var_pluginvar::session_update(THD *thd, set_var *var)
3317 {
3318 bool rc= false;
3319 DBUG_ASSERT(!is_readonly());
3320 DBUG_ASSERT(plugin_var->flags & PLUGIN_VAR_THDLOCAL);
3321 DBUG_ASSERT(thd == current_thd);
3322
3323 mysql_mutex_lock(&LOCK_global_system_variables);
3324 void *tgt= real_value_ptr(thd, var->type);
3325 const void *src= var->value ? (void*)&var->save_result
3326 : (void*)real_value_ptr(thd, OPT_GLOBAL);
3327 mysql_mutex_unlock(&LOCK_global_system_variables);
3328
3329 if ((plugin_var->flags & PLUGIN_VAR_TYPEMASK) == PLUGIN_VAR_STR &&
3330 plugin_var->flags & PLUGIN_VAR_MEMALLOC)
3331 rc= plugin_var_memalloc_session_update(thd, plugin_var, (char **) tgt,
3332 *(const char **) src);
3333 else
3334 plugin_var->update(thd, plugin_var, tgt, src);
3335
3336 return rc;
3337 }
3338
global_update(THD * thd,set_var * var)3339 bool sys_var_pluginvar::global_update(THD *thd, set_var *var)
3340 {
3341 bool rc= false;
3342 DBUG_ASSERT(!is_readonly());
3343 mysql_mutex_assert_owner(&LOCK_global_system_variables);
3344
3345 void *tgt= real_value_ptr(thd, var->type);
3346 const void *src= &var->save_result;
3347
3348 if (!var->value)
3349 {
3350 switch (plugin_var->flags & (PLUGIN_VAR_TYPEMASK | PLUGIN_VAR_THDLOCAL)) {
3351 case PLUGIN_VAR_INT:
3352 src= &((sysvar_uint_t*) plugin_var)->def_val;
3353 break;
3354 case PLUGIN_VAR_LONG:
3355 src= &((sysvar_ulong_t*) plugin_var)->def_val;
3356 break;
3357 case PLUGIN_VAR_LONGLONG:
3358 src= &((sysvar_ulonglong_t*) plugin_var)->def_val;
3359 break;
3360 case PLUGIN_VAR_ENUM:
3361 src= &((sysvar_enum_t*) plugin_var)->def_val;
3362 break;
3363 case PLUGIN_VAR_SET:
3364 src= &((sysvar_set_t*) plugin_var)->def_val;
3365 break;
3366 case PLUGIN_VAR_BOOL:
3367 src= &((sysvar_bool_t*) plugin_var)->def_val;
3368 break;
3369 case PLUGIN_VAR_STR:
3370 src= &((sysvar_str_t*) plugin_var)->def_val;
3371 break;
3372 case PLUGIN_VAR_DOUBLE:
3373 src= &((sysvar_double_t*) plugin_var)->def_val;
3374 break;
3375 case PLUGIN_VAR_INT | PLUGIN_VAR_THDLOCAL:
3376 src= &((thdvar_uint_t*) plugin_var)->def_val;
3377 break;
3378 case PLUGIN_VAR_LONG | PLUGIN_VAR_THDLOCAL:
3379 src= &((thdvar_ulong_t*) plugin_var)->def_val;
3380 break;
3381 case PLUGIN_VAR_LONGLONG | PLUGIN_VAR_THDLOCAL:
3382 src= &((thdvar_ulonglong_t*) plugin_var)->def_val;
3383 break;
3384 case PLUGIN_VAR_ENUM | PLUGIN_VAR_THDLOCAL:
3385 src= &((thdvar_enum_t*) plugin_var)->def_val;
3386 break;
3387 case PLUGIN_VAR_SET | PLUGIN_VAR_THDLOCAL:
3388 src= &((thdvar_set_t*) plugin_var)->def_val;
3389 break;
3390 case PLUGIN_VAR_BOOL | PLUGIN_VAR_THDLOCAL:
3391 src= &((thdvar_bool_t*) plugin_var)->def_val;
3392 break;
3393 case PLUGIN_VAR_STR | PLUGIN_VAR_THDLOCAL:
3394 src= &((thdvar_str_t*) plugin_var)->def_val;
3395 break;
3396 case PLUGIN_VAR_DOUBLE | PLUGIN_VAR_THDLOCAL:
3397 src= &((thdvar_double_t*) plugin_var)->def_val;
3398 break;
3399 default:
3400 DBUG_ASSERT(0);
3401 }
3402 }
3403
3404 if ((plugin_var->flags & PLUGIN_VAR_TYPEMASK) == PLUGIN_VAR_STR &&
3405 plugin_var->flags & PLUGIN_VAR_MEMALLOC)
3406 rc= plugin_var_memalloc_global_update(thd, plugin_var, (char **) tgt,
3407 *(const char **) src);
3408 else
3409 plugin_var->update(thd, plugin_var, tgt, src);
3410
3411 return rc;
3412 }
3413
3414
3415 #define OPTION_SET_LIMITS(type, options, opt) \
3416 options->var_type= type; \
3417 options->def_value= (opt)->def_val; \
3418 options->min_value= (opt)->min_val; \
3419 options->max_value= (opt)->max_val; \
3420 options->block_size= (long) (opt)->blk_sz
3421
3422 #define OPTION_SET_LIMITS_DOUBLE(options, opt) \
3423 options->var_type= GET_DOUBLE; \
3424 options->def_value= (longlong) getopt_double2ulonglong((opt)->def_val); \
3425 options->min_value= (longlong) getopt_double2ulonglong((opt)->min_val); \
3426 options->max_value= getopt_double2ulonglong((opt)->max_val); \
3427 options->block_size= (long) (opt)->blk_sz;
3428
3429
plugin_opt_set_limits(struct my_option * options,const struct st_mysql_sys_var * opt)3430 static void plugin_opt_set_limits(struct my_option *options,
3431 const struct st_mysql_sys_var *opt)
3432 {
3433 options->sub_size= 0;
3434
3435 switch (opt->flags & (PLUGIN_VAR_TYPEMASK |
3436 PLUGIN_VAR_UNSIGNED | PLUGIN_VAR_THDLOCAL)) {
3437 /* global system variables */
3438 case PLUGIN_VAR_INT:
3439 OPTION_SET_LIMITS(GET_INT, options, (sysvar_int_t*) opt);
3440 break;
3441 case PLUGIN_VAR_INT | PLUGIN_VAR_UNSIGNED:
3442 OPTION_SET_LIMITS(GET_UINT, options, (sysvar_uint_t*) opt);
3443 break;
3444 case PLUGIN_VAR_LONG:
3445 OPTION_SET_LIMITS(GET_LONG, options, (sysvar_long_t*) opt);
3446 break;
3447 case PLUGIN_VAR_LONG | PLUGIN_VAR_UNSIGNED:
3448 OPTION_SET_LIMITS(GET_ULONG, options, (sysvar_ulong_t*) opt);
3449 break;
3450 case PLUGIN_VAR_LONGLONG:
3451 OPTION_SET_LIMITS(GET_LL, options, (sysvar_longlong_t*) opt);
3452 break;
3453 case PLUGIN_VAR_LONGLONG | PLUGIN_VAR_UNSIGNED:
3454 OPTION_SET_LIMITS(GET_ULL, options, (sysvar_ulonglong_t*) opt);
3455 break;
3456 case PLUGIN_VAR_ENUM:
3457 options->var_type= GET_ENUM;
3458 options->typelib= ((sysvar_enum_t*) opt)->typelib;
3459 options->def_value= ((sysvar_enum_t*) opt)->def_val;
3460 options->min_value= options->block_size= 0;
3461 options->max_value= options->typelib->count - 1;
3462 break;
3463 case PLUGIN_VAR_SET:
3464 options->var_type= GET_SET;
3465 options->typelib= ((sysvar_set_t*) opt)->typelib;
3466 options->def_value= ((sysvar_set_t*) opt)->def_val;
3467 options->min_value= options->block_size= 0;
3468 options->max_value= (1ULL << options->typelib->count) - 1;
3469 break;
3470 case PLUGIN_VAR_BOOL:
3471 options->var_type= GET_BOOL;
3472 options->def_value= ((sysvar_bool_t*) opt)->def_val;
3473 break;
3474 case PLUGIN_VAR_STR:
3475 options->var_type= ((opt->flags & PLUGIN_VAR_MEMALLOC) ?
3476 GET_STR_ALLOC : GET_STR);
3477 options->def_value= (intptr) ((sysvar_str_t*) opt)->def_val;
3478 break;
3479 case PLUGIN_VAR_DOUBLE:
3480 OPTION_SET_LIMITS_DOUBLE(options, (sysvar_double_t*) opt);
3481 break;
3482 /* threadlocal variables */
3483 case PLUGIN_VAR_INT | PLUGIN_VAR_THDLOCAL:
3484 OPTION_SET_LIMITS(GET_INT, options, (thdvar_int_t*) opt);
3485 break;
3486 case PLUGIN_VAR_INT | PLUGIN_VAR_UNSIGNED | PLUGIN_VAR_THDLOCAL:
3487 OPTION_SET_LIMITS(GET_UINT, options, (thdvar_uint_t*) opt);
3488 break;
3489 case PLUGIN_VAR_LONG | PLUGIN_VAR_THDLOCAL:
3490 OPTION_SET_LIMITS(GET_LONG, options, (thdvar_long_t*) opt);
3491 break;
3492 case PLUGIN_VAR_LONG | PLUGIN_VAR_UNSIGNED | PLUGIN_VAR_THDLOCAL:
3493 OPTION_SET_LIMITS(GET_ULONG, options, (thdvar_ulong_t*) opt);
3494 break;
3495 case PLUGIN_VAR_LONGLONG | PLUGIN_VAR_THDLOCAL:
3496 OPTION_SET_LIMITS(GET_LL, options, (thdvar_longlong_t*) opt);
3497 break;
3498 case PLUGIN_VAR_LONGLONG | PLUGIN_VAR_UNSIGNED | PLUGIN_VAR_THDLOCAL:
3499 OPTION_SET_LIMITS(GET_ULL, options, (thdvar_ulonglong_t*) opt);
3500 break;
3501 case PLUGIN_VAR_DOUBLE | PLUGIN_VAR_THDLOCAL:
3502 OPTION_SET_LIMITS_DOUBLE(options, (thdvar_double_t*) opt);
3503 break;
3504 case PLUGIN_VAR_ENUM | PLUGIN_VAR_THDLOCAL:
3505 options->var_type= GET_ENUM;
3506 options->typelib= ((thdvar_enum_t*) opt)->typelib;
3507 options->def_value= ((thdvar_enum_t*) opt)->def_val;
3508 options->min_value= options->block_size= 0;
3509 options->max_value= options->typelib->count - 1;
3510 break;
3511 case PLUGIN_VAR_SET | PLUGIN_VAR_THDLOCAL:
3512 options->var_type= GET_SET;
3513 options->typelib= ((thdvar_set_t*) opt)->typelib;
3514 options->def_value= ((thdvar_set_t*) opt)->def_val;
3515 options->min_value= options->block_size= 0;
3516 options->max_value= (1ULL << options->typelib->count) - 1;
3517 break;
3518 case PLUGIN_VAR_BOOL | PLUGIN_VAR_THDLOCAL:
3519 options->var_type= GET_BOOL;
3520 options->def_value= ((thdvar_bool_t*) opt)->def_val;
3521 break;
3522 case PLUGIN_VAR_STR | PLUGIN_VAR_THDLOCAL:
3523 options->var_type= ((opt->flags & PLUGIN_VAR_MEMALLOC) ?
3524 GET_STR_ALLOC : GET_STR);
3525 options->def_value= (intptr) ((thdvar_str_t*) opt)->def_val;
3526 break;
3527 default:
3528 DBUG_ASSERT(0);
3529 }
3530 options->arg_type= REQUIRED_ARG;
3531 if (opt->flags & PLUGIN_VAR_NOCMDARG)
3532 options->arg_type= NO_ARG;
3533 if (opt->flags & PLUGIN_VAR_OPCMDARG)
3534 options->arg_type= OPT_ARG;
3535 }
3536
3537 extern "C" my_bool get_one_plugin_option(int optid, const struct my_option *,
3538 char *);
3539
get_one_plugin_option(int optid MY_ATTRIBUTE ((unused)),const struct my_option * opt,char * argument)3540 my_bool get_one_plugin_option(int optid MY_ATTRIBUTE((unused)),
3541 const struct my_option *opt,
3542 char *argument)
3543 {
3544 return 0;
3545 }
3546
3547
3548 /**
3549 Creates a set of my_option objects associated with a specified plugin-
3550 handle.
3551
3552 @param mem_root Memory allocator to be used.
3553 @param tmp A pointer to a plugin handle
3554 @param[out] options A pointer to a pre-allocated static array
3555
3556 The set is stored in the pre-allocated static array supplied to the function.
3557 The size of the array is calculated as (number_of_plugin_varaibles*2+3). The
3558 reason is that each option can have a prefix '--plugin-' in addtion to the
3559 shorter form '--<plugin-name>'. There is also space allocated for
3560 terminating NULL pointers.
3561
3562 @return
3563 @retval -1 An error occurred
3564 @retval 0 Success
3565 */
3566
construct_options(MEM_ROOT * mem_root,struct st_plugin_int * tmp,my_option * options)3567 static int construct_options(MEM_ROOT *mem_root, struct st_plugin_int *tmp,
3568 my_option *options)
3569 {
3570 const char *plugin_name= tmp->plugin->name;
3571 const LEX_STRING plugin_dash = { C_STRING_WITH_LEN("plugin-") };
3572 uint plugin_name_len= strlen(plugin_name);
3573 uint optnamelen;
3574 const int max_comment_len= 180;
3575 char *comment= (char *) alloc_root(mem_root, max_comment_len + 1);
3576 char *optname;
3577
3578 int index= 0, offset= 0;
3579 st_mysql_sys_var *opt, **plugin_option;
3580 st_bookmark *v;
3581
3582 /** Used to circumvent the const attribute on my_option::name */
3583 char *plugin_name_ptr, *plugin_name_with_prefix_ptr;
3584
3585 DBUG_ENTER("construct_options");
3586
3587 plugin_name_ptr= (char*) alloc_root(mem_root, plugin_name_len + 1);
3588 strcpy(plugin_name_ptr, plugin_name);
3589 my_casedn_str(&my_charset_latin1, plugin_name_ptr);
3590 convert_underscore_to_dash(plugin_name_ptr, plugin_name_len);
3591 plugin_name_with_prefix_ptr= (char*) alloc_root(mem_root,
3592 plugin_name_len +
3593 plugin_dash.length + 1);
3594 strxmov(plugin_name_with_prefix_ptr, plugin_dash.str, plugin_name_ptr, NullS);
3595
3596 if (tmp->load_option != PLUGIN_FORCE &&
3597 tmp->load_option != PLUGIN_FORCE_PLUS_PERMANENT)
3598 {
3599 /* support --skip-plugin-foo syntax */
3600 options[0].name= plugin_name_ptr;
3601 options[1].name= plugin_name_with_prefix_ptr;
3602 options[0].id= 0;
3603 options[1].id= -1;
3604 options[0].var_type= options[1].var_type= GET_ENUM;
3605 options[0].arg_type= options[1].arg_type= OPT_ARG;
3606 options[0].def_value= options[1].def_value= 1; /* ON */
3607 options[0].typelib= options[1].typelib= &global_plugin_typelib;
3608
3609 strxnmov(comment, max_comment_len, "Enable or disable ", plugin_name,
3610 " plugin. Possible values are ON, OFF, FORCE (don't start "
3611 "if the plugin fails to load).", NullS);
3612 options[0].comment= comment;
3613 /*
3614 Allocate temporary space for the value of the tristate.
3615 This option will have a limited lifetime and is not used beyond
3616 server initialization.
3617 GET_ENUM value is an unsigned long integer.
3618 */
3619 options[0].value= options[1].value=
3620 (uchar **)alloc_root(mem_root, sizeof(ulong));
3621 *((ulong*) options[0].value)= (ulong) options[0].def_value;
3622
3623 options+= 2;
3624 }
3625
3626 if (!my_strcasecmp(&my_charset_latin1, plugin_name_ptr, "NDBCLUSTER"))
3627 {
3628 plugin_name_ptr= const_cast<char*>("ndb"); // Use legacy "ndb" prefix
3629 plugin_name_len= 3;
3630 }
3631
3632 /*
3633 Two passes as the 2nd pass will take pointer addresses for use
3634 by my_getopt and register_var() in the first pass uses realloc
3635 */
3636
3637 for (plugin_option= tmp->plugin->system_vars;
3638 plugin_option && *plugin_option; plugin_option++, index++)
3639 {
3640 opt= *plugin_option;
3641 if (!(opt->flags & PLUGIN_VAR_THDLOCAL))
3642 continue;
3643 if (!(register_var(plugin_name_ptr, opt->name, opt->flags)))
3644 continue;
3645 switch (opt->flags & PLUGIN_VAR_TYPEMASK) {
3646 case PLUGIN_VAR_BOOL:
3647 ((thdvar_bool_t *) opt)->resolve= mysql_sys_var_char;
3648 break;
3649 case PLUGIN_VAR_INT:
3650 ((thdvar_int_t *) opt)->resolve= mysql_sys_var_int;
3651 break;
3652 case PLUGIN_VAR_LONG:
3653 ((thdvar_long_t *) opt)->resolve= mysql_sys_var_long;
3654 break;
3655 case PLUGIN_VAR_LONGLONG:
3656 ((thdvar_longlong_t *) opt)->resolve= mysql_sys_var_longlong;
3657 break;
3658 case PLUGIN_VAR_STR:
3659 ((thdvar_str_t *) opt)->resolve= mysql_sys_var_str;
3660 break;
3661 case PLUGIN_VAR_ENUM:
3662 ((thdvar_enum_t *) opt)->resolve= mysql_sys_var_ulong;
3663 break;
3664 case PLUGIN_VAR_SET:
3665 ((thdvar_set_t *) opt)->resolve= mysql_sys_var_ulonglong;
3666 break;
3667 case PLUGIN_VAR_DOUBLE:
3668 ((thdvar_double_t *) opt)->resolve= mysql_sys_var_double;
3669 break;
3670 default:
3671 sql_print_error("Unknown variable type code 0x%x in plugin '%s'.",
3672 opt->flags, plugin_name);
3673 DBUG_RETURN(-1);
3674 };
3675 }
3676
3677 for (plugin_option= tmp->plugin->system_vars;
3678 plugin_option && *plugin_option; plugin_option++, index++)
3679 {
3680 switch ((opt= *plugin_option)->flags & PLUGIN_VAR_TYPEMASK) {
3681 case PLUGIN_VAR_BOOL:
3682 if (!opt->check)
3683 opt->check= check_func_bool;
3684 if (!opt->update)
3685 opt->update= update_func_bool;
3686 break;
3687 case PLUGIN_VAR_INT:
3688 if (!opt->check)
3689 opt->check= check_func_int;
3690 if (!opt->update)
3691 opt->update= update_func_int;
3692 break;
3693 case PLUGIN_VAR_LONG:
3694 if (!opt->check)
3695 opt->check= check_func_long;
3696 if (!opt->update)
3697 opt->update= update_func_long;
3698 break;
3699 case PLUGIN_VAR_LONGLONG:
3700 if (!opt->check)
3701 opt->check= check_func_longlong;
3702 if (!opt->update)
3703 opt->update= update_func_longlong;
3704 break;
3705 case PLUGIN_VAR_STR:
3706 if (!opt->check)
3707 opt->check= check_func_str;
3708 if (!opt->update)
3709 {
3710 opt->update= update_func_str;
3711 if (!(opt->flags & (PLUGIN_VAR_MEMALLOC | PLUGIN_VAR_READONLY)))
3712 {
3713 opt->flags|= PLUGIN_VAR_READONLY;
3714 sql_print_warning("Server variable %s of plugin %s was forced "
3715 "to be read-only: string variable without "
3716 "update_func and PLUGIN_VAR_MEMALLOC flag",
3717 opt->name, plugin_name);
3718 }
3719 }
3720 break;
3721 case PLUGIN_VAR_ENUM:
3722 if (!opt->check)
3723 opt->check= check_func_enum;
3724 if (!opt->update)
3725 opt->update= update_func_long;
3726 break;
3727 case PLUGIN_VAR_SET:
3728 if (!opt->check)
3729 opt->check= check_func_set;
3730 if (!opt->update)
3731 opt->update= update_func_longlong;
3732 break;
3733 case PLUGIN_VAR_DOUBLE:
3734 if (!opt->check)
3735 opt->check= check_func_double;
3736 if (!opt->update)
3737 opt->update= update_func_double;
3738 break;
3739 default:
3740 sql_print_error("Unknown variable type code 0x%x in plugin '%s'.",
3741 opt->flags, plugin_name);
3742 DBUG_RETURN(-1);
3743 }
3744
3745 if ((opt->flags & (PLUGIN_VAR_NOCMDOPT | PLUGIN_VAR_THDLOCAL))
3746 == PLUGIN_VAR_NOCMDOPT)
3747 continue;
3748
3749 if (!opt->name)
3750 {
3751 sql_print_error("Missing variable name in plugin '%s'.",
3752 plugin_name);
3753 DBUG_RETURN(-1);
3754 }
3755
3756 if (!(opt->flags & PLUGIN_VAR_THDLOCAL))
3757 {
3758 optnamelen= strlen(opt->name);
3759 optname= (char*) alloc_root(mem_root, plugin_name_len + optnamelen + 2);
3760 strxmov(optname, plugin_name_ptr, "-", opt->name, NullS);
3761 optnamelen= plugin_name_len + optnamelen + 1;
3762 }
3763 else
3764 {
3765 /* this should not fail because register_var should create entry */
3766 if (!(v= find_bookmark(plugin_name_ptr, opt->name, opt->flags)))
3767 {
3768 sql_print_error("Thread local variable '%s' not allocated "
3769 "in plugin '%s'.", opt->name, plugin_name);
3770 DBUG_RETURN(-1);
3771 }
3772
3773 *(int*)(opt + 1)= offset= v->offset;
3774
3775 if (opt->flags & PLUGIN_VAR_NOCMDOPT)
3776 continue;
3777
3778 optname= (char*) memdup_root(mem_root, v->key + 1,
3779 (optnamelen= v->name_len) + 1);
3780 }
3781
3782 convert_underscore_to_dash(optname, optnamelen);
3783
3784 options->name= optname;
3785 options->comment= opt->comment;
3786 options->app_type= opt;
3787 options->id= 0;
3788
3789 plugin_opt_set_limits(options, opt);
3790
3791 if (opt->flags & PLUGIN_VAR_THDLOCAL)
3792 options->value= options->u_max_value= (uchar**)
3793 (global_system_variables.dynamic_variables_ptr + offset);
3794 else
3795 options->value= options->u_max_value= *(uchar***) (opt + 1);
3796
3797 char *option_name_ptr;
3798 options[1]= options[0];
3799 options[1].id= -1;
3800 options[1].name= option_name_ptr= (char*) alloc_root(mem_root,
3801 plugin_dash.length +
3802 optnamelen + 1);
3803 options[1].comment= 0; /* Hidden from the help text */
3804 strxmov(option_name_ptr, plugin_dash.str, optname, NullS);
3805
3806 options+= 2;
3807 }
3808
3809 DBUG_RETURN(0);
3810 }
3811
3812
construct_help_options(MEM_ROOT * mem_root,struct st_plugin_int * p)3813 static my_option *construct_help_options(MEM_ROOT *mem_root,
3814 struct st_plugin_int *p)
3815 {
3816 st_mysql_sys_var **opt;
3817 my_option *opts;
3818 uint count= EXTRA_OPTIONS;
3819 DBUG_ENTER("construct_help_options");
3820
3821 for (opt= p->plugin->system_vars; opt && *opt; opt++, count+= 2)
3822 ;
3823
3824 if (!(opts= (my_option*) alloc_root(mem_root, sizeof(my_option) * count)))
3825 DBUG_RETURN(NULL);
3826
3827 memset(opts, 0, sizeof(my_option) * count);
3828
3829 /**
3830 some plugin variables (those that don't have PLUGIN_VAR_NOSYSVAR flag)
3831 have their names prefixed with the plugin name. Restore the names here
3832 to get the correct (not double-prefixed) help text.
3833 We won't need @@sysvars anymore and don't care about their proper names.
3834 */
3835 restore_pluginvar_names(p->system_vars);
3836
3837 if (construct_options(mem_root, p, opts))
3838 DBUG_RETURN(NULL);
3839
3840 DBUG_RETURN(opts);
3841 }
3842
3843
3844 /**
3845 Check option being used and raise deprecation warning if required.
3846
3847 @param optid ID of the option that was passed through command line
3848 @param opt List of options
3849 @argument Status of the option : Enable or Disable
3850
3851 A deprecation warning will be raised if --plugin-xxx type of option
3852 is used.
3853
3854 @return Always returns success as purpose of the function is to raise
3855 warning only.
3856 @retval 0 Success
3857 */
3858
check_if_option_is_deprecated(int optid,const struct my_option * opt,char * argument MY_ATTRIBUTE ((unused)))3859 static my_bool check_if_option_is_deprecated(int optid,
3860 const struct my_option *opt,
3861 char *argument MY_ATTRIBUTE((unused)))
3862 {
3863 if (optid == -1)
3864 {
3865 WARN_DEPRECATED(NULL, opt->name, (opt->name + strlen("plugin-")));
3866 }
3867 else if (!my_strcasecmp(&my_charset_latin1, opt->name, "innodb"))
3868 {
3869 sql_print_warning("The option innodb (skip-innodb) is deprecated and "
3870 "will be removed in a future release");
3871 }
3872
3873 return 0;
3874 }
3875
3876
3877 /**
3878 Create and register system variables supplied from the plugin and
3879 assigns initial values from corresponding command line arguments.
3880
3881 @param tmp_root Temporary scratch space
3882 @param[out] plugin Internal plugin structure
3883 @param argc Number of command line arguments
3884 @param argv Command line argument vector
3885
3886 The plugin will be updated with a policy on how to handle errors during
3887 initialization.
3888
3889 @note Requires that a write-lock is held on LOCK_system_variables_hash
3890
3891 @return How initialization of the plugin should be handled.
3892 @retval 0 Initialization should proceed.
3893 @retval 1 Plugin is disabled.
3894 @retval -1 An error has occurred.
3895 */
3896
test_plugin_options(MEM_ROOT * tmp_root,struct st_plugin_int * tmp,int * argc,char ** argv)3897 static int test_plugin_options(MEM_ROOT *tmp_root, struct st_plugin_int *tmp,
3898 int *argc, char **argv)
3899 {
3900 struct sys_var_chain chain= { NULL, NULL };
3901 bool disable_plugin;
3902 enum_plugin_load_option plugin_load_option= tmp->load_option;
3903
3904 MEM_ROOT *mem_root= alloc_root_inited(&tmp->mem_root) ?
3905 &tmp->mem_root : &plugin_mem_root;
3906 st_mysql_sys_var **opt;
3907 my_option *opts= NULL;
3908 LEX_STRING plugin_name;
3909 char *varname;
3910 int error;
3911 sys_var *v MY_ATTRIBUTE((unused));
3912 struct st_bookmark *var;
3913 uint len, count= EXTRA_OPTIONS;
3914 DBUG_ENTER("test_plugin_options");
3915 DBUG_ASSERT(tmp->plugin && tmp->name.str);
3916
3917 /*
3918 The 'federated' and 'ndbcluster' storage engines are always disabled by
3919 default.
3920 */
3921 if (!(my_strcasecmp(&my_charset_latin1, tmp->name.str, "federated") &&
3922 my_strcasecmp(&my_charset_latin1, tmp->name.str, "ndbcluster")))
3923 plugin_load_option= PLUGIN_OFF;
3924
3925 for (opt= tmp->plugin->system_vars; opt && *opt; opt++)
3926 count+= 2; /* --{plugin}-{optname} and --plugin-{plugin}-{optname} */
3927
3928 if (count > EXTRA_OPTIONS || (*argc > 1))
3929 {
3930 if (!(opts= (my_option*) alloc_root(tmp_root, sizeof(my_option) * count)))
3931 {
3932 sql_print_error("Out of memory for plugin '%s'.", tmp->name.str);
3933 DBUG_RETURN(-1);
3934 }
3935 memset(opts, 0, sizeof(my_option) * count);
3936
3937 if (construct_options(tmp_root, tmp, opts))
3938 {
3939 sql_print_error("Bad options for plugin '%s'.", tmp->name.str);
3940 DBUG_RETURN(-1);
3941 }
3942
3943 /*
3944 We adjust the default value to account for the hardcoded exceptions
3945 we have set for the federated and ndbcluster storage engines.
3946 */
3947 if (tmp->load_option != PLUGIN_FORCE &&
3948 tmp->load_option != PLUGIN_FORCE_PLUS_PERMANENT)
3949 opts[0].def_value= opts[1].def_value= plugin_load_option;
3950
3951 error= handle_options(argc, &argv, opts, check_if_option_is_deprecated);
3952 (*argc)++; /* add back one for the program name */
3953
3954 if (error)
3955 {
3956 sql_print_error("Parsing options for plugin '%s' failed.",
3957 tmp->name.str);
3958 goto err;
3959 }
3960 /*
3961 Set plugin loading policy from option value. First element in the option
3962 list is always the <plugin name> option value.
3963 */
3964 if (tmp->load_option != PLUGIN_FORCE &&
3965 tmp->load_option != PLUGIN_FORCE_PLUS_PERMANENT)
3966 plugin_load_option= (enum_plugin_load_option) *(ulong*) opts[0].value;
3967 }
3968
3969 disable_plugin= (plugin_load_option == PLUGIN_OFF);
3970 tmp->load_option= plugin_load_option;
3971
3972 /*
3973 If the plugin is disabled it should not be initialized.
3974 */
3975 if (disable_plugin)
3976 {
3977 if (log_warnings)
3978 sql_print_information("Plugin '%s' is disabled.",
3979 tmp->name.str);
3980 if (opts)
3981 my_cleanup_options(opts);
3982 DBUG_RETURN(1);
3983 }
3984
3985 if (!my_strcasecmp(&my_charset_latin1, tmp->name.str, "NDBCLUSTER"))
3986 {
3987 plugin_name.str= const_cast<char*>("ndb"); // Use legacy "ndb" prefix
3988 plugin_name.length= 3;
3989 }
3990 else
3991 plugin_name= tmp->name;
3992
3993 error= 1;
3994 for (opt= tmp->plugin->system_vars; opt && *opt; opt++)
3995 {
3996 st_mysql_sys_var *o;
3997 if (((o= *opt)->flags & PLUGIN_VAR_NOSYSVAR))
3998 continue;
3999 if ((var= find_bookmark(plugin_name.str, o->name, o->flags)))
4000 v= new (mem_root) sys_var_pluginvar(&chain, var->key + 1, o);
4001 else
4002 {
4003 len= plugin_name.length + strlen(o->name) + 2;
4004 varname= (char*) alloc_root(mem_root, len);
4005 strxmov(varname, plugin_name.str, "-", o->name, NullS);
4006 my_casedn_str(&my_charset_latin1, varname);
4007 convert_dash_to_underscore(varname, len-1);
4008 v= new (mem_root) sys_var_pluginvar(&chain, varname, o);
4009 }
4010 DBUG_ASSERT(v); /* check that an object was actually constructed */
4011 } /* end for */
4012 if (chain.first)
4013 {
4014 chain.last->next = NULL;
4015 if (mysql_add_sys_var_chain(chain.first))
4016 {
4017 sql_print_error("Plugin '%s' has conflicting system variables",
4018 tmp->name.str);
4019 goto err;
4020 }
4021 tmp->system_vars= chain.first;
4022 }
4023 DBUG_RETURN(0);
4024
4025 err:
4026 if (opts)
4027 my_cleanup_options(opts);
4028 DBUG_RETURN(error);
4029 }
4030
4031
4032 /****************************************************************************
4033 Help Verbose text with Plugin System Variables
4034 ****************************************************************************/
4035
4036
add_plugin_options(std::vector<my_option> * options,MEM_ROOT * mem_root)4037 void add_plugin_options(std::vector<my_option> *options, MEM_ROOT *mem_root)
4038 {
4039 struct st_plugin_int *p;
4040 my_option *opt;
4041
4042 if (!initialized)
4043 return;
4044
4045 for (uint idx= 0; idx < plugin_array.elements; idx++)
4046 {
4047 p= *dynamic_element(&plugin_array, idx, struct st_plugin_int **);
4048
4049 if (!(opt= construct_help_options(mem_root, p)))
4050 continue;
4051
4052 /* Only options with a non-NULL comment are displayed in help text */
4053 for (;opt->name; opt++)
4054 if (opt->comment)
4055 options->push_back(*opt);
4056 }
4057 }
4058
4059 /**
4060 Searches for a correctly loaded plugin of a particular type by name
4061
4062 @param plugin the name of the plugin we're looking for
4063 @param type type of the plugin (0-MYSQL_MAX_PLUGIN_TYPE_NUM)
4064 @return plugin, or NULL if not found
4065 */
plugin_find_by_type(LEX_STRING * plugin,int type)4066 struct st_plugin_int *plugin_find_by_type(LEX_STRING *plugin, int type)
4067 {
4068 st_plugin_int *ret;
4069 DBUG_ENTER("plugin_find_by_type");
4070
4071 ret= plugin_find_internal(plugin, type);
4072 DBUG_RETURN(ret && ret->state == PLUGIN_IS_READY ? ret : NULL);
4073 }
4074
4075
4076 /**
4077 Locks the plugin strucutres so calls to plugin_find_inner can be issued.
4078
4079 Must be followed by unlock_plugin_data.
4080 */
lock_plugin_data()4081 int lock_plugin_data()
4082 {
4083 DBUG_ENTER("lock_plugin_data");
4084 DBUG_RETURN(mysql_mutex_lock(&LOCK_plugin));
4085 }
4086
4087
4088 /**
4089 Unlocks the plugin strucutres as locked by lock_plugin_data()
4090 */
unlock_plugin_data()4091 int unlock_plugin_data()
4092 {
4093 DBUG_ENTER("unlock_plugin_data");
4094 DBUG_RETURN(mysql_mutex_unlock(&LOCK_plugin));
4095 }
4096
4097 /**
4098 Create deep copy of system_variables instance.
4099 */
4100 struct system_variables *
copy_system_variables(THD * thd,bool enable_plugins)4101 copy_system_variables(THD *thd, bool enable_plugins)
4102 {
4103 struct system_variables *dst;
4104 ulong idx;
4105
4106 DBUG_ASSERT(thd);
4107
4108 dst= (struct system_variables *)
4109 my_malloc(sizeof(struct system_variables), MYF(MY_WME | MY_FAE));
4110 *dst = thd->variables;
4111
4112 if (dst->dynamic_variables_ptr)
4113 {
4114 dst->dynamic_variables_ptr=
4115 (char *)my_malloc(dst->dynamic_variables_size, MYF(MY_WME | MY_FAE));
4116 memcpy(dst->dynamic_variables_ptr,
4117 thd->variables.dynamic_variables_ptr,
4118 thd->variables.dynamic_variables_size);
4119 }
4120
4121 dst->dynamic_variables_allocs= thd->variables.dynamic_variables_allocs;
4122 thd->variables.dynamic_variables_allocs= NULL;
4123
4124 mysql_rwlock_rdlock(&LOCK_system_variables_hash);
4125
4126 /*
4127 Iterate through newly copied vars of string type with MEMALLOC
4128 flag and strdup value.
4129 */
4130 for (idx= 0; idx < malloced_string_type_sysvars_bookmark_hash.records; idx++)
4131 {
4132 char **var;
4133 st_bookmark *v=
4134 (st_bookmark*)my_hash_element(&malloced_string_type_sysvars_bookmark_hash,
4135 idx);
4136
4137 if (thd->variables.dynamic_variables_ptr == NULL ||
4138 (uint) v->offset > thd->variables.dynamic_variables_head)
4139 continue;
4140
4141 var= (char **) (thd->variables.dynamic_variables_ptr + v->offset);
4142
4143 if (*var)
4144 {
4145 size_t length= strlen(*var) + 1;
4146 LIST *element;
4147 element= (LIST *) my_malloc(sizeof(LIST) + length, MYF(MY_FAE));
4148 memcpy(element + 1, *var, length);
4149 *var= (char *) (element + 1);
4150 thd->variables.dynamic_variables_allocs=
4151 list_add(thd->variables.dynamic_variables_allocs, element);
4152 }
4153 }
4154
4155 mysql_rwlock_unlock(&LOCK_system_variables_hash);
4156
4157 if (enable_plugins)
4158 {
4159 mysql_mutex_lock(&LOCK_plugin);
4160 dst->table_plugin=
4161 my_intern_plugin_lock(NULL, thd->variables.table_plugin);
4162 dst->temp_table_plugin=
4163 my_intern_plugin_lock(NULL, thd->variables.temp_table_plugin);
4164 mysql_mutex_unlock(&LOCK_plugin);
4165 }
4166
4167 return dst;
4168 }
4169
free_system_variables(struct system_variables * v,bool enable_plugins)4170 void free_system_variables(struct system_variables *v, bool enable_plugins)
4171 {
4172 DBUG_ASSERT(v);
4173
4174 if (enable_plugins)
4175 {
4176 mysql_mutex_lock(&LOCK_plugin);
4177 intern_plugin_unlock(NULL, v->table_plugin);
4178 intern_plugin_unlock(NULL, v->temp_table_plugin);
4179 mysql_mutex_unlock(&LOCK_plugin);
4180 }
4181
4182 plugin_var_memalloc_free(v);
4183
4184 my_free(v->dynamic_variables_ptr);
4185 }
4186