Uploaded image for project: 'MariaDB Server'
  1. MariaDB Server
  2. MDEV-40872

DROP TEMPORARY TABLE is binlogged with a qualified dB name that rewrite cannot follow

    XMLWordPrintable

Details

    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
      
      

      Attachments

        Activity

          People

            Unassigned Unassigned
            Deepthi ES Deepthi Eranti Sreenivas
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

            Dates

              Created:
              Updated:

              Git Integration

                Error rendering 'com.xiplink.jira.git.jira_git_plugin:git-issue-webpanel'. Please contact your Jira administrators.