2 Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; version 2 of the License.
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 /* This implements 'user defined functions' */
23 Memory for functions is never freed!
24 Shared libraries are not closed before mysqld exits;
25 - This is because we can't be sure if some threads are using
28 The bugs only affect applications that create and free a lot of
29 dynamic functions, so this shouldn't be a real problem.
32 #ifdef USE_PRAGMA_IMPLEMENTATION
33 #pragma implementation // gcc: Class implementation
36 #include "mysql_priv.h"
37 #include <my_pthread.h>
46 static bool initialized
= 0;
49 static rw_lock_t THR_LOCK_udf
;
52 static udf_func
*add_udf(LEX_STRING
*name
, Item_result ret
,
53 char *dl
, Item_udftype typ
);
54 static void del_udf(udf_func
*udf
);
55 static void *find_udf_dl(const char *dl
);
57 static char *init_syms(udf_func
*tmp
, char *nm
)
61 if (!((tmp
->func
= (Udf_func_any
) dlsym(tmp
->dlhandle
, tmp
->name
.str
))))
64 end
=strmov(nm
,tmp
->name
.str
);
66 if (tmp
->type
== UDFTYPE_AGGREGATE
)
68 (void)strmov(end
, "_clear");
69 if (!((tmp
->func_clear
= (Udf_func_clear
) dlsym(tmp
->dlhandle
, nm
))))
71 (void)strmov(end
, "_add");
72 if (!((tmp
->func_add
= (Udf_func_add
) dlsym(tmp
->dlhandle
, nm
))))
76 (void) strmov(end
,"_deinit");
77 tmp
->func_deinit
= (Udf_func_deinit
) dlsym(tmp
->dlhandle
, nm
);
79 (void) strmov(end
,"_init");
80 tmp
->func_init
= (Udf_func_init
) dlsym(tmp
->dlhandle
, nm
);
83 to prefent loading "udf" from, e.g. libc.so
84 let's ensure that at least one auxiliary symbol is defined
86 if (!tmp
->func_init
&& !tmp
->func_deinit
&& tmp
->type
!= UDFTYPE_AGGREGATE
)
88 if (!opt_allow_suspicious_udfs
)
90 if (current_thd
->variables
.log_warnings
)
91 sql_print_warning(ER(ER_CANT_FIND_DL_ENTRY
), nm
);
97 extern "C" uchar
* get_hash_key(const uchar
*buff
, size_t *length
,
98 my_bool not_used
__attribute__((unused
)))
100 udf_func
*udf
=(udf_func
*) buff
;
101 *length
=(uint
) udf
->name
.length
;
102 return (uchar
*) udf
->name
.str
;
107 Read all predeclared functions from mysql.func and accept all that
115 READ_RECORD read_record_info
;
118 DBUG_ENTER("ufd_init");
119 char db
[]= "mysql"; /* A subject to casednstr, can't be constant */
124 my_rwlock_init(&THR_LOCK_udf
,NULL
);
126 init_sql_alloc(&mem
, UDF_ALLOC_BLOCK_SIZE
, 0);
127 THD
*new_thd
= new THD
;
129 hash_init(&udf_hash
,system_charset_info
,32,0,0,get_hash_key
, NULL
, 0))
131 sql_print_error("Can't allocate memory for udf structures");
132 hash_free(&udf_hash
);
133 free_root(&mem
,MYF(0));
138 new_thd
->thread_stack
= (char*) &new_thd
;
139 new_thd
->store_globals();
141 new_thd
->set_db(db
, sizeof(db
)-1);
143 bzero((uchar
*) &tables
,sizeof(tables
));
144 tables
.alias
= tables
.table_name
= (char*) "func";
145 tables
.lock_type
= TL_READ
;
148 if (simple_open_n_lock_tables(new_thd
, &tables
))
150 DBUG_PRINT("error",("Can't open udf table"));
151 sql_print_error("Can't open the mysql.func table. Please "
152 "run mysql_upgrade to create it.");
157 init_read_record(&read_record_info
, new_thd
, table
, NULL
,1,0,FALSE
);
158 table
->use_all_columns();
159 while (!(error
= read_record_info
.read_record(&read_record_info
)))
161 DBUG_PRINT("info",("init udf record"));
163 name
.str
=get_field(&mem
, table
->field
[0]);
164 name
.length
= (uint
) strlen(name
.str
);
165 char *dl_name
= get_field(&mem
, table
->field
[2]);
167 Item_udftype udftype
=UDFTYPE_FUNCTION
;
168 if (table
->s
->fields
>= 4) // New func table
169 udftype
=(Item_udftype
) table
->field
[3]->val_int();
172 Ensure that the .dll doesn't have a path
173 This is done to ensure that only approved dll from the system
174 directories are used (to make this even remotely secure).
176 On windows we must check both FN_LIBCHAR and '/'.
178 if (check_valid_path(dl_name
, strlen(dl_name
)) ||
179 check_string_char_length(&name
, "", NAME_CHAR_LEN
,
180 system_charset_info
, 1))
182 sql_print_error("Invalid row in mysql.func table for function '%.64s'",
187 if (!(tmp
= add_udf(&name
,(Item_result
) table
->field
[1]->val_int(),
190 sql_print_error("Can't alloc memory for udf function: '%.64s'", name
.str
);
194 void *dl
= find_udf_dl(tmp
->dl
);
197 char dlpath
[FN_REFLEN
];
198 strxnmov(dlpath
, sizeof(dlpath
) - 1, opt_plugin_dir
, "/", tmp
->dl
,
200 if (!(dl
= dlopen(dlpath
, RTLD_NOW
)))
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 */
211 char buf
[NAME_LEN
+16], *missing
;
212 if ((missing
= init_syms(tmp
, buf
)))
214 sql_print_error(ER(ER_CANT_FIND_DL_ENTRY
), missing
);
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
227 close_thread_tables(new_thd
);
229 /* Remember that we don't have a THD */
230 my_pthread_setspecific_ptr(THR_THD
, 0);
237 /* close all shared libraries */
238 DBUG_ENTER("udf_free");
239 for (uint idx
=0 ; idx
< udf_hash
.records
; idx
++)
241 udf_func
*udf
=(udf_func
*) hash_element(&udf_hash
,idx
);
242 if (udf
->dlhandle
) // Not closed before
244 /* Mark all versions using the same handler as closed */
245 for (uint j
=idx
+1 ; j
< udf_hash
.records
; j
++)
247 udf_func
*tmp
=(udf_func
*) hash_element(&udf_hash
,j
);
248 if (udf
->dlhandle
== tmp
->dlhandle
)
249 tmp
->dlhandle
=0; // Already closed
251 dlclose(udf
->dlhandle
);
254 hash_free(&udf_hash
);
255 free_root(&mem
,MYF(0));
259 rwlock_destroy(&THR_LOCK_udf
);
265 static void del_udf(udf_func
*udf
)
267 DBUG_ENTER("del_udf");
268 if (!--udf
->usage_count
)
270 hash_delete(&udf_hash
,(uchar
*) udf
);
271 using_udf_functions
=udf_hash
.records
!= 0;
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
280 char *name
= udf
->name
.str
;
281 uint name_length
=udf
->name
.length
;
282 udf
->name
.str
=(char*) "*";
284 hash_update(&udf_hash
,(uchar
*) udf
,(uchar
*) name
,name_length
);
290 void free_udf(udf_func
*udf
)
292 DBUG_ENTER("free_udf");
297 rw_wrlock(&THR_LOCK_udf
);
298 if (!--udf
->usage_count
)
301 We come here when someone has deleted the udf function
302 while another thread still was using the udf
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
);
309 rw_unlock(&THR_LOCK_udf
);
314 /* This is only called if using_udf_functions != 0 */
316 udf_func
*find_udf(const char *name
,uint length
,bool mark_used
)
319 DBUG_ENTER("find_udf");
324 /* TODO: This should be changed to reader locks someday! */
326 rw_wrlock(&THR_LOCK_udf
); /* Called during fix_fields */
328 rw_rdlock(&THR_LOCK_udf
); /* Called during parsing */
330 if ((udf
=(udf_func
*) hash_search(&udf_hash
,(uchar
*) name
,
331 length
? length
: (uint
) strlen(name
))))
334 udf
=0; // Could not be opened
338 rw_unlock(&THR_LOCK_udf
);
343 static void *find_udf_dl(const char *dl
)
345 DBUG_ENTER("find_udf_dl");
348 Because only the function name is hashed, we have to search trough
349 all rows to find the dl.
351 for (uint idx
=0 ; idx
< udf_hash
.records
; idx
++)
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
);
361 /* Assume that name && dl is already allocated */
363 static udf_func
*add_udf(LEX_STRING
*name
, Item_result ret
, char *dl
,
366 if (!name
|| !dl
|| !(uint
) type
|| (uint
) type
> (uint
) UDFTYPE_AGGREGATE
)
368 udf_func
*tmp
= (udf_func
*) alloc_root(&mem
, sizeof(udf_func
));
371 bzero((char*) tmp
,sizeof(*tmp
));
372 tmp
->name
= *name
; //dup !!
377 if (my_hash_insert(&udf_hash
,(uchar
*) tmp
))
379 using_udf_functions
=1;
385 Create a user defined function.
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().
392 int mysql_create_function(THD
*thd
,udf_func
*udf
)
400 bool save_binlog_row_based
;
401 DBUG_ENTER("mysql_create_function");
406 my_error(ER_CANT_INITIALIZE_UDF
, MYF(0),
408 "UDFs are unavailable with the --skip-grant-tables option");
410 my_message(ER_OUT_OF_RESOURCES
, ER(ER_OUT_OF_RESOURCES
), MYF(0));
415 Ensure that the .dll doesn't have a path
416 This is done to ensure that only approved dll from the system
417 directories are used (to make this even remotely secure).
419 if (check_valid_path(udf
->dl
, strlen(udf
->dl
)))
421 my_message(ER_UDF_NO_PATHS
, ER(ER_UDF_NO_PATHS
), MYF(0));
424 if (check_string_char_length(&udf
->name
, "", NAME_CHAR_LEN
,
425 system_charset_info
, 1))
427 my_error(ER_TOO_LONG_IDENT
, MYF(0), udf
->name
.str
);
432 Turn off row binlogging of this statement and use statement-based
433 so that all supporting tables are updated for CREATE FUNCTION command.
435 save_binlog_row_based
= thd
->current_stmt_binlog_row_based
;
436 thd
->clear_current_stmt_binlog_row_based();
438 rw_wrlock(&THR_LOCK_udf
);
439 if ((hash_search(&udf_hash
,(uchar
*) udf
->name
.str
, udf
->name
.length
)))
441 my_error(ER_UDF_EXISTS
, MYF(0), udf
->name
.str
);
444 if (!(dl
= find_udf_dl(udf
->dl
)))
446 char dlpath
[FN_REFLEN
];
447 strxnmov(dlpath
, sizeof(dlpath
) - 1, opt_plugin_dir
, "/", udf
->dl
, NullS
);
448 if (!(dl
= dlopen(dlpath
, RTLD_NOW
)))
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());
460 char buf
[NAME_LEN
+16], *missing
;
461 if ((missing
= init_syms(udf
, buf
)))
463 my_error(ER_CANT_FIND_DL_ENTRY
, MYF(0), missing
);
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
)))
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
;
478 /* create entry in mysql.func table */
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)))
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]);
497 my_error(ER_ERROR_ON_WRITE
, MYF(0), "mysql.func", error
);
501 rw_unlock(&THR_LOCK_udf
);
503 /* Binlog the create function. */
504 if (write_bin_log(thd
, TRUE
, thd
->query(), thd
->query_length()))
506 /* Restore the state of binlog format */
507 thd
->current_stmt_binlog_row_based
= save_binlog_row_based
;
510 /* Restore the state of binlog format */
511 thd
->current_stmt_binlog_row_based
= save_binlog_row_based
;
517 rw_unlock(&THR_LOCK_udf
);
518 /* Restore the state of binlog format */
519 thd
->current_stmt_binlog_row_based
= save_binlog_row_based
;
524 int mysql_drop_function(THD
*thd
,const LEX_STRING
*udf_name
)
529 char *exact_name_str
;
531 bool save_binlog_row_based
;
532 DBUG_ENTER("mysql_drop_function");
537 my_error(ER_FUNCTION_NOT_DEFINED
, MYF(0), udf_name
->str
);
539 my_message(ER_OUT_OF_RESOURCES
, ER(ER_OUT_OF_RESOURCES
), MYF(0));
544 Turn off row binlogging of this statement and use statement-based
545 so that all supporting tables are updated for DROP FUNCTION command.
547 save_binlog_row_based
= thd
->current_stmt_binlog_row_based
;
548 thd
->clear_current_stmt_binlog_row_based();
550 rw_wrlock(&THR_LOCK_udf
);
551 if (!(udf
=(udf_func
*) hash_search(&udf_hash
,(uchar
*) udf_name
->str
,
552 (uint
) udf_name
->length
)))
554 my_error(ER_FUNCTION_NOT_DEFINED
, MYF(0), udf_name
->str
);
557 exact_name_str
= udf
->name
.str
;
558 exact_name_len
= udf
->name
.length
;
561 Close the handle if this was function that was found during boot or
562 CREATE FUNCTION and it's not in use by any other udf function
564 if (udf
->dlhandle
&& !find_udf_dl(udf
->dl
))
565 dlclose(udf
->dlhandle
);
567 bzero((char*) &tables
,sizeof(tables
));
568 tables
.db
=(char*) "mysql";
569 tables
.table_name
= tables
.alias
= (char*) "func";
570 if (!(table
= open_ltable(thd
, &tables
, TL_WRITE
, 0)))
572 table
->use_all_columns();
573 table
->field
[0]->store(exact_name_str
, exact_name_len
, &my_charset_bin
);
574 if (!table
->file
->index_read_idx_map(table
->record
[0], 0,
575 (uchar
*) table
->field
[0]->ptr
,
580 if ((error
= table
->file
->ha_delete_row(table
->record
[0])))
581 table
->file
->print_error(error
, MYF(0));
583 close_thread_tables(thd
);
585 rw_unlock(&THR_LOCK_udf
);
587 /* Binlog the drop function. */
588 if (write_bin_log(thd
, TRUE
, thd
->query(), thd
->query_length()))
590 /* Restore the state of binlog format */
591 thd
->current_stmt_binlog_row_based
= save_binlog_row_based
;
594 /* Restore the state of binlog format */
595 thd
->current_stmt_binlog_row_based
= save_binlog_row_based
;
598 rw_unlock(&THR_LOCK_udf
);
599 /* Restore the state of binlog format */
600 thd
->current_stmt_binlog_row_based
= save_binlog_row_based
;
604 #endif /* HAVE_DLOPEN */