summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libmysqld/lib_sql.cc12
-rw-r--r--mysql-test/include/have_query_cache.inc3
-rw-r--r--mysql-test/r/query_cache.result23
-rw-r--r--mysql-test/r/query_cache_sql_prepare.result204
-rw-r--r--mysql-test/t/grant_cache.test26
-rw-r--r--mysql-test/t/ndb_cache_multi2.test31
-rw-r--r--mysql-test/t/query_cache.test20
-rw-r--r--mysql-test/t/query_cache_sql_prepare.test144
-rw-r--r--sql/mysql_priv.h8
-rw-r--r--sql/protocol.cc73
-rw-r--r--sql/protocol.h23
-rw-r--r--sql/set_var.cc4
-rw-r--r--sql/sql_cache.cc20
-rw-r--r--sql/sql_class.cc6
-rw-r--r--sql/sql_class.h4
-rw-r--r--sql/sql_prepare.cc102
-rw-r--r--tests/mysql_client_test.c270
17 files changed, 876 insertions, 97 deletions
diff --git a/libmysqld/lib_sql.cc b/libmysqld/lib_sql.cc
index 87518b41561..dbec5bf6bbf 100644
--- a/libmysqld/lib_sql.cc
+++ b/libmysqld/lib_sql.cc
@@ -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;
diff --git a/mysql-test/include/have_query_cache.inc b/mysql-test/include/have_query_cache.inc
index 39549157849..e5e6052c9a7 100644
--- a/mysql-test/include/have_query_cache.inc
+++ b/mysql-test/include/have_query_cache.inc
@@ -1,7 +1,4 @@
-- 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;
diff --git a/mysql-test/r/query_cache.result b/mysql-test/r/query_cache.result
index 54b35827cea..657f1387136 100644
--- a/mysql-test/r/query_cache.result
+++ b/mysql-test/r/query_cache.result
@@ -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;
diff --git a/mysql-test/r/query_cache_sql_prepare.result b/mysql-test/r/query_cache_sql_prepare.result
new file mode 100644
index 00000000000..64af5bc4ec2
--- /dev/null
+++ b/mysql-test/r/query_cache_sql_prepare.result
@@ -0,0 +1,204 @@
+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;
diff --git a/mysql-test/t/grant_cache.test b/mysql-test/t/grant_cache.test
index 7e17a03ec21..c28dd12cdee 100644
--- a/mysql-test/t/grant_cache.test
+++ b/mysql-test/t/grant_cache.test
@@ -1,6 +1,8 @@
# 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
diff --git a/mysql-test/t/ndb_cache_multi2.test b/mysql-test/t/ndb_cache_multi2.test
index 4abb537624a..2afcf0c18f7 100644
--- a/mysql-test/t/ndb_cache_multi2.test
+++ b/mysql-test/t/ndb_cache_multi2.test
@@ -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.
diff --git a/mysql-test/t/query_cache.test b/mysql-test/t/query_cache.test
index ad7fd90ed63..d475fc65136 100644
--- a/mysql-test/t/query_cache.test
+++ b/mysql-test/t/query_cache.test
@@ -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;
diff --git a/mysql-test/t/query_cache_sql_prepare.test b/mysql-test/t/query_cache_sql_prepare.test
new file mode 100644
index 00000000000..69b504e2fd1
--- /dev/null
+++ b/mysql-test/t/query_cache_sql_prepare.test
@@ -0,0 +1,144 @@
+# 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
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index ba8960950db..b7fe500324f 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -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*/
/*
diff --git a/sql/protocol.cc b/sql/protocol.cc
index 05e98c68e4e..5aa3b7b5055 100644
--- a/sql/protocol.cc
+++ b/sql/protocol.cc
@@ -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;
diff --git a/sql/protocol.h b/sql/protocol.h
index 6c4c7414ea5..da49cf769ae 100644
--- a/sql/protocol.h
+++ b/sql/protocol.h
@@ -98,16 +98,25 @@ public:
#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 @@ public:
#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 @@ public:
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);
diff --git a/sql/set_var.cc b/sql/set_var.cc
index e2c90b72feb..91d97b71c1b 100644
--- a/sql/set_var.cc
+++ b/sql/set_var.cc
@@ -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;
}
diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc
index c06d7161ec1..89b7a25033f 100644
--- a/sql/sql_cache.cc
+++ b/sql/sql_cache.cc
@@ -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,
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 836a4d07f0b..e8e39972020 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -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();
diff --git a/sql/sql_class.h b/sql/sql_class.h
index c24e18ccffa..f492db82ec7 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -904,8 +904,8 @@ public:
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
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 292f9ebd344..ae516485a2b 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -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 @@ public:
};
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));
diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c
index 841050a104e..b12d5fa1db0 100644
--- a/tests/mysql_client_test.c
+++ b/tests/mysql_client_test.c
@@ -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 }
};