List:Commits« Previous MessageNext Message »
From:Andrei Elkin Date:July 13 2010 2:57pm
Subject:bzr commit into mysql-5.1-bugteam branch (aelkin:3477) Bug#54702
View as plain text  
#At file:///home/andrei/MySQL/BZR/2a-23May/FIXES/5.1-bt-bug54702-innodb_strict_mode/ based on revid:mattias.jonsson@stripped

 3477 Andrei Elkin	2010-07-13
      Bug #54702  	Different innodb_strict_mode on master and slave causes replication failure
      
      The bug is due to a lack of query execution context info on the slave side.
      
      The current patch probs a pattern solution that can be elaborated on to cover the ultimate
      general issues of having the complete execution context for a Query (wl#5135).
      While this pattern solution tecnically fixes the imediate issue of the bug, it still should not be viewed
      as a bug-fix. It rather offers
      
      1. to elaborate on plugin interface of the plugin system variables to
         define accessors that aware of bilogging and setters from the server code (esp. replication);
      2. to classify all system variables and associate each with an unique binlog
         identifier that maps to a certain style of intercepting the var value and following 
         binlogging (e.g as  a bit of a bitmap, string etc).
         For instance boolean system vars that gover engine modes (such as innodb_strict_mode)
         can form their own subclass having a one bitmap status var of Query_log_event.
      3. to organize read access to system var:s as plugins' so the server's through well-defined 
         interface (see THDVAR3 and related in the patch) that makes sure an access 
         is memorized for binlogging.
     @ include/mysql/plugin.h
        an example of the accesser macro that expands into calling a function
        carrying a binlog/replication unique identifier for the variable that
        is being accessed (read).
     @ mysql-test/suite/rpl/r/rpl_bug54702_innodb_strict_mode.result
        new results file is added.
     @ mysql-test/suite/rpl/t/rpl_bug54702_innodb_strict_mode.test
        a regression test for the bug is added.
     @ sql/log_event.cc
        master side prepares Query event with a new Q_ENGINE_MODE_CODE status.
        slave side applies the status in assumption of the only one 
        ENGINE_MODE_INNODB_STRICT value of the 64-bits is meaningful.
     @ sql/log_event.h
        Extending the status vars of Query_log_event.
     @ sql/mysql_priv.h
        Allocating a global visible to mysql and engines enum engine_mode_codes declaration in their common header file;
     @ sql/sql_class.cc
        initialization with defaults for engine_mode_inited[] arrays.
     @ sql/sql_class.h
        adding uint64 engine_mode_inited[2] to serve as described in comments.
     @ sql/sql_plugin.cc
        an example of plugin var accessor function that requires the 3rd arg and
        performs a necessary action depending on the value of the 3rd arg.
        A pattern is added to show how to change initialization of a plugin vars if
        a variable requires its read-out value binlogging.
        
        An example value setter function to a plugin variable is added.
     @ sql/sql_plugin.h
        Adding the value setter function to a plug var that can be adapted by slave execution code.
     @ storage/innodb_plugin/handler/ha_innodb.cc
        Modifying declaration and accessor macro for strict_mode innodb_plugin var.
        The new declaration for the var makes sure the var will be associated with an accessor function
        which stores the var's value for eventual embedding into status variable of the replication Query
        event.

    added:
      mysql-test/suite/rpl/r/rpl_bug54702_innodb_strict_mode.result
      mysql-test/suite/rpl/t/rpl_bug54702_innodb_strict_mode.test
    modified:
      include/mysql/plugin.h
      sql/log_event.cc
      sql/log_event.h
      sql/mysql_priv.h
      sql/sql_class.cc
      sql/sql_class.h
      sql/sql_plugin.cc
      sql/sql_plugin.h
      storage/innodb_plugin/handler/ha_innodb.cc
=== modified file 'include/mysql/plugin.h'
--- a/include/mysql/plugin.h	2010-04-14 09:53:59 +0000
+++ b/include/mysql/plugin.h	2010-07-13 14:57:23 +0000
@@ -154,6 +154,7 @@ typedef int (*mysql_show_var_func)(MYSQL
 #define PLUGIN_VAR_RQCMDARG     0x0000 /* Argument required for cmd line */
 #define PLUGIN_VAR_OPCMDARG     0x2000 /* Argument optional for cmd line */
 #define PLUGIN_VAR_MEMALLOC     0x8000 /* String needs memory allocated */
+#define PLUGIN_VAR_AFFECTS_REPL 0x010000 /* read-out value should be binlogged */
 
 struct st_mysql_sys_var;
 struct st_mysql_value;
@@ -249,6 +250,9 @@ typedef void (*mysql_var_update_func)(MY
 #define DECLARE_THDVAR_FUNC(type) \
   type *(*resolve)(MYSQL_THD thd, int offset)
 
+#define DECLARE_THDVAR_FUNC3(type) \
+  type *(*resolve)(MYSQL_THD thd, int offset, uint binlog_enum_value)
+
 #define DECLARE_MYSQL_THDVAR_BASIC(name, type) struct { \
   MYSQL_PLUGIN_VAR_HEADER;      \
   int offset;                   \
@@ -256,6 +260,14 @@ typedef void (*mysql_var_update_func)(MY
   DECLARE_THDVAR_FUNC(type);    \
 } MYSQL_SYSVAR_NAME(name)
 
+#define DECLARE_MYSQL_THDVAR_BASIC_BINLOG(name, type) struct { \
+  MYSQL_PLUGIN_VAR_HEADER;      \
+  int offset;                   \
+  const type def_val;           \
+  DECLARE_THDVAR_FUNC3(type);    \
+  uint binlog_enum_value;       \
+} MYSQL_SYSVAR_NAME(name)
+
 #define DECLARE_MYSQL_THDVAR_SIMPLE(name, type) struct { \
   MYSQL_PLUGIN_VAR_HEADER;      \
   int offset;                   \
@@ -332,6 +344,11 @@ DECLARE_MYSQL_THDVAR_BASIC(name, char) =
   PLUGIN_VAR_BOOL | PLUGIN_VAR_THDLOCAL | ((opt) & PLUGIN_VAR_MASK), \
   #name, comment, check, update, -1, def, NULL}
 
+#define MYSQL_THDVAR_BOOL_BINLOG(name, opt, comment, check, update, def, binlog_enum) \
+DECLARE_MYSQL_THDVAR_BASIC_BINLOG(name, char) = { \
+  PLUGIN_VAR_BOOL | PLUGIN_VAR_THDLOCAL | ((opt) & PLUGIN_VAR_MASK) | PLUGIN_VAR_AFFECTS_REPL, \
+  #name, comment, check, update, -1, def, NULL, binlog_enum}
+
 #define MYSQL_THDVAR_STR(name, opt, comment, check, update, def) \
 DECLARE_MYSQL_THDVAR_BASIC(name, char *) = { \
   PLUGIN_VAR_STR | PLUGIN_VAR_THDLOCAL | ((opt) & PLUGIN_VAR_MASK), \
@@ -386,6 +403,14 @@ DECLARE_MYSQL_THDVAR_TYPELIB(name, unsig
 #define THDVAR(thd, name) \
   (*(MYSQL_SYSVAR_NAME(name).resolve(thd, MYSQL_SYSVAR_NAME(name).offset)))
 
+/* when thd == null, result points to global value */
+#define THDVAR3(thd, name) \
+  (*(MYSQL_SYSVAR_NAME(name).resolve(thd, MYSQL_SYSVAR_NAME(name).offset, MYSQL_SYSVAR_NAME(name).binlog_enum_value)))
+#define THDVAREF3(thd, name) \
+  MYSQL_SYSVAR_NAME(name).resolve(thd, MYSQL_SYSVAR_NAME(name).offset, MYSQL_SYSVAR_NAME(name).binlog_enum_value)
+
+
+
 
 /*
   Plugin description structure.

=== added file 'mysql-test/suite/rpl/r/rpl_bug54702_innodb_strict_mode.result'
--- a/mysql-test/suite/rpl/r/rpl_bug54702_innodb_strict_mode.result	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/rpl/r/rpl_bug54702_innodb_strict_mode.result	2010-07-13 14:57:23 +0000
@@ -0,0 +1,19 @@
+stop slave;
+drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9;
+reset master;
+reset slave;
+drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9;
+start slave;
+set @save_innodb_strict_mode=@@global.innodb_strict_mode;
+set @@global.innodb_strict_mode=1;
+include/stop_slave.inc
+start slave;
+set @@session.innodb_strict_mode=0;
+CREATE TABLE t1 (i INT) ROW_FORMAT=COMPACT KEY_BLOCK_SIZE=8 ENGINE=InnoDB;
+Warnings:
+Warning	1478	InnoDB: KEY_BLOCK_SIZE requires innodb_file_per_table.
+Warning	1478	InnoDB: KEY_BLOCK_SIZE requires innodb_file_format > Antelope.
+Warning	1478	InnoDB: ignoring KEY_BLOCK_SIZE=8.
+drop table t1;
+set@@global.innodb_strict_mode=@save_innodb_strict_mode;
+End of tests

=== added file 'mysql-test/suite/rpl/t/rpl_bug54702_innodb_strict_mode.test'
--- a/mysql-test/suite/rpl/t/rpl_bug54702_innodb_strict_mode.test	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/rpl/t/rpl_bug54702_innodb_strict_mode.test	2010-07-13 14:57:23 +0000
@@ -0,0 +1,24 @@
+--source include/master-slave.inc
+--source include/have_innodb_plugin.inc
+
+--connection slave
+set @save_innodb_strict_mode=@@global.innodb_strict_mode;
+set @@global.innodb_strict_mode=1;
+source include/stop_slave.inc;
+start slave;
+
+--connection master
+set @@session.innodb_strict_mode=0;
+CREATE TABLE t1 (i INT) ROW_FORMAT=COMPACT KEY_BLOCK_SIZE=8 ENGINE=InnoDB;
+
+#--connection slave
+sync_slave_with_master;
+
+
+--connection master
+drop table t1;
+
+sync_slave_with_master;
+set@@global.innodb_strict_mode=@save_innodb_strict_mode;
+
+--echo  End of tests

=== modified file 'sql/log_event.cc'
--- a/sql/log_event.cc	2010-07-09 12:00:17 +0000
+++ b/sql/log_event.cc	2010-07-13 14:57:23 +0000
@@ -2354,6 +2354,14 @@ bool Query_log_event::write(IO_CACHE* fi
       start+= host.length;
     }
   }
+  if (thd->engine_mode_inited[0]) {
+    *start++= Q_ENGINE_MODE_CODE;
+    int8store(start, thd->engine_mode_inited[0]);
+    start+= 8;
+    int8store(start, thd->engine_mode_inited[1]);
+    start+= 8;
+  }
+
   /*
     NOTE: When adding new status vars, please don't forget to update
     the MAX_SIZE_LOG_EVENT_STATUS in log_event.h and update the function
@@ -2580,6 +2588,8 @@ code_name(int code)
   case Q_CHARSET_DATABASE_CODE: return "Q_CHARSET_DATABASE_CODE";
   case Q_TABLE_MAP_FOR_UPDATE_CODE: return "Q_TABLE_MAP_FOR_UPDATE_CODE";
   case Q_MASTER_DATA_WRITTEN_CODE: return "Q_MASTER_DATA_WRITTEN_CODE";
+  case Q_INVOKER: return "Q_INVOKER";
+  case Q_ENGINE_MODE_CODE: return "Q_ENGINE_MODE_CODE";
   }
   sprintf(buf, "CODE#%d", code);
   return buf;
@@ -2797,6 +2807,26 @@ Query_log_event::Query_log_event(const c
       host.str= (char *)pos;
       pos+= host.length;
     }
+    case Q_ENGINE_MODE_CODE:
+    {
+      uint64 mode_inited;
+      uint64 mode;
+      CHECK_SPACE(pos, end, 8);
+      mode_inited= uint8korr(pos);
+      pos+= 8;
+      CHECK_SPACE(pos, end, 8);
+      mode= uint8korr(pos);
+      pos+= 8;
+#ifndef MYSQL_CLIENT
+      if (mode_inited)
+      {
+        sys_var *strict_mode= find_sys_var(thd, STRING_WITH_LEN("innodb_strict_mode"));
+        set_char_value_plugin_sysvar_session(strict_mode, current_thd,
+                                             (mode & ENGINE_MODE_INNODB_STRICT)? 1 : 0);
+      }
+#endif
+      break;
+    }
     default:
       /* That's why you must write status vars in growing order of code */
       DBUG_PRINT("info",("Query_log_event has unknown status vars (first has\

=== modified file 'sql/log_event.h'
--- a/sql/log_event.h	2010-07-04 04:02:49 +0000
+++ b/sql/log_event.h	2010-07-13 14:57:23 +0000
@@ -265,7 +265,8 @@ struct sql_ex_info
                                    1 + 2          /* type, charset_database_number */ + \
                                    1 + 8          /* type, table_map_for_update */ + \
                                    1 + 4          /* type, master_data_written */ + \
-                                   1 + 16 + 1 + 60/* type, user_len, user, host_len, host */)
+                                   1 + 16 + 1 + 60/* type, user_len, user, host_len, host */ + \
+                                   1 + 2 * 8      /* type, engine_mode_code */)
 #define MAX_LOG_EVENT_HEADER   ( /* in order of Query_log_event::write */ \
   LOG_EVENT_HEADER_LEN + /* write_header */ \
   QUERY_HEADER_LEN     + /* write_data */   \
@@ -336,6 +337,9 @@ struct sql_ex_info
 
 #define Q_INVOKER 11
 
+#define Q_ENGINE_MODE_CODE 12
+
+
 /* Intvar event post-header */
 
 /* Intvar event data */

=== modified file 'sql/mysql_priv.h'
--- a/sql/mysql_priv.h	2010-06-10 20:45:22 +0000
+++ b/sql/mysql_priv.h	2010-07-13 14:57:23 +0000
@@ -499,6 +499,18 @@ protected:
 */
 #define MAX_TIME_ZONE_NAME_LENGTH       (NAME_LEN + 1)
 
+
+/**
+  Engine preferences like sql_mode that affect replication
+  are embedded into Query_log_event as a status variable.
+  `engine_mode_codes' defines a position of a preference value 
+  in Q_ENGINE_MODE status var bitfield starting from zero.
+*/
+enum engine_mode_codes
+{
+  ENGINE_MODE_INNODB_STRICT= 0
+};
+
 /* The rest of the file is included in the server only */
 #ifndef MYSQL_CLIENT
 

=== modified file 'sql/sql_class.cc'
--- a/sql/sql_class.cc	2010-07-04 04:02:49 +0000
+++ b/sql/sql_class.cc	2010-07-13 14:57:23 +0000
@@ -739,6 +739,8 @@ THD::THD()
   current_user_used= FALSE;
   memset(&invoker_user, 0, sizeof(invoker_user));
   memset(&invoker_host, 0, sizeof(invoker_host));
+  engine_mode_inited[0]= 0LL;
+  engine_mode_inited[1]= 0LL;
 }
 
 

=== modified file 'sql/sql_class.h'
--- a/sql/sql_class.h	2010-07-04 04:02:49 +0000
+++ b/sql/sql_class.h	2010-07-13 14:57:23 +0000
@@ -1487,6 +1487,14 @@ public:
   uint get_binlog_table_maps() const {
     return binlog_table_maps;
   }
+  /*
+    Two 64-bitfield arrays, bits indexed with ENGINE_MODE_ enum values.
+    The first array sets the bit ON if a corresponding entity was
+    accessed during execution course. The 2nd array receives the actual
+    value at access time.
+  */
+  uint64 engine_mode_inited[2];
+
 #endif /* MYSQL_CLIENT */
 
 public:

=== modified file 'sql/sql_plugin.cc'
--- a/sql/sql_plugin.cc	2010-06-10 20:16:43 +0000
+++ b/sql/sql_plugin.cc	2010-07-13 14:57:23 +0000
@@ -1889,6 +1889,8 @@ err:
 
 #define EXTRA_OPTIONS 3 /* options for: 'foo', 'plugin-foo' and NULL */
 
+typedef DECLARE_MYSQL_THDVAR_BASIC_BINLOG(thdvar_bool_binlog_t, my_bool);
+
 typedef DECLARE_MYSQL_SYSVAR_BASIC(sysvar_bool_t, my_bool);
 typedef DECLARE_MYSQL_THDVAR_BASIC(thdvar_bool_t, my_bool);
 typedef DECLARE_MYSQL_SYSVAR_BASIC(sysvar_str_t, char *);
@@ -2450,6 +2452,14 @@ static uchar *intern_sys_var_ptr(THD* th
   construct_options to their respective types.
 */
 
+static char *mysql_sys_var_char3(THD* thd, int offset, uint binlog_enum_value)
+{
+  char *ret= (char *) intern_sys_var_ptr(thd, offset, true);
+  thd->engine_mode_inited[0] |= (1 << binlog_enum_value);
+  thd->engine_mode_inited[1] = (*ret) == 0? 0 : 1;
+  return ret;
+}
+
 static char *mysql_sys_var_char(THD* thd, int offset)
 {
   return (char *) intern_sys_var_ptr(thd, offset, true);
@@ -3054,7 +3064,12 @@ static int construct_options(MEM_ROOT *m
       continue;
     switch (opt->flags & PLUGIN_VAR_TYPEMASK) {
     case PLUGIN_VAR_BOOL:
-      ((thdvar_bool_t *) opt)->resolve= mysql_sys_var_char;
+      if (opt->flags & PLUGIN_VAR_AFFECTS_REPL)
+      {
+        ((thdvar_bool_binlog_t *) opt)->resolve= mysql_sys_var_char3;
+      }
+      else
+        ((thdvar_bool_t *) opt)->resolve= mysql_sys_var_char;
       break;
     case PLUGIN_VAR_INT:
       ((thdvar_int_t *) opt)->resolve= mysql_sys_var_int;
@@ -3445,3 +3460,11 @@ void my_print_help_inc_plugins(my_option
   free_root(&mem_root, MYF(0));
 }
 
+int set_char_value_plugin_sysvar_session(sys_var *v, THD *thd, char val)
+{
+  void *loc;
+  pthread_mutex_lock(&LOCK_global_system_variables);
+  loc= ((sys_var_pluginvar*) v)->real_value_ptr(thd, OPT_SESSION);
+  pthread_mutex_unlock(&LOCK_global_system_variables);
+  return * (char*) loc= val;
+}

=== modified file 'sql/sql_plugin.h'
--- a/sql/sql_plugin.h	2009-05-14 12:03:33 +0000
+++ b/sql/sql_plugin.h	2010-07-13 14:57:23 +0000
@@ -138,4 +138,5 @@ typedef my_bool (plugin_foreach_func)(TH
 #define plugin_foreach(A,B,C,D) plugin_foreach_with_mask(A,B,C,PLUGIN_IS_READY,D)
 extern bool plugin_foreach_with_mask(THD *thd, plugin_foreach_func *func,
                                      int type, uint state_mask, void *arg);
+int set_char_value_plugin_sysvar_session(sys_var *v, THD *thd, char val);
 #endif

=== modified file 'storage/innodb_plugin/handler/ha_innodb.cc'
--- a/storage/innodb_plugin/handler/ha_innodb.cc	2010-05-28 13:17:37 +0000
+++ b/storage/innodb_plugin/handler/ha_innodb.cc	2010-07-13 14:57:23 +0000
@@ -305,15 +305,20 @@ static MYSQL_THDVAR_BOOL(table_locks, PL
   /* check_func */ NULL, /* update_func */ NULL,
   /* default */ TRUE);
 
-static MYSQL_THDVAR_BOOL(strict_mode, PLUGIN_VAR_OPCMDARG,
+static MYSQL_THDVAR_BOOL_BINLOG(strict_mode, PLUGIN_VAR_OPCMDARG,
   "Use strict mode when evaluating create options.",
-  NULL, NULL, FALSE);
+  NULL, NULL, FALSE, ENGINE_MODE_INNODB_STRICT);
 
 static MYSQL_THDVAR_ULONG(lock_wait_timeout, PLUGIN_VAR_RQCMDARG,
   "Timeout in seconds an InnoDB transaction may wait for a lock before being rolled back. Values above 100000000 disable the timeout.",
   NULL, NULL, 50, 1, 1024 * 1024 * 1024, 0);
 
 
+static int set_strict_mode_to(THD* thd, char arg)
+{
+  return (*(THDVAREF3(thd, strict_mode))= arg);
+}
+
 static handler *innobase_create_handler(handlerton *hton,
                                         TABLE_SHARE *table,
                                         MEM_ROOT *mem_root)
@@ -1847,7 +1852,7 @@ trx_is_strict(
 	trx_t*	trx)	/*!< in: transaction */
 {
 	return(trx && trx->mysql_thd
-	       && THDVAR((THD*) trx->mysql_thd, strict_mode));
+	       && THDVAR3((THD*) trx->mysql_thd, strict_mode));
 }
 
 /**************************************************************//**
@@ -6269,7 +6274,7 @@ create_options_are_valid(
 	ut_ad(thd != NULL);
 
 	/* If innodb_strict_mode is not set don't do any validation. */
-	if (!(THDVAR(thd, strict_mode))) {
+	if (!(THDVAR3(thd, strict_mode))) {
 		return(TRUE);
 	}
 


Attachment: [text/bzr-bundle] bzr/aelkin@mysql.com-20100713145723-u2v7824bt49j8y4d.bundle
Thread
bzr commit into mysql-5.1-bugteam branch (aelkin:3477) Bug#54702Andrei Elkin13 Jul