mariadb/sql/sql_udf.cc

563 lines
14 KiB
C++
Raw Normal View History

/* Copyright (C) 2000 MySQL AB
2000-07-31 21:29:14 +02:00
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
2000-07-31 21:29:14 +02:00
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
2000-07-31 21:29:14 +02:00
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
/* This implements 'user defined functions' */
/*
Known bugs:
Memory for functions is never freed!
Shared libraries are not closed before mysqld exits;
- This is because we can't be sure if some threads are using
a function.
The bugs only affect applications that create and free a lot of
dynamic functions, so this shouldn't be a real problem.
2000-07-31 21:29:14 +02:00
*/
#ifdef USE_PRAGMA_IMPLEMENTATION
#pragma implementation // gcc: Class implementation
2000-07-31 21:29:14 +02:00
#endif
#include "mysql_priv.h"
#include <my_pthread.h>
2000-07-31 21:29:14 +02:00
#ifdef HAVE_DLOPEN
extern "C"
{
#if defined(__WIN__)
void* dlsym(void* lib,const char* name)
{
return GetProcAddress((HMODULE)lib,name);
}
void* dlopen(const char* libname,int unused)
{
return LoadLibraryEx(libname,NULL,0);
}
void dlclose(void* lib)
{
FreeLibrary((HMODULE)lib);
}
#elif !defined(OS2)
2000-07-31 21:29:14 +02:00
#include <dlfcn.h>
#endif
2000-07-31 21:29:14 +02:00
#include <stdarg.h>
#include <hash.h>
}
#ifndef RTLD_NOW
#define RTLD_NOW 1 // For FreeBSD 2.2.2
#endif
#ifndef HAVE_DLERROR
#define dlerror() ""
#endif
static bool initialized = 0;
static MEM_ROOT mem;
static HASH udf_hash;
static rw_lock_t THR_LOCK_udf;
2000-07-31 21:29:14 +02:00
2005-03-03 23:07:20 +01:00
static udf_func *add_udf(LEX_STRING *name, Item_result ret,
char *dl, Item_udftype typ);
2000-07-31 21:29:14 +02:00
static void del_udf(udf_func *udf);
static void *find_udf_dl(const char *dl);
static char *init_syms(udf_func *tmp, char *nm)
2000-07-31 21:29:14 +02:00
{
char *end;
if (!((tmp->func= (Udf_func_any) dlsym(tmp->dlhandle, tmp->name.str))))
2005-03-03 23:57:48 +01:00
return tmp->name.str;
2000-07-31 21:29:14 +02:00
end=strmov(nm,tmp->name.str);
2000-07-31 21:29:14 +02:00
if (tmp->type == UDFTYPE_AGGREGATE)
{
2005-03-03 23:07:20 +01:00
(void)strmov(end, "_clear");
if (!((tmp->func_clear= (Udf_func_clear) dlsym(tmp->dlhandle, nm))))
return nm;
(void)strmov(end, "_add");
if (!((tmp->func_add= (Udf_func_add) dlsym(tmp->dlhandle, nm))))
return nm;
}
(void) strmov(end,"_deinit");
tmp->func_deinit= (Udf_func_deinit) dlsym(tmp->dlhandle, nm);
(void) strmov(end,"_init");
tmp->func_init= (Udf_func_init) dlsym(tmp->dlhandle, nm);
/*
to prefent loading "udf" from, e.g. libc.so
let's ensure that at least one auxiliary symbol is defined
*/
if (!tmp->func_init && !tmp->func_deinit && tmp->type != UDFTYPE_AGGREGATE)
{
if (!opt_allow_suspicious_udfs)
return nm;
if (current_thd->variables.log_warnings)
sql_print_warning(ER(ER_CANT_FIND_DL_ENTRY), nm);
2000-07-31 21:29:14 +02:00
}
return 0;
2000-07-31 21:29:14 +02:00
}
extern "C" byte* get_hash_key(const byte *buff,uint *length,
my_bool not_used __attribute__((unused)))
2000-07-31 21:29:14 +02:00
{
udf_func *udf=(udf_func*) buff;
*length=(uint) udf->name.length;
return (byte*) udf->name.str;
2000-07-31 21:29:14 +02:00
}
2000-07-31 21:29:14 +02:00
/*
Read all predeclared functions from mysql.func and accept all that
can be used.
2000-07-31 21:29:14 +02:00
*/
void udf_init()
{
udf_func *tmp;
TABLE_LIST tables;
READ_RECORD read_record_info;
TABLE *table;
2000-07-31 21:29:14 +02:00
int error;
A fix and a test case for Bug#19022 "Memory bug when switching db during trigger execution" Bug#17199 "Problem when view calls function from another database." Bug#18444 "Fully qualified stored function names don't work correctly in SELECT statements" Documentation note: this patch introduces a change in behaviour of prepared statements. This patch adds a few new invariants with regard to how THD::db should be used. These invariants should be preserved in future: - one should never refer to THD::db by pointer and always make a deep copy (strmake, strdup) - one should never compare two databases by pointer, but use strncmp or my_strncasecmp - TABLE_LIST object table->db should be always initialized in the parser or by creator of the object. For prepared statements it means that if the current database is changed after a statement is prepared, the database that was current at prepare remains active. This also means that you can not prepare a statement that implicitly refers to the current database if the latter is not set. This is not documented, and therefore needs documentation. This is NOT a change in behavior for almost all SQL statements except: - ALTER TABLE t1 RENAME t2 - OPTIMIZE TABLE t1 - ANALYZE TABLE t1 - TRUNCATE TABLE t1 -- until this patch t1 or t2 could be evaluated at the first execution of prepared statement. CURRENT_DATABASE() still works OK and is evaluated at every execution of prepared statement. Note, that in stored routines this is not an issue as the default database is the database of the stored procedure and "use" statement is prohibited in stored routines. This patch makes obsolete the use of check_db_used (it was never used in the old code too) and all other places that check for table->db and assign it from THD::db if it's NULL, except the parser. How this patch was created: THD::{db,db_length} were replaced with a LEX_STRING, THD::db. All the places that refer to THD::{db,db_length} were manually checked and: - if the place uses thd->db by pointer, it was fixed to make a deep copy - if a place compared two db pointers, it was fixed to compare them by value (via strcmp/my_strcasecmp, whatever was approproate) Then this intermediate patch was used to write a smaller patch that does the same thing but without a rename. TODO in 5.1: - remove check_db_used - deploy THD::set_db in mysql_change_db See also comments to individual files.
2006-06-26 22:47:52 +02:00
char db[]= "mysql"; /* A subject to casednstr, can't be constant */
2000-07-31 21:29:14 +02:00
DBUG_ENTER("ufd_init");
if (initialized)
DBUG_VOID_RETURN;
my_rwlock_init(&THR_LOCK_udf,NULL);
init_sql_alloc(&mem, UDF_ALLOC_BLOCK_SIZE, 0);
2000-07-31 21:29:14 +02:00
THD *new_thd = new THD;
if (!new_thd ||
hash_init(&udf_hash,system_charset_info,32,0,0,get_hash_key, NULL, 0))
2000-07-31 21:29:14 +02:00
{
sql_print_error("Can't allocate memory for udf structures");
hash_free(&udf_hash);
free_root(&mem,MYF(0));
delete new_thd;
2000-07-31 21:29:14 +02:00
DBUG_VOID_RETURN;
}
initialized = 1;
new_thd->thread_stack= (char*) &new_thd;
new_thd->store_globals();
A fix and a test case for Bug#19022 "Memory bug when switching db during trigger execution" Bug#17199 "Problem when view calls function from another database." Bug#18444 "Fully qualified stored function names don't work correctly in SELECT statements" Documentation note: this patch introduces a change in behaviour of prepared statements. This patch adds a few new invariants with regard to how THD::db should be used. These invariants should be preserved in future: - one should never refer to THD::db by pointer and always make a deep copy (strmake, strdup) - one should never compare two databases by pointer, but use strncmp or my_strncasecmp - TABLE_LIST object table->db should be always initialized in the parser or by creator of the object. For prepared statements it means that if the current database is changed after a statement is prepared, the database that was current at prepare remains active. This also means that you can not prepare a statement that implicitly refers to the current database if the latter is not set. This is not documented, and therefore needs documentation. This is NOT a change in behavior for almost all SQL statements except: - ALTER TABLE t1 RENAME t2 - OPTIMIZE TABLE t1 - ANALYZE TABLE t1 - TRUNCATE TABLE t1 -- until this patch t1 or t2 could be evaluated at the first execution of prepared statement. CURRENT_DATABASE() still works OK and is evaluated at every execution of prepared statement. Note, that in stored routines this is not an issue as the default database is the database of the stored procedure and "use" statement is prohibited in stored routines. This patch makes obsolete the use of check_db_used (it was never used in the old code too) and all other places that check for table->db and assign it from THD::db if it's NULL, except the parser. How this patch was created: THD::{db,db_length} were replaced with a LEX_STRING, THD::db. All the places that refer to THD::{db,db_length} were manually checked and: - if the place uses thd->db by pointer, it was fixed to make a deep copy - if a place compared two db pointers, it was fixed to compare them by value (via strcmp/my_strcasecmp, whatever was approproate) Then this intermediate patch was used to write a smaller patch that does the same thing but without a rename. TODO in 5.1: - remove check_db_used - deploy THD::set_db in mysql_change_db See also comments to individual files.
2006-06-26 22:47:52 +02:00
new_thd->set_db(db, sizeof(db)-1);
2000-07-31 21:29:14 +02:00
bzero((gptr) &tables,sizeof(tables));
tables.alias= tables.table_name= (char*) "func";
2000-07-31 21:29:14 +02:00
tables.lock_type = TL_READ;
A fix and a test case for Bug#19022 "Memory bug when switching db during trigger execution" Bug#17199 "Problem when view calls function from another database." Bug#18444 "Fully qualified stored function names don't work correctly in SELECT statements" Documentation note: this patch introduces a change in behaviour of prepared statements. This patch adds a few new invariants with regard to how THD::db should be used. These invariants should be preserved in future: - one should never refer to THD::db by pointer and always make a deep copy (strmake, strdup) - one should never compare two databases by pointer, but use strncmp or my_strncasecmp - TABLE_LIST object table->db should be always initialized in the parser or by creator of the object. For prepared statements it means that if the current database is changed after a statement is prepared, the database that was current at prepare remains active. This also means that you can not prepare a statement that implicitly refers to the current database if the latter is not set. This is not documented, and therefore needs documentation. This is NOT a change in behavior for almost all SQL statements except: - ALTER TABLE t1 RENAME t2 - OPTIMIZE TABLE t1 - ANALYZE TABLE t1 - TRUNCATE TABLE t1 -- until this patch t1 or t2 could be evaluated at the first execution of prepared statement. CURRENT_DATABASE() still works OK and is evaluated at every execution of prepared statement. Note, that in stored routines this is not an issue as the default database is the database of the stored procedure and "use" statement is prohibited in stored routines. This patch makes obsolete the use of check_db_used (it was never used in the old code too) and all other places that check for table->db and assign it from THD::db if it's NULL, except the parser. How this patch was created: THD::{db,db_length} were replaced with a LEX_STRING, THD::db. All the places that refer to THD::{db,db_length} were manually checked and: - if the place uses thd->db by pointer, it was fixed to make a deep copy - if a place compared two db pointers, it was fixed to compare them by value (via strcmp/my_strcasecmp, whatever was approproate) Then this intermediate patch was used to write a smaller patch that does the same thing but without a rename. TODO in 5.1: - remove check_db_used - deploy THD::set_db in mysql_change_db See also comments to individual files.
2006-06-26 22:47:52 +02:00
tables.db= db;
2000-07-31 21:29:14 +02:00
if (simple_open_n_lock_tables(new_thd, &tables))
2000-07-31 21:29:14 +02:00
{
DBUG_PRINT("error",("Can't open udf table"));
sql_print_error("Can't open the mysql.func table. Please run the mysql_install_db script to create it.");
goto end;
2000-07-31 21:29:14 +02:00
}
table= tables.table;
2000-07-31 21:29:14 +02:00
init_read_record(&read_record_info, new_thd, table, NULL,1,0);
while (!(error = read_record_info.read_record(&read_record_info)))
{
DBUG_PRINT("info",("init udf record"));
LEX_STRING name;
name.str=get_field(&mem, table->field[0]);
name.length = strlen(name.str);
char *dl_name= get_field(&mem, table->field[2]);
2000-07-31 21:29:14 +02:00
bool new_dl=0;
Item_udftype udftype=UDFTYPE_FUNCTION;
if (table->s->fields >= 4) // New func table
2000-07-31 21:29:14 +02:00
udftype=(Item_udftype) table->field[3]->val_int();
/*
Ensure that the .dll doesn't have a path
This is done to ensure that only approved dll from the system
directories are used (to make this even remotely secure).
*/
if (strchr(dl_name, '/') ||
IF_WIN(strchr(dl_name, '\\'),0) ||
2005-05-19 09:47:13 +02:00
strlen(name.str) > NAME_LEN)
{
sql_print_error("Invalid row in mysql.func table for function '%.64s'",
2005-03-03 23:57:48 +01:00
name.str);
continue;
}
2005-03-03 23:07:20 +01:00
if (!(tmp= add_udf(&name,(Item_result) table->field[1]->val_int(),
dl_name, udftype)))
2000-07-31 21:29:14 +02:00
{
2005-03-03 23:57:48 +01:00
sql_print_error("Can't alloc memory for udf function: '%.64s'", name.str);
2000-07-31 21:29:14 +02:00
continue;
}
void *dl = find_udf_dl(tmp->dl);
if (dl == NULL)
{
if (!(dl = dlopen(tmp->dl, RTLD_NOW)))
{
/* Print warning to log */
2004-09-04 20:17:09 +02:00
sql_print_error(ER(ER_CANT_OPEN_LIBRARY), tmp->dl,errno,dlerror());
/* Keep the udf in the hash so that we can remove it later */
2000-07-31 21:29:14 +02:00
continue;
}
new_dl=1;
}
tmp->dlhandle = dl;
{
char buf[NAME_LEN+16], *missing;
if ((missing= init_syms(tmp, buf)))
{
sql_print_error(ER(ER_CANT_FIND_DL_ENTRY), missing);
del_udf(tmp);
if (new_dl)
dlclose(dl);
}
2000-07-31 21:29:14 +02:00
}
}
if (error > 0)
sql_print_error("Got unknown error: %d", my_errno);
2000-07-31 21:29:14 +02:00
end_read_record(&read_record_info);
new_thd->version--; // Force close to free memory
end:
2000-07-31 21:29:14 +02:00
close_thread_tables(new_thd);
delete new_thd;
/* Remember that we don't have a THD */
my_pthread_setspecific_ptr(THR_THD, 0);
2000-07-31 21:29:14 +02:00
DBUG_VOID_RETURN;
}
void udf_free()
{
/* close all shared libraries */
DBUG_ENTER("udf_free");
for (uint idx=0 ; idx < udf_hash.records ; idx++)
{
udf_func *udf=(udf_func*) hash_element(&udf_hash,idx);
if (udf->dlhandle) // Not closed before
2000-07-31 21:29:14 +02:00
{
/* Mark all versions using the same handler as closed */
2000-07-31 21:29:14 +02:00
for (uint j=idx+1 ; j < udf_hash.records ; j++)
{
udf_func *tmp=(udf_func*) hash_element(&udf_hash,j);
if (udf->dlhandle == tmp->dlhandle)
tmp->dlhandle=0; // Already closed
2000-07-31 21:29:14 +02:00
}
dlclose(udf->dlhandle);
2000-07-31 21:29:14 +02:00
}
}
hash_free(&udf_hash);
free_root(&mem,MYF(0));
if (initialized)
{
initialized= 0;
rwlock_destroy(&THR_LOCK_udf);
2005-03-03 23:57:48 +01:00
}
2000-07-31 21:29:14 +02:00
DBUG_VOID_RETURN;
}
static void del_udf(udf_func *udf)
{
DBUG_ENTER("del_udf");
if (!--udf->usage_count)
{
hash_delete(&udf_hash,(byte*) udf);
using_udf_functions=udf_hash.records != 0;
}
else
{
/*
The functions is in use ; Rename the functions instead of removing it.
The functions will be automaticly removed when the least threads
doesn't use it anymore
2000-07-31 21:29:14 +02:00
*/
char *name= udf->name.str;
uint name_length=udf->name.length;
udf->name.str=(char*) "*";
udf->name.length=1;
hash_update(&udf_hash,(byte*) udf,(byte*) name,name_length);
2000-07-31 21:29:14 +02:00
}
DBUG_VOID_RETURN;
}
void free_udf(udf_func *udf)
{
DBUG_ENTER("free_udf");
if (!initialized)
DBUG_VOID_RETURN;
rw_wrlock(&THR_LOCK_udf);
2000-07-31 21:29:14 +02:00
if (!--udf->usage_count)
{
/*
We come here when someone has deleted the udf function
while another thread still was using the udf
*/
2000-07-31 21:29:14 +02:00
hash_delete(&udf_hash,(byte*) udf);
using_udf_functions=udf_hash.records != 0;
if (!find_udf_dl(udf->dl))
dlclose(udf->dlhandle);
}
rw_unlock(&THR_LOCK_udf);
2000-07-31 21:29:14 +02:00
DBUG_VOID_RETURN;
}
2000-07-31 21:29:14 +02:00
/* This is only called if using_udf_functions != 0 */
udf_func *find_udf(const char *name,uint length,bool mark_used)
{
udf_func *udf=0;
DBUG_ENTER("find_udf");
if (!initialized)
DBUG_RETURN(NULL);
2000-07-31 21:29:14 +02:00
/* TODO: This should be changed to reader locks someday! */
2004-03-19 11:35:00 +01:00
if (mark_used)
rw_wrlock(&THR_LOCK_udf); /* Called during fix_fields */
else
rw_rdlock(&THR_LOCK_udf); /* Called during parsing */
if ((udf=(udf_func*) hash_search(&udf_hash,(byte*) name,
length ? length : (uint) strlen(name))))
{
if (!udf->dlhandle)
udf=0; // Could not be opened
else if (mark_used)
udf->usage_count++;
}
rw_unlock(&THR_LOCK_udf);
2000-07-31 21:29:14 +02:00
DBUG_RETURN(udf);
}
2000-07-31 21:29:14 +02:00
static void *find_udf_dl(const char *dl)
{
DBUG_ENTER("find_udf_dl");
/*
Because only the function name is hashed, we have to search trough
all rows to find the dl.
2000-07-31 21:29:14 +02:00
*/
for (uint idx=0 ; idx < udf_hash.records ; idx++)
{
udf_func *udf=(udf_func*) hash_element(&udf_hash,idx);
if (!strcmp(dl, udf->dl) && udf->dlhandle != NULL)
DBUG_RETURN(udf->dlhandle);
}
DBUG_RETURN(0);
}
/* Assume that name && dl is already allocated */
static udf_func *add_udf(LEX_STRING *name, Item_result ret, char *dl,
2000-07-31 21:29:14 +02:00
Item_udftype type)
{
if (!name || !dl || !(uint) type || (uint) type > (uint) UDFTYPE_AGGREGATE)
2000-07-31 21:29:14 +02:00
return 0;
udf_func *tmp= (udf_func*) alloc_root(&mem, sizeof(udf_func));
if (!tmp)
return 0;
bzero((char*) tmp,sizeof(*tmp));
tmp->name = *name; //dup !!
2000-07-31 21:29:14 +02:00
tmp->dl = dl;
tmp->returns = ret;
tmp->type = type;
tmp->usage_count=1;
if (my_hash_insert(&udf_hash,(byte*) tmp))
2000-07-31 21:29:14 +02:00
return 0;
using_udf_functions=1;
return tmp;
}
int mysql_create_function(THD *thd,udf_func *udf)
{
int error;
void *dl=0;
bool new_dl=0;
TABLE *table;
TABLE_LIST tables;
udf_func *u_d;
DBUG_ENTER("mysql_create_function");
if (!initialized)
{
2004-11-12 13:34:00 +01:00
my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
2000-07-31 21:29:14 +02:00
DBUG_RETURN(1);
}
/*
Ensure that the .dll doesn't have a path
This is done to ensure that only approved dll from the system
directories are used (to make this even remotely secure).
*/
if (strchr(udf->dl, '/') || IF_WIN(strchr(udf->dl, '\\'),0))
2000-07-31 21:29:14 +02:00
{
2004-11-12 13:34:00 +01:00
my_message(ER_UDF_NO_PATHS, ER(ER_UDF_NO_PATHS), MYF(0));
2000-07-31 21:29:14 +02:00
DBUG_RETURN(1);
}
if (udf->name.length > NAME_LEN)
2000-07-31 21:29:14 +02:00
{
my_error(ER_TOO_LONG_IDENT, MYF(0), udf->name);
2000-07-31 21:29:14 +02:00
DBUG_RETURN(1);
}
rw_wrlock(&THR_LOCK_udf);
2003-01-27 23:28:06 +01:00
if ((hash_search(&udf_hash,(byte*) udf->name.str, udf->name.length)))
2000-07-31 21:29:14 +02:00
{
my_error(ER_UDF_EXISTS, MYF(0), udf->name);
2000-07-31 21:29:14 +02:00
goto err;
}
if (!(dl = find_udf_dl(udf->dl)))
{
DBUG_PRINT("info", ("Calling dlopen, udf->dl: %s", udf->dl));
2000-07-31 21:29:14 +02:00
if (!(dl = dlopen(udf->dl, RTLD_NOW)))
{
DBUG_PRINT("error",("dlopen of %s failed, error: %d (%s)",
udf->dl,errno,dlerror()));
my_error(ER_CANT_OPEN_LIBRARY, MYF(0),
2004-11-12 13:34:00 +01:00
udf->dl, errno, dlerror());
2000-07-31 21:29:14 +02:00
goto err;
}
new_dl=1;
}
udf->dlhandle=dl;
{
char buf[NAME_LEN+16], *missing;
if ((missing= init_syms(udf, buf)))
{
2005-03-04 11:37:45 +01:00
my_error(ER_CANT_FIND_DL_ENTRY, MYF(0), missing);
goto err;
}
2000-07-31 21:29:14 +02:00
}
udf->name.str=strdup_root(&mem,udf->name.str);
2000-07-31 21:29:14 +02:00
udf->dl=strdup_root(&mem,udf->dl);
if (!(u_d=add_udf(&udf->name,udf->returns,udf->dl,udf->type)))
2000-07-31 21:29:14 +02:00
goto err;
u_d->dlhandle = dl;
u_d->func=udf->func;
u_d->func_init=udf->func_init;
u_d->func_deinit=udf->func_deinit;
u_d->func_clear=udf->func_clear;
2000-07-31 21:29:14 +02:00
u_d->func_add=udf->func_add;
/* create entry in mysql.func table */
2000-07-31 21:29:14 +02:00
bzero((char*) &tables,sizeof(tables));
tables.db= (char*) "mysql";
tables.table_name= tables.alias= (char*) "func";
2000-07-31 21:29:14 +02:00
/* Allow creation of functions even if we can't open func table */
if (!(table = open_ltable(thd,&tables,TL_WRITE)))
goto err;
restore_record(table, s->default_values); // Default values for fields
table->field[0]->store(u_d->name.str, u_d->name.length, system_charset_info);
table->field[1]->store((longlong) u_d->returns, TRUE);
table->field[2]->store(u_d->dl,(uint) strlen(u_d->dl), system_charset_info);
if (table->s->fields >= 4) // If not old func format
table->field[3]->store((longlong) u_d->type, TRUE);
2000-07-31 21:29:14 +02:00
error = table->file->write_row(table->record[0]);
close_thread_tables(thd);
if (error)
{
2005-03-04 11:37:45 +01:00
my_error(ER_ERROR_ON_WRITE, MYF(0), "mysql.func", error);
2000-07-31 21:29:14 +02:00
del_udf(u_d);
goto err;
}
rw_unlock(&THR_LOCK_udf);
2000-07-31 21:29:14 +02:00
DBUG_RETURN(0);
err:
if (new_dl)
dlclose(dl);
rw_unlock(&THR_LOCK_udf);
2000-07-31 21:29:14 +02:00
DBUG_RETURN(1);
}
int mysql_drop_function(THD *thd,const LEX_STRING *udf_name)
2000-07-31 21:29:14 +02:00
{
TABLE *table;
TABLE_LIST tables;
udf_func *udf;
char *exact_name_str;
uint exact_name_len;
2000-07-31 21:29:14 +02:00
DBUG_ENTER("mysql_drop_function");
if (!initialized)
{
2004-11-12 13:34:00 +01:00
my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
2000-07-31 21:29:14 +02:00
DBUG_RETURN(1);
}
rw_wrlock(&THR_LOCK_udf);
if (!(udf=(udf_func*) hash_search(&udf_hash,(byte*) udf_name->str,
(uint) udf_name->length)))
2000-07-31 21:29:14 +02:00
{
my_error(ER_FUNCTION_NOT_DEFINED, MYF(0), udf_name->str);
2000-07-31 21:29:14 +02:00
goto err;
}
exact_name_str= udf->name.str;
exact_name_len= udf->name.length;
2000-07-31 21:29:14 +02:00
del_udf(udf);
/*
Close the handle if this was function that was found during boot or
CREATE FUNCTION and it's not in use by any other udf function
*/
if (udf->dlhandle && !find_udf_dl(udf->dl))
2000-07-31 21:29:14 +02:00
dlclose(udf->dlhandle);
bzero((char*) &tables,sizeof(tables));
tables.db=(char*) "mysql";
tables.table_name= tables.alias= (char*) "func";
2000-07-31 21:29:14 +02:00
if (!(table = open_ltable(thd,&tables,TL_WRITE)))
goto err;
table->field[0]->store(exact_name_str, exact_name_len, &my_charset_bin);
table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
if (!table->file->index_read_idx(table->record[0], 0,
(byte*) table->field[0]->ptr,
2005-04-09 01:21:51 +02:00
table->key_info[0].key_length,
2000-07-31 21:29:14 +02:00
HA_READ_KEY_EXACT))
{
int error;
if ((error = table->file->delete_row(table->record[0])))
table->file->print_error(error, MYF(0));
}
close_thread_tables(thd);
rw_unlock(&THR_LOCK_udf);
2000-07-31 21:29:14 +02:00
DBUG_RETURN(0);
err:
rw_unlock(&THR_LOCK_udf);
2000-07-31 21:29:14 +02:00
DBUG_RETURN(1);
}
#endif /* HAVE_DLOPEN */