1
by brian
clean slate |
1 |
/* Copyright (C) 2000 MySQL AB
|
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 "mysql_priv.h" |
|
35 |
#include <my_pthread.h> |
|
36 |
||
37 |
#ifdef HAVE_DLOPEN
|
|
38 |
extern "C" |
|
39 |
{
|
|
40 |
#include <stdarg.h> |
|
41 |
#include <hash.h> |
|
42 |
}
|
|
43 |
||
44 |
static bool initialized = 0; |
|
45 |
static MEM_ROOT mem; |
|
46 |
static HASH udf_hash; |
|
47 |
static rw_lock_t THR_LOCK_udf; |
|
48 |
||
49 |
||
50 |
static udf_func *add_udf(LEX_STRING *name, Item_result ret, |
|
51 |
char *dl, Item_udftype typ); |
|
52 |
static void del_udf(udf_func *udf); |
|
53 |
static void *find_udf_dl(const char *dl); |
|
54 |
||
55 |
static char *init_syms(udf_func *tmp, char *nm) |
|
56 |
{
|
|
57 |
char *end; |
|
58 |
||
59 |
if (!((tmp->func= (Udf_func_any) dlsym(tmp->dlhandle, tmp->name.str)))) |
|
60 |
return tmp->name.str; |
|
61 |
||
62 |
end=strmov(nm,tmp->name.str); |
|
63 |
||
64 |
if (tmp->type == UDFTYPE_AGGREGATE) |
|
65 |
{
|
|
66 |
(void)strmov(end, "_clear"); |
|
67 |
if (!((tmp->func_clear= (Udf_func_clear) dlsym(tmp->dlhandle, nm)))) |
|
68 |
return nm; |
|
69 |
(void)strmov(end, "_add"); |
|
70 |
if (!((tmp->func_add= (Udf_func_add) dlsym(tmp->dlhandle, nm)))) |
|
71 |
return nm; |
|
72 |
}
|
|
73 |
||
74 |
(void) strmov(end,"_deinit"); |
|
75 |
tmp->func_deinit= (Udf_func_deinit) dlsym(tmp->dlhandle, nm); |
|
76 |
||
77 |
(void) strmov(end,"_init"); |
|
78 |
tmp->func_init= (Udf_func_init) dlsym(tmp->dlhandle, nm); |
|
79 |
||
80 |
/*
|
|
81 |
to prefent loading "udf" from, e.g. libc.so
|
|
82 |
let's ensure that at least one auxiliary symbol is defined
|
|
83 |
*/
|
|
84 |
if (!tmp->func_init && !tmp->func_deinit && tmp->type != UDFTYPE_AGGREGATE) |
|
85 |
{
|
|
86 |
if (!opt_allow_suspicious_udfs) |
|
87 |
return nm; |
|
88 |
if (current_thd->variables.log_warnings) |
|
89 |
sql_print_warning(ER(ER_CANT_FIND_DL_ENTRY), nm); |
|
90 |
}
|
|
91 |
return 0; |
|
92 |
}
|
|
93 |
||
94 |
||
95 |
extern "C" uchar* get_hash_key(const uchar *buff, size_t *length, |
|
96 |
my_bool not_used __attribute__((unused))) |
|
97 |
{
|
|
98 |
udf_func *udf=(udf_func*) buff; |
|
99 |
*length=(uint) udf->name.length; |
|
100 |
return (uchar*) udf->name.str; |
|
101 |
}
|
|
102 |
||
103 |
||
104 |
/*
|
|
105 |
Read all predeclared functions from mysql.func and accept all that
|
|
106 |
can be used.
|
|
107 |
*/
|
|
108 |
||
109 |
void udf_init() |
|
110 |
{
|
|
111 |
udf_func *tmp; |
|
112 |
TABLE_LIST tables; |
|
113 |
READ_RECORD read_record_info; |
|
114 |
TABLE *table; |
|
115 |
int error; |
|
116 |
DBUG_ENTER("ufd_init"); |
|
117 |
char db[]= "mysql"; /* A subject to casednstr, can't be constant */ |
|
118 |
||
119 |
if (initialized) |
|
120 |
DBUG_VOID_RETURN; |
|
121 |
||
122 |
my_rwlock_init(&THR_LOCK_udf,NULL); |
|
123 |
||
124 |
init_sql_alloc(&mem, UDF_ALLOC_BLOCK_SIZE, 0); |
|
125 |
THD *new_thd = new THD; |
|
126 |
if (!new_thd || |
|
127 |
hash_init(&udf_hash,system_charset_info,32,0,0,get_hash_key, NULL, 0)) |
|
128 |
{
|
|
129 |
sql_print_error("Can't allocate memory for udf structures"); |
|
130 |
hash_free(&udf_hash); |
|
131 |
free_root(&mem,MYF(0)); |
|
132 |
delete new_thd; |
|
133 |
DBUG_VOID_RETURN; |
|
134 |
}
|
|
135 |
initialized = 1; |
|
136 |
new_thd->thread_stack= (char*) &new_thd; |
|
137 |
new_thd->store_globals(); |
|
138 |
lex_start(new_thd); |
|
139 |
new_thd->set_db(db, sizeof(db)-1); |
|
140 |
||
141 |
bzero((uchar*) &tables,sizeof(tables)); |
|
142 |
tables.alias= tables.table_name= (char*) "func"; |
|
143 |
tables.lock_type = TL_READ; |
|
144 |
tables.db= db; |
|
145 |
||
146 |
if (simple_open_n_lock_tables(new_thd, &tables)) |
|
147 |
{
|
|
148 |
DBUG_PRINT("error",("Can't open udf table")); |
|
149 |
sql_print_error("Can't open the mysql.func table. Please " |
|
150 |
"run mysql_upgrade to create it."); |
|
151 |
goto end; |
|
152 |
}
|
|
153 |
||
154 |
table= tables.table; |
|
155 |
init_read_record(&read_record_info, new_thd, table, NULL,1,0); |
|
156 |
table->use_all_columns(); |
|
157 |
while (!(error= read_record_info.read_record(&read_record_info))) |
|
158 |
{
|
|
159 |
DBUG_PRINT("info",("init udf record")); |
|
160 |
LEX_STRING name; |
|
161 |
name.str=get_field(&mem, table->field[0]); |
|
162 |
name.length = strlen(name.str); |
|
163 |
char *dl_name= get_field(&mem, table->field[2]); |
|
164 |
bool new_dl=0; |
|
165 |
Item_udftype udftype=UDFTYPE_FUNCTION; |
|
166 |
if (table->s->fields >= 4) // New func table |
|
167 |
udftype=(Item_udftype) table->field[3]->val_int(); |
|
168 |
||
169 |
/*
|
|
170 |
Ensure that the .dll doesn't have a path
|
|
171 |
This is done to ensure that only approved dll from the system
|
|
172 |
directories are used (to make this even remotely secure).
|
|
173 |
||
174 |
On windows we must check both FN_LIBCHAR and '/'.
|
|
175 |
*/
|
|
176 |
if (my_strchr(files_charset_info, dl_name, |
|
177 |
dl_name + strlen(dl_name), FN_LIBCHAR) || |
|
178 |
IF_WIN(my_strchr(files_charset_info, dl_name, |
|
179 |
dl_name + strlen(dl_name), '/'), 0) || |
|
180 |
check_identifier_name(&name)) |
|
181 |
{
|
|
182 |
sql_print_error("Invalid row in mysql.func table for function '%.64s'", |
|
183 |
name.str); |
|
184 |
continue; |
|
185 |
}
|
|
186 |
||
187 |
if (!(tmp= add_udf(&name,(Item_result) table->field[1]->val_int(), |
|
188 |
dl_name, udftype))) |
|
189 |
{
|
|
190 |
sql_print_error("Can't alloc memory for udf function: '%.64s'", name.str); |
|
191 |
continue; |
|
192 |
}
|
|
193 |
||
194 |
void *dl = find_udf_dl(tmp->dl); |
|
195 |
if (dl == NULL) |
|
196 |
{
|
|
197 |
char dlpath[FN_REFLEN]; |
|
198 |
strxnmov(dlpath, sizeof(dlpath) - 1, opt_plugin_dir, "/", tmp->dl, |
|
199 |
NullS); |
|
200 |
if (!(dl= dlopen(dlpath, RTLD_NOW))) |
|
201 |
{
|
|
202 |
/* Print warning to log */
|
|
203 |
sql_print_error(ER(ER_CANT_OPEN_LIBRARY), tmp->dl, errno, dlerror()); |
|
204 |
/* Keep the udf in the hash so that we can remove it later */
|
|
205 |
continue; |
|
206 |
}
|
|
207 |
new_dl=1; |
|
208 |
}
|
|
209 |
tmp->dlhandle = dl; |
|
210 |
{
|
|
211 |
char buf[NAME_LEN+16], *missing; |
|
212 |
if ((missing= init_syms(tmp, buf))) |
|
213 |
{
|
|
214 |
sql_print_error(ER(ER_CANT_FIND_DL_ENTRY), missing); |
|
215 |
del_udf(tmp); |
|
216 |
if (new_dl) |
|
217 |
dlclose(dl); |
|
218 |
}
|
|
219 |
}
|
|
220 |
}
|
|
221 |
if (error > 0) |
|
222 |
sql_print_error("Got unknown error: %d", my_errno); |
|
223 |
end_read_record(&read_record_info); |
|
224 |
new_thd->version--; // Force close to free memory |
|
225 |
||
226 |
end: |
|
227 |
close_thread_tables(new_thd); |
|
228 |
delete new_thd; |
|
229 |
/* Remember that we don't have a THD */
|
|
230 |
my_pthread_setspecific_ptr(THR_THD, 0); |
|
231 |
DBUG_VOID_RETURN; |
|
232 |
}
|
|
233 |
||
234 |
||
235 |
void udf_free() |
|
236 |
{
|
|
237 |
/* close all shared libraries */
|
|
238 |
DBUG_ENTER("udf_free"); |
|
239 |
for (uint idx=0 ; idx < udf_hash.records ; idx++) |
|
240 |
{
|
|
241 |
udf_func *udf=(udf_func*) hash_element(&udf_hash,idx); |
|
242 |
if (udf->dlhandle) // Not closed before |
|
243 |
{
|
|
244 |
/* Mark all versions using the same handler as closed */
|
|
245 |
for (uint j=idx+1 ; j < udf_hash.records ; j++) |
|
246 |
{
|
|
247 |
udf_func *tmp=(udf_func*) hash_element(&udf_hash,j); |
|
248 |
if (udf->dlhandle == tmp->dlhandle) |
|
249 |
tmp->dlhandle=0; // Already closed |
|
250 |
}
|
|
251 |
dlclose(udf->dlhandle); |
|
252 |
}
|
|
253 |
}
|
|
254 |
hash_free(&udf_hash); |
|
255 |
free_root(&mem,MYF(0)); |
|
256 |
if (initialized) |
|
257 |
{
|
|
258 |
initialized= 0; |
|
259 |
rwlock_destroy(&THR_LOCK_udf); |
|
260 |
}
|
|
261 |
DBUG_VOID_RETURN; |
|
262 |
}
|
|
263 |
||
264 |
||
265 |
static void del_udf(udf_func *udf) |
|
266 |
{
|
|
267 |
DBUG_ENTER("del_udf"); |
|
268 |
if (!--udf->usage_count) |
|
269 |
{
|
|
270 |
hash_delete(&udf_hash,(uchar*) udf); |
|
271 |
using_udf_functions=udf_hash.records != 0; |
|
272 |
}
|
|
273 |
else
|
|
274 |
{
|
|
275 |
/*
|
|
276 |
The functions is in use ; Rename the functions instead of removing it.
|
|
277 |
The functions will be automaticly removed when the least threads
|
|
278 |
doesn't use it anymore
|
|
279 |
*/
|
|
280 |
char *name= udf->name.str; |
|
281 |
uint name_length=udf->name.length; |
|
282 |
udf->name.str=(char*) "*"; |
|
283 |
udf->name.length=1; |
|
284 |
hash_update(&udf_hash,(uchar*) udf,(uchar*) name,name_length); |
|
285 |
}
|
|
286 |
DBUG_VOID_RETURN; |
|
287 |
}
|
|
288 |
||
289 |
||
290 |
void free_udf(udf_func *udf) |
|
291 |
{
|
|
292 |
DBUG_ENTER("free_udf"); |
|
293 |
||
294 |
if (!initialized) |
|
295 |
DBUG_VOID_RETURN; |
|
296 |
||
297 |
rw_wrlock(&THR_LOCK_udf); |
|
298 |
if (!--udf->usage_count) |
|
299 |
{
|
|
300 |
/*
|
|
301 |
We come here when someone has deleted the udf function
|
|
302 |
while another thread still was using the udf
|
|
303 |
*/
|
|
304 |
hash_delete(&udf_hash,(uchar*) udf); |
|
305 |
using_udf_functions=udf_hash.records != 0; |
|
306 |
if (!find_udf_dl(udf->dl)) |
|
307 |
dlclose(udf->dlhandle); |
|
308 |
}
|
|
309 |
rw_unlock(&THR_LOCK_udf); |
|
310 |
DBUG_VOID_RETURN; |
|
311 |
}
|
|
312 |
||
313 |
||
314 |
/* This is only called if using_udf_functions != 0 */
|
|
315 |
||
316 |
udf_func *find_udf(const char *name,uint length,bool mark_used) |
|
317 |
{
|
|
318 |
udf_func *udf=0; |
|
319 |
DBUG_ENTER("find_udf"); |
|
320 |
||
321 |
if (!initialized) |
|
322 |
DBUG_RETURN(NULL); |
|
323 |
||
324 |
/* TODO: This should be changed to reader locks someday! */
|
|
325 |
if (mark_used) |
|
326 |
rw_wrlock(&THR_LOCK_udf); /* Called during fix_fields */ |
|
327 |
else
|
|
328 |
rw_rdlock(&THR_LOCK_udf); /* Called during parsing */ |
|
329 |
||
330 |
if ((udf=(udf_func*) hash_search(&udf_hash,(uchar*) name, |
|
331 |
length ? length : (uint) strlen(name)))) |
|
332 |
{
|
|
333 |
if (!udf->dlhandle) |
|
334 |
udf=0; // Could not be opened |
|
335 |
else if (mark_used) |
|
336 |
udf->usage_count++; |
|
337 |
}
|
|
338 |
rw_unlock(&THR_LOCK_udf); |
|
339 |
DBUG_RETURN(udf); |
|
340 |
}
|
|
341 |
||
342 |
||
343 |
static void *find_udf_dl(const char *dl) |
|
344 |
{
|
|
345 |
DBUG_ENTER("find_udf_dl"); |
|
346 |
||
347 |
/*
|
|
348 |
Because only the function name is hashed, we have to search trough
|
|
349 |
all rows to find the dl.
|
|
350 |
*/
|
|
351 |
for (uint idx=0 ; idx < udf_hash.records ; idx++) |
|
352 |
{
|
|
353 |
udf_func *udf=(udf_func*) hash_element(&udf_hash,idx); |
|
354 |
if (!strcmp(dl, udf->dl) && udf->dlhandle != NULL) |
|
355 |
DBUG_RETURN(udf->dlhandle); |
|
356 |
}
|
|
357 |
DBUG_RETURN(0); |
|
358 |
}
|
|
359 |
||
360 |
||
361 |
/* Assume that name && dl is already allocated */
|
|
362 |
||
363 |
static udf_func *add_udf(LEX_STRING *name, Item_result ret, char *dl, |
|
364 |
Item_udftype type) |
|
365 |
{
|
|
366 |
if (!name || !dl || !(uint) type || (uint) type > (uint) UDFTYPE_AGGREGATE) |
|
367 |
return 0; |
|
368 |
udf_func *tmp= (udf_func*) alloc_root(&mem, sizeof(udf_func)); |
|
369 |
if (!tmp) |
|
370 |
return 0; |
|
371 |
bzero((char*) tmp,sizeof(*tmp)); |
|
372 |
tmp->name = *name; //dup !! |
|
373 |
tmp->dl = dl; |
|
374 |
tmp->returns = ret; |
|
375 |
tmp->type = type; |
|
376 |
tmp->usage_count=1; |
|
377 |
if (my_hash_insert(&udf_hash,(uchar*) tmp)) |
|
378 |
return 0; |
|
379 |
using_udf_functions=1; |
|
380 |
return tmp; |
|
381 |
}
|
|
382 |
||
383 |
||
384 |
/**
|
|
385 |
Create a user defined function.
|
|
386 |
||
387 |
@note Like implementations of other DDL/DML in MySQL, this function
|
|
388 |
relies on the caller to close the thread tables. This is done in the
|
|
389 |
end of dispatch_command().
|
|
390 |
*/
|
|
391 |
||
392 |
int mysql_create_function(THD *thd,udf_func *udf) |
|
393 |
{
|
|
394 |
int error; |
|
395 |
void *dl=0; |
|
396 |
bool new_dl=0; |
|
397 |
TABLE *table; |
|
398 |
TABLE_LIST tables; |
|
399 |
udf_func *u_d; |
|
400 |
DBUG_ENTER("mysql_create_function"); |
|
401 |
||
402 |
if (!initialized) |
|
403 |
{
|
|
404 |
if (opt_noacl) |
|
405 |
my_error(ER_CANT_INITIALIZE_UDF, MYF(0), |
|
406 |
udf->name.str, |
|
407 |
"UDFs are unavailable with the --skip-grant-tables option"); |
|
408 |
else
|
|
409 |
my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0)); |
|
410 |
DBUG_RETURN(1); |
|
411 |
}
|
|
412 |
||
413 |
/*
|
|
414 |
Ensure that the .dll doesn't have a path
|
|
415 |
This is done to ensure that only approved dll from the system
|
|
416 |
directories are used (to make this even remotely secure).
|
|
417 |
||
418 |
On windows we must check both FN_LIBCHAR and '/'.
|
|
419 |
*/
|
|
420 |
if (my_strchr(files_charset_info, udf->dl, |
|
421 |
udf->dl + strlen(udf->dl), FN_LIBCHAR) || |
|
422 |
IF_WIN(my_strchr(files_charset_info, udf->dl, |
|
423 |
udf->dl + strlen(udf->dl), '/'), 0)) |
|
424 |
{
|
|
425 |
my_message(ER_UDF_NO_PATHS, ER(ER_UDF_NO_PATHS), MYF(0)); |
|
426 |
DBUG_RETURN(1); |
|
427 |
}
|
|
428 |
if (check_identifier_name(&udf->name, ER_TOO_LONG_IDENT)) |
|
429 |
DBUG_RETURN(1); |
|
430 |
||
431 |
/*
|
|
432 |
Turn off row binlogging of this statement and use statement-based
|
|
433 |
so that all supporting tables are updated for CREATE FUNCTION command.
|
|
434 |
*/
|
|
435 |
if (thd->current_stmt_binlog_row_based) |
|
436 |
thd->clear_current_stmt_binlog_row_based(); |
|
437 |
||
438 |
rw_wrlock(&THR_LOCK_udf); |
|
439 |
if ((hash_search(&udf_hash,(uchar*) udf->name.str, udf->name.length))) |
|
440 |
{
|
|
441 |
my_error(ER_UDF_EXISTS, MYF(0), udf->name.str); |
|
442 |
goto err; |
|
443 |
}
|
|
444 |
if (!(dl = find_udf_dl(udf->dl))) |
|
445 |
{
|
|
446 |
char dlpath[FN_REFLEN]; |
|
447 |
strxnmov(dlpath, sizeof(dlpath) - 1, opt_plugin_dir, "/", udf->dl, NullS); |
|
448 |
if (!(dl = dlopen(dlpath, RTLD_NOW))) |
|
449 |
{
|
|
450 |
DBUG_PRINT("error",("dlopen of %s failed, error: %d (%s)", |
|
451 |
udf->dl, errno, dlerror())); |
|
452 |
my_error(ER_CANT_OPEN_LIBRARY, MYF(0), |
|
453 |
udf->dl, errno, dlerror()); |
|
454 |
goto err; |
|
455 |
}
|
|
456 |
new_dl=1; |
|
457 |
}
|
|
458 |
udf->dlhandle=dl; |
|
459 |
{
|
|
460 |
char buf[NAME_LEN+16], *missing; |
|
461 |
if ((missing= init_syms(udf, buf))) |
|
462 |
{
|
|
463 |
my_error(ER_CANT_FIND_DL_ENTRY, MYF(0), missing); |
|
464 |
goto err; |
|
465 |
}
|
|
466 |
}
|
|
467 |
udf->name.str=strdup_root(&mem,udf->name.str); |
|
468 |
udf->dl=strdup_root(&mem,udf->dl); |
|
469 |
if (!(u_d=add_udf(&udf->name,udf->returns,udf->dl,udf->type))) |
|
470 |
goto err; |
|
471 |
u_d->dlhandle = dl; |
|
472 |
u_d->func=udf->func; |
|
473 |
u_d->func_init=udf->func_init; |
|
474 |
u_d->func_deinit=udf->func_deinit; |
|
475 |
u_d->func_clear=udf->func_clear; |
|
476 |
u_d->func_add=udf->func_add; |
|
477 |
||
478 |
/* create entry in mysql.func table */
|
|
479 |
||
480 |
bzero((char*) &tables,sizeof(tables)); |
|
481 |
tables.db= (char*) "mysql"; |
|
482 |
tables.table_name= tables.alias= (char*) "func"; |
|
483 |
/* Allow creation of functions even if we can't open func table */
|
|
484 |
if (!(table = open_ltable(thd, &tables, TL_WRITE, 0))) |
|
485 |
goto err; |
|
486 |
table->use_all_columns(); |
|
487 |
restore_record(table, s->default_values); // Default values for fields |
|
488 |
table->field[0]->store(u_d->name.str, u_d->name.length, system_charset_info); |
|
489 |
table->field[1]->store((longlong) u_d->returns, TRUE); |
|
490 |
table->field[2]->store(u_d->dl,(uint) strlen(u_d->dl), system_charset_info); |
|
491 |
if (table->s->fields >= 4) // If not old func format |
|
492 |
table->field[3]->store((longlong) u_d->type, TRUE); |
|
493 |
error = table->file->ha_write_row(table->record[0]); |
|
494 |
||
495 |
if (error) |
|
496 |
{
|
|
497 |
my_error(ER_ERROR_ON_WRITE, MYF(0), "mysql.func", error); |
|
498 |
del_udf(u_d); |
|
499 |
goto err; |
|
500 |
}
|
|
501 |
rw_unlock(&THR_LOCK_udf); |
|
502 |
||
503 |
/* Binlog the create function. */
|
|
504 |
write_bin_log(thd, TRUE, thd->query, thd->query_length); |
|
505 |
||
506 |
DBUG_RETURN(0); |
|
507 |
||
508 |
err: |
|
509 |
if (new_dl) |
|
510 |
dlclose(dl); |
|
511 |
rw_unlock(&THR_LOCK_udf); |
|
512 |
DBUG_RETURN(1); |
|
513 |
}
|
|
514 |
||
515 |
||
516 |
int mysql_drop_function(THD *thd,const LEX_STRING *udf_name) |
|
517 |
{
|
|
518 |
TABLE *table; |
|
519 |
TABLE_LIST tables; |
|
520 |
udf_func *udf; |
|
521 |
char *exact_name_str; |
|
522 |
uint exact_name_len; |
|
523 |
DBUG_ENTER("mysql_drop_function"); |
|
524 |
||
525 |
if (!initialized) |
|
526 |
{
|
|
527 |
if (opt_noacl) |
|
528 |
my_error(ER_FUNCTION_NOT_DEFINED, MYF(0), udf_name->str); |
|
529 |
else
|
|
530 |
my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0)); |
|
531 |
DBUG_RETURN(1); |
|
532 |
}
|
|
533 |
||
534 |
/*
|
|
535 |
Turn off row binlogging of this statement and use statement-based
|
|
536 |
so that all supporting tables are updated for DROP FUNCTION command.
|
|
537 |
*/
|
|
538 |
if (thd->current_stmt_binlog_row_based) |
|
539 |
thd->clear_current_stmt_binlog_row_based(); |
|
540 |
||
541 |
rw_wrlock(&THR_LOCK_udf); |
|
542 |
if (!(udf=(udf_func*) hash_search(&udf_hash,(uchar*) udf_name->str, |
|
543 |
(uint) udf_name->length))) |
|
544 |
{
|
|
545 |
my_error(ER_FUNCTION_NOT_DEFINED, MYF(0), udf_name->str); |
|
546 |
goto err; |
|
547 |
}
|
|
548 |
exact_name_str= udf->name.str; |
|
549 |
exact_name_len= udf->name.length; |
|
550 |
del_udf(udf); |
|
551 |
/*
|
|
552 |
Close the handle if this was function that was found during boot or
|
|
553 |
CREATE FUNCTION and it's not in use by any other udf function
|
|
554 |
*/
|
|
555 |
if (udf->dlhandle && !find_udf_dl(udf->dl)) |
|
556 |
dlclose(udf->dlhandle); |
|
557 |
||
558 |
bzero((char*) &tables,sizeof(tables)); |
|
559 |
tables.db=(char*) "mysql"; |
|
560 |
tables.table_name= tables.alias= (char*) "func"; |
|
561 |
if (!(table = open_ltable(thd, &tables, TL_WRITE, 0))) |
|
562 |
goto err; |
|
563 |
table->use_all_columns(); |
|
564 |
table->field[0]->store(exact_name_str, exact_name_len, &my_charset_bin); |
|
565 |
if (!table->file->index_read_idx_map(table->record[0], 0, |
|
566 |
(uchar*) table->field[0]->ptr, |
|
567 |
HA_WHOLE_KEY, |
|
568 |
HA_READ_KEY_EXACT)) |
|
569 |
{
|
|
570 |
int error; |
|
571 |
if ((error = table->file->ha_delete_row(table->record[0]))) |
|
572 |
table->file->print_error(error, MYF(0)); |
|
573 |
}
|
|
574 |
close_thread_tables(thd); |
|
575 |
||
576 |
rw_unlock(&THR_LOCK_udf); |
|
577 |
||
578 |
/* Binlog the drop function. */
|
|
579 |
write_bin_log(thd, TRUE, thd->query, thd->query_length); |
|
580 |
||
581 |
DBUG_RETURN(0); |
|
582 |
err: |
|
583 |
rw_unlock(&THR_LOCK_udf); |
|
584 |
DBUG_RETURN(1); |
|
585 |
}
|
|
586 |
||
587 |
#endif /* HAVE_DLOPEN */ |