Skip to content

Commit

Permalink
MDEV-12179: Per-engine mysql.gtid_slave_pos table
Browse files Browse the repository at this point in the history
Intermediate commit.

Implement a --gtid-pos-auto-engines system variable. The variable is a list
of engines for which mysql.gtid_slave_pos_ENGINE should be auto-created if
needed.

This commit only implements the option variable. It is not yet used to
actually auto-create any tables, nor is there a corresponding command-line
version of the option yet.
  • Loading branch information
knielsen committed Apr 21, 2017
1 parent 6a84473 commit 363d6a1
Show file tree
Hide file tree
Showing 9 changed files with 384 additions and 0 deletions.
33 changes: 33 additions & 0 deletions mysql-test/suite/rpl/r/rpl_mdev12179.result
Original file line number Diff line number Diff line change
@@ -1,6 +1,39 @@
include/rpl_init.inc [topology=1->2]
SET GLOBAL gtid_pos_auto_engines="innodb";
ERROR HY000: This operation cannot be performed as you have a running slave ''; run STOP SLAVE '' first
include/stop_slave.inc
CHANGE MASTER TO master_use_gtid=slave_pos;
SELECT @@gtid_pos_auto_engines;
@@gtid_pos_auto_engines

SELECT @@SESSION.gtid_pos_auto_engines;
ERROR HY000: Variable 'gtid_pos_auto_engines' is a GLOBAL variable
SET GLOBAL gtid_pos_auto_engines= NULL;
ERROR 42000: Variable 'gtid_pos_auto_engines' can't be set to the value of 'NULL'
SET GLOBAL gtid_pos_auto_engines="innodb";
SELECT @@gtid_pos_auto_engines;
@@gtid_pos_auto_engines
InnoDB
SET GLOBAL gtid_pos_auto_engines="myisam,innodb";
SELECT @@gtid_pos_auto_engines;
@@gtid_pos_auto_engines
MyISAM,InnoDB
SET GLOBAL gtid_pos_auto_engines="innodb,myisam";
SELECT @@gtid_pos_auto_engines;
@@gtid_pos_auto_engines
InnoDB,MyISAM
SET GLOBAL gtid_pos_auto_engines="innodb,innodb,myisam,innodb,myisam,myisam,innodb";
SELECT @@gtid_pos_auto_engines;
@@gtid_pos_auto_engines
InnoDB,MyISAM
SET GLOBAL gtid_pos_auto_engines=DEFAULT;
SELECT @@gtid_pos_auto_engines;
@@gtid_pos_auto_engines

SET GLOBAL gtid_pos_auto_engines="";
SELECT @@gtid_pos_auto_engines;
@@gtid_pos_auto_engines

include/start_slave.inc
CREATE TABLE t1 (a INT PRIMARY KEY);
INSERT INTO t1 VALUES (1);
Expand Down
22 changes: 22 additions & 0 deletions mysql-test/suite/rpl/t/rpl_mdev12179.test
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,30 @@
--source include/rpl_init.inc

--connection server_2
--error ER_SLAVE_MUST_STOP
SET GLOBAL gtid_pos_auto_engines="innodb";
--source include/stop_slave.inc
CHANGE MASTER TO master_use_gtid=slave_pos;

# Test the @@gtid_pos_auto_engines sysvar.
SELECT @@gtid_pos_auto_engines;
--error ER_INCORRECT_GLOBAL_LOCAL_VAR
SELECT @@SESSION.gtid_pos_auto_engines;
--error ER_WRONG_VALUE_FOR_VAR
SET GLOBAL gtid_pos_auto_engines= NULL;
SET GLOBAL gtid_pos_auto_engines="innodb";
SELECT @@gtid_pos_auto_engines;
SET GLOBAL gtid_pos_auto_engines="myisam,innodb";
SELECT @@gtid_pos_auto_engines;
SET GLOBAL gtid_pos_auto_engines="innodb,myisam";
SELECT @@gtid_pos_auto_engines;
SET GLOBAL gtid_pos_auto_engines="innodb,innodb,myisam,innodb,myisam,myisam,innodb";
SELECT @@gtid_pos_auto_engines;
SET GLOBAL gtid_pos_auto_engines=DEFAULT;
SELECT @@gtid_pos_auto_engines;
SET GLOBAL gtid_pos_auto_engines="";
SELECT @@gtid_pos_auto_engines;

--source include/start_slave.inc

