Commit 3d6cea32 authored by Davi Arnaut's avatar Davi Arnaut

Bug#36326: nested transaction and select

The problem is that the query cache stores packets containing
the server status of the time when the cached statement was run.
This might lead to a wrong transaction status in the client side
if a statement is cached during a transaction and is later served
outside a transaction context (and vice-versa).

The solution is to take into account the transaction status when
storing in and serving from the query cache.

mysql-test/r/innodb_cache.result:
  Update test case result.
mysql-test/r/query_cache.result:
  Add test case result for Bug#36326
mysql-test/t/query_cache.test:
  Add test case for Bug#36326
sql/mysql_priv.h:
  Add new flags.
sql/sql_cache.cc:
  Remember the transaction and autocommit status stored in the packet.
tests/mysql_client_test.c:
  Add test case for Bug#36326
parent 386ec13b
......@@ -66,10 +66,10 @@ a
2
show status like "Qcache_queries_in_cache";
Variable_name Value
Qcache_queries_in_cache 3
Qcache_queries_in_cache 6
show status like "Qcache_hits";
Variable_name Value
Qcache_hits 3
Qcache_hits 0
insert into t1 values (3);
insert into t2 values (3);
insert into t1 values (4);
......@@ -90,14 +90,14 @@ a
2
show status like "Qcache_queries_in_cache";
Variable_name Value
Qcache_queries_in_cache 1
Qcache_queries_in_cache 2
show status like "Qcache_hits";
Variable_name Value
Qcache_hits 4
Qcache_hits 1
commit;
show status like "Qcache_queries_in_cache";
Variable_name Value
Qcache_queries_in_cache 1
Qcache_queries_in_cache 2
drop table t3,t2,t1;
CREATE TABLE t1 (id int(11) NOT NULL auto_increment, PRIMARY KEY (id)) ENGINE=InnoDB;
select count(*) from t1;
......
......@@ -1681,3 +1681,54 @@ Qcache_hits 1
DROP TABLE t1;
SET GLOBAL concurrent_insert= @save_concurrent_insert;
SET GLOBAL query_cache_size= default;
DROP TABLE IF EXISTS t1;
FLUSH STATUS;
SET GLOBAL query_cache_size=1048576;
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (1),(2),(3),(4),(5);
SHOW STATUS LIKE 'Qcache_queries_in_cache';
Variable_name Value
Qcache_queries_in_cache 0
SELECT * FROM t1;
a
1
2
3
4
5
BEGIN;
SELECT * FROM t1;
a
1
2
3
4
5
COMMIT;
SHOW STATUS LIKE 'Qcache_queries_in_cache';
Variable_name Value
Qcache_queries_in_cache 2
SHOW STATUS LIKE "Qcache_hits";
Variable_name Value
Qcache_hits 0
SELECT * FROM t1;
a
1
2
3
4
5
BEGIN;
SELECT * FROM t1;
a
1
2
3
4
5
COMMIT;
SHOW STATUS LIKE "Qcache_hits";
Variable_name Value
Qcache_hits 2
DROP TABLE t1;
SET GLOBAL query_cache_size= default;
......@@ -1276,4 +1276,31 @@ DROP TABLE t1;
SET GLOBAL concurrent_insert= @save_concurrent_insert;
SET GLOBAL query_cache_size= default;
#
# Bug#36326: nested transaction and select
#
--disable_warnings
DROP TABLE IF EXISTS t1;
--enable_warnings
FLUSH STATUS;
SET GLOBAL query_cache_size=1048576;
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (1),(2),(3),(4),(5);
SHOW STATUS LIKE 'Qcache_queries_in_cache';
SELECT * FROM t1;
BEGIN;
SELECT * FROM t1;
COMMIT;
SHOW STATUS LIKE 'Qcache_queries_in_cache';
SHOW STATUS LIKE "Qcache_hits";
SELECT * FROM t1;
BEGIN;
SELECT * FROM t1;
COMMIT;
SHOW STATUS LIKE "Qcache_hits";
DROP TABLE t1;
SET GLOBAL query_cache_size= default;
# End of 5.0 tests
......@@ -651,6 +651,8 @@ struct Query_cache_query_flags
unsigned int client_long_flag:1;
unsigned int client_protocol_41:1;
unsigned int more_results_exists:1;
unsigned int in_trans:1;
unsigned int autocommit:1;
unsigned int pkt_nr;
uint character_set_client_num;
uint character_set_results_num;
......
......@@ -859,6 +859,8 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used)
CLIENT_PROTOCOL_41);
flags.more_results_exists= test(thd->server_status &
SERVER_MORE_RESULTS_EXISTS);
flags.in_trans= test(thd->server_status & SERVER_STATUS_IN_TRANS);
flags.autocommit= test(thd->server_status & SERVER_STATUS_AUTOCOMMIT);
flags.pkt_nr= net->pkt_nr;
flags.character_set_client_num=
thd->variables.character_set_client->number;
......@@ -879,7 +881,7 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used)
DBUG_PRINT("qcache", ("long %d, 4.1: %d, more results %d, pkt_nr: %d, \
CS client: %u, CS result: %u, CS conn: %u, limit: %lu, TZ: 0x%lx, \
sql mode: 0x%lx, sort len: %lu, conncat len: %lu, div_precision: %lu, \
def_week_frmt: %lu",
def_week_frmt: %lu, in_trans: %d, autocommit: %d",
(int)flags.client_long_flag,
(int)flags.client_protocol_41,
(int)flags.more_results_exists,
......@@ -893,7 +895,10 @@ def_week_frmt: %lu",
flags.max_sort_length,
flags.group_concat_max_len,
flags.div_precision_increment,
flags.default_week_format));
flags.default_week_format,
(int)flags.in_trans,
(int)flags.autocommit));
/*
Make InnoDB to release the adaptive hash index latch before
acquiring the query cache mutex.
......@@ -1144,6 +1149,8 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
CLIENT_PROTOCOL_41);
flags.more_results_exists= test(thd->server_status &
SERVER_MORE_RESULTS_EXISTS);
flags.in_trans= test(thd->server_status & SERVER_STATUS_IN_TRANS);
flags.autocommit= test(thd->server_status & SERVER_STATUS_AUTOCOMMIT);
flags.pkt_nr= thd->net.pkt_nr;
flags.character_set_client_num= thd->variables.character_set_client->number;
flags.character_set_results_num=
......@@ -1162,7 +1169,7 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
DBUG_PRINT("qcache", ("long %d, 4.1: %d, more results %d, pkt_nr: %d, \
CS client: %u, CS result: %u, CS conn: %u, limit: %lu, TZ: 0x%lx, \
sql mode: 0x%lx, sort len: %lu, conncat len: %lu, div_precision: %lu, \
def_week_frmt: %lu",
def_week_frmt: %lu, in_trans: %d, autocommit: %d",
(int)flags.client_long_flag,
(int)flags.client_protocol_41,
(int)flags.more_results_exists,
......@@ -1176,7 +1183,9 @@ def_week_frmt: %lu",
flags.max_sort_length,
flags.group_concat_max_len,
flags.div_precision_increment,
flags.default_week_format));
flags.default_week_format,
(int)flags.in_trans,
(int)flags.autocommit));
memcpy((void *)(sql + (tot_length - QUERY_CACHE_FLAGS_SIZE)),
&flags, QUERY_CACHE_FLAGS_SIZE);
query_block = (Query_cache_block *) hash_search(&queries, (byte*) sql,
......
......@@ -16354,6 +16354,63 @@ static void test_bug40365(void)
DBUG_VOID_RETURN;
}
/**
Bug#36326: nested transaction and select
*/
#ifdef HAVE_QUERY_CACHE
static void test_bug36326()
{
int rc;
DBUG_ENTER("test_bug36326");
myheader("test_bug36326");
rc= mysql_autocommit(mysql, TRUE);
myquery(rc);
rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1");
myquery(rc);
rc= mysql_query(mysql, "CREATE TABLE t1 (a INTEGER)");
myquery(rc);
rc= mysql_query(mysql, "INSERT INTO t1 VALUES (1)");
myquery(rc);
rc= mysql_query(mysql, "SET GLOBAL query_cache_type = 1");
myquery(rc);
rc= mysql_query(mysql, "SET GLOBAL query_cache_size = 1048576");
myquery(rc);
DIE_UNLESS(!(mysql->server_status & SERVER_STATUS_IN_TRANS));
DIE_UNLESS(mysql->server_status & SERVER_STATUS_AUTOCOMMIT);
rc= mysql_query(mysql, "BEGIN");
myquery(rc);
DIE_UNLESS(mysql->server_status & SERVER_STATUS_IN_TRANS);
rc= mysql_query(mysql, "SELECT * FROM t1");
myquery(rc);
rc= my_process_result(mysql);
DIE_UNLESS(rc == 1);
rc= mysql_rollback(mysql);
myquery(rc);
rc= mysql_query(mysql, "ROLLBACK");
myquery(rc);
DIE_UNLESS(!(mysql->server_status & SERVER_STATUS_IN_TRANS));
rc= mysql_query(mysql, "SELECT * FROM t1");
myquery(rc);
DIE_UNLESS(!(mysql->server_status & SERVER_STATUS_IN_TRANS));
rc= my_process_result(mysql);
DIE_UNLESS(rc == 1);
rc= mysql_query(mysql, "DROP TABLE t1");
myquery(rc);
rc= mysql_query(mysql, "SET GLOBAL query_cache_size = 0");
myquery(rc);
DBUG_VOID_RETURN;
}
#endif
/*
Read and parse arguments and MySQL options from my.cnf
*/
......@@ -16652,6 +16709,9 @@ static struct my_tests_st my_tests[]= {
{ "test_bug40365", test_bug40365 },
#ifdef HAVE_SPATIAL
{ "test_bug37956", test_bug37956 },
#endif
#ifdef HAVE_QUERY_CACHE
{ "test_bug36326", test_bug36326 },
#endif
{ 0, 0 }
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment