Commit 783b7748 authored by unknown's avatar unknown

Merge gbichot@bk-internal.mysql.com:/home/bk/mysql-5.1-runtime

into  gbichot3.local:/home/mysql_src/mysql-5.1-runtime-735-realfix


sql/mysql_priv.h:
  Auto merged
sql/set_var.cc:
  Auto merged
sql/sql_prepare.cc:
  Auto merged
parents 2fad8ac2 dca006df
......@@ -822,7 +822,7 @@ int Protocol::begin_dataset()
remove last row of current recordset
SYNOPSIS
Protocol_simple::remove_last_row()
Protocol_text::remove_last_row()
NOTES
does the loop from the beginning of the current recordset to
......@@ -830,12 +830,12 @@ int Protocol::begin_dataset()
Not supposed to be frequently called.
*/
void Protocol_simple::remove_last_row()
void Protocol_text::remove_last_row()
{
MYSQL_DATA *data= thd->cur_data;
MYSQL_ROWS **last_row_hook= &data->data;
uint count= data->rows;
DBUG_ENTER("Protocol_simple::remove_last_row");
DBUG_ENTER("Protocol_text::remove_last_row");
while (--count)
last_row_hook= &(*last_row_hook)->next;
......@@ -964,7 +964,7 @@ bool Protocol::write()
return false;
}
bool Protocol_prep::write()
bool Protocol_binary::write()
{
MYSQL_ROWS *cur;
MYSQL_DATA *data= thd->cur_data;
......@@ -1031,7 +1031,7 @@ void net_send_error_packet(THD *thd, uint sql_errno, const char *err)
}
void Protocol_simple::prepare_for_resend()
void Protocol_text::prepare_for_resend()
{
MYSQL_ROWS *cur;
MYSQL_DATA *data= thd->cur_data;
......@@ -1056,7 +1056,7 @@ void Protocol_simple::prepare_for_resend()
DBUG_VOID_RETURN;
}
bool Protocol_simple::store_null()
bool Protocol_text::store_null()
{
*(next_field++)= NULL;
++next_mysql_field;
......
-- require r/have_query_cache.require
# As PS are not cached we disable them to ensure the we get the right number
# of query cache hits
-- disable_ps_protocol
disable_query_log;
show variables like "have_query_cache";
enable_query_log;
......@@ -1325,4 +1325,27 @@ start transaction;
insert into t1(c1) select c1 from v1;
drop table t1, t2, t3;
drop view v1;
create table t1(c1 int);
insert into t1 values(1),(10),(100);
select * from t1;
c1
1
10
100
select * from t1;
c1
1
10
100
select * from t1;
c1
1
10
100
select * from t1;
c1
1
10
100
drop table t1;
set global query_cache_size=0;
set global query_cache_size=100000;
flush status;
create table t1(c1 int);
insert into t1 values(1),(10),(100);
prepare stmt1 from "select * from t1 where c1=10";
show status like 'Qcache_hits';
Variable_name Value
Qcache_hits 0
execute stmt1;
c1
10
show status like 'Qcache_hits';
Variable_name Value
Qcache_hits 0
execute stmt1;
c1
10
show status like 'Qcache_hits';
Variable_name Value
Qcache_hits 1
execute stmt1;
c1
10
show status like 'Qcache_hits';
Variable_name Value
Qcache_hits 2
prepare stmt2 from "select * from t1 where c1=10";
execute stmt2;
c1
10
show status like 'Qcache_hits';
Variable_name Value
Qcache_hits 3
execute stmt2;
c1
10
show status like 'Qcache_hits';
Variable_name Value
Qcache_hits 4
execute stmt2;
c1
10
show status like 'Qcache_hits';
Variable_name Value
Qcache_hits 5
prepare stmt3 from "select * from t1 where c1=10";
execute stmt3;
c1
10
show status like 'Qcache_hits';
Variable_name Value
Qcache_hits 6
execute stmt3;
c1
10
show status like 'Qcache_hits';
Variable_name Value
Qcache_hits 7
execute stmt3;
c1
10
show status like 'Qcache_hits';
Variable_name Value
Qcache_hits 8
select * from t1 where c1=10;
c1
10
show status like 'Qcache_hits';
Variable_name Value
Qcache_hits 9
flush tables;
execute stmt1;
c1
10
show status like 'Qcache_hits';
Variable_name Value
Qcache_hits 9
select * from t1 where c1=10;
c1
10
show status like 'Qcache_hits';
Variable_name Value
Qcache_hits 10
prepare stmt1 from "select * from t1 where c1=?";
show status like 'Qcache_hits';
Variable_name Value
Qcache_hits 10
set @a=1;
execute stmt1 using @a;
c1
1
show status like 'Qcache_hits';
Variable_name Value
Qcache_hits 10
set @a=100;
execute stmt1 using @a;
c1
100
show status like 'Qcache_hits';
Variable_name Value
Qcache_hits 10
set @a=10;
execute stmt1 using @a;
c1
10
show status like 'Qcache_hits';
Variable_name Value
Qcache_hits 10
prepare stmt1 from "select * from t1 where c1=10";
set global query_cache_size=0;
show status like 'Qcache_hits';
Variable_name Value
Qcache_hits 10
execute stmt1;
c1
10
show status like 'Qcache_hits';
Variable_name Value
Qcache_hits 10
execute stmt1;
c1
10
show status like 'Qcache_hits';
Variable_name Value
Qcache_hits 10
execute stmt1;
c1
10
show status like 'Qcache_hits';
Variable_name Value
Qcache_hits 10
set global query_cache_size=100000;
execute stmt1;
c1
10
show status like 'Qcache_hits';
Variable_name Value
Qcache_hits 10
execute stmt1;
c1
10
show status like 'Qcache_hits';
Variable_name Value
Qcache_hits 11
execute stmt1;
c1
10
show status like 'Qcache_hits';
Variable_name Value
Qcache_hits 12
set global query_cache_size=0;
prepare stmt1 from "select * from t1 where c1=10";
set global query_cache_size=100000;
show status like 'Qcache_hits';
Variable_name Value
Qcache_hits 12
execute stmt1;
c1
10
show status like 'Qcache_hits';
Variable_name Value
Qcache_hits 12
execute stmt1;
c1
10
show status like 'Qcache_hits';
Variable_name Value
Qcache_hits 12
execute stmt1;
c1
10
show status like 'Qcache_hits';
Variable_name Value
Qcache_hits 12
set global query_cache_size=0;
prepare stmt1 from "select * from t1 where c1=?";
set global query_cache_size=100000;
show status like 'Qcache_hits';
Variable_name Value
Qcache_hits 12
set @a=1;
execute stmt1 using @a;
c1
1
show status like 'Qcache_hits';
Variable_name Value
Qcache_hits 12
set @a=100;
execute stmt1 using @a;
c1
100
show status like 'Qcache_hits';
Variable_name Value
Qcache_hits 12
set @a=10;
execute stmt1 using @a;
c1
10
show status like 'Qcache_hits';
Variable_name Value
Qcache_hits 12
drop table t1;
set global query_cache_size=0;
flush status;
# Grant tests not performed with embedded server
-- source include/not_embedded.inc
-- source include/have_query_cache.inc
# See at the end of the test why we disable the ps protocol (*)
-- disable_ps_protocol
#
# Test grants with query cache
......@@ -151,3 +153,27 @@ drop database mysqltest;
set GLOBAL query_cache_size=default;
# End of 4.1 tests
# (*) Why we disable the ps protocol: because in normal protocol,
# a SELECT failing due to insufficient privileges increments
# Qcache_not_cached, while in ps-protocol, no.
# In detail: in normal protocol,
# the "access denied" errors on SELECT are issued at (stack trace):
# mysql_parse/mysql_execute_command/execute_sqlcom_select/handle_select/
# mysql_select/JOIN::prepare/setup_wild/insert_fields/
# check_grant_all_columns/my_error/my_message_sql, which then calls
# push_warning/query_cache_abort: at this moment,
# query_cache_store_query() has been called, so query exists in cache,
# so thd->net.query_cache_query!=NULL, so query_cache_abort() removes
# the query from cache, which causes a query_cache.refused++ (thus,
# a Qcache_not_cached++).
# While in ps-protocol, the error is issued at prepare time;
# for this mysql_test_select() is called, not execute_sqlcom_select()
# (and that also leads to JOIN::prepare/etc). Thus, as
# query_cache_store_query() has not been called,
# thd->net.query_cache_query==NULL, so query_cache_abort() does nothing:
# Qcache_not_cached is not incremented.
# As this test prints Qcache_not_cached after SELECT failures,
# we cannot enable this test in ps-protocol.
--enable_ps_protocol
......@@ -36,7 +36,11 @@ insert into t1 value (2);
insert into t2 value (3);
select * from t1;
# Run the check query once to load it into qc on server1
# See at the end of this test why we need to disable ps-protocol for
# this query (*)
--disable_ps_protocol
select a != 3 from t1;
--enable_ps_protocol
select * from t2;
show status like "Qcache_queries_in_cache";
show status like "Qcache_inserts";
......@@ -93,3 +97,30 @@ set GLOBAL query_cache_size=0;
set GLOBAL ndb_cache_check_time=0;
reset query cache;
flush status;
# (*) Why we need to execute the query in non-ps mode.
# The principle of this test is: two mysqlds connected to one cluster,
# both using their query cache. Queries are cached in server1
# ("select a!=3 from t1", "select * from t1"),
# table t1 is modified in server2, we want to see that this invalidates
# the query cache of server1. Invalidation with NDB works like this:
# when a query is found in the query cache, NDB is asked if the tables
# have changed. In this test, ha_ndbcluster calls NDB every millisecond
# to collect change information about tables.
# Due to this millisecond delay, there is need for a loop ("while...")
# in this test, which waits until a query1 ("select a!=3 from t1") is
# invalidated (which is equivalent to it returning
# up-to-date results), and then expects query2 ("select * from t1")
# to have been invalidated (see up-to-date results).
# But when enabling --ps-protocol in this test, the logic breaks,
# because query1 is still done via mysql_real_query() (see mysqltest.c:
# eval_expr() always uses mysql_real_query()). So, query1 returning
# up-to-date results is not a sign of it being invalidated in the cache,
# because it was NOT in the cache ("select a!=3 from t1" on line 39
# was done with prep stmts, while `select a!=3 from t1` is not,
# thus the second does not see the first in the cache). Thus, we may run
# query2 when cache still has not been invalidated.
# The solution is to make the initial "select a!=3 from t1" run
# as a normal query, this repairs the broken logic.
# But note, "select * from t1" is still using prepared statements
# which was the goal of this test with --ps-protocol.
......@@ -907,4 +907,24 @@ start transaction;
insert into t1(c1) select c1 from v1;
drop table t1, t2, t3;
drop view v1;
#
# If running with --ps-protocol:
# see if a query from the text protocol is served with results cached
# from a query which used the binary (which would be wrong, results
# are in different formats); if that happens, the results will
# be incorrect and the test will fail.
#
create table t1(c1 int);
insert into t1 values(1),(10),(100);
select * from t1;
-- disable_ps_protocol
select * from t1;
select * from t1;
-- enable_ps_protocol
select * from t1;
drop table t1;
set global query_cache_size=0;
# This is to see how statements prepared via the PREPARE SQL command
# go into the query cache: if using parameters they cannot; if not
# using parameters they can.
# Query cache is abbreviated as "QC"
-- source include/have_query_cache.inc
connect (con1,127.0.0.1,root,,test,$MASTER_MYPORT,);
connection default;
set global query_cache_size=100000;
flush status;
create table t1(c1 int);
insert into t1 values(1),(10),(100);
# Prepared statements has no parameters, query caching should happen
prepare stmt1 from "select * from t1 where c1=10";
show status like 'Qcache_hits';
execute stmt1;
show status like 'Qcache_hits';
execute stmt1;
show status like 'Qcache_hits';
execute stmt1;
show status like 'Qcache_hits';
# Another prepared statement (same text, same connection), should hit the QC
prepare stmt2 from "select * from t1 where c1=10";
execute stmt2;
show status like 'Qcache_hits';
execute stmt2;
show status like 'Qcache_hits';
execute stmt2;
show status like 'Qcache_hits';
# Another prepared statement (same text, other connection), should hit the QC
connection con1;
prepare stmt3 from "select * from t1 where c1=10";
execute stmt3;
show status like 'Qcache_hits';
execute stmt3;
show status like 'Qcache_hits';
execute stmt3;
show status like 'Qcache_hits';
connection default;
# A non-prepared statement (same text, same connection), should hit
# the QC (as it uses the text protocol like SQL EXECUTE).
# But if it uses the binary protocol, it will not hit. So we make sure
# that it uses the text protocol:
-- disable_ps_protocol
select * from t1 where c1=10;
show status like 'Qcache_hits';
# A non-prepared statement (same text, other connection), should hit
# the QC. To test that it hits the result of SQL EXECUTE, we need to
# empty/repopulate the QC (to remove the result from the non-prepared
# SELECT just above).
flush tables;
execute stmt1;
show status like 'Qcache_hits';
connection con1;
select * from t1 where c1=10;
show status like 'Qcache_hits';
-- enable_ps_protocol
connection default;
# Prepared statement has parameters, query caching should not happen
prepare stmt1 from "select * from t1 where c1=?";
show status like 'Qcache_hits';
set @a=1;
execute stmt1 using @a;
show status like 'Qcache_hits';
set @a=100;
execute stmt1 using @a;
show status like 'Qcache_hits';
set @a=10;
execute stmt1 using @a;
show status like 'Qcache_hits';
# See if enabling/disabling the query cache between PREPARE and
# EXECUTE is an issue; the expected result is that the query cache
# will not be used.
# Indeed, decision to read/write the query cache is taken at PREPARE
# time, so if the query cache was disabled at PREPARE time then no
# execution of the statement will read/write the query cache.
# If the query cache was enabled at PREPARE time, but disabled at
# EXECUTE time, at EXECUTE time the query cache internal functions do
# nothing so again the query cache is not read/written. But if the
# query cache is re-enabled before another execution then that
# execution will read/write the query cache.
# QC is enabled at PREPARE
prepare stmt1 from "select * from t1 where c1=10";
# then QC is disabled at EXECUTE
set global query_cache_size=0;
show status like 'Qcache_hits';
execute stmt1;
show status like 'Qcache_hits';
execute stmt1;
show status like 'Qcache_hits';
execute stmt1;
show status like 'Qcache_hits';
# then QC is re-enabled for more EXECUTE.
set global query_cache_size=100000;
# Note that this execution will not hit results from the
# beginning of the test (because QC has been emptied meanwhile by
# setting its size to 0).
execute stmt1;
show status like 'Qcache_hits';
execute stmt1;
show status like 'Qcache_hits';
execute stmt1;
show status like 'Qcache_hits';
# QC is disabled at PREPARE
set global query_cache_size=0;
prepare stmt1 from "select * from t1 where c1=10";
# then QC is enabled at EXECUTE
set global query_cache_size=100000;
show status like 'Qcache_hits';
execute stmt1;
show status like 'Qcache_hits';
execute stmt1;
show status like 'Qcache_hits';
execute stmt1;
show status like 'Qcache_hits';
# QC is disabled at PREPARE
set global query_cache_size=0;
prepare stmt1 from "select * from t1 where c1=?";
# then QC is enabled at EXECUTE
set global query_cache_size=100000;
show status like 'Qcache_hits';
set @a=1;
execute stmt1 using @a;
show status like 'Qcache_hits';
set @a=100;
execute stmt1 using @a;
show status like 'Qcache_hits';
set @a=10;
execute stmt1 using @a;
show status like 'Qcache_hits';
drop table t1;
set global query_cache_size=0;
flush status; # reset Qcache status variables for next tests
......@@ -647,6 +647,7 @@ struct Query_cache_query_flags
{
unsigned int client_long_flag:1;
unsigned int client_protocol_41:1;
unsigned int result_in_binary_protocol:1;
unsigned int more_results_exists:1;
unsigned int pkt_nr;
uint character_set_client_num;
......@@ -673,6 +674,11 @@ struct Query_cache_query_flags
query_cache.send_result_to_client(A, B, C)
#define query_cache_invalidate_by_MyISAM_filename_ref \
&query_cache_invalidate_by_MyISAM_filename
/* note the "maybe": it's a read without mutex */
#define query_cache_maybe_disabled(T) \
(T->variables.query_cache_type == 0 || query_cache.query_cache_size == 0)
#define query_cache_is_cacheable_query(L) \
(((L)->sql_command == SQLCOM_SELECT) && (L)->safe_to_cache_query)
#else
#define QUERY_CACHE_FLAGS_SIZE 0
#define query_cache_store_query(A, B)
......@@ -689,6 +695,8 @@ struct Query_cache_query_flags
#define query_cache_abort(A)
#define query_cache_end_of_result(A)
#define query_cache_invalidate_by_MyISAM_filename_ref NULL
#define query_cache_maybe_disabled(T) 1
#define query_cache_is_cacheable_query(L) 0
#endif /*HAVE_QUERY_CACHE*/
/*
......
......@@ -35,7 +35,7 @@ static void write_eof_packet(THD *thd, NET *net);
#ifndef EMBEDDED_LIBRARY
bool Protocol::net_store_data(const char *from, uint length)
#else
bool Protocol_prep::net_store_data(const char *from, uint length)
bool Protocol_binary::net_store_data(const char *from, uint length)
#endif
{
ulong packet_length=packet->length();
......@@ -557,7 +557,7 @@ bool Protocol::send_fields(List<Item> *list, uint flags)
Item *item;
char buff[80];
String tmp((char*) buff,sizeof(buff),&my_charset_bin);
Protocol_simple prot(thd);
Protocol_text prot(thd);
String *local_packet= prot.storage_packet();
CHARSET_INFO *thd_charset= thd->variables.character_set_results;
DBUG_ENTER("send_fields");
......@@ -760,7 +760,7 @@ bool Protocol::store(I_List<i_string>* str_list)
****************************************************************************/
#ifndef EMBEDDED_LIBRARY
void Protocol_simple::prepare_for_resend()
void Protocol_text::prepare_for_resend()
{
packet->length(0);
#ifndef DBUG_OFF
......@@ -768,7 +768,7 @@ void Protocol_simple::prepare_for_resend()
#endif
}
bool Protocol_simple::store_null()
bool Protocol_text::store_null()
{
#ifndef DBUG_OFF
field_pos++;
......@@ -801,7 +801,7 @@ bool Protocol::store_string_aux(const char *from, uint length,
}
bool Protocol_simple::store(const char *from, uint length,
bool Protocol_text::store(const char *from, uint length,
CHARSET_INFO *fromcs, CHARSET_INFO *tocs)
{
#ifndef DBUG_OFF
......@@ -817,8 +817,8 @@ bool Protocol_simple::store(const char *from, uint length,
}
bool Protocol_simple::store(const char *from, uint length,
CHARSET_INFO *fromcs)
bool Protocol_text::store(const char *from, uint length,
CHARSET_INFO *fromcs)
{
CHARSET_INFO *tocs= this->thd->variables.character_set_results;
#ifndef DBUG_OFF
......@@ -834,7 +834,7 @@ bool Protocol_simple::store(const char *from, uint length,
}
bool Protocol_simple::store_tiny(longlong from)
bool Protocol_text::store_tiny(longlong from)
{
#ifndef DBUG_OFF
DBUG_ASSERT(field_types == 0 || field_types[field_pos] == MYSQL_TYPE_TINY);
......@@ -846,7 +846,7 @@ bool Protocol_simple::store_tiny(longlong from)
}
bool Protocol_simple::store_short(longlong from)
bool Protocol_text::store_short(longlong from)
{
#ifndef DBUG_OFF
DBUG_ASSERT(field_types == 0 ||
......@@ -860,7 +860,7 @@ bool Protocol_simple::store_short(longlong from)
}
bool Protocol_simple::store_long(longlong from)
bool Protocol_text::store_long(longlong from)
{
#ifndef DBUG_OFF
DBUG_ASSERT(field_types == 0 ||
......@@ -874,7 +874,7 @@ bool Protocol_simple::store_long(longlong from)
}
bool Protocol_simple::store_longlong(longlong from, bool unsigned_flag)
bool Protocol_text::store_longlong(longlong from, bool unsigned_flag)
{
#ifndef DBUG_OFF
DBUG_ASSERT(field_types == 0 ||
......@@ -889,7 +889,7 @@ bool Protocol_simple::store_longlong(longlong from, bool unsigned_flag)
}
bool Protocol_simple::store_decimal(const my_decimal *d)
bool Protocol_text::store_decimal(const my_decimal *d)
{
#ifndef DBUG_OFF
DBUG_ASSERT(field_types == 0 ||
......@@ -903,7 +903,7 @@ bool Protocol_simple::store_decimal(const my_decimal *d)
}
bool Protocol_simple::store(float from, uint32 decimals, String *buffer)
bool Protocol_text::store(float from, uint32 decimals, String *buffer)
{
#ifndef DBUG_OFF
DBUG_ASSERT(field_types == 0 ||
......@@ -915,7 +915,7 @@ bool Protocol_simple::store(float from, uint32 decimals, String *buffer)
}
bool Protocol_simple::store(double from, uint32 decimals, String *buffer)
bool Protocol_text::store(double from, uint32 decimals, String *buffer)
{
#ifndef DBUG_OFF
DBUG_ASSERT(field_types == 0 ||
......@@ -927,7 +927,7 @@ bool Protocol_simple::store(double from, uint32 decimals, String *buffer)
}
bool Protocol_simple::store(Field *field)
bool Protocol_text::store(Field *field)
{
if (field->is_null())
return store_null();
......@@ -961,7 +961,7 @@ bool Protocol_simple::store(Field *field)
*/
bool Protocol_simple::store(TIME *tm)
bool Protocol_text::store(TIME *tm)
{
#ifndef DBUG_OFF
DBUG_ASSERT(field_types == 0 ||
......@@ -984,7 +984,7 @@ bool Protocol_simple::store(TIME *tm)
}
bool Protocol_simple::store_date(TIME *tm)
bool Protocol_text::store_date(TIME *tm)
{
#ifndef DBUG_OFF
DBUG_ASSERT(field_types == 0 ||
......@@ -1003,7 +1003,7 @@ bool Protocol_simple::store_date(TIME *tm)
we support 0-6 decimals for time.
*/
bool Protocol_simple::store_time(TIME *tm)
bool Protocol_text::store_time(TIME *tm)
{
#ifndef DBUG_OFF
DBUG_ASSERT(field_types == 0 ||
......@@ -1043,7 +1043,7 @@ bool Protocol_simple::store_time(TIME *tm)
[..]..[[length]data] data
****************************************************************************/
bool Protocol_prep::prepare_for_send(List<Item> *item_list)
bool Protocol_binary::prepare_for_send(List<Item> *item_list)
{
Protocol::prepare_for_send(item_list);
bit_fields= (field_count+9)/8;
......@@ -1054,7 +1054,7 @@ bool Protocol_prep::prepare_for_send(List<Item> *item_list)
}
void Protocol_prep::prepare_for_resend()
void Protocol_binary::prepare_for_resend()
{
packet->length(bit_fields+1);
bzero((char*) packet->ptr(), 1+bit_fields);
......@@ -1062,21 +1062,22 @@ void Protocol_prep::prepare_for_resend()
}
bool Protocol_prep::store(const char *from, uint length, CHARSET_INFO *fromcs)
bool Protocol_binary::store(const char *from, uint length,
CHARSET_INFO *fromcs)
{
CHARSET_INFO *tocs= thd->variables.character_set_results;
field_pos++;
return store_string_aux(from, length, fromcs, tocs);
}
bool Protocol_prep::store(const char *from,uint length,
CHARSET_INFO *fromcs, CHARSET_INFO *tocs)
bool Protocol_binary::store(const char *from,uint length,
CHARSET_INFO *fromcs, CHARSET_INFO *tocs)
{
field_pos++;
return store_string_aux(from, length, fromcs, tocs);
}
bool Protocol_prep::store_null()
bool Protocol_binary::store_null()
{
uint offset= (field_pos+2)/8+1, bit= (1 << ((field_pos+2) & 7));
/* Room for this as it's allocated in prepare_for_send */
......@@ -1087,7 +1088,7 @@ bool Protocol_prep::store_null()
}
bool Protocol_prep::store_tiny(longlong from)
bool Protocol_binary::store_tiny(longlong from)
{
char buff[1];
field_pos++;
......@@ -1096,7 +1097,7 @@ bool Protocol_prep::store_tiny(longlong from)
}
bool Protocol_prep::store_short(longlong from)
bool Protocol_binary::store_short(longlong from)
{
field_pos++;
char *to= packet->prep_append(2, PACKET_BUFFER_EXTRA_ALLOC);
......@@ -1107,7 +1108,7 @@ bool Protocol_prep::store_short(longlong from)
}
bool Protocol_prep::store_long(longlong from)
bool Protocol_binary::store_long(longlong from)
{
field_pos++;
char *to= packet->prep_append(4, PACKET_BUFFER_EXTRA_ALLOC);
......@@ -1118,7 +1119,7 @@ bool Protocol_prep::store_long(longlong from)
}
bool Protocol_prep::store_longlong(longlong from, bool unsigned_flag)
bool Protocol_binary::store_longlong(longlong from, bool unsigned_flag)
{
field_pos++;
char *to= packet->prep_append(8, PACKET_BUFFER_EXTRA_ALLOC);
......@@ -1128,7 +1129,7 @@ bool Protocol_prep::store_longlong(longlong from, bool unsigned_flag)
return 0;
}
bool Protocol_prep::store_decimal(const my_decimal *d)
bool Protocol_binary::store_decimal(const my_decimal *d)
{
#ifndef DBUG_OFF
DBUG_ASSERT(field_types == 0 ||
......@@ -1141,7 +1142,7 @@ bool Protocol_prep::store_decimal(const my_decimal *d)
return store(str.ptr(), str.length(), str.charset());
}
bool Protocol_prep::store(float from, uint32 decimals, String *buffer)
bool Protocol_binary::store(float from, uint32 decimals, String *buffer)
{
field_pos++;
char *to= packet->prep_append(4, PACKET_BUFFER_EXTRA_ALLOC);
......@@ -1152,7 +1153,7 @@ bool Protocol_prep::store(float from, uint32 decimals, String *buffer)
}
bool Protocol_prep::store(double from, uint32 decimals, String *buffer)
bool Protocol_binary::store(double from, uint32 decimals, String *buffer)
{
field_pos++;
char *to= packet->prep_append(8, PACKET_BUFFER_EXTRA_ALLOC);
......@@ -1163,7 +1164,7 @@ bool Protocol_prep::store(double from, uint32 decimals, String *buffer)
}
bool Protocol_prep::store(Field *field)
bool Protocol_binary::store(Field *field)
{
/*
We should not increment field_pos here as send_binary() will call another
......@@ -1175,7 +1176,7 @@ bool Protocol_prep::store(Field *field)
}
bool Protocol_prep::store(TIME *tm)
bool Protocol_binary::store(TIME *tm)
{
char buff[12],*pos;
uint length;
......@@ -1201,15 +1202,15 @@ bool Protocol_prep::store(TIME *tm)
return packet->append(buff, length+1, PACKET_BUFFER_EXTRA_ALLOC);
}
bool Protocol_prep::store_date(TIME *tm)
bool Protocol_binary::store_date(TIME *tm)
{
tm->hour= tm->minute= tm->second=0;
tm->second_part= 0;
return Protocol_prep::store(tm);
return Protocol_binary::store(tm);
}
bool Protocol_prep::store_time(TIME *tm)
bool Protocol_binary::store_time(TIME *tm)
{
char buff[13], *pos;
uint length;
......
......@@ -98,16 +98,25 @@ class Protocol
#else
void remove_last_row() {}
#endif
enum enum_protocol_type
{
PROTOCOL_TEXT= 0, PROTOCOL_BINARY= 1
/*
before adding here or change the values, consider that it is cast to a
bit in sql_cache.cc.
*/
};
virtual enum enum_protocol_type type()= 0;
};
/* Class used for the old (MySQL 4.0 protocol) */
class Protocol_simple :public Protocol
class Protocol_text :public Protocol
{
public:
Protocol_simple() {}
Protocol_simple(THD *thd_arg) :Protocol(thd_arg) {}
Protocol_text() {}
Protocol_text(THD *thd_arg) :Protocol(thd_arg) {}
virtual void prepare_for_resend();
virtual bool store_null();
virtual bool store_tiny(longlong from);
......@@ -127,16 +136,17 @@ class Protocol_simple :public Protocol
#ifdef EMBEDDED_LIBRARY
void remove_last_row();
#endif
virtual enum enum_protocol_type type() { return PROTOCOL_TEXT; };
};
class Protocol_prep :public Protocol
class Protocol_binary :public Protocol
{
private:
uint bit_fields;
public:
Protocol_prep() {}
Protocol_prep(THD *thd_arg) :Protocol(thd_arg) {}
Protocol_binary() {}
Protocol_binary(THD *thd_arg) :Protocol(thd_arg) {}
virtual bool prepare_for_send(List<Item> *item_list);
virtual void prepare_for_resend();
#ifdef EMBEDDED_LIBRARY
......@@ -158,6 +168,7 @@ class Protocol_prep :public Protocol
virtual bool store(float nr, uint32 decimals, String *buffer);
virtual bool store(double from, uint32 decimals, String *buffer);
virtual bool store(Field *field);
virtual enum enum_protocol_type type() { return PROTOCOL_BINARY; };
};
void send_warning(THD *thd, uint sql_errno, const char *err=0);
......
......@@ -2739,8 +2739,8 @@ int set_var_collation_client::update(THD *thd)
thd->variables.character_set_results= character_set_results;
thd->variables.collation_connection= collation_connection;
thd->update_charset();
thd->protocol_simple.init(thd);
thd->protocol_prep.init(thd);
thd->protocol_text.init(thd);
thd->protocol_binary.init(thd);
return 0;
}
......
......@@ -844,6 +844,12 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used)
flags.client_long_flag= test(thd->client_capabilities & CLIENT_LONG_FLAG);
flags.client_protocol_41= test(thd->client_capabilities &
CLIENT_PROTOCOL_41);
/*
Protocol influences result format, so statement results in the binary
protocol (COM_EXECUTE) cannot be served to statements asking for results
in the text protocol (COM_QUERY) and vice-versa.
*/
flags.result_in_binary_protocol= (unsigned int) thd->protocol->type();
flags.more_results_exists= test(thd->server_status &
SERVER_MORE_RESULTS_EXISTS);
flags.pkt_nr= net->pkt_nr;
......@@ -861,11 +867,13 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used)
flags.max_sort_length= thd->variables.max_sort_length;
flags.lc_time_names= thd->variables.lc_time_names;
flags.group_concat_max_len= thd->variables.group_concat_max_len;
DBUG_PRINT("qcache", ("long %d, 4.1: %d, more results %d, pkt_nr: %d, \
DBUG_PRINT("qcache", ("\
long %d, 4.1: %d, bin_proto: %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",
(int)flags.client_long_flag,
(int)flags.client_protocol_41,
(int)flags.result_in_binary_protocol,
(int)flags.more_results_exists,
flags.pkt_nr,
flags.character_set_client_num,
......@@ -1089,6 +1097,7 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
flags.client_long_flag= test(thd->client_capabilities & CLIENT_LONG_FLAG);
flags.client_protocol_41= test(thd->client_capabilities &
CLIENT_PROTOCOL_41);
flags.result_in_binary_protocol= (unsigned int)thd->protocol->type();
flags.more_results_exists= test(thd->server_status &
SERVER_MORE_RESULTS_EXISTS);
flags.pkt_nr= thd->net.pkt_nr;
......@@ -1104,11 +1113,13 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
flags.max_sort_length= thd->variables.max_sort_length;
flags.group_concat_max_len= thd->variables.group_concat_max_len;
flags.lc_time_names= thd->variables.lc_time_names;
DBUG_PRINT("qcache", ("long %d, 4.1: %d, more results %d, pkt_nr: %d, \
DBUG_PRINT("qcache", ("\
long %d, 4.1: %d, bin_proto: %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",
(int)flags.client_long_flag,
(int)flags.client_protocol_41,
(int)flags.result_in_binary_protocol,
(int)flags.more_results_exists,
flags.pkt_nr,
flags.character_set_client_num,
......@@ -3048,11 +3059,10 @@ Query_cache::is_cacheable(THD *thd, uint32 query_len, char *query, LEX *lex,
TABLE_COUNTER_TYPE table_count;
DBUG_ENTER("Query_cache::is_cacheable");
if (lex->sql_command == SQLCOM_SELECT &&
if (query_cache_is_cacheable_query(lex) &&
(thd->variables.query_cache_type == 1 ||
(thd->variables.query_cache_type == 2 && (lex->select_lex.options &
OPTION_TO_QUERY_CACHE))) &&
lex->safe_to_cache_query)
OPTION_TO_QUERY_CACHE))))
{
DBUG_PRINT("qcache", ("options: %lx %lx type: %u",
(long) OPTION_TO_QUERY_CACHE,
......
......@@ -301,9 +301,9 @@ THD::THD()
bzero((char*) &user_var_events, sizeof(user_var_events));
/* Protocol */
protocol= &protocol_simple; // Default protocol
protocol_simple.init(this);
protocol_prep.init(this);
protocol= &protocol_text; // Default protocol
protocol_text.init(this);
protocol_binary.init(this);
tablespace_op=FALSE;
tmp= sql_rnd_with_mutex();
......
......@@ -904,8 +904,8 @@ class THD :public Statement,
NET net; // client connection descriptor
MEM_ROOT warn_root; // For warnings and errors
Protocol *protocol; // Current protocol
Protocol_simple protocol_simple; // Normal protocol
Protocol_prep protocol_prep; // Binary protocol
Protocol_text protocol_text; // Normal protocol
Protocol_binary protocol_binary; // Binary protocol
HASH user_vars; // hash for user variables
String packet; // dynamic buffer for network I/O
String convert_buffer; // buffer for charset conversions
......
......@@ -93,11 +93,11 @@ When one supplies long data for a placeholder:
/* A result class used to send cursor rows using the binary protocol. */
class Select_fetch_protocol_prep: public select_send
class Select_fetch_protocol_binary: public select_send
{
Protocol_prep protocol;
Protocol_binary protocol;
public:
Select_fetch_protocol_prep(THD *thd);
Select_fetch_protocol_binary(THD *thd);
virtual bool send_fields(List<Item> &list, uint flags);
virtual bool send_data(List<Item> &items);
virtual bool send_eof();
......@@ -125,7 +125,7 @@ class Prepared_statement: public Statement
};
THD *thd;
Select_fetch_protocol_prep result;
Select_fetch_protocol_binary result;
Protocol *protocol;
Item_param **param_array;
uint param_count;
......@@ -247,9 +247,9 @@ static bool send_prep_stmt(Prepared_statement *stmt, uint columns)
*/
DBUG_RETURN(my_net_write(net, buff, sizeof(buff)) ||
(stmt->param_count &&
stmt->thd->protocol_simple.send_fields((List<Item> *)
&stmt->lex->param_list,
Protocol::SEND_EOF)));
stmt->thd->protocol_text.send_fields((List<Item> *)
&stmt->lex->param_list,
Protocol::SEND_EOF)));
}
#else
static bool send_prep_stmt(Prepared_statement *stmt,
......@@ -691,7 +691,7 @@ static void setup_one_conversion_function(THD *thd, Item_param *param,
and generate a valid query for logging.
NOTES
This function, along with other _withlog functions is called when one of
This function, along with other _with_log functions is called when one of
binary, slow or general logs is open. Logging of prepared statements in
all cases is performed by means of conventional queries: if parameter
data was supplied from C API, each placeholder in the query is
......@@ -715,9 +715,9 @@ static void setup_one_conversion_function(THD *thd, Item_param *param,
0 if success, 1 otherwise
*/
static bool insert_params_withlog(Prepared_statement *stmt, uchar *null_array,
uchar *read_pos, uchar *data_end,
String *query)
static bool insert_params_with_log(Prepared_statement *stmt, uchar *null_array,
uchar *read_pos, uchar *data_end,
String *query)
{
THD *thd= stmt->thd;
Item_param **begin= stmt->param_array;
......@@ -725,7 +725,7 @@ static bool insert_params_withlog(Prepared_statement *stmt, uchar *null_array,
uint32 length= 0;
String str;
const String *res;
DBUG_ENTER("insert_params_withlog");
DBUG_ENTER("insert_params_with_log");
if (query->copy(stmt->query, stmt->query_length, default_charset_info))
DBUG_RETURN(1);
......@@ -869,7 +869,8 @@ static bool emb_insert_params(Prepared_statement *stmt, String *expanded_query)
}
static bool emb_insert_params_withlog(Prepared_statement *stmt, String *query)
static bool emb_insert_params_with_log(Prepared_statement *stmt,
String *query)
{
THD *thd= stmt->thd;
Item_param **it= stmt->param_array;
......@@ -880,7 +881,7 @@ static bool emb_insert_params_withlog(Prepared_statement *stmt, String *query)
const String *res;
uint32 length= 0;
DBUG_ENTER("emb_insert_params_withlog");
DBUG_ENTER("emb_insert_params_with_log");
if (query->copy(stmt->query, stmt->query_length, default_charset_info))
DBUG_RETURN(1);
......@@ -1889,7 +1890,7 @@ void mysql_stmt_prepare(THD *thd, const char *packet, uint packet_length)
/* First of all clear possible warnings from the previous command */
mysql_reset_thd_for_next_command(thd);
if (! (stmt= new Prepared_statement(thd, &thd->protocol_prep)))
if (! (stmt= new Prepared_statement(thd, &thd->protocol_binary)))
DBUG_VOID_RETURN; /* out of memory: error is set in Sql_alloc */
if (thd->stmt_map.insert(thd, stmt))
......@@ -2061,8 +2062,8 @@ void mysql_sql_stmt_prepare(THD *thd)
const char *query;
uint query_len;
DBUG_ENTER("mysql_sql_stmt_prepare");
DBUG_ASSERT(thd->protocol == &thd->protocol_simple);
LINT_INIT(query_len);
DBUG_ASSERT(thd->protocol == &thd->protocol_text);
if ((stmt= (Prepared_statement*) thd->stmt_map.find_by_name(name)))
{
......@@ -2075,7 +2076,7 @@ void mysql_sql_stmt_prepare(THD *thd)
}
if (! (query= get_dynamic_sql_string(lex, &query_len)) ||
! (stmt= new Prepared_statement(thd, &thd->protocol_simple)))
! (stmt= new Prepared_statement(thd, &thd->protocol_text)))
{
DBUG_VOID_RETURN; /* out of memory */
}
......@@ -2628,14 +2629,14 @@ void mysql_stmt_get_longdata(THD *thd, char *packet, ulong packet_length)
/***************************************************************************
Select_fetch_protocol_prep
Select_fetch_protocol_binary
****************************************************************************/
Select_fetch_protocol_prep::Select_fetch_protocol_prep(THD *thd_arg)
Select_fetch_protocol_binary::Select_fetch_protocol_binary(THD *thd_arg)
:protocol(thd_arg)
{}
bool Select_fetch_protocol_prep::send_fields(List<Item> &list, uint flags)
bool Select_fetch_protocol_binary::send_fields(List<Item> &list, uint flags)
{
bool rc;
Protocol *save_protocol= thd->protocol;
......@@ -2653,7 +2654,7 @@ bool Select_fetch_protocol_prep::send_fields(List<Item> &list, uint flags)
return rc;
}
bool Select_fetch_protocol_prep::send_eof()
bool Select_fetch_protocol_binary::send_eof()
{
Protocol *save_protocol= thd->protocol;
......@@ -2665,7 +2666,7 @@ bool Select_fetch_protocol_prep::send_eof()
bool
Select_fetch_protocol_prep::send_data(List<Item> &fields)
Select_fetch_protocol_binary::send_data(List<Item> &fields)
{
Protocol *save_protocol= thd->protocol;
bool rc;
......@@ -2699,15 +2700,26 @@ Prepared_statement::Prepared_statement(THD *thd_arg, Protocol *protocol_arg)
void Prepared_statement::setup_set_params()
{
/* Setup binary logging */
/*
Note: BUG#25843 applies here too (query cache lookup uses thd->db, not
db from "prepare" time).
*/
if (query_cache_maybe_disabled(thd)) // we won't expand the query
lex->safe_to_cache_query= FALSE; // so don't cache it at Execution
/*
Decide if we have to expand the query (because we must write it to logs or
because we want to look it up in the query cache) or not.
*/
if (mysql_bin_log.is_open() && is_update_query(lex->sql_command) ||
opt_log || opt_slow_log)
opt_log || opt_slow_log ||
query_cache_is_cacheable_query(lex))
{
set_params_from_vars= insert_params_from_vars_with_log;
#ifndef EMBEDDED_LIBRARY
set_params= insert_params_withlog;
set_params= insert_params_with_log;
#else
set_params_data= emb_insert_params_withlog;
set_params_data= emb_insert_params_with_log;
#endif
}
else
......@@ -2844,7 +2856,6 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
error= MYSQLparse((void *)thd) || thd->is_fatal_error ||
thd->net.report_error || init_param_array(this);
lex->safe_to_cache_query= FALSE;
/*
While doing context analysis of the query (in check_prepared_statement)
we allocate a lot of additional memory: for open tables, JOINs, derived
......@@ -2887,6 +2898,18 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
thd->restore_backup_statement(this, &stmt_backup);
thd->stmt_arena= old_stmt_arena;
if ((protocol->type() == Protocol::PROTOCOL_TEXT) && (param_count > 0))
{
/*
This is a mysql_sql_stmt_prepare(); query expansion will insert user
variable references, and user variables are uncacheable, thus we have to
mark this statement as uncacheable.
This has to be done before setup_set_params(), as it may make expansion
unneeded.
*/
lex->safe_to_cache_query= FALSE;
}
if (error == 0)
{
setup_set_params();
......@@ -3003,11 +3026,26 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor)
reinit_stmt_before_use(thd, lex);
thd->protocol= protocol; /* activate stmt protocol */
error= (open_cursor ?
mysql_open_cursor(thd, (uint) ALWAYS_MATERIALIZED_CURSOR,
&result, &cursor) :
mysql_execute_command(thd));
thd->protocol= &thd->protocol_simple; /* use normal protocol */
if (open_cursor)
error= mysql_open_cursor(thd, (uint) ALWAYS_MATERIALIZED_CURSOR,
&result, &cursor);
else
{
/*
Try to find it in the query cache, if not, execute it.
Note that multi-statements cannot exist here (they are not supported in
prepared statements).
*/
if (query_cache_send_result_to_client(thd, thd->query,
thd->query_length) <= 0)
{
error= mysql_execute_command(thd);
query_cache_end_of_result(thd);
}
}
thd->protocol= &thd->protocol_text; /* use normal protocol */
/* Assert that if an error, no cursor is open */
DBUG_ASSERT(! (error && cursor));
......
......@@ -2354,6 +2354,271 @@ static void test_ps_conj_select()
}
/* reads Qcache_hits from server and returns its value */
static uint query_cache_hits(MYSQL *conn)
{
MYSQL_RES *res;
MYSQL_ROW row;
int rc;
uint result;
rc= mysql_query(conn, "show status like 'qcache_hits'");
myquery(rc);
res= mysql_use_result(conn);
DIE_UNLESS(res);
row= mysql_fetch_row(res);
DIE_UNLESS(row);
result= atoi(row[1]);
mysql_free_result(res);
return result;
}
/*
utility for the next test; expects 3 rows in the result from a SELECT,
compares each row/field with an expected value.
*/
#define test_ps_query_cache_result(i1,s1,l1,i2,s2,l2,i3,s3,l3) \
r_metadata= mysql_stmt_result_metadata(stmt); \
DIE_UNLESS(r_metadata != NULL); \
rc= mysql_stmt_fetch(stmt); \
check_execute(stmt, rc); \
if (!opt_silent) \
fprintf(stdout, "\n row 1: %d, %s(%lu)", r_int_data, \
r_str_data, r_str_length); \
DIE_UNLESS((r_int_data == i1) && (r_str_length == l1) && \
(strcmp(r_str_data, s1) == 0)); \
rc= mysql_stmt_fetch(stmt); \
check_execute(stmt, rc); \
if (!opt_silent) \
fprintf(stdout, "\n row 2: %d, %s(%lu)", r_int_data, \
r_str_data, r_str_length); \
DIE_UNLESS((r_int_data == i2) && (r_str_length == l2) && \
(strcmp(r_str_data, s2) == 0)); \
rc= mysql_stmt_fetch(stmt); \
check_execute(stmt, rc); \
if (!opt_silent) \
fprintf(stdout, "\n row 3: %d, %s(%lu)", r_int_data, \
r_str_data, r_str_length); \
DIE_UNLESS((r_int_data == i3) && (r_str_length == l3) && \
(strcmp(r_str_data, s3) == 0)); \
rc= mysql_stmt_fetch(stmt); \
DIE_UNLESS(rc == MYSQL_NO_DATA); \
mysql_free_result(r_metadata);
/*
Test that prepared statements make use of the query cache just as normal
statements (BUG#735).
*/
static void test_ps_query_cache()
{
MYSQL *org_mysql= mysql, *lmysql;
MYSQL_STMT *stmt;
int rc;
MYSQL_BIND p_bind[2],r_bind[2]; /* p: param bind; r: result bind */
int32 p_int_data, r_int_data;
char p_str_data[32], r_str_data[32];
unsigned long p_str_length, r_str_length;
MYSQL_RES *r_metadata;
char query[MAX_TEST_QUERY_LENGTH];
uint hits1, hits2;
enum enum_test_ps_query_cache
{
/*
We iterate the same prepare/executes block, but have iterations where
we vary the query cache conditions.
*/
/* the query cache is enabled for the duration of prep&execs: */
TEST_QCACHE_ON= 0,
/*
same but using a new connection (to see if qcache serves results from
the previous connection as it should):
*/
TEST_QCACHE_ON_WITH_OTHER_CONN,
/*
First border case: disables the query cache before prepare and
re-enables it before execution (to test if we have no bug then):
*/
TEST_QCACHE_OFF_ON,
/*
Second border case: enables the query cache before prepare and
disables it before execution:
*/
TEST_QCACHE_ON_OFF
};
enum enum_test_ps_query_cache iteration;
LINT_INIT(lmysql);
myheader("test_ps_query_cache");
/* prepare the table */
rc= mysql_query(mysql, "drop table if exists t1");
myquery(rc);
rc= mysql_query(mysql, "create table t1 (id1 int(11) NOT NULL default '0', "
"value2 varchar(100), value1 varchar(100))");
myquery(rc);
rc= mysql_query(mysql, "insert into t1 values (1, 'hh', 'hh'), "
"(2, 'hh', 'hh'), (1, 'ii', 'ii'), (2, 'ii', 'ii')");
myquery(rc);
for (iteration= TEST_QCACHE_ON; iteration < TEST_QCACHE_ON_OFF; iteration++)
{
switch (iteration)
{
case TEST_QCACHE_ON:
case TEST_QCACHE_ON_OFF:
rc= mysql_query(mysql, "set global query_cache_size=1000000");
myquery(rc);
break;
case TEST_QCACHE_OFF_ON:
rc= mysql_query(mysql, "set global query_cache_size=0");
myquery(rc);
break;
case TEST_QCACHE_ON_WITH_OTHER_CONN:
if (!opt_silent)
fprintf(stdout, "\n Establishing a test connection ...");
if (!(lmysql= mysql_init(NULL)))
{
myerror("mysql_init() failed");
exit(1);
}
if (!(mysql_real_connect(lmysql, opt_host, opt_user,
opt_password, current_db, opt_port,
opt_unix_socket, 0)))
{
myerror("connection failed");
mysql_close(lmysql);
exit(1);
}
if (!opt_silent)
fprintf(stdout, " OK");
mysql= lmysql;
}
strmov(query, "select id1, value1 from t1 where id1= ? or "
"CONVERT(value1 USING utf8)= ?");
stmt= mysql_simple_prepare(mysql, query);
check_stmt(stmt);
verify_param_count(stmt, 2);
switch(iteration)
{
case TEST_QCACHE_OFF_ON:
rc= mysql_query(mysql, "set global query_cache_size=1000000");
myquery(rc);
break;
case TEST_QCACHE_ON_OFF:
rc= mysql_query(mysql, "set global query_cache_size=0");
myquery(rc);
default:
break;
}
bzero((char*) p_bind, sizeof(p_bind));
p_bind[0].buffer_type= MYSQL_TYPE_LONG;
p_bind[0].buffer= (void *)&p_int_data;
p_bind[1].buffer_type= MYSQL_TYPE_VAR_STRING;
p_bind[1].buffer= (void *)p_str_data;
p_bind[1].buffer_length= array_elements(p_str_data);
p_bind[1].length= &p_str_length;
rc= mysql_stmt_bind_param(stmt, p_bind);
check_execute(stmt, rc);
p_int_data= 1;
strmov(p_str_data, "hh");
p_str_length= strlen(p_str_data);
bzero((char*) r_bind, sizeof(r_bind));
r_bind[0].buffer_type= MYSQL_TYPE_LONG;
r_bind[0].buffer= (void *)&r_int_data;
r_bind[1].buffer_type= MYSQL_TYPE_VAR_STRING;
r_bind[1].buffer= (void *)r_str_data;
r_bind[1].buffer_length= array_elements(r_str_data);
r_bind[1].length= &r_str_length;
rc= mysql_stmt_bind_result(stmt, r_bind);
check_execute(stmt, rc);
rc= mysql_stmt_execute(stmt);
check_execute(stmt, rc);
test_ps_query_cache_result(1, "hh", 2, 2, "hh", 2, 1, "ii", 2);
/* now retry with the same parameter values and see qcache hits */
hits1= query_cache_hits(mysql);
rc= mysql_stmt_execute(stmt);
check_execute(stmt, rc);
test_ps_query_cache_result(1, "hh", 2, 2, "hh", 2, 1, "ii", 2);
hits2= query_cache_hits(mysql);
switch(iteration)
{
case TEST_QCACHE_ON_WITH_OTHER_CONN:
case TEST_QCACHE_ON: /* should have hit */
DIE_UNLESS(hits2-hits1 == 1);
break;
case TEST_QCACHE_OFF_ON:
case TEST_QCACHE_ON_OFF: /* should not have hit */
DIE_UNLESS(hits2-hits1 == 0);
}
/* now modify parameter values and see qcache hits */
strmov(p_str_data, "ii");
p_str_length= strlen(p_str_data);
rc= mysql_stmt_execute(stmt);
check_execute(stmt, rc);
test_ps_query_cache_result(1, "hh", 2, 1, "ii", 2, 2, "ii", 2);
hits1= query_cache_hits(mysql);
switch(iteration)
{
case TEST_QCACHE_ON:
case TEST_QCACHE_OFF_ON:
case TEST_QCACHE_ON_OFF: /* should not have hit */
DIE_UNLESS(hits2-hits1 == 0);
break;
case TEST_QCACHE_ON_WITH_OTHER_CONN: /* should have hit */
DIE_UNLESS(hits1-hits2 == 1);
}
rc= mysql_stmt_execute(stmt);
check_execute(stmt, rc);
test_ps_query_cache_result(1, "hh", 2, 1, "ii", 2, 2, "ii", 2);
hits2= query_cache_hits(mysql);
mysql_stmt_close(stmt);
switch(iteration)
{
case TEST_QCACHE_ON: /* should have hit */
DIE_UNLESS(hits2-hits1 == 1);
break;
case TEST_QCACHE_OFF_ON:
case TEST_QCACHE_ON_OFF: /* should not have hit */
DIE_UNLESS(hits2-hits1 == 0);
break;
case TEST_QCACHE_ON_WITH_OTHER_CONN:
mysql_close(lmysql);
mysql= org_mysql;
}
} /* for(iteration=...) */
rc= mysql_query(mysql, "set global query_cache_size=0");
myquery(rc);
}
/* Test BUG#1115 (incorrect string parameter value allocation) */
static void test_bug1115()
......@@ -4722,7 +4987,7 @@ static void test_stmt_close()
close statements by hand once mysql_close() had been called.
Now mysql_close() doesn't free any statements, so this test doesn't
serve its original designation any more.
Here we free stmt2 and stmt3 by hande to avoid memory leaks.
Here we free stmt2 and stmt3 by hand to avoid memory leaks.
*/
mysql_stmt_close(stmt2);
mysql_stmt_close(stmt3);
......@@ -16067,8 +16332,9 @@ static struct my_tests_st my_tests[]= {
{ "test_bug15518", test_bug15518 },
{ "test_bug23383", test_bug23383 },
{ "test_bug21635", test_bug21635 },
{ "test_status", test_status},
{ "test_status", test_status },
{ "test_bug24179", test_bug24179 },
{ "test_ps_query_cache", test_ps_query_cache },
{ 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