summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Docs/manual.texi10
-rw-r--r--client/mysqltest.c9
-rw-r--r--include/raid.h4
-rw-r--r--mysql-test/r/drop.result36
-rw-r--r--mysql-test/r/flush.result12
-rw-r--r--mysql-test/r/grant_cache.result153
-rw-r--r--mysql-test/r/innodb.result18
-rw-r--r--mysql-test/r/query_cache.result12
-rw-r--r--mysql-test/t/drop.test38
-rw-r--r--mysql-test/t/flush.test12
-rw-r--r--mysql-test/t/grant_cache-master.opt1
-rw-r--r--mysql-test/t/grant_cache.test102
-rw-r--r--mysql-test/t/innodb.test18
-rw-r--r--mysql-test/t/query_cache.test12
-rw-r--r--sql/mysql_priv.h5
-rw-r--r--sql/net_pkg.cc7
-rw-r--r--sql/net_serv.cc3
-rw-r--r--sql/sql_cache.cc223
-rw-r--r--sql/sql_cache.h16
-rw-r--r--sql/sql_do.cc1
-rw-r--r--sql/sql_parse.cc40
21 files changed, 527 insertions, 205 deletions
diff --git a/Docs/manual.texi b/Docs/manual.texi
index 80eed993837..fdcc11687d2 100644
--- a/Docs/manual.texi
+++ b/Docs/manual.texi
@@ -35574,6 +35574,10 @@ In addition, a query may be seen as different if for instance one
client is using a new communication protocol format or another
character set than another client.
+Queries that uses different databases, uses different protocol versions
+or the uses different default character sets are considered different
+queries and cached separately.
+
The cache does work for @code{SELECT CALC_ROWS ...} and
@code{SELECT FOUND_ROWS() ...} type queries because the number of
found rows is also stored in the cache.
@@ -35612,8 +35616,12 @@ of the form @code{SELECT * FROM AUTOINCREMENT_FIELD IS NULL}
However, @code{FOUND ROWS()} will return the correct value,
even if the preceding query was fetched from the cache.
-Queries that don't use any tables are not cached.
+Queries that don't use any tables or if the user has a column privilege for
+any of the involved tables are not cached.
+Before a query is fetched from the query cache, MySQL will check that
+the user has SELECT privilege to all the involved databases and
+tables. If this is not the case, the cached result will not be used.
@node Query Cache Configuration, Query Cache in SELECT, Query Cache How, Query Cache
@subsection Query Cache Configuration
diff --git a/client/mysqltest.c b/client/mysqltest.c
index 9d98f25eb7b..762589b0374 100644
--- a/client/mysqltest.c
+++ b/client/mysqltest.c
@@ -1248,7 +1248,8 @@ int close_connection(struct st_query* q)
}
-/* this one now is a hack - we may want to improve in in the
+/*
+ This one now is a hack - we may want to improve in in the
future to handle quotes. For now we assume that anything that is not
a comma, a space or ) belongs to the argument. space is a chopper, comma or
) are delimiters/terminators
@@ -1291,8 +1292,7 @@ int safe_connect(MYSQL* con, const char* host, const char* user,
int i;
for (i = 0; i < MAX_CON_TRIES; ++i)
{
- if(mysql_real_connect(con, host,user, pass,
- db, port, sock, 0))
+ if (mysql_real_connect(con, host,user, pass, db, port, sock, 0))
{
con_error = 0;
break;
@@ -1365,6 +1365,9 @@ int do_connect(struct st_query* q)
con_sock=fn_format(buff, con_sock, TMPDIR, "",0);
if (!con_db[0])
con_db=db;
+ /* Special database to allow one to connect without a database name */
+ if (!strcmp(con_db,"*NO-ONE*"))
+ con_db=0;
if ((con_error = safe_connect(&next_con->mysql, con_host,
con_user, con_pass,
con_db, con_port, con_sock ? con_sock: 0)))
diff --git a/include/raid.h b/include/raid.h
index 7655fbe09f7..4a988760157 100644
--- a/include/raid.h
+++ b/include/raid.h
@@ -22,8 +22,10 @@
#define RAID_DEFAULT_CHUNKS 4
#define RAID_DEFAULT_CHUNKSIZE 256*1024 /* 256kB */
-extern const char *raid_type_string[];
+C_MODE_START
#define my_raid_type(raid_type) raid_type_string[(int)(raid_type)]
+extern const char *raid_type_string[];
+C_MODE_END
#if defined(USE_RAID) && !defined(DONT_USE_RAID)
diff --git a/mysql-test/r/drop.result b/mysql-test/r/drop.result
index 7c1b7c5b60d..178c3a8cb4f 100644
--- a/mysql-test/r/drop.result
+++ b/mysql-test/r/drop.result
@@ -12,37 +12,37 @@ drop table t1;
select * from t1;
n
1
-drop database if exists foo;
-create database foo;
-drop database if exists foo;
-create database foo;
-create table foo.foo (n int);
-insert into foo.foo values (4);
-select * from foo.foo;
+drop database if exists mysqltest;
+create database mysqltest;
+drop database if exists mysqltest;
+create database mysqltest;
+create table mysqltest.mysqltest (n int);
+insert into mysqltest.mysqltest values (4);
+select * from mysqltest.mysqltest;
n
4
-drop database if exists foo;
-create database foo;
-drop database foo;
-drop database if exists foo;
+drop database if exists mysqltest;
+create database mysqltest;
+drop database mysqltest;
+drop database if exists mysqltest;
flush tables with read lock;
-create database foo;
+create database mysqltest;
Got one of the listed errors
unlock tables;
-create database foo;
+create database mysqltest;
show databases;
Database
-foo
mysql
+mysqltest
test
flush tables with read lock;
-drop database foo;
+drop database mysqltest;
Got one of the listed errors
unlock tables;
-drop database foo;
+drop database mysqltest;
show databases;
Database
mysql
test
-drop database foo;
-Can't drop database 'foo'. Database doesn't exist
+drop database mysqltest;
+Can't drop database 'mysqltest'. Database doesn't exist
diff --git a/mysql-test/r/flush.result b/mysql-test/r/flush.result
index 99d212ee49c..a7f73a6840b 100644
--- a/mysql-test/r/flush.result
+++ b/mysql-test/r/flush.result
@@ -11,13 +11,13 @@ drop table t2;
Table 't2' was locked with a READ lock and can't be updated
drop table t2;
unlock tables;
-drop database if exists foo;
-create database foo;
-create table foo.t1(n int);
-insert into foo.t1 values (23);
+drop database if exists mysqltest;
+create database mysqltest;
+create table mysqltest.t1(n int);
+insert into mysqltest.t1 values (23);
flush tables with read lock;
- drop database foo;
-select * from foo.t1;
+ drop database mysqltest;
+select * from mysqltest.t1;
n
23
unlock tables;
diff --git a/mysql-test/r/grant_cache.result b/mysql-test/r/grant_cache.result
new file mode 100644
index 00000000000..d236c26d71a
--- /dev/null
+++ b/mysql-test/r/grant_cache.result
@@ -0,0 +1,153 @@
+drop table if exists test.t1,mysqltest.t1,mysqltest.t2;
+reset query cache;
+flush status;
+create database if not exists mysqltest;
+create table mysqltest.t1 (a int,b int,c int);
+create table mysqltest.t2 (a int,b int,c int);
+insert into mysqltest.t1 values (1,1,1),(2,2,2);
+insert into mysqltest.t2 values (3,3,3);
+create table test.t1 (a char (10));
+insert into test.t1 values ("test.t1");
+select * from t1;
+a
+test.t1
+select * from t1;
+a b c
+1 1 1
+2 2 2
+select a from t1;
+a
+1
+2
+select c from t1;
+c
+1
+2
+select * from t2;
+a b c
+3 3 3
+select * from mysqltest.t1,test.t1;
+a b c a
+1 1 1 test.t1
+2 2 2 test.t1
+show status like "Qcache_queries_in_cache";
+Variable_name Value
+Qcache_queries_in_cache 6
+show status like "Qcache_hits%";
+Variable_name Value
+Qcache_hits 0
+grant SELECT on mysqltest.* to mysqltest_1@localhost;
+grant SELECT on mysqltest.t1 to mysqltest_2@localhost;
+grant SELECT on test.t1 to mysqltest_2@localhost;
+grant SELECT(a) on mysqltest.t1 to mysqltest_3@localhost;
+select "user1";
+user1
+user1
+select * from t1;
+a b c
+1 1 1
+2 2 2
+select a from t1 ;
+a
+1
+2
+select c from t1;
+c
+1
+2
+show status like "Qcache_queries_in_cache";
+Variable_name Value
+Qcache_queries_in_cache 6
+show status like "Qcache_hits";
+Variable_name Value
+Qcache_hits 3
+show status like "Qcache_not_cached";
+Variable_name Value
+Qcache_not_cached 1
+select "user2";
+user2
+user2
+select * from t1;
+a b c
+1 1 1
+2 2 2
+select a from t1;
+a
+1
+2
+select c from t1;
+c
+1
+2
+select * from mysqltest.t1,test.t1;
+a b c a
+1 1 1 test.t1
+2 2 2 test.t1
+select * from t2;
+select command denied to user: 'mysqltest_2@localhost' for table 't2'
+show status like "Qcache_queries_in_cache";
+Variable_name Value
+Qcache_queries_in_cache 6
+show status like "Qcache_hits";
+Variable_name Value
+Qcache_hits 7
+show status like "Qcache_not_cached";
+Variable_name Value
+Qcache_not_cached 3
+select "user3";
+user3
+user3
+select * from t1;
+select command denied to user: 'mysqltest_3@localhost' for column 'b' in table 't1'
+select a from t1;
+a
+1
+2
+select c from t1;
+select command denied to user: 'mysqltest_3@localhost' for column 'c' in table 't1'
+select * from t2;
+select command denied to user: 'mysqltest_3@localhost' for table 't2'
+select mysqltest.t1.c from test.t1,mysqltest.t1;
+select command denied to user: 'mysqltest_3@localhost' for column 'c' in table 't1'
+show status like "Qcache_queries_in_cache";
+Variable_name Value
+Qcache_queries_in_cache 6
+show status like "Qcache_hits";
+Variable_name Value
+Qcache_hits 7
+show status like "Qcache_not_cached";
+Variable_name Value
+Qcache_not_cached 8
+select "user4";
+user4
+user4
+select a from t1;
+No Database Selected
+select * from mysqltest.t1,test.t1;
+a b c a
+1 1 1 test.t1
+2 2 2 test.t1
+select a from mysqltest.t1;
+a
+1
+2
+select a from mysqltest.t1;
+a
+1
+2
+show status like "Qcache_queries_in_cache";
+Variable_name Value
+Qcache_queries_in_cache 8
+show status like "Qcache_hits";
+Variable_name Value
+Qcache_hits 8
+show status like "Qcache_not_cached";
+Variable_name Value
+Qcache_not_cached 9
+delete from mysql.user where user in ("mysqltest_1","mysqltest_2","mysqltest_3");
+delete from mysql.db where user in ("mysqltest_1","mysqltest_2","mysqltest_3");
+delete from mysql.tables_priv where user in ("mysqltest_1","mysqltest_2","mysqltest_3");
+delete from mysql.columns_priv where user in ("mysqltest_1","mysqltest_2","mysqltest_3");
+flush privileges;
+drop table test.t1,mysqltest.t1,mysqltest.t2;
+drop database mysqltest;
diff --git a/mysql-test/r/innodb.result b/mysql-test/r/innodb.result
index 5d3634376d8..ff3254c03b2 100644
--- a/mysql-test/r/innodb.result
+++ b/mysql-test/r/innodb.result
@@ -848,16 +848,16 @@ id name value uid
3 three three value 103
6 two other value 102
drop table t1;
-create database test_$1;
-create table test_$1.t1 (a int not null) type= innodb;
-insert into test_$1.t1 values(1);
-create table test_$1.t2 (a int not null) type= myisam;
-insert into test_$1.t2 values(1);
-create table test_$1.t3 (a int not null) type= heap;
-insert into test_$1.t3 values(1);
+create database mysqltest;
+create table mysqltest.t1 (a int not null) type= innodb;
+insert into mysqltest.t1 values(1);
+create table mysqltest.t2 (a int not null) type= myisam;
+insert into mysqltest.t2 values(1);
+create table mysqltest.t3 (a int not null) type= heap;
+insert into mysqltest.t3 values(1);
commit;
-drop database test_$1;
-show tables from test_$1;
+drop database mysqltest;
+show tables from mysqltest;
Got one of the listed errors
create table t1 (a int not null) type= innodb;
insert into t1 values(1),(2);
diff --git a/mysql-test/r/query_cache.result b/mysql-test/r/query_cache.result
index 9ed7e44ba2a..79d101807cd 100644
--- a/mysql-test/r/query_cache.result
+++ b/mysql-test/r/query_cache.result
@@ -346,19 +346,19 @@ show status like "Qcache_queries_in_cache";
Variable_name Value
Qcache_queries_in_cache 0
drop table t1,t2;
-create database foo;
-create table foo.t1 (i int not null auto_increment, a int, primary key (i));
-insert into foo.t1 (a) values (1);
-select * from foo.t1 where i is null;
+create database mysqltest;
+create table mysqltest.t1 (i int not null auto_increment, a int, primary key (i));
+insert into mysqltest.t1 (a) values (1);
+select * from mysqltest.t1 where i is null;
i a
1 1
-select * from foo.t1;
+select * from mysqltest.t1;
i a
1 1
show status like "Qcache_queries_in_cache";
Variable_name Value
Qcache_queries_in_cache 1
-drop database foo;
+drop database mysqltest;
show status like "Qcache_queries_in_cache";
Variable_name Value
Qcache_queries_in_cache 0
diff --git a/mysql-test/t/drop.test b/mysql-test/t/drop.test
index faf0a1a31b4..c92f2b1f3b9 100644
--- a/mysql-test/t/drop.test
+++ b/mysql-test/t/drop.test
@@ -11,33 +11,33 @@ create table t1(n int);
drop table t1;
select * from t1;
-#now test for a bug in drop database - it is important that the name
-#of the table is the same as the name of the database - in the original
-#code this triggered a bug
-drop database if exists foo;
-create database foo;
-drop database if exists foo;
-create database foo;
-create table foo.foo (n int);
-insert into foo.foo values (4);
-select * from foo.foo;
-drop database if exists foo;
-create database foo;
-drop database foo;
+# now test for a bug in drop database - it is important that the name
+# of the table is the same as the name of the database - in the original
+# code this triggered a bug
+drop database if exists mysqltest;
+create database mysqltest;
+drop database if exists mysqltest;
+create database mysqltest;
+create table mysqltest.mysqltest (n int);
+insert into mysqltest.mysqltest values (4);
+select * from mysqltest.mysqltest;
+drop database if exists mysqltest;
+create database mysqltest;
+drop database mysqltest;
# test drop/create database and FLUSH TABLES WITH READ LOCK
-drop database if exists foo;
+drop database if exists mysqltest;
flush tables with read lock;
--error 1209,1223;
-create database foo;
+create database mysqltest;
unlock tables;
-create database foo;
+create database mysqltest;
show databases;
flush tables with read lock;
--error 1208,1223;
-drop database foo;
+drop database mysqltest;
unlock tables;
-drop database foo;
+drop database mysqltest;
show databases;
--error 1008
-drop database foo;
+drop database mysqltest;
diff --git a/mysql-test/t/flush.test b/mysql-test/t/flush.test
index 6a09b903873..38aa37caa4f 100644
--- a/mysql-test/t/flush.test
+++ b/mysql-test/t/flush.test
@@ -44,15 +44,15 @@ reap;
#test if drop database will wait until we release the global read lock
connection con1;
-drop database if exists foo;
-create database foo;
-create table foo.t1(n int);
-insert into foo.t1 values (23);
+drop database if exists mysqltest;
+create database mysqltest;
+create table mysqltest.t1(n int);
+insert into mysqltest.t1 values (23);
flush tables with read lock;
connection con2;
-send drop database foo;
+send drop database mysqltest;
connection con1;
-select * from foo.t1;
+select * from mysqltest.t1;
unlock tables;
connection con2;
reap;
diff --git a/mysql-test/t/grant_cache-master.opt b/mysql-test/t/grant_cache-master.opt
new file mode 100644
index 00000000000..cfdce628e74
--- /dev/null
+++ b/mysql-test/t/grant_cache-master.opt
@@ -0,0 +1 @@
+--set-variable=query_cache_size=1355776
diff --git a/mysql-test/t/grant_cache.test b/mysql-test/t/grant_cache.test
new file mode 100644
index 00000000000..a6c878cc31c
--- /dev/null
+++ b/mysql-test/t/grant_cache.test
@@ -0,0 +1,102 @@
+#
+# Test grants with query cache
+#
+drop table if exists test.t1,mysqltest.t1,mysqltest.t2;
+reset query cache;
+flush status;
+connect (root,localhost,root,,test,0,master.sock);
+connection root;
+create database if not exists mysqltest;
+
+create table mysqltest.t1 (a int,b int,c int);
+create table mysqltest.t2 (a int,b int,c int);
+insert into mysqltest.t1 values (1,1,1),(2,2,2);
+insert into mysqltest.t2 values (3,3,3);
+create table test.t1 (a char (10));
+insert into test.t1 values ("test.t1");
+select * from t1;
+connect (root2,localhost,root,,mysqltest,0,master.sock);
+connection root2;
+# put queries in cache
+select * from t1;
+select a from t1;
+select c from t1;
+select * from t2;
+select * from mysqltest.t1,test.t1;
+show status like "Qcache_queries_in_cache";
+show status like "Qcache_hits%";
+
+# Create the test users
+grant SELECT on mysqltest.* to mysqltest_1@localhost;
+grant SELECT on mysqltest.t1 to mysqltest_2@localhost;
+grant SELECT on test.t1 to mysqltest_2@localhost;
+grant SELECT(a) on mysqltest.t1 to mysqltest_3@localhost;
+
+# The following queries should be fetched from cache
+connect (user1,localhost,mysqltest_1,,mysqltest,0,master.sock);
+connection user1;
+select "user1";
+select * from t1;
+# The pre and end space are intentional
+ select a from t1 ;
+select c from t1;
+show status like "Qcache_queries_in_cache";
+show status like "Qcache_hits";
+show status like "Qcache_not_cached";
+
+# The following queries should be fetched from cache
+connect (user2,localhost,mysqltest_2,,mysqltest,0,master.sock);
+connection user2;
+select "user2";
+select * from t1;
+select a from t1;
+select c from t1;
+select * from mysqltest.t1,test.t1;
+--error 1142
+select * from t2;
+show status like "Qcache_queries_in_cache";
+show status like "Qcache_hits";
+show status like "Qcache_not_cached";
+
+# The following queries should not be fetched from cache
+connect (user3,localhost,mysqltest_3,,mysqltest,0,master.sock);
+connection user3;
+select "user3";
+--error 1143
+select * from t1;
+select a from t1;
+--error 1143
+select c from t1;
+--error 1142
+select * from t2;
+--error 1143
+select mysqltest.t1.c from test.t1,mysqltest.t1;
+show status like "Qcache_queries_in_cache";
+show status like "Qcache_hits";
+show status like "Qcache_not_cached";
+
+# Connect without a database
+connect (user4,localhost,mysqltest_1,,*NO-ONE*,0,master.sock);
+connection user4;
+select "user4";
+--error 1046
+select a from t1;
+# The following query is not cached before (different database)
+select * from mysqltest.t1,test.t1;
+# Cache a query with 'no database'
+select a from mysqltest.t1;
+select a from mysqltest.t1;
+show status like "Qcache_queries_in_cache";
+show status like "Qcache_hits";
+show status like "Qcache_not_cached";
+
+# Cleanup
+
+connection root;
+delete from mysql.user where user in ("mysqltest_1","mysqltest_2","mysqltest_3");
+delete from mysql.db where user in ("mysqltest_1","mysqltest_2","mysqltest_3");
+delete from mysql.tables_priv where user in ("mysqltest_1","mysqltest_2","mysqltest_3");
+delete from mysql.columns_priv where user in ("mysqltest_1","mysqltest_2","mysqltest_3");
+flush privileges;
+drop table test.t1,mysqltest.t1,mysqltest.t2;
+drop database mysqltest;
diff --git a/mysql-test/t/innodb.test b/mysql-test/t/innodb.test
index 36bcad1db3c..68617e99b31 100644
--- a/mysql-test/t/innodb.test
+++ b/mysql-test/t/innodb.test
@@ -518,18 +518,18 @@ drop table t1;
# Test DROP DATABASE
#
-create database test_$1;
-create table test_$1.t1 (a int not null) type= innodb;
-insert into test_$1.t1 values(1);
-create table test_$1.t2 (a int not null) type= myisam;
-insert into test_$1.t2 values(1);
-create table test_$1.t3 (a int not null) type= heap;
-insert into test_$1.t3 values(1);
+create database mysqltest;
+create table mysqltest.t1 (a int not null) type= innodb;
+insert into mysqltest.t1 values(1);
+create table mysqltest.t2 (a int not null) type= myisam;
+insert into mysqltest.t2 values(1);
+create table mysqltest.t3 (a int not null) type= heap;
+insert into mysqltest.t3 values(1);
commit;
-drop database test_$1;
+drop database mysqltest;
# Don't check error message
--error 12,12
-show tables from test_$1;
+show tables from mysqltest;
#
# Test truncate table
diff --git a/mysql-test/t/query_cache.test b/mysql-test/t/query_cache.test
index b22814f2fd9..955abeaabc4 100644
--- a/mysql-test/t/query_cache.test
+++ b/mysql-test/t/query_cache.test
@@ -235,17 +235,17 @@ drop table t1,t2;
#
# noncachable ODBC work around (and prepare cache for drop database)
#
-create database foo;
-create table foo.t1 (i int not null auto_increment, a int, primary key (i));
-insert into foo.t1 (a) values (1);
-select * from foo.t1 where i is null;
+create database mysqltest;
+create table mysqltest.t1 (i int not null auto_increment, a int, primary key (i));
+insert into mysqltest.t1 (a) values (1);
+select * from mysqltest.t1 where i is null;
#
# drop db
#
-select * from foo.t1;
+select * from mysqltest.t1;
show status like "Qcache_queries_in_cache";
-drop database foo;
+drop database mysqltest;
show status like "Qcache_queries_in_cache";
#
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index 27dba4d62fd..629f29a343e 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -283,8 +283,9 @@ uint cached_tables(void);
void kill_mysql(void);
void close_connection(NET *net,uint errcode=0,bool lock=1);
bool check_access(THD *thd,uint access,const char *db=0,uint *save_priv=0,
- bool no_grant=0);
-bool check_table_access(THD *thd,uint want_access, TABLE_LIST *tables);
+ bool no_grant=0, bool no_errors=0);
+bool check_table_access(THD *thd,uint want_access, TABLE_LIST *tables,
+ bool no_errors=0);
bool check_process_priv(THD *thd=0);
int mysql_backup_table(THD* thd, TABLE_LIST* table_list);
diff --git a/sql/net_pkg.cc b/sql/net_pkg.cc
index 9fb478ca664..2f26ad81bd5 100644
--- a/sql/net_pkg.cc
+++ b/sql/net_pkg.cc
@@ -30,6 +30,7 @@ void send_error(NET *net, uint sql_errno, const char *err)
err ? err : net->last_error[0] ?
net->last_error : "NULL"));
+ query_cache_abort(net);
if (thd)
thd->query_error = 1; // needed to catch query errors during replication
if (!err)
@@ -102,9 +103,9 @@ net_printf(NET *net, uint errcode, ...)
DBUG_ENTER("net_printf");
DBUG_PRINT("enter",("message: %u",errcode));
- if(thd) thd->query_error = 1;
- // if we are here, something is wrong :-)
-
+ if (thd)
+ thd->query_error = 1; // if we are here, something is wrong :-)
+ query_cache_abort(net); // Safety
va_start(args,errcode);
format=ER(errcode);
offset= net->return_errno ? 2 : 0;
diff --git a/sql/net_serv.cc b/sql/net_serv.cc
index 666c572ccee..3e4dcb75ebb 100644
--- a/sql/net_serv.cc
+++ b/sql/net_serv.cc
@@ -330,7 +330,8 @@ net_real_write(NET *net,const char *packet,ulong len)
DBUG_ENTER("net_real_write");
#ifdef MYSQL_SERVER
- query_cache_insert(net, packet, len);
+ if (net->query_cache_query != 0)
+ query_cache_insert(net, packet, len);
#endif
if (net->error == 2)
diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc
index 5051c9b775b..3486ccc18c5 100644
--- a/sql/sql_cache.cc
+++ b/sql/sql_cache.cc
@@ -1,4 +1,4 @@
-/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
+/* Copyright (C) 2000 MySQL AB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -530,9 +530,14 @@ byte *query_cache_query_get_key(const byte *record, uint *length,
}
/*****************************************************************************
- Functions to store things into the query cache
+ Functions to store things into the query cache
*****************************************************************************/
+/*
+ Insert the packet into the query cache.
+ This should only be called if net->query_cache_query != 0
+*/
+
void query_cache_insert(NET *net, const char *packet, ulong length)
{
DBUG_ENTER("query_cache_insert");
@@ -543,45 +548,41 @@ void query_cache_insert(NET *net, const char *packet, ulong length)
DBUG_VOID_RETURN;
#endif
- // Quick check on unlocked structure
- if (net->query_cache_query != 0)
+ STRUCT_LOCK(&query_cache.structure_guard_mutex);
+ Query_cache_block *query_block = ((Query_cache_block*)
+ net->query_cache_query);
+ if (query_block)
{
- STRUCT_LOCK(&query_cache.structure_guard_mutex);
- Query_cache_block *query_block = ((Query_cache_block*)
- net->query_cache_query);
- if (query_block)
- {
- Query_cache_query *header = query_block->query();
- Query_cache_block *result = header->result();
+ Query_cache_query *header = query_block->query();
+ Query_cache_block *result = header->result();
- DUMP(&query_cache);
- BLOCK_LOCK_WR(query_block);
- DBUG_PRINT("qcache", ("insert packet %lu bytes long",length));
+ DUMP(&query_cache);
+ BLOCK_LOCK_WR(query_block);
+ DBUG_PRINT("qcache", ("insert packet %lu bytes long",length));
- /*
- On success STRUCT_UNLOCK(&query_cache.structure_guard_mutex) will be
- done by query_cache.append_result_data if success (if not we need
- query_cache.structure_guard_mutex locked to free query)
- */
- if (!query_cache.append_result_data(&result, length, (gptr) packet,
- query_block))
- {
- query_cache.refused++;
- DBUG_PRINT("warning", ("Can't append data"));
- header->result(result);
- DBUG_PRINT("qcache", ("free query 0x%lx", (ulong) query_block));
- // The following call will remove the lock on query_block
- query_cache.free_query(query_block);
- // append_result_data no success => we need unlock
- STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
- DBUG_VOID_RETURN;
- }
+ /*
+ On success STRUCT_UNLOCK(&query_cache.structure_guard_mutex) will be
+ done by query_cache.append_result_data if success (if not we need
+ query_cache.structure_guard_mutex locked to free query)
+ */
+ if (!query_cache.append_result_data(&result, length, (gptr) packet,
+ query_block))
+ {
+ query_cache.refused++;
+ DBUG_PRINT("warning", ("Can't append data"));
header->result(result);
- BLOCK_UNLOCK_WR(query_block);
- }
- else
+ DBUG_PRINT("qcache", ("free query 0x%lx", (ulong) query_block));
+ // The following call will remove the lock on query_block
+ query_cache.free_query(query_block);
+ // append_result_data no success => we need unlock
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
+ DBUG_VOID_RETURN;
+ }
+ header->result(result);
+ BLOCK_UNLOCK_WR(query_block);
}
+ else
+ STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
DBUG_EXECUTE("check_querycache",query_cache.check_integrity(););
DBUG_VOID_RETURN;
}
@@ -607,11 +608,11 @@ void query_cache_abort(NET *net)
BLOCK_LOCK_WR(query_block);
// The following call will remove the lock on query_block
query_cache.free_query(query_block);
- net->query_cache_query=0;
}
+ net->query_cache_query=0;
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
+ DBUG_EXECUTE("check_querycache",query_cache.check_integrity(););
}
- DBUG_EXECUTE("check_querycache",query_cache.check_integrity(););
DBUG_VOID_RETURN;
}
@@ -648,7 +649,6 @@ void query_cache_end_of_result(NET *net)
#endif
header->found_rows(current_thd->limit_found_rows);
header->result()->type = Query_cache_block::RESULT;
- net->query_cache_query=0;
header->writer(0);
BLOCK_UNLOCK_WR(query_block);
}
@@ -658,8 +658,8 @@ void query_cache_end_of_result(NET *net)
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
}
net->query_cache_query=0;
+ DBUG_EXECUTE("check_querycache",query_cache.check_integrity(););
}
- DBUG_EXECUTE("check_querycache",query_cache.check_integrity(););
DBUG_VOID_RETURN;
}
@@ -724,6 +724,7 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used)
(Not important at this stage)
*/
TABLE_COUNTER_TYPE tables;
+ ulong tot_length;
DBUG_ENTER("Query_cache::store_query");
if (query_cache_size == 0)
DBUG_VOID_RETURN;
@@ -739,6 +740,17 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used)
DBUG_VOID_RETURN;
DUMP(this);
+ /* Key is query + database + flag */
+ if (thd->db_length)
+ {
+ memcpy(thd->query+thd->query_length, thd->db, thd->db_length);
+ DBUG_PRINT("qcache", ("database : %s length %u",
+ thd->db, thd->db_length));
+ }
+ else
+ {
+ DBUG_PRINT("qcache", ("No active database"));
+ }
/*
Prepare flags:
most significant bit - CLIENT_LONG_FLAG,
@@ -749,32 +761,19 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used)
flags|= (byte) thd->convert_set->number();
DBUG_ASSERT(thd->convert_set->number() < 128);
}
+ tot_length=thd->query_length+1+thd->db_length;
+ thd->query[tot_length-1] = (char) flags;
/* Check if another thread is processing the same query? */
- thd->query[thd->query_length] = (char) flags;
- if (thd->db_length)
- {
- memcpy(thd->query+thd->query_length+1, thd->db, thd->db_length);
- DBUG_PRINT("qcache", ("database : %s length %u",
- thd->db, thd->db_length));
- }
- else
- {
- DBUG_PRINT("qcache", ("No active database"));
- }
-
Query_cache_block *competitor = (Query_cache_block *)
- hash_search(&queries, (byte*) thd->query,
- thd->query_length+1+thd->db_length);
+ hash_search(&queries, (byte*) thd->query, tot_length);
DBUG_PRINT("qcache", ("competitor 0x%lx, flags %x", (ulong) competitor,
flags));
if (competitor == 0)
{
/* Query is not in cache and no one is working with it; Store it */
- thd->query[thd->query_length] = (char) flags;
Query_cache_block *query_block;
- query_block= write_block_data(thd->query_length+1+thd->db_length,
- (gptr) thd->query,
+ query_block= write_block_data(tot_length, (gptr) thd->query,
ALIGN_SIZE(sizeof(Query_cache_query)),
Query_cache_block::QUERY, tables, 1);
if (query_block != 0)
@@ -801,7 +800,7 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used)
header->unlock_n_destroy();
free_memory_block(query_block);
STRUCT_UNLOCK(&structure_guard_mutex);
- DBUG_VOID_RETURN;
+ goto end;
}
double_linked_list_simple_include(query_block, &queries_blocks);
inserts++;
@@ -837,13 +836,26 @@ end:
DBUG_VOID_RETURN;
}
+/*
+ Check if the query is in the cache. If it was cached, send it
+ to the user.
+
+ RESULTS
+ 1 Query was not cached.
+ 0 The query was cached and user was sent the result.
+ -1 The query was cached but we didn't have rights to use it.
+ No error is sent to the client yet.
+*/
+
+
-my_bool
+int
Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
{
Query_cache_query *query;
Query_cache_block *first_result_block, *result_block;
Query_cache_block_table *block_table, *block_table_end;
+ ulong tot_length;
byte flags;
DBUG_ENTER("Query_cache::send_result_to_client");
@@ -856,9 +868,13 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
thd->query_cache_type == 0)
{
- DBUG_PRINT("qcache", ("query cache disabled on not in autocommit mode"));
+ DBUG_PRINT("qcache", ("query cache disabled or not in autocommit mode"));
goto err;
}
+
+ /* Check that we haven't forgot to reset the query cache variables */
+ DBUG_ASSERT(thd->net.query_cache_query == 0);
+
/*
We can't cache the query if we are using a temporary table because
we don't know if the query is using a temporary table.
@@ -868,7 +884,9 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
*/
if (thd->temporary_tables != 0 || !thd->safe_to_cache_query)
{
- DBUG_PRINT("qcache", ("SELECT is non-cacheable"));
+ DBUG_PRINT("qcache", ("SELECT is non-cacheable: tmp_tables: %d safe: %d",
+ thd->temporary_tables,
+ thd->safe_to_cache_query));
goto err;
}
@@ -888,13 +906,22 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
STRUCT_LOCK(&structure_guard_mutex);
if (query_cache_size == 0)
{
- DBUG_PRINT("qcache", ("query cache disabled and not in autocommit mode"));
- STRUCT_UNLOCK(&structure_guard_mutex);
- goto err;
+ DBUG_PRINT("qcache", ("query cache disabled"));
+ goto err_unlock;
}
- DBUG_PRINT("qcache", (" sql %u '%s'", query_length, sql));
Query_cache_block *query_block;
+ tot_length=query_length+thd->db_length+1;
+ if (thd->db_length)
+ {
+ memcpy(sql+query_length, thd->db, thd->db_length);
+ DBUG_PRINT("qcache", ("database: '%s' length %u",
+ thd->db, thd->db_length));
+ }
+ else
+ {
+ DBUG_PRINT("qcache", ("No active database"));
+ }
/*
prepare flags:
Most significant bit - CLIENT_LONG_FLAG,
@@ -906,31 +933,19 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
flags |= (byte) thd->convert_set->number();
DBUG_ASSERT(thd->convert_set->number() < 128);
}
- sql[query_length] = (char) flags;
- if (thd->db_length)
- {
- memcpy(sql+query_length+1, thd->db, thd->db_length);
- DBUG_PRINT("qcache", ("database : %s length %u",
- thd->db, thd->db_length));
- }
- else
- {
- DBUG_PRINT("qcache", ("No active database"));
- }
+ sql[tot_length-1] = (char) flags;
query_block = (Query_cache_block *) hash_search(&queries, (byte*) sql,
- query_length+1+
- thd->db_length);
+ tot_length);
- sql[query_length] = '\0';
+ sql[query_length] = '\0'; // Restore end null
/* Quick abort on unlocked data */
if (query_block == 0 ||
query_block->query()->result() == 0 ||
query_block->query()->result()->type != Query_cache_block::RESULT)
{
- STRUCT_UNLOCK(&structure_guard_mutex);
DBUG_PRINT("qcache", ("No query in query hash or no results"));
- goto err;
+ goto err_unlock;
}
DBUG_PRINT("qcache", ("Query in query hash 0x%lx", (ulong)query_block));
@@ -945,7 +960,7 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
/* The query is probably yet processed */
DBUG_PRINT("qcache", ("query found, but no data or data incomplete"));
BLOCK_UNLOCK_RD(query_block);
- goto err;
+ goto err_unlock;
}
DBUG_PRINT("qcache", ("Query have result 0x%lx", (ulong) query));
@@ -960,14 +975,24 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
Query_cache_table *table = block_table->parent;
table_list.db = table->db();
table_list.name = table_list.real_name = table->table();
- if (check_table_access(thd,SELECT_ACL,&table_list))
+ if (check_table_access(thd,SELECT_ACL,&table_list,1))
{
DBUG_PRINT("qcache",
("probably no SELECT access to %s.%s => return to normal processing",
table_list.db, table_list.name));
- BLOCK_UNLOCK_RD(query_block);
+ refused++; // This is actually a hit
STRUCT_UNLOCK(&structure_guard_mutex);
- goto err;
+ thd->safe_to_cache_query=0; // Don't try to cache this
+ BLOCK_UNLOCK_RD(query_block);
+ DBUG_RETURN(-1); // Privilege error
+ }
+ if (table_list.grant.want_privilege)
+ {
+ DBUG_PRINT("qcache", ("Need to check column privileges for %s.%s",
+ table_list.db, table_list.name));
+ BLOCK_UNLOCK_RD(query_block);
+ thd->safe_to_cache_query=0; // Don't try to cache this
+ goto err_unlock; // Parse query
}
}
move_to_query_list_end(query_block);
@@ -996,10 +1021,12 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
thd->limit_found_rows = query->found_rows();
BLOCK_UNLOCK_RD(query_block);
- DBUG_RETURN(0);
+ DBUG_RETURN(1); // Result sent to client
+err_unlock:
+ STRUCT_UNLOCK(&structure_guard_mutex);
err:
- DBUG_RETURN(1);
+ DBUG_RETURN(0); // Query was not cached
}
/*
@@ -2307,7 +2334,7 @@ void Query_cache::double_linked_list_join(Query_cache_block *head_tail,
*****************************************************************************/
/*
- if query is cacheable return number tables in query
+ If query is cacheable return number tables in query
(query without tables are not cached)
*/
@@ -2554,10 +2581,12 @@ my_bool Query_cache::move_by_type(byte **border,
Query_cache_query *new_query= ((Query_cache_query *) new_block->data());
pthread_cond_init(&new_query->lock, NULL);
pthread_mutex_init(&new_query->clients_guard,MY_MUTEX_INIT_FAST);
+
NET *net = new_block->query()->writer();
+ /* QQ: When could this happen ? */
if (net != 0)
{
- net->query_cache_query = (gptr) new_block;
+ net->query_cache_query= (gptr) new_block;
}
/* Fix hash to point at moved block */
hash_replace(&queries, queries.current_record, (byte*) new_block);
@@ -2747,7 +2776,19 @@ uint Query_cache::filename_2_table_key (char *key, const char *path)
Functions to be used when debugging
****************************************************************************/
-#ifndef DBUG_OFF
+#if defined(DBUG_OFF) && !defined(USE_QUERY_CACHE_INTEGRITY_CHECK)
+
+void wreck(uint line, const char *message) {}
+void bins_dump() {}
+void cache_dump() {}
+void queries_dump() {}
+void tables_dump() {}
+my_bool check_integrity() {}
+my_bool in_list(Query_cache_block * root, Query_cache_block * point,
+ const char *name) { return 0;}
+my_bool in_blocks(Query_cache_block * point) { return 0; }
+
+#else
void Query_cache::wreck(uint line, const char *message)
{
@@ -2836,10 +2877,10 @@ void Query_cache::queries_dump()
{
uint len;
char *str = (char*) query_cache_query_get_key((byte*) block, &len, 0);
- byte flags = (byte) str[len-1];
+ uint flags = (uint) (uchar) str[len-1];
DBUG_PRINT("qcache", ("%u (%u,%u) %.*s",len,
((flags & QUERY_CACHE_CLIENT_LONG_FLAG_MASK)? 1:0),
- (flags & QUERY_CACHE_CHARSET_CONVERT_MASK), len,
+ (flags & QUERY_CACHE_CHARSET_CONVERT_MASK), len-1,
str));
DBUG_PRINT("qcache", ("-b- 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx", (ulong) block,
(ulong) block->next, (ulong) block->prev,
diff --git a/sql/sql_cache.h b/sql/sql_cache.h
index 6f8d9bb6dbf..50ae765e446 100644
--- a/sql/sql_cache.h
+++ b/sql/sql_cache.h
@@ -354,7 +354,7 @@ protected:
Check if the query is in the cache and if this is true send the
data to client.
*/
- my_bool send_result_to_client(THD *thd, char *query, uint query_length);
+ int send_result_to_client(THD *thd, char *query, uint query_length);
/* Remove all queries that uses any of the listed following tables */
void invalidate(TABLE_LIST *tables_used);
@@ -375,7 +375,15 @@ protected:
void destroy();
-#ifndef DBUG_OFF
+ friend void query_cache_insert(NET *net, const char *packet, ulong length);
+ friend void query_cache_end_of_result(NET *net);
+ friend void query_cache_abort(NET *net);
+
+ /*
+ The following functions are only used when debugging
+ We don't protect these with ifndef DEBUG_OFF to not have to recompile
+ everything if we want to add checks of the cache at some places.
+ */
void wreck(uint line, const char *message);
void bins_dump();
void cache_dump();
@@ -385,10 +393,6 @@ protected:
my_bool in_list(Query_cache_block * root, Query_cache_block * point,
const char *name);
my_bool in_blocks(Query_cache_block * point);
-#endif
- friend void query_cache_insert(NET *net, const char *packet, ulong length);
- friend void query_cache_end_of_result(NET *net);
- friend void query_cache_abort(NET *net);
};
extern Query_cache query_cache;
diff --git a/sql/sql_do.cc b/sql/sql_do.cc
index 77be253766c..70124c2d796 100644
--- a/sql/sql_do.cc
+++ b/sql/sql_do.cc
@@ -22,7 +22,6 @@
int mysql_do(THD *thd, List<Item> &values)
{
- int error;
List_iterator<Item> li(values);
Item *value;
DBUG_ENTER("mysql_do");
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index e61bb83ee29..af6bd07cdc5 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -842,7 +842,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
{
char *pos=packet-1+packet_length; // Point at end null
/* Remove garage at end of query */
- while (packet_length > 0 && pos[-1] == ';')
+ while (packet_length > 0 && (pos[-1] == ';' || isspace(pos[-1])))
{
pos--;
packet_length--;
@@ -2261,7 +2261,7 @@ error:
bool
check_access(THD *thd,uint want_access,const char *db, uint *save_priv,
- bool dont_check_global_grants)
+ bool dont_check_global_grants, bool no_errors)
{
uint db_access,dummy;
if (save_priv)
@@ -2271,7 +2271,8 @@ check_access(THD *thd,uint want_access,const char *db, uint *save_priv,
if ((!db || !db[0]) && !thd->db && !dont_check_global_grants)
{
- send_error(&thd->net,ER_NO_DB_ERROR); /* purecov: tested */
+ if (!no_errors)
+ send_error(&thd->net,ER_NO_DB_ERROR); /* purecov: tested */
return TRUE; /* purecov: tested */
}
@@ -2283,10 +2284,11 @@ check_access(THD *thd,uint want_access,const char *db, uint *save_priv,
if ((want_access & ~thd->master_access) & ~(DB_ACLS | EXTRA_ACL) ||
! db && dont_check_global_grants)
{ // We can never grant this
- net_printf(&thd->net,ER_ACCESS_DENIED_ERROR,
- thd->priv_user,
- thd->host_or_ip,
- thd->password ? ER(ER_YES) : ER(ER_NO));/* purecov: tested */
+ if (!no_errors)
+ net_printf(&thd->net,ER_ACCESS_DENIED_ERROR,
+ thd->priv_user,
+ thd->host_or_ip,
+ thd->password ? ER(ER_YES) : ER(ER_NO));/* purecov: tested */
return TRUE; /* purecov: tested */
}
@@ -2306,10 +2308,11 @@ check_access(THD *thd,uint want_access,const char *db, uint *save_priv,
((grant_option && !dont_check_global_grants) &&
!(want_access & ~TABLE_ACLS)))
return FALSE; /* Ok */
- net_printf(&thd->net,ER_DBACCESS_DENIED_ERROR,
- thd->priv_user,
- thd->host_or_ip,
- db ? db : thd->db ? thd->db : "unknown"); /* purecov: tested */
+ if (!no_errors)
+ net_printf(&thd->net,ER_DBACCESS_DENIED_ERROR,
+ thd->priv_user,
+ thd->host_or_ip,
+ db ? db : thd->db ? thd->db : "unknown"); /* purecov: tested */
return TRUE; /* purecov: tested */
}
@@ -2326,7 +2329,8 @@ bool check_process_priv(THD *thd)
*/
bool
-check_table_access(THD *thd,uint want_access,TABLE_LIST *tables)
+check_table_access(THD *thd,uint want_access,TABLE_LIST *tables,
+ bool no_errors)
{
uint found=0,found_access=0;
TABLE_LIST *org_tables=tables;
@@ -2341,18 +2345,20 @@ check_table_access(THD *thd,uint want_access,TABLE_LIST *tables)
tables->grant.privilege=found_access;
else
{
- if (check_access(thd,want_access,tables->db,&tables->grant.privilege))
+ if (check_access(thd,want_access,tables->db,&tables->grant.privilege,
+ 0, no_errors))
return TRUE; // Access denied
found_access=tables->grant.privilege;
found=1;
}
}
- else if (check_access(thd,want_access,tables->db,&tables->grant.privilege))
+ else if (check_access(thd,want_access,tables->db,&tables->grant.privilege,
+ 0, no_errors))
return TRUE; // Access denied
}
if (grant_option)
return check_grant(thd,want_access & ~EXTRA_ACL,org_tables,
- test(want_access & EXTRA_ACL));
+ test(want_access & EXTRA_ACL), no_errors);
return FALSE;
}
@@ -2474,6 +2480,7 @@ mysql_init_query(THD *thd)
thd->fatal_error=0; // Safety
thd->last_insert_id_used=thd->query_start_used=thd->insert_id_used=0;
thd->sent_row_count=thd->examined_row_count=0;
+ thd->safe_to_cache_query=1;
DBUG_VOID_RETURN;
}
@@ -2522,9 +2529,8 @@ mysql_parse(THD *thd,char *inBuf,uint length)
mysql_init_query(thd);
thd->query_length = length;
- if (query_cache.send_result_to_client(thd, inBuf, length))
+ if (query_cache.send_result_to_client(thd, inBuf, length) <= 0)
{
- thd->safe_to_cache_query=1;
LEX *lex=lex_start(thd, (uchar*) inBuf, length);
if (!yyparse() && ! thd->fatal_error)
{