From 653f14c2659160b1fda7f3fceb0988fa90731d4d Mon Sep 17 00:00:00 2001
From: Magne Mahre <magne.mahre@sun.com>
Date: Wed, 6 Oct 2010 11:01:24 +0200
Subject: [PATCH] Bug#56452 Assertion failed: thd->transaction.stmt.is_empty()
 ||           thd->in_sub_stmt

In a precursor patch for Bug#52044
(revid:bzr/kostja@stripped), a
number of reorganizations of code was made. In addition some
assertions were added to ensure the correct transactional state.

The reorganization had a small glitch so statements that was
active in the query cache was not followed by a
statement commit/rollback (this code was removed). A section
in the trans_commit_stmt/trans_rollback_stmt code is to
clear the thd->transaction.stmt list of affected storage
engines.  When a new statement is initiated, an assert
introduced by the 523044 patch checks if this list is cleared.
When the query cache is accessed, this list may be populated,
and since it's not committed it will not be cleared.

This fix adds explicit statement commit or rollback for
statements that is contained in the query cache.
---
 mysql-test/r/cache_innodb.result | 11 +++++++++++
 mysql-test/t/cache_innodb.test   | 15 +++++++++++++++
 sql/sql_cache.cc                 | 10 ++++++++++
 3 files changed, 36 insertions(+)

diff --git a/mysql-test/r/cache_innodb.result b/mysql-test/r/cache_innodb.result
index 600ed84c3c..293d7a3f41 100644
--- a/mysql-test/r/cache_innodb.result
+++ b/mysql-test/r/cache_innodb.result
@@ -220,3 +220,14 @@ Variable_name	Value
 Qcache_hits	1
 set GLOBAL query_cache_size=1048576;
 drop table t2;
+CREATE TABLE t1 (a INT) ENGINE=InnoDB;
+BEGIN;
+INSERT INTO t1 VALUES(1);
+ROLLBACK WORK AND CHAIN NO RELEASE;
+SELECT a FROM t1;
+a
+ROLLBACK WORK AND CHAIN NO RELEASE;
+SELECT a FROM t1;
+a
+ROLLBACK;
+DROP TABLE t1;
diff --git a/mysql-test/t/cache_innodb.test b/mysql-test/t/cache_innodb.test
index 33a328d1d6..f710262750 100644
--- a/mysql-test/t/cache_innodb.test
+++ b/mysql-test/t/cache_innodb.test
@@ -14,3 +14,18 @@ let $engine_type= InnoDB;
 let $test_foreign_keys= 1;
 
 --source include/query_cache.inc
+
+#
+# Bug#56452 Assertion failed: thd->transaction.stmt.is_empty() || 
+#           thd->in_sub_stmt
+#
+CREATE TABLE t1 (a INT) ENGINE=InnoDB;
+BEGIN;
+  INSERT INTO t1 VALUES(1);
+ROLLBACK WORK AND CHAIN NO RELEASE;
+SELECT a FROM t1;
+ROLLBACK WORK AND CHAIN NO RELEASE;
+SELECT a FROM t1;
+ROLLBACK;
+DROP TABLE t1;
+
diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc
index b57c851edf..a10e470afd 100644
--- a/sql/sql_cache.cc
+++ b/sql/sql_cache.cc
@@ -341,6 +341,7 @@ TODO list:
 #include "../storage/myisammrg/ha_myisammrg.h"
 #include "../storage/myisammrg/myrg_def.h"
 #include "probes_mysql.h"
+#include "transaction.h"
 
 #ifdef EMBEDDED_LIBRARY
 #include "emb_qcache.h"
@@ -1683,6 +1684,8 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d",
       }
       else
         thd->lex->safe_to_cache_query= 0;       // Don't try to cache this
+      /* End the statement transaction potentially started by engine. */
+      trans_rollback_stmt(thd);
       goto err_unlock;				// Parse query
     }
     else
@@ -1724,6 +1727,13 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d",
 
   thd->limit_found_rows = query->found_rows();
   thd->status_var.last_query_cost= 0.0;
+  /*
+    End the statement transaction potentially started by an
+    engine callback. We ignore the return value for now,
+    since as long as EOF packet is part of the query cache
+    response, we can't handle it anyway.
+  */
+  (void) trans_commit_stmt(thd);
   if (!thd->stmt_da->is_set())
     thd->stmt_da->disable_status();
 
-- 
2.30.9