List:Internals« Previous MessageNext Message »
From:guilhem Date:November 19 2005 4:33pm
Subject:bk commit into 5.1 tree (guilhem:1.1966)
View as plain text  
Below is the list of changes that have just been committed into a local
5.1 repository of guilhem. When guilhem does a push these changes will
be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html

ChangeSet
  1.1966 05/11/19 16:33:29 guilhem@stripped +55 -0
  WL#1012 Merge mysql.com:/home/mysql_src/mysql-5.0-wl1012
  into  mysql.com:/home/mysql_src/mysql-5.1; this is for local 5.1, not for the main 5.1.
  Will fix manually this merge.

  sql/sql_update.cc
    1.176 05/11/19 16:33:23 guilhem@stripped +2 -4
    merge

  sql/sql_table.cc
    1.279 05/11/19 16:33:23 guilhem@stripped +37 -6
    merge

  sql/sql_delete.cc
    1.159 05/11/19 16:33:23 guilhem@stripped +1 -3
    merge

  sql/slave.h
    1.93 05/11/19 16:33:23 guilhem@stripped +1 -2
    merge

  sql/slave.cc
    1.257 05/11/19 16:33:23 guilhem@stripped +14 -15
    merge

  sql/share/errmsg.txt
    1.55 05/11/19 16:33:23 guilhem@stripped +0 -12
    merge (will re-apply RBR messages by hand)

  sql/set_var.cc
    1.147 05/11/19 16:33:23 guilhem@stripped +2 -0
    merge

  sql/mysqld.cc
    1.494 05/11/19 16:33:22 guilhem@stripped +6 -0
    merge

  sql/mysql_priv.h
    1.345 05/11/19 16:33:22 guilhem@stripped +0 -0
    merge

  sql/handler.h
    1.167 05/11/19 16:33:22 guilhem@stripped +0 -1
    merge

  sql/handler.cc
    1.192 05/11/19 16:33:22 guilhem@stripped +47 -48
    merge (mmm I picked the wrong order in the merge too so there is a diff, but it
changes nothing)

  sql/Makefile.am
    1.121 05/11/19 16:33:22 guilhem@stripped +1 -0
    merge

  mysql-test/Makefile.am
    1.66 05/11/19 16:33:22 guilhem@stripped +1 -0
    merge

  configure.in
    1.311 05/11/19 16:33:22 guilhem@stripped +1 -0
    merge

  storage/ndb/src/mgmsrv/Services.cpp
    1.58 05/11/19 16:14:08 guilhem@stripped +0 -0
    Auto merged

  sql/table.h
    1.117 05/11/19 16:14:08 guilhem@stripped +0 -0
    Auto merged

  sql/table.cc
    1.187 05/11/19 16:14:08 guilhem@stripped +0 -1
    Auto merged

  sql/sql_udf.cc
    1.58 05/11/19 16:14:08 guilhem@stripped +0 -0
    Auto merged

  sql/sql_show.cc
    1.281 05/11/19 16:14:07 guilhem@stripped +0 -0
    Auto merged

  sql/sql_select.cc
    1.370 05/11/19 16:14:07 guilhem@stripped +0 -0
    Auto merged

  sql/sql_repl.h
    1.40 05/11/19 16:14:07 guilhem@stripped +0 -0
    Auto merged

  sql/sql_prepare.cc
    1.156 05/11/19 16:14:07 guilhem@stripped +0 -0
    Auto merged

  sql/sql_parse.cc
    1.485 05/11/19 16:14:07 guilhem@stripped +0 -0
    Auto merged

  sql/sql_load.cc
    1.86 05/11/19 16:14:06 guilhem@stripped +0 -0
    Auto merged

  sql/sql_insert.cc
    1.174 05/11/19 16:14:06 guilhem@stripped +0 -0
    Auto merged

  sql/sql_class.h
    1.269 05/11/19 16:14:06 guilhem@stripped +0 -0
    Auto merged

  sql/sql_class.cc
    1.221 05/11/19 16:14:06 guilhem@stripped +0 -0
    Auto merged

  sql/sql_base.cc
    1.279 05/11/19 16:14:06 guilhem@stripped +0 -0
    Auto merged

  sql/sql_acl.cc
    1.161 05/11/19 16:14:06 guilhem@stripped +0 -0
    Auto merged

  sql/sp_head.cc
    1.196 05/11/19 16:14:06 guilhem@stripped +0 -0
    Auto merged

  sql/sp.cc
    1.95 05/11/19 16:14:06 guilhem@stripped +0 -0
    Auto merged

  sql/log_event.cc
    1.190 05/11/19 16:14:05 guilhem@stripped +0 -1
    Auto merged

  sql/log.cc
    1.176 05/11/19 16:14:05 guilhem@stripped +0 -0
    Auto merged

  sql/item_func.cc
    1.261 05/11/19 16:14:05 guilhem@stripped +0 -0
    Auto merged

  sql/ha_innodb.cc
    1.238 05/11/19 16:14:05 guilhem@stripped +0 -0
    Auto merged

  mysys/Makefile.am
    1.72 05/11/19 16:14:04 guilhem@stripped +0 -0
    Auto merged

  mysql-test/t/sp.test
    1.164 05/11/19 16:14:04 guilhem@stripped +0 -0
    Auto merged

  mysql-test/t/rpl_stm_sp.test
    1.13 05/11/19 16:14:04 guilhem@stripped +0 -0
    Auto merged

  mysql-test/t/disabled.def
    1.13 05/11/19 16:14:04 guilhem@stripped +0 -0
    Auto merged

  mysql-test/r/sp.result
    1.170 05/11/19 16:14:04 guilhem@stripped +0 -0
    Auto merged

  mysql-test/r/rpl_stm_sp.result
    1.14 05/11/19 16:14:04 guilhem@stripped +0 -0
    Auto merged

  mysql-test/r/binlog_stm_mix_innodb_myisam.result
    1.27 05/11/19 16:14:04 guilhem@stripped +1 -5
    Auto merged

  mysql-test/mysql-test-run.sh
    1.280 05/11/19 16:14:04 guilhem@stripped +0 -0
    Auto merged

  mysql-test/mysql-test-run.pl
    1.42 05/11/19 16:14:04 guilhem@stripped +0 -0
    Auto merged

  mysql-test/extra/binlog_tests/mix_innodb_myisam_binlog.test
    1.21 05/11/19 16:14:04 guilhem@stripped +0 -0
    Auto merged

  libmysqld/Makefile.am
    1.71 05/11/19 16:14:04 guilhem@stripped +0 -0
    Auto merged

  include/my_global.h
    1.115 05/11/19 16:14:04 guilhem@stripped +0 -0
    Auto merged

  include/my_base.h
    1.74 05/11/19 16:14:04 guilhem@stripped +0 -0
    Auto merged

  include/Makefile.am
    1.56 05/11/19 16:14:04 guilhem@stripped +0 -0
    Auto merged

  Makefile.am
    1.79 05/11/19 16:14:04 guilhem@stripped +0 -0
    Auto merged

  BitKeeper/etc/config
    1.25 05/11/19 16:14:04 guilhem@stripped +0 -0
    Auto merged

  BitKeeper/deleted/.del-rpl_filter.h
    1.2 05/11/19 16:14:04 guilhem@stripped +0 -0
    Delete: sql/rpl_filter.h

  BUILD/SETUP.sh
    1.54 05/11/19 16:14:04 guilhem@stripped +0 -0
    Auto merged

  BitKeeper/deleted/.del-rpl_filter.cc
    1.3 05/11/19 16:14:00 guilhem@stripped +0 -0
    Delete: sql/rpl_filter.cc

  storage/ndb/src/mgmsrv/Services.cpp
    1.45.11.3 05/11/19 16:12:44 guilhem@stripped +0 -0
    Merge rename: ndb/src/mgmsrv/Services.cpp -> storage/ndb/src/mgmsrv/Services.cpp

  mysql-test/t/rpl_stm_sp.test
    1.4.5.2 05/11/19 16:12:44 guilhem@stripped +0 -0
    Merge rename: mysql-test/t/rpl_sp.test -> mysql-test/t/rpl_stm_sp.test

  mysql-test/r/rpl_stm_sp.result
    1.5.6.2 05/11/19 16:12:44 guilhem@stripped +0 -0
    Merge rename: mysql-test/r/rpl_sp.result -> mysql-test/r/rpl_stm_sp.result

  mysql-test/r/binlog_stm_mix_innodb_myisam.result
    1.22.1.3 05/11/19 16:12:44 guilhem@stripped +0 -0
    Merge rename: mysql-test/r/mix_innodb_myisam_binlog.result ->
mysql-test/r/binlog_stm_mix_innodb_myisam.result

  mysql-test/extra/binlog_tests/mix_innodb_myisam_binlog.test
    1.15.1.6 05/11/19 16:12:44 guilhem@stripped +0 -0
    Merge rename: mysql-test/t/mix_innodb_myisam_binlog.test ->
mysql-test/extra/binlog_tests/mix_innodb_myisam_binlog.test

  BitKeeper/etc/gone
    1.109 05/11/19 16:12:25 guilhem@stripped +0 -0
    auto-union

# This is a BitKeeper patch.  What follows are the unified diffs for the
# set of deltas contained in the patch.  The rest of the patch, the part
# that BitKeeper cares about, is below these diffs.
# User:	guilhem
# Host:	gbichot3.local
# Root:	/home/mysql_src/mysql-5.1/RESYNC

--- 1.78/Makefile.am	2005-11-07 16:24:33 +01:00
+++ 1.79/Makefile.am	2005-11-19 16:14:04 +01:00
@@ -110,24 +110,32 @@
 
 test:
 	cd mysql-test ; \
-	./mysql-test-run && \
-	./mysql-test-run --ps-protocol
+	./mysql-test-run.pl --mysqld=--binlog-format=statement $(MYSQL_TEST_RUN_ARGS) &&
\
+	./mysql-test-run.pl --ps-protocol --mysqld=--binlog-format=statement
$(MYSQL_TEST_RUN_ARGS) &&\
+        ./mysql-test-run.pl --mysqld=--binlog-format=row --do-test=rpl
$(MYSQL_TEST_RUN_ARGS) && \
+        ./mysql-test-run.pl --ps-protocol --mysqld=--binlog-format=row --do-test=rpl
$(MYSQL_TEST_RUN_ARGS)
 
 test-force:
-	cd mysql-test; \
-	./mysql-test-run --force && \
-	./mysql-test-run --ps-protocol --force
+	cd mysql-test ; \
+	./mysql-test-run --force --mysqld=--binlog-format=statement $(MYSQL_TEST_RUN_ARGS) ; \
+	./mysql-test-run --ps-protocol --force --mysqld=--binlog-format=statement
$(MYSQL_TEST_RUN_ARGS) ; \
+        ./mysql-test-run --force --mysqld=--binlog-format=row --do-test=rpl
$(MYSQL_TEST_RUN_ARGS) ; \
+        ./mysql-test-run --ps-protocol --force --mysqld=--binlog-format=statement
--do-test=rpl $(MYSQL_TEST_RUN_ARGS) 
 
 # We are testing a new Perl version of the test script
 test-pl:
-	cd mysql-test; \
-	./mysql-test-run.pl && \
-	./mysql-test-run.pl --ps-protocol
+	cd mysql-test ; \
+	./mysql-test-run.pl $(MYSQL_TEST_RUN_ARGS) --mysqld=--binlog-format=statement &&
\
+	./mysql-test-run.pl $(MYSQL_TEST_RUN_ARGS) --ps-protocol
--mysqld=--binlog-format=statement $(MYSQL_TEST_RUN_ARGS) && \
+        ./mysql-test-run.pl $(MYSQL_TEST_RUN_ARGS) --mysqld=--binlog-format=row
--do-test=rpl $(MYSQL_TEST_RUN_ARGS) && \
+        ./mysql-test-run.pl $(MYSQL_TEST_RUN_ARGS) --ps-protocol
--mysqld=--binlog-format=row --do-test=rpl $(MYSQL_TEST_RUN_ARGS)
 
 test-force-pl:
-	cd mysql-test; \
-	./mysql-test-run.pl --force && \
-	./mysql-test-run.pl --ps-protocol --force
+	cd mysql-test ; \
+	./mysql-test-run.pl $(MYSQL_TEST_RUN_ARGS) --force --mysqld=--binlog-format=statement
+	./mysql-test-run.pl $(MYSQL_TEST_RUN_ARGS) --ps-protocol --force
--mysqld=--binlog-format=statement
+        ./mysql-test-run.pl $(MYSQL_TEST_RUN_ARGS) --force --mysqld=--binlog-format=row
--do-test=rpl
+        ./mysql-test-run.pl $(MYSQL_TEST_RUN_ARGS) --ps-protocol --force
--mysqld=--binlog-format=statement --do-test=rpl 
 
 # Don't update the files from bitkeeper
 %::SCCS/s.%

--- 1.310/configure.in	2005-11-19 10:41:37 +01:00
+++ 1.311/configure.in	2005-11-19 16:33:22 +01:00
@@ -37,6 +37,7 @@
 sinclude(config/ac-macros/misc.m4)
 sinclude(config/ac-macros/openssl.m4)
 sinclude(config/ac-macros/readline.m4)
+sinclude(config/ac-macros/replication.m4)
 sinclude(config/ac-macros/yassl.m4)
 sinclude(config/ac-macros/zlib.m4)
 
@@ -1816,7 +1817,8 @@
 AC_FUNC_UTIME_NULL
 AC_FUNC_VPRINTF
 
