From 279529c11c124918c2c5b82b66c00c01c5339ade Mon Sep 17 00:00:00 2001
From: unknown <tnurnberg@sin.intern.azundris.com>
Date: Mon, 25 Jun 2007 11:34:23 +0200
Subject: [PATCH] Bug #22540: Incorrect value in column End_log_pos of SHOW
 BINLOG EVENTS using InnoDB

fix binlog-writing so that end_log_pos is given correctly even
within transactions for both SHOW BINLOG and SHOW MASTER STATUS,
that is as absolute values (from log start) rather than relative
values (from transaction's start).
---
Merge tnurnberg@bk-internal.mysql.com:/home/bk/mysql-5.0-maint
into  sin.intern.azundris.com:/home/tnurnberg/22540/50-22540


mysql-test/r/binlog.result:
  Bug #22540: Incorrect value in column End_log_pos of SHOW BINLOG EVENTS using InnoDB

  show that end_log_pos in SHOW BINLOG EVENTS is correct even in transactions.
  show that SHOW MASTER STATUS returns correct values while in transactions
  (so that mysqldump --master-data will work correctly).
  also remove bdb dependency.
  ---
  manual merge
mysql-test/t/binlog.test:
  Bug #22540: Incorrect value in column End_log_pos of SHOW BINLOG EVENTS using InnoDB

  show that end_log_pos in SHOW BINLOG EVENTS is correct even in transactions.
  show that SHOW MASTER STATUS returns correct values while in transactions
  (so that mysqldump --master-data will work correctly).
  also remove bdb dependency.
sql/log.cc:
  Bug #22540: Incorrect value in column End_log_pos of SHOW BINLOG EVENTS using InnoDB

  fix output for SHOW BINLOG EVENTS so that end_log_pos is given correctly
  even within transactions. do this by rewriting the commit-buffer in place.
---
 mysql-test/r/binlog.result | 122 ++++++++++++++++++++++++++++++++++++-
 mysql-test/t/binlog.test   |  93 +++++++++++++++++++++++++++-
 sql/log.cc                 |  63 +++++++++++++++++--
 3 files changed, 269 insertions(+), 9 deletions(-)

diff --git a/mysql-test/r/binlog.result b/mysql-test/r/binlog.result
index 6807da16e6..41c75a2067 100644
--- a/mysql-test/r/binlog.result
+++ b/mysql-test/r/binlog.result
@@ -1,6 +1,6 @@
 drop table if exists t1, t2;
 reset master;
-create table t1 (a int) engine=bdb;
+create table t1 (a int) engine=innodb;
 create table t2 (a int) engine=innodb;
 begin;
 insert t1 values (5);
@@ -10,11 +10,11 @@ insert t2 values (5);
 commit;
 show binlog events from 98;
 Log_name	Pos	Event_type	Server_id	End_log_pos	Info
-master-bin.000001	#	Query	1	#	use `test`; create table t1 (a int) engine=bdb
+master-bin.000001	#	Query	1	#	use `test`; create table t1 (a int) engine=innodb
 master-bin.000001	#	Query	1	#	use `test`; create table t2 (a int) engine=innodb
 master-bin.000001	#	Query	1	#	use `test`; BEGIN
 master-bin.000001	#	Query	1	#	use `test`; insert t1 values (5)
-master-bin.000001	#	Query	1	#	use `test`; COMMIT
+master-bin.000001	#	Xid	1	#	COMMIT /* XID */
 master-bin.000001	#	Query	1	#	use `test`; BEGIN
 master-bin.000001	#	Query	1	#	use `test`; insert t2 values (5)
 master-bin.000001	#	Xid	1	#	COMMIT /* XID */
@@ -133,3 +133,119 @@ master-bin.000001	#	Rotate	1	#	master-bin.000002;pos=4
 show binlog events in 'master-bin.000002' from 98;
 Log_name	Pos	Event_type	Server_id	End_log_pos	Info
 master-bin.000002	#	Query	1	#	use `test`; drop table t1
+set @ac = @@autocommit;
+set autocommit= 0;
+reset master;
+create table t1(n int) engine=innodb;
+begin;
+insert into t1 values (1);
+insert into t1 values (2);
+insert into t1 values (3);
+commit;
+drop table t1;
+show binlog events from 0;
+Log_name	Pos	Event_type	Server_id	End_log_pos	Info
+master-bin.000001	4	Format_desc	1	98	Server version, Binlog ver: 4
+master-bin.000001	98	Query	1	197	use `test`; create table t1(n int) engine=innodb
+master-bin.000001	197	Query	1	265	use `test`; BEGIN
+master-bin.000001	265	Query	1	353	use `test`; insert into t1 values (1)
+master-bin.000001	353	Query	1	441	use `test`; insert into t1 values (2)
+master-bin.000001	441	Query	1	529	use `test`; insert into t1 values (3)
+master-bin.000001	529	Xid	1	556	COMMIT /* XID */
+master-bin.000001	556	Query	1	632	use `test`; drop table t1
+set autocommit= 1;
+reset master;
+create table t1(n int) engine=innodb;
+insert into t1 values (1);
+insert into t1 values (2);
+insert into t1 values (3);
+commit;
+drop table t1;
+show binlog events from 0;
+Log_name	Pos	Event_type	Server_id	End_log_pos	Info
+master-bin.000001	4	Format_desc	1	98	Server version, Binlog ver: 4
+master-bin.000001	98	Query	1	197	use `test`; create table t1(n int) engine=innodb
+master-bin.000001	197	Query	1	285	use `test`; insert into t1 values (1)
+master-bin.000001	285	Xid	1	312	COMMIT /* XID */
+master-bin.000001	312	Query	1	400	use `test`; insert into t1 values (2)
+master-bin.000001	400	Xid	1	427	COMMIT /* XID */
+master-bin.000001	427	Query	1	515	use `test`; insert into t1 values (3)
+master-bin.000001	515	Xid	1	542	COMMIT /* XID */
+master-bin.000001	542	Query	1	618	use `test`; drop table t1
+reset master;
+create table t1(n int) engine=myisam;
+begin;
+insert into t1 values (4);
+insert into t1 values (5);
+insert into t1 values (6);
+commit;
+drop table t1;
+show binlog events from 0;
+Log_name	Pos	Event_type	Server_id	End_log_pos	Info
+master-bin.000001	4	Format_desc	1	98	Server version, Binlog ver: 4
+master-bin.000001	98	Query	1	197	use `test`; create table t1(n int) engine=myisam
+master-bin.000001	197	Query	1	285	use `test`; insert into t1 values (4)
+master-bin.000001	285	Query	1	373	use `test`; insert into t1 values (5)
+master-bin.000001	373	Query	1	461	use `test`; insert into t1 values (6)
+master-bin.000001	461	Query	1	537	use `test`; drop table t1
+set autocommit= 1;
+reset master;
+create table t1(n int) engine=innodb;
+show master status;
+File	Position	Binlog_Do_DB	Binlog_Ignore_DB
+master-bin.000001	197		
+insert into t1 values (1);
+show master status;
+File	Position	Binlog_Do_DB	Binlog_Ignore_DB
+master-bin.000001	312		
+insert into t1 values (2);
+insert into t1 values (3);
+show master status;
+File	Position	Binlog_Do_DB	Binlog_Ignore_DB
+master-bin.000001	542		
+commit;
+show master status;
+File	Position	Binlog_Do_DB	Binlog_Ignore_DB
+master-bin.000001	542		
+drop table t1;
+show binlog events from 0;
+Log_name	Pos	Event_type	Server_id	End_log_pos	Info
+master-bin.000001	4	Format_desc	1	98	Server version, Binlog ver: 4
+master-bin.000001	98	Query	1	197	use `test`; create table t1(n int) engine=innodb
+master-bin.000001	197	Query	1	285	use `test`; insert into t1 values (1)
+master-bin.000001	285	Xid	1	312	COMMIT /* XID */
+master-bin.000001	312	Query	1	400	use `test`; insert into t1 values (2)
+master-bin.000001	400	Xid	1	427	COMMIT /* XID */
+master-bin.000001	427	Query	1	515	use `test`; insert into t1 values (3)
+master-bin.000001	515	Xid	1	542	COMMIT /* XID */
+master-bin.000001	542	Query	1	618	use `test`; drop table t1
+set autocommit= 0;
+reset master;
+create table t1(n int) engine=myisam;
+show master status;
+File	Position	Binlog_Do_DB	Binlog_Ignore_DB
+master-bin.000001	197		
+insert into t1 values (4);
+show master status;
+File	Position	Binlog_Do_DB	Binlog_Ignore_DB
+master-bin.000001	285		
+insert into t1 values (5);
+insert into t1 values (6);
+show master status;
+File	Position	Binlog_Do_DB	Binlog_Ignore_DB
+master-bin.000001	461		
+commit;
+show master status;
+File	Position	Binlog_Do_DB	Binlog_Ignore_DB
+master-bin.000001	461		
+drop table t1;
+show binlog events from 0;
+Log_name	Pos	Event_type	Server_id	End_log_pos	Info
+master-bin.000001	4	Format_desc	1	98	Server version, Binlog ver: 4
+master-bin.000001	98	Query	1	197	use `test`; create table t1(n int) engine=myisam
+master-bin.000001	197	Query	1	285	use `test`; insert into t1 values (4)
+master-bin.000001	285	Query	1	373	use `test`; insert into t1 values (5)
+master-bin.000001	373	Query	1	461	use `test`; insert into t1 values (6)
+master-bin.000001	461	Query	1	537	use `test`; drop table t1
+set session autocommit = @ac;
+End of 5.0 tests
diff --git a/mysql-test/t/binlog.test b/mysql-test/t/binlog.test
index accfa6a577..6271a8f95a 100644
--- a/mysql-test/t/binlog.test
+++ b/mysql-test/t/binlog.test
@@ -3,7 +3,6 @@
 #
 -- source include/have_log_bin.inc
 -- source include/not_embedded.inc
--- source include/have_bdb.inc
 -- source include/have_innodb.inc
 -- source include/have_log_bin.inc
 
@@ -12,7 +11,7 @@ drop table if exists t1, t2;
 --enable_warnings
 reset master;
 
-create table t1 (a int) engine=bdb;
+create table t1 (a int) engine=innodb;
 create table t2 (a int) engine=innodb;
 begin;
 insert t1 values (5);
@@ -49,3 +48,93 @@ show binlog events in 'master-bin.000001' from 98;
 --replace_column 2 # 5 #
 show binlog events in 'master-bin.000002' from 98;
 
+#
+# Bug#22540 - Incorrect value in column End_log_pos of
+# SHOW BINLOG EVENTS using InnoDB
+#
+
+# the following tests will show that certain queries now return
+# absolute offsets (from binlog start, rather than relative to
+# the beginning of the current transaction).  under what
+# conditions it should be allowed / is sensible to put the
+# slider into the middle of a transaction is not our concern
+# here; we just guarantee that if and when it's done, the
+# user has valid offsets to use.  if the setter function still
+# wants to throw a "positioning into middle of transaction"
+# warning, that's its prerogative and handled elsewhere.
+
+set @ac = @@autocommit;
+
+# first show this to work for SHOW BINLOG EVENTS
+
+set autocommit= 0;
+reset master;
+create table t1(n int) engine=innodb;
+begin;
+insert into t1 values (1);
+insert into t1 values (2);
+insert into t1 values (3);
+commit;
+drop table t1;
+--replace_regex /\/\* xid=.* \*\//\/* XID *\// /table_id: [0-9]+/table_id: #/ /Server ver: [^,]*,/Server version,/
+show binlog events from 0;
+
+set autocommit= 1;
+reset master;
+create table t1(n int) engine=innodb;
+insert into t1 values (1);
+insert into t1 values (2);
+insert into t1 values (3);
+commit;
+drop table t1;
+--replace_regex /\/\* xid=.* \*\//\/* XID *\// /table_id: [0-9]+/table_id: #/ /Server ver: [^,]*,/Server version,/
+show binlog events from 0;
+
+reset master;
+create table t1(n int) engine=myisam;
+begin;
+insert into t1 values (4);
+insert into t1 values (5);
+insert into t1 values (6);
+commit;
+drop table t1;
+--replace_regex /\/\* xid=.* \*\//\/* XID *\// /table_id: [0-9]+/table_id: #/ /Server ver: [^,]*,/Server version,/
+show binlog events from 0;
+
+# now show this also works for SHOW MASTER STATUS
+# as this is what "mysqldump --master-data=1" uses.
+
+set autocommit= 1;
+reset master;
+create table t1(n int) engine=innodb;
+show master status;
+insert into t1 values (1);
+show master status;
+insert into t1 values (2);
+insert into t1 values (3);
+show master status;
+commit;
+show master status;
+drop table t1;
+--replace_regex /\/\* xid=.* \*\//\/* XID *\// /table_id: [0-9]+/table_id: #/ /Server ver: [^,]*,/Server version,/
+show binlog events from 0;
+
+set autocommit= 0;
+reset master;
+create table t1(n int) engine=myisam;
+show master status;
+insert into t1 values (4);
+show master status;
+insert into t1 values (5);
+insert into t1 values (6);
+
+show master status;
+commit;
+show master status;
+drop table t1;
+--replace_regex /\/\* xid=.* \*\//\/* XID *\// /table_id: [0-9]+/table_id: #/ /Server ver: [^,]*,/Server version,/
+show binlog events from 0;
+
+set session autocommit = @ac;
+
+--echo End of 5.0 tests
diff --git a/sql/log.cc b/sql/log.cc
index 818828f955..465d694ba1 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -134,7 +134,7 @@ static int binlog_commit(THD *thd, bool all)
     // we're here because trans_log was flushed in MYSQL_LOG::log_xid()
     DBUG_RETURN(0);
   }