--connection server_1
Expand Down
3 changes: 3 additions & 0 deletions sql/mysqld.cc
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,8 @@ char *my_bind_addr_str;
static char *default_collation_name;
char *default_storage_engine, *default_tmp_storage_engine;
char *enforced_storage_engine=NULL;
char *gtid_pos_auto_engines;
plugin_ref *opt_gtid_pos_auto_plugins;
static char compiled_default_collation_name[]= MYSQL_DEFAULT_COLLATION_NAME;
static I_List<CONNECT> thread_cache;
static bool binlog_format_used= false;
Expand Down Expand Up @@ -4234,6 +4236,7 @@ static int init_common_variables()
default_storage_engine= const_cast<char *>("MyISAM");
#endif
default_tmp_storage_engine= NULL;
gtid_pos_auto_engines= const_cast<char *>("");

/*
Add server status variables to the dynamic list of
Expand Down
3 changes: 3 additions & 0 deletions sql/mysqld.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

#include <my_global.h> /* MYSQL_PLUGIN_IMPORT, FN_REFLEN, FN_EXTLEN */
#include "sql_basic_types.h" /* query_id_t */
#include "sql_plugin.h"
#include "sql_bitmap.h" /* Bitmap */
#include "my_decimal.h" /* my_decimal */
#include "mysql_com.h" /* SERVER_VERSION_LENGTH */
Expand Down Expand Up @@ -153,6 +154,8 @@ extern char *default_tz_name;
extern Time_zone *default_tz;
extern char *default_storage_engine, *default_tmp_storage_engine;
extern char *enforced_storage_engine;
extern char *gtid_pos_auto_engines;
extern plugin_ref *opt_gtid_pos_auto_plugins;
extern bool opt_endinfo, using_udf_functions;
extern my_bool locked_in_memory;
extern bool opt_using_transactions;
Expand Down
174 changes: 174 additions & 0 deletions sql/set_var.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1278,3 +1278,177 @@ enum sys_var::where get_sys_var_value_origin(void *ptr)
return sys_var::CONFIG;
}


/*
Find the next item in string of comma-separated items.
END_POS points at the end of the string.
ITEM_START and ITEM_END return the limits of the next item.
Returns true while items are available, false at the end.
*/
static bool
engine_list_next_item(const char **pos, const char *end_pos,
const char **item_start, const char **item_end)
{
if (*pos >= end_pos)
return false;
*item_start= *pos;
while (*pos < end_pos && **pos != ',')
++*pos;
*item_end= *pos;
++*pos;
return true;
}


static bool
resolve_engine_list_item(plugin_ref *list, uint32 *idx,
const char *pos, const char *pos_end)
{
LEX_STRING item_str;
plugin_ref ref;
uint32_t i;

item_str.str= const_cast<char*>(pos);
item_str.length= pos_end-pos;
ref= ha_resolve_by_name(NULL, &item_str, false);
if (!ref)
{
ErrConvString err(pos, pos_end-pos, system_charset_info);
my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), err.ptr());
return true;
}
/* Ignore duplicates, like --plugin-load does. */
for (i= 0; i < *idx; ++i)
{
if (plugin_hton(list[i]) == plugin_hton(ref))
{
plugin_unlock(NULL, ref);
return false;
}
}
list[*idx]= ref;
++*idx;
return false;
}


/*
Helper for class Sys_var_pluginlist.
Resolve a comma-separated list of storage engine names to a null-terminated
array of plugin_ref.
*/
plugin_ref *
resolve_engine_list(const char *str_arg, size_t str_arg_len)
{
uint32 count, idx;
const char *pos, *item_start, *item_end;
const char *str_arg_end= str_arg + str_arg_len;
plugin_ref *res;

count= 0;
pos= str_arg;
for (;;)
{
if (!engine_list_next_item(&pos, str_arg_end, &item_start, &item_end))
break;
++count;
}

res= (plugin_ref *)my_malloc((count+1)*sizeof(*res), MYF(MY_ZEROFILL|MY_WME));
if (!res)
{
my_error(ER_OUTOFMEMORY, MYF(0), (int)((count+1)*sizeof(*res)));
goto err;
}

idx= 0;
pos= str_arg;
for (;;)
{
if (!engine_list_next_item(&pos, str_arg_end, &item_start, &item_end))
break;
DBUG_ASSERT(idx < count);
if (idx >= count)
break;
if (resolve_engine_list_item(res, &idx, item_start, item_end))
goto err;
}

return res;

err:
free_engine_list(res);
return NULL;
}


void
free_engine_list(plugin_ref *list)
{
plugin_ref *p;

if (!list)
return;
for (p= list; *p; ++p)
plugin_unlock(NULL, *p);
my_free(list);
}


