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