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