Commit e7332e64 authored by unknown's avatar unknown

Fix crash in embedded server due to incorrect storage of results

in the query cache. (Bug #9549)


libmysqld/emb_qcache.h:
  Fix Querycache_stream::use_next_block() to actually use the next
  block and mark blocks as used when it writes to them.
mysql-test/r/query_cache.result:
  Update results.
mysql-test/t/query_cache.test:
  Add new regression test.
libmysqld/emb_qcache.cc:
  Fix calls to use_next_block() to indicate whether we are writing
  to the next block or not.
sql/sql_cache.cc:
  Initialize the first block properly when storing a result in
  the embedded server.
parent ab77d7d7
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
void Querycache_stream::store_char(char c) void Querycache_stream::store_char(char c)
{ {
if (data_end == cur_data) if (data_end == cur_data)
use_next_block(); use_next_block(TRUE);
*(cur_data++)= c; *(cur_data++)= c;
#ifndef DBUG_OFF #ifndef DBUG_OFF
stored_size++; stored_size++;
...@@ -42,13 +42,13 @@ void Querycache_stream::store_short(ushort s) ...@@ -42,13 +42,13 @@ void Querycache_stream::store_short(ushort s)
} }
if (data_end == cur_data) if (data_end == cur_data)
{ {
use_next_block(); use_next_block(TRUE);
int2store(cur_data, s); int2store(cur_data, s);
cur_data+= 2; cur_data+= 2;
return; return;
} }
*cur_data= ((byte *)(&s))[0]; *cur_data= ((byte *)(&s))[0];
use_next_block(); use_next_block(TRUE);
*(cur_data++)= ((byte *)(&s))[1]; *(cur_data++)= ((byte *)(&s))[1];
} }
...@@ -66,7 +66,7 @@ void Querycache_stream::store_int(uint i) ...@@ -66,7 +66,7 @@ void Querycache_stream::store_int(uint i)
} }
if (!rest_len) if (!rest_len)
{ {
use_next_block(); use_next_block(TRUE);
int4store(cur_data, i); int4store(cur_data, i);
cur_data+= 4; cur_data+= 4;
return; return;
...@@ -74,7 +74,7 @@ void Querycache_stream::store_int(uint i) ...@@ -74,7 +74,7 @@ void Querycache_stream::store_int(uint i)
char buf[4]; char buf[4];
int4store(buf, i); int4store(buf, i);
memcpy(cur_data, buf, rest_len); memcpy(cur_data, buf, rest_len);
use_next_block(); use_next_block(TRUE);
memcpy(cur_data, buf+rest_len, 4-rest_len); memcpy(cur_data, buf+rest_len, 4-rest_len);
cur_data+= 4-rest_len; cur_data+= 4-rest_len;
} }
...@@ -93,13 +93,13 @@ void Querycache_stream::store_ll(ulonglong ll) ...@@ -93,13 +93,13 @@ void Querycache_stream::store_ll(ulonglong ll)
} }
if (!rest_len) if (!rest_len)
{ {
use_next_block(); use_next_block(TRUE);
int8store(cur_data, ll); int8store(cur_data, ll);
cur_data+= 8; cur_data+= 8;
return; return;
} }
memcpy(cur_data, &ll, rest_len); memcpy(cur_data, &ll, rest_len);
use_next_block(); use_next_block(TRUE);
memcpy(cur_data, ((byte*)&ll)+rest_len, 8-rest_len); memcpy(cur_data, ((byte*)&ll)+rest_len, 8-rest_len);
cur_data+= 8-rest_len; cur_data+= 8-rest_len;
} }
...@@ -112,14 +112,14 @@ void Querycache_stream::store_str_only(const char *str, uint str_len) ...@@ -112,14 +112,14 @@ void Querycache_stream::store_str_only(const char *str, uint str_len)
do do
{ {
size_t rest_len= data_end - cur_data; size_t rest_len= data_end - cur_data;
if (rest_len > str_len) if (rest_len >= str_len)
{ {
memcpy(cur_data, str, str_len); memcpy(cur_data, str, str_len);
cur_data+= str_len; cur_data+= str_len;
return; return;
} }
memcpy(cur_data, str, rest_len); memcpy(cur_data, str, rest_len);
use_next_block(); use_next_block(TRUE);
str_len-= rest_len; str_len-= rest_len;
str+= rest_len; str+= rest_len;
} while(str_len); } while(str_len);
...@@ -145,7 +145,7 @@ void Querycache_stream::store_safe_str(const char *str, uint str_len) ...@@ -145,7 +145,7 @@ void Querycache_stream::store_safe_str(const char *str, uint str_len)
char Querycache_stream::load_char() char Querycache_stream::load_char()
{ {
if (cur_data == data_end) if (cur_data == data_end)
use_next_block(); use_next_block(FALSE);
return *(cur_data++); return *(cur_data++);
} }
...@@ -160,13 +160,13 @@ ushort Querycache_stream::load_short() ...@@ -160,13 +160,13 @@ ushort Querycache_stream::load_short()
} }
if (data_end == cur_data) if (data_end == cur_data)
{ {
use_next_block(); use_next_block(FALSE);
result= uint2korr(cur_data); result= uint2korr(cur_data);
cur_data+= 2; cur_data+= 2;
return result; return result;
} }
((byte*)&result)[0]= *cur_data; ((byte*)&result)[0]= *cur_data;
use_next_block(); use_next_block(FALSE);
((byte*)&result)[1]= *(cur_data++); ((byte*)&result)[1]= *(cur_data++);
return result; return result;
} }
...@@ -183,14 +183,14 @@ uint Querycache_stream::load_int() ...@@ -183,14 +183,14 @@ uint Querycache_stream::load_int()
} }
if (!rest_len) if (!rest_len)
{ {
use_next_block(); use_next_block(FALSE);
result= uint4korr(cur_data); result= uint4korr(cur_data);
cur_data+= 4; cur_data+= 4;
return result; return result;
} }
char buf[4]; char buf[4];
memcpy(buf, cur_data, rest_len); memcpy(buf, cur_data, rest_len);
use_next_block(); use_next_block(FALSE);
memcpy(buf+rest_len, cur_data, 4-rest_len); memcpy(buf+rest_len, cur_data, 4-rest_len);
cur_data+= 4-rest_len; cur_data+= 4-rest_len;
result= uint4korr(buf); result= uint4korr(buf);
...@@ -209,13 +209,13 @@ ulonglong Querycache_stream::load_ll() ...@@ -209,13 +209,13 @@ ulonglong Querycache_stream::load_ll()
} }
if (!rest_len) if (!rest_len)
{ {
use_next_block(); use_next_block(FALSE);
result= uint8korr(cur_data); result= uint8korr(cur_data);
cur_data+= 8; cur_data+= 8;
return result; return result;
} }
memcpy(&result, cur_data, rest_len); memcpy(&result, cur_data, rest_len);
use_next_block(); use_next_block(FALSE);
memcpy(((byte*)&result)+rest_len, cur_data, 8-rest_len); memcpy(((byte*)&result)+rest_len, cur_data, 8-rest_len);
cur_data+= 8-rest_len; cur_data+= 8-rest_len;
return result; return result;
...@@ -226,7 +226,7 @@ void Querycache_stream::load_str_only(char *buffer, uint str_len) ...@@ -226,7 +226,7 @@ void Querycache_stream::load_str_only(char *buffer, uint str_len)
do do
{ {
size_t rest_len= data_end - cur_data; size_t rest_len= data_end - cur_data;
if (rest_len > str_len) if (rest_len >= str_len)
{ {
memcpy(buffer, cur_data, str_len); memcpy(buffer, cur_data, str_len);
cur_data+= str_len; cur_data+= str_len;
...@@ -234,7 +234,7 @@ void Querycache_stream::load_str_only(char *buffer, uint str_len) ...@@ -234,7 +234,7 @@ void Querycache_stream::load_str_only(char *buffer, uint str_len)
break; break;
} }
memcpy(buffer, cur_data, rest_len); memcpy(buffer, cur_data, rest_len);
use_next_block(); use_next_block(FALSE);
str_len-= rest_len; str_len-= rest_len;
buffer+= rest_len; buffer+= rest_len;
} while(str_len); } while(str_len);
......
...@@ -22,22 +22,43 @@ class Querycache_stream ...@@ -22,22 +22,43 @@ class Querycache_stream
uint headers_len; uint headers_len;
public: public:
#ifndef DBUG_OFF #ifndef DBUG_OFF
Query_cache_block *first_block;
uint stored_size; uint stored_size;
#endif #endif
Querycache_stream(Query_cache_block *ini_block, uint ini_headers_len) : Querycache_stream(Query_cache_block *ini_block, uint ini_headers_len) :
block(ini_block), headers_len(ini_headers_len) block(ini_block), headers_len(ini_headers_len)
{ {
use_next_block(); cur_data= ((byte*)block)+headers_len;
data_end= cur_data + (block->used-headers_len);
#ifndef DBUG_OFF #ifndef DBUG_OFF
first_block= ini_block;
stored_size= 0; stored_size= 0;
#endif #endif
} }
void use_next_block() void use_next_block(bool writing)
{ {
/*
This shouldn't be called if there is only one block, or to loop
around to the first block again. That means we're trying to write
more data than we allocated space for.
*/
DBUG_ASSERT(block->next != block);
DBUG_ASSERT(block->next != first_block);
block= block->next;
/*
While writing, update the type of each block as we write to it.
While reading, make sure that the block is of the expected type.
*/
if (writing)
block->type= Query_cache_block::RES_CONT;
else
DBUG_ASSERT(block->type == Query_cache_block::RES_CONT);
cur_data= ((byte*)block)+headers_len; cur_data= ((byte*)block)+headers_len;
data_end= cur_data + (block->used-headers_len); data_end= cur_data + (block->used-headers_len);
} }
void store_char(char c); void store_char(char c);
void store_short(ushort s); void store_short(ushort s);
void store_int(uint i); void store_int(uint i);
......
This diff is collapsed.
...@@ -684,4 +684,49 @@ repair table t1; ...@@ -684,4 +684,49 @@ repair table t1;
show status like 'qcache_queries_in_cache'; show status like 'qcache_queries_in_cache';
drop table t1; drop table t1;
# Bug #9549: Make sure cached queries that span more than one cache block
# are handled properly in the embedded server.
# We just want a small query cache, so we can fragment it easily
set GLOBAL query_cache_size=64*1024;
# This actually gives us a usable cache size of about 48K
# Each table is about 14K
create table t1 (a text);
insert into t1 values (repeat('abcdefghijklmnopqrstuvwxyz', 550));
create table t2 (a text);
insert into t2 values (repeat('ijklmnopqrstuvwxyzabcdefgh', 550));
# Load a query from each table into the query cache
--disable_result_log
select a from t1; # Q1
select a from t2; # Q2
--enable_result_log
show status like 'Qcache_%_blocks';
# Now the cache looks like (14K for Q1)(14K for Q2)(20K free)
# Flush Q1 from the cache by adding an out-of-order chunk to t1
insert into t1 select reverse(a) from t1;
show status like 'Qcache_%_blocks';
# Now the cache looks like (14K free)(14K for Q2)(20K free)
# Load our new data into the query cache
--disable_result_log
select a from t1; # Q3
--enable_result_log
show status like 'Qcache_%_blocks';
# Now the cache should be like (14K for Q3)(14K for Q2)(14K for Q3)(6K free)
# Note that Q3 is split across two chunks!
# Load Q3 from the cache, and actually pay attention to the results
select a from t1;
flush query cache;
drop table t1, t2;
set GLOBAL query_cache_size=0; set GLOBAL query_cache_size=0;
...@@ -1933,6 +1933,11 @@ my_bool Query_cache::write_result_data(Query_cache_block **result_block, ...@@ -1933,6 +1933,11 @@ my_bool Query_cache::write_result_data(Query_cache_block **result_block,
type = Query_cache_block::RES_CONT; type = Query_cache_block::RES_CONT;
} while (block != *result_block); } while (block != *result_block);
#else #else
/*
Set type of first block, emb_store_querycache_result() will handle
the others.
*/
(*result_block)->type= type;
Querycache_stream qs(*result_block, headers_len); Querycache_stream qs(*result_block, headers_len);
emb_store_querycache_result(&qs, (THD*)data); emb_store_querycache_result(&qs, (THD*)data);
#endif /*!EMBEDDED_LIBRARY*/ #endif /*!EMBEDDED_LIBRARY*/
......
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