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).
This commit is contained in:
Oleksandr Byelkin 2020-07-29 21:54:24 +02:00
parent 95bb3cb886
commit bba22543b1
5 changed files with 164 additions and 56 deletions

View file

@ -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

View file

@ -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

View file

@ -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)
{
if (check_access(thd, DELETE_ACL, "mysql", NULL, NULL, 1, 0))
goto error;
if (!(res = mysql_drop_function(thd, &lex->spname->m_name)))
enum drop_udf_result rc= mysql_drop_function(thd,
&lex->spname->m_name);
if (rc == UDF_DEL_RESULT_DELETED)
{
my_ok(thd);
break;
}
my_error(ER_SP_DROP_FAILED, MYF(0),
"FUNCTION (UDF)", lex->spname->m_name.str);
goto error;
}
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())

View file

@ -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));
bool ret= remove_udf_in_table(exact_name_str, exact_name_len, table);
DBUG_RETURN(ret);
}
DBUG_RETURN(0);
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 (!initialized)
if (!(table= open_udf_func_table(thd)))
DBUG_RETURN(UDF_DEL_RESULT_ERROR);
// Fast pre-check
if (!mysql_rwlock_tryrdlock(&THR_LOCK_udf))
{
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);
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);
}
}
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);
if (!initialized)
{
close_mysql_tables(thd);
if (opt_noacl)
DBUG_RETURN(UDF_DEL_RESULT_ABSENT); // SP should be checked
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);
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 */

View file

@ -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) { }