mirror of
https://github.com/MariaDB/server.git
synced 2025-08-08 11:22:35 +03:00
Merge gbichot@bk-internal.mysql.com:/home/bk/mysql-5.1-runtime
into gbichot3.local:/home/mysql_src/mysql-5.1-runtime-735-realfix
This commit is contained in:
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
204
mysql-test/r/query_cache_sql_prepare.result
Normal file
204
mysql-test/r/query_cache_sql_prepare.result
Normal file
@@ -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;
|
@@ -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
|
||||
|
@@ -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;
|
||||
|
144
mysql-test/t/query_cache_sql_prepare.test
Normal file
144
mysql-test/t/query_cache_sql_prepare.test
Normal file
@@ -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
|
@@ -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 @@ 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);
|
||||
|
@@ -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 @@ 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
|
||||
|
@@ -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));
|
||||
|
@@ -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 }
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user