1 /* Copyright (c) 2000, 2012, Oracle and/or its affiliates.
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
14    Foundation, Inc., 51 Franklin St, Fifth Floor, 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 #ifdef USE_PRAGMA_IMPLEMENTATION
31 #pragma implementation				// gcc: Class implementation
32 #endif
33 
34 #include "mariadb.h"
35 #include "sql_priv.h"
36 #include "unireg.h"
37 #include "sql_base.h"                           // close_mysql_tables
38 #include "sql_parse.h"                        // check_identifier_name
39 #include "sql_table.h"                        // write_bin_log
40 #include "records.h"          // init_read_record, end_read_record
41 #include <my_pthread.h>
42 #include "lock.h"                               // MYSQL_LOCK_IGNORE_TIMEOUT
43 
44 #ifdef HAVE_DLOPEN
45 extern "C"
46 {
47 #include <stdarg.h>
48 #include <hash.h>
49 }
50 
51 static bool initialized = 0;
52 static MEM_ROOT mem;
53 static HASH udf_hash;
54 static mysql_rwlock_t THR_LOCK_udf;
55 static LEX_CSTRING MYSQL_FUNC_NAME= {STRING_WITH_LEN("func") };
56 
57 static udf_func *add_udf(LEX_CSTRING *name, Item_result ret,
58                          const char *dl, Item_udftype typ);
59 static void del_udf(udf_func *udf);
60 static void *find_udf_dl(const char *dl);
61 static bool find_udf_everywhere(THD* thd, const LEX_CSTRING &name,
62                                 TABLE *table);
63 
init_syms(udf_func * tmp,char * nm)64 static const char *init_syms(udf_func *tmp, char *nm)
65 {
66   char *end;
67 
68   if (!((tmp->func= (Udf_func_any) dlsym(tmp->dlhandle, tmp->name.str))))
69     return tmp->name.str;
70 
71   end=strmov(nm,tmp->name.str);
72 
73   if (tmp->type == UDFTYPE_AGGREGATE)
74   {
75     (void)strmov(end, "_clear");
76     if (!((tmp->func_clear= (Udf_func_clear) dlsym(tmp->dlhandle, nm))))
77       return nm;
78     (void)strmov(end, "_add");
79     if (!((tmp->func_add= (Udf_func_add) dlsym(tmp->dlhandle, nm))))
80       return nm;
81     (void)strmov(end, "_remove");
82     tmp->func_remove= (Udf_func_add) dlsym(tmp->dlhandle, nm);
83   }
84 
85   (void) strmov(end,"_deinit");
86   tmp->func_deinit= (Udf_func_deinit) dlsym(tmp->dlhandle, nm);
87 
88   (void) strmov(end,"_init");
89   tmp->func_init= (Udf_func_init) dlsym(tmp->dlhandle, nm);
90 
91   /*
92     to prefent loading "udf" from, e.g. libc.so
93     let's ensure that at least one auxiliary symbol is defined
94   */
95   if (!tmp->func_init && !tmp->func_deinit && tmp->type != UDFTYPE_AGGREGATE)
96   {
97     THD *thd= current_thd;
98     if (!opt_allow_suspicious_udfs)
99       return nm;
100     if (thd->variables.log_warnings)
101       sql_print_warning(ER_THD(thd, ER_CANT_FIND_DL_ENTRY), nm);
102   }
103   return 0;
104 }
105 
106 
get_hash_key(const uchar * buff,size_t * length,my_bool not_used)107 extern "C" uchar* get_hash_key(const uchar *buff, size_t *length,
108 			      my_bool not_used __attribute__((unused)))
109 {
110   udf_func *udf=(udf_func*) buff;
111   *length=(uint) udf->name.length;
112   return (uchar*) udf->name.str;
113 }
114 
115 #ifdef HAVE_PSI_INTERFACE
116 static PSI_rwlock_key key_rwlock_THR_LOCK_udf;
117 
118 static PSI_rwlock_info all_udf_rwlocks[]=
119 {
120   { &key_rwlock_THR_LOCK_udf, "THR_LOCK_udf", PSI_FLAG_GLOBAL}
121 };
122 
init_udf_psi_keys(void)123 static void init_udf_psi_keys(void)
124 {
125   const char* category= "sql";
126   int count;
127 
128   if (PSI_server == NULL)
129     return;
130 
131   count= array_elements(all_udf_rwlocks);
132   PSI_server->register_rwlock(category, all_udf_rwlocks, count);
133 }
134 #endif
135 
136 /*
137   Read all predeclared functions from mysql.func and accept all that
138   can be used.
139 */
140 
udf_init()141 void udf_init()
142 {
143   udf_func *tmp;
144   TABLE_LIST tables;
145   READ_RECORD read_record_info;
146   TABLE *table;
147   int error;
148   DBUG_ENTER("ufd_init");
149 
150   if (initialized || opt_noacl)
151     DBUG_VOID_RETURN;
152 
153 #ifdef HAVE_PSI_INTERFACE
154   init_udf_psi_keys();
155 #endif
156 
157   mysql_rwlock_init(key_rwlock_THR_LOCK_udf, &THR_LOCK_udf);
158 
159   init_sql_alloc(&mem, "udf", UDF_ALLOC_BLOCK_SIZE, 0, MYF(0));
160   THD *new_thd = new THD(0);
161   if (!new_thd ||
162       my_hash_init(&udf_hash,system_charset_info,32,0,0,get_hash_key, NULL, 0))
163   {
164     sql_print_error("Can't allocate memory for udf structures");
165     my_hash_free(&udf_hash);
166     free_root(&mem,MYF(0));
167     delete new_thd;
168     DBUG_VOID_RETURN;
169   }
170   initialized = 1;
171   new_thd->thread_stack= (char*) &new_thd;
172   new_thd->store_globals();
173   new_thd->set_db(&MYSQL_SCHEMA_NAME);
174 
175   tables.init_one_table(&new_thd->db, &MYSQL_FUNC_NAME, 0, TL_READ);
176 
177   if (open_and_lock_tables(new_thd, &tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
178   {
179     DBUG_PRINT("error",("Can't open udf table"));
180     sql_print_error("Can't open the mysql.func table. Please "
181                     "run mysql_upgrade to create it.");
182     goto end;
183   }
184 
185   table= tables.table;
186   if (init_read_record(&read_record_info, new_thd, table, NULL, NULL, 1, 0,
187                        FALSE))
188   {
189     sql_print_error("Could not initialize init_read_record; udf's not "
190                     "loaded");
191     goto end;
192   }
193 
194   table->use_all_columns();
195   while (!(error= read_record_info.read_record()))
196   {
197     DBUG_PRINT("info",("init udf record"));
198     LEX_CSTRING name;
199     name.str=get_field(&mem, table->field[0]);
200     name.length = (uint) safe_strlen(name.str);
201     char *dl_name= get_field(&mem, table->field[2]);
202     bool new_dl=0;
203     Item_udftype udftype=UDFTYPE_FUNCTION;
204     if (table->s->fields >= 4)			// New func table
205       udftype=(Item_udftype) table->field[3]->val_int();
206 
207     /*
208       Ensure that the .dll doesn't have a path
209       This is done to ensure that only approved dll from the system
210       directories are used (to make this even remotely secure).
211 
212       On windows we must check both FN_LIBCHAR and '/'.
213     */
214     if (!name.str || !dl_name || check_valid_path(dl_name, strlen(dl_name)) ||
215         check_string_char_length(&name, 0, NAME_CHAR_LEN,
216                                  system_charset_info, 1))
217     {
218       sql_print_error("Invalid row in mysql.func table for function '%.64s'",
219                       safe_str(name.str));
220       continue;
221     }
222 
223     if (!(tmp= add_udf(&name,(Item_result) table->field[1]->val_int(),
224                        dl_name, udftype)))
225     {
226       sql_print_error("Can't alloc memory for udf function: '%.64s'", name.str);
227       continue;
228     }
229 
230     void *dl = find_udf_dl(tmp->dl);
231     if (dl == NULL)
232     {
233       char dlpath[FN_REFLEN];
234       strxnmov(dlpath, sizeof(dlpath) - 1, opt_plugin_dir, "/", tmp->dl, NullS);
235       (void) unpack_filename(dlpath, dlpath);
236       if (!(dl= dlopen(dlpath, RTLD_NOW)))
237       {
238 	/* Print warning to log */
239         sql_print_error(ER_THD(new_thd, ER_CANT_OPEN_LIBRARY),
240                         tmp->dl, errno, my_dlerror(dlpath));
241 	/* Keep the udf in the hash so that we can remove it later */
242 	continue;
243       }
244       new_dl=1;
245     }
246     tmp->dlhandle = dl;
247     {
248       char buf[SAFE_NAME_LEN+16];
249       const char *missing;
250       if ((missing= init_syms(tmp, buf)))
251       {
252         sql_print_error(ER_THD(new_thd, ER_CANT_FIND_DL_ENTRY), missing);
253         del_udf(tmp);
254         if (new_dl)
255           dlclose(dl);
256       }
257     }
258   }
259   if (unlikely(error > 0))
260     sql_print_error("Got unknown error: %d", my_errno);
261   end_read_record(&read_record_info);
262 
263   // Force close to free memory
264   table->mark_table_for_reopen();
265 
266 end:
267   close_mysql_tables(new_thd);
268   delete new_thd;
269   DBUG_VOID_RETURN;
270 }
271 
272 
udf_free()273 void udf_free()
274 {
275   /* close all shared libraries */
276   DBUG_ENTER("udf_free");
277   if (opt_noacl)
278     DBUG_VOID_RETURN;
279   for (uint idx=0 ; idx < udf_hash.records ; idx++)
280   {
281     udf_func *udf=(udf_func*) my_hash_element(&udf_hash,idx);
282     if (udf->dlhandle)				// Not closed before
283     {
284       /* Mark all versions using the same handler as closed */
285       for (uint j=idx+1 ;  j < udf_hash.records ; j++)
286       {
287 	udf_func *tmp=(udf_func*) my_hash_element(&udf_hash,j);
288 	if (udf->dlhandle == tmp->dlhandle)
289 	  tmp->dlhandle=0;			// Already closed
290       }
291       dlclose(udf->dlhandle);
292     }
293   }
294   my_hash_free(&udf_hash);
295   free_root(&mem,MYF(0));
296   if (initialized)
297   {
298     initialized= 0;
299     mysql_rwlock_destroy(&THR_LOCK_udf);
300   }
301   DBUG_VOID_RETURN;
302 }
303 
304 
del_udf(udf_func * udf)305 static void del_udf(udf_func *udf)
306 {
307   DBUG_ENTER("del_udf");
308   if (!--udf->usage_count)
309   {
310     my_hash_delete(&udf_hash,(uchar*) udf);
311     using_udf_functions=udf_hash.records != 0;
312   }
313   else
314   {
315     /*
316       The functions is in use ; Rename the functions instead of removing it.
317       The functions will be automaticly removed when the least threads
318       doesn't use it anymore
319     */
320     const char *name= udf->name.str;
321     size_t name_length=udf->name.length;
322     udf->name.str= "*";
323     udf->name.length=1;
324     my_hash_update(&udf_hash,(uchar*) udf,(uchar*) name,name_length);
325   }
326   DBUG_VOID_RETURN;
327 }
328 
329 
free_udf(udf_func * udf)330 void free_udf(udf_func *udf)
331 {
332   DBUG_ENTER("free_udf");
333 
334   if (!initialized)
335     DBUG_VOID_RETURN;
336 
337   mysql_rwlock_wrlock(&THR_LOCK_udf);
338   if (!--udf->usage_count)
339   {
340     /*
341       We come here when someone has deleted the udf function
342       while another thread still was using the udf
343     */
344     my_hash_delete(&udf_hash,(uchar*) udf);
345     using_udf_functions=udf_hash.records != 0;
346     if (!find_udf_dl(udf->dl))
347       dlclose(udf->dlhandle);
348   }
349   mysql_rwlock_unlock(&THR_LOCK_udf);
350   DBUG_VOID_RETURN;
351 }
352 
353 
354 /* This is only called if using_udf_functions != 0 */
355 
find_udf(const char * name,size_t length,bool mark_used)356 udf_func *find_udf(const char *name,size_t length,bool mark_used)
357 {
358   udf_func *udf=0;
359   DBUG_ENTER("find_udf");
360   DBUG_ASSERT(strlen(name) == length);
361 
362   if (!initialized)
363     DBUG_RETURN(NULL);
364 
365   DEBUG_SYNC(current_thd, "find_udf_before_lock");
366   /* TODO: This should be changed to reader locks someday! */
367   if (mark_used)
368     mysql_rwlock_wrlock(&THR_LOCK_udf);  /* Called during fix_fields */
369   else
370     mysql_rwlock_rdlock(&THR_LOCK_udf);  /* Called during parsing */
371 
372   if ((udf=(udf_func*) my_hash_search(&udf_hash,(uchar*) name, length)))
373   {
374     if (!udf->dlhandle)
375       udf=0;					// Could not be opened
376     else if (mark_used)
377       udf->usage_count++;
378   }
379   mysql_rwlock_unlock(&THR_LOCK_udf);
380   DBUG_RETURN(udf);
381 }
382 
383 
find_udf_dl(const char * dl)384 static void *find_udf_dl(const char *dl)
385 {
386   DBUG_ENTER("find_udf_dl");
387 
388   /*
389     Because only the function name is hashed, we have to search trough
390     all rows to find the dl.
391   */
392   for (uint idx=0 ; idx < udf_hash.records ; idx++)
393   {
394     udf_func *udf=(udf_func*) my_hash_element(&udf_hash,idx);
395     if (!strcmp(dl, udf->dl) && udf->dlhandle != NULL)
396       DBUG_RETURN(udf->dlhandle);
397   }
398   DBUG_RETURN(0);
399 }
400 
401 
402 /* Assume that name && dl is already allocated */
403 
add_udf(LEX_CSTRING * name,Item_result ret,const char * dl,Item_udftype type)404 static udf_func *add_udf(LEX_CSTRING *name, Item_result ret, const char *dl,
405 			 Item_udftype type)
406 {
407   if (!name || !dl || !(uint) type || (uint) type > (uint) UDFTYPE_AGGREGATE)
408     return 0;
409   udf_func *tmp= (udf_func*) alloc_root(&mem, sizeof(udf_func));
410   if (!tmp)
411     return 0;
412   bzero((char*) tmp,sizeof(*tmp));
413   tmp->name = *name; //dup !!
414   tmp->dl = dl;
415   tmp->returns = ret;
416   tmp->type = type;
417   tmp->usage_count=1;
418   if (my_hash_insert(&udf_hash,(uchar*)  tmp))
419     return 0;
420   using_udf_functions=1;
421   return tmp;
422 }
423 
424 /**
425   Find record with the udf in the udf func table
426 
427   @param exact_name      udf name
428   @param table           table of mysql.func
429 
430   @retval TRUE  found
431   @retral FALSE not found
432 */
433 
find_udf_in_table(const LEX_CSTRING & exact_name,TABLE * table)434 static bool find_udf_in_table(const LEX_CSTRING &exact_name, TABLE *table)
435 {
436   table->use_all_columns();
437   table->field[0]->store(exact_name.str, exact_name.length, &my_charset_bin);
438   return (!table->file->ha_index_read_idx_map(table->record[0], 0,
439                                               (uchar*) table->field[0]->ptr,
440                                               HA_WHOLE_KEY,
441                                               HA_READ_KEY_EXACT));
442 }
443 
remove_udf_in_table(const LEX_CSTRING & exact_name,TABLE * table)444 static bool remove_udf_in_table(const LEX_CSTRING &exact_name, TABLE *table)
445 {
446   if (find_udf_in_table(exact_name, table))
447   {
448     int error;
449     if ((error= table->file->ha_delete_row(table->record[0])))
450     {
451       table->file->print_error(error, MYF(0));
452       return TRUE;
453     }
454   }
455   return FALSE;
456 }
457 
458 
459 /*
460   Drop user defined function.
461 
462   @param thd    Thread handler.
463   @param udf    Existing udf_func pointer which is to be deleted.
464   @param table  mysql.func table reference (opened and locked)
465 
466   Assumption
467 
468   - udf is not null.
469   - table is already opened and locked
470 */
mysql_drop_function_internal(THD * thd,udf_func * udf,TABLE * table)471 static int mysql_drop_function_internal(THD *thd, udf_func *udf, TABLE *table)
472 {
473   DBUG_ENTER("mysql_drop_function_internal");
474 
475   const LEX_CSTRING exact_name= udf->name;
476 
477   del_udf(udf);
478   /*
479     Close the handle if this was function that was found during boot or
480     CREATE FUNCTION and it's not in use by any other udf function
481   */
482   if (udf->dlhandle && !find_udf_dl(udf->dl))
483     dlclose(udf->dlhandle);
484 
485   if (!table)
486     DBUG_RETURN(1);
487 
488   bool ret= remove_udf_in_table(exact_name, table);
489   DBUG_RETURN(ret);
490 }
491 
492 
open_udf_func_table(THD * thd)493 static TABLE *open_udf_func_table(THD *thd)
494 {
495   TABLE_LIST tables;
496   tables.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_FUNC_NAME,
497                         &MYSQL_FUNC_NAME, TL_WRITE);
498   return open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT);
499 }
500 
501 
502 /**
503   Create a user defined function.
504 
505   @note Like implementations of other DDL/DML in MySQL, this function
506   relies on the caller to close the thread tables. This is done in the
507   end of dispatch_command().
508 */
509 
mysql_create_function(THD * thd,udf_func * udf)510 int mysql_create_function(THD *thd,udf_func *udf)
511 {
512   int error;
513   void *dl=0;
514   bool new_dl=0;
515   TABLE *table;
516   TABLE_LIST tables;
517   udf_func *u_d;
518   DBUG_ENTER("mysql_create_function");
519 
520   if (!initialized)
521   {
522     if (opt_noacl)
523       my_error(ER_CANT_INITIALIZE_UDF, MYF(0),
524                udf->name.str,
525                "UDFs are unavailable with the --skip-grant-tables option");
526     else
527       my_message(ER_OUT_OF_RESOURCES, ER_THD(thd, ER_OUT_OF_RESOURCES),
528                  MYF(0));
529     DBUG_RETURN(1);
530   }
531 
532   /*
533     Ensure that the .dll doesn't have a path
534     This is done to ensure that only approved dll from the system
535     directories are used (to make this even remotely secure).
536   */
537   if (check_valid_path(udf->dl, strlen(udf->dl)))
538   {
539     my_message(ER_UDF_NO_PATHS, ER_THD(thd, ER_UDF_NO_PATHS), MYF(0));
540     DBUG_RETURN(1);
541   }
542   if (check_ident_length(&udf->name))
543     DBUG_RETURN(1);
544 
545   table= open_udf_func_table(thd);
546 
547   mysql_rwlock_wrlock(&THR_LOCK_udf);
548   DEBUG_SYNC(current_thd, "mysql_create_function_after_lock");
549   if ((u_d= (udf_func*) my_hash_search(&udf_hash, (uchar*) udf->name.str,
550                                                   udf->name.length)))
551   {
552     if (thd->lex->create_info.or_replace())
553     {
554       if (unlikely((error= mysql_drop_function_internal(thd, u_d, table))))
555         goto err;
556     }
557     else if (thd->lex->create_info.if_not_exists())
558     {
559       push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_UDF_EXISTS,
560                           ER_THD(thd, ER_UDF_EXISTS), udf->name.str);
561 
562       goto done;
563     }
564     else
565     {
566       my_error(ER_UDF_EXISTS, MYF(0), udf->name.str);
567       goto err;
568     }
569   }
570   if (!(dl = find_udf_dl(udf->dl)))
571   {
572     char dlpath[FN_REFLEN];
573     strxnmov(dlpath, sizeof(dlpath) - 1, opt_plugin_dir, "/", udf->dl, NullS);
574     (void) unpack_filename(dlpath, dlpath);
575 
576     if (!(dl = dlopen(dlpath, RTLD_NOW)))
577     {
578       my_error(ER_CANT_OPEN_LIBRARY, MYF(0),
579                udf->dl, errno, my_dlerror(dlpath));
580       DBUG_PRINT("error",("dlopen of %s failed, error: %d (%s)",
581                           udf->dl, errno, dlerror()));
582       goto err;
583     }
584     new_dl=1;
585   }
586   udf->dlhandle=dl;
587   {
588     char buf[SAFE_NAME_LEN+16];
589     const char *missing;
590     if ((missing= init_syms(udf, buf)))
591     {
592       my_error(ER_CANT_FIND_DL_ENTRY, MYF(0), missing);
593       goto err;
594     }
595   }
596   udf->name.str= strdup_root(&mem,udf->name.str);
597   udf->dl= strdup_root(&mem,udf->dl);
598   if (!(u_d=add_udf(&udf->name,udf->returns,udf->dl,udf->type)))
599     goto err;
600   u_d->dlhandle= dl;
601   u_d->func= udf->func;
602   u_d->func_init= udf->func_init;
603   u_d->func_deinit= udf->func_deinit;
604   u_d->func_clear= udf->func_clear;
605   u_d->func_add= udf->func_add;
606   u_d->func_remove= udf->func_remove;
607 
608   /* create entry in mysql.func table */
609 
610   /* Allow creation of functions even if we can't open func table */
611   if (unlikely(!table))
612     goto err;
613   table->use_all_columns();
614   restore_record(table, s->default_values);	// Default values for fields
615   table->field[0]->store(u_d->name.str, u_d->name.length, system_charset_info);
616   table->field[1]->store((longlong) u_d->returns, TRUE);
617   table->field[2]->store(u_d->dl,(uint) strlen(u_d->dl), system_charset_info);
618   if (table->s->fields >= 4)			// If not old func format
619     table->field[3]->store((longlong) u_d->type, TRUE);
620   error= table->file->ha_write_row(table->record[0]);
621 
622   if (unlikely(error))
623   {
624     my_error(ER_ERROR_ON_WRITE, MYF(0), "mysql.func", error);
625     del_udf(u_d);
626     goto err;
627   }
628 
629 done:
630   mysql_rwlock_unlock(&THR_LOCK_udf);
631 
632   /* Binlog the create function. */
633   if (unlikely(write_bin_log(thd, TRUE, thd->query(), thd->query_length())))
634     DBUG_RETURN(1);
635 
636   DBUG_RETURN(0);
637 
638 err:
639   if (new_dl)
640     dlclose(dl);
641   mysql_rwlock_unlock(&THR_LOCK_udf);
642   DBUG_RETURN(1);
643 }
644 
645 
mysql_drop_function(THD * thd,const LEX_CSTRING * udf_name)646 enum drop_udf_result mysql_drop_function(THD *thd, const LEX_CSTRING *udf_name)
647 {
648   TABLE *table;
649   udf_func *udf;
650   DBUG_ENTER("mysql_drop_function");
651 
652   if (thd->locked_tables_mode)
653   {
654     my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0));
655     DBUG_RETURN(UDF_DEL_RESULT_ERROR);
656   }
657 
658   if (!(table= open_udf_func_table(thd)))
659     DBUG_RETURN(UDF_DEL_RESULT_ERROR);
660 
661   // Fast pre-check
662   if (!mysql_rwlock_tryrdlock(&THR_LOCK_udf))
663   {
664     bool found= find_udf_everywhere(thd, *udf_name, table);
665     mysql_rwlock_unlock(&THR_LOCK_udf);
666     if (!found)
667     {
668       close_mysql_tables(thd);
669       DBUG_RETURN(UDF_DEL_RESULT_ABSENT);
670     }
671   }
672 
673   if (!initialized)
674   {
675     close_mysql_tables(thd);
676     if (opt_noacl)
677       DBUG_RETURN(UDF_DEL_RESULT_ABSENT); // SP should be checked
678 
679     my_message(ER_OUT_OF_RESOURCES, ER_THD(thd, ER_OUT_OF_RESOURCES), MYF(0));
680     DBUG_RETURN(UDF_DEL_RESULT_ERROR);
681   }
682 
683   mysql_rwlock_wrlock(&THR_LOCK_udf);
684 
685   // re-check under protection
686   if (!find_udf_everywhere(thd, *udf_name, table))
687   {
688     close_mysql_tables(thd);
689     mysql_rwlock_unlock(&THR_LOCK_udf);
690     DBUG_RETURN(UDF_DEL_RESULT_ABSENT);
691   }
692 
693   if (check_access(thd, DELETE_ACL, "mysql", NULL, NULL, 1, 0))
694     goto err;
695 
696 
697   DEBUG_SYNC(current_thd, "mysql_drop_function_after_lock");
698 
699   if (!(udf= (udf_func*) my_hash_search(&udf_hash, (uchar*) udf_name->str,
700                                         (uint) udf_name->length)) )
701   {
702     if (remove_udf_in_table(*udf_name, table))
703       goto err;
704     goto done;
705   }
706 
707   if (mysql_drop_function_internal(thd, udf, table))
708     goto err;
709 
710 done:
711   mysql_rwlock_unlock(&THR_LOCK_udf);
712 
713   /*
714     Binlog the drop function. Keep the table open and locked
715     while binlogging, to avoid binlog inconsistency.
716   */
717   if (write_bin_log(thd, TRUE, thd->query(), thd->query_length()))
718     DBUG_RETURN(UDF_DEL_RESULT_ERROR);
719 
720   close_mysql_tables(thd);
721   DBUG_RETURN(UDF_DEL_RESULT_DELETED);
722 
723 err:
724   close_mysql_tables(thd);
725   mysql_rwlock_unlock(&THR_LOCK_udf);
726   DBUG_RETURN(UDF_DEL_RESULT_ERROR);
727 }
728 
find_udf_everywhere(THD * thd,const LEX_CSTRING & name,TABLE * table)729 static bool find_udf_everywhere(THD* thd, const LEX_CSTRING &name,
730                                 TABLE *table)
731 {
732   if (initialized && my_hash_search(&udf_hash, (uchar*) name.str, name.length))
733     return true;
734 
735   return find_udf_in_table(name, table);
736 }
737 
738 #endif /* HAVE_DLOPEN */
739