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