diff options
Diffstat (limited to 'sql/sql_udf.cc')
-rw-r--r-- | sql/sql_udf.cc | 165 |
1 files changed, 101 insertions, 64 deletions
diff --git a/sql/sql_udf.cc b/sql/sql_udf.cc index 2906e69fc75..169e0d9e418 100644 --- a/sql/sql_udf.cc +++ b/sql/sql_udf.cc @@ -1,5 +1,4 @@ -/* - Copyright (c) 2000, 2010, Oracle and/or its affiliates. +/* Copyright (c) 2000, 2012, Oracle and/or its affiliates. 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 @@ -12,8 +11,7 @@ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* This implements 'user defined functions' */ @@ -33,8 +31,14 @@ #pragma implementation // gcc: Class implementation #endif -#include "mysql_priv.h" +#include "sql_priv.h" +#include "unireg.h" +#include "sql_base.h" // close_mysql_tables +#include "sql_parse.h" // check_identifier_name +#include "sql_table.h" // write_bin_log +#include "records.h" // init_read_record, end_read_record #include <my_pthread.h> +#include "lock.h" // MYSQL_LOCK_IGNORE_TIMEOUT #ifdef HAVE_DLOPEN extern "C" @@ -46,7 +50,7 @@ extern "C" static bool initialized = 0; static MEM_ROOT mem; static HASH udf_hash; -static rw_lock_t THR_LOCK_udf; +static mysql_rwlock_t THR_LOCK_udf; static udf_func *add_udf(LEX_STRING *name, Item_result ret, @@ -102,6 +106,26 @@ extern "C" uchar* get_hash_key(const uchar *buff, size_t *length, return (uchar*) udf->name.str; } +#ifdef HAVE_PSI_INTERFACE +static PSI_rwlock_key key_rwlock_THR_LOCK_udf; + +static PSI_rwlock_info all_udf_rwlocks[]= +{ + { &key_rwlock_THR_LOCK_udf, "THR_LOCK_udf", PSI_FLAG_GLOBAL} +}; + +static void init_udf_psi_keys(void) +{ + const char* category= "sql"; + int count; + + if (PSI_server == NULL) + return; + + count= array_elements(all_udf_rwlocks); + PSI_server->register_rwlock(category, all_udf_rwlocks, count); +} +#endif /* Read all predeclared functions from mysql.func and accept all that @@ -121,15 +145,19 @@ void udf_init() if (initialized) DBUG_VOID_RETURN; - my_rwlock_init(&THR_LOCK_udf,NULL); - +#ifdef HAVE_PSI_INTERFACE + init_udf_psi_keys(); +#endif + + mysql_rwlock_init(key_rwlock_THR_LOCK_udf, &THR_LOCK_udf); + init_sql_alloc(&mem, UDF_ALLOC_BLOCK_SIZE, 0); THD *new_thd = new THD; if (!new_thd || - hash_init(&udf_hash,system_charset_info,32,0,0,get_hash_key, NULL, 0)) + my_hash_init(&udf_hash,system_charset_info,32,0,0,get_hash_key, NULL, 0)) { sql_print_error("Can't allocate memory for udf structures"); - hash_free(&udf_hash); + my_hash_free(&udf_hash); free_root(&mem,MYF(0)); delete new_thd; DBUG_VOID_RETURN; @@ -137,15 +165,11 @@ void udf_init() initialized = 1; new_thd->thread_stack= (char*) &new_thd; new_thd->store_globals(); - lex_start(new_thd); new_thd->set_db(db, sizeof(db)-1); - bzero((uchar*) &tables,sizeof(tables)); - tables.alias= tables.table_name= (char*) "func"; - tables.lock_type = TL_READ; - tables.db= db; + tables.init_one_table(db, sizeof(db)-1, "func", 4, "func", TL_READ); - if (simple_open_n_lock_tables(new_thd, &tables)) + if (open_and_lock_tables(new_thd, &tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT)) { DBUG_PRINT("error",("Can't open udf table")); sql_print_error("Can't open the mysql.func table. Please " @@ -203,6 +227,7 @@ void udf_init() char dlpath[FN_REFLEN]; strxnmov(dlpath, sizeof(dlpath) - 1, opt_plugin_dir, "/", tmp->dl, NullS); + (void) unpack_filename(dlpath, dlpath); if (!(dl= dlopen(dlpath, RTLD_NOW))) { /* Print warning to log */ @@ -227,10 +252,10 @@ void udf_init() if (error > 0) sql_print_error("Got unknown error: %d", my_errno); end_read_record(&read_record_info); - new_thd->version--; // Force close to free memory + table->m_needs_reopen= TRUE; // Force close to free memory end: - close_thread_tables(new_thd); + close_mysql_tables(new_thd); delete new_thd; /* Remember that we don't have a THD */ my_pthread_setspecific_ptr(THR_THD, 0); @@ -244,25 +269,25 @@ void udf_free() DBUG_ENTER("udf_free"); for (uint idx=0 ; idx < udf_hash.records ; idx++) { - udf_func *udf=(udf_func*) hash_element(&udf_hash,idx); + udf_func *udf=(udf_func*) my_hash_element(&udf_hash,idx); if (udf->dlhandle) // Not closed before { /* Mark all versions using the same handler as closed */ for (uint j=idx+1 ; j < udf_hash.records ; j++) { - udf_func *tmp=(udf_func*) hash_element(&udf_hash,j); + udf_func *tmp=(udf_func*) my_hash_element(&udf_hash,j); if (udf->dlhandle == tmp->dlhandle) tmp->dlhandle=0; // Already closed } dlclose(udf->dlhandle); } } - hash_free(&udf_hash); + my_hash_free(&udf_hash); free_root(&mem,MYF(0)); if (initialized) { initialized= 0; - rwlock_destroy(&THR_LOCK_udf); + mysql_rwlock_destroy(&THR_LOCK_udf); } DBUG_VOID_RETURN; } @@ -273,7 +298,7 @@ static void del_udf(udf_func *udf) DBUG_ENTER("del_udf"); if (!--udf->usage_count) { - hash_delete(&udf_hash,(uchar*) udf); + my_hash_delete(&udf_hash,(uchar*) udf); using_udf_functions=udf_hash.records != 0; } else @@ -287,7 +312,7 @@ static void del_udf(udf_func *udf) uint name_length=udf->name.length; udf->name.str=(char*) "*"; udf->name.length=1; - hash_update(&udf_hash,(uchar*) udf,(uchar*) name,name_length); + my_hash_update(&udf_hash,(uchar*) udf,(uchar*) name,name_length); } DBUG_VOID_RETURN; } @@ -300,19 +325,19 @@ void free_udf(udf_func *udf) if (!initialized) DBUG_VOID_RETURN; - rw_wrlock(&THR_LOCK_udf); + mysql_rwlock_wrlock(&THR_LOCK_udf); if (!--udf->usage_count) { /* We come here when someone has deleted the udf function while another thread still was using the udf */ - hash_delete(&udf_hash,(uchar*) udf); + my_hash_delete(&udf_hash,(uchar*) udf); using_udf_functions=udf_hash.records != 0; if (!find_udf_dl(udf->dl)) dlclose(udf->dlhandle); } - rw_unlock(&THR_LOCK_udf); + mysql_rwlock_unlock(&THR_LOCK_udf); DBUG_VOID_RETURN; } @@ -329,19 +354,19 @@ udf_func *find_udf(const char *name,uint length,bool mark_used) /* TODO: This should be changed to reader locks someday! */ if (mark_used) - rw_wrlock(&THR_LOCK_udf); /* Called during fix_fields */ + mysql_rwlock_wrlock(&THR_LOCK_udf); /* Called during fix_fields */ else - rw_rdlock(&THR_LOCK_udf); /* Called during parsing */ + mysql_rwlock_rdlock(&THR_LOCK_udf); /* Called during parsing */ - if ((udf=(udf_func*) hash_search(&udf_hash,(uchar*) name, - length ? length : (uint) strlen(name)))) + if ((udf=(udf_func*) my_hash_search(&udf_hash,(uchar*) 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); + mysql_rwlock_unlock(&THR_LOCK_udf); DBUG_RETURN(udf); } @@ -356,7 +381,7 @@ static void *find_udf_dl(const char *dl) */ for (uint idx=0 ; idx < udf_hash.records ; idx++) { - udf_func *udf=(udf_func*) hash_element(&udf_hash,idx); + udf_func *udf=(udf_func*) my_hash_element(&udf_hash,idx); if (!strcmp(dl, udf->dl) && udf->dlhandle != NULL) DBUG_RETURN(udf->dlhandle); } @@ -438,11 +463,11 @@ int mysql_create_function(THD *thd,udf_func *udf) Turn off row binlogging of this statement and use statement-based so that all supporting tables are updated for CREATE FUNCTION command. */ - save_binlog_row_based= thd->current_stmt_binlog_row_based; - thd->clear_current_stmt_binlog_row_based(); + if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row())) + thd->clear_current_stmt_binlog_format_row(); - rw_wrlock(&THR_LOCK_udf); - if ((hash_search(&udf_hash,(uchar*) udf->name.str, udf->name.length))) + mysql_rwlock_wrlock(&THR_LOCK_udf); + if ((my_hash_search(&udf_hash,(uchar*) udf->name.str, udf->name.length))) { my_error(ER_UDF_EXISTS, MYF(0), udf->name.str); goto err; @@ -451,6 +476,8 @@ int mysql_create_function(THD *thd,udf_func *udf) { char dlpath[FN_REFLEN]; strxnmov(dlpath, sizeof(dlpath) - 1, opt_plugin_dir, "/", udf->dl, NullS); + (void) unpack_filename(dlpath, dlpath); + if (!(dl = dlopen(dlpath, RTLD_NOW))) { DBUG_PRINT("error",("dlopen of %s failed, error: %d (%s)", @@ -483,11 +510,9 @@ int mysql_create_function(THD *thd,udf_func *udf) /* create entry in mysql.func table */ - bzero((char*) &tables,sizeof(tables)); - tables.db= (char*) "mysql"; - tables.table_name= tables.alias= (char*) "func"; + tables.init_one_table("mysql", 5, "func", 4, "func", TL_WRITE); /* Allow creation of functions even if we can't open func table */ - if (!(table = open_ltable(thd, &tables, TL_WRITE, 0))) + if (!(table = open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT))) goto err; table->use_all_columns(); restore_record(table, s->default_values); // Default values for fields @@ -504,25 +529,31 @@ int mysql_create_function(THD *thd,udf_func *udf) del_udf(u_d); goto err; } - rw_unlock(&THR_LOCK_udf); + mysql_rwlock_unlock(&THR_LOCK_udf); /* Binlog the create function. */ if (write_bin_log(thd, TRUE, thd->query(), thd->query_length())) { /* Restore the state of binlog format */ - thd->current_stmt_binlog_row_based= save_binlog_row_based; + DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row()); + if (save_binlog_row_based) + thd->set_current_stmt_binlog_format_row(); DBUG_RETURN(1); } /* Restore the state of binlog format */ - thd->current_stmt_binlog_row_based= save_binlog_row_based; + DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row()); + if (save_binlog_row_based) + thd->set_current_stmt_binlog_format_row(); DBUG_RETURN(0); err: if (new_dl) dlclose(dl); - rw_unlock(&THR_LOCK_udf); + mysql_rwlock_unlock(&THR_LOCK_udf); /* Restore the state of binlog format */ - thd->current_stmt_binlog_row_based= save_binlog_row_based; + DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row()); + if (save_binlog_row_based) + thd->set_current_stmt_binlog_format_row(); DBUG_RETURN(1); } @@ -550,12 +581,12 @@ int mysql_drop_function(THD *thd,const LEX_STRING *udf_name) Turn off row binlogging of this statement and use statement-based so that all supporting tables are updated for DROP FUNCTION command. */ - save_binlog_row_based= thd->current_stmt_binlog_row_based; - thd->clear_current_stmt_binlog_row_based(); + if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row())) + thd->clear_current_stmt_binlog_format_row(); - rw_wrlock(&THR_LOCK_udf); - if (!(udf=(udf_func*) hash_search(&udf_hash,(uchar*) udf_name->str, - (uint) udf_name->length))) + mysql_rwlock_wrlock(&THR_LOCK_udf); + if (!(udf=(udf_func*) my_hash_search(&udf_hash,(uchar*) udf_name->str, + (uint) udf_name->length))) { my_error(ER_FUNCTION_NOT_DEFINED, MYF(0), udf_name->str); goto err; @@ -570,10 +601,9 @@ int mysql_drop_function(THD *thd,const LEX_STRING *udf_name) if (udf->dlhandle && !find_udf_dl(udf->dl)) dlclose(udf->dlhandle); - bzero((char*) &tables,sizeof(tables)); - tables.db=(char*) "mysql"; - tables.table_name= tables.alias= (char*) "func"; - if (!(table = open_ltable(thd, &tables, TL_WRITE, 0))) + tables.init_one_table("mysql", 5, "func", 4, "func", TL_WRITE); + + if (!(table = open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT))) goto err; table->use_all_columns(); table->field[0]->store(exact_name_str, exact_name_len, &my_charset_bin); @@ -586,24 +616,31 @@ int mysql_drop_function(THD *thd,const LEX_STRING *udf_name) if ((error = table->file->ha_delete_row(table->record[0]))) table->file->print_error(error, MYF(0)); } - close_thread_tables(thd); - - rw_unlock(&THR_LOCK_udf); + mysql_rwlock_unlock(&THR_LOCK_udf); - /* Binlog the drop function. */ + /* + Binlog the drop function. Keep the table open and locked + while binlogging, to avoid binlog inconsistency. + */ if (write_bin_log(thd, TRUE, thd->query(), thd->query_length())) { /* Restore the state of binlog format */ - thd->current_stmt_binlog_row_based= save_binlog_row_based; + DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row()); + if (save_binlog_row_based) + thd->set_current_stmt_binlog_format_row(); DBUG_RETURN(1); } /* Restore the state of binlog format */ - thd->current_stmt_binlog_row_based= save_binlog_row_based; + DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row()); + if (save_binlog_row_based) + thd->set_current_stmt_binlog_format_row(); DBUG_RETURN(0); - err: - rw_unlock(&THR_LOCK_udf); +err: + mysql_rwlock_unlock(&THR_LOCK_udf); /* Restore the state of binlog format */ - thd->current_stmt_binlog_row_based= save_binlog_row_based; + DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row()); + if (save_binlog_row_based) + thd->set_current_stmt_binlog_format_row(); DBUG_RETURN(1); } |