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