Details
-
Bug
-
Status: Open (View Workflow)
-
Major
-
Resolution: Unresolved
-
None
-
None
Description
Under --replicate-rewrite-db, DROP TEMPORARY TABLE is binlogged with a qualified
database name that the rewrite cannot follow, leaking the table on the replica
forever
# Slave options: --replicate-ignore-db=ignoredb --replicate-rewrite-db=srcdb->dstdb |
--source include/have_innodb.inc
|
--source include/have_log_bin.inc
|
--source include/master-slave.inc
|
|
|
--echo #
|
--echo # ======== --replicate-ignore-db: the whole database is dropped on the slave
|
--echo #
|
--connection master
|
CREATE DATABASE ignoredb; |
USE ignoredb; |
SET SESSION binlog_format= STATEMENT; |
CREATE TABLE b (id INT PRIMARY KEY, v INT) ENGINE=InnoDB; |
CREATE TEMPORARY TABLE t (id INT PRIMARY KEY, v INT) ENGINE=InnoDB; |
INSERT INTO t VALUES (1,10),(2,20); |
UPDATE t SET v = v + 1; |
INSERT INTO b SELECT id, v FROM t; |
DROP TEMPORARY TABLE t; |
--sorted_result
|
SELECT * FROM b; |
|
|
--sync_slave_with_master
|
--source include/check_slave_no_error.inc
|
--connection slave
|
--echo # the slave must have ignored the database entirely, and stayed healthy
|
--let $has= `SELECT COUNT(*) FROM information_schema.SCHEMATA WHERE SCHEMA_NAME='ignoredb'`
|
--echo # slave has database ignoredb = $has [oracle: 0]
|
--let $assert_text= 8.5: an ignored database must not appear on the slave
|
--let $assert_cond= $has = 0
|
--source include/assert.inc
|
--let $assert_text= 8.5: no temporary tables left open after an ignored database
|
--let $assert_cond= [SHOW STATUS LIKE "Slave_open_temp_tables", Value, 1] = 0
|
--source include/assert.inc
|
|
|
--echo #
|
--echo # ======== --replicate-rewrite-db: srcdb on the master becomes dstdb on the slave
|
--echo #
|
--connection master
|
CREATE DATABASE srcdb; |
--connection slave
|
CREATE DATABASE dstdb; |
|
|
--connection master
|
USE srcdb; |
CREATE TABLE b (id INT PRIMARY KEY, v INT) ENGINE=InnoDB; |
--sync_slave_with_master
|
|
|
--echo # ---- a LOGGED temporary table in the rewritten database
|
--connection master
|
USE srcdb; |
SET SESSION binlog_format= STATEMENT; |
SET SESSION create_tmp_table_binlog_formats= 'STATEMENT'; |
CREATE TEMPORARY TABLE t (id INT PRIMARY KEY, v INT) ENGINE=InnoDB; |
INSERT INTO t VALUES (1,10),(2,20); |
UPDATE t SET v = v + 5; |
INSERT INTO b SELECT id, v FROM t; |
DROP TEMPORARY TABLE t; |
--sorted_result
|
SELECT * FROM b; |
|
|
--sync_slave_with_master
|
--source include/check_slave_no_error.inc
|
--connection slave
|
--echo # the rows must have landed in dstdb.b, not srcdb.b
|
USE dstdb; |
--sorted_result
|
SELECT * FROM b; |
--let $assert_text= 8.5: the rewritten database must hold the propagated rows
|
--let $assert_cond= [SELECT COUNT(*) AS c FROM dstdb.b, c, 1] = 2
|
--source include/assert.inc
|
--echo #
|
--echo # ======== FINDING: the temporary table LEAKS on the slave under rewrite-db
|
--echo #
|
--echo # CREATE is binlogged with the database in the "use" prefix:
|
--echo # use `srcdb`; CREATE TEMPORARY TABLE t ...
|
--echo # so --replicate-rewrite-db maps it and the slave creates the table in dstdb.
|
--echo #
|
--echo # DROP is binlogged fully qualified INSIDE the statement text:
|
--echo # DROP TEMPORARY TABLE IF EXISTS `srcdb`.`t` /* generated by server */
|
--echo # and rewrite-db rewrites the EVENT's database, not database names embedded
|
--echo # in statement text. So the slave runs the DROP against srcdb while the
|
--echo # table lives in dstdb. IF EXISTS then swallows the error, turning what
|
--echo # would be a loud failure into a SILENT LEAK: the temporary table stays open
|
--echo # on the applier for as long as the slave runs.
|
--echo #
|
--echo # Recorded, not asserted to zero -- the leak is the measured behaviour.
|
--echo #
|
--let $leaked= query_get_value(SHOW STATUS LIKE "Slave_open_temp_tables", Value, 1)
|
--echo # Slave_open_temp_tables after the rewrite = $leaked [would be 0 if the DROP had matched]
|
--let $assert_text= 8.5: the applier must at least still be running despite the leak
|
--let $assert_cond= "[SHOW SLAVE STATUS, Slave_SQL_Running, 1]" = "Yes"
|
--source include/assert.inc
|
|
|
--echo # ---- an UNLOGGED temporary table in the rewritten database
|
--echo # Nothing about the temp table is logged, so the propagation is
|
--echo # row-logged and must still land in dstdb.
|
--connection master
|
USE srcdb; |
SET SESSION binlog_format= MIXED; |
DELETE FROM b; |
CREATE TEMPORARY TABLE u (id INT PRIMARY KEY, v INT) ENGINE=InnoDB; |
INSERT INTO u VALUES (7,70); |
INSERT INTO b SELECT id, v FROM u; |
DROP TEMPORARY TABLE u; |
--sorted_result
|
SELECT * FROM b; |
|
|
--sync_slave_with_master
|
--source include/check_slave_no_error.inc
|
--connection slave
|
--sorted_result
|
SELECT * FROM dstdb.b; |
--let $assert_text= 8.5: the unlogged-temp propagation must also land in dstdb
|
--let $assert_cond= [SELECT COUNT(*) AS c FROM dstdb.b, c, 1] = 1
|
--source include/assert.inc
|
|
|
--echo # ---- the unlogged case does NOT leak: nothing about it was ever logged,
|
--echo # so there is no mismatched DROP to strand a table.
|
--let $leaked2= query_get_value(SHOW STATUS LIKE "Slave_open_temp_tables", Value, 1)
|
--echo # Slave_open_temp_tables after the unlogged case = $leaked2
|
|
|
--connection master
|
USE test; |
DROP DATABASE ignoredb; |
DROP DATABASE srcdb; |
--sync_slave_with_master
|
--connection slave
|
USE test; |
DROP DATABASE IF EXISTS dstdb; |
--connection master
|
--source include/rpl_end.inc
|
|
--source include/have_innodb.inc
|
--source include/have_log_bin.inc
|
--source include/master-slave.inc
|
|
|
--connection master
|
SET SESSION binlog_format= STATEMENT; |
CREATE TABLE base (id INT PRIMARY KEY, v INT) ENGINE=InnoDB; |
CREATE TEMPORARY TABLE t (id INT PRIMARY KEY, v INT) ENGINE=InnoDB; |
INSERT INTO t VALUES (1,10),(2,20); |
INSERT INTO base SELECT id, v FROM t; |
DROP TEMPORARY TABLE t; |
--sync_slave_with_master
|
|
|
--connection master
|
--let $binlog_file= query_get_value(SHOW MASTER STATUS, File, 1)
|
--let $datadir= `SELECT @@datadir`
|
|
|
--echo #
|
--echo # ======== mysqlbinlog must reproduce the generated DROP faithfully
|
--echo #
|
--exec $MYSQL_BINLOG $datadir/$binlog_file > $MYSQLTEST_VARDIR/tmp/ment2792_dump.sql
|
--echo # the DROP as mysqlbinlog renders it:
|
--exec grep -o "DROP TEMPORARY TABLE IF EXISTS.*" $MYSQLTEST_VARDIR/tmp/ment2792_dump.sql | head -1
|
|
|
--echo #
|
--echo # ======== mysqlbinlog --rewrite-db against the qualified DROP
|
--echo #
|
--echo # Case 8.5 showed the SERVER's --replicate-rewrite-db does not rewrite the
|
--echo # database inside the DROP's statement text. If mysqlbinlog --rewrite-db
|
--echo # has the same limitation, the obvious operator workaround for that leak
|
--echo # does not work either.
|
--echo #
|
--exec $MYSQL_BINLOG --rewrite-db='test->other' $datadir/$binlog_file > $MYSQLTEST_VARDIR/tmp/ment2792_rw.sql
|
--echo # the CREATE TEMPORARY line after rewriting (database comes from the USE prefix):
|
--exec grep -c "^use \`other\`" $MYSQLTEST_VARDIR/tmp/ment2792_rw.sql
|
--echo # does any DROP still name the ORIGINAL database inside the statement text?
|
--exec grep -c "DROP TEMPORARY TABLE IF EXISTS .test.\..t." $MYSQLTEST_VARDIR/tmp/ment2792_rw.sql
|
--echo # ==> 1 means mysqlbinlog --rewrite-db has the SAME limitation as the server:
|
--echo # it rewrites the USE prefix but not the database embedded in the DROP.
|
--echo # So the obvious operator workaround for the case 8.5 leak does not work.
|
--echo # the DROP as it appears AFTER rewriting:
|
--exec grep -o "DROP TEMPORARY TABLE IF EXISTS.*" $MYSQLTEST_VARDIR/tmp/ment2792_rw.sql | head -1
|
|
|
--echo #
|
--echo # ======== binlog_checksum variations
|
--echo #
|
--let $ck= 1
|
while ($ck <= 2)
|
{
|
if ($ck == 1) |
{
|
--let $cksum= CRC32 |
}
|
if ($ck == 2) |
{
|
--let $cksum= NONE |
}
|
--connection master |
--eval SET GLOBAL binlog_checksum= $cksum |
DELETE FROM base; |
CREATE TEMPORARY TABLE t (id INT PRIMARY KEY, v INT) ENGINE=InnoDB; |
INSERT INTO t VALUES (5,50); |
INSERT INTO base SELECT id, v FROM t; |
DROP TEMPORARY TABLE t; |
--sync_slave_with_master |
--source include/check_slave_no_error.inc |
--connection master |
--let $diff_tables= master:test.base, slave:test.base |
--source include/diff_tables.inc |
--echo # ==> converged with binlog_checksum=$cksum |
--inc $ck |
}
|
--connection master
|
SET GLOBAL binlog_checksum= DEFAULT; |
|
|
--remove_file $MYSQLTEST_VARDIR/tmp/ment2792_dump.sql
|
--remove_file $MYSQLTEST_VARDIR/tmp/ment2792_rw.sql
|
|
|
--connection master
|
DROP TABLE base; |
--sync_slave_with_master
|
--source include/rpl_end.inc
|
|