2001-12-06 13:10:51 +01:00
|
|
|
/* 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
|
2006-12-23 20:17:15 +01:00
|
|
|
the Free Software Foundation; version 2 of the License.
|
2001-12-06 13:10:51 +01:00
|
|
|
|
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.
|
2001-12-06 13:10:51 +01:00
|
|
|
|
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' */
|
|
|
|
|
|
|
|
/*
|
2005-03-05 19:42:05 +01:00
|
|
|
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
|
|
|
*/
|
|
|
|
|
2005-05-26 12:09:14 +02:00
|
|
|
#ifdef USE_PRAGMA_IMPLEMENTATION
|
2005-06-03 22:46:03 +02:00
|
|
|
#pragma implementation // gcc: Class implementation
|
2000-07-31 21:29:14 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "mysql_priv.h"
|
2002-12-31 12:58:41 +01:00
|
|
|
#include <my_pthread.h>
|
2001-04-09 20:08:56 +02:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
#ifdef HAVE_DLOPEN
|
|
|
|
extern "C"
|
|
|
|
{
|
2001-04-09 20:08:56 +02:00
|
|
|
#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);
|
|
|
|
}
|
2001-12-06 13:10:51 +01:00
|
|
|
|
2001-08-22 00:45:07 +02:00
|
|
|
#elif !defined(OS2)
|
2000-07-31 21:29:14 +02:00
|
|
|
#include <dlfcn.h>
|
2001-04-09 20:08:56 +02:00
|
|
|
#endif
|
2001-12-06 13:10:51 +01:00
|
|
|
|
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;
|
2002-12-31 12:58:41 +01:00
|
|
|
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,
|
2005-03-03 19:51:29 +01:00
|
|
|
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);
|
|
|
|
|
2005-03-03 19:51:29 +01:00
|
|
|
static char *init_syms(udf_func *tmp, char *nm)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2005-03-03 19:51:29 +01:00
|
|
|
char *end;
|
|
|
|
|
2006-07-09 11:03:51 +02:00
|
|
|
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
|
|
|
|
2003-01-09 14:09:21 +01: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");
|
2006-07-09 11:03:51 +02:00
|
|
|
if (!((tmp->func_clear= (Udf_func_clear) dlsym(tmp->dlhandle, nm))))
|
2005-03-03 19:51:29 +01:00
|
|
|
return nm;
|
|
|
|
(void)strmov(end, "_add");
|
2006-07-09 11:03:51 +02:00
|
|
|
if (!((tmp->func_add= (Udf_func_add) dlsym(tmp->dlhandle, nm))))
|
2005-03-03 19:51:29 +01:00
|
|
|
return nm;
|
|
|
|
}
|
|
|
|
|
|
|
|
(void) strmov(end,"_deinit");
|
2006-07-09 11:03:51 +02:00
|
|
|
tmp->func_deinit= (Udf_func_deinit) dlsym(tmp->dlhandle, nm);
|
2005-03-03 19:51:29 +01:00
|
|
|
|
|
|
|
(void) strmov(end,"_init");
|
2006-07-09 11:03:51 +02:00
|
|
|
tmp->func_init= (Udf_func_init) dlsym(tmp->dlhandle, nm);
|
2005-03-03 19:51:29 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
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)
|
|
|
|
{
|
2005-03-15 15:07:28 +01:00
|
|
|
if (!opt_allow_suspicious_udfs)
|
2005-03-03 19:51:29 +01:00
|
|
|
return nm;
|
2005-03-15 15:07:28 +01:00
|
|
|
if (current_thd->variables.log_warnings)
|
|
|
|
sql_print_warning(ER(ER_CANT_FIND_DL_ENTRY), nm);
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2005-03-03 19:51:29 +01:00
|
|
|
return 0;
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
|
2005-03-15 15:07:28 +01:00
|
|
|
|
2002-11-07 11:49:02 +01: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;
|
2003-01-09 14:09:21 +01:00
|
|
|
*length=(uint) udf->name.length;
|
|
|
|
return (byte*) udf->name.str;
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
|
2005-03-15 15:07:28 +01:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
/*
|
2005-03-15 15:07:28 +01: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;
|
2004-02-05 09:12:23 +01:00
|
|
|
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;
|
|
|
|
|
2002-12-31 12:58:41 +01:00
|
|
|
my_rwlock_init(&THR_LOCK_udf,NULL);
|
|
|
|
|
2003-10-11 21:00:24 +02:00
|
|
|
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 ||
|
2003-04-01 09:45:16 +02:00
|
|
|
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);
|
2000-09-12 02:02:33 +02:00
|
|
|
free_root(&mem,MYF(0));
|
2002-09-05 15:17:08 +02:00
|
|
|
delete new_thd;
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_VOID_RETURN;
|
|
|
|
}
|
|
|
|
initialized = 1;
|
2005-11-23 19:18:10 +01:00
|
|
|
new_thd->thread_stack= (char*) &new_thd;
|
2002-09-05 15:17:08 +02:00
|
|
|
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));
|
2005-01-06 12:00:13 +01:00
|
|
|
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
|
|
|
|
2004-02-01 14:30:32 +01: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"));
|
2005-03-03 19:51:29 +01:00
|
|
|
sql_print_error("Can't open the mysql.func table. Please run the mysql_install_db script to create it.");
|
2004-02-05 09:12:23 +01:00
|
|
|
goto end;
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
|
2004-02-05 09:12:23 +01: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"));
|
2003-01-09 14:09:21 +01:00
|
|
|
LEX_STRING name;
|
2003-02-12 20:55:37 +01:00
|
|
|
name.str=get_field(&mem, table->field[0]);
|
2003-01-09 14:09:21 +01:00
|
|
|
name.length = strlen(name.str);
|
2003-02-12 20:55:37 +01:00
|
|
|
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;
|
2005-01-06 12:00:13 +01:00
|
|
|
if (table->s->fields >= 4) // New func table
|
2000-07-31 21:29:14 +02:00
|
|
|
udftype=(Item_udftype) table->field[3]->val_int();
|
|
|
|
|
2005-03-03 19:51:29 +01:00
|
|
|
/*
|
|
|
|
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).
|
|
|
|
*/
|
2005-05-18 21:31:39 +02:00
|
|
|
if (strchr(dl_name, '/') ||
|
|
|
|
IF_WIN(strchr(dl_name, '\\'),0) ||
|
2005-05-19 09:47:13 +02:00
|
|
|
strlen(name.str) > NAME_LEN)
|
2005-03-03 19:51:29 +01:00
|
|
|
{
|
|
|
|
sql_print_error("Invalid row in mysql.func table for function '%.64s'",
|
2005-03-03 23:57:48 +01:00
|
|
|
name.str);
|
2005-03-03 19:51:29 +01:00
|
|
|
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)))
|
|
|
|
{
|
2002-09-05 15:17:08 +02:00
|
|
|
/* Print warning to log */
|
2004-09-04 20:17:09 +02:00
|
|
|
sql_print_error(ER(ER_CANT_OPEN_LIBRARY), tmp->dl,errno,dlerror());
|
2002-09-05 15:17:08 +02:00
|
|
|
/* 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;
|
|
|
|
{
|
2005-05-18 21:31:39 +02:00
|
|
|
char buf[NAME_LEN+16], *missing;
|
2005-03-03 19:51:29 +01:00
|
|
|
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)
|
2006-06-07 14:07:11 +02:00
|
|
|
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
|
2004-02-05 09:12:23 +01:00
|
|
|
|
|
|
|
end:
|
2000-07-31 21:29:14 +02:00
|
|
|
close_thread_tables(new_thd);
|
|
|
|
delete new_thd;
|
2002-12-05 02:40:33 +01:00
|
|
|
/* 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);
|
2002-09-05 15:17:08 +02:00
|
|
|
if (udf->dlhandle) // Not closed before
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2002-09-05 15:17:08 +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);
|
2002-09-05 15:17:08 +02:00
|
|
|
if (udf->dlhandle == tmp->dlhandle)
|
|
|
|
tmp->dlhandle=0; // Already closed
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2002-09-05 15:17:08 +02:00
|
|
|
dlclose(udf->dlhandle);
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
hash_free(&udf_hash);
|
2000-09-12 02:02:33 +02:00
|
|
|
free_root(&mem,MYF(0));
|
2003-01-28 07:38:28 +01:00
|
|
|
if (initialized)
|
|
|
|
{
|
|
|
|
initialized= 0;
|
2003-02-07 14:47:24 +01:00
|
|
|
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
|
|
|
|
{
|
|
|
|
/*
|
2002-09-05 15:17:08 +02:00
|
|
|
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
|
|
|
*/
|
2003-01-09 14:09:21 +01:00
|
|
|
char *name= udf->name.str;
|
|
|
|
uint name_length=udf->name.length;
|
|
|
|
udf->name.str=(char*) "*";
|
|
|
|
udf->name.length=1;
|
2001-04-09 20:08:56 +02:00
|
|
|
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");
|
2006-02-27 14:27:34 +01:00
|
|
|
|
|
|
|
if (!initialized)
|
|
|
|
DBUG_VOID_RETURN;
|
|
|
|
|
2002-12-31 12:58:41 +01:00
|
|
|
rw_wrlock(&THR_LOCK_udf);
|
2000-07-31 21:29:14 +02:00
|
|
|
if (!--udf->usage_count)
|
|
|
|
{
|
2002-09-05 15:17:08 +02:00
|
|
|
/*
|
|
|
|
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);
|
|
|
|
}
|
2002-12-31 12:58:41 +01:00
|
|
|
rw_unlock(&THR_LOCK_udf);
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_VOID_RETURN;
|
|
|
|
}
|
|
|
|
|
2002-09-05 15:17:08 +02:00
|
|
|
|
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");
|
|
|
|
|
2006-02-27 14:27:34 +01:00
|
|
|
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 */
|
|
|
|
|
2002-09-11 05:40:08 +02:00
|
|
|
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++;
|
|
|
|
}
|
2002-12-31 12:58:41 +01:00
|
|
|
rw_unlock(&THR_LOCK_udf);
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_RETURN(udf);
|
|
|
|
}
|
|
|
|
|
2002-09-05 15:17:08 +02:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
static void *find_udf_dl(const char *dl)
|
|
|
|
{
|
|
|
|
DBUG_ENTER("find_udf_dl");
|
|
|
|
|
2002-09-05 15:17:08 +02:00
|
|
|
/*
|
|
|
|
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 */
|
|
|
|
|
2003-01-09 14:09:21 +01:00
|
|
|
static udf_func *add_udf(LEX_STRING *name, Item_result ret, char *dl,
|
2000-07-31 21:29:14 +02:00
|
|
|
Item_udftype type)
|
|
|
|
{
|
2002-09-05 15:17:08 +02:00
|
|
|
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));
|
2003-01-09 14:09:21 +01:00
|
|
|
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;
|
2003-09-19 11:44:31 +02:00
|
|
|
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).
|
|
|
|
*/
|
2005-05-19 22:18:49 +02:00
|
|
|
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);
|
|
|
|
}
|
2003-01-09 14:09:21 +01:00
|
|
|
if (udf->name.length > NAME_LEN)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_TOO_LONG_IDENT, MYF(0), udf->name);
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_RETURN(1);
|
|
|
|
}
|
|
|
|
|
2002-12-31 12:58:41 +01:00
|
|
|
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
|
|
|
{
|
2004-11-13 18:35:51 +01: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)))
|
|
|
|
{
|
2006-03-10 10:41:04 +01:00
|
|
|
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()));
|
2004-11-13 18:35:51 +01:00
|
|
|
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;
|
|
|
|
{
|
2005-05-18 21:31:39 +02:00
|
|
|
char buf[NAME_LEN+16], *missing;
|
2005-03-03 19:51:29 +01:00
|
|
|
if ((missing= init_syms(udf, buf)))
|
|
|
|
{
|
2005-03-04 11:37:45 +01:00
|
|
|
my_error(ER_CANT_FIND_DL_ENTRY, MYF(0), missing);
|
2005-03-03 19:51:29 +01:00
|
|
|
goto err;
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2003-01-09 14:09:21 +01: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);
|
2003-01-09 14:09:21 +01:00
|
|
|
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;
|
2003-08-27 21:30:50 +02:00
|
|
|
u_d->func_clear=udf->func_clear;
|
2000-07-31 21:29:14 +02:00
|
|
|
u_d->func_add=udf->func_add;
|
|
|
|
|
2005-03-03 19:51:29 +01:00
|
|
|
/* create entry in mysql.func table */
|
2000-07-31 21:29:14 +02:00
|
|
|
|
|
|
|
bzero((char*) &tables,sizeof(tables));
|
|
|
|
tables.db= (char*) "mysql";
|
2005-01-06 12:00:13 +01:00
|
|
|
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;
|
|
|
|
|
2005-01-06 12:00:13 +01:00
|
|
|
restore_record(table, s->default_values); // Default values for fields
|
2003-02-26 11:08:31 +01:00
|
|
|
table->field[0]->store(u_d->name.str, u_d->name.length, system_charset_info);
|
2005-09-14 00:41:44 +02:00
|
|
|
table->field[1]->store((longlong) u_d->returns, TRUE);
|
2003-02-26 11:08:31 +01:00
|
|
|
table->field[2]->store(u_d->dl,(uint) strlen(u_d->dl), system_charset_info);
|
2005-01-06 12:00:13 +01:00
|
|
|
if (table->s->fields >= 4) // If not old func format
|
2005-09-14 00:41:44 +02:00
|
|
|
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;
|
|
|
|
}
|
2002-12-31 12:58:41 +01:00
|
|
|
rw_unlock(&THR_LOCK_udf);
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_RETURN(0);
|
|
|
|
|
|
|
|
err:
|
|
|
|
if (new_dl)
|
|
|
|
dlclose(dl);
|
2002-12-31 12:58:41 +01:00
|
|
|
rw_unlock(&THR_LOCK_udf);
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_RETURN(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-01-09 14:09:21 +01:00
|
|
|
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;
|
2007-01-03 17:26:39 +01:00
|
|
|
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);
|
|
|
|
}
|
2002-12-31 12:58:41 +01:00
|
|
|
rw_wrlock(&THR_LOCK_udf);
|
2003-01-09 14:09:21 +01:00
|
|
|
if (!(udf=(udf_func*) hash_search(&udf_hash,(byte*) udf_name->str,
|
|
|
|
(uint) udf_name->length)))
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_FUNCTION_NOT_DEFINED, MYF(0), udf_name->str);
|
2000-07-31 21:29:14 +02:00
|
|
|
goto err;
|
|
|
|
}
|
2007-01-03 17:26:39 +01:00
|
|
|
exact_name_str= udf->name.str;
|
|
|
|
exact_name_len= udf->name.length;
|
2000-07-31 21:29:14 +02:00
|
|
|
del_udf(udf);
|
2002-09-05 15:17:08 +02:00
|
|
|
/*
|
|
|
|
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";
|
2005-01-06 12:00:13 +01:00
|
|
|
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;
|
2007-01-03 17:26:39 +01:00
|
|
|
table->field[0]->store(exact_name_str, exact_name_len, &my_charset_bin);
|
2005-03-07 14:37:10 +01:00
|
|
|
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);
|
|
|
|
|
2002-12-31 12:58:41 +01:00
|
|
|
rw_unlock(&THR_LOCK_udf);
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_RETURN(0);
|
|
|
|
err:
|
2002-12-31 12:58:41 +01:00
|
|
|
rw_unlock(&THR_LOCK_udf);
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_RETURN(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* HAVE_DLOPEN */
|