-AC_CHECK_FUNCS(alarm bcmp bfill bmove bzero chsize cuserid fchmod fcntl \
+AC_CHECK_FUNCS(alarm bcmp bfill bmove bsearch bzero \
+  chsize cuserid fchmod fcntl \
   fconvert fdatasync finite fpresetsticky fpsetmask fsync ftruncate \
   getcwd gethostbyaddr_r gethostbyname_r getpass getpassphrase getpwnam \
   getpwuid getrlimit getrusage getwd gmtime_r index initgroups isnan \
@@ -2362,6 +2364,7 @@
 AC_SUBST(readline_h_ln_cmd)
 
 MYSQL_CHECK_BIG_TABLES
+MYSQL_CHECK_REPLICATION
 
 MYSQL_STORAGE_ENGINE(innobase,,innodb,,,,storage/innobase,ha_innodb.o,[ dnl
   \$(top_builddir)/storage/innobase/usr/libusr.a dnl

--- 1.55/include/Makefile.am	2005-11-05 12:20:31 +01:00
+++ 1.56/include/Makefile.am	2005-11-19 16:14:04 +01:00
@@ -30,7 +30,8 @@
 			my_nosys.h my_alarm.h queues.h rijndael.h sha1.h \
 			my_aes.h my_tree.h my_trie.h hash.h thr_alarm.h \
 			thr_lock.h t_ctype.h violite.h md5.h base64.h \
-			mysql_version.h.in my_handler.h my_time.h decimal.h
+			mysql_version.h.in my_handler.h my_time.h decimal.h \
+			my_vle.h
 
 # mysql_version.h are generated
 CLEANFILES =            mysql_version.h my_config.h readline

--- 1.73/include/my_base.h	2005-11-05 12:20:31 +01:00
+++ 1.74/include/my_base.h	2005-11-19 16:14:04 +01:00
@@ -309,8 +309,9 @@
 #define HA_ERR_NO_CONNECTION     157  /* Could not connect to storage engine */
 #define HA_ERR_NULL_IN_SPATIAL   158  /* NULLs are not supported in spatial index */
 #define HA_ERR_TABLE_DEF_CHANGED 159  /* The table changed in storage engine */
+#define HA_ERR_RBR_LOGGING_FAILED 160  /* row-based binlogging of row failed */
 
-#define HA_ERR_LAST              159  /*Copy last error nr.*/
+#define HA_ERR_LAST              160  /*Copy last error nr.*/
 /* Add error numbers before HA_ERR_LAST and change it accordingly. */
 #define HA_ERR_ERRORS            (HA_ERR_LAST - HA_ERR_FIRST + 1)
 

--- 1.279/mysql-test/mysql-test-run.sh	2005-11-18 16:06:32 +01:00
+++ 1.280/mysql-test/mysql-test-run.sh	2005-11-19 16:14:04 +01:00
@@ -240,6 +240,8 @@
 FAILED_CASES=
 
 EXTRA_MASTER_OPT=""
+EXTRA_MASTER_MYSQLD_OPT=@EXTRA_MASTER_MYSQLD_OPT@
+EXTRA_SLAVE_MYSQLD_OPT=@EXTRA_SLAVE_MYSQLD_OPT@
 EXTRA_MYSQL_TEST_OPT=""
 EXTRA_MYSQLCHECK_OPT=""
 EXTRA_MYSQLDUMP_OPT=""
@@ -796,12 +798,13 @@
 export MYSQL_DUMP_DIR
 MYSQL_CHECK="$MYSQL_CHECK --no-defaults -uroot --socket=$MASTER_MYSOCK
--password=$DBPASSWD $EXTRA_MYSQLCHECK_OPT"
 MYSQL_DUMP="$MYSQL_DUMP --no-defaults -uroot --socket=$MASTER_MYSOCK --password=$DBPASSWD
$EXTRA_MYSQLDUMP_OPT"
+MYSQL_DUMP_SLAVE="$MYSQL_DUMP_DIR --no-defaults -uroot --socket=$SLAVE_MYSOCK
--password=$DBPASSWD $EXTRA_MYSQLDUMP_OPT"
 MYSQL_SHOW="$MYSQL_SHOW -uroot --socket=$MASTER_MYSOCK --password=$DBPASSWD
$EXTRA_MYSQLSHOW_OPT"
 MYSQL_BINLOG="$MYSQL_BINLOG --no-defaults --local-load=$MYSQL_TMP_DIR 
--character-sets-dir=$CHARSETSDIR $EXTRA_MYSQLBINLOG_OPT"
 MYSQL_IMPORT="$MYSQL_IMPORT -uroot --socket=$MASTER_MYSOCK --password=$DBPASSWD
$EXTRA_MYSQLDUMP_OPT"
 MYSQL_FIX_SYSTEM_TABLES="$MYSQL_FIX_SYSTEM_TABLES --no-defaults --host=localhost
--port=$MASTER_MYPORT --socket=$MASTER_MYSOCK --user=root --password=$DBPASSWD
--basedir=$BASEDIR --bindir=$CLIENT_BINDIR --verbose"
 MYSQL="$MYSQL --no-defaults --host=localhost --port=$MASTER_MYPORT
--socket=$MASTER_MYSOCK --user=root --password=$DBPASSWD"
-export MYSQL MYSQL_CHECK MYSQL_DUMP MYSQL_SHOW MYSQL_BINLOG MYSQL_FIX_SYSTEM_TABLES
MYSQL_IMPORT
+export MYSQL MYSQL_CHECK MYSQL_DUMP MYSQL_DUMP_SLAVE MYSQL_SHOW MYSQL_BINLOG
MYSQL_FIX_SYSTEM_TABLES MYSQL_IMPORT
 export CLIENT_BINDIR MYSQL_CLIENT_TEST CHARSETSDIR MYSQL_MY_PRINT_DEFAULTS
 export NDB_TOOLS_DIR
 export NDB_MGM
@@ -1278,7 +1281,7 @@
           --log-bin-trust-function-creators \
 	   $MASTER_40_ARGS \
            $SMALL_SERVER \
-           $EXTRA_MASTER_OPT $EXTRA_MASTER_MYSQLD_OPT \
+           $EXTRA_MASTER_MYSQLD_OPT $EXTRA_MASTER_OPT \
            $NOT_FIRST_MASTER_EXTRA_OPTS $CURR_MASTER_MYSQLD_TRACE"
   else
     master_args="--no-defaults --log-bin=$MYSQL_TEST_DIR/var/log/master-bin$1 \
@@ -1299,7 +1302,7 @@
           --log-bin-trust-function-creators \
 	   $MASTER_40_ARGS \
            $SMALL_SERVER \
-           $EXTRA_MASTER_OPT $EXTRA_MASTER_MYSQLD_OPT \
+           $EXTRA_MASTER_MYSQLD_OPT $EXTRA_MASTER_OPT \
            $NOT_FIRST_MASTER_EXTRA_OPTS"
   fi
 
@@ -1431,7 +1434,7 @@
           -O slave_net_timeout=10 \
           --log-bin-trust-function-creators \
            $SMALL_SERVER \
-           $EXTRA_SLAVE_OPT $EXTRA_SLAVE_MYSQLD_OPT"
+           $EXTRA_SLAVE_MYSQLD_OPT $EXTRA_SLAVE_OPT"
   CUR_MYERR=$slave_err
   CUR_MYSOCK=$slave_sock
 

--- 1.71/mysys/Makefile.am	2005-11-04 21:09:53 +01:00
+++ 1.72/mysys/Makefile.am	2005-11-19 16:14:04 +01:00
@@ -35,6 +35,7 @@
 			mf_tempdir.c my_lock.c mf_brkhant.c my_alarm.c \
 			my_malloc.c my_realloc.c my_once.c mulalloc.c \
 			my_alloc.c safemalloc.c my_new.cc \
+			my_vle.c \
 			my_fopen.c my_fstream.c my_getsystime.c \
 			my_error.c errors.c my_div.c my_messnc.c \
 			mf_format.c mf_same.c mf_dirname.c mf_fn_ext.c \

--- 1.120/sql/Makefile.am	2005-11-08 06:40:58 +01:00
+++ 1.121/sql/Makefile.am	2005-11-19 16:33:22 +01:00
@@ -51,6 +51,7 @@
 			procedure.h sql_class.h sql_lex.h sql_list.h \
 			sql_manager.h sql_map.h sql_string.h unireg.h \
 			sql_error.h field.h handler.h mysqld_suffix.h \
+	                rpl_tblmap.h rpl_filter.h \
 			ha_heap.h ha_myisam.h ha_myisammrg.h ha_partition.h \
 			opt_range.h protocol.h \
 			sql_select.h structs.h table.h sql_udf.h hash_filo.h\
@@ -82,6 +83,7 @@
 			unireg.cc des_key_file.cc \
 			discover.cc time.cc opt_range.cc opt_sum.cc \
 		   	records.cc filesort.cc handler.cc \
+	                rpl_tblmap.cc rpl_filter.cc \
 			ha_heap.cc ha_myisam.cc ha_myisammrg.cc \
 			sql_db.cc sql_table.cc sql_rename.cc sql_crypt.cc \
 			sql_load.cc mf_iocache.cc field_conv.cc sql_show.cc \

--- 1.191/sql/handler.cc	2005-11-07 16:24:40 +01:00
+++ 1.192/sql/handler.cc	2005-11-19 16:33:22 +01:00
@@ -22,6 +22,7 @@
 #endif
 
 #include "mysql_priv.h"
+#include "rpl_filter.h"
 #include "ha_heap.h"
 #include "ha_myisam.h"
 #include "ha_myisammrg.h"
@@ -37,10 +38,10 @@
 #ifdef WITH_PARTITION_STORAGE_ENGINE
 #include "ha_partition.h"
 #endif
-
 extern handlerton *sys_table_types[];
   
-/* static functions defined in this file */
+#define BITMAP_STACKBUF_SIZE (128/8)
+
 
 static SHOW_COMP_OPTION have_yes= SHOW_OPTION_YES;
 
@@ -679,6 +680,83 @@
       thd->variables.tx_isolation=thd->session_tx_isolation;
       thd->transaction.cleanup();
     }
+    /*
+      TODO: REORGANIZATION (Guilhem 27 Oct 2005).
+
+      1) as the pending event is a member of thd->transaction, it would be
+      tidy to move prepare_for_commit|rollback into the st_transaction class;
+      roughly they are dependent on nothing which is in thd and which is not in
+      thd->transaction. So we would do:
+      thd->transaction.prepare_for_rollback().
+
+
+      2) prepare_for_rollback(), intended to do thread's preparations for
+      rollback, not only related to binlog, currently does only binlog things,
+      while some preparation is done in ha_rollback_trans(thd). Not optimal.
+      Guilhem thinks that prepare_for_commit|rollback() is not a symmetric
+      couple in reality: see how in ha_rollback_trans we have to call
+      prepare_for_rollback while in ha_commit_trans we don't have to call
+      prepare_for_commit (because it was already called by binlog_query).
+      So maybe names should not be symmetric...
+
+      3) prepare_for_rollback() can be seen as some cleanup to do: when they
+      execute a statement (on a transactional table), engines allocate
+      resources, write to them, and they free these resources above in
+      (*ht)->rollback. The pending rows event is also a resource allocated by
+      THD during the statement, and at rollback THD also needs to free this
+      resource.
+
+      4) What bothers Guilhem is that this resource is in THD, so again it makes
+      the binlog a special case, by requiring an explicit line "rollback
+      binlog" here in ha_rollback_trans(). In his XA patch, Serg had organized
+      things so that the binlog looks like a storage engine, registers in the
+      transaction if needed, and so does its cleanup in (*ht)->rollback 
+      above. But "pending" happens earlier in the process, so we may have a
+      pending event, without the binlog being registered as a participant in
+      the transaction.
+      As "pending" is "handler's private per-connection data" (quoting
+      sql_class.h) it should be in thd->ha_data[binlog_hton.slot], Serg
+      confirms.
+      Then we should register the binlog as soon as we create a pending event.
+      Then pending would be wiped out by binlog_rollback() and that would be
+      tidy.
+      The fact to have a binlog transaction cache is considered as having
+      binlog a participant. But a pending rows event is yet another binlog
+      transaction cache, so it should be placed in the same containers.
+      
+      https://intranet.mysql.com/worklog/Server-RawIdeaBin/?tid=2967
+      is the Worklog entry for this reorganization.
+      When this is done, we won't need the explicit call below, as binlog will
+      be registered as participant even in the case where this is an
+      autocommit statement on a transactional table, one row has been updated
+      but the statement rolls back because it fails on another row. With the
+      current code and without the call below, the updated row causes a
+      pending event which is not flushed (because binlog_query() is not called
+      because the statement is going to be rolled back); this pending event
+      then influences the next statement. Testcase extracted
+      from the sp_trans test, which causes a crash without the below call:
+-- source include/have_innodb.inc
+delimiter |;
+create table t1 (id int) engine=innodb|
+create table t2 (id int primary key, j int) engine=innodb|
+      insert into t2 values (1, 0)|
+create function bug10015_5(i int) returns int
+  begin
+    if (i = 5) then
+      insert into t2 values (1, 0);
+    end if;
+    return i;
+  end|
+--error 1062
+insert into t1 values (bug10015_5(4)), (bug10015_5(5))|
+### if you exit here master won't crash
+drop function bug10015_5|
+# if you exit here master will crash when it shuts down
+
+    */
+
+    thd->prepare_for_rollback();
+
   }
 #endif /* USING_TRANSACTIONS */
   /*
@@ -1775,6 +1853,9 @@
     my_error(ER_NO_SUCH_TABLE, MYF(0), db, table->alias);
     break;
   }
+  case HA_ERR_RBR_LOGGING_FAILED:
+    textno= ER_BINLOG_ROW_LOGGING_FAILED;
+    break;
   default:
     {
       /* The error was "unknown" to this function.
@@ -2567,6 +2648,101 @@
   send_eof(thd);
   return FALSE;
 }
+/*
+  Function to check if the conditions for row-based binlogging is
+  correct for the table. 
+
+  A row in the given table should be replicated if:
+  - Row-based replication is on
+  - It is not a temporary table
+  - The table shall be binlogged (binlog_*_db rules)
+*/
+  
+#ifdef HAVE_ROW_BASED_REPLICATION
+static bool check_table_binlog_row_based(TABLE *table) 
+{
+  return
+    binlog_row_based &&
+    (table->s->tmp_table == NO_TMP_TABLE);
+}
+
+template<class RowsEventT> int binlog_log_row(TABLE* table,
+                                              const byte *before_record,
+                                              const byte *after_record)
+{
+  bool error= 0;
+  if (check_table_binlog_row_based(table)) 
+  {
+    MY_BITMAP cols;
+    /* Potential buffer on the stack for the bitmap */
+    uchar bitbuf[BITMAP_STACKBUF_SIZE];
+    uint n_fields= table->s->fields;
+    my_bool use_bitbuf= n_fields <= sizeof(bitbuf)*8;
+    if (likely(!(error= bitmap_init(&cols,
+                                    use_bitbuf ? bitbuf : NULL, 
+                                    (n_fields + 7) & ~7UL, 
+                                    false))))
+    {
+      bitmap_set_all(&cols);
+      error=
+        RowsEventT::binlog_row_logging_function(current_thd, table,
+                                                table->file->has_transactions(),
+                                                &cols, table->s->fields,
+                                                before_record, after_record);
+      if (!use_bitbuf)
+        bitmap_free(&cols);
+    }
+  }
+  return error ? HA_ERR_RBR_LOGGING_FAILED : 0;
+}
+
+
+/*
+  Instantiate the versions we need for the above template function, because we
+  have -fno-implicit-template as compiling option.
+*/
+
+template int binlog_log_row<Write_rows_log_event>(TABLE *, const byte *, const byte
*);
+template int binlog_log_row<Delete_rows_log_event>(TABLE *, const byte *, const
byte *);
+template int binlog_log_row<Update_rows_log_event>(TABLE *, const byte *, const
byte *);
+
+#endif /* HAVE_ROW_BASED_REPLICATION */
+
+int handler::ha_write_row(byte *buf) 
+{
+  int error;
+  if (likely(!(error= write_row(buf))))
+  {
+#ifdef HAVE_ROW_BASED_REPLICATION
+    error= binlog_log_row<Write_rows_log_event>(table, 0, buf);
+#endif
+  }
+  return error;
+}
+  
+int handler::ha_update_row(const byte *old_data, byte *new_data) 
+{
+  int error;
+  if (likely(!(error= update_row(old_data, new_data))))
+  {
+#ifdef HAVE_ROW_BASED_REPLICATION
+    error= binlog_log_row<Update_rows_log_event>(table, old_data, new_data);
+#endif
+  }
+  return error;
+}
+  
+int handler::ha_delete_row(const byte *buf) 
+{
+  int error;
+  if (likely(!(error= delete_row(buf))))
+  {
+#ifdef HAVE_ROW_BASED_REPLICATION
+    error= binlog_log_row<Delete_rows_log_event>(table, buf, 0);
+#endif
+  }
+  return error;
+}    
 
 
 #ifdef HAVE_REPLICATION

--- 1.166/sql/handler.h	2005-11-18 16:37:56 +01:00
+++ 1.167/sql/handler.h	2005-11-19 16:33:22 +01:00
@@ -74,6 +74,13 @@
 */
 #define HA_CAN_INSERT_DELAYED  (1 << 14)
 #define HA_PRIMARY_KEY_IN_READ_INDEX (1 << 15)
+/*
+  If HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS is set, it means that the engine can
+  do this: the position of an arbitrary record can be retrieved using
+  position() when the table has a primary key, effectively allowing random
+  access on the table based on a given record.
+*/ 
+#define HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS (1 << 16) 
 #define HA_NOT_DELETE_WITH_CACHE (1 << 18)
 #define HA_NO_PREFIX_CHAR_KEYS (1 << 20)
 #define HA_CAN_FULLTEXT        (1 << 21)
@@ -1039,11 +1046,9 @@
   uint get_index(void) const { return active_index; }
   virtual int open(const char *name, int mode, uint test_if_locked)=0;
   virtual int close(void)=0;
-  virtual int write_row(byte * buf) { return  HA_ERR_WRONG_COMMAND; }
-  virtual int update_row(const byte * old_data, byte * new_data)
-   { return  HA_ERR_WRONG_COMMAND; }
-  virtual int delete_row(const byte * buf)
-   { return  HA_ERR_WRONG_COMMAND; }
+  virtual int ha_write_row(byte * buf);
+  virtual int ha_update_row(const byte * old_data, byte * new_data);
+  virtual int ha_delete_row(const byte * buf);
   /*
     SYNOPSIS
       start_bulk_update()
@@ -1386,6 +1391,30 @@
      Pops the top if condition stack, if stack is not empty
  */
  virtual void cond_pop() { return; };
+
+private:
+
+  /*
+    Row-level primitives for storage engines. 
+    These should be overridden by the storage engine class. To call
+    these methods, use the corresponding 'ha_*' method above.
+  */
+  friend int ndb_add_binlog_index(THD *, void *);
+
+  virtual int write_row(byte *buf) 
+  { 
+    return HA_ERR_WRONG_COMMAND; 
+  }
+
+  virtual int update_row(const byte *old_data, byte *new_data)
+  { 
+    return HA_ERR_WRONG_COMMAND; 
+  }
+
+  virtual int delete_row(const byte *buf)
+  { 
+    return HA_ERR_WRONG_COMMAND; 
+  }
  virtual bool check_if_incompatible_data(HA_CREATE_INFO *create_info,
 					 uint table_changes)
  { return COMPATIBLE_DATA_NO; }

--- 1.175/sql/log.cc	2005-11-18 16:37:56 +01:00
+++ 1.176/sql/log.cc	2005-11-19 16:14:05 +01:00
@@ -105,6 +105,7 @@
 {
   int error=0;
   DBUG_ENTER("binlog_end_trans");
+
   if (end_ev)
     error= mysql_bin_log.write(thd, trans_log, end_ev);
 
@@ -369,6 +370,7 @@
   :bytes_written(0), last_time(0), query_start(0), name(0),
    prepared_xids(0), log_type(LOG_CLOSED), file_id(1), open_count(1),
    write_error(FALSE), inited(FALSE), need_start_event(TRUE),
+   m_next_table_id(0), m_table_map_version(0),
    description_event_for_exec(0), description_event_for_queue(0)
 {
   /*
@@ -396,6 +398,7 @@
     (void) pthread_mutex_destroy(&LOCK_log);
     (void) pthread_mutex_destroy(&LOCK_index);
     (void) pthread_cond_destroy(&update_cond);
+    (void) pthread_mutex_destroy(&LOCK_next_table_id);
   }
   DBUG_VOID_RETURN;
 }
@@ -441,6 +444,7 @@
   (void) pthread_mutex_init(&LOCK_log,MY_MUTEX_INIT_SLOW);
   (void) pthread_mutex_init(&LOCK_index, MY_MUTEX_INIT_SLOW);
   (void) pthread_cond_init(&update_cond, 0);
+  (void) pthread_mutex_init(&LOCK_next_table_id, MY_MUTEX_INIT_FAST);
 }
 
 const char *MYSQL_LOG::generate_name(const char *log_name,
@@ -1696,7 +1700,11 @@
       of the SQL command
     */
 
-    if (thd)
+    /*
+      If row-based binlogging, Insert_id, Rand and other kind of "setting
+      context" events are not needed.
+    */
+    if (thd && !binlog_row_based)
     {
       if (thd->last_insert_id_used)
       {
@@ -2308,6 +2316,51 @@
   pthread_cond_broadcast(&update_cond);
   DBUG_VOID_RETURN;
 }
+
+#ifndef MYSQL_CLIENT
+
+ulong MYSQL_LOG::get_table_id(TABLE* table)
+{
+  DBUG_ENTER("MYSQL_LOG::get_table_id(TABLE*)");
+  DBUG_PRINT("enter", ("table=%p (%s.%s)",
+                       table, table->s->db, table->s->table_name));
+  DBUG_ASSERT(table != NULL);
+  ulong tid= table->s->table_map_id;
+  if (tid == table_mapping::NO_TABLE)
+  {
+    pthread_mutex_lock(&LOCK_next_table_id);
+    /*
+      need to double check under mutex that no other thread has snuck in
+      and set the table_map_id.
+      Guilhem asks: so first we check without mutex. Imagine the == was false,
+      and another thread just made it true with mutex. It may be that the
+      first == test still sees that == is false, thus not even entering the
+      second == test. I wonder if this is ok. I hope something in this
+      scenario makes it impossible to happen?
+    */
+    if ((tid= table->s->table_map_id) == table_mapping::NO_TABLE)
+    {
+      /* get next id */
+      tid= m_next_table_id++;
+      /* There is one reserved number that cannot be used. */
+      if (unlikely(tid == table_mapping::NO_TABLE))
+        tid= m_next_table_id++;
+      table->s->table_map_id= tid;
+    }
+    pthread_mutex_unlock(&LOCK_next_table_id);
+  }
+  DBUG_PRINT("return", ("table_id=%d", tid));
+  DBUG_RETURN(tid);
+}
+
+void MYSQL_LOG::
+update_table_map_version()
+{
+  /* BUG this is an incrementation without LOCK_log mutex */
+  m_table_map_version++;
+}
+#endif /* !defined(MYSQL_CLIENT) */
+
 
 #ifdef __NT__
 void print_buffer_to_nt_eventlog(enum loglevel level, char *buff,

--- 1.189/sql/log_event.cc	2005-11-04 21:09:55 +01:00
+++ 1.190/sql/log_event.cc	2005-11-19 16:14:05 +01:00
@@ -21,11 +21,13 @@
 #pragma implementation				// gcc: Class implementation
 #endif
 
-#include  "mysql_priv.h"
+#include "mysql_priv.h"
 #include "slave.h"
 #include "rpl_filter.h"
 #include <my_dir.h>
 #endif /* MYSQL_CLIENT */
+#include <my_bitmap.h>
+#include <my_vle.h>
 
 #define log_cs	&my_charset_latin1
 
@@ -232,6 +234,7 @@
   commands just before it prints a query.
 */
 
+#ifdef MYSQL_CLIENT
 static void print_set_option(FILE* file, uint32 bits_changed, uint32 option,
                              uint32 flags, const char* name, bool* need_comma) 
 {
@@ -243,6 +246,7 @@
     *need_comma= 1;
   }
 }
+#endif
 
 /**************************************************************************
 	Log_event methods (= the parent class of all events)
@@ -271,6 +275,10 @@
   case XID_EVENT: return "Xid";
   case USER_VAR_EVENT: return "User var";
   case FORMAT_DESCRIPTION_EVENT: return "Format_desc";
+  case TABLE_MAP_EVENT: return "Table_map";
+  case WRITE_ROWS_EVENT: return "Write_rows";
+  case UPDATE_ROWS_EVENT: return "Update_rows";
+  case DELETE_ROWS_EVENT: return "Delete_rows";
   case BEGIN_LOAD_QUERY_EVENT: return "Begin_load_query";
   case EXECUTE_LOAD_QUERY_EVENT: return "Execute_load_query";
   default: return "Unknown";				/* impossible */
@@ -778,6 +786,9 @@
     DBUG_RETURN(NULL); // general sanity check - will fail on a partial read
   }
 
+  /* To check the integrity of the Log_event_type enumeration */
+  DBUG_ASSERT(buf[EVENT_TYPE_OFFSET] < ENUM_END_EVENT);
+
   switch(buf[EVENT_TYPE_OFFSET]) {
   case QUERY_EVENT:
     ev  = new Query_log_event(buf, event_len, description_event, QUERY_EVENT);
@@ -829,6 +840,20 @@
   case FORMAT_DESCRIPTION_EVENT:
     ev = new Format_description_log_event(buf, event_len, description_event); 
     break;
+#if defined(HAVE_REPLICATION) && defined(HAVE_ROW_BASED_REPLICATION)
+  case WRITE_ROWS_EVENT:
+    ev = new Write_rows_log_event(buf, event_len, description_event);
+    break;
+  case UPDATE_ROWS_EVENT:
+    ev = new Update_rows_log_event(buf, event_len, description_event);
+    break;
+  case DELETE_ROWS_EVENT:
+    ev = new Delete_rows_log_event(buf, event_len, description_event);
+    break;
+  case TABLE_MAP_EVENT:
+    ev = new Table_map_log_event(buf, event_len, description_event);
+    break;
+#endif
   case BEGIN_LOAD_QUERY_EVENT:
     ev = new Begin_load_query_log_event(buf, event_len, description_event);
     break;
@@ -2049,6 +2074,10 @@
       post_header_len[DELETE_FILE_EVENT-1]= DELETE_FILE_HEADER_LEN;
       post_header_len[NEW_LOAD_EVENT-1]= post_header_len[LOAD_EVENT-1];
       post_header_len[FORMAT_DESCRIPTION_EVENT-1]= FORMAT_DESCRIPTION_HEADER_LEN;
+      post_header_len[TABLE_MAP_EVENT-1]=    TABLE_MAP_HEADER_LEN;
+      post_header_len[WRITE_ROWS_EVENT-1]=   ROWS_HEADER_LEN;
+      post_header_len[UPDATE_ROWS_EVENT-1]=  ROWS_HEADER_LEN;
+      post_header_len[DELETE_ROWS_EVENT-1]=  ROWS_HEADER_LEN;
       post_header_len[BEGIN_LOAD_QUERY_EVENT-1]= post_header_len[APPEND_BLOCK_EVENT-1];
       post_header_len[EXECUTE_LOAD_QUERY_EVENT-1]= EXECUTE_LOAD_QUERY_HEADER_LEN;
     }
@@ -2745,6 +2774,9 @@
   thd->query_length= 0;                         // Should not be needed
   thd->query_error= 0;
   clear_all_errors(thd, rli);
+
+  /* see Query_log_event::exec_event() and BUG#13360 */
+  DBUG_ASSERT(!rli->m_table_map.count());
   /*
     Usually mysql_init_query() is called by mysql_parse(), but we need it here
     as the present method does not call mysql_parse().
@@ -4936,3 +4968,1693 @@
   }
   return buf;
 }
+
+
+#ifdef HAVE_ROW_BASED_REPLICATION
+
+/**************************************************************************
+	Rows_log_event member functions
+**************************************************************************/
+
+#ifndef MYSQL_CLIENT
+Rows_log_event::Rows_log_event(THD *thd_arg, TABLE *tbl_arg, ulong tid,
+                               MY_BITMAP const *cols, bool is_transactional)
+  : Log_event(thd_arg, 0, is_transactional),
+    m_dbnam(tbl_arg->s->db), m_dblen(m_dbnam ? strlen(m_dbnam) : 0),
+    m_table(tbl_arg),
+    m_tblnam(tbl_arg->s->table_name),
+    m_tbllen(strlen(tbl_arg->s->table_name)),
+    m_table_id(tid),
+    m_width(tbl_arg->s->fields),
+    /*
+      TODO: allocating several kB for maybe no use (e.g. read-only session)
+      is too much.
+    */
+    m_rows_buf(my_malloc(opt_binlog_rows_event_max_size * sizeof(*m_rows_buf),
MYF(MY_WME))),
+    m_rows_cur(m_rows_buf),
+    m_rows_end(m_rows_buf + opt_binlog_rows_event_max_size),
+    m_flags(0)
+{
+  DBUG_ASSERT(m_table && m_table->s);
+
+  if (thd_arg->options & OPTION_NO_FOREIGN_KEY_CHECKS)
+      set_flags(NO_FOREIGN_KEY_CHECKS_F);
+  if (thd_arg->options & OPTION_RELAXED_UNIQUE_CHECKS)
+      set_flags(RELAXED_UNIQUE_CHECKS_F);
+  /* if bitmap_init fails, catched in is_valid() */
+  if (likely(!bitmap_init(&m_cols,
+                          m_width <= sizeof(m_bitbuf)*8 ? m_bitbuf : NULL,
+                          (m_width + 7) & ~7UL,
+                          false)))
+    memcpy(m_cols.bitmap, cols->bitmap, byte_count(cols));
+}
+#endif
+
+Rows_log_event::Rows_log_event(const char *buf, uint event_len,
+                               Log_event_type event_type,
+                               const Format_description_log_event
+                               *description_event)
+  : Log_event(buf, description_event),
+    m_rows_buf(0), m_rows_cur(0), m_rows_end(0)
+{
+  DBUG_ENTER("Rows_log_event::Rows_log_event(const char*,...)");
+  uint8 const common_header_len= description_event->common_header_len;
+  uint8 const post_header_len= description_event->post_header_len[event_type-1];
+
+  DBUG_PRINT("enter",("event_len=%ld, common_header_len=%d, "
+		      "post_header_len=%d",
+		      event_len, common_header_len,
+		      post_header_len));
+
+  char const* const post_begin= buf + common_header_len;
+  m_table_id= uint4korr(post_begin + RW_MAPID_OFFSET);
+
+  m_flags= uint2korr(post_begin + RW_FLAGS_OFFSET);
+
+  byte const *const var_begin= post_begin + ROWS_HEADER_LEN;
+  byte const *const ptr_width= var_begin;
+  byte const *const ptr_after_width= my_vle_decode(&m_width, ptr_width);
+
+  const uint byte_count= (m_width + 7) / 8;
+  const char* const ptr_rows_data= var_begin + byte_count + 1;
+
+  my_size_t const data_size= event_len - (ptr_rows_data - buf);
+  DBUG_PRINT("info",("m_table_id=%d, m_flags=%d, m_width=%u, data_size=%lu",
+                     m_table_id, m_flags, m_width, data_size));
+
+  m_rows_buf= my_malloc(data_size, MYF(MY_WME));
+  if (m_rows_buf)
+  {
+    /* if bitmap_init fails, catched in is_valid() */
+    if (likely(!bitmap_init(&m_cols,
+                            m_width <= sizeof(m_bitbuf)*8 ? m_bitbuf : NULL,
+                            (m_width + 7) & ~7UL,
+                            false)))
+      memcpy(m_cols.bitmap, ptr_after_width, byte_count);
+    m_rows_end= m_rows_buf + data_size;
+    m_rows_cur= m_rows_end;
+    memcpy(m_rows_buf, ptr_rows_data, data_size);
+  }
+
+  DBUG_VOID_RETURN;
+}
+
+Rows_log_event::~Rows_log_event()
+{
+  my_free(m_rows_buf, MYF(MY_ALLOW_ZERO_PTR));
+}
+
+#ifndef MYSQL_CLIENT
+int Rows_log_event::do_add_row_data(byte *const row_data,
+                                    my_size_t const length)
+{
+  /*
+    When the table has a primary key, we would probably want, by default, to
+    log only the primary key value instead of the entire "before image". This
+    would save binlog space. TODO
+  */
+  DBUG_ENTER("Rows_log_event::do_add_row_data(byte *data, my_size_t length)");
+  DBUG_PRINT("enter", ("row_data= %p, length= %lu", row_data, length));
+  DBUG_DUMP("row_data", row_data, min(length, 32));
+
+  DBUG_ASSERT(m_rows_buf <= m_rows_cur);
+  DBUG_ASSERT(m_rows_buf <  m_rows_end);
+  DBUG_ASSERT(m_rows_cur <= m_rows_end);
+
+  /* The cast will always work since m_rows_cur <= m_rows_end */
+  if (static_cast<my_size_t>(m_rows_end - m_rows_cur) < length)
+  {
+    my_size_t const block_size= 1024;
+    my_ptrdiff_t const old_alloc= m_rows_end - m_rows_buf;
+    my_ptrdiff_t const new_alloc= 
+        old_alloc + block_size * (length / block_size + block_size - 1);
+    my_ptrdiff_t const cur_size= m_rows_cur - m_rows_buf;
+
+    byte* const new_buf= my_realloc(m_rows_buf, new_alloc, MYF(MY_WME));
+    if (unlikely(!new_buf))
+      DBUG_RETURN(HA_ERR_OUT_OF_MEM);
+
+    /* If the memory moved, we need to move the pointers */
+    if (new_buf != m_rows_buf)
+    {
+      m_rows_buf= new_buf;
+      m_rows_cur= m_rows_buf + cur_size;
+    }
+
+    /*
+       The end pointer should always be changed to point to the end of
+       the allocated memory.
+    */
+    m_rows_end= m_rows_buf + new_alloc;
+  }
+
+  DBUG_ASSERT(m_rows_cur + length < m_rows_end);
+  memcpy(m_rows_cur, row_data, length);
+  m_rows_cur+= length;
+  DBUG_RETURN(0);
+}
+#endif
+
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+/*
+  Unpack a row into a record. The row is assumed to only consist of the fields
+  for which the bitset represented by 'arr' and 'bits'; the other parts of the
+  record are left alone.
+ */
+static char const *unpack_row(THD *thd, TABLE *table,
+                              char *record, char const *row,
+                              MY_BITMAP const *cols)
+{
+  DBUG_ASSERT(record && row);
+
+  my_size_t const n_null_bytes= table->s->null_bytes;
+  my_ptrdiff_t const offset= record - (byte*) table->record[0];
+
+  memcpy(record, row, n_null_bytes);
+  char const *ptr= row + n_null_bytes;
+
+  Field **const begin_ptr = table->field;
+  for (Field **field_ptr= begin_ptr ; *field_ptr ; ++field_ptr)
+  {
+    Field *const f= *field_ptr;
+
+    if (bitmap_is_set(cols, field_ptr -  begin_ptr))
+    {
+      /* Field...::unpack() cannot return 0 */
+      ptr= f->unpack(f->ptr + offset, ptr);
+      f->query_id= thd->query_id;
+    }
+    else
+    {
+        f->query_id= 0UL;
+    }
+  }
+  return ptr;
+}
+
+int Rows_log_event::exec_event(st_relay_log_info *rli)
+{
+  DBUG_ENTER("Rows_log_event::exec_event(st_relay_log_info*)");
+  int error= 0;
+  TABLE* table= rli->m_table_map.get_table(m_table_id);
+  char const *row_start= m_rows_buf;
+
+  /*
+    'thd' has been set by exec_relay_log_event(), just before calling
+    exec_event(). We still check here to prevent future coding errors.
+  */
+  DBUG_ASSERT(rli->sql_thd == thd);
+
+  /*
+    lock_tables() reads the contents of thd->lex, so they must be
+    initialized, so we should call lex_start(); to be even safer, we call
+    mysql_init_query() which does a more complete set of inits.
+  */
+  mysql_init_query(thd, NULL, 0);
+
+  if (table)
+  {
+    /*
+      table == NULL means that this table should not be
+      replicated (this was set up by Table_map_log_event::exec_event() which
+      tested replicate-* rules).
+    */
+    TABLE_LIST table_list;
+    bool need_reopen;
+    uint count= 1;
+    bzero(&table_list, sizeof(table_list));
+    table_list.lock_type= TL_WRITE;
+    table_list.next_global= table_list.next_local= 0;
+    table_list.table= table;
+
+    for ( ; ; )
+    {
+      table_list.db= const_cast<char*>(table->s->db);
+      table_list.alias= table_list.table_name=
const_cast<char*>(table->s->table_name);
+
+      if ((error= lock_tables(thd, &table_list, count, &need_reopen)) == 0)
+        break;
+      if (!need_reopen)
+      {
+        slave_print_error(rli, error, 
+                            "Error in %s event: error during table %s.%s lock",
+                            get_type_str(), table->s->db,
table->s->table_name);
+        DBUG_RETURN(error);
+      }
+      /*
+        we need to store a local copy of the table names since the table object
+        will become invalid after close_tables_for_reopen
+      */
+      char *db= my_strdup(table->s->db,MYF(MY_WME));
+      char *table_name= my_strdup(table->s->table_name,MYF(MY_WME));
+
+      if (db == 0 || table_name == 0)
+      {
+        /*
+           Since the lock_tables() failed, the table is not locked, so
+           we don't need to unlock them.
+        */
+        DBUG_RETURN(HA_ERR_OUT_OF_MEM);
+      }
+
+      /*
+        We also needs to flush the pending RBR event, since it keeps a
+        pointer to an open table.  
+
+        ALTERNATIVE SOLUTION: Extract a pointer to the pending RBR
+        event and reset the table pointer after the tables has been
+        reopened.
+       */
+      thd->binlog_flush_pending_rows_event(false);
+
+      close_tables_for_reopen(thd, &table_list);
+
+      /* open the table again, same as in Table_map_event::exec_event */
+      table_list.db= const_cast<char*>(db);
+      table_list.alias= table_list.table_name= const_cast<char*>(table_name);
+      table_list.updating= 1;
+      TABLE_LIST *tables= &table_list;
+      if ((error= open_tables(thd, &tables, &count, 0)) == 0)
+      {
+        /* reset some variables for the table list*/
+        table_list.updating= 0;
+        /* retrieve the new table reference and update the table map */
+        table= table_list.table;
+        error= rli->m_table_map.set_table(m_table_id, table);
+      }
+      else /* error in open_tables */
+      {
+        if (thd->query_error || thd->is_fatal_error)
+        {
+          /*
+            Error reporting borrowed from Query_log_event with many excessive
+            simplifications (we don't honour --slave-skip-errors)
+          */
+          uint actual_error= thd->net.last_errno;
+          slave_print_error(rli,actual_error,
+                            "Error '%s' on reopening table `%s`.`%s`",
+                            (actual_error ? thd->net.last_error :
+                             "unexpected success or fatal error"),
+                            db, table_name);
+          thd->query_error= 1;
+        }
+      }
+      my_free((char*) db, MYF(MY_ALLOW_ZERO_PTR));
+      my_free((char*) table_name, MYF(MY_ALLOW_ZERO_PTR));
+
+      if (error)
+        DBUG_RETURN(error);
+    }
+
+    /*
+      It's not needed to set_time() but
+      1) it continues the property that "Time" in SHOW PROCESSLIST shows how
+      much slave is behind
+      2) it will be needed when we allow replication from a table with no
+      TIMESTAMP column to a table with one.
+      So we call set_time(), like in SBR. Presently it changes nothing.
+    */
+    thd->set_time((time_t)when);
+    /*
+      There are a few flags that are replicated with each row event.
+      Make sure to set/clear them before executing the main body of
+      the event.
+    */
+    if (get_flags(NO_FOREIGN_KEY_CHECKS_F))
+        thd->options|= OPTION_NO_FOREIGN_KEY_CHECKS;
+    else
+        thd->options&= ~OPTION_NO_FOREIGN_KEY_CHECKS;
+
+    if (get_flags(RELAXED_UNIQUE_CHECKS_F))
+        thd->options|= OPTION_RELAXED_UNIQUE_CHECKS;
+    else
+        thd->options&= ~OPTION_RELAXED_UNIQUE_CHECKS;
+    /* A small test to verify that objects have consistent types */
+    DBUG_ASSERT(sizeof(thd->options) == sizeof(OPTION_RELAXED_UNIQUE_CHECKS));
+
+    error= do_before_row_operations(table);
+    while (error == 0 && row_start < m_rows_end) {
+      char const *row_end= do_prepare_row(thd, table, row_start);
+      DBUG_ASSERT(row_end != NULL); // cannot happen
+      DBUG_ASSERT(row_end <= m_rows_end);
+
+      error= do_exec_row(table, rli);
+      switch (error)
+      {
+        /* Some recoverable errors */
+      case HA_ERR_RECORD_CHANGED:
+      case HA_ERR_KEY_NOT_FOUND:	/* Idempotency support: OK if
+                                           tuple does not exist */
+	/*
+          ToDo: clear and warnings/errors that has been pushed to the
+          thread?
+        */
+	error= 0;
+      case 0:
+	break;
+
+      default:
+	slave_print_error(rli, error,
+			  "Error in %s event: row application failed",
+			  get_type_str());
+	thd->query_error= 1;
+	break;
+      }
+
+      row_start= row_end;
+    }
+    error= do_after_row_operations(table, error);
+  }
+
+  if (error)
+  {                     /* error has occured during the transaction */
+    rli->m_table_map.clear_tables();
+    /*
+      We do the two next calls to be sure to rollback whether it was
+      autocommit or in a transaction.
+      If one day we honour --skip-slave-errors in row-based replication, and
+      the error should be skipped, then we would clear mappings, rollback,
+      close tables, but the slave SQL thread would not stop and then may
+      assume the mapping is still available, the tables are still open...
+      So then we should clear mappings/rollback/close here only if this is a
+      TRANS_END_F.
+      For now we code, knowing that error is not skippable and so slave SQL
+      thread is certainly going to stop.
+    */
+    ha_autocommit_or_rollback(thd, 1);
+    ha_rollback(thd);
+
+    close_thread_tables(thd);
+    thd->query_error= 1;
+    DBUG_RETURN(error);
+  }
+
+  /*
+    Some of this is copied from the Log_event::exec_event(). We step
+    the relay log positions ourself, since the generic version does
+    not behave the way we want.
+   */
+  if (get_flags(TRANS_END_F))
+  {
+    /*
+      This is the end of a statement or transaction, so close (and
+      unlock) the tables we opened when processing the
+      Table_map_log_event starting the statement.
+
+      OBSERVER.  This will clear *all* mappings, not only those that
+      are open for the table. There is not good handle for on-close
+      actions for tables.
+
+      NOTE. Even if we have no table ('table' == 0) we still need to be
+      here, so that we increase the group relay log position. If we didn't, we
+      could have a group relay log position which lags behind "forever"
+      (assume the last master's transaction is ignored by the slave because of
+      replicate-ignore rules).
+    */
+    rli->m_table_map.clear_tables();
+
+    thd->prepare_for_commit();
+    /*
+      If this event is not in a transaction, the call below will, if some
+      transactional storage engines are involved, commit the statement into
+      them and flush the pending event to binlog.
+      If this event is in a transaction, the call will do nothing, but a
+      Xid_log_event will come next which will, if some transactional engines
+      are involved, commit the transaction and flush the pending event to the
+      binlog.
+    */
+    error= ha_autocommit_or_rollback(thd, 0);
+    /*
+      Now what if this is not a transactional engine? we still need to flush
+      the pending event to the binlog; we did it with
+      thd->prepare_for_commit(). Note that we imitate what is done for real
+      queries: a call to ha_autocommit_or_rollback() (sometimes only if
+      involves a transactional engine), and a call to be sure to have the
+      pending event flushed.
+    */
+
+    close_thread_tables(thd); // unlocks and closes tables
+    rli->transaction_end(thd);
+
+    if (error == 0)
+    {
+      rli->inc_group_relay_log_pos(log_pos);
+      flush_relay_log_info(rli);
+      rli->last_master_timestamp= when;
+    }
+    else
+    {
+      slave_print_error(rli, error,
+			"Error in %s event: commit of row events failed, "
+			"table `%s`.`%s`",
+			get_type_str(), table->s->db, table->s->table_name);
+    }
+    DBUG_RETURN(error);
+  }
+
+  if (table)
+  {
+    /*
+      As "table" is not NULL, we did a successful lock_tables(), without any
+      prior LOCK TABLES and are not in prelocked mode, so this assertion should
+      be true.
+    */
+    DBUG_ASSERT(thd->lock);
+    /*
+      If we are here, there are more events to come which may use our mappings
+      and our table. So don't clear mappings or close tables, just unlock
+      tables.
+      Why don't we lock the table once for all in
+      Table_map_log_event::exec_event() ? Because we could have in binlog:
+      BEGIN;
+      Table_map t1 -> 1
+      Write_rows to id 1
+      Table_map t2 -> 2
+      Write_rows to id 2
+      Xid_log_event
+      So we cannot lock t1 when executing the first Table_map, because at that
+      moment we don't know we'll also have to lock t2, and all tables must be
+      locked at once in MySQL.
+    */
+    mysql_unlock_tables(thd, thd->lock);
+    thd->lock= 0;
+  }
+
+  DBUG_ASSERT(error == 0);
+  rli->inc_event_relay_log_pos();
+  
+  DBUG_RETURN(0);
+}
+#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
+
+bool Rows_log_event::write_data_header(IO_CACHE *file)
+{
+  byte buf[ROWS_HEADER_LEN];	// No need to init the buffer
+  int4store(buf + RW_MAPID_OFFSET, m_table_id);
+  int2store(buf + RW_FLAGS_OFFSET, m_flags);
+  return (my_b_safe_write(file, buf, ROWS_HEADER_LEN));
+}
+
+bool Rows_log_event::write_data_body(IO_CACHE*file)
+{
+  /*
+     Note that this should be the number of *bits*, not the number of
+     bytes.
+  */
+  byte sbuf[my_vle_sizeof(m_width)];
+  my_ptrdiff_t const data_size= m_rows_cur - m_rows_buf;
+
+  char *const sbuf_end= my_vle_encode(sbuf, sizeof(sbuf), m_width);
+  DBUG_ASSERT(static_cast<my_size_t>(sbuf_end - sbuf) <= sizeof(sbuf));
+
+  return (my_b_safe_write(file, sbuf, sbuf_end - sbuf) ||
+          my_b_safe_write(file, reinterpret_cast<byte*>(m_cols.bitmap),
+                          byte_count(&m_cols)) ||
+          my_b_safe_write(file, m_rows_buf, data_size));
+}
+
+#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) &&
defined(DBUG_RBR)
+void Rows_log_event::pack_info(Protocol *protocol)
+{
+    char buf[256];
+    char const *const flagstr= get_flags(TRANS_END_F) ? "TRANS_END_F" : "";
+    char const *const dbnam= m_table->s->db;
+    char const *const tblnam= m_table->s->table_name;
+    my_size_t bytes= snprintf(buf, sizeof(buf),
+                              "%s.%s - %s", dbnam, tblnam, flagstr);
+    protocol->store(buf, bytes, &my_charset_bin);
+}
+#endif
+
+
+/**************************************************************************
+	Table_map_log_event member functions
+**************************************************************************/
+
+/*
+  Constructor used to build an event for writing to the binary log.
+  Mats says tbl->s lives longer than this event so it's ok to copy pointers
+  (tbl->s->db etc) and not pointer content.
+ */
+#if !defined(MYSQL_CLIENT)
+Table_map_log_event::Table_map_log_event(THD *thd, TABLE *tbl, ulong tid,
+                                         bool is_transactional, uint16 flags)
+  : Log_event(thd, 0, is_transactional),
+    m_table(tbl),
+    m_dbnam(tbl->s->db),
+    m_dblen(m_dbnam ? strlen(m_dbnam) : 0),
+    m_tblnam(tbl->s->table_name),
+    m_tbllen(strlen(m_tblnam)),
+    m_colcnt(tbl->s->fields), m_coltype(0),
+    m_table_id(tid),
+    m_flags(flags)
+{
+  m_data_size=  TABLE_MAP_HEADER_LEN;
+  m_data_size+= m_dblen + 2;	// Include length and terminating \0
+  m_data_size+= m_tbllen + 2;	// Include length and terminating \0
+  m_data_size+= 1 + m_colcnt;	// COLCNT and column types
+
+  /* If malloc fails, catched in is_valid() */
+  if ((m_memory= my_malloc(m_colcnt, MYF(MY_WME))))
+  {
+    m_coltype= reinterpret_cast<unsigned char*>(m_memory);
+    for (unsigned int i= 0 ; i < m_table->s->fields ; ++i)
+      m_coltype[i]= m_table->field[i]->type();
+  }
+}
+#endif /* !defined(MYSQL_CLIENT) */
+
+/*
+  Constructor used by slave to read the event from the binary log.
+ */
+#if defined(HAVE_REPLICATION)
+Table_map_log_event::Table_map_log_event(const char *buf, uint event_len,
+                                         const Format_description_log_event
+                                         *description_event)
+
+  : Log_event(buf, description_event),
+#ifndef MYSQL_CLIENT
+  m_table(NULL),
+#endif
+  m_memory(NULL)
+{
+  DBUG_ENTER("Table_map_log_event::Table_map_log_event(const char*,uint,...)");
+
+  uint8 common_header_len= description_event->common_header_len;
+  uint8 post_header_len= description_event->post_header_len[TABLE_MAP_EVENT-1];
+  DBUG_PRINT("info",("event_len=%ld, common_header_len=%d, post_header_len=%d",
+                     event_len, common_header_len, post_header_len));
+
+  DBUG_DUMP("event buffer", buf, event_len);
+
+  /* Read the post-header */
+  const char *const post_start= buf + common_header_len;
+
+  m_table_id= uint4korr(post_start + TM_MAPID_OFFSET);
+
+  m_flags= uint2korr(post_start + TM_FLAGS_OFFSET);
+
+  /* Read the variable part of the event */
+  const char *const vpart= post_start + post_header_len;
+
+  /* Extract the length of the various parts from the buffer */
+  byte const* const ptr_dblen= vpart + 0;
+  m_dblen= *(unsigned char*) ptr_dblen;
+
+  /* Length of database name + counter + terminating null */
+  byte const* const ptr_tbllen= ptr_dblen + m_dblen + 2;
+  m_tbllen= *(unsigned char*) ptr_tbllen;
+
+  /* Length of table name + counter + terminating null */
+  byte const* const ptr_colcnt= ptr_tbllen + m_tbllen + 2;
+  byte const* const ptr_after_colcnt= my_vle_decode(&m_colcnt, ptr_colcnt);
+
+  DBUG_PRINT("info",("m_dblen=%d off=%d m_tbllen=%d off=%d m_colcnt=%d off=%d",
+                     m_dblen, ptr_dblen-vpart, m_tbllen, ptr_tbllen-vpart,
+                     m_colcnt, ptr_colcnt-vpart));
+
+  /* Allocate mem for all fields in one go. If fails, catched in is_valid() */
+  m_memory= my_multi_malloc(MYF(MY_WME),
+			    &m_dbnam, m_dblen + 1,
+			    &m_tblnam, m_tbllen + 1,
+			    &m_coltype, m_colcnt,
+			    NULL);
+
+  if (m_memory)
+  {
+    /* Copy the different parts into their memory */
+    strncpy(const_cast<char*>(m_dbnam),  ptr_dblen  + 1, m_dblen + 1);
+    strncpy(const_cast<char*>(m_tblnam), ptr_tbllen + 1, m_tbllen + 1);
+    memcpy(m_coltype, ptr_after_colcnt, m_colcnt);
+  }
+
+  DBUG_VOID_RETURN;
+}
+#endif
+
+Table_map_log_event::~Table_map_log_event()
+{
+  my_free(m_memory, MYF(MY_ALLOW_ZERO_PTR));
+}
+
+/*
+  Find a table based on database name and table name.
+
+  DESCRIPTION
+
+    Currently, only the first table of the 'table_list' is located. If the
+    table is found in the list of open tables for the thread, the 'table'
+    field of 'table_list' is filled in.
+
+  PARAMETERS
+
+    thd        Thread structure
+    table_list List of tables to locate in the thd->open_tables list.
+    count      Pointer to a variable that will be set to the number of
+               tables found. If the pointer is NULL, nothing will be stored.
+
+  RETURN VALUE
+
+    The number of tables found.
+
+  TO DO
+
+    Replace the list of table searches with a hash based on the combined
+    database and table name.  The handler_tables_hash is inappropriate since
+    it hashes on the table alias.  At the same time, the function can be
+    extended to handle a full list of table names, in the same spirit as
+    open_tables() and lock_tables().
+*/
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+static uint find_tables(THD *thd, TABLE_LIST *table_list, uint *count)
+{
+  uint result= 0;
+
+  /* we verify that the caller knows our limitation */
+  DBUG_ASSERT(table_list->next_global == 0);
+  for (TABLE *table= thd->open_tables; table ; table= table->next)
+  {
+    if (strcmp(table->s->db, table_list->db) == 0
+	&& strcmp(table->s->table_name, table_list->table_name) == 0)
+    {
+      /* Copy the table pointer into the table list. */
+      table_list->table= table;
+      result= 1;
+      break;
+    }
+  }
+
+  if (count)
+    *count= result;
+  return result;
+}
+#endif
+
+/*
+  Return value is an error code, one of:
+
+      -1     Failure to open table   [from open_tables()]
+       0     Success
+       1     No room for more tables [from set_table()]
+       2     Out of memory           [from set_table()]
+       3     Wrong table definition
+       4     Daisy-chaining RBR with SBR not possible
+ */
+
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+int Table_map_log_event::exec_event(st_relay_log_info *rli)
+{
+  DBUG_ENTER("Table_map_log_event::exec_event(st_relay_log_info*)");
+
+  DBUG_ASSERT(rli->sql_thd == thd);
+
+  /* Step the query id to mark what columns that are actually used. */
+  pthread_mutex_lock(&LOCK_thread_count);
+  thd->query_id= next_query_id();
+  pthread_mutex_unlock(&LOCK_thread_count);
+
+  TABLE_LIST table_list;
+  uint32 dummy_len;
+  bzero(&table_list, sizeof(table_list));
+  table_list.db= const_cast<char *>(rewrite_db(m_dbnam, &dummy_len));
+  table_list.alias= table_list.table_name= const_cast<char*>(m_tblnam);
+  table_list.lock_type= TL_WRITE;
+  table_list.next_global= table_list.next_local= 0;
+  table_list.updating= 1;
+
+  int error= 0;
+
+  if (db_ok(table_list.db, replicate_do_db, replicate_ignore_db) &&
+      (!table_rules_on || tables_ok(thd, &table_list)))
+  {
+    /*
+      Check if the slave is set to use SBR.  If so, the slave should
+      stop immediately since it is not possible to daisy-chain from
+      RBR to SBR.  Once RBR is used, the rest of the chain has to use
+      RBR.
+    */
+    if (mysql_bin_log.is_open() && (thd->options & OPTION_BIN_LOG)
&&
+        !binlog_row_based)
+    {
+      slave_print_error(rli, ER_BINLOG_ROW_RBR_TO_SBR,
+                        "It is not possible to use statement-based binlogging "
+                        "on a slave that replicates row-based.  Please use "
+                        "--binrow-format=row on slave if you want to use "
+                        "--log-slave-updates and read row-based binlog events.");
+      DBUG_RETURN(ERR_RBR_TO_SBR);
+    }
+
+    /*
+      Open the table if it is not already open and add the table to table map.
+      If the table should not be replicated, we don't bother to do anything.
+      The table map will return NULL and the row-level event will effectively
+      be a no-op.
+    */
+    uint count;
+    if (find_tables(thd, &table_list, &count) == 0)
+    {
+      /*
+        open_tables() reads the contents of thd->lex, so they must be
+        initialized, so we should call lex_start(); to be even safer, we call
+        mysql_init_query() which does a more complete set of inits.
+      */
+      mysql_init_query(thd, NULL, 0);
+      TABLE_LIST *tables= &table_list;
+      if ((error= open_tables(thd, &tables, &count, 0)))
+      {
+        if (thd->query_error || thd->is_fatal_error)
+        {
+          /*
+            Error reporting borrowed from Query_log_event with many excessive
+            simplifications (we don't honour --slave-skip-errors)
+          */
+          uint actual_error= thd->net.last_errno;
+          slave_print_error(rli,actual_error,
+                            "Error '%s' on opening table `%s`.`%s`",
+                            (actual_error ? thd->net.last_error :
+                             "unexpected success or fatal error"),
+                            table_list.db, table_list.table_name);
+          thd->query_error= 1;
+        }
+        DBUG_RETURN(error);
+      }
+    }
+
+    m_table= table_list.table;
+
+    /*
+      This will fail later otherwise, the 'in_use' field should be
+      set to the current thread.
+    */
+    DBUG_ASSERT(m_table->in_use);
+
+    /*
+      Check that the number of columns and the field types in the
+      event match the number of columns and field types in the opened
+      table.
+     */
+    uint col= m_table->s->fields;
+
+    if (col == m_colcnt)
+    {
+      while (col-- > 0)
+        if (m_table->field[col]->type() != m_coltype[col])
+          break;
+    }
+
+    TABLE_SHARE const *const tsh= m_table->s;
+
+    /*
+      Check the following termination conditions:
+
+      (col == m_table->s->fields)
+          ==> (m_table->s->fields != m_colcnt)
+      (0 <= col < m_table->s->fields)
+          ==> (m_table->field[col]->type() != m_coltype[col])
+
+      Logically, A ==> B is equivalent to !A || B
+
+      Since col is unsigned, is suffices to check that col <=
+      tsh->fields.  If col wrapped (by decreasing col when it is 0),
+      the number will be UINT_MAX, which is greater than tsh->fields.
+    */
+    DBUG_ASSERT(!(col == tsh->fields) || tsh->fields != m_colcnt);
+    DBUG_ASSERT(!(col < tsh->fields) ||
+                (m_table->field[col]->type() != m_coltype[col]));
+
+    if (col <= tsh->fields)
+    {
+      /*
+        If we get here, the number of columns in the event didn't
+        match the number of columns in the table on the slave, *or*
+        there were a column in the table on the slave that did not
+        have the same type as given in the event.
+
+        If 'col' has the value that was assigned to it, it was a
+        mismatch between the number of columns on the master and the
+        slave.
+       */
+      if (col == tsh->fields)
+      {
+        DBUG_ASSERT(tsh->db && tsh->table_name);
+        slave_print_error(rli, ER_BINLOG_ROW_WRONG_TABLE_DEF,
+                          "Table width mismatch - "
+                          "received %u columns, %s.%s has %u columns",
+                          m_colcnt, tsh->db, tsh->table_name, tsh->fields);
+      }
+      else
+      {
+        DBUG_ASSERT(col < m_colcnt && col < tsh->fields);
+        DBUG_ASSERT(tsh->db && tsh->table_name);
+        slave_print_error(rli, ER_BINLOG_ROW_WRONG_TABLE_DEF,
+                          "Column %d type mismatch - "
+                          "received type %d, %s.%s has type %d",
+                          col, m_coltype[col], tsh->db, tsh->table_name, 
+                          m_table->field[col]->type());
+      }
+
+      thd->query_error= 1;
+      DBUG_RETURN(ERR_BAD_TABLE_DEF);
+    }
+
+    /*
+      We record in the slave's information that the number m_table_id is
+      mapped to the m_table object
+    */
+    if (!error)
+      error= rli->m_table_map.set_table(m_table_id, m_table);
+
+    /*
+      Tell the RLI that we are touching a table.
+
+      TODO: Maybe we can combine this with the previous operation?
+    */
+    if (!error)
+        rli->touching_table(m_dbnam, m_tblnam, m_table_id);
+  }
+
+  /*
+    We explicitly do not call Log_event::exec_event() here since we do not
+    want the relay log position to be flushed to disk. The flushing will be
+    done by the last Rows_log_event that either ends a statement (outside a
+    transaction) or a transaction.
+
+    A table map event can *never* end a transaction or a statement, so we
+    just step the relay log position.
+  */
+
+  if (likely(!error))
+    rli->inc_event_relay_log_pos();
+
+  DBUG_RETURN(error);
+}
+#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
+
+#ifndef MYSQL_CLIENT
+bool Table_map_log_event::write_data_header(IO_CACHE *file)
+{
+  byte buf[TABLE_MAP_HEADER_LEN];
+  int4store(buf + TM_MAPID_OFFSET, m_table_id);
+  int2store(buf + TM_FLAGS_OFFSET, m_flags);
+  return (my_b_safe_write(file, buf, TABLE_MAP_HEADER_LEN));
+}
+
+bool Table_map_log_event::write_data_body(IO_CACHE *file)
+{
+  DBUG_ASSERT(m_dbnam != NULL);
+  DBUG_ASSERT(m_tblnam != NULL);
+  /* We use only one byte per length for storage in event: */
+  DBUG_ASSERT(m_dblen < 128);
+  DBUG_ASSERT(m_tbllen < 128);
+
+  byte const dbuf[]= { m_dblen };
+  byte const tbuf[]= { m_tbllen };
+
+  byte cbuf[my_vle_sizeof(m_colcnt)];
+  byte *const cbuf_end= my_vle_encode(cbuf, sizeof(cbuf), m_colcnt);
+  DBUG_ASSERT(static_cast<my_size_t>(cbuf_end - cbuf) <= sizeof(cbuf));
+
+  return (my_b_safe_write(file, dbuf,      sizeof(dbuf)) ||
+          my_b_safe_write(file, m_dbnam,   m_dblen+1) ||
+          my_b_safe_write(file, tbuf,      sizeof(tbuf)) ||
+          my_b_safe_write(file, m_tblnam,  m_tbllen+1) ||
+          my_b_safe_write(file, cbuf,      cbuf_end - cbuf) ||
+          my_b_safe_write(file, reinterpret_cast<char*>(m_coltype), m_colcnt));
+ }
+#endif
+
+#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
+
+/*
+  Print some useful information for the SHOW BINARY LOG information
+  field.
+ */
+
+void Table_map_log_event::pack_info(Protocol *protocol)
+{
+    char buf[256];
+    my_size_t bytes= snprintf(buf, sizeof(buf), "%s.%s", m_dbnam, m_tblnam);
+    protocol->store(buf, bytes, &my_charset_bin);
+}
+
+#endif
+
+
+#ifdef MYSQL_CLIENT
+void Table_map_log_event::print(FILE *file, PRINT_EVENT_INFO *print_event_info)
+{
+  if (!print_event_info->short_form)
+  {
+    print_header(file, print_event_info);
+    fprintf(file, "\tTable_map\t"
+            "`%s`.`%s` mapped to number %lu\n",
+            m_dbnam, m_tblnam, m_table_id);
+  }
+}
+#endif
+
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+void Table_map_log_event::cleanup(THD *thd_arg, struct st_relay_log_info *rli)
+{
+  /*
+    Instances of Table_map_log_event, if ::exec_event() was called on them,
+    may have opened tables, which we cannot be sure have been closed (because
+    maybe the Rows_log_event have not been found or will not be, because slave
+    SQL thread is stopping, or relay log has a missing tail etc). So we close
+    all thread's tables. And so the table mappings have to be cancelled.
+  */
+  rli->m_table_map.clear_tables();
+  close_thread_tables(thd_arg);
+}
+#endif
+
+/**************************************************************************
+	Write_rows_log_event member functions
+**************************************************************************/
+
+/*
+  Constructor used to build an event for writing to the binary log.
+ */
+#if !defined(MYSQL_CLIENT)
+Write_rows_log_event::Write_rows_log_event(THD *thd_arg, TABLE *tbl_arg,
+                                           ulong tid_arg,
+                                           MY_BITMAP const *cols,
+                                           bool is_transactional)
+  : Rows_log_event(thd_arg, tbl_arg, tid_arg, cols, is_transactional)
+{
+}
+#endif
+
+/*
+  Constructor used by slave to read the event from the binary log.
+ */
+#ifdef HAVE_REPLICATION
+Write_rows_log_event::Write_rows_log_event(const char *buf, uint event_len,
+                                           const Format_description_log_event
+                                           *description_event)
+: Rows_log_event(buf, event_len, WRITE_ROWS_EVENT, description_event)
+{
+}
+#endif
+
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+int Write_rows_log_event::do_before_row_operations(TABLE *table)
+{
+  int error= 0;
+
+  /*
+    We are using REPLACE semantics and not INSERT IGNORE semantics
+    when writing rows, that is: new rows replace old rows.  We need to
+    inform the storage engine that it should use this behaviour.
+  */
+
+  /* Tell the SE that we are using REPLACE semantics. */
+  thd->lex->duplicates= DUP_REPLACE;
+
+  /*
+    Pretend we're executing a REPLACE command: this is needed for
+    InnoDB and NDB Cluster since they are not (properly) checking the
+    lex->duplicates flag.
+  */
+  thd->lex->sql_command= SQLCOM_REPLACE;
+
+  // needed for ndbcluster
+  table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
+  /* TODO: Ensure that myisam allow write_row() with duplicate key. */
+
+  /*
+    TODO: the cluster team (Tomas?) says that it's better if the engine knows
+    how many rows are going to be inserted, then it can allocate needed memory
+    from the start.
+  */
+  table->file->start_bulk_insert(0);
+  /*
+    We need TIMESTAMP_NO_AUTO_SET otherwise ha_write_row() will not use fill
+    any TIMESTAMP column with data from the row but instead will use
+    the event's current time.
+    As we replicate from TIMESTAMP to TIMESTAMP and slave has no extra
+    columns, we know that all TIMESTAMP columns on slave will receive explicit
+    data from the row, so TIMESTAMP_NO_AUTO_SET is ok.
+    When we allow a table without TIMESTAMP to be replicated to a table having
+    more columns including a TIMESTAMP column, or when we allow a TIMESTAMP
+    column to be replicated into a BIGINT column and the slave's table has a
+    TIMESTAMP column, then the slave's TIMESTAMP column will take its value
+    from set_time() which we called earlier (consistent with SBR). And then in
+    some cases we won't want TIMESTAMP_NO_AUTO_SET (will require some code to
+    analyze if explicit data is provided for slave's TIMESTAMP columns).
+  */
+  table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
+  return error;
+}
+
+int Write_rows_log_event::do_after_row_operations(TABLE *table, int error)
+{
+  DBUG_ENTER("Write_rows_log_event::do_exec_row");
+  if (error == 0)
+    error= table->file->end_bulk_insert();
+  DBUG_RETURN(error);
+}
+
+char const *Write_rows_log_event::do_prepare_row(THD *thd, TABLE *table,
+                                                 char const *row_start)
+{
+  char const *ptr= row_start;
+  DBUG_ASSERT(table != NULL);
+  /*
+    This assertion actually checks that there is at least as many
+    columns on the slave as on the master.
+  */
+  DBUG_ASSERT(table->s->fields >= m_width);
+  DBUG_ASSERT(ptr);
+  ptr= unpack_row(thd, table, table->record[0], ptr, &m_cols);
+  return ptr;
+}
+
+/*
+  Check if there are more UNIQUE keys after the given key.
+*/
+static int
+last_uniq_key(TABLE *table, uint keyno)
+{
+  while (++keyno < table->s->keys)
+    if (table->key_info[keyno].flags & HA_NOSAME)
+      return 0;
+  return 1;
+}
+
+// Anonymous namespace for template functions/classes
+namespace {
+
+  /*
+    Smart pointer that will automatically call my_afree (a macro) when
+    the pointer goes out of scope.  This is used so that I do not have
+    to remember to call my_afree() before each return.  There is no
+    overhead associated with this, since all functions are inline.
+
+    I (Matz) would prefer to use the free function as a template
+    parameter, but that is not possible when the "function" is a
+    macro.
+  */
+  template <class Obj>
+  class auto_afree_ptr
+  {
+    Obj* m_ptr;
+  public:
+    auto_afree_ptr(Obj* ptr) : m_ptr(ptr) { }
+    ~auto_afree_ptr() { if (m_ptr) my_afree(m_ptr); }
+    void assign(Obj* ptr) {
+      /* Only to be called if it hasn't been given a value before. */
+      DBUG_ASSERT(m_ptr == NULL);
+      m_ptr= ptr;
+    }
+    Obj* get() { return m_ptr; }
+  };
+
+}
+
+
+/*
+  Replace the provided record in the database.
+
+  Similar to how it is done in <code>mysql_insert()</code>, we first
+  try to do a <code>ha_write_row()</code> and of that fails due to
+  duplicated keys (or indices), we do an <code>ha_update_row()</code>
+  or a <code>ha_delete_row()</code> instead.
+
+  @param thd    Thread context for writing the record.
+  @param table  Table to which record should be written.
+
+  @return Error code on failure, 0 on success.
+ */
+static int
+replace_record(THD *thd, TABLE *table)
+{
+  DBUG_ASSERT(table != NULL && thd != NULL);
+
+  int error;
+  int keynum;
+  auto_afree_ptr<char> key(NULL);
+
+  while ((error= table->file->ha_write_row(table->record[0])))
+  {
+    if ((keynum= table->file->get_dup_key(error)) < 0)
+    {
+      /* We failed to retrieve the duplicate key */
+      return HA_ERR_FOUND_DUPP_KEY;
+    }
+
+    /*
+       We need to retrieve the old row into record[1] to be able to
+       either update or delete the offending record.  We either:
+
+       - use rnd_pos() with a row-id (available as dupp_row) to the
+         offending row, if that is possible (MyISAM and Blackhole), or else
+
+       - use index_read_idx() with the key that is duplicated, to
+         retrieve the offending row.
+     */
+    if (table->file->table_flags() & HA_DUPP_POS)
+    {
+      error= table->file->rnd_pos(table->record[1],
table->file->dupp_ref);
+      if (error)
+        return error;
+    }
+    else
+    {
+      if (table->file->extra(HA_EXTRA_FLUSH_CACHE))
+      {
+        return my_errno;
+      }
+
+      if (key.get() == NULL)
+      {
+       
key.assign(static_cast<char*>(my_alloca(table->s->max_unique_length)));
+        if (key.get() == NULL)
+          return ENOMEM;
+      }
+
+      key_copy(key.get(), table->record[0], table->key_info + keynum, 0);
+      error= table->file->index_read_idx(table->record[1], keynum, key.get(),
+                                         table->key_info[keynum].key_length,
+                                         HA_READ_KEY_EXACT);
+      if (error)
+        return error;
+    }
+
+    /*
+       Now, table->record[1] should contain the offending row.  That
+       will enable us to update it or, alternatively, delete it (so
+       that we can insert the new row afterwards).
+
+       REPLACE is defined as either INSERT or DELETE + INSERT.  If
+       possible, we can replace it with an UPDATE, but that will not
+       work on InnoDB if FOREIGN KEY checks are necessary.
+
+       I (Matz) am not sure of the reason for the last_uniq_key()
+       check as, but I'm guessing that it's something along the
+       following lines.
+
+       Suppose that we got the duplicate key to be a key that is not
+       the last unique key for the table and we perform an update:
+       then there might be another key for which the unique check will
+       fail, so we're better off just deleting the row and inserting
+       the correct row.
+     */
+    if (last_uniq_key(table, keynum) &&
+        !table->file->referenced_by_foreign_key())
+    {
+      error=table->file->ha_update_row(table->record[1],
+                                       table->record[0]);
+      return error;
+    }
+    else
+    {
+      if ((error= table->file->ha_delete_row(table->record[1])))
+        return error;
+
+      /*
+         Matz: Do we need to set no_trans_update for non-transactional
+         tables ???
+      */
+
+      /* Will retry ha_write_row() with the offending row removed. */
+    }
+  }
+  return error;
+}
+
+int Write_rows_log_event::do_exec_row(TABLE *table, st_relay_log_info *rli)
+{
+  DBUG_ASSERT(table != NULL);
+  int error= replace_record(thd, table);
+  return error;
+}
+#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
+
+#ifdef MYSQL_CLIENT
+void Write_rows_log_event::print(FILE *file, PRINT_EVENT_INFO* print_event_info)
+{
+  if (!print_event_info->short_form)
+  {
+    print_header(file, print_event_info);
+    fprintf(file, "\nWrite_rows: table id %lu\n", m_table_id); // TODO: WL#2321
+  }
+}
+#endif
+
+/**************************************************************************
+	Delete_rows_log_event member functions
+**************************************************************************/
+
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+static int record_compare(TABLE *table, byte const *a, byte const *b)
+{
+  for (my_size_t i= 0 ; i < table->s->fields ; ++i)
+  {
+    uint const off= table->field[i]->offset();
+    uint const res= table->field[i]->cmp_binary(a + off, b + off);
+    if (res != 0) {
+      return res;
+    }
+  }
+  return 0;
+}
+
+
+/*
+  Find the row given by 'key', if the table has keys, or else use a table scan
+  to find (and fetch) the row.  If the engine allows random access of the
+  records, a combination of position() and rnd_pos() will be used.
+
+  The 'record_buf' will be used as buffer for records while locating the
+  correct row.
+ */
+static int find_and_fetch_row(TABLE *table, byte *key, byte *record_buf)
+{
+  DBUG_ENTER("find_and_fetch_row(TABLE *table, byte *key, byte *record)");
+  DBUG_PRINT("enter", ("table=%p, key=%p, record=%p",
+		       table, key, record_buf));
+
+  DBUG_ASSERT(table->in_use != NULL);
+
+  if ((table->file->table_flags() & HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS)
+      && table->s->primary_key < MAX_KEY)
+  {
+    /*
+      Use a more efficient method to fetch the record given by
+      table->record[0] if the engine allows it.  We first compute a
+      row reference using the position() member function (it will be
+      stored in table->file->ref) and the use rnd_pos() to position
+      the "cursor" at the correct row.
+    */
+    table->file->position(table->record[0]);
+    DBUG_RETURN(table->file->rnd_pos(table->record[0], table->file->ref));
+  }
+
+  DBUG_ASSERT(record_buf);
+
+  if (table->s->keys > 0)
+  {
+    int error;
+    if ((error= table->file->index_read_idx(record_buf, 0, key,
+                                            table->key_info->key_length,
+                                            HA_READ_KEY_EXACT)))
+    {
+      table->file->print_error(error, MYF(0));
+      DBUG_RETURN(error);
+    }
+
+    /*
+      Below is a minor "optimization".  If the key (i.e., key number
+      0) has the HA_NOSAME flag set, we know that we have found the
+      correct record (since there can be no duplicates); otherwise, we
+      have to compare the record with the one found to see if it is
+      the correct one.
+
+      CAVEAT! This behaviour is essential for the replication of,
+      e.g., the mysql.proc table since the correct record *shall* be
+      found using the primary key *only*.  There shall be no
+      comparison of non-PK columns to decide if the correct record is
+      found.  I can see no scenario where it would be incorrect to
+      chose the row to change only using a PK or an UNNI.
+     */
+    if (table->key_info->flags & HA_NOSAME)
+      DBUG_RETURN(0);
+
+    while (record_compare(table, table->record[0], record_buf) != 0)
+    {
+      int error;
+      if ((error= table->file->index_next(record_buf)))
+      {
+	table->file->print_error(error, MYF(0));
+	DBUG_RETURN(error);
+      }
+    }
+  }
+  else
+  {
+    /* Continue until we find the right record or have made a full loop */
+    int restart_count= 0; // Number of times scanning has restarted from top
+    int error= 0;
+    do
+    {
+      error= table->file->rnd_next(record_buf);
+      switch (error)
+      {
+      case 0:
+      case HA_ERR_RECORD_DELETED:
+	break;
+
+      case HA_ERR_END_OF_FILE:
+	if (++restart_count < 2)
+	  table->file->ha_rnd_init(1);
+	break;
+
+      default:
+	table->file->print_error(error, MYF(0));
+	DBUG_RETURN(error);
+      }
+    }
+    while (restart_count < 2 &&
+           record_compare(table, table->record[0], record_buf) != 0);
+
+    DBUG_ASSERT(error == HA_ERR_END_OF_FILE || error == 0);
+    DBUG_RETURN(error);
+  }
+
+  DBUG_RETURN(0);
+}
+#endif
+
+/*
+  Constructor used to build an event for writing to the binary log.
+ */
+
+#ifndef MYSQL_CLIENT
+Delete_rows_log_event::Delete_rows_log_event(THD *thd_arg, TABLE *tbl_arg,
+                                             ulong tid, MY_BITMAP const *cols,
+                                             bool is_transactional)
+  : Rows_log_event(thd_arg, tbl_arg, tid, cols, is_transactional)
+#ifdef HAVE_REPLICATION
+  ,m_memory(NULL), m_key(NULL), m_search_record(NULL)
+#endif
+{
+}
+#endif /* #if !defined(MYSQL_CLIENT) */
+
+/*
+  Constructor used by slave to read the event from the binary log.
+ */
+#ifdef HAVE_REPLICATION
+Delete_rows_log_event::Delete_rows_log_event(const char *buf, uint event_len,
+                                             const Format_description_log_event
+                                             *description_event)
+#if defined(MYSQL_CLIENT)
+  : Rows_log_event(buf, event_len, DELETE_ROWS_EVENT, description_event)
+#else
+  : Rows_log_event(buf, event_len, DELETE_ROWS_EVENT, description_event),
+    m_memory(NULL), m_key(NULL), m_search_record(NULL)
+#endif
+{
+}
+#endif
+
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+int Delete_rows_log_event::do_before_row_operations(TABLE *table)
+{
+  DBUG_ASSERT(m_memory == NULL);
+
+  if ((table->file->table_flags() & HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS)
&&
+      table->s->primary_key < MAX_KEY)
+  {
+    /*
+      We don't need to allocate any memory for m_search_record and
+      m_key since they are not used.
+    */
+    return 0;
+  }
+
+  int error= 0;
+
+  if (table->s->keys > 0)
+  {
+    m_memory=
+      my_multi_malloc(MYF(MY_WME),
+		      &m_search_record, table->s->reclength,
+		      &m_key, table->key_info->key_length,
+		      NULL);
+  }
+  else
+  {
+    m_memory= m_search_record= my_malloc(table->s->reclength, MYF(MY_WME));
+    m_key= NULL;
+  }
+  if (!m_memory)
+    return HA_ERR_OUT_OF_MEM;
+
+  if (table->s->keys > 0)
+  {
+    /* We have a key: search the table using the index */
+    if (!table->file->inited)
+      error= table->file->ha_index_init(0);
+  }
+  else
+  {
+    /* We doesn't have a key: search the table using rnd_next() */
+    error= table->file->ha_rnd_init(1);
+  }
+
+  return error;
+}
+
+int Delete_rows_log_event::do_after_row_operations(TABLE *table, int error)
+{
+  /*error= ToDo:find out what this should really be, this triggers close_scan in nbd,
returning error?*/
+  table->file->ha_index_or_rnd_end();
+  my_free(m_memory, MYF(MY_ALLOW_ZERO_PTR)); // Free for multi_malloc
+  m_memory= m_search_record= m_key= NULL;
+
+  return error;
+}
+
+char const *Delete_rows_log_event::do_prepare_row(THD *thd, TABLE *table,
+                                                  char const *row_start)
+{
+  char const *ptr= row_start;
+  DBUG_ASSERT(ptr);
+  /*
+    This assertion actually checks that there is at least as many
+    columns on the slave as on the master.
+  */
+  DBUG_ASSERT(table->s->fields >= m_width);
+
+  DBUG_ASSERT(ptr != NULL);
+  ptr= unpack_row(thd, table, table->record[0], ptr, &m_cols);
+
+  /*
+    If we will access rows using the random access method, m_key will
+    be set to NULL, so we do not need to make a key copy in that case.
+   */
+  if (m_key)
+  {
+    /*
+      We have a key, let's use the primary key if there is one.
+      Otherwise, we use the first key (index) we find for the table.
+    */
+    uint const pk= table->s->primary_key;
+    KEY *const key_info= table->key_info;
+
+    key_copy(m_key, table->record[0], key_info, 0);
+  }
+
+  return ptr;
+}
+
+int Delete_rows_log_event::do_exec_row(TABLE *table, st_relay_log_info *rli)
+{
+  DBUG_ENTER("Delete_rows_log_event::do_exec_row(TABLE*,...)");
+  DBUG_PRINT("enter", ("table=%p (%s), rli=%p",
+		       table, table->s->table_name, rli));
+  DBUG_ASSERT(table != NULL);
+
+  int error= find_and_fetch_row(table, m_key, m_search_record);
+  if (error)
+  {
+    DBUG_PRINT("return", ("error: %d", error));
+    DBUG_RETURN(error);
+  }
+
+  /*
+     Now we should have the right row to delete.  We are using
+     record[0] since it is guaranteed to point to a record with the
+     correct value.
+  */
+  error= table->file->ha_delete_row(table->record[0]);
+
+  DBUG_RETURN(error);
+}
+
+#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
+
+#ifdef MYSQL_CLIENT
+void Delete_rows_log_event::print(FILE *file,
+                                  PRINT_EVENT_INFO* print_event_info)
+{
+  if (!print_event_info->short_form)
+  {
+    print_header(file, print_event_info);
+    fprintf(file, "\nDelete_rows: table id %lu\n", m_table_id); // TODO: WL#2321
+  }
+}
+#endif
+
+
+/**************************************************************************
+	Update_rows_log_event member functions
+**************************************************************************/
+
+/*
+  Constructor used to build an event for writing to the binary log.
+ */
+#if !defined(MYSQL_CLIENT)
+Update_rows_log_event::Update_rows_log_event(THD *thd_arg, TABLE *tbl_arg,
+                                             ulong tid, MY_BITMAP const *cols,
+                                             bool is_transactional)
+: Rows_log_event(thd_arg, tbl_arg, tid, cols, is_transactional)
+#ifdef HAVE_REPLICATION
+  , m_memory(NULL), m_key(NULL)
+#endif
+{
+}
+#endif /* !defined(MYSQL_CLIENT) */
+
+/*
+  Constructor used by slave to read the event from the binary log.
+ */
+#ifdef HAVE_REPLICATION
+Update_rows_log_event::Update_rows_log_event(const char *buf, uint event_len,
+                                             const
+                                             Format_description_log_event
+                                             *description_event)
+#if defined(MYSQL_CLIENT)
+  : Rows_log_event(buf, event_len, UPDATE_ROWS_EVENT, description_event)
+#else
+  : Rows_log_event(buf, event_len, UPDATE_ROWS_EVENT, description_event),
+    m_memory(NULL), m_key(NULL)
+#endif
+{
+}
+#endif
+
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+int Update_rows_log_event::do_before_row_operations(TABLE *table)
+{
+  DBUG_ASSERT(m_memory == NULL);
+
+  if ((table->file->table_flags() & HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS)
&&
+      table->s->primary_key < MAX_KEY)
+  {
+    /*
+      We don't need to allocate any memory for m_search_record and
+      m_key since they are not used.
+    */
+    return 0;
+  }
+
+  int error= 0;
+
+  if (table->s->keys > 0)
+  {
+    m_memory=
+      my_multi_malloc(MYF(MY_WME),
+		      &m_search_record, table->s->reclength,
+		      &m_key, table->key_info->key_length,
+		      NULL);
+  }
+  else
+  {
+    m_memory= m_search_record= my_malloc(table->s->reclength, MYF(MY_WME));
+    m_key= NULL;
+  }
+  if (!m_memory)
+    return HA_ERR_OUT_OF_MEM;
+
+  if (table->s->keys > 0)
+  {
+    /* We have a key: search the table using the index */
+    if (!table->file->inited)
+      error= table->file->ha_index_init(0);
+  }
+  else
+  {
+    /* We doesn't have a key: search the table using rnd_next() */
+    error= table->file->ha_rnd_init(1);
+  }
+  table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
+
+  return error;
+}
+
+int Update_rows_log_event::do_after_row_operations(TABLE *table, int error)
+{
+  /*error= ToDo:find out what this should really be, this triggers close_scan in nbd,
returning error?*/
+  table->file->ha_index_or_rnd_end();
+  my_free(m_memory, MYF(MY_ALLOW_ZERO_PTR));
+  m_memory= m_search_record= m_key= NULL;
+
+  return error;
+}
+
+char const *Update_rows_log_event::do_prepare_row(THD *thd, TABLE *table,
+                                                  char const *row_start)
+{
+  char const *ptr= row_start;
+  DBUG_ASSERT(ptr);
+  /*
+    This assertion actually checks that there is at least as many
+    columns on the slave as on the master.
+  */
+  DBUG_ASSERT(table->s->fields >= m_width);
+
+  /* record[0] is the before image for the update */
+  ptr= unpack_row(thd, table, table->record[0], ptr, &m_cols);
+  DBUG_ASSERT(ptr != NULL);
+  /* record[1] is the after image for the update */
+  ptr= unpack_row(thd, table, table->record[1], ptr, &m_cols);
+
+  /*
+    If we will access rows using the random access method, m_key will
+    be set to NULL, so we do not need to make a key copy in that case.
+   */
+  if (m_key)
+  {
+    /*
+      We have a key, let's use the primary key if there is one.
+      Otherwise, we use the first key (index) we find for the table.
+    */
+    uint const pk= table->s->primary_key;
+    KEY *const key_info= table->key_info;
+
+    key_copy(m_key, table->record[0], key_info, 0);
+  }
+
+  return ptr;
+}
+
+int Update_rows_log_event::do_exec_row(TABLE *table, st_relay_log_info *rli)
+{
+  DBUG_ENTER("Update_rows_log_event::do_exec_row(TABLE*,...)");
+  DBUG_PRINT("enter", ("table=%p (%s), rli=%p",
+		       table, table->s->table_name, rli));
+  DBUG_ASSERT(table != NULL);
+
+  int error= find_and_fetch_row(table, m_key, m_search_record);
+  if (error)
+  {
+    DBUG_PRINT("return", ("error=%d", error));
+    DBUG_RETURN(error);
+  }
+
+  /*
+    Now we should have the right row to update.  The record that has
+    been fetched is guaranteed to be in record[0], so we use that.
+  */
+  error= table->file->ha_update_row(table->record[0], table->record[1]);
+
+  DBUG_RETURN(error);
+}
+#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
+
+#ifdef MYSQL_CLIENT
+void Update_rows_log_event::print(FILE *file,
+				  PRINT_EVENT_INFO* print_event_info)
+{
+  if (!print_event_info->short_form)
+  {
+    print_header(file, print_event_info);
+    fprintf(file, "\nUpdate_rows: table id %lu\n", m_table_id); // TODO: WL#2321
+  }
+}
+#endif
+
+#endif /* defined(HAVE_ROW_BASED_REPLICATION) */

--- 1.344/sql/mysql_priv.h	2005-11-19 10:41:37 +01:00
+++ 1.345/sql/mysql_priv.h	2005-11-19 16:33:22 +01:00
@@ -242,50 +242,50 @@
    TODO: separate three contexts above, move them to separate bitfields.
 */
 
-#define SELECT_DISTINCT         (1L << 0)       // SELECT, user
-#define SELECT_STRAIGHT_JOIN    (1L << 1)       // SELECT, user
-#define SELECT_DESCRIBE         (1L << 2)       // SELECT, user
-#define SELECT_SMALL_RESULT     (1L << 3)       // SELECT, user
-#define SELECT_BIG_RESULT       (1L << 4)       // SELECT, user
-#define OPTION_FOUND_ROWS       (1L << 5)       // SELECT, user
-#define OPTION_TO_QUERY_CACHE   (1L << 6)       // SELECT, user
-#define SELECT_NO_JOIN_CACHE    (1L << 7)       // intern
-#define OPTION_BIG_TABLES       (1L << 8)       // THD, user
-#define OPTION_BIG_SELECTS      (1L << 9)       // THD, user
-#define OPTION_LOG_OFF          (1L << 10)      // THD, user
-#define OPTION_UPDATE_LOG       (1L << 11)      // THD, user, unused
-#define TMP_TABLE_ALL_COLUMNS   (1L << 12)      // SELECT, intern
-#define OPTION_WARNINGS         (1L << 13)      // THD, user
-#define OPTION_AUTO_IS_NULL     (1L << 14)      // THD, user, binlog
-#define OPTION_FOUND_COMMENT    (1L << 15)      // SELECT, intern, parser
-#define OPTION_SAFE_UPDATES     (1L << 16)      // THD, user
-#define OPTION_BUFFER_RESULT    (1L << 17)      // SELECT, user
-#define OPTION_BIN_LOG          (1L << 18)      // THD, user
-#define OPTION_NOT_AUTOCOMMIT   (1L << 19)      // THD, user
-#define OPTION_BEGIN            (1L << 20)      // THD, intern
-#define OPTION_TABLE_LOCK       (1L << 21)      // THD, intern
-#define OPTION_QUICK            (1L << 22)      // SELECT (for DELETE)
-#define OPTION_QUOTE_SHOW_CREATE (1L << 23)     // THD, user
+#define SELECT_DISTINCT         (LL(1) << 0)       // SELECT, user
+#define SELECT_STRAIGHT_JOIN    (LL(1) << 1)       // SELECT, user
+#define SELECT_DESCRIBE         (LL(1) << 2)       // SELECT, user
+#define SELECT_SMALL_RESULT     (LL(1) << 3)       // SELECT, user
+#define SELECT_BIG_RESULT       (LL(1) << 4)       // SELECT, user
+#define OPTION_FOUND_ROWS       (LL(1) << 5)       // SELECT, user
+#define OPTION_TO_QUERY_CACHE   (LL(1) << 6)       // SELECT, user
+#define SELECT_NO_JOIN_CACHE    (LL(1) << 7)       // intern
+#define OPTION_BIG_TABLES       (LL(1) << 8)       // THD, user
+#define OPTION_BIG_SELECTS      (LL(1) << 9)       // THD, user
+#define OPTION_LOG_OFF          (LL(1) << 10)      // THD, user
+#define OPTION_UPDATE_LOG       (LL(1) << 11)      // THD, user, unused
+#define TMP_TABLE_ALL_COLUMNS   (LL(1) << 12)      // SELECT, intern
+#define OPTION_WARNINGS         (LL(1) << 13)      // THD, user
+#define OPTION_AUTO_IS_NULL     (LL(1) << 14)      // THD, user, binlog
+#define OPTION_FOUND_COMMENT    (LL(1) << 15)      // SELECT, intern, parser
+#define OPTION_SAFE_UPDATES     (LL(1) << 16)      // THD, user
+#define OPTION_BUFFER_RESULT    (LL(1) << 17)      // SELECT, user
+#define OPTION_BIN_LOG          (LL(1) << 18)      // THD, user
+#define OPTION_NOT_AUTOCOMMIT   (LL(1) << 19)      // THD, user
+#define OPTION_BEGIN            (LL(1) << 20)      // THD, intern
+#define OPTION_TABLE_LOCK       (LL(1) << 21)      // THD, intern
+#define OPTION_QUICK            (LL(1) << 22)      // SELECT (for DELETE)
+#define OPTION_QUOTE_SHOW_CREATE (LL(1) << 23)     // THD, user
 
 /* Thr following is used to detect a conflict with DISTINCT
    in the user query has requested */
-#define SELECT_ALL              (1L << 24)      // SELECT, user, parser
+#define SELECT_ALL              (LL(1) << 24)      // SELECT, user, parser
 
 /* Set if we are updating a non-transaction safe table */
-#define OPTION_STATUS_NO_TRANS_UPDATE   (1L << 25) // THD, intern
+#define OPTION_STATUS_NO_TRANS_UPDATE   (LL(1) << 25) // THD, intern
 
 /* The following can be set when importing tables in a 'wrong order'
    to suppress foreign key checks */
-#define OPTION_NO_FOREIGN_KEY_CHECKS    (1L << 26) // THD, user, binlog
+#define OPTION_NO_FOREIGN_KEY_CHECKS    (LL(1) << 26) // THD, user, binlog
 /* The following speeds up inserts to InnoDB tables by suppressing unique
    key checks in some cases */
-#define OPTION_RELAXED_UNIQUE_CHECKS    (1L << 27) // THD, user, binlog
-#define SELECT_NO_UNLOCK                (1L << 28) // SELECT, intern
-#define OPTION_SCHEMA_TABLE             (1L << 29) // SELECT, intern
+#define OPTION_RELAXED_UNIQUE_CHECKS    (LL(1) << 27) // THD, user, binlog
+#define SELECT_NO_UNLOCK                (LL(1) << 28) // SELECT, intern
+#define OPTION_SCHEMA_TABLE             (LL(1) << 29) // SELECT, intern
 /* Flag set if setup_tables already done */
-#define OPTION_SETUP_TABLES_DONE        (1L << 30) // intern
+#define OPTION_SETUP_TABLES_DONE        (LL(1) << 30) // intern
 /* If not set then the thread will ignore all warnings with level notes. */
-#define OPTION_SQL_NOTES                (1UL << 31) // THD, user
+#define OPTION_SQL_NOTES                (LL(1) << 31) // THD, user
 /* 
   Force the used temporary table to be a MyISAM table (because we will use
   fulltext functions when reading from it.
@@ -1183,6 +1183,13 @@
 extern ulong query_buff_size, thread_stack;
 extern ulong binlog_cache_size, max_binlog_cache_size, open_files_limit;
 extern ulong max_binlog_size, max_relay_log_size;
+extern const char *opt_binlog_format;
+#ifdef HAVE_ROW_BASED_REPLICATION
+extern my_bool binlog_row_based;
+extern ulong opt_binlog_rows_event_max_size;
+#else
+extern const my_bool binlog_row_based;
+#endif
 extern ulong rpl_recovery_rank, thread_cache_size;
 extern ulong back_log;
 extern ulong specialflag, current_pid;
@@ -1319,6 +1326,7 @@
 #endif
 
 extern SHOW_COMP_OPTION have_isam;
+extern SHOW_COMP_OPTION have_row_based_replication;
 extern SHOW_COMP_OPTION have_raid, have_openssl, have_symlink;
 extern SHOW_COMP_OPTION have_query_cache;
 extern SHOW_COMP_OPTION have_geometry, have_rtree_keys;

--- 1.493/sql/mysqld.cc	2005-11-19 10:41:38 +01:00
+++ 1.494/sql/mysqld.cc	2005-11-19 16:33:22 +01:00
@@ -434,6 +434,33 @@
 my_bool opt_noacl;
 my_bool sp_automatic_privileges= 1;
 
+#ifdef HAVE_ROW_BASED_REPLICATION
+/*
+  This variable below serves as an optimization for (opt_binlog_format ==
+  BF_ROW) as we need to do this test for every row. Stmt-based is default.
+*/
+my_bool binlog_row_based= FALSE;
+ulong opt_binlog_rows_event_max_size;
+const char *binlog_format_names[]= {"STATEMENT", "ROW", NullS};
+/*
+  Note that BF_UNSPECIFIED is last, after the end of binlog_format_names: it
+  has no corresponding cell in this array. We use this value to be able to
+  know if the user has explicitely specified a binlog format (then we require
+  also --log-bin) or not (then we fall back to statement-based).
+*/
+enum binlog_format { BF_STMT= 0, BF_ROW= 1, BF_UNSPECIFIED= 2 };
+#else
+const my_bool binlog_row_based= FALSE;
+const char *binlog_format_names[]= {"STATEMENT", NullS};
+enum binlog_format { BF_STMT= 0, BF_UNSPECIFIED= 2 };
+#endif
+
+TYPELIB binlog_format_typelib=
+  { array_elements(binlog_format_names)-1,"",
+    binlog_format_names, NULL };
+const char *opt_binlog_format= 0;
+enum binlog_format opt_binlog_format_id= BF_UNSPECIFIED;
+
 #ifdef HAVE_INITGROUPS
 static bool calling_initgroups= FALSE; /* Used in SIGSEGV handler. */
 #endif
@@ -520,6 +547,7 @@
 CHARSET_INFO *system_charset_info, *files_charset_info ;
 CHARSET_INFO *national_charset_info, *table_alias_charset;
 
+SHOW_COMP_OPTION have_row_based_replication;
 SHOW_COMP_OPTION have_raid, have_openssl, have_symlink, have_query_cache;
 SHOW_COMP_OPTION have_geometry, have_rtree_keys;
 SHOW_COMP_OPTION have_crypt, have_compress;
@@ -3004,9 +3032,45 @@
   {
     sql_print_warning("You need to use --log-bin to make "
                       "--log-slave-updates work.");
-      unireg_abort(1);
+    unireg_abort(1);
   }
 
+  if (!opt_bin_log && (opt_binlog_format_id != BF_UNSPECIFIED))
+  {
+    sql_print_warning("You need to use --log-bin to make "
+                      "--binlog-format work.");
+    unireg_abort(1);
+  }
+  if (opt_binlog_format_id == BF_UNSPECIFIED)
+  {
+    /*
+      We use statement-based by default, but could change this to be row-based
+      if this is a cluster build (i.e. have_ndbcluster is true)...
+    */
+    opt_binlog_format_id= BF_STMT;
+  }
+#ifdef HAVE_ROW_BASED_REPLICATION
+  if (opt_binlog_format_id == BF_ROW) 
+  {
+    binlog_row_based= TRUE;
+    /*
+      Row-based binlogging turns on InnoDB unsafe locking, because the locks
+      are not needed when using row-based binlogging. In fact
+      innodb-locks-unsafe-for-binlog is unsafe only for stmt-based, it's
+      safe for row-based.
+    */
+#ifdef HAVE_INNOBASE_DB
+    innobase_locks_unsafe_for_binlog= TRUE;
+#endif
+    /* Trust stored function creators because they can do no harm */
+    trust_function_creators= 1;
+  }
+#endif
+  /* Check that we have not let the format to unspecified at this point */
+  DBUG_ASSERT((uint)opt_binlog_format_id <=
+              array_elements(binlog_format_names)-1);
+  opt_binlog_format= binlog_format_names[opt_binlog_format_id];
+
   if (opt_slow_log)
     mysql_slow_log.open_slow_log(opt_slow_logname);
 
@@ -4480,6 +4544,10 @@
   OPT_SQL_BIN_UPDATE_SAME,     OPT_REPLICATE_DO_DB,
   OPT_REPLICATE_IGNORE_DB,     OPT_LOG_SLAVE_UPDATES,
   OPT_BINLOG_DO_DB,            OPT_BINLOG_IGNORE_DB,
+  OPT_BINLOG_FORMAT,
+#ifdef HAVE_ROW_BASED_REPLICATION
+  OPT_BINLOG_ROWS_EVENT_MAX_SIZE, 
+#endif
   OPT_WANT_CORE,               OPT_CONCURRENT_INSERT,
   OPT_MEMLOCK,                 OPT_MYISAM_RECOVER,
   OPT_REPLICATE_REWRITE_DB,    OPT_SERVER_ID,
@@ -4700,12 +4768,38 @@
   {"bind-address", OPT_BIND_ADDRESS, "IP address to bind to.",
    (gptr*) &my_bind_addr_str, (gptr*) &my_bind_addr_str, 0, GET_STR,
    REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+  {"binlog-format", OPT_BINLOG_FORMAT,
+#ifdef HAVE_ROW_BASED_REPLICATION
+   "Tell the master the form of binary logging to use: either 'row' for "
+   "row-based binary logging (which automatically turns on "
+   "innodb_locks_unsafe_for_binlog as it is safe in this case), or "
+   "'statement' for statement-based logging. ",
+#else
+   "Tell the master the form of binary logging to use: this release build "
+   "supports only statement-based binary logging, so only 'statement' is "
+   "a legal value; MySQL-Max release builds support row-based binary logging "
+   "in addition.",
+#endif
+   0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },   
   {"binlog-do-db", OPT_BINLOG_DO_DB,
    "Tells the master it should log updates for the specified database, and exclude all
others not explicitly mentioned.",
    0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
   {"binlog-ignore-db", OPT_BINLOG_IGNORE_DB,
    "Tells the master that updates to the given database should not be logged tothe binary
log.",
    0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+#ifdef HAVE_ROW_BASED_REPLICATION
+  {"binlog-row-event-max-size", OPT_BINLOG_ROWS_EVENT_MAX_SIZE,
+   "The maximum size of a row-based binary log event in bytes. Rows will be "
+   "grouped into events smaller than this size if possible. "
+   "The value has to be a multiple of 256.",
+   (gptr*) &opt_binlog_rows_event_max_size, 
+   (gptr*) &opt_binlog_rows_event_max_size, 0, 
+   GET_ULONG, REQUIRED_ARG, 
+   /* def_value */ 1024, /* min_value */  256, /* max_value */ ULONG_MAX, 
+   /* sub_size */     0, /* block_size */ 256, 
+   /* app_type */ 0
+  },
+#endif
   {"bootstrap", OPT_BOOTSTRAP, "Used by mysql installation scripts.", 0, 0, 0,
    GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
   {"character-set-client-handshake", OPT_CHARACTER_SET_CLIENT_HANDSHAKE,
@@ -4873,7 +4967,9 @@
    (gptr*) &innobase_unix_file_flush_method, 0, GET_STR, REQUIRED_ARG, 0, 0, 0,
    0, 0, 0},
   {"innodb_locks_unsafe_for_binlog", OPT_INNODB_LOCKS_UNSAFE_FOR_BINLOG,
-   "Force InnoDB not to use next-key locking. Instead use only row-level locking",
+   "Force InnoDB not to use next-key locking, to use only row-level locking."
+   " This is unsafe if you are using statement-based binary logging, and safe"
+   " if you are using row-based binary logging.",
    (gptr*) &innobase_locks_unsafe_for_binlog,
    (gptr*) &innobase_locks_unsafe_for_binlog, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
   {"innodb_log_arch_dir", OPT_INNODB_LOG_ARCH_DIR,
@@ -4952,8 +5048,12 @@
   {"log-bin-trust-function-creators", OPT_LOG_BIN_TRUST_FUNCTION_CREATORS,
    "If equal to 0 (the default), then when --log-bin is used, creation of "
    "a function is allowed only to users having the SUPER privilege and only "
-   "if this function may not break binary logging.",
-   (gptr*) &trust_function_creators, (gptr*) &trust_function_creators, 0,
+   "if this function may not break binary logging."
+#ifdef HAVE_ROW_BASED_REPLICATION
+   " If using --binlog-format=row, the security issues do not exist and the "
+   "binary logging cannot break so this option is automatically set to 1."
+#endif
+   ,(gptr*) &trust_function_creators, (gptr*) &trust_function_creators, 0,
    GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
   {"log-error", OPT_ERROR_LOG_FILE, "Error log file.",
    (gptr*) &log_error_file_ptr, (gptr*) &log_error_file_ptr, 0, GET_STR,
@@ -6407,6 +6507,11 @@
 			     "d:t:i:o,/tmp/mysqld.trace");
 #endif
   opt_error_log= IF_WIN(1,0);
+#ifdef HAVE_ROW_BASED_REPLICATION
+  have_row_based_replication= SHOW_OPTION_YES;
+#else
+  have_row_based_replication= SHOW_OPTION_NO;
+#endif
 #ifdef WITH_NDBCLUSTER_STORAGE_ENGINE
   have_ndbcluster=SHOW_OPTION_DISABLED;
   global_system_variables.ndb_index_stat_enable=TRUE;
@@ -6630,6 +6735,28 @@
     binlog_filter->add_ignore_db(argument);
     break;
   }
+  case OPT_BINLOG_FORMAT:
+  {
+    int id;
+    if ((id= find_type(argument, &binlog_format_typelib, 2)) <= 0)
+    {
+#ifdef HAVE_ROW_BASED_REPLICATION
+      fprintf(stderr, 
+	      "Unknown binary log format: '%s' "
+	      "(should be '%s' or '%s')\n", 
+	      argument,
+              binlog_format_names[BF_STMT],
+              binlog_format_names[BF_ROW]);
+#else
+      fprintf(stderr, 
+	      "Unknown binary log format: '%s' (only legal value is '%s')\n", 
+	      argument, binlog_format_names[BF_STMT]);
+#endif
+      exit(1);
+    }
+    opt_binlog_format_id= (enum binlog_format)(id-1);
+    break;
+  }
   case (int)OPT_BINLOG_DO_DB:
   {
     binlog_filter->add_do_db(argument);
@@ -7169,6 +7296,7 @@
       init_global_datetime_format(MYSQL_TIMESTAMP_DATETIME,
 				  &global_system_variables.datetime_format))
     exit(1);
+
 }
 
 

--- 1.256/sql/slave.cc	2005-11-04 21:09:55 +01:00
+++ 1.257/sql/slave.cc	2005-11-19 16:33:23 +01:00
@@ -16,8 +16,6 @@
 
 #include "mysql_priv.h"
 
-#ifdef HAVE_REPLICATION
-
 #include <mysql.h>
 #include <myisam.h>
 #include "slave.h"
@@ -28,6 +26,10 @@
 #include <my_dir.h>
 #include <sql_common.h>
 
+#ifdef HAVE_REPLICATION
+
+#include "rpl_tblmap.h"
+
 #define MAX_SLAVE_RETRY_PAUSE 5
 bool use_slave_mask = 0;
 MY_BITMAP slave_error_mask;
@@ -806,6 +808,20 @@
 }
 
 
+Check if this is still needed
+static void free_string_array(DYNAMIC_ARRAY *a)
+{
+  uint i;
+  for (i = 0; i < a->elements; i++)
+    {
+      char* p;
+      get_dynamic(a, (gptr) &p, i);
+      my_free(p, MYF(MY_WME));
+    }
+  delete_dynamic(a);
+}
+
+
 #ifdef NOT_USED_YET
 static int end_slave_on_walk(MASTER_INFO* mi, gptr /*unused*/)
 {
@@ -919,7 +935,6 @@
   DBUG_RETURN(net_write_command(net, 251, fname, strlen(fname), "", 0));
 }
 
-
 /*
   From other comments and tests in code, it looks like
   sometimes Query_log_event and Load_log_event can have db == 0
@@ -932,7 +947,6 @@
   return (db ? db : "");
 }
 
-
 static int init_strvar_from_file(char *var, int max_size, IO_CACHE *f,
 				 const char *default_val)
 {
@@ -2244,12 +2258,11 @@
    ignore_log_space_limit(0), last_master_timestamp(0), slave_skip_counter(0),
    abort_pos_wait(0), slave_run_id(0), sql_thd(0), last_slave_errno(0),
    inited(0), abort_slave(0), slave_running(0), until_condition(UNTIL_NONE),
-   until_log_pos(0), retried_trans(0)
+   until_log_pos(0), retried_trans(0), m_reload_flags(RELOAD_NONE_F)
 {
   group_relay_log_name[0]= event_relay_log_name[0]=
     group_master_log_name[0]= 0;
   last_slave_error[0]= until_log_name[0]= ign_master_log_name_end[0]= 0;
-
   bzero((char*) &info_file, sizeof(info_file));
   bzero((char*) &cache_buf, sizeof(cache_buf));
   cached_charset_invalidate();
@@ -2948,7 +2961,7 @@
     thd->lex->current_select= 0;
     if (!ev->when)
       ev->when = time(NULL);
-    ev->thd = thd;
+    ev->thd = thd; // because up to this point, ev->thd == 0
     exec_res = ev->exec_event(rli);
     DBUG_ASSERT(rli->sql_thd==thd);
     /*
@@ -3518,6 +3531,17 @@
 		        RPL_LOG_NAME, llstr(rli->group_master_log_pos,llbuff));
 
  err:
+
+#ifdef HAVE_ROW_BASED_REPLICATION
+  /*
+    Some events set some playgrounds, which won't be cleared because thread
+    stops. Stopping of this thread may not be known to these events ("stop"
+    request is detected only by the present function, not by events), so we
+    must "proactively" clear playgrounds:
+  */
+  Table_map_log_event::cleanup(thd, rli);
+#endif
+
   VOID(pthread_mutex_lock(&LOCK_thread_count));
   /*
     Some extra safety, which should not been needed (normally, event deletion
@@ -4735,11 +4759,86 @@
   DBUG_VOID_RETURN;
 }
 
+/*
+  Some system tables needed to be re-read by the MySQL server after it has
+  updated them; in statement-based replication, the GRANT and other commands
+  are sent verbatim to the slave which then reloads; in row-based replication,
+  changes to these tables are done through ordinary Rows binlog events, so
+  master must add some flag for the slave to know it has to reload the tables.
+*/
+struct st_reload_entry
+{
+  char const *table;
+  st_relay_log_info::enum_reload_flag flag;
+};
+
+/*
+  Sorted array of table names, please keep it sorted since we are
+  using bsearch() on it below.
+ */
+static st_reload_entry s_mysql_tables[] = 
+{
+  { "columns_priv", st_relay_log_info::RELOAD_GRANT_F },
+  { "db",           st_relay_log_info::RELOAD_ACCESS_F },
+  { "host",         st_relay_log_info::RELOAD_ACCESS_F },
+  { "procs_priv",   st_relay_log_info::RELOAD_GRANT_F },
+  { "tables_priv",  st_relay_log_info::RELOAD_GRANT_F },
+  { "user",         st_relay_log_info::RELOAD_ACCESS_F }
+};
+
+static const my_size_t s_mysql_tables_size =
+  sizeof(s_mysql_tables)/sizeof(*s_mysql_tables);
+
+static int reload_entry_compare(const void *lhs, const void *rhs)
+{
+  const char *lstr = static_cast<const char *>(lhs);
+  const char *rstr = static_cast<const st_reload_entry*>(rhs)->table;
+  return strcmp(lstr, rstr);
+}
+
+void st_relay_log_info::touching_table(char const* db, char const* table,
+                                       ulong table_id) 
+{
+  if (strcmp(db,"mysql") == 0)
+  {
+#if defined(HAVE_BSEARCH) && defined(HAVE_SIZE_T)
+    void *const ptr= bsearch(table, s_mysql_tables,
+                             s_mysql_tables_size,
+                             sizeof(*s_mysql_tables), reload_entry_compare);
+    st_reload_entry const *const entry= static_cast<st_reload_entry*>(ptr);
+#else
+    /*
+      Fall back to full scan, there are few rows anyway and updating the
+      "mysql" database is rare.
+    */
+    st_reload_entry const *entry= s_mysql_tables;
+    for ( ; entry < s_mysql_tables + s_mysql_tables_size ; entry++)
+      if (reload_entry_compare(table, entry) == 0)
+        break;
+#endif
+    if (entry)
+      m_reload_flags|= entry->flag;
+  }
+}
+
+void st_relay_log_info::transaction_end(THD* thd) 
+{
+  if (m_reload_flags != RELOAD_NONE_F)
+  {
+    if (m_reload_flags & RELOAD_ACCESS_F)
+      acl_reload(thd);
+
+    if (m_reload_flags & RELOAD_GRANT_F)
+      grant_reload(thd);
+
+    m_reload_flags= RELOAD_NONE_F;
+  }
+}
 
 #ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
 template class I_List_iterator<i_string>;
 template class I_List_iterator<i_string_pair>;
 #endif
 
-
 #endif /* HAVE_REPLICATION */
+

--- 1.160/sql/sql_acl.cc	2005-11-19 15:56:50 +01:00
+++ 1.161/sql/sql_acl.cc	2005-11-19 16:14:06 +01:00
@@ -1467,8 +1467,7 @@
                   acl_user->host.hostname ? acl_user->host.hostname : "",
                   new_password));
     thd->clear_error();
-    Query_log_event qinfo(thd, buff, query_length, 0, FALSE);
-    mysql_bin_log.write(&qinfo);
+    thd->binlog_query(THD::ROW_QUERY_TYPE, buff, query_length, FALSE, FALSE);
   }
 end:
   close_thread_tables(thd);
@@ -1653,7 +1652,7 @@
   }
   store_record(table,record[1]);
   table->field[2]->store(new_password, new_password_len, system_charset_info);
-  if ((error=table->file->update_row(table->record[1],table->record[0])))
+  if ((error=table->file->ha_update_row(table->record[1],table->record[0])))
   {
     table->file->print_error(error,MYF(0));	/* purecov: deadcode */
     DBUG_RETURN(1);
@@ -1867,14 +1866,14 @@
     */
     table->file->ha_retrieve_all_cols();
     if (cmp_record(table,record[1]) &&
-	(error=table->file->update_row(table->record[1],table->record[0])))
+	(error=table->file->ha_update_row(table->record[1],table->record[0])))
     {						// This should never happen
       table->file->print_error(error,MYF(0));	/* purecov: deadcode */
       error= -1;				/* purecov: deadcode */
       goto end;					/* purecov: deadcode */
     }
   }
-  else if ((error=table->file->write_row(table->record[0]))) // insert
+  else if ((error=table->file->ha_write_row(table->record[0]))) // insert
   {						// This should never happen
     if (error && error != HA_ERR_FOUND_DUPP_KEY &&
 	error != HA_ERR_FOUND_DUPP_UNIQUE)	/* purecov: inspected */
@@ -1984,16 +1983,17 @@
     if (rights)
     {
       table->file->ha_retrieve_all_cols();
-      if ((error=table->file->update_row(table->record[1],table->record[0])))
+      if ((error=table->file->ha_update_row(table->record[1],
+                                            table->record[0])))
 	goto table_error;			/* purecov: deadcode */
     }
     else	/* must have been a revoke of all privileges */
     {
-      if ((error = table->file->delete_row(table->record[1])))
+      if ((error = table->file->ha_delete_row(table->record[1])))
 	goto table_error;			/* purecov: deadcode */
     }
   }
-  else if (rights && (error=table->file->write_row(table->record[0])))
+  else if (rights &&
(error=table->file->ha_write_row(table->record[0])))
   {
     if (error && error != HA_ERR_FOUND_DUPP_KEY) /* purecov: inspected */
       goto table_error; /* purecov: deadcode */
@@ -2361,9 +2361,9 @@
     {
       GRANT_COLUMN *grant_column;
       if (privileges)
-	error=table->file->update_row(table->record[1],table->record[0]);
+	error=table->file->ha_update_row(table->record[1],table->record[0]);
       else
-	error=table->file->delete_row(table->record[1]);
+	error=table->file->ha_delete_row(table->record[1]);
       if (error)
       {
 	table->file->print_error(error,MYF(0)); /* purecov: inspected */
@@ -2378,7 +2378,7 @@
     else					// new grant
     {
       GRANT_COLUMN *grant_column;
-      if ((error=table->file->write_row(table->record[0])))
+      if ((error=table->file->ha_write_row(table->record[0])))
       {
 	table->file->print_error(error,MYF(0)); /* purecov: inspected */
 	result= -1;				/* purecov: inspected */
@@ -2430,8 +2430,8 @@
 	if (privileges)
 	{
 	  int tmp_error;
-	  if ((tmp_error=table->file->update_row(table->record[1],
-						 table->record[0])))
+	  if ((tmp_error=table->file->ha_update_row(table->record[1],
+						    table->record[0])))
 	  {					/* purecov: deadcode */
 	    table->file->print_error(tmp_error,MYF(0)); /* purecov: deadcode */
 	    result= -1;				/* purecov: deadcode */
@@ -2443,7 +2443,7 @@
 	else
 	{
 	  int tmp_error;
-	  if ((tmp_error = table->file->delete_row(table->record[1])))
+	  if ((tmp_error = table->file->ha_delete_row(table->record[1])))
 	  {					/* purecov: deadcode */
 	    table->file->print_error(tmp_error,MYF(0)); /* purecov: deadcode */
 	    result= -1;				/* purecov: deadcode */
@@ -2551,15 +2551,15 @@
   {
     if (store_table_rights || store_col_rights)
     {
-      if ((error=table->file->update_row(table->record[1],table->record[0])))
+      if
((error=table->file->ha_update_row(table->record[1],table->record[0])))
 	goto table_error;			/* purecov: deadcode */
     }
-    else if ((error = table->file->delete_row(table->record[1])))
+    else if ((error = table->file->ha_delete_row(table->record[1])))
       goto table_error;				/* purecov: deadcode */
   }
   else
   {
-    error=table->file->write_row(table->record[0]);
+    error=table->file->ha_write_row(table->record[0]);
     if (error && error != HA_ERR_FOUND_DUPP_KEY)
       goto table_error;				/* purecov: deadcode */
   }
@@ -2668,15 +2668,15 @@
   {
     if (store_proc_rights)
     {
-      if ((error=table->file->update_row(table->record[1],table->record[0])))
+      if
((error=table->file->ha_update_row(table->record[1],table->record[0])))
 	goto table_error;
     }
-    else if ((error= table->file->delete_row(table->record[1])))
+    else if ((error= table->file->ha_delete_row(table->record[1])))
       goto table_error;
   }
   else
   {
-    error=table->file->write_row(table->record[0]);
+    error=table->file->ha_write_row(table->record[0]);
     if (error && error != HA_ERR_FOUND_DUPP_KEY)
       goto table_error;
   }
@@ -4601,13 +4601,13 @@
                       system_charset_info);
     user_field->store(user_to->user.str, user_to->user.length,
                       system_charset_info);
-    if ((error= table->file->update_row(table->record[1], table->record[0])))
+    if ((error= table->file->ha_update_row(table->record[1],
table->record[0])))
       table->file->print_error(error, MYF(0));
   }
   else
   {
     /* delete */
-    if ((error=table->file->delete_row(table->record[0])))
+    if ((error=table->file->ha_delete_row(table->record[0])))
       table->file->print_error(error, MYF(0));
   }
 
@@ -5614,7 +5614,7 @@
     table->field[i++]->store(column, col_length, cs);
   table->field[i++]->store(priv, priv_length, cs);
   table->field[i]->store(is_grantable, strlen(is_grantable), cs);
-  table->file->write_row(table->record[0]);
+  table->file->ha_write_row(table->record[0]);
 }
 
 

--- 1.278/sql/sql_base.cc	2005-11-19 10:41:38 +01:00
+++ 1.279/sql/sql_base.cc	2005-11-19 16:14:06 +01:00
@@ -637,7 +637,8 @@
     next=table->next;
     close_temporary(table, 1);
   }
-  if (query && found_user_tables && mysql_bin_log.is_open())
+  if (query && found_user_tables && mysql_bin_log.is_open() &&
+      !binlog_row_based) // CREATE TEMP TABLE not binlogged if row-based
   {
     /* The -1 is to remove last ',' */
     thd->clear_error();
@@ -1869,10 +1870,11 @@
       uint query_buf_size= 20 + 2*NAME_LEN + 1;
       if ((query= (char*)my_malloc(query_buf_size,MYF(MY_WME))))
       {
+        /* this DELETE FROM is needed even with row-based binlogging */
         end = strxmov(strmov(query, "DELETE FROM `"),
                       db,"`.`",name,"`", NullS);
-        Query_log_event qinfo(thd, query, (ulong)(end-query), 0, FALSE);
-        mysql_bin_log.write(&qinfo);
+        thd->binlog_query(THD::STMT_QUERY_TYPE,
+                          query, (ulong)(end-query), FALSE, FALSE);
         my_free(query, MYF(0));
       }
       else

--- 1.220/sql/sql_class.cc	2005-11-15 17:03:07 +01:00
+++ 1.221/sql/sql_class.cc	2005-11-19 16:14:06 +01:00
@@ -292,7 +292,7 @@
 					       variables.date_format);
   variables.datetime_format= date_time_format_copy((THD*) 0,
 						   variables.datetime_format);
-#ifdef HAVE_NDBCLUSTER_DB
+#ifdef WITH_NDBCLUSTER_STORAGE_ENGINE
   variables.ndb_use_transactions= 1;
 #endif
   pthread_mutex_unlock(&LOCK_global_system_variables);
@@ -381,7 +381,7 @@
     close_thread_tables(this);
   }
   mysql_ha_flush(this, (TABLE_LIST*) 0,
-                 MYSQL_HA_CLOSE_FINAL | MYSQL_HA_FLUSH_ALL);
+                 MYSQL_HA_CLOSE_FINAL | MYSQL_HA_FLUSH_ALL, FALSE);
   hash_free(&handler_tables_hash);
   delete_dynamic(&user_var_events);
   hash_free(&user_vars);
@@ -907,7 +907,7 @@
     return 0;
   }
 
-#ifdef HAVE_INNOBASE_DB
+#ifdef WITH_INNOBASE_STORAGE_ENGINE
   /*
     We may be passing the control from mysqld to the client: release the
     InnoDB adaptive hash S-latch to avoid thread deadlocks if it was reserved
@@ -943,7 +943,7 @@
 
 bool select_send::send_eof()
 {
-#ifdef HAVE_INNOBASE_DB
+#ifdef WITH_INNOBASE_STORAGE_ENGINE
   /* We may be passing the control from mysqld to the client: release the
      InnoDB adaptive hash S-latch to avoid thread deadlocks if it was reserved
      by thd */

--- 1.268/sql/sql_class.h	2005-11-18 16:06:33 +01:00
+++ 1.269/sql/sql_class.h	2005-11-19 16:14:06 +01:00
@@ -23,12 +23,15 @@
 
 // TODO: create log.h and move all the log header stuff there
 
+#include "rpl_tblmap.h"
+
 class Query_log_event;
 class Load_log_event;
 class Slave_log_event;
 class Format_description_log_event;
 class sp_rcontext;
 class sp_cache;
+class Rows_log_event;
 
 enum enum_enable_or_disable { LEAVE_AS_IS, ENABLE, DISABLE };
 enum enum_ha_read_modes { RFIRST, RNEXT, RPREV, RLAST, RKEY, RNEXT_SAME };
@@ -231,7 +234,11 @@
   bool no_auto_events;
   friend class Log_event;
 
+  pthread_mutex_t LOCK_next_table_id;
+  ulong m_next_table_id;
 public:
+  ulonglong m_table_map_version;
+
   /*
     These describe the log's format. This is used only for relay logs.
     _for_exec is used by the SQL thread, _for_queue by the I/O thread. It's
@@ -255,6 +262,14 @@
   int log(THD *thd, my_xid xid);
   void unlog(ulong cookie, my_xid xid);
   int recover(IO_CACHE *log, Format_description_log_event *fdle);
+#if !defined(MYSQL_CLIENT)
+  /*
+    This will return a table id for the table. If the table is not known, a
+    new table id will be invented and returned. 
+  */
+  ulong get_table_id(TABLE* table);
+  void update_table_map_version();
+#endif /* !defined(MYSQL_CLIENT) */
   void reset_bytes_written()
   {
     bytes_written = 0;
@@ -1113,6 +1128,13 @@
            public Open_tables_state
 {
 public:
+  enum enum_error {
+    ALL_OK,
+    TABLE_MAP_WRITE_FAILURE,
+    BINLOG_WRITE_FAILURE,
+    COUNT
+  };
+
   /*
     Constant for THD::where initialization in the beginning of every query.
 
@@ -1217,12 +1239,142 @@
 
   /* container for handler's private per-connection data */
   void *ha_data[MAX_HA];
+
+#ifdef HAVE_ROW_BASED_REPLICATION
+/*
+  When mysqlbinlog is made to understand Rows events, we may have
+  to include sql_class.h in client/ so Mats already put the ifndef below.
+*/
+#if !defined(MYSQL_CLIENT)
+
+  /*
+    Public interface to write rows to the binlog
+  */
+  int binlog_write_row(TABLE* table, bool is_transactional,
+                       MY_BITMAP const* cols, my_size_t colcnt,
+                       const byte *buf);
+  int binlog_delete_row(TABLE* table, bool is_transactional,
+                        MY_BITMAP const* cols, my_size_t colcnt,
+                        const byte *buf);
+  int binlog_update_row(TABLE* table, bool is_transactional,
+                        MY_BITMAP const* cols, my_size_t colcnt,
+                        const byte *old_data, const byte *new_data);
+
+  void set_server_id(uint32 sid) { server_id = sid; }
+
+  /*
+    Member functions to handle pending event for row-level logging.
+  */
+  template <class RowsEventT> Rows_log_event*
+    binlog_prepare_pending_rows_event(TABLE* table, uint32 serv_id,
+                                      MY_BITMAP const* cols,
+                                      my_size_t colcnt,
+                                      my_size_t needed,
+                                      bool is_transactional);
+  Rows_log_event* binlog_get_pending_rows_event() const
+  { return transaction.m_pending_rows_event; }
+  bool            binlog_set_pending_rows_event(Rows_log_event* ev)
+  { transaction.m_pending_rows_event= ev; return 0; }
+  int             binlog_flush_and_set_pending_rows_event(Rows_log_event*);
+
+  my_size_t max_row_length_blob(TABLE* table, const byte *data) const;
+  my_size_t max_row_length(TABLE* table, const byte *data) const
+  {
+    TABLE_SHARE *table_s= table->s;
+    my_size_t length= table_s->reclength + 2 * table_s->fields;
+    if (table_s->blob_fields == 0)
+      return length;
+
+    return (length+max_row_length_blob(table,data));
+  }
+
+  my_size_t pack_row(TABLE* table, MY_BITMAP const* cols, byte *row_data,
+                     my_size_t max_len, const byte *data) const;
+
+  int binlog_flush_pending_rows_event(bool transaction_end);
+  void binlog_delete_pending_rows_event();
+
+  ulong get_new_table_id(TABLE* table);
+  ulong get_table_id(TABLE* table)
+  {
+    ulong id= table->s->table_map_id;
+    if (id != table_mapping::NO_TABLE)
+      return id;
+    return get_new_table_id(table);
+  }
+
+  bool is_table_mapped(TABLE* table) const;
+
+  /*
+    Write a table map to the binary log 'mysql_bin_log'.
+
+    PARAMETERS
+      table     The table to write a table map for
+      is_trans  'true' if the handler is transactional,
+                'false' otherwise
+
+    RETURN VALUE
+      Error code, or zero if no error occured.
+  */
+  int binlog_write_table_map(TABLE* table, bool is_trans);
+
+#endif
+#endif /* HAVE_ROW_BASED_REPLICATION */
+#ifndef MYSQL_CLIENT
+  enum enum_binlog_query_type {
+      /* The query can be logged row-based or statement-based */
+      ROW_QUERY_TYPE,
+      /* The query has to be logged statement-based */
+      STMT_QUERY_TYPE
+  };
+
+  int binlog_query(enum_binlog_query_type qtype,
+                   char const *query, ulong query_len,
+                   bool is_trans, bool suppress_use);
+#endif
+
+public:
+
+  /*
+    This function will be called from ha_commit_trans() to prepare for a
+    commit. The thread should take all necessary actions to prepare itself for
+    commit; presently it's writing the last buffered row-based binlog event to
+    the binlog.
+  */
+  int prepare_for_commit() {
+#if !defined(MYSQL_CLIENT) && defined(HAVE_ROW_BASED_REPLICATION)
+    return binlog_flush_pending_rows_event(true);
+#else
+    return 0;
+#endif
+  }
+
+  /*
+    This function will be called from ha_rollback_trans() to prepare for a
+    rollback. The thread should take all necessary actions to prepare itself
+    for rollback; presently it's discarding the last buffered row-based binlog
+    event (as the changes it contains are going to be discarded in the storage
+    engines).
+  */
+  int prepare_for_rollback() {
+#if !defined(MYSQL_CLIENT) && defined(HAVE_ROW_BASED_REPLICATION)
+    binlog_delete_pending_rows_event();
+#endif
+    return 0;
+  }
+
   struct st_transactions {
     SAVEPOINT *savepoints;
     THD_TRANS all;			// Trans since BEGIN WORK
     THD_TRANS stmt;			// Trans for current statement
     bool on;                            // see ha_enable_transaction()
+    XID  xid;                           // transaction identifier
+    enum xa_states xa_state;            // used by external XA only
     XID_STATE xid_state;
+#ifdef HAVE_ROW_BASED_REPLICATION
+    Rows_log_event *m_pending_rows_event;
+#endif
+
     /*
        Tables changed in transaction (that must be invalidated in query cache).
        List contain only transactional tables, that not invalidated in query

--- 1.158/sql/sql_delete.cc	2005-11-04 21:09:56 +01:00
+++ 1.159/sql/sql_delete.cc	2005-11-19 16:33:23 +01:00
@@ -40,6 +40,7 @@
   ha_rows	deleted;
   uint usable_index= MAX_KEY;
   SELECT_LEX   *select_lex= &thd->lex->select_lex;
+  bool          ha_delete_row_bypassed= 0;
   DBUG_ENTER("mysql_delete");
 
   if (open_and_lock_tables(thd, table_list))
@@ -77,15 +78,18 @@
       !(specialflag & (SPECIAL_NO_NEW_FUNC | SPECIAL_SAFE_MODE)) &&
       !(table->triggers && table->triggers->has_delete_triggers()))
   {
-    deleted= table->file->records;
+    ha_rows const maybe_deleted= table->file->records;
     if (!(error=table->file->delete_all_rows()))
     {
       error= -1;				// ok
+      deleted= maybe_deleted;
+      ha_delete_row_bypassed= 1;
       goto cleanup;
     }
     if (error != HA_ERR_WRONG_COMMAND)
     {
       table->file->print_error(error,MYF(0));
+      ha_delete_row_bypassed= 1;
       error=0;
       goto cleanup;
     }
@@ -211,7 +215,7 @@
         break;
       }
 
-      if (!(error= table->file->delete_row(table->record[0])))
+      if (!(error= table->file->ha_delete_row(table->record[0])))
       {
 	deleted++;
         if (table->triggers &&
@@ -293,10 +297,24 @@
     {
       if (error < 0)
         thd->clear_error();
-      Query_log_event qinfo(thd, thd->query, thd->query_length,
-			    transactional_table, FALSE);
-      if (mysql_bin_log.write(&qinfo) && transactional_table)
+
+      /*
+        If 'handler::delete_all_rows()' was called, we replicate
+        statement-based; otherwise, 'ha_delete_row()' was used to
+        delete specific rows which we might log row-based.
+      */
+      THD::enum_binlog_query_type const
+          query_type(ha_delete_row_bypassed ?
+                     THD::STMT_QUERY_TYPE :
+                     THD::ROW_QUERY_TYPE);
+      int log_result= thd->binlog_query(query_type,
+                                        thd->query, thd->query_length,
+                                        transactional_table, FALSE);
+
+      if (log_result && transactional_table)
+      {
 	error=1;
+      }
     }
     if (!transactional_table)
       thd->options|=OPTION_STATUS_NO_TRANS_UPDATE;
@@ -592,7 +610,7 @@
                                             TRG_ACTION_BEFORE, FALSE))
 	DBUG_RETURN(1);
       table->status|= STATUS_DELETED;
-      if (!(error=table->file->delete_row(table->record[0])))
+      if (!(error=table->file->ha_delete_row(table->record[0])))
       {
 	deleted++;
         if (table->triggers &&
@@ -705,7 +723,7 @@
         local_error= 1;
         break;
       }
-      if ((local_error=table->file->delete_row(table->record[0])))
+      if ((local_error=table->file->ha_delete_row(table->record[0])))
       {
 	table->file->print_error(local_error,MYF(0));
 	break;
@@ -772,10 +790,13 @@
     {
       if (local_error == 0)
         thd->clear_error();
-      Query_log_event qinfo(thd, thd->query, thd->query_length,
-			    transactional_tables, FALSE);
-      if (mysql_bin_log.write(&qinfo) && !normal_tables)
+      if (thd->binlog_query(THD::ROW_QUERY_TYPE,
+                            thd->query, thd->query_length,
+                            transactional_tables, FALSE) &&
+          !normal_tables)
+      {
 	local_error=1;  // Log write failed: roll back the SQL statement
+      }
     }
     if (!transactional_tables)
       thd->options|=OPTION_STATUS_NO_TRANS_UPDATE;
@@ -876,10 +897,13 @@
     {
       if (mysql_bin_log.is_open())
       {
+        /*
+          TRUNCATE must always be statement-based binlogged (not row-based) so
+          we don't test binlog_row_based.
+        */
         thd->clear_error();
-	Query_log_event qinfo(thd, thd->query, thd->query_length,
-			      0, FALSE);
-	mysql_bin_log.write(&qinfo);
+        thd->binlog_query(THD::STMT_QUERY_TYPE,
+                          thd->query, thd->query_length, FALSE, FALSE);
       }
       send_ok(thd);		// This should return record count
     }

--- 1.173/sql/sql_insert.cc	2005-11-04 21:09:56 +01:00
+++ 1.174/sql/sql_insert.cc	2005-11-19 16:14:06 +01:00
@@ -604,10 +604,13 @@
       {
         if (error <= 0)
           thd->clear_error();
-	Query_log_event qinfo(thd, thd->query, thd->query_length,
-			      transactional_table, FALSE);
-	if (mysql_bin_log.write(&qinfo) && transactional_table)
-	  error=1;
+	if (thd->binlog_query(THD::ROW_QUERY_TYPE,
+                              thd->query, thd->query_length,
+                              transactional_table, FALSE) &&
+            transactional_table)
+        {
+          error=1;
+        }
       }
       if (!transactional_table)
 	thd->options|=OPTION_STATUS_NO_TRANS_UPDATE;
@@ -991,10 +994,11 @@
   DBUG_ENTER("write_record");
 
   info->records++;
+
   if (info->handle_duplicates == DUP_REPLACE ||
       info->handle_duplicates == DUP_UPDATE)
   {
-    while ((error=table->file->write_row(table->record[0])))
+    while ((error=table->file->ha_write_row(table->record[0])))
     {
       uint key_nr;
       if (error != HA_WRITE_SKIP)
@@ -1078,7 +1082,7 @@
           thd->clear_next_insert_id= 0;
           thd->next_insert_id= 0;
         }
-        if
((error=table->file->update_row(table->record[1],table->record[0])))
+        if
((error=table->file->ha_update_row(table->record[1],table->record[0])))
 	{
 	  if ((error == HA_ERR_FOUND_DUPP_KEY) && info->ignore)
             goto ok_or_after_trg_err;
@@ -1117,8 +1121,8 @@
             thd->clear_next_insert_id= 0;
             thd->next_insert_id= 0;
           }
-          if ((error=table->file->update_row(table->record[1],
-					     table->record[0])))
+          if ((error=table->file->ha_update_row(table->record[1],
+					        table->record[0])))
             goto err;
           info->deleted++;
           trg_error= (table->triggers &&
@@ -1135,7 +1139,7 @@
               table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
                                                 TRG_ACTION_BEFORE, TRUE))
             goto before_trg_err;
-          if ((error=table->file->delete_row(table->record[1])))
+          if ((error=table->file->ha_delete_row(table->record[1])))
             goto err;
           info->deleted++;
           if (!table->file->has_transactions())
@@ -1156,7 +1160,7 @@
                 table->triggers->process_triggers(thd, TRG_EVENT_INSERT,
                                                   TRG_ACTION_AFTER, TRUE));
   }
-  else if ((error=table->file->write_row(table->record[0])))
+  else if ((error=table->file->ha_write_row(table->record[0])))
   {
     if (!info->ignore ||
 	(error != HA_ERR_FOUND_DUPP_KEY && error != HA_ERR_FOUND_DUPP_UNIQUE))
@@ -1242,16 +1246,15 @@
 
 class delayed_row :public ilink {
 public:
-  char *record,*query;
+  char *record;
   enum_duplicates dup;
   time_t start_time;
   bool query_start_used,last_insert_id_used,insert_id_used, ignore, log_query;
   ulonglong last_insert_id;
   timestamp_auto_set_type timestamp_field_type;
-  uint query_length;
 
   delayed_row(enum_duplicates dup_arg, bool ignore_arg, bool log_query_arg)
-    :record(0), query(0), dup(dup_arg), ignore(ignore_arg), log_query(log_query_arg) {}
+    :record(0), dup(dup_arg), ignore(ignore_arg), log_query(log_query_arg) {}
   ~delayed_row()
   {
     x_free(record);
@@ -1261,6 +1264,9 @@
 
 class delayed_insert :public ilink {
   uint locks_in_memory;
+  char *query;
+  ulong query_length;
+  ulong query_allocated;
 public:
   THD thd;
   TABLE *table;
@@ -1274,7 +1280,7 @@
   TABLE_LIST table_list;			// Argument
 
   delayed_insert()
-    :locks_in_memory(0),
+    :locks_in_memory(0), query(0), query_length(0), query_allocated(0),
      table(0),tables_in_use(0),stacked_inserts(0), status(0), dead(0),
      group_count(0)
   {
@@ -1300,6 +1306,7 @@
   }
   ~delayed_insert()
   {
+    my_free(query, MYF(MY_WME|MY_ALLOW_ZERO_PTR));
     /* The following is not really needed, but just for safety */
     delayed_row *row;
     while ((row=rows.get()))
@@ -1319,6 +1326,25 @@
     VOID(pthread_cond_broadcast(&COND_thread_count)); /* Tell main we are ready */
   }
 
+  int set_query(char const *q, ulong qlen) {
+    if (q && qlen > 0)
+    {
+      if (query_allocated < qlen + 1)
+      {
+        ulong const flags(MY_WME|MY_FREE_ON_ERROR|MY_ALLOW_ZERO_PTR);
+        query= my_realloc(query, qlen + 1, MYF(flags));
+        if (query == 0)
+          return HA_ERR_OUT_OF_MEM;
+        query_allocated= qlen;
+      }
+      query_length= qlen;
+      memcpy(query, q, qlen + 1);
+    }
+    else
+      query_length= 0;
+    return 0;
+  }
+
   /* The following is for checking when we can delete ourselves */
   inline void lock()
   {
@@ -1611,18 +1637,22 @@
   if (thd->killed || !(row= new delayed_row(duplic, ignore, log_on)))
     goto err;
 
+#if 0
   if (!query)
     query_length=0;
-  if (!(row->record= (char*) my_malloc(table->s->reclength+query_length+1,
-				       MYF(MY_WME))))
+#endif
+  if (!(row->record= (char*) my_malloc(table->s->reclength, MYF(MY_WME))))
     goto err;
   memcpy(row->record, table->record[0], table->s->reclength);
+  di->set_query(query, query_length);
+#if 0
   if (query_length)
   {
     row->query= row->record+table->s->reclength;
     memcpy(row->query,query,query_length+1);
   }
   row->query_length=		query_length;
+#endif
   row->start_time=		thd->start_time;
   row->query_start_used=	thd->query_start_used;
   row->last_insert_id_used=	thd->last_insert_id_used;
@@ -1945,7 +1975,21 @@
 {
   int error;
   ulong max_rows;
-  bool using_ignore=0, using_bin_log=mysql_bin_log.is_open();
+  bool using_ignore=0,
+    using_bin_log= mysql_bin_log.is_open();
+
+#if 0
+  /*
+    The actual text for the query is added to the first row in the
+    list.  Since the row is destroyed, with all it's memory, we need
+    to take a copy of it to be able to log it after all rows have been
+    applied.
+  */
+  uint const query_length= rows.head()->query_length;
+  char *const query= static_cast<char*>(my_alloca(query_length+1));
+  memcpy(query, rows.head()->query, query_length);
+#endif
+
   delayed_row *row;
   DBUG_ENTER("handle_inserts");
 
@@ -2011,11 +2055,6 @@
       using_ignore=0;
       table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
     }
-    if (row->query && row->log_query && using_bin_log)
-    {
-      Query_log_event qinfo(&thd, row->query, row->query_length, 0, FALSE);
-      mysql_bin_log.write(&qinfo);
-    }
     if (table->s->blob_fields)
       free_delayed_insert_blobs(table);
     thread_safe_sub(delayed_rows_in_use,1,&LOCK_delayed_status);
@@ -2030,8 +2069,7 @@
       on this table until all entries has been processed
     */
     if (group_count++ >= max_rows && (row= rows.head()) &&
-	(!(row->log_query & using_bin_log) ||
-	 row->query))
+	(!(row->log_query & using_bin_log)))
     {
       group_count=0;
       if (stacked_inserts || tables_in_use)	// Let these wait a while
@@ -2066,6 +2104,10 @@
   thd.proc_info=0;
   table->next_number_field=0;
   pthread_mutex_unlock(&mutex);
+
+  /* After releasing the mutex, to prevent deadlocks. */
+  thd.binlog_query(THD::ROW_QUERY_TYPE, query, query_length, FALSE, FALSE);
+
   if ((error=table->file->extra(HA_EXTRA_NO_CACHE)))
   {						// This shouldn't happen
     table->file->print_error(error,MYF(0));
@@ -2415,9 +2457,8 @@
       thd->insert_id(last_insert_id);		// For binary log
     if (mysql_bin_log.is_open())
     {
-      Query_log_event qinfo(thd, thd->query, thd->query_length,
-                            table->file->has_transactions(), FALSE);
-      mysql_bin_log.write(&qinfo);
+      thd->binlog_query(THD::ROW_QUERY_TYPE, thd->query, thd->query_length,
+                        table->file->has_transactions(), FALSE);
     }
     if (!table->s->tmp_table)
       thd->options|=OPTION_STATUS_NO_TRANS_UPDATE;
@@ -2458,9 +2499,9 @@
   {
     if (!error)
       thd->clear_error();
-    Query_log_event qinfo(thd, thd->query, thd->query_length,
-			  table->file->has_transactions(), FALSE);
-    mysql_bin_log.write(&qinfo);
+    thd->binlog_query(THD::ROW_QUERY_TYPE,
+                      thd->query, thd->query_length,
+                      table->file->has_transactions(), FALSE);
   }
   if ((error2=ha_autocommit_or_rollback(thd,error)) && ! error)
     error=error2;

--- 1.85/sql/sql_load.cc	2005-11-04 21:09:56 +01:00
+++ 1.86/sql/sql_load.cc	2005-11-19 16:14:06 +01:00
@@ -414,38 +414,55 @@
 #ifndef EMBEDDED_LIBRARY
     if (mysql_bin_log.is_open())
     {
+#ifdef HAVE_ROW_BASED_REPLICATION
       /*
-        Make sure last block (the one which caused the error) gets logged.
-        This is needed because otherwise after write of
-        (to the binlog, not to read_info (which is a cache))
-        Delete_file_log_event the bad block will remain in read_info (because
-        pre_read is not called at the end of the last block; remember pre_read
-        is called whenever a new block is read from disk).
-        At the end of mysql_load(), the destructor of read_info will call
-        end_io_cache() which will flush read_info, so we will finally have
-        this in the binlog:
-        Append_block # The last successfull block
-        Delete_file
-        Append_block # The failing block
-        which is nonsense.
-        Or could also be (for a small file)
-        Create_file  # The failing block
-        which is nonsense (Delete_file is not written in this case, because:
-        Create_file has not been written, so Delete_file is not written, then
-        when read_info is destroyed end_io_cache() is called which writes
-        Create_file.
+        We need to do the job that is normally done inside
+        binlog_query() here, which is to ensure that the pending event
+        is written before tables are unlocked and before any other
+        events are written.  We also need to update the table map
+        version for the binary log to mark that table maps are invalid
+        after this point.
       */
-      read_info.end_io_cache();
-      /* If the file was not empty, wrote_create_file is true */
-      if (lf_info.wrote_create_file)
+      if (binlog_row_based)
+        thd->binlog_flush_pending_rows_event(true);
+      else
+#endif
       {
-	if ((info.copied || info.deleted) && !transactional_table)
-	  write_execute_load_query_log_event(thd, handle_duplicates,
-					     ignore, transactional_table);
-	else
+	/*
+	  Make sure last block (the one which caused the error) gets
+	  logged.  This is needed because otherwise after write of (to
+	  the binlog, not to read_info (which is a cache))
+	  Delete_file_log_event the bad block will remain in read_info
+	  (because pre_read is not called at the end of the last
+	  block; remember pre_read is called whenever a new block is
+	  read from disk).  At the end of mysql_load(), the destructor
+	  of read_info will call end_io_cache() which will flush
+	  read_info, so we will finally have this in the binlog:
+
+	  Append_block # The last successfull block
+	  Delete_file
+	  Append_block # The failing block
+	  which is nonsense.
+	  Or could also be (for a small file)
+	  Create_file  # The failing block
+	  which is nonsense (Delete_file is not written in this case, because:
+	  Create_file has not been written, so Delete_file is not written, then
+	  when read_info is destroyed end_io_cache() is called which writes
+	  Create_file.
+	*/
+	read_info.end_io_cache();
+	/* If the file was not empty, wrote_create_file is true */
+	if (lf_info.wrote_create_file)
 	{
-	  Delete_file_log_event d(thd, db, transactional_table);
-	  mysql_bin_log.write(&d);
+	  if ((info.copied || info.deleted) && !transactional_table)
+	    write_execute_load_query_log_event(thd, handle_duplicates,
+					       ignore, transactional_table);
+	  else
+	  {
+	    Delete_file_log_event d(thd, db, transactional_table);
+	    mysql_bin_log.write(&d);
+	  }
+          mysql_bin_log.update_table_map_version();
 	}
       }
     }
@@ -462,15 +479,34 @@
 #ifndef EMBEDDED_LIBRARY
   if (mysql_bin_log.is_open())
   {
+#ifdef HAVE_ROW_BASED_REPLICATION
     /*
-      As already explained above, we need to call end_io_cache() or the last
-      block will be logged only after Execute_load_query_log_event (which is
-      wrong), when read_info is destroyed.
-    */
-    read_info.end_io_cache();
-    if (lf_info.wrote_create_file)
-      write_execute_load_query_log_event(thd, handle_duplicates,
-					 ignore, transactional_table);
+      We need to do the job that is normally done inside
+      binlog_query() here, which is to ensure that the pending event
+      is written before tables are unlocked and before any other
+      events are written.  We also need to update the table map
+      version for the binary log to mark that table maps are invalid
+      after this point.
+     */
+    if (binlog_row_based)
+      thd->binlog_flush_pending_rows_event(true);
+    else
+#endif
+    {
+      /*
+        As already explained above, we need to call end_io_cache() or the last
+        block will be logged only after Execute_load_query_log_event (which is
+        wrong), when read_info is destroyed.
+      */
+      read_info.end_io_cache();
+      if (lf_info.wrote_create_file)
+      {
+        write_execute_load_query_log_event(thd, handle_duplicates,
+                                           ignore, transactional_table);
+        mysql_bin_log.update_table_map_version();
+      }
+      
+    }
   }
 #endif /*!EMBEDDED_LIBRARY*/
   if (transactional_table)
@@ -909,7 +945,7 @@
       if (get_it_from_net)
 	cache.read_function = _my_b_net_read;
 
-      if (mysql_bin_log.is_open())
+      if (!binlog_row_based && mysql_bin_log.is_open())
 	cache.pre_read = cache.pre_close =
 	  (IO_CACHE_CALLBACK) log_loaded_block;
 #endif

--- 1.484/sql/sql_parse.cc	2005-11-19 15:56:50 +01:00
+++ 1.485/sql/sql_parse.cc	2005-11-19 16:14:07 +01:00
@@ -3696,8 +3696,8 @@
     {
       if (mysql_bin_log.is_open())
       {
-        Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
-        mysql_bin_log.write(&qinfo);
+        thd->binlog_query(THD::ROW_QUERY_TYPE,
+                          thd->query, thd->query_length, FALSE, FALSE);
       }
       send_ok(thd);
     }
@@ -3714,8 +3714,8 @@
     {
       if (mysql_bin_log.is_open())
       {
-        Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
-        mysql_bin_log.write(&qinfo);
+        thd->binlog_query(THD::ROW_QUERY_TYPE,
+                          thd->query, thd->query_length, FALSE, FALSE);
       }
       send_ok(thd);
     }
@@ -3732,8 +3732,8 @@
     {
       if (mysql_bin_log.is_open())
       {
-        Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
-        mysql_bin_log.write(&qinfo);
+        thd->binlog_query(THD::ROW_QUERY_TYPE,
+                          thd->query, thd->query_length, FALSE, FALSE);
       }
       send_ok(thd);
     }
@@ -3748,8 +3748,8 @@
     {
       if (mysql_bin_log.is_open())
       {
-	Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
-	mysql_bin_log.write(&qinfo);
+        thd->binlog_query(THD::ROW_QUERY_TYPE,
+                          thd->query, thd->query_length, FALSE, FALSE);
       }
       send_ok(thd);
     }
@@ -3828,8 +3828,8 @@
       if (!res && mysql_bin_log.is_open())
       {
         thd->clear_error();
-        Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
-        mysql_bin_log.write(&qinfo);
+        thd->binlog_query(THD::ROW_QUERY_TYPE,
+                          thd->query, thd->query_length, FALSE, FALSE);
       }
     }
     else
@@ -3848,8 +3848,8 @@
 	if (mysql_bin_log.is_open())
 	{
           thd->clear_error();
-	  Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
-	  mysql_bin_log.write(&qinfo);
+          thd->binlog_query(THD::ROW_QUERY_TYPE,
+                            thd->query, thd->query_length, FALSE, FALSE);
 	}
 	if (lex->sql_command == SQLCOM_GRANT)
 	{
@@ -4383,8 +4383,8 @@
         if (mysql_bin_log.is_open())
         {
           thd->clear_error();
-          Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
-          mysql_bin_log.write(&qinfo);
+          thd->binlog_query(THD::ROW_QUERY_TYPE,
+                            thd->query, thd->query_length, FALSE, FALSE);
         }
 	send_ok(thd);
 	break;
@@ -4470,8 +4470,8 @@
         if (mysql_bin_log.is_open())
         {
           thd->clear_error();
-          Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
-          mysql_bin_log.write(&qinfo);
+          thd->binlog_query(THD::ROW_QUERY_TYPE,
+                            thd->query, thd->query_length, FALSE, FALSE);
         }
 	send_ok(thd);
 	break;
@@ -4568,8 +4568,8 @@
         buff.append(" AS ", 4);
         buff.append(first_table->source.str, first_table->source.length);
 
-        Query_log_event qinfo(thd, buff.ptr(), buff.length(), 0, FALSE);
-        mysql_bin_log.write(&qinfo);
+        thd->binlog_query(THD::STMT_QUERY_TYPE,
+                          buff.ptr(), buff.length(), FALSE, FALSE);
       }
       break;
     }
@@ -4582,8 +4582,8 @@
           mysql_bin_log.is_open())
       {
         thd->clear_error();
-        Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
-        mysql_bin_log.write(&qinfo);
+        thd->binlog_query(THD::STMT_QUERY_TYPE,
+                          thd->query, thd->query_length, FALSE, FALSE);
       }
       break;
     }

--- 1.369/sql/sql_select.cc	2005-11-19 10:41:39 +01:00
+++ 1.370/sql/sql_select.cc	2005-11-19 16:14:07 +01:00
@@ -9204,11 +9204,11 @@
   */
   while (!table->file->rnd_next(new_table.record[1]))
   {
-    if ((write_err=new_table.file->write_row(new_table.record[1])))
+    if ((write_err=new_table.file->ha_write_row(new_table.record[1])))
       goto err;
   }
   /* copy row that filled HEAP table */
-  if ((write_err=new_table.file->write_row(table->record[0])))
+  if ((write_err=new_table.file->ha_write_row(table->record[0])))
   {
     if (write_err != HA_ERR_FOUND_DUPP_KEY &&
 	write_err != HA_ERR_FOUND_DUPP_UNIQUE || !ignore_last_dupp_key_error)
@@ -10597,7 +10597,7 @@
     {
       int error;
       join->found_records++;
-      if ((error=table->file->write_row(table->record[0])))
+      if ((error=table->file->ha_write_row(table->record[0])))
       {
 	if (error == HA_ERR_FOUND_DUPP_KEY ||
 	    error == HA_ERR_FOUND_DUPP_UNIQUE)
@@ -10659,8 +10659,8 @@
   {						/* Update old record */
     restore_record(table,record[1]);
     update_tmptable_sum_func(join->sum_funcs,table);
-    if ((error=table->file->update_row(table->record[1],
-				       table->record[0])))
+    if ((error=table->file->ha_update_row(table->record[1],
+					  table->record[0])))
     {
       table->file->print_error(error,MYF(0));	/* purecov: inspected */
       DBUG_RETURN(NESTED_LOOP_ERROR);            /* purecov: inspected */
@@ -10683,7 +10683,7 @@
   }
   init_tmptable_sum_functions(join->sum_funcs);
   copy_funcs(join->tmp_table_param.items_to_copy);
-  if ((error=table->file->write_row(table->record[0])))
+  if ((error=table->file->ha_write_row(table->record[0])))
   {
     if (create_myisam_from_heap(join->thd, table, &join->tmp_table_param,
 				error, 0))
@@ -10719,7 +10719,7 @@
   copy_fields(&join->tmp_table_param);		// Groups are copied twice.
   copy_funcs(join->tmp_table_param.items_to_copy);
 
-  if (!(error=table->file->write_row(table->record[0])))
+  if (!(error=table->file->ha_write_row(table->record[0])))
     join->send_records++;			// New group
   else
   {
@@ -10735,8 +10735,8 @@
     }
     restore_record(table,record[1]);
     update_tmptable_sum_func(join->sum_funcs,table);
-    if ((error=table->file->update_row(table->record[1],
-				       table->record[0])))
+    if ((error=table->file->ha_update_row(table->record[1],
+					  table->record[0])))
     {
       table->file->print_error(error,MYF(0));	/* purecov: inspected */
       DBUG_RETURN(NESTED_LOOP_ERROR);            /* purecov: inspected */
@@ -10779,7 +10779,7 @@
                        join->sum_funcs_end[send_group_parts]);
 	if (!join->having || join->having->val_int())
 	{
-          int error= table->file->write_row(table->record[0]);
+          int error= table->file->ha_write_row(table->record[0]);
           if (error && create_myisam_from_heap(join->thd, table,
                                                &join->tmp_table_param,
                                                error, 0))
@@ -11641,7 +11641,7 @@
     }
     if (having && !having->val_int())
     {
-      if ((error=file->delete_row(record)))
+      if ((error=file->ha_delete_row(record)))
 	goto err;
       error=file->rnd_next(record);
       continue;
@@ -11668,7 +11668,7 @@
       }
       if (compare_record(table, first_field) == 0)
       {
-	if ((error=file->delete_row(record)))
+	if ((error=file->ha_delete_row(record)))
 	  goto err;
       }
       else if (!found)
@@ -11765,7 +11765,7 @@
     }
     if (having && !having->val_int())
     {
-      if ((error=file->delete_row(record)))
+      if ((error=file->ha_delete_row(record)))
 	goto err;
       continue;
     }
@@ -11782,7 +11782,7 @@
     if (hash_search(&hash, org_key_pos, key_length))
     {
       /* Duplicated found ; Remove the row */
-      if ((error=file->delete_row(record)))
+      if ((error=file->ha_delete_row(record)))
 	goto err;
     }
     else
@@ -13608,7 +13608,7 @@
           item->save_in_result_field(1);
       }
       copy_sum_funcs(sum_funcs_end[i+1], sum_funcs_end[i]);
-      if ((error= table->file->write_row(table->record[0])))
+      if ((error= table->file->ha_write_row(table->record[0])))
       {
 	if (create_myisam_from_heap(thd, table, &tmp_table_param,
 				      error, 0))

--- 1.280/sql/sql_show.cc	2005-11-19 10:41:39 +01:00
+++ 1.281/sql/sql_show.cc	2005-11-19 16:14:07 +01:00
@@ -1813,7 +1813,7 @@
 static bool schema_table_store_record(THD *thd, TABLE *table)
 {
   int error;
-  if ((error= table->file->write_row(table->record[0])))
+  if ((error= table->file->ha_write_row(table->record[0])))
   {
     if (create_myisam_from_heap(thd, table, 
                                 table->pos_in_table_list->schema_table_param,

--- 1.278/sql/sql_table.cc	2005-11-19 10:41:39 +01:00
+++ 1.279/sql/sql_table.cc	2005-11-19 16:33:23 +01:00
@@ -1791,13 +1791,22 @@
     }
     thd->tmp_table_used= 1;
   }
-  if (!internal_tmp_table && mysql_bin_log.is_open())
+
+  /*
+    Don't write statement if:
+    - It is an internal temporary table,
+    - Row-based logging is used and it we are creating a temporary table, or
+    - The binary log is not open.
+   */
+  if (!internal_tmp_table &&
+      !(binlog_row_based &&
+        (create_info->options & HA_LEX_CREATE_TMP_TABLE)) &&
+      mysql_bin_log.is_open())
   {
     thd->clear_error();
-    Query_log_event qinfo(thd, thd->query, thd->query_length, FALSE, FALSE);
-    mysql_bin_log.write(&qinfo);
+    thd->binlog_query(THD::STMT_QUERY_TYPE,
+                      thd->query, thd->query_length, FALSE, FALSE);
   }
-
   error= FALSE;
 unlock_and_end:
   VOID(pthread_mutex_unlock(&LOCK_open));
@@ -2908,6 +2917,17 @@
   }
 
   // Must be written before unlock
+  if (!binlog_row_based && mysql_bin_log.is_open())
+  {
+    thd->clear_error();
+    /*
+      CREATE TABLE ... LIKE ... cannot be logged statement-based nor
+      row-based.  This is filed as WL#2867.
+    */
+    Query_log_event qinfo(thd, thd->query, thd->query_length, FALSE, FALSE);
+    mysql_bin_log.write(&qinfo);
+  }
+Fix the below to do what the above does and then remove the above
   write_bin_log(thd, TRUE);
   res= FALSE;
   goto err;
@@ -3978,6 +3998,13 @@
     }
     if (!error)
     {
+      if (mysql_bin_log.is_open())
+      {
+	thd->clear_error();
+        thd->binlog_query(THD::STMT_QUERY_TYPE,
+                          thd->query, thd->query_length, FALSE, FALSE);
+      }
+fix the below and remove the above
       write_bin_log(thd, TRUE);
       if (do_send_ok)
         send_ok(thd);
@@ -4516,6 +4543,14 @@
       my_free((gptr) new_table,MYF(0));
       goto err;
     }
+    /* We don't replicate alter table statement on temporary tables */
+    if (!binlog_row_based && mysql_bin_log.is_open())
+    {
+      thd->clear_error();
+      Query_log_event qinfo(thd, thd->query, thd->query_length, FALSE, FALSE);
+      mysql_bin_log.write(&qinfo);
+    }
+fix the below remove the above
     write_bin_log(thd, TRUE);
     goto end_temporary;
   }
@@ -4647,6 +4682,15 @@
     goto err;
   }
   thd->proc_info="end";
+  if (mysql_bin_log.is_open())
+  {
+    DBUG_ASSERT(!(binlog_row_based &&
+		  (create_info->options & HA_LEX_CREATE_TMP_TABLE)));
+    thd->clear_error();
+    thd->binlog_query(THD::STMT_QUERY_TYPE,
+                      thd->query, thd->query_length, FALSE, FALSE);
+  }
+fix the below remove the above
   write_bin_log(thd, TRUE);
   VOID(pthread_cond_broadcast(&COND_refresh));
   VOID(pthread_mutex_unlock(&LOCK_open));
@@ -4824,7 +4868,7 @@
     {
       copy_ptr->do_copy(copy_ptr);
     }
-    if ((error=to->file->write_row((byte*) to->record[0])))
+    if ((error=to->file->ha_write_row((byte*) to->record[0])))
     {
       if ((!ignore &&
 	   handle_duplicates != DUP_REPLACE) ||

--- 1.57/sql/sql_udf.cc	2005-11-07 17:14:56 +01:00
+++ 1.58/sql/sql_udf.cc	2005-11-19 16:14:08 +01:00
@@ -454,7 +454,7 @@
   table->field[2]->store(u_d->dl,(uint) strlen(u_d->dl),
system_charset_info);
   if (table->s->fields >= 4)			// If not old func format
     table->field[3]->store((longlong) u_d->type, TRUE);
-  error = table->file->write_row(table->record[0]);
+  error = table->file->ha_write_row(table->record[0]);
 
   close_thread_tables(thd);
   if (error)
@@ -513,7 +513,7 @@
 				   HA_READ_KEY_EXACT))
   {
     int error;
-    if ((error = table->file->delete_row(table->record[0])))
+    if ((error = table->file->ha_delete_row(table->record[0])))
       table->file->print_error(error, MYF(0));
   }
   close_thread_tables(thd);

--- 1.175/sql/sql_update.cc	2005-11-07 16:24:44 +01:00
+++ 1.176/sql/sql_update.cc	2005-11-19 16:33:23 +01:00
@@ -498,8 +498,8 @@
         else
         {
           /* Non-batched update */
-	  error= table->file->update_row((byte*) table->record[1],
-	                                 (byte*) table->record[0]);
+	  error= table->file->ha_update_row((byte*) table->record[1],
+	                                   (byte*) table->record[0]);
         }
         if (!error)
 	{
@@ -619,10 +619,13 @@
     {
       if (error < 0)
         thd->clear_error();
-      Query_log_event qinfo(thd, thd->query, thd->query_length,
-			    transactional_table, FALSE);
-      if (mysql_bin_log.write(&qinfo) && transactional_table)
+      if (thd->binlog_query(THD::ROW_QUERY_TYPE,
+                            thd->query, thd->query_length,
+                            transactional_table, FALSE) &&
+          transactional_table)
+      {
 	error=1;				// Rollback update
+      }
     }
     if (!transactional_table)
       thd->options|=OPTION_STATUS_NO_TRANS_UPDATE;
@@ -1358,8 +1361,8 @@
 	  */
 	  main_table->file->extra(HA_EXTRA_PREPARE_FOR_UPDATE);
 	}
-	if ((error=table->file->update_row(table->record[1],
-					   table->record[0])))
+	if ((error=table->file->ha_update_row(table->record[1],
+					      table->record[0])))
 	{
 	  updated--;
           if (!ignore || error != HA_ERR_FOUND_DUPP_KEY)
@@ -1390,7 +1393,7 @@
       memcpy((char*) tmp_table->field[0]->ptr,
 	     (char*) table->file->ref, table->file->ref_length);
       /* Write row, ignoring duplicated updates to a row */
-      if ((error= tmp_table->file->write_row(tmp_table->record[0])) &&
+      if ((error= tmp_table->file->ha_write_row(tmp_table->record[0]))
&&
 	  (error != HA_ERR_FOUND_DUPP_KEY &&
 	   error != HA_ERR_FOUND_DUPP_UNIQUE))
       {
@@ -1505,8 +1508,8 @@
 
       if (compare_record(table, thd->query_id))
       {
-	if ((local_error=table->file->update_row(table->record[1],
-						 table->record[0])))
+	if ((local_error=table->file->ha_update_row(table->record[1],
+						    table->record[0])))
 	{
 	  if (!ignore || local_error != HA_ERR_FOUND_DUPP_KEY)
 	    goto err;
@@ -1585,10 +1588,13 @@
     {
       if (local_error == 0)
         thd->clear_error();
-      Query_log_event qinfo(thd, thd->query, thd->query_length,
-			    transactional_tables, FALSE);
-      if (mysql_bin_log.write(&qinfo) && trans_safe)
+      if (thd->binlog_query(THD::ROW_QUERY_TYPE,
+                            thd->query, thd->query_length,
+                            transactional_tables, FALSE) &&
+          trans_safe)
+      {
 	local_error= 1;				// Rollback update
+      }
     }
     if (!transactional_tables)
       thd->options|=OPTION_STATUS_NO_TRANS_UPDATE;

--- 1.186/sql/table.cc	2005-11-07 16:24:45 +01:00
+++ 1.187/sql/table.cc	2005-11-19 16:14:08 +01:00
@@ -424,6 +424,7 @@
   if (!(record= (char *) alloc_root(&outparam->mem_root,
                                     rec_buff_length * records)))
     goto err;                                   /* purecov: inspected */
+  outparam->write_row_record= NULL;
   share->default_values= (byte *) record;
 
   if (my_pread(file,(byte*) record, (uint) share->reclength,
@@ -1032,6 +1033,16 @@
   if (use_hash)
     (void) hash_check(&share->name_hash);
 #endif
+#ifdef HAVE_REPLICATION
+  share->table_map_id= table_mapping::NO_TABLE;
+  /*
+     This constant is used to mark that no table map version has been
+     assigned.  No arithmetic is done on the value: it will be
+     overwritten with a value taken from MYSQL_BIN_LOG.
+  */
+  share->table_map_version= ~(ulonglong)0;
+#endif
+
   DBUG_RETURN (0);
 
  err:

--- 1.116/sql/table.h	2005-11-18 16:06:35 +01:00
+++ 1.117/sql/table.h	2005-11-19 16:14:08 +01:00
@@ -178,6 +178,8 @@
   my_bool crashed;
   my_bool is_view;
   my_bool name_lock, replace_with_name_lock;
+  ulong table_map_id;
+  ulonglong table_map_version;
   /*
     TRUE if this is a system table like 'mysql.proc', which we want to be
     able to open and lock even when we already have some tables open and
@@ -203,6 +205,8 @@
   Field **field;			/* Pointer to fields */
 
   byte *record[2];			/* Pointer to records */
+  byte *write_row_record;		/* Used as optimisation in
+					   THD::write_row */
   byte *insert_values;                  /* used by INSERT ... UPDATE */
   key_map quick_keys, used_keys, keys_in_use_for_query;
   KEY  *key_info;			/* data of keys in database */

--- 1.5.6.1/mysql-test/r/rpl_sp.result	2005-11-17 01:15:00 +01:00
+++ 1.14/mysql-test/r/rpl_stm_sp.result	2005-11-19 16:14:04 +01:00
@@ -261,7 +261,7 @@
 select * from t1;
 a
 1
-show binlog events in 'master-bin.000001' from 98;
+show binlog events in 'master-bin.000001' from 102;
 Log_name	Pos	Event_type	Server_id	End_log_pos	Info
 master-bin.000001	#	Query	1	#	drop database if exists mysqltest1
 master-bin.000001	#	Query	1	#	create database mysqltest1

--- 1.4.5.1/mysql-test/t/rpl_sp.test	2005-11-17 01:15:00 +01:00
+++ 1.13/mysql-test/t/rpl_stm_sp.test	2005-11-19 16:14:04 +01:00
@@ -1,3 +1,6 @@
+# row-based and statement have expected binlog difference in result files
+-- source include/have_binlog_format_statement.inc
+
 # Test of replication of stored procedures (WL#2146 for MySQL 5.0)
 # Modified by WL#2971.
 
@@ -333,7 +336,7 @@
 insert into t1 values (1);
 select * from t1;
 --replace_column 2 # 5 #
-show binlog events in 'master-bin.000001' from 98;
+show binlog events in 'master-bin.000001' from 102;
 sync_slave_with_master;
 select * from t1;
 

--- 1.22.1.2/mysql-test/r/mix_innodb_myisam_binlog.result	2005-11-18 05:17:45 +01:00
+++ 1.27/mysql-test/r/binlog_stm_mix_innodb_myisam.result	2005-11-19 16:14:04 +01:00
@@ -6,12 +6,12 @@
 insert into t1 values(1);
 insert into t2 select * from t1;
 commit;
-show binlog events from 98;
+show binlog events from 102;
 Log_name	Pos	Event_type	Server_id	End_log_pos	Info
-master-bin.000001	98	Query	1	#	use `test`; BEGIN
-master-bin.000001	166	Query	1	#	use `test`; insert into t1 values(1)
-master-bin.000001	253	Query	1	#	use `test`; insert into t2 select * from t1
-master-bin.000001	347	Xid	1	#	COMMIT /* xid=8 */
+master-bin.000001	102	Query	1	#	use `test`; BEGIN
+master-bin.000001	170	Query	1	#	use `test`; insert into t1 values(1)
+master-bin.000001	257	Query	1	#	use `test`; insert into t2 select * from t1
+master-bin.000001	351	Xid	1	#	COMMIT /* xid=2182 */
 delete from t1;
 delete from t2;
 reset master;
@@ -21,12 +21,12 @@
 rollback;
 Warnings:
 Warning	1196	Some non-transactional changed tables couldn't be rolled back
-show binlog events from 98;
+show binlog events from 102;
 Log_name	Pos	Event_type	Server_id	End_log_pos	Info
-master-bin.000001	98	Query	1	#	use `test`; BEGIN
-master-bin.000001	166	Query	1	#	use `test`; insert into t1 values(2)
-master-bin.000001	253	Query	1	#	use `test`; insert into t2 select * from t1
-master-bin.000001	347	Query	1	#	use `test`; ROLLBACK
+master-bin.000001	102	Query	1	#	use `test`; BEGIN
+master-bin.000001	170	Query	1	#	use `test`; insert into t1 values(2)
+master-bin.000001	257	Query	1	#	use `test`; insert into t2 select * from t1
+master-bin.000001	351	Query	1	#	use `test`; ROLLBACK
 delete from t1;
 delete from t2;
 reset master;
@@ -39,15 +39,15 @@
 Warnings:
 Warning	1196	Some non-transactional changed tables couldn't be rolled back
 commit;
-show binlog events from 98;
+show binlog events from 102;
 Log_name	Pos	Event_type	Server_id	End_log_pos	Info
-master-bin.000001	98	Query	1	#	use `test`; BEGIN
-master-bin.000001	166	Query	1	#	use `test`; insert into t1 values(3)
-master-bin.000001	253	Query	1	#	use `test`; savepoint my_savepoint
-master-bin.000001	338	Query	1	#	use `test`; insert into t1 values(4)
-master-bin.000001	425	Query	1	#	use `test`; insert into t2 select * from t1
-master-bin.000001	519	Query	1	#	use `test`; rollback to savepoint my_savepoint
-master-bin.000001	616	Xid	1	#	COMMIT /* xid=25 */
+master-bin.000001	102	Query	1	#	use `test`; BEGIN
+master-bin.000001	170	Query	1	#	use `test`; insert into t1 values(3)
+master-bin.000001	257	Query	1	#	use `test`; savepoint my_savepoint
+master-bin.000001	342	Query	1	#	use `test`; insert into t1 values(4)
+master-bin.000001	429	Query	1	#	use `test`; insert into t2 select * from t1
+master-bin.000001	523	Query	1	#	use `test`; rollback to savepoint my_savepoint
+master-bin.000001	620	Xid	1	#	COMMIT /* xid=2199 */
 delete from t1;
 delete from t2;
 reset master;
@@ -65,16 +65,16 @@
 a
 5
 7
-show binlog events from 98;
+show binlog events from 102;
 Log_name	Pos	Event_type	Server_id	End_log_pos	Info
-master-bin.000001	98	Query	1	#	use `test`; BEGIN
-master-bin.000001	166	Query	1	#	use `test`; insert into t1 values(5)
-master-bin.000001	253	Query	1	#	use `test`; savepoint my_savepoint
-master-bin.000001	338	Query	1	#	use `test`; insert into t1 values(6)
-master-bin.000001	425	Query	1	#	use `test`; insert into t2 select * from t1
-master-bin.000001	519	Query	1	#	use `test`; rollback to savepoint my_savepoint
-master-bin.000001	616	Query	1	#	use `test`; insert into t1 values(7)
-master-bin.000001	703	Xid	1	#	COMMIT /* xid=37 */
+master-bin.000001	102	Query	1	#	use `test`; BEGIN
+master-bin.000001	170	Query	1	#	use `test`; insert into t1 values(5)
+master-bin.000001	257	Query	1	#	use `test`; savepoint my_savepoint
+master-bin.000001	342	Query	1	#	use `test`; insert into t1 values(6)
+master-bin.000001	429	Query	1	#	use `test`; insert into t2 select * from t1
+master-bin.000001	523	Query	1	#	use `test`; rollback to savepoint my_savepoint
+master-bin.000001	620	Query	1	#	use `test`; insert into t1 values(7)
+master-bin.000001	707	Xid	1	#	COMMIT /* xid=2211 */
 delete from t1;
 delete from t2;
 reset master;
@@ -87,43 +87,43 @@
 select get_lock("a",10);
 get_lock("a",10)
 1
-show binlog events from 98;
+show binlog events from 102;
 Log_name	Pos	Event_type	Server_id	End_log_pos	Info
-master-bin.000001	98	Query	1	#	use `test`; BEGIN
-master-bin.000001	166	Query	1	#	use `test`; insert into t1 values(8)
-master-bin.000001	253	Query	1	#	use `test`; insert into t2 select * from t1
-master-bin.000001	347	Query	1	#	use `test`; ROLLBACK
+master-bin.000001	102	Query	1	#	use `test`; BEGIN
+master-bin.000001	170	Query	1	#	use `test`; insert into t1 values(8)
+master-bin.000001	257	Query	1	#	use `test`; insert into t2 select * from t1
+master-bin.000001	351	Query	1	#	use `test`; ROLLBACK
 delete from t1;
 delete from t2;
 reset master;
 insert into t1 values(9);
 insert into t2 select * from t1;
-show binlog events from 98;
+show binlog events from 102;
 Log_name	Pos	Event_type	Server_id	End_log_pos	Info
-master-bin.000001	98	Query	1	#	use `test`; insert into t1 values(9)
-master-bin.000001	185	Xid	1	#	COMMIT /* xid=60 */
-master-bin.000001	212	Query	1	#	use `test`; insert into t2 select * from t1
+master-bin.000001	102	Query	1	#	use `test`; insert into t1 values(9)
+master-bin.000001	189	Xid	1	#	COMMIT /* xid=2234 */
+master-bin.000001	216	Query	1	#	use `test`; insert into t2 select * from t1
 delete from t1;
 delete from t2;
 reset master;
 insert into t1 values(10);
 begin;
 insert into t2 select * from t1;
-show binlog events from 98;
+show binlog events from 102;
 Log_name	Pos	Event_type	Server_id	End_log_pos	Info
-master-bin.000001	98	Query	1	#	use `test`; insert into t1 values(10)
-master-bin.000001	186	Xid	1	#	COMMIT /* xid=66 */
-master-bin.000001	213	Query	1	#	use `test`; insert into t2 select * from t1
+master-bin.000001	102	Query	1	#	use `test`; insert into t1 values(10)
+master-bin.000001	190	Xid	1	#	COMMIT /* xid=2240 */
+master-bin.000001	217	Query	1	#	use `test`; insert into t2 select * from t1
 insert into t1 values(11);
 commit;
-show binlog events from 98;
+show binlog events from 102;
 Log_name	Pos	Event_type	Server_id	End_log_pos	Info
-master-bin.000001	98	Query	1	#	use `test`; insert into t1 values(10)
-master-bin.000001	186	Xid	1	#	COMMIT /* xid=66 */
-master-bin.000001	213	Query	1	#	use `test`; insert into t2 select * from t1
-master-bin.000001	307	Query	1	#	use `test`; BEGIN
-master-bin.000001	375	Query	1	#	use `test`; insert into t1 values(11)
-master-bin.000001	463	Xid	1	#	COMMIT /* xid=68 */
+master-bin.000001	102	Query	1	#	use `test`; insert into t1 values(10)
+master-bin.000001	190	Xid	1	#	COMMIT /* xid=2240 */
+master-bin.000001	217	Query	1	#	use `test`; insert into t2 select * from t1
+master-bin.000001	311	Query	1	#	use `test`; BEGIN
+master-bin.000001	379	Query	1	#	use `test`; insert into t1 values(11)
+master-bin.000001	467	Xid	1	#	COMMIT /* xid=2242 */
 alter table t2 engine=INNODB;
 delete from t1;
 delete from t2;
@@ -132,12 +132,12 @@
 insert into t1 values(12);
 insert into t2 select * from t1;
 commit;
-show binlog events from 98;
+show binlog events from 102;
 Log_name	Pos	Event_type	Server_id	End_log_pos	Info
-master-bin.000001	98	Query	1	#	use `test`; BEGIN
-master-bin.000001	166	Query	1	#	use `test`; insert into t1 values(12)
-master-bin.000001	254	Query	1	#	use `test`; insert into t2 select * from t1
-master-bin.000001	348	Xid	1	#	COMMIT /* xid=78 */
+master-bin.000001	102	Query	1	#	use `test`; BEGIN
+master-bin.000001	170	Query	1	#	use `test`; insert into t1 values(12)
+master-bin.000001	258	Query	1	#	use `test`; insert into t2 select * from t1
+master-bin.000001	352	Xid	1	#	COMMIT /* xid=2252 */
 delete from t1;
 delete from t2;
 reset master;
@@ -145,7 +145,7 @@
 insert into t1 values(13);
 insert into t2 select * from t1;
 rollback;
-show binlog events from 98;
+show binlog events from 102;
 Log_name	Pos	Event_type	Server_id	End_log_pos	Info
 delete from t1;
 delete from t2;
@@ -157,11 +157,11 @@
 insert into t2 select * from t1;
 rollback to savepoint my_savepoint;
 commit;
-show binlog events from 98;
+show binlog events from 102;
 Log_name	Pos	Event_type	Server_id	End_log_pos	Info
-master-bin.000001	98	Query	1	#	use `test`; BEGIN
-master-bin.000001	166	Query	1	#	use `test`; insert into t1 values(14)
-master-bin.000001	254	Xid	1	#	COMMIT /* xid=94 */
+master-bin.000001	102	Query	1	#	use `test`; BEGIN
+master-bin.000001	170	Query	1	#	use `test`; insert into t1 values(14)
+master-bin.000001	258	Xid	1	#	COMMIT /* xid=2268 */
 delete from t1;
 delete from t2;
 reset master;
@@ -177,12 +177,12 @@
 a
 16
 18
-show binlog events from 98;
+show binlog events from 102;
 Log_name	Pos	Event_type	Server_id	End_log_pos	Info
-master-bin.000001	98	Query	1	#	use `test`; BEGIN
-master-bin.000001	166	Query	1	#	use `test`; insert into t1 values(16)
-master-bin.000001	254	Query	1	#	use `test`; insert into t1 values(18)
-master-bin.000001	342	Xid	1	#	COMMIT /* xid=105 */
+master-bin.000001	102	Query	1	#	use `test`; BEGIN
+master-bin.000001	170	Query	1	#	use `test`; insert into t1 values(16)
+master-bin.000001	258	Query	1	#	use `test`; insert into t1 values(18)
+master-bin.000001	346	Xid	1	#	COMMIT /* xid=2279 */
 delete from t1;
 delete from t2;
 alter table t2 type=MyISAM;

--- 1.15.1.5/mysql-test/t/mix_innodb_myisam_binlog.test	2005-11-18 05:17:45 +01:00
+++ 1.21/mysql-test/extra/binlog_tests/mix_innodb_myisam_binlog.test	2005-11-19 16:14:04
+01:00
@@ -5,6 +5,8 @@
 # did some tests manually on a slave; tables are replicated fine and
 # Exec_Master_Log_Pos advances as expected.
 
+#-- source include/have_binlog_format_statement.inc
+
 # Embedded server doesn't support binlogging
 -- source include/not_embedded.inc
 
@@ -30,7 +32,7 @@
 
 --replace_column 5 #
 --replace_result "xid=15" "xid=8"
-show binlog events from 98;
+show binlog events from 102;
 
 delete from t1;
 delete from t2;
@@ -43,7 +45,7 @@
 rollback;
 
 --replace_column 5 #
-show binlog events from 98;
+show binlog events from 102;
 
 delete from t1;
 delete from t2;
@@ -59,7 +61,7 @@
 
 --replace_column 5 #
 --replace_result "xid=48" "xid=25"
-show binlog events from 98;
+show binlog events from 102;
 
 delete from t1;
 delete from t2;
@@ -77,7 +79,7 @@
 
 --replace_column 5 #
 --replace_result "xid=70" "xid=37"
-show binlog events from 98;
+show binlog events from 102;
 
 # and when ROLLBACK is not explicit?
 delete from t1;
@@ -98,7 +100,7 @@
 # logging has been done, we use a user lock.
 select get_lock("a",10);
 --replace_column 5 #
-show binlog events from 98;
+show binlog events from 102;
 
 # and when not in a transact1on?
 delete from t1;
@@ -110,7 +112,7 @@
 
 --replace_column 5 #
 --replace_result "xid=119" "xid=60"
-show binlog events from 98;
+show binlog events from 102;
 
 # Check that when the query updat1ng the MyISAM table is the first in the
 # transaction, we log it immediately.
@@ -123,13 +125,13 @@
 insert into t2 select * from t1;
 --replace_column 5 #
 --replace_result "xid=133" "xid=66"
-show binlog events from 98;
+show binlog events from 102;
 insert into t1 values(11);
 commit;
 
 --replace_column 5 #
 --replace_result "xid=133" "xid=66" "xid=136" "xid=68"
-show binlog events from 98;
+show binlog events from 102;
 
 
 # Check that things work like before this BEGIN/ROLLBACK code was added,
@@ -148,7 +150,7 @@
 
 --replace_column 5 #
 --replace_result "xid=155" "xid=78"
-show binlog events from 98;
+show binlog events from 102;
 
 delete from t1;
 delete from t2;
@@ -160,7 +162,7 @@
 rollback;
 
 --replace_column 5 #
-show binlog events from 98;
+show binlog events from 102;
 
 delete from t1;
 delete from t2;
@@ -176,7 +178,7 @@
 
 --replace_column 5 #
 --replace_result "xid=187" "xid=94"
-show binlog events from 98;
+show binlog events from 102;
 
 delete from t1;
 delete from t2;
@@ -194,7 +196,7 @@
 
 --replace_column 5 #
 --replace_result "xid=208" "xid=105"
-show binlog events from 98;
+show binlog events from 102;
 
 # Test for BUG#5714, where a MyISAM update in the transaction used to
 # release row-level locks in InnoDB

--- 1.41/mysql-test/mysql-test-run.pl	2005-11-18 16:06:32 +01:00
+++ 1.42/mysql-test/mysql-test-run.pl	2005-11-19 16:14:04 +01:00
@@ -2453,6 +2453,10 @@
   my $cmdline_mysqldump= "$exe_mysqldump --no-defaults -uroot " .
                          "--port=$master->[0]->{'path_myport'} " .
                          "--socket=$master->[0]->{'path_mysock'} --password=";
+
+ my $cmdline_mysqldumpslave= "$exe_mysqldump --no-defaults -uroot " .
+                         "--socket=$slave->[0]->{'path_mysock'} --password=";
+
   if ( $opt_debug )
   {
     $cmdline_mysqldump .=
@@ -2520,6 +2524,7 @@
   $ENV{'MYSQL_CHECK'}=              $cmdline_mysqlcheck;
   $ENV{'MYSQL_DUMP'}=               $cmdline_mysqldump;
   $ENV{'MYSQL_IMPORT'}=             $cmdline_mysqlimport;
+  $ENV{'MYSQL_DUMP_SLAVE'}=         $cmdline_mysqldumpslave;
   $ENV{'MYSQL_SHOW'}=               $cmdline_mysqlshow;
   $ENV{'MYSQL_BINLOG'}=             $cmdline_mysqlbinlog;
   $ENV{'MYSQL_FIX_SYSTEM_TABLES'}=  $cmdline_mysql_fix_system_tables;

--- 1.12/mysql-test/t/disabled.def	2005-11-19 10:43:23 +01:00
+++ 1.13/mysql-test/t/disabled.def	2005-11-19 16:14:04 +01:00
@@ -6,7 +6,7 @@
 #
 #      <testcasename>  : Comment test
 #
-#  Don't use any TAB characters for whitespace.
+#  Do not use any TAB characters for whitespace.
 #
 ##############################################################################
 
@@ -15,4 +15,14 @@
 rpl_until       : Unstable test case, bug#12429
 rpl_deadlock    : Unstable test case, bug#12429
 kill            : Unstable test case, bug#9712
+rpl_row_replicate_do:Causes wrong result on slave Bug 12421
+rpl_row_mystery22:shows rbr slave issues Bug 12418
+rpl_row_relayrotate:Bug 14082
+rpl_row_timezone:Timestamp incorrectly replicated Bug 12443
+rpl_row_000002:create table from temporary Bug 12345
+rpl_row_sp006_InnoDB:Bug 12586
+rpl_row_func002:Bug 12510
+rpl_row_view01:Bug 12687
+rpl_row_NOW:Bug 12574 
+rpl_bit_npk:Bug 13418
 compress        : Magnus will fix

--- 1.237/sql/ha_innodb.cc	2005-11-19 10:41:37 +01:00
+++ 1.238/sql/ha_innodb.cc	2005-11-19 16:14:05 +01:00
@@ -833,6 +833,7 @@
                   HA_CAN_INDEX_BLOBS |
                   HA_CAN_SQL_HANDLER |
                   HA_NOT_EXACT_COUNT |
+                  HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS |
                   HA_PRIMARY_KEY_IN_READ_INDEX |
                   HA_CAN_GEOMETRY |
                   HA_TABLE_SCAN_ON_INDEX),

--- 1.114/include/my_global.h	2005-11-18 16:06:31 +01:00
+++ 1.115/include/my_global.h	2005-11-19 16:14:04 +01:00
@@ -834,6 +834,14 @@
 typedef long long	my_ptrdiff_t;
 #endif
 
+#if HAVE_SIZE_T
+typedef size_t             my_size_t;
+#elif SIZEOF_CHARP <= SIZEOF_LONG
+typedef unsigned long      my_size_t;
+#else
+typedef unsigned long long my_size_t;
+#endif
+       
 #define MY_ALIGN(A,L)	(((A) + (L) - 1) & ~((L) - 1))
 #define ALIGN_SIZE(A)	MY_ALIGN((A),sizeof(double))
 /* Size to make adressable obj. */

--- 1.146/sql/set_var.cc	2005-11-19 10:41:38 +01:00
+++ 1.147/sql/set_var.cc	2005-11-19 16:33:23 +01:00
@@ -635,6 +635,7 @@
   {"bdb_shared_data",	      (char*) &berkeley_shared_data,	    SHOW_BOOL},
   {"bdb_tmpdir",              (char*) &berkeley_tmpdir,             SHOW_CHAR_PTR},
   {sys_binlog_cache_size.name,(char*) &sys_binlog_cache_size,	    SHOW_SYS},
+  {"binlog_format",           (char*) &opt_binlog_format,           SHOW_CHAR_PTR},
   {sys_bulk_insert_buff_size.name,(char*) &sys_bulk_insert_buff_size,SHOW_SYS},
   {sys_character_set_client.name,(char*) &sys_character_set_client, SHOW_SYS},
   {sys_character_set_connection.name,(char*) &sys_character_set_connection,SHOW_SYS},
@@ -687,6 +688,8 @@
   {sys_have_raid.name,        (char*) &have_raid,                   SHOW_HAVE},
   {sys_have_rtree_keys.name,  (char*) &have_rtree_keys,             SHOW_HAVE},
   {sys_have_symlink.name,     (char*) &have_symlink,                SHOW_HAVE},
+This "have_row_based_replication" must be fixed to become like above sys_have_symlink
probably
+  {"have_row_based_replication",(char*) &have_row_based_replication,SHOW_HAVE},
   {"init_connect",            (char*) &sys_init_connect,            SHOW_SYS},
   {"init_file",               (char*) &opt_init_file,               SHOW_CHAR_PTR},
   {"init_slave",              (char*) &sys_init_slave,              SHOW_SYS},

--- 1.70/libmysqld/Makefile.am	2005-11-07 16:24:34 +01:00
+++ 1.71/libmysqld/Makefile.am	2005-11-19 16:14:04 +01:00
@@ -45,6 +45,7 @@
 sqlsources = derror.cc field.cc field_conv.cc strfunc.cc filesort.cc \
 	ha_heap.cc ha_myisam.cc ha_myisammrg.cc handler.cc sql_handler.cc \
 	hostname.cc init.cc password.c \
+	rpl_filter.cc \
 	item.cc item_buff.cc item_cmpfunc.cc item_create.cc \
 	item_func.cc item_strfunc.cc item_sum.cc item_timefunc.cc \
 	item_geofunc.cc item_uniq.cc item_subselect.cc item_row.cc\

--- 1.169/mysql-test/r/sp.result	2005-11-04 10:43:44 +01:00
+++ 1.170/mysql-test/r/sp.result	2005-11-19 16:14:04 +01:00
@@ -3617,4 +3617,54 @@
 drop table t3, t4|
 drop procedure bug14210|
 set @@session.max_heap_table_size=default|
+drop function if exists bug14723|
+drop procedure if exists bug14723|
+/*!50003 create function bug14723()
+returns bigint(20)
+main_loop: begin
+return 42;
+end */;;
+show create function bug14723;;
+Function	sql_mode	Create Function
+bug14723		CREATE FUNCTION `bug14723`() RETURNS bigint(20)
+main_loop: begin
+return 42;
+end
+select bug14723();;
+bug14723()
+42
+/*!50003 create procedure bug14723()
+main_loop: begin
+select 42;
+end */;;
+show create procedure bug14723;;
+Procedure	sql_mode	Create Procedure
+bug14723		CREATE PROCEDURE `bug14723`()
+main_loop: begin
+select 42;
+end
+call bug14723();;
+42
+42
+drop function bug14723|
+drop procedure bug14723|
+create procedure bug14845()
+begin
+declare a char(255);
+declare done int default 0;
+declare c cursor for select count(*) from t1 where 1 = 0;
+declare continue handler for sqlstate '02000' set done = 1;
+open c;
+repeat
+fetch c into a;
+if not done then
+select a;
+end if;
+until done end repeat;
+close c;
+end|
+call bug14845()|
+a
+0
+drop procedure bug14845|
 drop table t1,t2;

--- 1.163/mysql-test/t/sp.test	2005-11-04 10:43:45 +01:00
+++ 1.164/mysql-test/t/sp.test	2005-11-19 16:14:04 +01:00
@@ -4545,6 +4545,59 @@
 drop procedure bug14210|
 set @@session.max_heap_table_size=default|
 
+
+#
+# BUG#1473: Dumping of stored functions seems to cause corruption in
+#           the function body
+#
+--disable_warnings
+drop function if exists bug14723|
+drop procedure if exists bug14723|
+--enable_warnings
+
+delimiter ;;|
+/*!50003 create function bug14723()
+ returns bigint(20)
+main_loop: begin
+  return 42;
+end */;;
+show create function bug14723;;
+select bug14723();;
+
+/*!50003 create procedure bug14723()
+main_loop: begin
+  select 42;
+end */;;
+show create procedure bug14723;;
+call bug14723();;
+
+delimiter |;;
+
+drop function bug14723|
+drop procedure bug14723|
+
+#
+# Bug#14845 "mysql_stmt_fetch returns MYSQL_NO_DATA when COUNT(*) is 0"
+# Check that when fetching from a cursor, COUNT(*) works properly.
+#
+create procedure bug14845()
+begin
+  declare a char(255);
+  declare done int default 0;
+  declare c cursor for select count(*) from t1 where 1 = 0;
+  declare continue handler for sqlstate '02000' set done = 1;
+  open c;
+  repeat
+    fetch c into a;
+    if not done then
+      select a;
+    end if;
+  until done end repeat;
+  close c;
+end|
+call bug14845()|
+drop procedure bug14845|
+
 #
 # BUG#NNNN: New bug synopsis
 #

--- 1.94/sql/sp.cc	2005-11-18 16:06:33 +01:00
+++ 1.95/sql/sp.cc	2005-11-19 16:14:06 +01:00
@@ -582,14 +582,14 @@
     }
 
     ret= SP_OK;
-    if (table->file->write_row(table->record[0]))
+    if (table->file->ha_write_row(table->record[0]))
       ret= SP_WRITE_ROW_FAILED;
     else if (mysql_bin_log.is_open())
     {
       thd->clear_error();
       /* Such a statement can always go directly to binlog, no trans cache */
-      Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
-      mysql_bin_log.write(&qinfo);
+      thd->binlog_query(THD::ROW_QUERY_TYPE,
+                        thd->query, thd->query_length, FALSE, FALSE);
     }
 
   }
@@ -615,7 +615,7 @@
     DBUG_RETURN(SP_OPEN_TABLE_FAILED);
   if ((ret= db_find_routine_aux(thd, type, name, table)) == SP_OK)
   {
-    if (table->file->delete_row(table->record[0]))
+    if (table->file->ha_delete_row(table->record[0]))
       ret= SP_DELETE_ROW_FAILED;
   }
   close_thread_tables(thd);
@@ -650,7 +650,7 @@
       table->field[MYSQL_PROC_FIELD_COMMENT]->store(chistics->comment.str,
 						    chistics->comment.length,
 						    system_charset_info);
-    if ((table->file->update_row(table->record[1],table->record[0])))
+    if ((table->file->ha_update_row(table->record[1],table->record[0])))
       ret= SP_WRITE_ROW_FAILED;
   }
   close_thread_tables(thd);
@@ -870,7 +870,7 @@
 
     do
     {
-      if (! table->file->delete_row(table->record[0]))
+      if (! table->file->ha_delete_row(table->record[0]))
 	deleted= TRUE;		/* We deleted something */
       else
       {

--- 1.195/sql/sp_head.cc	2005-11-14 17:01:00 +01:00
+++ 1.196/sql/sp_head.cc	2005-11-19 16:14:06 +01:00
@@ -87,11 +87,11 @@
   case SQLCOM_SHOW_ERRORS:
   case SQLCOM_SHOW_FIELDS:
   case SQLCOM_SHOW_GRANTS:
-  case SQLCOM_SHOW_INNODB_STATUS:
+  case SQLCOM_SHOW_ENGINE_STATUS:
+  case SQLCOM_SHOW_ENGINE_LOGS:
+  case SQLCOM_SHOW_ENGINE_MUTEX:
   case SQLCOM_SHOW_KEYS:
-  case SQLCOM_SHOW_LOGS:
   case SQLCOM_SHOW_MASTER_STAT:
-  case SQLCOM_SHOW_MUTEX_STATUS:
   case SQLCOM_SHOW_NEW_MASTER:
   case SQLCOM_SHOW_OPEN_TABLES:
   case SQLCOM_SHOW_PRIVILEGES:
@@ -120,6 +120,45 @@
   case SQLCOM_DEALLOCATE_PREPARE:
     flags= sp_head::CONTAINS_DYNAMIC_SQL;
     break;
+  case SQLCOM_CREATE_TABLE:
+    if (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE)
+      flags= 0;
+    else
+      flags= sp_head::HAS_COMMIT_OR_ROLLBACK;
+    break;
+  case SQLCOM_DROP_TABLE:
+    if (lex->drop_temporary)
+      flags= 0;
+    else
+      flags= sp_head::HAS_COMMIT_OR_ROLLBACK;
+    break;
+  case SQLCOM_CREATE_INDEX:
+  case SQLCOM_CREATE_DB:
+  case SQLCOM_CREATE_VIEW:
+  case SQLCOM_CREATE_TRIGGER:
+  case SQLCOM_CREATE_USER:
+  case SQLCOM_ALTER_TABLE:
+  case SQLCOM_BEGIN:
+  case SQLCOM_RENAME_TABLE:
+  case SQLCOM_RENAME_USER:
+  case SQLCOM_DROP_INDEX:
+  case SQLCOM_DROP_DB:
+  case SQLCOM_DROP_USER:
+  case SQLCOM_DROP_VIEW:
+  case SQLCOM_DROP_TRIGGER:
+  case SQLCOM_TRUNCATE:
+  case SQLCOM_COMMIT:
+  case SQLCOM_ROLLBACK:
+  case SQLCOM_LOAD_MASTER_DATA:
+  case SQLCOM_LOCK_TABLES:
+  case SQLCOM_CREATE_PROCEDURE:
+  case SQLCOM_CREATE_SPFUNCTION:
+  case SQLCOM_ALTER_PROCEDURE:
+  case SQLCOM_ALTER_FUNCTION:
+  case SQLCOM_DROP_PROCEDURE:
+  case SQLCOM_DROP_FUNCTION:
+    flags= sp_head::HAS_COMMIT_OR_ROLLBACK;
+    break;
   default:
     flags= 0;
     break;
@@ -476,7 +515,7 @@
 sp_head::init_strings(THD *thd, LEX *lex, sp_name *name)
 {
   DBUG_ENTER("sp_head::init_strings");
-  uint n;			/* Counter for nul trimming */ 
+  uchar *endp;                  /* Used to trim the end */
   /* During parsing, we must use thd->mem_root */
   MEM_ROOT *root= thd->mem_root;
 
@@ -509,17 +548,20 @@
                                (char *)m_param_begin, m_params.length);
   }
 
-  m_body.length= lex->ptr - m_body_begin;
-  /* Trim nuls at the end */
-  n= 0;
-  while (m_body.length && m_body_begin[m_body.length-1] == '\0')
-  {
-    m_body.length-= 1;
-    n+= 1;
-  }
+  /* If ptr has overrun end_of_query then end_of_query is the end */
+  endp= (lex->ptr > lex->end_of_query ? lex->end_of_query : lex->ptr);
+  /*
+    Trim "garbage" at the end. This is sometimes needed with the
+    "/ * ! VERSION... * /" wrapper in dump files.
+  */
+  while (m_body_begin < endp &&
+         (endp[-1] <= ' ' || endp[-1] == '*' ||
+          endp[-1] == '/' || endp[-1] == ';'))
+    endp-= 1;
+
+  m_body.length= endp - m_body_begin;
   m_body.str= strmake_root(root, (char *)m_body_begin, m_body.length);
-  m_defstr.length= lex->ptr - lex->buf;
-  m_defstr.length-= n;
+  m_defstr.length= endp - lex->buf;
   m_defstr.str= strmake_root(root, (char *)lex->buf, m_defstr.length);
   DBUG_VOID_RETURN;
 }
@@ -2873,33 +2915,34 @@
   DBUG_ENTER("sp_head::add_used_tables_to_table_list");
 
   /*
-    Use persistent arena for table list allocation to be PS friendly.
+    Use persistent arena for table list allocation to be PS/SP friendly.
+    Note that we also have to copy database/table names and alias to PS/SP
+    memory since current instance of sp_head object can pass away before
+    next execution of PS/SP for which tables are added to prelocking list.
+    This will be fixed by introducing of proper invalidation mechanism
+    once new TDC is ready.
   */
   arena= thd->activate_stmt_arena_if_needed(&backup);
 
   for (i=0 ; i < m_sptabs.records ; i++)
   {
-    char *tab_buff;
+    char *tab_buff, *key_buff;
     TABLE_LIST *table;
     SP_TABLE *stab= (SP_TABLE *)hash_element(&m_sptabs, i);
     if (stab->temp)
       continue;
 
     if (!(tab_buff= (char *)thd->calloc(ALIGN_SIZE(sizeof(TABLE_LIST)) *
-                                        stab->lock_count)))
+                                        stab->lock_count)) ||
+        !(key_buff= (char*)thd->memdup(stab->qname.str,
+                                       stab->qname.length + 1)))
       DBUG_RETURN(FALSE);
 
     for (uint j= 0; j < stab->lock_count; j++)
     {
       table= (TABLE_LIST *)tab_buff;
 
-      /*
-        It's enough to just copy the pointers as the data will not change
-        during the lifetime of the SP. If the SP is used by PS, we assume
-        that the PS will be invalidated if the functions is deleted or
-        changed.
-      */
-      table->db= stab->qname.str;
+      table->db= key_buff;
       table->db_length= stab->db_length;
       table->table_name= table->db + table->db_length + 1;
       table->table_name_length= stab->table_name_length;

--- 1.92/sql/slave.h	2005-11-04 21:09:55 +01:00
+++ 1.93/sql/slave.h	2005-11-19 16:33:23 +01:00
@@ -14,19 +14,23 @@
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
 
-#ifdef HAVE_REPLICATION
-
 #ifndef SLAVE_H
 #define SLAVE_H
 
+#ifdef HAVE_REPLICATION
+
 #include "mysql.h"
 #include "my_list.h"
 #include "rpl_filter.h"
+#include "rpl_tblmap.h"
 
 #define SLAVE_NET_TIMEOUT  3600
 #define MAX_SLAVE_ERRMSG   1024
 #define MAX_SLAVE_ERROR    2000
 
+/* Forward declarations */
+class table_mapping;
+
 /*****************************************************************************
 
   MySQL Replication
@@ -357,6 +361,9 @@
     return ((until_condition == UNTIL_MASTER_POS) ? group_master_log_pos :
 	    group_relay_log_pos);
   }
+
+  table_mapping m_table_map;
+
   /*
     Last charset (6 bytes) seen by slave SQL thread is cached here; it helps
     the thread save 3 get_charset() per Query_log_event if the charset is not
@@ -365,6 +372,24 @@
   */
   void cached_charset_invalidate();
   bool cached_charset_compare(char *charset);
+
+  /*
+    To reload special tables when they are changes, we introduce a set
+    of functions that will mark whenever special functions need to be
+    called after modifying tables.  Right now, the tables are either
+    ACL tables or grants tables.
+  */
+  enum enum_reload_flag
+  {
+    RELOAD_NONE_F   = 0UL,
+    RELOAD_GRANT_F  = (1UL << 0), 
+    RELOAD_ACCESS_F = (1UL << 1)
+  };
+
+  ulong m_reload_flags;
+
+  void touching_table(char const* db, char const* table, ulong table_id);
+  void transaction_end(THD*);
 } RELAY_LOG_INFO;
 
 
@@ -481,10 +506,6 @@
 #define IO_RPL_LOG_NAME (mi->master_log_name[0] ? mi->master_log_name :\
  "FIRST")
 
-/* masks for start/stop operations on io and sql slave threads */
-#define SLAVE_IO  1
-#define SLAVE_SQL 2
-
 /*
   If the following is set, if first gives an error, second will be
   tried. Otherwise, if first fails, we fail.
@@ -579,8 +600,12 @@
        
 extern I_List<THD> threads;
 
-#endif
-#else
+#endif /* HAVE_REPLICATION */
+
+/* masks for start/stop operations on io and sql slave threads */
 #define SLAVE_IO  1
 #define SLAVE_SQL 2
-#endif /* HAVE_REPLICATION */
+
+#endif
+
+

--- 1.108/BitKeeper/etc/gone	2005-05-31 19:59:44 +02:00
+++ 1.109/BitKeeper/etc/gone	2005-11-19 16:12:25 +01:00
@@ -455,9 +455,14 @@
 arjen@stripped|scripts/mysql_fix_extensions.sh|20020516001337|12363|f1048a78f4759b4d
 ccarkner@stripped|mysql-test/r/isolation.result|20010327145543|25059|4da11e109a3d93a9
 ccarkner@stripped|mysql-test/t/isolation.test|20010327145543|39049|6a39e4138dd4a456
+dlenev@stripped|mysql-test/std_data/Moscow_leap|20041103173923|53231|472e28419723fbf9
+dlenev@stripped|mysql-test/std_data/loaddata5.dat|20050316013242|11110|5b34de3eb99facb9
 fs
+gbichot@stripped|mysql-test/std_data/rpl_timezone.dat|20050324154340|31361|8173bf0298d07b73
+guilhem@stripped|mysql-test/std_data/trunc_binlog.001|20031003201250|32063|b504d840c7efde25
 jani@stripped|client/mysqlcheck|20010419221207|26716|363e3278166d84ec
 jcole@stripped|BitKeeper/etc/logging_ok|20001004201211|30554
+jimw@stripped|mysql-test/std_data/loaddata5.dat|20050622231411|15283|5a8ab25a784a0fd3
 jimw@stripped|mysql-test/t/ndb_alter_table.disabled|20050311230559|27526|411e026940e7a0aa
 jimw@stripped|mysql-test/t/ndb_alter_table.disabled|20050311230559|27526|411e026940e7a0aajimw@stripped|mysql-test/t/ndb_autodiscover2.disabled|20050311230559|22363|afa8e5b6e46a3ea1jimw@stripped|mysql-test/t/ndb_autodiscover.disabled|20050311230559|58101|dda20d04dddbb06jimw@stripped|mysql-test/t/ndb_cache_multi2.disabled|20050311230600|47901|84fed1a78c0d3e6djimw@stripped|mysql-test/t/ndb_cache_multi.disabled|20050311230600|18039|9657b6eff7deb27ajimw@stripped|mysql-test/t/ndb_multi.disabled|20050311230600|12240|2599367ad06100f6jimw@stripped|mysql-test/t/ndb_restore.disabled|20050311230600|30718|3c2453d6164b1a30jimw@stripped|mysql-test/t/ndb_alter_table.disabled|20050311230559|27526|411e026940e7a0aajimw@stripped|mysql-test/t/ndb_autodiscover2.disabled|20050311230559|22363|afa8e5b6e46a3ea1jimw@stripped|mysql-test/t/ndb_autodiscover.disabled|20050311230559|58101|dda20d04dddbb06jimw@stripped|mysql-test/t/ndb_cache_multi2.disabled|20050311230600|47901|84fed1a78c0d3e6dji!
 mw@stripped|mysql-test/t/ndb_cache_multi.disabled|20050311230600|18039|9657b6eff7deb27ajimw@stripped|mysql-test/t/ndb_multi.disabled|20050311230600|12240|2599367ad06100f6jimw@stripped|mysql-test/t/ndb_restore.disabled|20050311230600|30718|3c2453d6164b1a30magnus@neptunus.(none)|ndb/src/client/Makefile|20040414084436|02010|6c2778d2bf4954a2
 jimw@stripped|mysql-test/t/ndb_autodiscover.disabled|20050311230559|58101|dda20d04dddbb06
@@ -466,6 +471,7 @@
 jimw@stripped|mysql-test/t/ndb_cache_multi2.disabled|20050311230600|47901|84fed1a78c0d3e6d
 jimw@stripped|mysql-test/t/ndb_multi.disabled|20050311230600|12240|2599367ad06100f6
 jimw@stripped|mysql-test/t/ndb_restore.disabled|20050311230600|30718|3c2453d6164b1a30
+jonas@stripped|mysql-test/std_data/ndb_config_mycnf1.cnf|20050930101913|11601|21edd8026a187d3
 korbit-kernel-2.4.1
 magnus@neptunus.(none)|ndb/src/client/Makefile|20040414084436|02010|6c2778d2bf4954a2
 magnus@neptunus.(none)|ndb/src/client/odbc/Extra.mk|20040414082358|47442|eabbb28986ca817d
@@ -893,6 +899,7 @@
 magnus@neptunus.(none)|ndb/tools/ndbnet/ndbnet.pl|20040414082442|59371|8f8f1b18f0458269
 magnus@neptunus.(none)|ndb/tools/ndbnet/ndbnetd.pl|20040414082442|62363|e44f1cfe7e8bbe14
 magnus@neptunus.(none)|ndb/tools/ndbnet/ndbrun|20040414082442|65454|5db549d7436ac81b
+mats@stripped|mysql-test/std_data/loaddata_pair.dat|20050705115550|00828|625b4e01749b4e14
 miguel@stripped|zlib/ChangeLog|20020319032513|28917|5d5425fc84737083
 miguel@stripped|zlib/Make_vms.com|20020319032513|57151|35050a50ec612bbf
 miguel@stripped|zlib/Makefile.riscos|20020319032513|63798|8ab53f195fe429af
@@ -1037,6 +1044,7 @@
 monty@stripped|sql-bench/Results/wisconsin-pg-Linux_2.2.14_my_SMP_i686-cmp-mysql,pg|20000817132749|24748|6a468dcd3e6f5405
 monty@stripped|sql-bench/Results/wisconsin-pg_fast-Linux_2.2.14_my_SMP_i686-cmp-mysql,pg|20000817132749|25437|24a02e007a58bf73
 monty@stripped|sql/violite.c|20010523223654|08838|53d4251a69d3c
+monty@stripped|mysql-test/std_data/des_key_file|20011213135317|16242|8fff791353ac642a
 monty@stripped|sql-bench/Results/ATIS-mysql-Linux_2.4.0_64GB_SMP_i686-cmp-mysql,pg|20010605132759|32241|dd306b2e583ebde4
 monty@stripped|sql-bench/Results/ATIS-pg-Linux_2.4.0_64GB_SMP_i686-cmp-mysql,pg|20010603134548|59551|d002b0bc548ff8b3
 monty@stripped|sql-bench/Results/RUN-mysql-Linux_2.4.0_64GB_SMP_i686-cmp-mysql,pg|20010605132759|35759|11038a44f73070e7
@@ -1056,6 +1064,10 @@
 monty@stripped|sql-bench/Results/wisconsin-mysql-Linux_2.4.0_64GB_SMP_i686-cmp-mysql,pg|20010605132759|60398|8ba598d217450157
 monty@stripped|sql-bench/Results/wisconsin-pg-Linux_2.4.0_64GB_SMP_i686-cmp-mysql,pg|20010603134548|23386|1ed1dc6abd24e7e3
 monty@stripped|support-files/make_mysql_pkg.sh|20010915122456|03682|c616a18bed4b9c2
+monty@stripped|mysql-test/std_data/rpl_loaddata.dat|20030114092724|06478|b17e0f9a81718223
+monty@stripped|mysql-test/std_data/loaddata3.dat|20031214043950|22953|5d5c62868866058a
+monty@stripped|mysql-test/std_data/loaddata4.dat|20031214043950|35108|6f198e2fc9d738f1
+monty@stripped|mysql-test/std_data/vchar.frm|20041207134656|46682|3d600738f1afb8a
 monty@stripped|sql-bench/Results/ATIS-mysql-Linux_2.2.14_my_SMP_i686-cmp-mysql,pg|20000817171625|04677|f761da5546f0d362
 monty@stripped|sql-bench/Results/ATIS-mysql_fast-Linux_2.2.14_my_SMP_i686-cmp-mysql,pg|20000817171625|07879|2ac8fe298953d43
 monty@stripped|sql-bench/Results/RUN-mysql-Linux_2.2.14_my_SMP_i686-cmp-mysql,pg|20000817171625|09727|79ac0482599eace1
@@ -1074,6 +1086,8 @@
 monty@stripped|sql-bench/Results/select-mysql_fast-Linux_2.2.14_my_SMP_i686-cmp-mysql,pg|20000817171625|35818|34a39fbcb58d8945
 monty@stripped|sql-bench/Results/wisconsin-mysql-Linux_2.2.14_my_SMP_i686-cmp-mysql,pg|20000817171625|37931|2db07249379f36
 monty@stripped|sql-bench/Results/wisconsin-mysql_fast-Linux_2.2.14_my_SMP_i686-cmp-mysql,pg|20000817171625|40155|8101a5823c17e58a
+monty@stripped|mysql-test/std_data/loaddata1.dat|20030505115238|56289|32e70ca86aca4b52
+monty@stripped|mysql-test/std_data/loaddata2.dat|20030822010739|00985|bf192c5d3ad528b8
 monty@stripped|sql-bench/Results/ATIS-mysql-Linux_2.2.13_SMP_alpha|20001014001004|08145|21ddf9425cbdd58
 monty@stripped|sql-bench/Results/ATIS-mysql-Linux_2.2.14_my_SMP_i686|20001218015322|06287|d275df58a04737c8
 monty@stripped|sql-bench/Results/RUN-mysql-Linux_2.2.13_SMP_alpha|20001014001004|13092|583091e05a25fb6
@@ -1186,10 +1200,16 @@
 mwagner@stripped|mysql-test/r/3.23/sel000002.result|20001010091454|29230|d1787e6fd5dbc1cc
 mysql-test/t/reserved_win_names-master.opt
 ndb/src/client/Makefile
+ndbdev@stripped|mysql-test/std_data/rpl_timezone2.dat|20050808185126|21230|ad8d5bb373130aa
+ndbdev@stripped|mysql-test/std_data/words2.dat|20050803193007|62032|290d0426e57dc00
+nick@stripped|mysql-test/std_data/master-bin.000001|20021029221112|31643|dc591a9d28fdafa7
 nick@stripped|mysql-test/r/rpl_empty_master_crash.result|20020531235552|47718|615f521be2132141
 nick@stripped|mysql-test/t/rpl_empty_master_crash.test|20020531235552|52328|99464e737639ccc6
 reggie@mdk10.(none)|BitKeeper/deleted/.del-reserved_win_names-master.opt~e56da049a7ce9a5b|20050523193219|41081
 reggie@mdk10.(none)|mysql-test/t/reserved_win_names-master.opt|20050520210356|14878|e56da049a7ce9a5b
+rpl_row_err_ignoredtable.test
+rpl_row_loaddata_m.test
+rpl_row_multi_query.test
 sasha@stripped|BitKeeper/etc/logging_ok|20000801000905|12967|5b7d847a2158554
 sasha@stripped|build-tags|20011125054855|05181|7afb7e785b80f97
 sasha@stripped|build-tags|20011201050944|25384|b6f6fff142121618
@@ -1229,6 +1249,7 @@
 sasha@stripped|mysql-test/std_data/m.frm|20001212152450|13897|e351dfe0b6824c0c
 sasha@stripped|mysql-test/std_data/select-key.master|20001009234916|07315|e6b83af25df0ce5
 sasha@stripped|mysql-test/std_data/simple-select.master|20001009234916|08299|6f3eb98812926caf
+sasha@stripped|mysql-test/std_data/words|20001118063528|58962|5673bdeb9cb72ccd
 sasha@stripped|mysql-test/t/3.23/alt000001.test|20001122072330|31588|633aed61c4bad94c
 sasha@stripped|mysql-test/t/3.23/sel000004.test|20001103140433|32471|daf9ad4a1a31cd3c
 sasha@stripped|mysql-test/t/3.23/sel000005.test|20001103140433|36002|982fde89a4d6d886
@@ -2333,4 +2354,8 @@
 tim@stripped|bdb/test/include.tcl|20010305004141|34016|20fc297b040cbe2
 tim@stripped|bdb/test/logtrack.list|20010305004142|05743|7f4f1382b37d98e5
 tim@stripped|bdb/txn/txn_auto.c|20010305004143|19863|6eb282f016f606d9
+tomas@stripped|mysql-test/std_data/ndb_config_mycnf2.cnf|20051012102722|02391|528e93dc96bf223e
 tonu@stripped|include/vio.h|20010520213124|42404|c62fd2b86c03da7d
+venu@stripped|mysql-test/std_data/warnings_loaddata.dat|20030422075410|56080|6a1ffa66643fe700
+vva@stripped|mysql-test/std_data/rpl_loaddata2.dat|20030403181907|29985|54a1a348d116a28b
+vva@stripped|mysql-test/t/init_file.sql|20040204173502|29287|5a9fbe1a6c7be7ac

--- 1.65/mysql-test/Makefile.am	2005-10-26 19:57:30 +02:00
+++ 1.66/mysql-test/Makefile.am	2005-11-19 16:33:22 +01:00
@@ -17,19 +17,10 @@
 
 ## Process this file with automake to create Makefile.in
 
-if HAVE_NDBCLUSTER_DB
 SUBDIRS = ndb
 DIST_SUBDIRS=ndb
-USE_NDBCLUSTER=\"--ndbcluster\"
-else
-# If one uses automake conditionals, automake will automatically
-# include all possible branches to DIST_SUBDIRS goal.
-# Reset DIST_SUBDIRS if we don't use NDB
-SUBDIRS=
-DIST_SUBDIRS=
-USE_NDBCLUSTER=\"\"
-endif
 
+# This should not be pushed into the main 5.1
 if HAVE_ROW_BASED_REPLICATION
 EXTRA_MASTER_MYSQLD_OPT=\"--binlog-format=row\"
 EXTRA_SLAVE_MYSQLD_OPT=\"--binlog-format=row\"
@@ -149,7 +140,7 @@
 	  -e 's!@''MYSQL_TCP_PORT''@!@MYSQL_TCP_PORT@!' \
 	  -e 's!@''MYSQL_NO_DASH_VERSION''@!@MYSQL_NO_DASH_VERSION@!' \
 	  -e 's!@''MYSQL_SERVER_SUFFIX''@!@MYSQL_SERVER_SUFFIX@!' \
-	  -e 's!@''USE_NDBCLUSTER''@!$(USE_NDBCLUSTER)!g' \
+	  -e 's!@''USE_NDBCLUSTER''@!@TEST_NDBCLUSTER@!g' \
 	  -e 's!@''EXTRA_MASTER_MYSQLD_OPT''@!$(EXTRA_MASTER_MYSQLD_OPT)!g' \
 	  -e 's!@''EXTRA_SLAVE_MYSQLD_OPT''@!$(EXTRA_SLAVE_MYSQLD_OPT)!g' \
 	  $< > $@-t

--- 1.39/sql/sql_repl.h	2005-03-22 10:04:18 +01:00
+++ 1.40/sql/sql_repl.h	2005-11-19 16:14:07 +01:00
@@ -14,6 +14,8 @@
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
 
+#include "rpl_filter.h"
+
 #ifdef HAVE_REPLICATION
 #include "slave.h"
 

--- 1.53/BUILD/SETUP.sh	2005-11-04 10:43:44 +01:00
+++ 1.54/BUILD/SETUP.sh	2005-11-19 16:14:04 +01:00
@@ -56,9 +56,9 @@
 #debug_extra_warnings="-Wuninitialized"
 c_warnings="$global_warnings -Wunused"
 cxx_warnings="$global_warnings -Woverloaded-virtual -Wsign-promo -Wreorder
-Wctor-dtor-privacy -Wnon-virtual-dtor"
-base_max_configs="--with-innodb --with-berkeley-db --with-ndbcluster
--with-archive-storage-engine --with-big-tables --with-blackhole-storage-engine
--with-federated-storage-engine --with-csv-storage-engine $SSL_LIBRARY"
+base_max_configs="--with-innodb --with-berkeley-db --with-ndbcluster
--with-archive-storage-engine --with-big-tables --with-blackhole-storage-engine
--with-federated-storage-engine --with-csv-storage-engine --with-partition $SSL_LIBRARY"
 base_max_no_ndb_configs="--with-innodb --with-berkeley-db --without-ndbcluster
--with-archive-storage-engine --with-big-tables --with-blackhole-storage-engine
--with-federated-storage-engine --with-csv-storage-engine $SSL_LIBRARY"
-max_leave_isam_configs="--with-innodb --with-berkeley-db --with-ndbcluster
--with-archive-storage-engine --with-federated-storage-engine
--with-blackhole-storage-engine --with-csv-storage-engine $SSL_LIBRARY
--with-embedded-server --with-big-tables"
+max_leave_isam_configs="--with-innodb --with-berkeley-db --with-ndbcluster
--with-archive-storage-engine --with-federated-storage-engine
--with-blackhole-storage-engine --with-csv-storage-engine --with-embedded-server
--with-big-tables --with-partition $SSL_LIBRARY"
 max_configs="$base_max_configs --with-embedded-server --with-row-based-replication"
 max_no_ndb_configs="$base_max_no_ndb_configs --with-embedded-server
--with-row-based-replication"
 
Thread
bk commit into 5.1 tree (guilhem:1.1966)guilhem19 Nov