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) |
|
80.2.1
by mark
remove handling of suspicious UDFs |
85 |
return nm; |
1
by brian
clean slate |
86 |
return 0; |
87 |
}
|
|
88 |
||
89 |
||
90 |
extern "C" uchar* get_hash_key(const uchar *buff, size_t *length, |
|
91 |
my_bool not_used __attribute__((unused))) |
|
92 |
{
|
|
93 |
udf_func *udf=(udf_func*) buff; |
|
94 |
*length=(uint) udf->name.length; |
|
95 |
return (uchar*) udf->name.str; |
|
96 |
}
|
|
97 |
||
98 |
||
99 |
/*
|
|
100 |
Read all predeclared functions from mysql.func and accept all that
|
|
101 |
can be used.
|
|
102 |
*/
|
|
103 |
||
104 |
void udf_init() |
|
105 |
{
|
|
106 |
TABLE_LIST tables; |
|
107 |
||
108 |
if (initialized) |
|
51.2.2
by Patrick Galbraith
Removed DBUGs from |
109 |
return; |
1
by brian
clean slate |
110 |
|
111 |
my_rwlock_init(&THR_LOCK_udf,NULL); |
|
77.1.46
by Monty Taylor
Finished the warnings work! |
112 |
|
1
by brian
clean slate |
113 |
init_sql_alloc(&mem, UDF_ALLOC_BLOCK_SIZE, 0); |
114 |
THD *new_thd = new THD; |
|
115 |
if (!new_thd || |
|
116 |
hash_init(&udf_hash,system_charset_info,32,0,0,get_hash_key, NULL, 0)) |
|
117 |
{
|
|
118 |
sql_print_error("Can't allocate memory for udf structures"); |
|
119 |
hash_free(&udf_hash); |
|
120 |
free_root(&mem,MYF(0)); |
|
121 |
delete new_thd; |
|
51.2.2
by Patrick Galbraith
Removed DBUGs from |
122 |
return; |
1
by brian
clean slate |
123 |
}
|
86
by Brian Aker
Remove bootstrap error on udf. |
124 |
|
1
by brian
clean slate |
125 |
initialized = 1; |
126 |
close_thread_tables(new_thd); |
|
127 |
delete new_thd; |
|
128 |
/* Remember that we don't have a THD */
|
|
129 |
my_pthread_setspecific_ptr(THR_THD, 0); |
|
51.2.2
by Patrick Galbraith
Removed DBUGs from |
130 |
return; |
1
by brian
clean slate |
131 |
}
|
132 |
||
133 |
||
134 |
void udf_free() |
|
135 |
{
|
|
136 |
/* close all shared libraries */
|
|
51.2.2
by Patrick Galbraith
Removed DBUGs from |
137 |
|
1
by brian
clean slate |
138 |
for (uint idx=0 ; idx < udf_hash.records ; idx++) |
139 |
{
|
|
140 |
udf_func *udf=(udf_func*) hash_element(&udf_hash,idx); |
|
141 |
if (udf->dlhandle) // Not closed before |
|
142 |
{
|
|
143 |
/* Mark all versions using the same handler as closed */
|
|
144 |
for (uint j=idx+1 ; j < udf_hash.records ; j++) |
|
145 |
{
|
|
146 |
udf_func *tmp=(udf_func*) hash_element(&udf_hash,j); |
|
147 |
if (udf->dlhandle == tmp->dlhandle) |
|
148 |
tmp->dlhandle=0; // Already closed |
|
149 |
}
|
|
150 |
dlclose(udf->dlhandle); |
|
151 |
}
|
|
152 |
}
|
|
153 |
hash_free(&udf_hash); |
|
154 |
free_root(&mem,MYF(0)); |
|
155 |
if (initialized) |
|
156 |
{
|
|
157 |
initialized= 0; |
|
158 |
rwlock_destroy(&THR_LOCK_udf); |
|
159 |
}
|
|
51.2.2
by Patrick Galbraith
Removed DBUGs from |
160 |
return; |
1
by brian
clean slate |
161 |
}
|
162 |
||
163 |
||
164 |
static void del_udf(udf_func *udf) |
|
165 |
{
|
|
51.2.2
by Patrick Galbraith
Removed DBUGs from |
166 |
|
1
by brian
clean slate |
167 |
if (!--udf->usage_count) |
168 |
{
|
|
169 |
hash_delete(&udf_hash,(uchar*) udf); |
|
170 |
using_udf_functions=udf_hash.records != 0; |
|
171 |
}
|
|
172 |
else
|
|
173 |
{
|
|
174 |
/*
|
|
175 |
The functions is in use ; Rename the functions instead of removing it.
|
|
176 |
The functions will be automaticly removed when the least threads
|
|
177 |
doesn't use it anymore
|
|
178 |
*/
|
|
179 |
char *name= udf->name.str; |
|
180 |
uint name_length=udf->name.length; |
|
181 |
udf->name.str=(char*) "*"; |
|
182 |
udf->name.length=1; |
|
183 |
hash_update(&udf_hash,(uchar*) udf,(uchar*) name,name_length); |
|
184 |
}
|
|
51.2.2
by Patrick Galbraith
Removed DBUGs from |
185 |
return; |
1
by brian
clean slate |
186 |
}
|
187 |
||
188 |
||
189 |
void free_udf(udf_func *udf) |
|
190 |
{
|
|
51.2.2
by Patrick Galbraith
Removed DBUGs from |
191 |
|
1
by brian
clean slate |
192 |
|
193 |
if (!initialized) |
|
51.2.2
by Patrick Galbraith
Removed DBUGs from |
194 |
return; |
1
by brian
clean slate |
195 |
|
196 |
rw_wrlock(&THR_LOCK_udf); |
|
197 |
if (!--udf->usage_count) |
|
198 |
{
|
|
199 |
/*
|
|
200 |
We come here when someone has deleted the udf function
|
|
201 |
while another thread still was using the udf
|
|
202 |
*/
|
|
203 |
hash_delete(&udf_hash,(uchar*) udf); |
|
204 |
using_udf_functions=udf_hash.records != 0; |
|
205 |
if (!find_udf_dl(udf->dl)) |
|
206 |
dlclose(udf->dlhandle); |
|
207 |
}
|
|
208 |
rw_unlock(&THR_LOCK_udf); |
|
51.2.2
by Patrick Galbraith
Removed DBUGs from |
209 |
return; |
1
by brian
clean slate |
210 |
}
|
211 |
||
212 |
||
213 |
/* This is only called if using_udf_functions != 0 */
|
|
214 |
||
215 |
udf_func *find_udf(const char *name,uint length,bool mark_used) |
|
216 |
{
|
|
217 |
udf_func *udf=0; |
|
51.2.2
by Patrick Galbraith
Removed DBUGs from |
218 |
|
1
by brian
clean slate |
219 |
|
220 |
if (!initialized) |
|
51.2.2
by Patrick Galbraith
Removed DBUGs from |
221 |
return(NULL); |
1
by brian
clean slate |
222 |
|
223 |
/* TODO: This should be changed to reader locks someday! */
|
|
224 |
if (mark_used) |
|
225 |
rw_wrlock(&THR_LOCK_udf); /* Called during fix_fields */ |
|
226 |
else
|
|
227 |
rw_rdlock(&THR_LOCK_udf); /* Called during parsing */ |
|
228 |
||
229 |
if ((udf=(udf_func*) hash_search(&udf_hash,(uchar*) name, |
|
230 |
length ? length : (uint) strlen(name)))) |
|
231 |
{
|
|
232 |
if (!udf->dlhandle) |
|
233 |
udf=0; // Could not be opened |
|
234 |
else if (mark_used) |
|
235 |
udf->usage_count++; |
|
236 |
}
|
|
237 |
rw_unlock(&THR_LOCK_udf); |
|
51.2.2
by Patrick Galbraith
Removed DBUGs from |
238 |
return(udf); |
1
by brian
clean slate |
239 |
}
|
240 |
||
241 |
||
242 |
static void *find_udf_dl(const char *dl) |
|
243 |
{
|
|
51.2.2
by Patrick Galbraith
Removed DBUGs from |
244 |
|
1
by brian
clean slate |
245 |
|
246 |
/*
|
|
247 |
Because only the function name is hashed, we have to search trough
|
|
248 |
all rows to find the dl.
|
|
249 |
*/
|
|
250 |
for (uint idx=0 ; idx < udf_hash.records ; idx++) |
|
251 |
{
|
|
252 |
udf_func *udf=(udf_func*) hash_element(&udf_hash,idx); |
|
253 |
if (!strcmp(dl, udf->dl) && udf->dlhandle != NULL) |
|
51.2.2
by Patrick Galbraith
Removed DBUGs from |
254 |
return(udf->dlhandle); |
1
by brian
clean slate |
255 |
}
|
51.2.2
by Patrick Galbraith
Removed DBUGs from |
256 |
return(0); |
1
by brian
clean slate |
257 |
}
|
258 |
||
259 |
||
260 |
/* Assume that name && dl is already allocated */
|
|
261 |
||
262 |
static udf_func *add_udf(LEX_STRING *name, Item_result ret, char *dl, |
|
263 |
Item_udftype type) |
|
264 |
{
|
|
265 |
if (!name || !dl || !(uint) type || (uint) type > (uint) UDFTYPE_AGGREGATE) |
|
266 |
return 0; |
|
267 |
udf_func *tmp= (udf_func*) alloc_root(&mem, sizeof(udf_func)); |
|
268 |
if (!tmp) |
|
269 |
return 0; |
|
270 |
bzero((char*) tmp,sizeof(*tmp)); |
|
271 |
tmp->name = *name; //dup !! |
|
272 |
tmp->dl = dl; |
|
273 |
tmp->returns = ret; |
|
274 |
tmp->type = type; |
|
275 |
tmp->usage_count=1; |
|
276 |
if (my_hash_insert(&udf_hash,(uchar*) tmp)) |
|
277 |
return 0; |
|
278 |
using_udf_functions=1; |
|
279 |
return tmp; |
|
280 |
}
|
|
281 |
||
282 |
||
283 |
/**
|
|
284 |
Create a user defined function.
|
|
285 |
||
286 |
@note Like implementations of other DDL/DML in MySQL, this function
|
|
287 |
relies on the caller to close the thread tables. This is done in the
|
|
288 |
end of dispatch_command().
|
|
289 |
*/
|
|
290 |
||
291 |
int mysql_create_function(THD *thd,udf_func *udf) |
|
292 |
{
|
|
293 |
int error; |
|
294 |
void *dl=0; |
|
295 |
bool new_dl=0; |
|
296 |
TABLE *table; |
|
297 |
TABLE_LIST tables; |
|
298 |
udf_func *u_d; |
|
51.2.2
by Patrick Galbraith
Removed DBUGs from |
299 |
|
1
by brian
clean slate |
300 |
|
301 |
if (!initialized) |
|
302 |
{
|
|
303 |
if (opt_noacl) |
|
304 |
my_error(ER_CANT_INITIALIZE_UDF, MYF(0), |
|
305 |
udf->name.str, |
|
306 |
"UDFs are unavailable with the --skip-grant-tables option"); |
|
307 |
else
|
|
308 |
my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0)); |
|
51.2.2
by Patrick Galbraith
Removed DBUGs from |
309 |
return(1); |
1
by brian
clean slate |
310 |
}
|
311 |
||
312 |
/*
|
|
313 |
Ensure that the .dll doesn't have a path
|
|
314 |
This is done to ensure that only approved dll from the system
|
|
315 |
directories are used (to make this even remotely secure).
|
|
316 |
||
317 |
On windows we must check both FN_LIBCHAR and '/'.
|
|
318 |
*/
|
|
319 |
if (my_strchr(files_charset_info, udf->dl, |
|
320 |
udf->dl + strlen(udf->dl), FN_LIBCHAR) || |
|
321 |
IF_WIN(my_strchr(files_charset_info, udf->dl, |
|
322 |
udf->dl + strlen(udf->dl), '/'), 0)) |
|
323 |
{
|
|
324 |
my_message(ER_UDF_NO_PATHS, ER(ER_UDF_NO_PATHS), MYF(0)); |
|
51.2.2
by Patrick Galbraith
Removed DBUGs from |
325 |
return(1); |
1
by brian
clean slate |
326 |
}
|
327 |
if (check_identifier_name(&udf->name, ER_TOO_LONG_IDENT)) |
|
51.2.2
by Patrick Galbraith
Removed DBUGs from |
328 |
return(1); |
1
by brian
clean slate |
329 |
|
330 |
/*
|
|
331 |
Turn off row binlogging of this statement and use statement-based
|
|
332 |
so that all supporting tables are updated for CREATE FUNCTION command.
|
|
333 |
*/
|
|
334 |
if (thd->current_stmt_binlog_row_based) |
|
335 |
thd->clear_current_stmt_binlog_row_based(); |
|
336 |
||
337 |
rw_wrlock(&THR_LOCK_udf); |
|
338 |
if ((hash_search(&udf_hash,(uchar*) udf->name.str, udf->name.length))) |
|
339 |
{
|
|
340 |
my_error(ER_UDF_EXISTS, MYF(0), udf->name.str); |
|
341 |
goto err; |
|
342 |
}
|
|
343 |
if (!(dl = find_udf_dl(udf->dl))) |
|
344 |
{
|
|
345 |
char dlpath[FN_REFLEN]; |
|
346 |
strxnmov(dlpath, sizeof(dlpath) - 1, opt_plugin_dir, "/", udf->dl, NullS); |
|
347 |
if (!(dl = dlopen(dlpath, RTLD_NOW))) |
|
348 |
{
|
|
349 |
my_error(ER_CANT_OPEN_LIBRARY, MYF(0), |
|
350 |
udf->dl, errno, dlerror()); |
|
351 |
goto err; |
|
352 |
}
|
|
353 |
new_dl=1; |
|
354 |
}
|
|
355 |
udf->dlhandle=dl; |
|
356 |
{
|
|
357 |
char buf[NAME_LEN+16], *missing; |
|
358 |
if ((missing= init_syms(udf, buf))) |
|
359 |
{
|
|
360 |
my_error(ER_CANT_FIND_DL_ENTRY, MYF(0), missing); |
|
361 |
goto err; |
|
362 |
}
|
|
363 |
}
|
|
364 |
udf->name.str=strdup_root(&mem,udf->name.str); |
|
365 |
udf->dl=strdup_root(&mem,udf->dl); |
|
366 |
if (!(u_d=add_udf(&udf->name,udf->returns,udf->dl,udf->type))) |
|
367 |
goto err; |
|
368 |
u_d->dlhandle = dl; |
|
369 |
u_d->func=udf->func; |
|
370 |
u_d->func_init=udf->func_init; |
|
371 |
u_d->func_deinit=udf->func_deinit; |
|
372 |
u_d->func_clear=udf->func_clear; |
|
373 |
u_d->func_add=udf->func_add; |
|
374 |
||
375 |
/* create entry in mysql.func table */
|
|
376 |
||
377 |
bzero((char*) &tables,sizeof(tables)); |
|
378 |
tables.db= (char*) "mysql"; |
|
379 |
tables.table_name= tables.alias= (char*) "func"; |
|
380 |
/* Allow creation of functions even if we can't open func table */
|
|
381 |
if (!(table = open_ltable(thd, &tables, TL_WRITE, 0))) |
|
382 |
goto err; |
|
383 |
table->use_all_columns(); |
|
384 |
restore_record(table, s->default_values); // Default values for fields |
|
385 |
table->field[0]->store(u_d->name.str, u_d->name.length, system_charset_info); |
|
386 |
table->field[1]->store((longlong) u_d->returns, TRUE); |
|
387 |
table->field[2]->store(u_d->dl,(uint) strlen(u_d->dl), system_charset_info); |
|
388 |
if (table->s->fields >= 4) // If not old func format |
|
389 |
table->field[3]->store((longlong) u_d->type, TRUE); |
|
390 |
error = table->file->ha_write_row(table->record[0]); |
|
391 |
||
392 |
if (error) |
|
393 |
{
|
|
394 |
my_error(ER_ERROR_ON_WRITE, MYF(0), "mysql.func", error); |
|
395 |
del_udf(u_d); |
|
396 |
goto err; |
|
397 |
}
|
|
398 |
rw_unlock(&THR_LOCK_udf); |
|
399 |
||
400 |
/* Binlog the create function. */
|
|
401 |
write_bin_log(thd, TRUE, thd->query, thd->query_length); |
|
402 |
||
51.2.2
by Patrick Galbraith
Removed DBUGs from |
403 |
return(0); |
1
by brian
clean slate |
404 |
|
405 |
err: |
|
406 |
if (new_dl) |
|
407 |
dlclose(dl); |
|
408 |
rw_unlock(&THR_LOCK_udf); |
|
51.2.2
by Patrick Galbraith
Removed DBUGs from |
409 |
return(1); |
1
by brian
clean slate |
410 |
}
|
411 |
||
412 |
||
413 |
int mysql_drop_function(THD *thd,const LEX_STRING *udf_name) |
|
414 |
{
|
|
415 |
TABLE *table; |
|
416 |
TABLE_LIST tables; |
|
417 |
udf_func *udf; |
|
418 |
char *exact_name_str; |
|
419 |
uint exact_name_len; |
|
51.2.2
by Patrick Galbraith
Removed DBUGs from |
420 |
|
1
by brian
clean slate |
421 |
|
422 |
if (!initialized) |
|
423 |
{
|
|
424 |
if (opt_noacl) |
|
425 |
my_error(ER_FUNCTION_NOT_DEFINED, MYF(0), udf_name->str); |
|
426 |
else
|
|
427 |
my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0)); |
|
51.2.2
by Patrick Galbraith
Removed DBUGs from |
428 |
return(1); |
1
by brian
clean slate |
429 |
}
|
430 |
||
431 |
/*
|
|
432 |
Turn off row binlogging of this statement and use statement-based
|
|
433 |
so that all supporting tables are updated for DROP 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 (!(udf=(udf_func*) hash_search(&udf_hash,(uchar*) udf_name->str, |
|
440 |
(uint) udf_name->length))) |
|
441 |
{
|
|
442 |
my_error(ER_FUNCTION_NOT_DEFINED, MYF(0), udf_name->str); |
|
443 |
goto err; |
|
444 |
}
|
|
445 |
exact_name_str= udf->name.str; |
|
446 |
exact_name_len= udf->name.length; |
|
447 |
del_udf(udf); |
|
448 |
/*
|
|
449 |
Close the handle if this was function that was found during boot or
|
|
450 |
CREATE FUNCTION and it's not in use by any other udf function
|
|
451 |
*/
|
|
452 |
if (udf->dlhandle && !find_udf_dl(udf->dl)) |
|
453 |
dlclose(udf->dlhandle); |
|
454 |
||
455 |
bzero((char*) &tables,sizeof(tables)); |
|
456 |
tables.db=(char*) "mysql"; |
|
457 |
tables.table_name= tables.alias= (char*) "func"; |
|
458 |
if (!(table = open_ltable(thd, &tables, TL_WRITE, 0))) |
|
459 |
goto err; |
|
460 |
table->use_all_columns(); |
|
461 |
table->field[0]->store(exact_name_str, exact_name_len, &my_charset_bin); |
|
462 |
if (!table->file->index_read_idx_map(table->record[0], 0, |
|
463 |
(uchar*) table->field[0]->ptr, |
|
464 |
HA_WHOLE_KEY, |
|
465 |
HA_READ_KEY_EXACT)) |
|
466 |
{
|
|
467 |
int error; |
|
468 |
if ((error = table->file->ha_delete_row(table->record[0]))) |
|
469 |
table->file->print_error(error, MYF(0)); |
|
470 |
}
|
|
471 |
close_thread_tables(thd); |
|
472 |
||
473 |
rw_unlock(&THR_LOCK_udf); |
|
474 |
||
475 |
/* Binlog the drop function. */
|
|
476 |
write_bin_log(thd, TRUE, thd->query, thd->query_length); |
|
477 |
||
51.2.2
by Patrick Galbraith
Removed DBUGs from |
478 |
return(0); |
1
by brian
clean slate |
479 |
err: |
480 |
rw_unlock(&THR_LOCK_udf); |
|
51.2.2
by Patrick Galbraith
Removed DBUGs from |
481 |
return(1); |
1
by brian
clean slate |
482 |
}
|
483 |
||
484 |
#endif /* HAVE_DLOPEN */ |