-  if (all) 
+  if (all)
   {
     Query_log_event qev(thd, STRING_WITH_LEN("COMMIT"), TRUE, FALSE);
     qev.error_code= 0; // see comment in MYSQL_LOG::write(THD, IO_CACHE)
@@ -1835,7 +1835,9 @@ bool MYSQL_LOG::write(THD *thd, IO_CACHE *cache, Log_event *commit_event)
 
   if (likely(is_open()))                       // Should always be true
   {
-    uint length;
+    uint length, group, carry, hdr_offs;
+    long val;
+    byte header[LOG_EVENT_HEADER_LEN];
 
     /*
       Log "BEGIN" at the beginning of the transaction.
@@ -1867,13 +1869,66 @@ bool MYSQL_LOG::write(THD *thd, IO_CACHE *cache, Log_event *commit_event)
     /* Read from the file used to cache the queries .*/
     if (reinit_io_cache(cache, READ_CACHE, 0, 0, 0))
       goto err;
-    length=my_b_bytes_in_cache(cache);
+
+    length= my_b_bytes_in_cache(cache);
     DBUG_EXECUTE_IF("half_binlogged_transaction", length-=100;);
+
+    group= my_b_tell(&log_file);
+    hdr_offs= carry= 0;
+
     do
     {
+      if (likely(carry > 0))
+      {
+        DBUG_ASSERT(carry < LOG_EVENT_HEADER_LEN);
+
+        memcpy(&header[carry], (char *)cache->read_pos, LOG_EVENT_HEADER_LEN - carry);
+
+        val= uint4korr(&header[LOG_POS_OFFSET]) + group;
+        int4store(&header[LOG_POS_OFFSET], val);
+
+        if (my_b_write(&log_file, header, carry))
+          goto err;
+
+        memcpy((char *)cache->read_pos, &header[carry], LOG_EVENT_HEADER_LEN - carry);
+
+        hdr_offs = LOG_EVENT_HEADER_LEN - carry +
+          uint4korr(&header[EVENT_LEN_OFFSET]);
+
+        carry= 0;
+      }
+
+      if(likely(length > 0))
+      {
+        do {
+          DBUG_ASSERT((hdr_offs + max(EVENT_LEN_OFFSET, LOG_POS_OFFSET) + 4) <= length);
+
+          val= uint4korr((char *)cache->read_pos + hdr_offs + LOG_POS_OFFSET) + group;
+          int4store((char *)cache->read_pos + hdr_offs + LOG_POS_OFFSET, val);
+          hdr_offs += uint4korr((char *)cache->read_pos + hdr_offs + EVENT_LEN_OFFSET);
+
+          /* header beyond current read-buffer? */
+          if (hdr_offs >= length)
+          {
+            hdr_offs -= length;
+            break;
+          }
+
+          /* split header? */
+          if (hdr_offs + LOG_EVENT_HEADER_LEN > length)
+          {
+            carry= length - hdr_offs;
+
+            memcpy(header, (char *)cache->read_pos + hdr_offs, carry);
+            length -= carry;
+          }
+
+        } while (hdr_offs < length);
+      }
+
       /* Write data to the binary log file */
       if (my_b_write(&log_file, cache->read_pos, length))
-	goto err;
+        goto err;
       cache->read_pos=cache->read_end;		// Mark buffer used up
       DBUG_EXECUTE_IF("half_binlogged_transaction", goto DBUG_skip_commit;);
     } while ((length=my_b_fill(cache)));
-- 
2.30.9