plugin_ref *
copy_engine_list(plugin_ref *list)
{
plugin_ref *p;
uint32 count, i;

for (p= list, count= 0; *p; ++p, ++count)
;
p= (plugin_ref *)my_malloc((count+1)*sizeof(*p), MYF(0));
if (!p)
{
my_error(ER_OUTOFMEMORY, MYF(0), (int)((count+1)*sizeof(*p)));
return NULL;
}
for (i= 0; i < count; ++i)
p[i]= my_plugin_lock(NULL, list[i]);
return p;
}


char *
pretty_print_engine_list(THD *thd, plugin_ref *list)
{
plugin_ref *p;
size_t size;
char *buf, *pos;

if (!list)
return thd->strmake("", 0);

size= 0;
for (p= list; *p; ++p)
size+= plugin_name(*p)->length + 1;
buf= static_cast<char *>(thd->alloc(size));
if (!buf)
return NULL;
pos= buf;
for (p= list; *p; ++p)
{
LEX_STRING *name;
size_t remain;

remain= buf + size - pos;
DBUG_ASSERT(remain > 0);
if (remain <= 1)
break;
if (pos != buf)
{
pos= strmake(pos, ",", remain-1);
--remain;
}
name= plugin_name(*p);
pos= strmake(pos, name->str, MY_MIN(name->length, remain-1));
}
*pos= '\0';
return buf;
}
5 changes: 5 additions & 0 deletions sql/set_var.h
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@ class set_var :public set_var_base
longlong longlong_value; ///< for signed integer
double double_value; ///< for Sys_var_double
plugin_ref plugin; ///< for Sys_var_plugin
plugin_ref *plugins; ///< for Sys_var_pluginlist
Time_zone *time_zone; ///< for Sys_var_tz
LEX_STRING string_value; ///< for Sys_var_charptr and others
const void *ptr; ///< for Sys_var_struct
Expand Down Expand Up @@ -422,6 +423,10 @@ int sys_var_init();
uint sys_var_elements();
int sys_var_add_options(DYNAMIC_ARRAY *long_options, int parse_flags);
void sys_var_end(void);
plugin_ref *resolve_engine_list(const char *str_arg, size_t str_arg_len);
void free_engine_list(plugin_ref *list);
plugin_ref *copy_engine_list(plugin_ref *list);
char *pretty_print_engine_list(THD *thd, plugin_ref *list);

#endif

6 changes: 6 additions & 0 deletions sql/sql_plugin.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1922,6 +1922,12 @@ void plugin_shutdown(void)

if (initialized)
{
if (opt_gtid_pos_auto_plugins)
{
free_engine_list(opt_gtid_pos_auto_plugins);
opt_gtid_pos_auto_plugins= NULL;
}

mysql_mutex_lock(&LOCK_plugin);

reap_needed= true;
Expand Down
40 changes: 40 additions & 0 deletions sql/sys_vars.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3521,6 +3521,46 @@ static Sys_var_plugin Sys_enforce_storage_engine(
NO_CMD_LINE, MYSQL_STORAGE_ENGINE_PLUGIN,
DEFAULT(&enforced_storage_engine), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(check_has_super));

/*
Check
1. Value for gtid_pos_auto_engines is not NULL.
2. No slave SQL thread is running.
*/
static bool
check_gtid_pos_auto_engines(sys_var *self, THD *thd, set_var *var)
{
bool running;
bool err= false;

DBUG_ASSERT(var->type == OPT_GLOBAL);
if (var->value && var->value->is_null())
err= true;
else
{
running= give_error_if_slave_running(false);
if (running)
err= true;
}
if (err && var->save_result.plugins)
{
free_engine_list(var->save_result.plugins);
var->save_result.plugins= NULL;
}
return err;
}


static Sys_var_pluginlist Sys_gtid_pos_auto_engines(
"gtid_pos_auto_engines",
"List of engines for which to automatically create a "
"mysql.gtid_slave_pos_ENGINE table, if a transaction using that engine "
"is replicated. This can be used to avoid introducing cross-engine "
"transactions, if engines are used different from that used by table "
"mysql.gtid_slave_pos",
GLOBAL_VAR(opt_gtid_pos_auto_plugins), NO_CMD_LINE,
DEFAULT(&gtid_pos_auto_engines),
NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(check_gtid_pos_auto_engines));

#if defined(ENABLED_DEBUG_SYNC)
/*
Variable can be set for the session only.
Expand Down
Loading

0 comments on commit 363d6a1

Please sign in to comment.