summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mysql-test/r/udf.result14
-rw-r--r--mysql-test/t/udf.test22
-rw-r--r--sql/sql_parse.cc23
-rw-r--r--sql/sql_udf.cc149
-rw-r--r--sql/sql_udf.h8
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 543c877b7f1..969e33b2353 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -5620,23 +5620,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) { }