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 ...@@ -66,10 +66,10 @@ a
2 2
show status like "Qcache_queries_in_cache"; show status like "Qcache_queries_in_cache";
Variable_name Value Variable_name Value
Qcache_queries_in_cache 3 Qcache_queries_in_cache 6
show status like "Qcache_hits"; show status like "Qcache_hits";
Variable_name Value Variable_name Value
Qcache_hits 3 Qcache_hits 0
insert into t1 values (3); insert into t1 values (3);
insert into t2 values (3); insert into t2 values (3);
insert into t1 values (4); insert into t1 values (4);
...@@ -90,14 +90,14 @@ a ...@@ -90,14 +90,14 @@ a
2 2
show status like "Qcache_queries_in_cache"; show status like "Qcache_queries_in_cache";
Variable_name Value Variable_name Value
Qcache_queries_in_cache 1 Qcache_queries_in_cache 2
show status like "Qcache_hits"; show status like "Qcache_hits";
Variable_name Value Variable_name Value
Qcache_hits 4 Qcache_hits 1
commit; commit;
show status like "Qcache_queries_in_cache"; show status like "Qcache_queries_in_cache";
Variable_name Value Variable_name Value
Qcache_queries_in_cache 1 Qcache_queries_in_cache 2
drop table t3,t2,t1; drop table t3,t2,t1;
CREATE TABLE t1 (id int(11) NOT NULL auto_increment, PRIMARY KEY (id)) ENGINE=InnoDB; CREATE TABLE t1 (id int(11) NOT NULL auto_increment, PRIMARY KEY (id)) ENGINE=InnoDB;
select count(*) from t1; select count(*) from t1;
......
...@@ -1681,3 +1681,54 @@ Qcache_hits 1 ...@@ -1681,3 +1681,54 @@ Qcache_hits 1
DROP TABLE t1; DROP TABLE t1;
SET GLOBAL concurrent_insert= @save_concurrent_insert; SET GLOBAL concurrent_insert= @save_concurrent_insert;
SET GLOBAL query_cache_size= default; 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; ...@@ -1276,4 +1276,31 @@ DROP TABLE t1;
SET GLOBAL concurrent_insert= @save_concurrent_insert; SET GLOBAL concurrent_insert= @save_concurrent_insert;
SET GLOBAL query_cache_size= default; 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 # End of 5.0 tests
...@@ -651,6 +651,8 @@ struct Query_cache_query_flags ...@@ -651,6 +651,8 @@ struct Query_cache_query_flags
unsigned int client_long_flag:1; unsigned int client_long_flag:1;
unsigned int client_protocol_41:1; unsigned int client_protocol_41:1;
unsigned int more_results_exists:1; unsigned int more_results_exists:1;
unsigned int in_trans:1;
unsigned int autocommit:1;
unsigned int pkt_nr; unsigned int pkt_nr;
uint character_set_client_num; uint character_set_client_num;
uint character_set_results_num; uint character_set_results_num;
......
...@@ -859,6 +859,8 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used) ...@@ -859,6 +859,8 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used)
CLIENT_PROTOCOL_41); CLIENT_PROTOCOL_41);
flags.more_results_exists= test(thd->server_status & flags.more_results_exists= test(thd->server_status &
SERVER_MORE_RESULTS_EXISTS); 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.pkt_nr= net->pkt_nr;
flags.character_set_client_num= flags.character_set_client_num=
thd->variables.character_set_client->number; thd->variables.character_set_client->number;
...@@ -879,7 +881,7 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used) ...@@ -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, \ 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, \ 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, \ 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_long_flag,
(int)flags.client_protocol_41, (int)flags.client_protocol_41,
(int)flags.more_results_exists, (int)flags.more_results_exists,
...@@ -893,7 +895,10 @@ def_week_frmt: %lu", ...@@ -893,7 +895,10 @@ def_week_frmt: %lu",
flags.max_sort_length, flags.max_sort_length,
flags.group_concat_max_len, flags.group_concat_max_len,
flags.div_precision_increment, 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 Make InnoDB to release the adaptive hash index latch before
acquiring the query cache mutex. acquiring the query cache mutex.
...@@ -1144,6 +1149,8 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length) ...@@ -1144,6 +1149,8 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
CLIENT_PROTOCOL_41); CLIENT_PROTOCOL_41);
flags.more_results_exists= test(thd->server_status & flags.more_results_exists= test(thd->server_status &
SERVER_MORE_RESULTS_EXISTS); 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.pkt_nr= thd->net.pkt_nr;
flags.character_set_client_num= thd->variables.character_set_client->number; flags.character_set_client_num= thd->variables.character_set_client->number;
flags.character_set_results_num= flags.character_set_results_num=
...@@ -1162,7 +1169,7 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length) ...@@ -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, \ 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, \ 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, \ 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_long_flag,
(int)flags.client_protocol_41, (int)flags.client_protocol_41,
(int)flags.more_results_exists, (int)flags.more_results_exists,
...@@ -1176,7 +1183,9 @@ def_week_frmt: %lu", ...@@ -1176,7 +1183,9 @@ def_week_frmt: %lu",
flags.max_sort_length, flags.max_sort_length,
flags.group_concat_max_len, flags.group_concat_max_len,
flags.div_precision_increment, 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)), memcpy((void *)(sql + (tot_length - QUERY_CACHE_FLAGS_SIZE)),
&flags, QUERY_CACHE_FLAGS_SIZE); &flags, QUERY_CACHE_FLAGS_SIZE);
query_block = (Query_cache_block *) hash_search(&queries, (byte*) sql, query_block = (Query_cache_block *) hash_search(&queries, (byte*) sql,
......
...@@ -16354,6 +16354,63 @@ static void test_bug40365(void) ...@@ -16354,6 +16354,63 @@ static void test_bug40365(void)
DBUG_VOID_RETURN; 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 Read and parse arguments and MySQL options from my.cnf
*/ */
...@@ -16652,6 +16709,9 @@ static struct my_tests_st my_tests[]= { ...@@ -16652,6 +16709,9 @@ static struct my_tests_st my_tests[]= {
{ "test_bug40365", test_bug40365 }, { "test_bug40365", test_bug40365 },
#ifdef HAVE_SPATIAL #ifdef HAVE_SPATIAL
{ "test_bug37956", test_bug37956 }, { "test_bug37956", test_bug37956 },
#endif
#ifdef HAVE_QUERY_CACHE
{ "test_bug36326", test_bug36326 },
#endif #endif
{ 0, 0 } { 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