Skip to content

Commit c10127a

Browse files
committed
Bug #26173244: THE UDF INSTALL SERVICE CAN'T BE USED AT
PLUGIN INSTALLS Split the UDF initialization/deinitialization into two: 1. Initialization/deinitialization of the global structures 2. Loading of the UDF definitions from the table and removing them from the global Then kept the #2 at the place of the current initialization/deinitalization routines and added #2 initialization very early (before component/plugin initialization) and #2 deinitialization very late (after the plugin/compononent deinitialization. Added a test plugin and a regression test.
1 parent 69ff70a commit c10127a

File tree

8 files changed

+211
-27
lines changed

8 files changed

+211
-27
lines changed

mysql-test/include/plugin.defs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ rewrite_example plugin_output_directory REWRITE_EXAMPLE
5555
rewriter plugin_output_directory REWRITER
5656
mysql_no_login plugin_output_directory MYSQL_NO_LOGIN mysql_no_login
5757
test_udf_services plugin_output_directory TESTUDFSERVICES
58+
test_udf_services plugin_output_directory TESTUDFREGISTRATION test_udf_registration
5859
group_replication plugin_output_directory GROUP_REPLICATION
5960
locking_service plugin_output_directory LOCKING_SERVICE
6061
version_token plugin_output_directory VERSION_TOKEN
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
INSTALL PLUGIN test_udf_registration SONAME 'test_udf_services.so';
2+
# Must work
3+
SELECT test_udf_registration_udf();
4+
test_udf_registration_udf()
5+
0
6+
UNINSTALL PLUGIN test_udf_registration;
7+
# Must fail: no UDF
8+
SELECT test_udf_registration_udf();
9+
ERROR 42000: FUNCTION test.test_udf_registration_udf does not exist
10+
INSTALL PLUGIN test_udf_registration SONAME 'test_udf_services.so';
11+
# Restart the server
12+
# restart
13+
# Must work after a restart
14+
SELECT test_udf_registration_udf();
15+
test_udf_registration_udf()
16+
0
17+
# Cleanup
18+
UNINSTALL PLUGIN test_udf_registration;
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
$TESTUDFREGISTRATION_OPT
2+
--local-infile=true
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Save the initial number of concurrent sessions
2+
--source include/count_sessions.inc
3+
4+
--replace_regex /\.dll/.so/
5+
eval INSTALL PLUGIN test_udf_registration SONAME '$TESTUDFREGISTRATION';
6+
7+
--echo # Must work
8+
SELECT test_udf_registration_udf();
9+
10+
UNINSTALL PLUGIN test_udf_registration;
11+
12+
--echo # Must fail: no UDF
13+
--error ER_SP_DOES_NOT_EXIST
14+
SELECT test_udf_registration_udf();
15+
16+
--replace_regex /\.dll/.so/
17+
eval INSTALL PLUGIN test_udf_registration SONAME '$TESTUDFREGISTRATION';
18+
19+
20+
--echo # Restart the server
21+
--source include/restart_mysqld.inc
22+
23+
24+
--echo # Must work after a restart
25+
SELECT test_udf_registration_udf();
26+
27+
--echo # Cleanup
28+
UNINSTALL PLUGIN test_udf_registration;

plugin/udf_services/test_udf_services.cc

Lines changed: 92 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
#include "my_compiler.h"
2222
#include "my_inttypes.h"
2323

24+
static int test_udf_registration_init(MYSQL_PLUGIN p);
25+
static int test_udf_registration_deinit(MYSQL_PLUGIN p);
26+
2427
/**
2528
@file test_udf_services.cc
2629
@@ -36,6 +39,9 @@
3639
static struct st_mysql_daemon test_udf_services_plugin=
3740
{ MYSQL_DAEMON_INTERFACE_VERSION };
3841

42+
static struct st_mysql_daemon test_udf_registration_plugin=
43+
{ MYSQL_DAEMON_INTERFACE_VERSION };
44+
3945
mysql_declare_plugin(test_udf_services)
4046
{
4147
MYSQL_DAEMON_PLUGIN,
@@ -52,6 +58,22 @@ mysql_declare_plugin(test_udf_services)
5258
NULL, /* system variables */
5359
NULL, /* config options */
5460
0, /* flags */
61+
},
62+
{
63+
MYSQL_DAEMON_PLUGIN,
64+
&test_udf_registration_plugin,
65+
"test_udf_registration",
66+
"Georgi Kodinov",
67+
"MySQL mtr test framework",
68+
PLUGIN_LICENSE_GPL,
69+
test_udf_registration_init, /* Plugin Init */
70+
NULL, /* Plugin Check uninstall */
71+
test_udf_registration_deinit, /* Plugin Deinit */
72+
0x0100, /* Plugin version: 1.0 */
73+
NULL, /* status variables */
74+
NULL, /* system variables */
75+
NULL, /* config options */
76+
0, /* flags */
5577
}
5678
mysql_declare_plugin_end;
5779

