MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
sql_udf.cc
1 /* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
2 
3  This program is free software; you can redistribute it and/or modify
4  it under the terms of the GNU General Public License as published by
5  the Free Software Foundation; version 2 of the License.
6 
7  This program is distributed in the hope that it will be useful,
8  but WITHOUT ANY WARRANTY; without even the implied warranty of
9  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  GNU General Public License for more details.
11 
12  You should have received a copy of the GNU General Public License
13  along with this program; if not, write to the Free Software Foundation,
14  51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
15 
16 /* This implements 'user defined functions' */
17 
18 /*
19  Known bugs:
20 
21  Memory for functions is never freed!
22  Shared libraries are not closed before mysqld exits;
23  - This is because we can't be sure if some threads are using
24  a function.
25 
26  The bugs only affect applications that create and free a lot of
27  dynamic functions, so this shouldn't be a real problem.
28 */
29 
30 #include "sql_priv.h"
31 #include "unireg.h"
32 #include "sql_base.h" // close_mysql_tables
33 #include "sql_parse.h" // check_identifier_name
34 #include "sql_table.h" // write_bin_log
35 #include "records.h" // init_read_record, end_read_record
36 #include <my_pthread.h>
37 #include "lock.h" // MYSQL_LOCK_IGNORE_TIMEOUT
38 
39 #ifdef HAVE_DLOPEN
40 extern "C"
41 {
42 #include <stdarg.h>
43 #include <hash.h>
44 }
45 
46 static bool initialized = 0;
47 static MEM_ROOT mem;
48 static HASH udf_hash;
49 static mysql_rwlock_t THR_LOCK_udf;
50 
51 
52 static udf_func *add_udf(LEX_STRING *name, Item_result ret,
53  char *dl, Item_udftype typ);
54 static void del_udf(udf_func *udf);
55 static void *find_udf_dl(const char *dl);
56 
57 static char *init_syms(udf_func *tmp, char *nm)
58 {
59  char *end;
60 
61  if (!((tmp->func= (Udf_func_any) dlsym(tmp->dlhandle, tmp->name.str))))
62  return tmp->name.str;
63 
64  end=strmov(nm,tmp->name.str);
65 
66  if (tmp->type == UDFTYPE_AGGREGATE)
67  {
68  (void)strmov(end, "_clear");
69  if (!((tmp->func_clear= (Udf_func_clear) dlsym(tmp->dlhandle, nm))))
70  return nm;
71  (void)strmov(end, "_add");
72  if (!((tmp->func_add= (Udf_func_add) dlsym(tmp->dlhandle, nm))))
73  return nm;
74  }
75 
76  (void) strmov(end,"_deinit");
77  tmp->func_deinit= (Udf_func_deinit) dlsym(tmp->dlhandle, nm);
78 
79  (void) strmov(end,"_init");
80  tmp->func_init= (Udf_func_init) dlsym(tmp->dlhandle, nm);
81 
82  /*
83  to prefent loading "udf" from, e.g. libc.so
84  let's ensure that at least one auxiliary symbol is defined
85  */
86  if (!tmp->func_init && !tmp->func_deinit && tmp->type != UDFTYPE_AGGREGATE)
87  {
88  if (!opt_allow_suspicious_udfs)
89  return nm;
90  sql_print_warning(ER(ER_CANT_FIND_DL_ENTRY), nm);
91  }
92  return 0;
93 }
94 
95 
96 extern "C" uchar* get_hash_key(const uchar *buff, size_t *length,
97  my_bool not_used __attribute__((unused)))
98 {
99  udf_func *udf=(udf_func*) buff;
100  *length=(uint) udf->name.length;
101  return (uchar*) udf->name.str;
102 }
103 
104 #ifdef HAVE_PSI_INTERFACE
105 static PSI_rwlock_key key_rwlock_THR_LOCK_udf;
106 
107 static PSI_rwlock_info all_udf_rwlocks[]=
108 {
109  { &key_rwlock_THR_LOCK_udf, "THR_LOCK_udf", PSI_FLAG_GLOBAL}
110 };
111 
112 static void init_udf_psi_keys(void)
113 {
114  const char* category= "sql";
115  int count;
116 
117  count= array_elements(all_udf_rwlocks);
118  mysql_rwlock_register(category, all_udf_rwlocks, count);
119 }
120 #endif
121 
122 /*
123  Read all predeclared functions from mysql.func and accept all that
124  can be used.
125 */
126 
127 void udf_init()
128 {
129  udf_func *tmp;
130  TABLE_LIST tables;
131  READ_RECORD read_record_info;
132  TABLE *table;
133  int error;
134  DBUG_ENTER("ufd_init");
135  char db[]= "mysql"; /* A subject to casednstr, can't be constant */
136 
137  if (initialized)
138  DBUG_VOID_RETURN;
139 
140 #ifdef HAVE_PSI_INTERFACE
141  init_udf_psi_keys();
142 #endif
143 
144  mysql_rwlock_init(key_rwlock_THR_LOCK_udf, &THR_LOCK_udf);
145 
146  init_sql_alloc(&mem, UDF_ALLOC_BLOCK_SIZE, 0);
147  THD *new_thd = new THD;
148  if (!new_thd ||
149  my_hash_init(&udf_hash,system_charset_info,32,0,0,get_hash_key, NULL, 0))
150  {
151  sql_print_error("Can't allocate memory for udf structures");
152  my_hash_free(&udf_hash);
153  free_root(&mem,MYF(0));
154  delete new_thd;
155  DBUG_VOID_RETURN;
156  }
157  initialized = 1;
158  new_thd->thread_stack= (char*) &new_thd;
159  new_thd->store_globals();
160  new_thd->set_db(db, sizeof(db)-1);
161 
162  tables.init_one_table(db, sizeof(db)-1, "func", 4, "func", TL_READ);
163 
164  if (open_and_lock_tables(new_thd, &tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
165  {
166  DBUG_PRINT("error",("Can't open udf table"));
167  sql_print_error("Can't open the mysql.func table. Please "
168  "run mysql_upgrade to create it.");
169  goto end;
170  }
171 
172  table= tables.table;
173  if (init_read_record(&read_record_info, new_thd, table, NULL, 1, 1, FALSE))
174  goto end;
175  table->use_all_columns();
176  while (!(error= read_record_info.read_record(&read_record_info)))
177  {
178  DBUG_PRINT("info",("init udf record"));
180  name.str=get_field(&mem, table->field[0]);
181  name.length = (uint) strlen(name.str);
182  char *dl_name= get_field(&mem, table->field[2]);
183  bool new_dl=0;
184  Item_udftype udftype=UDFTYPE_FUNCTION;
185  if (table->s->fields >= 4) // New func table
186  udftype=(Item_udftype) table->field[3]->val_int();
187 
188  /*
189  Ensure that the .dll doesn't have a path
190  This is done to ensure that only approved dll from the system
191  directories are used (to make this even remotely secure).
192 
193  On windows we must check both FN_LIBCHAR and '/'.
194  */
195  if (check_valid_path(dl_name, strlen(dl_name)) ||
196  check_string_char_length(&name, "", NAME_CHAR_LEN,
197  system_charset_info, 1))
198  {
199  sql_print_error("Invalid row in mysql.func table for function '%.64s'",
200  name.str);
201  continue;
202  }
203 
204  if (!(tmp= add_udf(&name,(Item_result) table->field[1]->val_int(),
205  dl_name, udftype)))
206  {
207  sql_print_error("Can't alloc memory for udf function: '%.64s'", name.str);
208  continue;
209  }
210 
211  void *dl = find_udf_dl(tmp->dl);
212  if (dl == NULL)
213  {
214  char dlpath[FN_REFLEN];
215  strxnmov(dlpath, sizeof(dlpath) - 1, opt_plugin_dir, "/", tmp->dl,
216  NullS);
217  (void) unpack_filename(dlpath, dlpath);
218  if (!(dl= dlopen(dlpath, RTLD_NOW)))
219  {
220  const char *errmsg;
221  int error_number= dlopen_errno;
222  DLERROR_GENERATE(errmsg, error_number);
223 
224  /* Print warning to log */
225  sql_print_error(ER(ER_CANT_OPEN_LIBRARY), tmp->dl, error_number, errmsg);
226  /* Keep the udf in the hash so that we can remove it later */
227  continue;
228  }
229  new_dl=1;
230  }
231  tmp->dlhandle = dl;
232  {
233  char buf[NAME_LEN+16], *missing;
234  if ((missing= init_syms(tmp, buf)))
235  {
236  sql_print_error(ER(ER_CANT_FIND_DL_ENTRY), missing);
237  del_udf(tmp);
238  if (new_dl)
239  dlclose(dl);
240  }
241  }
242  }
243  if (error > 0)
244  sql_print_error("Got unknown error: %d", my_errno);
245  end_read_record(&read_record_info);
246  table->m_needs_reopen= TRUE; // Force close to free memory
247 
248 end:
249  close_mysql_tables(new_thd);
250  delete new_thd;
251  /* Remember that we don't have a THD */
252  my_pthread_setspecific_ptr(THR_THD, 0);
253  DBUG_VOID_RETURN;
254 }
255 
256 
257 void udf_free()
258 {
259  /* close all shared libraries */
260  DBUG_ENTER("udf_free");
261  for (uint idx=0 ; idx < udf_hash.records ; idx++)
262  {
263  udf_func *udf=(udf_func*) my_hash_element(&udf_hash,idx);
264  if (udf->dlhandle) // Not closed before
265  {
266  /* Mark all versions using the same handler as closed */
267  for (uint j=idx+1 ; j < udf_hash.records ; j++)
268  {
269  udf_func *tmp=(udf_func*) my_hash_element(&udf_hash,j);
270  if (udf->dlhandle == tmp->dlhandle)
271  tmp->dlhandle=0; // Already closed
272  }
273  dlclose(udf->dlhandle);
274  }
275  }
276  my_hash_free(&udf_hash);
277  free_root(&mem,MYF(0));
278  if (initialized)
279  {
280  initialized= 0;
281  mysql_rwlock_destroy(&THR_LOCK_udf);
282  }
283  DBUG_VOID_RETURN;
284 }
285 
286 
287 static void del_udf(udf_func *udf)
288 {
289  DBUG_ENTER("del_udf");
290  if (!--udf->usage_count)
291  {
292  my_hash_delete(&udf_hash,(uchar*) udf);
293  using_udf_functions=udf_hash.records != 0;
294  }
295  else
296  {
297  /*
298  The functions is in use ; Rename the functions instead of removing it.
299  The functions will be automaticly removed when the least threads
300  doesn't use it anymore
301  */
302  char *name= udf->name.str;
303  uint name_length=udf->name.length;
304  udf->name.str=(char*) "*";
305  udf->name.length=1;
306  my_hash_update(&udf_hash,(uchar*) udf,(uchar*) name,name_length);
307  }
308  DBUG_VOID_RETURN;
309 }
310 
311 
312 void free_udf(udf_func *udf)
313 {
314  DBUG_ENTER("free_udf");
315 
316  if (!initialized)
317  DBUG_VOID_RETURN;
318 
319  mysql_rwlock_wrlock(&THR_LOCK_udf);
320  if (!--udf->usage_count)
321  {
322  /*
323  We come here when someone has deleted the udf function
324  while another thread still was using the udf
325  */
326  my_hash_delete(&udf_hash,(uchar*) udf);
327  using_udf_functions=udf_hash.records != 0;
328  if (!find_udf_dl(udf->dl))
329  dlclose(udf->dlhandle);
330  }
331  mysql_rwlock_unlock(&THR_LOCK_udf);
332  DBUG_VOID_RETURN;
333 }
334 
335 
336 /* This is only called if using_udf_functions != 0 */
337 
338 udf_func *find_udf(const char *name,uint length,bool mark_used)
339 {
340  udf_func *udf=0;
341  DBUG_ENTER("find_udf");
342 
343  if (!initialized)
344  DBUG_RETURN(NULL);
345 
346  /* TODO: This should be changed to reader locks someday! */
347  if (mark_used)
348  mysql_rwlock_wrlock(&THR_LOCK_udf); /* Called during fix_fields */
349  else
350  mysql_rwlock_rdlock(&THR_LOCK_udf); /* Called during parsing */
351 
352  if ((udf=(udf_func*) my_hash_search(&udf_hash,(uchar*) name,
353  length ? length : (uint) strlen(name))))
354  {
355  if (!udf->dlhandle)
356  udf=0; // Could not be opened
357  else if (mark_used)
358  udf->usage_count++;
359  }
360  mysql_rwlock_unlock(&THR_LOCK_udf);
361  DBUG_RETURN(udf);
362 }
363 
364 
365 static void *find_udf_dl(const char *dl)
366 {
367  DBUG_ENTER("find_udf_dl");
368 
369  /*
370  Because only the function name is hashed, we have to search trough
371  all rows to find the dl.
372  */
373  for (uint idx=0 ; idx < udf_hash.records ; idx++)
374  {
375  udf_func *udf=(udf_func*) my_hash_element(&udf_hash,idx);
376  if (!strcmp(dl, udf->dl) && udf->dlhandle != NULL)
377  DBUG_RETURN(udf->dlhandle);
378  }
379  DBUG_RETURN(0);
380 }
381 
382 
383 /* Assume that name && dl is already allocated */
384 
385 static udf_func *add_udf(LEX_STRING *name, Item_result ret, char *dl,
386  Item_udftype type)
387 {
388  if (!name || !dl || !(uint) type || (uint) type > (uint) UDFTYPE_AGGREGATE)
389  return 0;
390  udf_func *tmp= (udf_func*) alloc_root(&mem, sizeof(udf_func));
391  if (!tmp)
392  return 0;
393  memset(tmp, 0, sizeof(*tmp));
394  tmp->name = *name; //dup !!
395  tmp->dl = dl;
396  tmp->returns = ret;
397  tmp->type = type;
398  tmp->usage_count=1;
399  if (my_hash_insert(&udf_hash,(uchar*) tmp))
400  return 0;
401  using_udf_functions=1;
402  return tmp;
403 }
404 
405 
414 int mysql_create_function(THD *thd,udf_func *udf)
415 {
416  int error;
417  void *dl=0;
418  bool new_dl=0;
419  TABLE *table;
420  TABLE_LIST tables;
421  udf_func *u_d;
422  bool save_binlog_row_based;
423  DBUG_ENTER("mysql_create_function");
424 
425  if (!initialized)
426  {
427  if (opt_noacl)
428  my_error(ER_CANT_INITIALIZE_UDF, MYF(0),
429  udf->name.str,
430  "UDFs are unavailable with the --skip-grant-tables option");
431  else
432  my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
433  DBUG_RETURN(1);
434  }
435 
436  /*
437  Ensure that the .dll doesn't have a path
438  This is done to ensure that only approved dll from the system
439  directories are used (to make this even remotely secure).
440  */
441  if (check_valid_path(udf->dl, strlen(udf->dl)))
442  {
443  my_message(ER_UDF_NO_PATHS, ER(ER_UDF_NO_PATHS), MYF(0));
444  DBUG_RETURN(1);
445  }
446  if (check_string_char_length(&udf->name, "", NAME_CHAR_LEN,
447  system_charset_info, 1))
448  {
449  my_error(ER_TOO_LONG_IDENT, MYF(0), udf->name.str);
450  DBUG_RETURN(1);
451  }
452 
453  tables.init_one_table("mysql", 5, "func", 4, "func", TL_WRITE);
454  if (!(table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT)))
455  DBUG_RETURN(1);
456 
457  /*
458  Turn off row binlogging of this statement and use statement-based
459  so that all supporting tables are updated for CREATE FUNCTION command.
460  */
461  if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
462  thd->clear_current_stmt_binlog_format_row();
463 
464  mysql_rwlock_wrlock(&THR_LOCK_udf);
465  if ((my_hash_search(&udf_hash,(uchar*) udf->name.str, udf->name.length)))
466  {
467  my_error(ER_UDF_EXISTS, MYF(0), udf->name.str);
468  goto err;
469  }
470  if (!(dl = find_udf_dl(udf->dl)))
471  {
472  char dlpath[FN_REFLEN];
473  strxnmov(dlpath, sizeof(dlpath) - 1, opt_plugin_dir, "/", udf->dl, NullS);
474  (void) unpack_filename(dlpath, dlpath);
475 
476  if (!(dl = dlopen(dlpath, RTLD_NOW)))
477  {
478  const char *errmsg;
479  int error_number= dlopen_errno;
480  DLERROR_GENERATE(errmsg, error_number);
481 
482  DBUG_PRINT("error",("dlopen of %s failed, error: %d (%s)",
483  udf->dl, error_number, errmsg));
484  my_error(ER_CANT_OPEN_LIBRARY, MYF(0),
485  udf->dl, error_number, errmsg);
486  goto err;
487  }
488  new_dl=1;
489  }
490  udf->dlhandle=dl;
491  {
492  char buf[NAME_LEN+16], *missing;
493  if ((missing= init_syms(udf, buf)))
494  {
495  my_error(ER_CANT_FIND_DL_ENTRY, MYF(0), missing);
496  goto err;
497  }
498  }
499  udf->name.str=strdup_root(&mem,udf->name.str);
500  udf->dl=strdup_root(&mem,udf->dl);
501  if (!(u_d=add_udf(&udf->name,udf->returns,udf->dl,udf->type)))
502  goto err;
503  u_d->dlhandle = dl;
504  u_d->func=udf->func;
505  u_d->func_init=udf->func_init;
506  u_d->func_deinit=udf->func_deinit;
507  u_d->func_clear=udf->func_clear;
508  u_d->func_add=udf->func_add;
509 
510  /* create entry in mysql.func table */
511 
512  table->use_all_columns();
513  restore_record(table, s->default_values); // Default values for fields
514  table->field[0]->store(u_d->name.str, u_d->name.length, system_charset_info);
515  table->field[1]->store((longlong) u_d->returns, TRUE);
516  table->field[2]->store(u_d->dl,(uint) strlen(u_d->dl), system_charset_info);
517  if (table->s->fields >= 4) // If not old func format
518  table->field[3]->store((longlong) u_d->type, TRUE);
519  error = table->file->ha_write_row(table->record[0]);
520 
521  if (error)
522  {
523  char errbuf[MYSYS_STRERROR_SIZE];
524  my_error(ER_ERROR_ON_WRITE, MYF(0), "mysql.func", error,
525  my_strerror(errbuf, sizeof(errbuf), error));
526  del_udf(u_d);
527  goto err;
528  }
529  mysql_rwlock_unlock(&THR_LOCK_udf);
530 
531  /* Binlog the create function. */
532  if (write_bin_log(thd, TRUE, thd->query(), thd->query_length()))
533  {
534  /* Restore the state of binlog format */
535  DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
536  if (save_binlog_row_based)
537  thd->set_current_stmt_binlog_format_row();
538  DBUG_RETURN(1);
539  }
540  /* Restore the state of binlog format */
541  DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
542  if (save_binlog_row_based)
543  thd->set_current_stmt_binlog_format_row();
544  DBUG_RETURN(0);
545 
546  err:
547  if (new_dl)
548  dlclose(dl);
549  mysql_rwlock_unlock(&THR_LOCK_udf);
550  /* Restore the state of binlog format */
551  DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
552  if (save_binlog_row_based)
553  thd->set_current_stmt_binlog_format_row();
554  DBUG_RETURN(1);
555 }
556 
557 
558 int mysql_drop_function(THD *thd,const LEX_STRING *udf_name)
559 {
560  TABLE *table;
561  TABLE_LIST tables;
562  udf_func *udf;
563  char *exact_name_str;
564  uint exact_name_len;
565  bool save_binlog_row_based;
566  int error= 1;
567  DBUG_ENTER("mysql_drop_function");
568 
569  if (!initialized)
570  {
571  if (opt_noacl)
572  my_error(ER_FUNCTION_NOT_DEFINED, MYF(0), udf_name->str);
573  else
574  my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
575  DBUG_RETURN(1);
576  }
577 
578  tables.init_one_table("mysql", 5, "func", 4, "func", TL_WRITE);
579  if (!(table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT)))
580  DBUG_RETURN(1);
581 
582  /*
583  Turn off row binlogging of this statement and use statement-based
584  so that all supporting tables are updated for DROP FUNCTION command.
585  */
586  if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
587  thd->clear_current_stmt_binlog_format_row();
588 
589  mysql_rwlock_wrlock(&THR_LOCK_udf);
590  if (!(udf=(udf_func*) my_hash_search(&udf_hash,(uchar*) udf_name->str,
591  (uint) udf_name->length)))
592  {
593  my_error(ER_FUNCTION_NOT_DEFINED, MYF(0), udf_name->str);
594  mysql_rwlock_unlock(&THR_LOCK_udf);
595  goto exit;
596  }
597  exact_name_str= udf->name.str;
598  exact_name_len= udf->name.length;
599  del_udf(udf);
600  /*
601  Close the handle if this was function that was found during boot or
602  CREATE FUNCTION and it's not in use by any other udf function
603  */
604  if (udf->dlhandle && !find_udf_dl(udf->dl))
605  dlclose(udf->dlhandle);
606  mysql_rwlock_unlock(&THR_LOCK_udf);
607 
608  table->use_all_columns();
609  table->field[0]->store(exact_name_str, exact_name_len, &my_charset_bin);
610  if (!table->file->ha_index_read_idx_map(table->record[0], 0,
611  (uchar*) table->field[0]->ptr,
612  HA_WHOLE_KEY,
613  HA_READ_KEY_EXACT))
614  {
615  int delete_err;
616  if ((delete_err = table->file->ha_delete_row(table->record[0])))
617  table->file->print_error(delete_err, MYF(0));
618  }
619 
620  /*
621  Binlog the drop function. Keep the table open and locked
622  while binlogging, to avoid binlog inconsistency.
623  */
624  if (!write_bin_log(thd, TRUE, thd->query(), thd->query_length()))
625  error= 0;
626 exit:
627  /* Restore the state of binlog format */
628  DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
629  if (save_binlog_row_based)
630  thd->set_current_stmt_binlog_format_row();
631  DBUG_RETURN(error);
632 }
633 
634 #endif /* HAVE_DLOPEN */