diff options
-rw-r--r-- | mysql-test/r/udf.result | 14 | ||||
-rw-r--r-- | mysql-test/t/udf.test | 22 | ||||
-rw-r--r-- | sql/sql_parse.cc | 23 | ||||
-rw-r--r-- | sql/sql_udf.cc | 149 | ||||
-rw-r--r-- | sql/sql_udf.h | 8 |
5 files changed, 162 insertions, 54 deletions
diff --git a/mysql-test/r/udf.result b/mysql-test/r/udf.result index 56bc2e26b2a..49768f6c514 100644 --- a/mysql-test/r/udf.result +++ b/mysql-test/r/udf.result @@ -479,3 +479,17 @@ myfunc_int(max(a) over (order by b) , b) drop view v1; drop function myfunc_int; drop table t1; +# +# MDEV-23327: Can't uninstall UDF if the implementation library +# file doesn't exist +# +insert into mysql.func values ("unexisting_udf", 0, "soname", "function"); +select * from mysql.func WHERE name='unexisting_udf'; +name ret dl type +unexisting_udf 0 soname function +DROP FUNCTION unexisting_udf; +select * from mysql.plugin WHERE name='unexisting_udf'; +name dl +DROP FUNCTION unexisting_udf; +ERROR 42000: FUNCTION test.unexisting_udf does not exist +# End of 10.2 tests diff --git a/mysql-test/t/udf.test b/mysql-test/t/udf.test index 7cdff2b6000..07c7f599db7 100644 --- a/mysql-test/t/udf.test +++ b/mysql-test/t/udf.test @@ -541,3 +541,25 @@ select * from v1; drop view v1; drop function myfunc_int; drop table t1; + +--echo # +--echo # MDEV-23327: Can't uninstall UDF if the implementation library +--echo # file doesn't exist +--echo # + +# emulate brocken so/ddl udf +insert into mysql.func values ("unexisting_udf", 0, "soname", "function"); + +# check that we have the function "installed" +select * from mysql.func WHERE name='unexisting_udf'; + +# make attempt to drop the function +DROP FUNCTION unexisting_udf; + +# check that we have the plugin uninstalled +select * from mysql.plugin WHERE name='unexisting_udf'; + +--error ER_SP_DOES_NOT_EXIST +DROP FUNCTION unexisting_udf; + +--echo # End of 10.2 tests diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index e34181e2eeb..5dddbf0360d 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -5623,23 +5623,20 @@ mysql_execute_command(THD *thd) ! lex->spname->m_explicit_name) { /* DROP FUNCTION <non qualified name> */ - udf_func *udf = find_udf(lex->spname->m_name.str, - lex->spname->m_name.length); - if (udf) + enum drop_udf_result rc= mysql_drop_function(thd, + &lex->spname->m_name); + if (rc == UDF_DEL_RESULT_DELETED) { - if (check_access(thd, DELETE_ACL, "mysql", NULL, NULL, 1, 0)) - goto error; + my_ok(thd); + break; + } - if (!(res = mysql_drop_function(thd, &lex->spname->m_name))) - { - my_ok(thd); - break; - } - my_error(ER_SP_DROP_FAILED, MYF(0), - "FUNCTION (UDF)", lex->spname->m_name.str); + if (rc == UDF_DEL_RESULT_ERROR) goto error; - } + DBUG_ASSERT(rc == UDF_DEL_RESULT_ABSENT); + + // If there was no current database, so it can not be SP if (lex->spname->m_db.str == NULL) { if (lex->if_exists()) diff --git a/sql/sql_udf.cc b/sql/sql_udf.cc index 9099345a64c..b4e863709d5 100644 --- a/sql/sql_udf.cc +++ b/sql/sql_udf.cc @@ -58,6 +58,8 @@ static udf_func *add_udf(LEX_STRING *name, Item_result ret, char *dl, Item_udftype typ); static void del_udf(udf_func *udf); static void *find_udf_dl(const char *dl); +static bool find_udf_everywhere(THD* thd, const char *name, uint len, + TABLE *table); static char *init_syms(udf_func *tmp, char *nm) { @@ -417,6 +419,45 @@ static udf_func *add_udf(LEX_STRING *name, Item_result ret, char *dl, return tmp; } +/** + Find record with the udf in the udf func table + + @param exact_name_str udf name + @param exact_name_len udf name length + @param table table of mysql.func + + @retval TRUE found + @retral FALSE not found +*/ + +static bool find_udf_in_table(const char *exact_name_str, uint exact_name_len, + TABLE *table) +{ + table->use_all_columns(); + table->field[0]->store(exact_name_str, exact_name_len, &my_charset_bin); + return (!table->file->ha_index_read_idx_map(table->record[0], 0, + (uchar*) table->field[0]->ptr, + HA_WHOLE_KEY, + HA_READ_KEY_EXACT)); +} + +static bool remove_udf_in_table(const char *exact_name_str, + uint exact_name_len, + TABLE *table) +{ + if (find_udf_in_table(exact_name_str, exact_name_len, table)) + { + int error; + if ((error= table->file->ha_delete_row(table->record[0]))) + { + table->file->print_error(error, MYF(0)); + return TRUE; + } + } + return FALSE; +} + + /* Drop user defined function. @@ -447,18 +488,21 @@ static int mysql_drop_function_internal(THD *thd, udf_func *udf, TABLE *table) if (!table) DBUG_RETURN(1); - table->use_all_columns(); - table->field[0]->store(exact_name_str, exact_name_len, &my_charset_bin); - if (!table->file->ha_index_read_idx_map(table->record[0], 0, - (uchar*) table->field[0]->ptr, - HA_WHOLE_KEY, - HA_READ_KEY_EXACT)) - { - int error; - if ((error= table->file->ha_delete_row(table->record[0]))) - table->file->print_error(error, MYF(0)); - } - DBUG_RETURN(0); + bool ret= remove_udf_in_table(exact_name_str, exact_name_len, table); + DBUG_RETURN(ret); +} + + +static TABLE *open_udf_func_table(THD *thd) +{ + TABLE_LIST tables; + TABLE *table; + + tables.init_one_table(STRING_WITH_LEN("mysql"), STRING_WITH_LEN("func"), + "func", TL_WRITE); + table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT); + + return table; } @@ -505,9 +549,7 @@ int mysql_create_function(THD *thd,udf_func *udf) if (check_ident_length(&udf->name)) DBUG_RETURN(1); - tables.init_one_table(STRING_WITH_LEN("mysql"), STRING_WITH_LEN("func"), - "func", TL_WRITE); - table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT); + table= open_udf_func_table(thd); mysql_rwlock_wrlock(&THR_LOCK_udf); DEBUG_SYNC(current_thd, "mysql_create_function_after_lock"); @@ -606,43 +648,59 @@ err: } -int mysql_drop_function(THD *thd,const LEX_STRING *udf_name) +enum drop_udf_result mysql_drop_function(THD *thd, const LEX_STRING *udf_name) { TABLE *table; - TABLE_LIST tables; udf_func *udf; DBUG_ENTER("mysql_drop_function"); + if (!(table= open_udf_func_table(thd))) + DBUG_RETURN(UDF_DEL_RESULT_ERROR); + + // Fast pre-check + if (!mysql_rwlock_tryrdlock(&THR_LOCK_udf)) + { + bool found=find_udf_everywhere(thd, udf_name->str, udf_name->length, table); + mysql_rwlock_unlock(&THR_LOCK_udf); + if (!found) + { + close_mysql_tables(thd); + DBUG_RETURN(UDF_DEL_RESULT_ABSENT); + } + } + if (!initialized) { + close_mysql_tables(thd); if (opt_noacl) - my_error(ER_FUNCTION_NOT_DEFINED, MYF(0), udf_name->str); - else - my_message(ER_OUT_OF_RESOURCES, ER_THD(thd, ER_OUT_OF_RESOURCES), - MYF(0)); - DBUG_RETURN(1); - } + DBUG_RETURN(UDF_DEL_RESULT_ABSENT); // SP should be checked - tables.init_one_table(STRING_WITH_LEN("mysql"), STRING_WITH_LEN("func"), - "func", TL_WRITE); - table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT); + my_message(ER_OUT_OF_RESOURCES, ER_THD(thd, ER_OUT_OF_RESOURCES), MYF(0)); + DBUG_RETURN(UDF_DEL_RESULT_ERROR); + } mysql_rwlock_wrlock(&THR_LOCK_udf); + + // re-check under protection + if (!find_udf_everywhere(thd, udf_name->str, udf_name->length, table)) + { + close_mysql_tables(thd); + mysql_rwlock_unlock(&THR_LOCK_udf); + DBUG_RETURN(UDF_DEL_RESULT_ABSENT); + } + + if (check_access(thd, DELETE_ACL, "mysql", NULL, NULL, 1, 0)) + goto err; + + DEBUG_SYNC(current_thd, "mysql_drop_function_after_lock"); + if (!(udf= (udf_func*) my_hash_search(&udf_hash, (uchar*) udf_name->str, (uint) udf_name->length)) ) { - if (thd->lex->check_exists) - { - push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, - ER_FUNCTION_NOT_DEFINED, - ER_THD(thd, ER_FUNCTION_NOT_DEFINED), - udf_name->str); - goto done; - } - - my_error(ER_FUNCTION_NOT_DEFINED, MYF(0), udf_name->str); - goto err; + if (remove_udf_in_table(udf_name->str, (uint) udf_name->length, table)) + goto err; + goto done; } if (mysql_drop_function_internal(thd, udf, table)) @@ -656,13 +714,24 @@ done: while binlogging, to avoid binlog inconsistency. */ if (write_bin_log(thd, TRUE, thd->query(), thd->query_length())) - DBUG_RETURN(1); + DBUG_RETURN(UDF_DEL_RESULT_ERROR); - DBUG_RETURN(0); + close_mysql_tables(thd); + DBUG_RETURN(UDF_DEL_RESULT_DELETED); err: + close_mysql_tables(thd); mysql_rwlock_unlock(&THR_LOCK_udf); - DBUG_RETURN(1); + DBUG_RETURN(UDF_DEL_RESULT_ERROR); +} + +static bool find_udf_everywhere(THD* thd, const char *name, uint len, + TABLE *table) +{ + if (initialized && my_hash_search(&udf_hash, (uchar*) name, len)) + return true; + + return find_udf_in_table(name, len, table); } #endif /* HAVE_DLOPEN */ diff --git a/sql/sql_udf.h b/sql/sql_udf.h index a683b8a7554..bfa03dcce33 100644 --- a/sql/sql_udf.h +++ b/sql/sql_udf.h @@ -140,7 +140,13 @@ void udf_init(void),udf_free(void); udf_func *find_udf(const char *name, uint len=0,bool mark_used=0); void free_udf(udf_func *udf); int mysql_create_function(THD *thd,udf_func *udf); -int mysql_drop_function(THD *thd,const LEX_STRING *name); +enum drop_udf_result +{ + UDF_DEL_RESULT_ABSENT, + UDF_DEL_RESULT_DELETED, + UDF_DEL_RESULT_ERROR +}; +enum drop_udf_result mysql_drop_function(THD *thd,const LEX_STRING *name); #else static inline void udf_init(void) { } static inline void udf_free(void) { } |