@@ -78,7 +100,7 @@ test_udf_services_udf_init(UDF_INIT *initid MY_ATTRIBUTE((unused)),
78100
UDF_ARGS *args MY_ATTRIBUTE((unused)),
79101
char *message MY_ATTRIBUTE((unused)))
80102
{
81-
return FALSE;
103+
return false;
82104
}
83105

84106

@@ -103,3 +125,72 @@ test_udf_services_udf(UDF_INIT *initid MY_ATTRIBUTE((unused)),
103125
my_snprintf(buffer, sizeof(buffer), "test");
104126
return 0;
105127
}
128+
129+
#include <mysql/service_plugin_registry.h>
130+
#include <mysql/components/my_service.h>
131+
#include <mysql/components/services/udf_registration.h>
132+
133+
/** Sample plugin init function that registers a UDF */
134+
static int test_udf_registration_init(MYSQL_PLUGIN /*p */)
135+
{
136+
SERVICE_TYPE(registry) *reg;
137+
SERVICE_TYPE(udf_registration) *udf;
138+
bool ret= false;
139+
140+
reg= mysql_plugin_registry_acquire();
141+
if (!reg)
142+
{
143+
ret= true;
144+
goto end;
145+
}
146+
reg->acquire("udf_registration", (my_h_service *) &udf);
147+
if (!udf)
148+
{
149+
ret= true;
150+
goto end;
151+
}
152+
ret= udf->udf_register("test_udf_registration_udf", INT_RESULT,
153+
(Udf_func_any) test_udf_services_udf,
154+
test_udf_services_udf_init,
155+
NULL);
156+
157+
reg->release((my_h_service) udf);
158+
end:
159+
if (reg)
160+
mysql_plugin_registry_release(reg);
161+
return ret ? 1 : 0;
162+
}
163+
164+
165+
/** Sample plugin init function that unregisters a UDF */
166+
static int test_udf_registration_deinit(MYSQL_PLUGIN /* p */)
167+
{
168+
SERVICE_TYPE(registry) *reg;
169+
SERVICE_TYPE(udf_registration) *udf;
170+
bool ret= false;
171+
int was_present;
172+
173+
reg= mysql_plugin_registry_acquire();
174+
if (!reg)
175+
{
176+
ret= true;
177+
goto end;
178+
}
179+
reg->acquire("udf_registration", (my_h_service *) &udf);
180+
if (!udf)
181+
{
182+
ret= true;
183+
goto end;
184+
}
185+
186+
ret= udf->udf_unregister("test_udf_registration_udf", &was_present);
187+
188+
end:
189+
if (reg)
190+
{
191+
if (udf)
192+
reg->release((my_h_service) udf);
193+
mysql_plugin_registry_release(reg);
194+
}
195+
return ret ? 1 : 0;
196+
}

sql/mysqld.cc

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1973,7 +1973,7 @@ static void clean_up(bool print_message)
19731973
lex_free(); /* Free some memory */
19741974
item_create_cleanup();
19751975
if (!opt_noacl)
1976-
udf_deinit();
1976+
udf_unload_udfs();
19771977
table_def_start_shutdown();
19781978
plugin_shutdown();
19791979
gtid_server_cleanup(); // after plugin_shutdown
@@ -2065,6 +2065,7 @@ static void clean_up(bool print_message)
20652065

20662066
persisted_variables_cache.cleanup();
20672067

2068+
udf_deinit_globals();
20682069
/*
20692070
The following lines may never be executed as the main thread may have
20702071
killed us
@@ -4587,6 +4588,13 @@ static int init_server_components()
45874588
unireg_abort(MYSQLD_ABORT_EXIT);
45884589
}
45894590

4591+
/*
4592+
We need to initialize the UDF globals early before reading the proc table
4593+
and before the server component initialization to allow other components
4594+
to register their UDFs at init time and de-register them at deinit time.
4595+
*/
4596+
udf_init_globals();
4597+
45904598
/*
45914599
Set tc_log to point to TC_LOG_DUMMY early in order to allow plugin_init()
45924600
to commit attachable transaction after reading from mysql.plugin table.
@@ -5796,7 +5804,7 @@ int mysqld_main(int argc, char **argv)
57965804

57975805
if (!opt_noacl)
57985806
{
5799-
udf_init();
5807+
udf_read_functions_table();
58005808
}
58015809

58025810
init_status_vars();

sql/sql_udf.cc

Lines changed: 56 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -173,42 +173,62 @@ static void init_udf_psi_keys(void)
173173
}
174174
#endif
175175

176+
/**
177+
Initialize the UDF global structures.
178+
This is done as a separate step so that the UDF registration
179+
service can work when initalizing plugins, which happens
180+
before reading the UDF table.
181+
*/
182+
void udf_init_globals()
183+
{
184+
DBUG_ENTER("udf_init_globals");
185+
if (initialized)
186+
DBUG_VOID_RETURN;
187+
188+
#ifdef HAVE_PSI_INTERFACE
189+
init_udf_psi_keys();
190+
#endif
191+
192+
mysql_rwlock_init(key_rwlock_THR_LOCK_udf, &THR_LOCK_udf);
193+
init_sql_alloc(key_memory_udf_mem, &mem, UDF_ALLOC_BLOCK_SIZE, 0);
194+
195+
udf_hash= new collation_unordered_map<std::string, udf_func *>(
196+
system_charset_info, key_memory_udf_mem);
197+
initialized= 1;
198+
199+
DBUG_VOID_RETURN;
200+
}
201+
202+
176203
/*
177204
Read all predeclared functions from mysql.func and accept all that
178205
can be used.
206+
The global structures must be initialized first.
179207
*/
180-
181-
void udf_init()
208+
void udf_read_functions_table()
182209
{
183210
udf_func *tmp;
184211
TABLE_LIST tables;
185212
READ_RECORD read_record_info;
186213
TABLE *table;
187214
int error;
188-
DBUG_ENTER("ufd_init");
215+
DBUG_ENTER("ufd_read_functions_table");
189216
char db[]= "mysql"; /* A subject to casednstr, can't be constant */
190217

191-
if (initialized)
218+
if (!initialized)
219+
{
220+
DBUG_ASSERT("wrong init order: trying to read the UDFs without initializaton");
192221
DBUG_VOID_RETURN;
193-
194-
#ifdef HAVE_PSI_INTERFACE
195-
init_udf_psi_keys();
196-
#endif
197-
198-
mysql_rwlock_init(key_rwlock_THR_LOCK_udf, &THR_LOCK_udf);
199-
init_sql_alloc(key_memory_udf_mem, &mem, UDF_ALLOC_BLOCK_SIZE, 0);
222+
}
200223

201224
THD *new_thd = new(std::nothrow) THD;
202225
if (new_thd == nullptr)
203226
{
204227
LogErr(ERROR_LEVEL, ER_UDF_CANT_ALLOC_FOR_STRUCTURES);
205-
free_root(&mem,MYF(0));
228+
free_root(&mem, MYF(0));
206229
delete new_thd;
207230
DBUG_VOID_RETURN;
208231
}
209-
udf_hash= new collation_unordered_map<std::string, udf_func*>(
210-
system_charset_info, key_memory_udf_mem);
211-
initialized = 1;
212232
new_thd->thread_stack= (char*) &new_thd;
213233
new_thd->store_globals();
214234
{
@@ -312,16 +332,11 @@ void udf_init()
312332
/**
313333
Deintialize the UDF subsystem.
314334
315-
This function does the following:
316-
1. Closes the shared libaries.
317-
2. Free the UDF hash.
318-
3. Free the memroot allocated.
319-
4. Destroy the RW mutex object.
335+
This function closes the shared libaries.
320336
*/
321-
void udf_deinit()
337+
void udf_unload_udfs()
322338
{
323-
/* close all shared libraries */
324-
DBUG_ENTER("udf_free");
339+
DBUG_ENTER("udf_unload_udfs");
325340
if (udf_hash != nullptr)
326341
{
327342
for (auto it1= udf_hash->begin(); it1 != udf_hash->end(); ++it1)
@@ -339,6 +354,24 @@ void udf_deinit()
339354
dlclose(udf->dlhandle);
340355
}
341356
}
357+
}
358+
DBUG_VOID_RETURN;
359+
}
360+
361+
362+
/**
363+
Deintialize the UDF subsystem.
364+
365+
This function does the following:
366+
1. Free the UDF hash.
367+
2. Free the memroot allocated.
368+
3. Destroy the RW mutex object.
369+
*/
370+
void udf_deinit_globals()
371+
{
372+
DBUG_ENTER("udf_deinit_globals");
373+
if (udf_hash != nullptr)
374+
{
342375
delete udf_hash;
343376
udf_hash= nullptr;
344377
}

sql/sql_udf.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,10 @@ class udf_handler :public Sql_alloc
136136
};
137137

138138

139-
void udf_init(void),udf_deinit(void);
139+
void udf_init_globals();
140+
void udf_read_functions_table();
141+
void udf_unload_udfs();
142+
void udf_deinit_globals();
140143
udf_func *find_udf(const char *name, size_t len=0,bool mark_used=0);
141144
void free_udf(udf_func *udf);
142145
bool mysql_create_function(THD *thd, udf_func *udf);

0 commit comments

Comments
 (0)