mirror of
https://github.com/MariaDB/server.git
synced 2025-08-01 03:47:19 +03:00
MDEV-4403 Attempting to use cassandra storage engine causes "service 'my_snprintf_service' interface version mismatch"
When a DSO is loaded we rewrite service pointers to point to the actual service structures. But when a DSO is unloaded, we have to restore their original values, in case this DSO wasn't removed from memory on dlclose() and is later loaded again.
This commit is contained in:
14
mysql-test/suite/plugins/r/cassandra_reinstall.result
Normal file
14
mysql-test/suite/plugins/r/cassandra_reinstall.result
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
install soname 'ha_cassandra';
|
||||||
|
select plugin_name,plugin_status,plugin_library from information_schema.plugins where plugin_name = 'cassandra';
|
||||||
|
plugin_name plugin_status plugin_library
|
||||||
|
CASSANDRA ACTIVE ha_cassandra.so
|
||||||
|
uninstall plugin cassandra;
|
||||||
|
select plugin_name,plugin_status,plugin_library from information_schema.plugins where plugin_name = 'cassandra';
|
||||||
|
plugin_name plugin_status plugin_library
|
||||||
|
install soname 'ha_cassandra';
|
||||||
|
select plugin_name,plugin_status,plugin_library from information_schema.plugins where plugin_name = 'cassandra';
|
||||||
|
plugin_name plugin_status plugin_library
|
||||||
|
CASSANDRA ACTIVE ha_cassandra.so
|
||||||
|
uninstall plugin cassandra;
|
||||||
|
select plugin_name,plugin_status,plugin_library from information_schema.plugins where plugin_name = 'cassandra';
|
||||||
|
plugin_name plugin_status plugin_library
|
16
mysql-test/suite/plugins/t/cassandra_reinstall.test
Normal file
16
mysql-test/suite/plugins/t/cassandra_reinstall.test
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#
|
||||||
|
# MDEV-4403 Attempting to use cassandra storage engine causes "service 'my_snprintf_service' interface version mismatch"
|
||||||
|
#
|
||||||
|
if (!$HA_CASSANDRA_SO) {
|
||||||
|
skip No Cassandra engine;
|
||||||
|
}
|
||||||
|
|
||||||
|
install soname 'ha_cassandra';
|
||||||
|
select plugin_name,plugin_status,plugin_library from information_schema.plugins where plugin_name = 'cassandra';
|
||||||
|
uninstall plugin cassandra;
|
||||||
|
select plugin_name,plugin_status,plugin_library from information_schema.plugins where plugin_name = 'cassandra';
|
||||||
|
install soname 'ha_cassandra';
|
||||||
|
select plugin_name,plugin_status,plugin_library from information_schema.plugins where plugin_name = 'cassandra';
|
||||||
|
uninstall plugin cassandra;
|
||||||
|
select plugin_name,plugin_status,plugin_library from information_schema.plugins where plugin_name = 'cassandra';
|
||||||
|
|
@ -309,6 +309,7 @@ static void unlock_variables(THD *thd, struct system_variables *vars);
|
|||||||
static void cleanup_variables(THD *thd, struct system_variables *vars);
|
static void cleanup_variables(THD *thd, struct system_variables *vars);
|
||||||
static void plugin_vars_free_values(sys_var *vars);
|
static void plugin_vars_free_values(sys_var *vars);
|
||||||
static void restore_pluginvar_names(sys_var *first);
|
static void restore_pluginvar_names(sys_var *first);
|
||||||
|
static void restore_ptr_backup(uint n, st_ptr_backup *backup);
|
||||||
static plugin_ref intern_plugin_lock(LEX *lex, plugin_ref plugin);
|
static plugin_ref intern_plugin_lock(LEX *lex, plugin_ref plugin);
|
||||||
static void intern_plugin_unlock(LEX *lex, plugin_ref plugin);
|
static void intern_plugin_unlock(LEX *lex, plugin_ref plugin);
|
||||||
static void reap_plugins(void);
|
static void reap_plugins(void);
|
||||||
@ -473,9 +474,16 @@ static st_plugin_dl *plugin_dl_insert_or_reuse(struct st_plugin_dl *plugin_dl)
|
|||||||
#endif /* HAVE_DLOPEN */
|
#endif /* HAVE_DLOPEN */
|
||||||
|
|
||||||
|
|
||||||
static inline void free_plugin_mem(struct st_plugin_dl *p)
|
static void free_plugin_mem(struct st_plugin_dl *p)
|
||||||
{
|
{
|
||||||
#ifdef HAVE_DLOPEN
|
#ifdef HAVE_DLOPEN
|
||||||
|
if (p->ptr_backup)
|
||||||
|
{
|
||||||
|
DBUG_ASSERT(p->nbackups);
|
||||||
|
DBUG_ASSERT(p->handle);
|
||||||
|
restore_ptr_backup(p->nbackups, p->ptr_backup);
|
||||||
|
my_free(p->ptr_backup);
|
||||||
|
}
|
||||||
if (p->handle)
|
if (p->handle)
|
||||||
dlclose(p->handle);
|
dlclose(p->handle);
|
||||||
#endif
|
#endif
|
||||||
@ -706,6 +714,7 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report)
|
|||||||
uint plugin_dir_len, dummy_errors, dlpathlen, i;
|
uint plugin_dir_len, dummy_errors, dlpathlen, i;
|
||||||
struct st_plugin_dl *tmp= 0, plugin_dl;
|
struct st_plugin_dl *tmp= 0, plugin_dl;
|
||||||
void *sym;
|
void *sym;
|
||||||
|
st_ptr_backup tmp_backup[array_elements(list_of_services)];
|
||||||
DBUG_ENTER("plugin_dl_add");
|
DBUG_ENTER("plugin_dl_add");
|
||||||
DBUG_PRINT("enter", ("dl->str: '%s', dl->length: %d",
|
DBUG_PRINT("enter", ("dl->str: '%s', dl->length: %d",
|
||||||
dl->str, (int) dl->length));
|
dl->str, (int) dl->length));
|
||||||
@ -772,7 +781,8 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report)
|
|||||||
{
|
{
|
||||||
if ((sym= dlsym(plugin_dl.handle, list_of_services[i].name)))
|
if ((sym= dlsym(plugin_dl.handle, list_of_services[i].name)))
|
||||||
{
|
{
|
||||||
uint ver= (uint)(intptr)*(void**)sym;
|
void **ptr= (void **)sym;
|
||||||
|
uint ver= (uint)(intptr)*ptr;
|
||||||
if (ver > list_of_services[i].version ||
|
if (ver > list_of_services[i].version ||
|
||||||
(ver >> 8) < (list_of_services[i].version >> 8))
|
(ver >> 8) < (list_of_services[i].version >> 8))
|
||||||
{
|
{
|
||||||
@ -783,10 +793,24 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report)
|
|||||||
report_error(report, ER_CANT_OPEN_LIBRARY, dlpath, ENOEXEC, buf);
|
report_error(report, ER_CANT_OPEN_LIBRARY, dlpath, ENOEXEC, buf);
|
||||||
goto ret;
|
goto ret;
|
||||||
}
|
}
|
||||||
*(void**)sym= list_of_services[i].service;
|
tmp_backup[plugin_dl.nbackups++].save(ptr);
|
||||||
|
*ptr= list_of_services[i].service;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (plugin_dl.nbackups)
|
||||||
|
{
|
||||||
|
size_t bytes= plugin_dl.nbackups * sizeof(plugin_dl.ptr_backup[0]);
|
||||||
|
plugin_dl.ptr_backup= (st_ptr_backup *)my_malloc(bytes, MYF(0));
|
||||||
|
if (!plugin_dl.ptr_backup)
|
||||||
|
{
|
||||||
|
restore_ptr_backup(plugin_dl.nbackups, tmp_backup);
|
||||||
|
report_error(report, ER_OUTOFMEMORY, bytes);
|
||||||
|
goto ret;
|
||||||
|
}
|
||||||
|
memcpy(plugin_dl.ptr_backup, tmp_backup, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
/* Duplicate and convert dll name */
|
/* Duplicate and convert dll name */
|
||||||
plugin_dl.dl.length= dl->length * files_charset_info->mbmaxlen + 1;
|
plugin_dl.dl.length= dl->length * files_charset_info->mbmaxlen + 1;
|
||||||
if (! (plugin_dl.dl.str= (char*) my_malloc(plugin_dl.dl.length, MYF(0))))
|
if (! (plugin_dl.dl.str= (char*) my_malloc(plugin_dl.dl.length, MYF(0))))
|
||||||
@ -4017,3 +4041,38 @@ sys_var *find_plugin_sysvar(st_plugin_int *plugin, st_mysql_sys_var *plugin_var)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
On dlclose() we need to restore values of all symbols that we've modified in
|
||||||
|
the DSO. The reason is - the DSO might not actually be unloaded, so on the
|
||||||
|
next dlopen() these symbols will have old values, they won't be
|
||||||
|
reinitialized.
|
||||||
|
|
||||||
|
Perhaps, there can be many reason, why a DSO won't be unloaded. Strictly
|
||||||
|
speaking, it's implementation defined whether to unload an unused DSO or to
|
||||||
|
keep it in memory.
|
||||||
|
|
||||||
|
In particular, this happens for some plugins: In 2009 a new ELF stub was
|
||||||
|
introduced, see Ulrich Drepper's email "Unique symbols for C++"
|
||||||
|
http://www.redhat.com/archives/posix-c++-wg/2009-August/msg00002.html
|
||||||
|
|
||||||
|
DSO that has objects with this stub (STB_GNU_UNIQUE) cannot be unloaded
|
||||||
|
(this is mentioned in the email, see the url above).
|
||||||
|
|
||||||
|
These "unique" objects are, for example, static variables in templates,
|
||||||
|
in inline functions, in classes. So any DSO that uses them can
|
||||||
|
only be loaded once. And because Boost has them, any DSO that uses Boost
|
||||||
|
almost certainly cannot be unloaded.
|
||||||
|
|
||||||
|
To know whether a particular DSO has these objects, one can use
|
||||||
|
|
||||||
|
readelf -s /path/to/plugin.so|grep UNIQUE
|
||||||
|
|
||||||
|
There's nothing we can do about it, but to reset the DSO to its initial
|
||||||
|
state before dlclose().
|
||||||
|
*/
|
||||||
|
static void restore_ptr_backup(uint n, st_ptr_backup *backup)
|
||||||
|
{
|
||||||
|
while (n--)
|
||||||
|
(backup++)->restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -81,15 +81,24 @@ typedef struct st_mysql_show_var SHOW_VAR;
|
|||||||
|
|
||||||
/* A handle for the dynamic library containing a plugin or plugins. */
|
/* A handle for the dynamic library containing a plugin or plugins. */
|
||||||
|
|
||||||
|
struct st_ptr_backup {
|
||||||
|
void **ptr;
|
||||||
|
void *value;
|
||||||
|
void save(void **p) { ptr= p; value= *p; }
|
||||||
|
void restore() { *ptr= value; }
|
||||||
|
};
|
||||||
|
|
||||||
struct st_plugin_dl
|
struct st_plugin_dl
|
||||||
{
|
{
|
||||||
LEX_STRING dl;
|
LEX_STRING dl;
|
||||||
void *handle;
|
void *handle;
|
||||||
struct st_maria_plugin *plugins;
|
struct st_maria_plugin *plugins;
|
||||||
|
st_ptr_backup *ptr_backup;
|
||||||
|
uint nbackups;
|
||||||
|
uint ref_count; /* number of plugins loaded from the library */
|
||||||
int mysqlversion;
|
int mysqlversion;
|
||||||
int mariaversion;
|
int mariaversion;
|
||||||
bool allocated;
|
bool allocated;
|
||||||
uint ref_count; /* number of plugins loaded from the library */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* A handle of a plugin */
|
/* A handle of a plugin */
|
||||||
|
Reference in New Issue
Block a user