Commit bba22543 authored by Oleksandr Byelkin's avatar Oleksandr Byelkin

MDEV-23327 Can't uninstall UDF if the implementation library file doesn't exist

Made cleanup of DROP (udf) FUNCTION procedure and also check of mysql.func (not only loaded udf).
parent 95bb3cb8
...@@ -479,3 +479,17 @@ myfunc_int(max(a) over (order by b) , b) ...@@ -479,3 +479,17 @@ myfunc_int(max(a) over (order by b) , b)
drop view v1; drop view v1;
drop function myfunc_int; drop function myfunc_int;
drop table t1; 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
...@@ -541,3 +541,25 @@ select * from v1; ...@@ -541,3 +541,25 @@ select * from v1;
drop view v1; drop view v1;
drop function myfunc_int; drop function myfunc_int;
drop table t1; 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
...@@ -5623,23 +5623,20 @@ mysql_execute_command(THD *thd) ...@@ -5623,23 +5623,20 @@ mysql_execute_command(THD *thd)
! lex->spname->m_explicit_name) ! lex->spname->m_explicit_name)
{ {
/* DROP FUNCTION <non qualified name> */ /* DROP FUNCTION <non qualified name> */
udf_func *udf = find_udf(lex->spname->m_name.str, enum drop_udf_result rc= mysql_drop_function(thd,
lex->spname->m_name.length); &lex->spname->m_name);
if (udf) if (rc == UDF_DEL_RESULT_DELETED)
{ {
if (check_access(thd, DELETE_ACL, "mysql", NULL, NULL, 1, 0)) my_ok(thd);
goto error; break;
}
if (!(res = mysql_drop_function(thd, &lex->spname->m_name))) if (rc == UDF_DEL_RESULT_ERROR)
{
my_ok(thd);
break;
}
my_error(ER_SP_DROP_FAILED, MYF(0),
"FUNCTION (UDF)", lex->spname->m_name.str);
goto 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->spname->m_db.str == NULL)
{ {
if (lex->if_exists()) if (lex->if_exists())
......
...@@ -58,6 +58,8 @@ static udf_func *add_udf(LEX_STRING *name, Item_result ret, ...@@ -58,6 +58,8 @@ static udf_func *add_udf(LEX_STRING *name, Item_result ret,
char *dl, Item_udftype typ); char *dl, Item_udftype typ);
static void del_udf(udf_func *udf); static void del_udf(udf_func *udf);
static void *find_udf_dl(const char *dl); 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) 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, ...@@ -417,6 +419,45 @@ static udf_func *add_udf(LEX_STRING *name, Item_result ret, char *dl,
return tmp; 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. Drop user defined function.
...@@ -447,18 +488,21 @@ static int mysql_drop_function_internal(THD *thd, udf_func *udf, TABLE *table) ...@@ -447,18 +488,21 @@ static int mysql_drop_function_internal(THD *thd, udf_func *udf, TABLE *table)
if (!table) if (!table)
DBUG_RETURN(1); DBUG_RETURN(1);
table->use_all_columns(); bool ret= remove_udf_in_table(exact_name_str, exact_name_len, table);
table->field[0]->store(exact_name_str, exact_name_len, &my_charset_bin); DBUG_RETURN(ret);
if (!table->file->ha_index_read_idx_map(table->record[0], 0, }
(uchar*) table->field[0]->ptr,
HA_WHOLE_KEY,
HA_READ_KEY_EXACT)) static TABLE *open_udf_func_table(THD *thd)
{ {
int error; TABLE_LIST tables;
if ((error= table->file->ha_delete_row(table->record[0]))) TABLE *table;
table->file->print_error(error, MYF(0));
} tables.init_one_table(STRING_WITH_LEN("mysql"), STRING_WITH_LEN("func"),
DBUG_RETURN(0); "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) ...@@ -505,9 +549,7 @@ int mysql_create_function(THD *thd,udf_func *udf)
if (check_ident_length(&udf->name)) if (check_ident_length(&udf->name))
DBUG_RETURN(1); DBUG_RETURN(1);
tables.init_one_table(STRING_WITH_LEN("mysql"), STRING_WITH_LEN("func"), table= open_udf_func_table(thd);
"func", TL_WRITE);
table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT);
mysql_rwlock_wrlock(&THR_LOCK_udf); mysql_rwlock_wrlock(&THR_LOCK_udf);
DEBUG_SYNC(current_thd, "mysql_create_function_after_lock"); DEBUG_SYNC(current_thd, "mysql_create_function_after_lock");
...@@ -606,43 +648,59 @@ int mysql_create_function(THD *thd,udf_func *udf) ...@@ -606,43 +648,59 @@ int mysql_create_function(THD *thd,udf_func *udf)
} }
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 *table;
TABLE_LIST tables;
udf_func *udf; udf_func *udf;
DBUG_ENTER("mysql_drop_function"); 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) if (!initialized)
{ {
close_mysql_tables(thd);
if (opt_noacl) if (opt_noacl)
my_error(ER_FUNCTION_NOT_DEFINED, MYF(0), udf_name->str); DBUG_RETURN(UDF_DEL_RESULT_ABSENT); // SP should be checked
else
my_message(ER_OUT_OF_RESOURCES, ER_THD(thd, ER_OUT_OF_RESOURCES),
MYF(0));
DBUG_RETURN(1);
}
tables.init_one_table(STRING_WITH_LEN("mysql"), STRING_WITH_LEN("func"), my_message(ER_OUT_OF_RESOURCES, ER_THD(thd, ER_OUT_OF_RESOURCES), MYF(0));
"func", TL_WRITE); DBUG_RETURN(UDF_DEL_RESULT_ERROR);
table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT); }
mysql_rwlock_wrlock(&THR_LOCK_udf); 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"); DEBUG_SYNC(current_thd, "mysql_drop_function_after_lock");
if (!(udf= (udf_func*) my_hash_search(&udf_hash, (uchar*) udf_name->str, if (!(udf= (udf_func*) my_hash_search(&udf_hash, (uchar*) udf_name->str,
(uint) udf_name->length)) ) (uint) udf_name->length)) )
{ {
if (thd->lex->check_exists) if (remove_udf_in_table(udf_name->str, (uint) udf_name->length, table))
{ goto err;
push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, goto done;
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 (mysql_drop_function_internal(thd, udf, table)) if (mysql_drop_function_internal(thd, udf, table))
...@@ -656,13 +714,24 @@ int mysql_drop_function(THD *thd,const LEX_STRING *udf_name) ...@@ -656,13 +714,24 @@ int mysql_drop_function(THD *thd,const LEX_STRING *udf_name)
while binlogging, to avoid binlog inconsistency. while binlogging, to avoid binlog inconsistency.
*/ */
if (write_bin_log(thd, TRUE, thd->query(), thd->query_length())) 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: err:
close_mysql_tables(thd);
mysql_rwlock_unlock(&THR_LOCK_udf); 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 */ #endif /* HAVE_DLOPEN */
...@@ -140,7 +140,13 @@ void udf_init(void),udf_free(void); ...@@ -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); udf_func *find_udf(const char *name, uint len=0,bool mark_used=0);
void free_udf(udf_func *udf); void free_udf(udf_func *udf);
int mysql_create_function(THD *thd,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 #else
static inline void udf_init(void) { } static inline void udf_init(void) { }
static inline void udf_free(void) { } static inline void udf_free(void) { }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment