summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--BitKeeper/etc/collapsed3
-rw-r--r--include/mysql_com.h6
-rw-r--r--mysql-test/include/im_check_env.inc6
-rw-r--r--mysql-test/include/im_check_os.inc14
-rw-r--r--mysql-test/lib/mtr_io.pl38
-rw-r--r--mysql-test/r/im_daemon_life_cycle.result1
-rw-r--r--mysql-test/r/im_life_cycle.result1
-rw-r--r--mysql-test/r/im_utils.result1
-rw-r--r--mysql-test/r/ps.result15
-rw-r--r--mysql-test/r/sp-error.result39
-rw-r--r--mysql-test/r/trigger.result86
-rw-r--r--mysql-test/r/type_varchar.result31
-rw-r--r--mysql-test/r/view.result37
-rw-r--r--mysql-test/std_data/14897.frmbin0 -> 8608 bytes
-rw-r--r--mysql-test/t/grant.test51
-rw-r--r--mysql-test/t/im_daemon_life_cycle.imtest1
-rw-r--r--mysql-test/t/im_life_cycle.imtest1
-rw-r--r--mysql-test/t/im_utils.imtest1
-rw-r--r--mysql-test/t/ps.test31
-rw-r--r--mysql-test/t/sp-error.test39
-rw-r--r--mysql-test/t/sp.test138
-rw-r--r--mysql-test/t/trigger.test120
-rw-r--r--mysql-test/t/type_varchar.test41
-rw-r--r--mysql-test/t/view.test74
-rw-r--r--sql/Makefile.am4
-rw-r--r--sql/field.cc26
-rw-r--r--sql/item.cc72
-rw-r--r--sql/item.h22
-rw-r--r--sql/item_cmpfunc.cc26
-rw-r--r--sql/item_func.cc9
-rw-r--r--sql/item_row.cc12
-rw-r--r--sql/item_strfunc.cc20
-rw-r--r--sql/item_strfunc.h9
-rw-r--r--sql/mysql_priv.h4
-rw-r--r--sql/mysqld.cc4
-rw-r--r--sql/net_serv.cc17
-rw-r--r--sql/protocol.cc40
-rw-r--r--sql/share/errmsg.txt6
-rw-r--r--sql/sp.cc11
-rw-r--r--sql/sp_head.cc6
-rw-r--r--sql/sp_head.h16
-rw-r--r--sql/sp_rcontext.cc59
-rw-r--r--sql/sp_rcontext.h6
-rw-r--r--sql/sql_acl.cc33
-rw-r--r--sql/sql_cache.cc582
-rw-r--r--sql/sql_cache.h17
-rw-r--r--sql/sql_class.cc2
-rw-r--r--sql/sql_error.cc8
-rw-r--r--sql/sql_parse.cc47
-rw-r--r--sql/sql_trigger.cc119
-rw-r--r--sql/sql_trigger.h6
-rw-r--r--sql/sql_view.cc51
-rw-r--r--sql/sql_yacc.yy59
53 files changed, 1552 insertions, 516 deletions
diff --git a/BitKeeper/etc/collapsed b/BitKeeper/etc/collapsed
index 8b90deae622..d4d681937a2 100644
--- a/BitKeeper/etc/collapsed
+++ b/BitKeeper/etc/collapsed
@@ -1 +1,4 @@
44d03f27qNdqJmARzBoP3Is_cN5e0w
+44ec850ac2k4y2Omgr92GiWPBAVKGQ
+44edb86b1iE5knJ97MbliK_3lCiAXA
+44f33f3aj5KW5qweQeekY1LU0E9ZCg
diff --git a/include/mysql_com.h b/include/mysql_com.h
index 196930fcb3f..cd75dbd6ab6 100644
--- a/include/mysql_com.h
+++ b/include/mysql_com.h
@@ -213,7 +213,13 @@ typedef struct st_net {
char last_error[MYSQL_ERRMSG_SIZE], sqlstate[SQLSTATE_LENGTH+1];
unsigned int last_errno;
unsigned char error;
+
+ /*
+ 'query_cache_query' should be accessed only via query cache
+ functions and methods to maintain proper locking.
+ */
gptr query_cache_query;
+
my_bool report_error; /* We should report error (we have unreported error) */
my_bool return_errno;
} NET;
diff --git a/mysql-test/include/im_check_env.inc b/mysql-test/include/im_check_env.inc
index 169edbac6b3..019e0984614 100644
--- a/mysql-test/include/im_check_env.inc
+++ b/mysql-test/include/im_check_env.inc
@@ -2,10 +2,6 @@
# that ensure that starting conditions (environment) for the IM-test are as
# expected.
-# Wait for mysqld1 (guarded instance) to start.
-
---exec $MYSQL_TEST_DIR/t/wait_for_process.sh $IM_MYSQLD1_PATH_PID 30 started
-
# Check the running instances.
--connect (mysql1_con,localhost,root,,mysql,$IM_MYSQLD1_PORT,$IM_MYSQLD1_SOCK)
@@ -14,6 +10,8 @@
SHOW VARIABLES LIKE 'server_id';
+--source include/not_windows.inc
+
--connection default
# Let IM detect that mysqld1 is online. This delay should be longer than
diff --git a/mysql-test/include/im_check_os.inc b/mysql-test/include/im_check_os.inc
deleted file mode 100644
index 33105f79d52..00000000000
--- a/mysql-test/include/im_check_os.inc
+++ /dev/null
@@ -1,14 +0,0 @@
---connect (dflt_server_con,localhost,root,,mysql,$IM_MYSQLD1_PORT,$IM_MYSQLD1_SOCK)
---connection dflt_server_con
-
---source include/not_windows.inc
-
-# check that CSV engine was compiled in, as IM the test suite uses
-# logs tables-specific option and the option is not present if CSV
-# (and => the log tables) are not in.
-# NOTE: In future we should remove this check and make the test suite
-# to pass correct opyions to IM depending on the CSV presence
---source include/have_csv.inc
-
---connection default
---disconnect dflt_server_con
diff --git a/mysql-test/lib/mtr_io.pl b/mysql-test/lib/mtr_io.pl
index df32ea4692a..e8dcb0262c9 100644
--- a/mysql-test/lib/mtr_io.pl
+++ b/mysql-test/lib/mtr_io.pl
@@ -20,13 +20,39 @@ sub mtr_lastlinefromfile($);
##############################################################################
sub mtr_get_pid_from_file ($) {
- my $file= shift;
+ my $pid_file_path= shift;
+ my $TOTAL_ATTEMPTS= 30;
+ my $timeout= 1;
- open(FILE,"<",$file) or mtr_error("can't open file \"$file\": $!");
- my $pid= <FILE>;
- chomp($pid);
- close FILE;
- return $pid;
+ # We should read from the file until we get correct pid. As it is
+ # stated in BUG#21884, pid file can be empty at some moment. So, we should
+ # read it until we get valid data.
+
+ for (my $cur_attempt= 1; $cur_attempt <= $TOTAL_ATTEMPTS; ++$cur_attempt)
+ {
+ mtr_debug("Reading pid file '$pid_file_path' " .
+ "($cur_attempt of $TOTAL_ATTEMPTS)...");
+
+ open(FILE, '<', $pid_file_path)
+ or mtr_error("can't open file \"$pid_file_path\": $!");
+
+ my $pid= <FILE>;
+
+ chomp($pid) if defined $pid;
+
+ close FILE;
+
+ return $pid if defined $pid && $pid ne '';
+
+ mtr_debug("Pid file '$pid_file_path' is empty. " .
+ "Sleeping $timeout second(s)...");
+
+ sleep(1);
+ }
+
+ mtr_error("Pid file '$pid_file_path' is corrupted. " .
+ "Can not retrieve PID in " .
+ ($timeout * $TOTAL_ATTEMPTS) . " seconds.");
}
sub mtr_get_opts_from_file ($) {
diff --git a/mysql-test/r/im_daemon_life_cycle.result b/mysql-test/r/im_daemon_life_cycle.result
index 927ea86dfdd..86b196659bf 100644
--- a/mysql-test/r/im_daemon_life_cycle.result
+++ b/mysql-test/r/im_daemon_life_cycle.result
@@ -1,4 +1,3 @@
-Success: the process has been started.
SHOW VARIABLES LIKE 'server_id';
Variable_name Value
server_id 1
diff --git a/mysql-test/r/im_life_cycle.result b/mysql-test/r/im_life_cycle.result
index 46579d4afa1..e462a8b53d6 100644
--- a/mysql-test/r/im_life_cycle.result
+++ b/mysql-test/r/im_life_cycle.result
@@ -1,4 +1,3 @@
-Success: the process has been started.
SHOW VARIABLES LIKE 'server_id';
Variable_name Value
server_id 1
diff --git a/mysql-test/r/im_utils.result b/mysql-test/r/im_utils.result
index bbd377a4af3..6e40c9bb1c0 100644
--- a/mysql-test/r/im_utils.result
+++ b/mysql-test/r/im_utils.result
@@ -1,4 +1,3 @@
-Success: the process has been started.
SHOW VARIABLES LIKE 'server_id';
Variable_name Value
server_id 1
diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result
index 5188900579e..e6bbd3f3124 100644
--- a/mysql-test/r/ps.result
+++ b/mysql-test/r/ps.result
@@ -1277,6 +1277,21 @@ ERROR 3D000: No database selected
create temporary table t1 (i int);
ERROR 3D000: No database selected
use test;
+DROP TABLE IF EXISTS t1, t2, t3;
+CREATE TABLE t1 (i BIGINT, j BIGINT);
+CREATE TABLE t2 (i BIGINT);
+CREATE TABLE t3 (i BIGINT, j BIGINT);
+PREPARE stmt FROM "SELECT * FROM t1 JOIN t2 ON (t2.i = t1.i)
+ LEFT JOIN t3 ON ((t3.i, t3.j) = (t1.i, t1.j))
+ WHERE t1.i = ?";
+SET @a= 1;
+EXECUTE stmt USING @a;
+i j i i j
+EXECUTE stmt USING @a;
+i j i i j
+DEALLOCATE PREPARE stmt;
+DROP TABLE IF EXISTS t1, t2, t3;
+End of 5.0 tests.
create procedure proc_1() reset query cache;
call proc_1();
call proc_1();
diff --git a/mysql-test/r/sp-error.result b/mysql-test/r/sp-error.result
index af27a4c74c2..a2d783dbcbe 100644
--- a/mysql-test/r/sp-error.result
+++ b/mysql-test/r/sp-error.result
@@ -634,6 +634,45 @@ flush tables;
return 5;
end|
ERROR 0A000: FLUSH is not allowed in stored function or trigger
+create function bug8409() returns int begin reset query cache;
+return 1; end|
+ERROR 0A000: RESET is not allowed in stored function or trigger
+create function bug8409() returns int begin reset master;
+return 1; end|
+ERROR 0A000: RESET is not allowed in stored function or trigger
+create function bug8409() returns int begin reset slave;
+return 1; end|
+ERROR 0A000: RESET is not allowed in stored function or trigger
+create function bug8409() returns int begin flush hosts;
+return 1; end|
+ERROR 0A000: FLUSH is not allowed in stored function or trigger
+create function bug8409() returns int begin flush privileges;
+return 1; end|
+ERROR 0A000: FLUSH is not allowed in stored function or trigger
+create function bug8409() returns int begin flush tables with read lock;
+return 1; end|
+ERROR 0A000: FLUSH is not allowed in stored function or trigger
+create function bug8409() returns int begin flush tables;
+return 1; end|
+ERROR 0A000: FLUSH is not allowed in stored function or trigger
+create function bug8409() returns int begin flush logs;
+return 1; end|
+ERROR 0A000: FLUSH is not allowed in stored function or trigger
+create function bug8409() returns int begin flush status;
+return 1; end|
+ERROR 0A000: FLUSH is not allowed in stored function or trigger
+create function bug8409() returns int begin flush slave;
+return 1; end|
+ERROR 0A000: FLUSH is not allowed in stored function or trigger
+create function bug8409() returns int begin flush master;
+return 1; end|
+ERROR 0A000: FLUSH is not allowed in stored function or trigger
+create function bug8409() returns int begin flush des_key_file;
+return 1; end|
+ERROR 0A000: FLUSH is not allowed in stored function or trigger
+create function bug8409() returns int begin flush user_resources;
+return 1; end|
+ERROR 0A000: FLUSH is not allowed in stored function or trigger
create procedure bug9529_901234567890123456789012345678901234567890123456789012345()
begin
end|
diff --git a/mysql-test/r/trigger.result b/mysql-test/r/trigger.result
index 2f24d7f1d52..7fe3194304c 100644
--- a/mysql-test/r/trigger.result
+++ b/mysql-test/r/trigger.result
@@ -626,12 +626,51 @@ Trigger Event Table Statement Timing Created sql_mode Definer
t1_bi INSERT t1 set new.a = '2004-01-00' BEFORE # root@localhost
drop table t1;
create table t1 (id int);
+create trigger t1_ai after insert on t1 for each row reset query cache;
+ERROR 0A000: RESET is not allowed in stored function or trigger
+create trigger t1_ai after insert on t1 for each row reset master;
+ERROR 0A000: RESET is not allowed in stored function or trigger
+create trigger t1_ai after insert on t1 for each row reset slave;
+ERROR 0A000: RESET is not allowed in stored function or trigger
+create trigger t1_ai after insert on t1 for each row flush hosts;
+ERROR 0A000: FLUSH is not allowed in stored function or trigger
+create trigger t1_ai after insert on t1 for each row flush tables with read lock;
+ERROR 0A000: FLUSH is not allowed in stored function or trigger
+create trigger t1_ai after insert on t1 for each row flush logs;
+ERROR 0A000: FLUSH is not allowed in stored function or trigger
+create trigger t1_ai after insert on t1 for each row flush status;
+ERROR 0A000: FLUSH is not allowed in stored function or trigger
+create trigger t1_ai after insert on t1 for each row flush slave;
+ERROR 0A000: FLUSH is not allowed in stored function or trigger
+create trigger t1_ai after insert on t1 for each row flush master;
+ERROR 0A000: FLUSH is not allowed in stored function or trigger
+create trigger t1_ai after insert on t1 for each row flush des_key_file;
+ERROR 0A000: FLUSH is not allowed in stored function or trigger
+create trigger t1_ai after insert on t1 for each row flush user_resources;
+ERROR 0A000: FLUSH is not allowed in stored function or trigger
create trigger t1_ai after insert on t1 for each row flush tables;
ERROR 0A000: FLUSH is not allowed in stored function or trigger
create trigger t1_ai after insert on t1 for each row flush privileges;
ERROR 0A000: FLUSH is not allowed in stored function or trigger
-create procedure p1() flush tables;
+drop procedure if exists p1;
create trigger t1_ai after insert on t1 for each row call p1();
+create procedure p1() flush tables;
+insert into t1 values (0);
+ERROR 0A000: FLUSH is not allowed in stored function or trigger
+drop procedure p1;
+create procedure p1() reset query cache;
+insert into t1 values (0);
+ERROR 0A000: RESET is not allowed in stored function or trigger
+drop procedure p1;
+create procedure p1() reset master;
+insert into t1 values (0);
+ERROR 0A000: RESET is not allowed in stored function or trigger
+drop procedure p1;
+create procedure p1() reset slave;
+insert into t1 values (0);
+ERROR 0A000: RESET is not allowed in stored function or trigger
+drop procedure p1;
+create procedure p1() flush hosts;
insert into t1 values (0);
ERROR 0A000: FLUSH is not allowed in stored function or trigger
drop procedure p1;
@@ -639,6 +678,38 @@ create procedure p1() flush privileges;
insert into t1 values (0);
ERROR 0A000: FLUSH is not allowed in stored function or trigger
drop procedure p1;
+create procedure p1() flush tables with read lock;
+insert into t1 values (0);
+ERROR 0A000: FLUSH is not allowed in stored function or trigger
+drop procedure p1;
+create procedure p1() flush tables;
+insert into t1 values (0);
+ERROR 0A000: FLUSH is not allowed in stored function or trigger
+drop procedure p1;
+create procedure p1() flush logs;
+insert into t1 values (0);
+ERROR 0A000: FLUSH is not allowed in stored function or trigger
+drop procedure p1;
+create procedure p1() flush status;
+insert into t1 values (0);
+ERROR 0A000: FLUSH is not allowed in stored function or trigger
+drop procedure p1;
+create procedure p1() flush slave;
+insert into t1 values (0);
+ERROR 0A000: FLUSH is not allowed in stored function or trigger
+drop procedure p1;
+create procedure p1() flush master;
+insert into t1 values (0);
+ERROR 0A000: FLUSH is not allowed in stored function or trigger
+drop procedure p1;
+create procedure p1() flush des_key_file;
+insert into t1 values (0);
+ERROR 0A000: FLUSH is not allowed in stored function or trigger
+drop procedure p1;
+create procedure p1() flush user_resources;
+insert into t1 values (0);
+ERROR 0A000: FLUSH is not allowed in stored function or trigger
+drop procedure p1;
drop table t1;
create table t1 (id int, data int, username varchar(16));
insert into t1 (id, data) values (1, 0);
@@ -1089,4 +1160,17 @@ begin
set @a:= 1;
end|
ERROR HY000: Triggers can not be created on system tables
+use test|
+DROP TABLE IF EXISTS t1;
+DROP TABLE IF EXISTS t2;
+CREATE TABLE t1(c INT);
+CREATE TABLE t2(c INT);
+CREATE DEFINER=1234567890abcdefGHIKL@localhost
+TRIGGER t1_bi BEFORE INSERT ON t1 FOR EACH ROW SET @a = 1;
+ERROR HY000: String '1234567890abcdefGHIKL' is too long for user name (should be no longer than 16)
+CREATE DEFINER=some_user_name@1234567890abcdefghij1234567890abcdefghij1234567890abcdefghijQWERTY
+TRIGGER t2_bi BEFORE INSERT ON t2 FOR EACH ROW SET @a = 2;
+ERROR HY000: String '1234567890abcdefghij1234567890abcdefghij1234567890abcdefghijQWERTY' is too long for host name (should be no longer than 60)
+DROP TABLE t1;
+DROP TABLE t2;
End of 5.0 tests
diff --git a/mysql-test/r/type_varchar.result b/mysql-test/r/type_varchar.result
index 6ddd8a7f68e..9ad3d5bb611 100644
--- a/mysql-test/r/type_varchar.result
+++ b/mysql-test/r/type_varchar.result
@@ -422,3 +422,34 @@ DROP TABLE IF EXISTS t1;
CREATE TABLE t1(f1 CHAR(100) DEFAULT 'test');
INSERT INTO t1 VALUES(SUBSTR(f1, 1, 3));
DROP TABLE IF EXISTS t1;
+drop table if exists t1, t2, t3;
+create table t3 (
+id int(11),
+en varchar(255) character set utf8,
+cz varchar(255) character set utf8
+);
+truncate table t3;
+insert into t3 (id, en, cz) values
+(1,'en string 1','cz string 1'),
+(2,'en string 2','cz string 2'),
+(3,'en string 3','cz string 3');
+create table t1 (
+id int(11),
+name_id int(11)
+);
+insert into t1 (id, name_id) values (1,1), (2,3), (3,3);
+create table t2 (id int(11));
+insert into t2 (id) values (1), (2), (3);
+select t1.*, t2.id, t3.en, t3.cz from t1 left join t2 on t1.id=t2.id
+left join t3 on t1.id=t3.id order by t3.id;
+Catalog Database Table Table_alias Column Column_alias Type Length Max length Is_null Flags Decimals Charsetnr
+def test t1 t1 id id 3 11 1 Y 32768 0 63
+def test t1 t1 name_id name_id 3 11 1 Y 32768 0 63
+def test t2 t2 id id 3 11 1 Y 32768 0 63
+def test t3 t3 en en 253 255 11 Y 0 0 8
+def test t3 t3 cz cz 253 255 11 Y 0 0 8
+id name_id id en cz
+1 1 1 en string 1 cz string 1
+2 3 2 en string 2 cz string 2
+3 3 3 en string 3 cz string 3
+drop table t1, t2, t3;
diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result
index 4ef13a0f307..3958ffded74 100644
--- a/mysql-test/r/view.result
+++ b/mysql-test/r/view.result
@@ -2849,4 +2849,41 @@ SHOW TABLES;
Tables_in_test
t1
DROP TABLE t1;
+DROP TABLE IF EXISTS t1;
DROP VIEW IF EXISTS v1;
+DROP VIEW IF EXISTS v2;
+CREATE TABLE t1(a INT, b INT);
+CREATE DEFINER=1234567890abcdefGHIKL@localhost
+VIEW v1 AS SELECT a FROM t1;
+ERROR HY000: String '1234567890abcdefGHIKL' is too long for user name (should be no longer than 16)
+CREATE DEFINER=some_user_name@1234567890abcdefghij1234567890abcdefghij1234567890abcdefghijQWERTY
+VIEW v2 AS SELECT b FROM t1;
+ERROR HY000: String '1234567890abcdefghij1234567890abcdefghij1234567890abcdefghijQWERTY' is too long for host name (should be no longer than 60)
+DROP TABLE t1;
+DROP FUNCTION IF EXISTS f1;
+DROP FUNCTION IF EXISTS f2;
+DROP VIEW IF EXISTS v1, v2;
+DROP TABLE IF EXISTS t1;
+CREATE TABLE t1 (i INT);
+CREATE VIEW v1 AS SELECT * FROM t1;
+CREATE FUNCTION f1() RETURNS INT
+BEGIN
+INSERT INTO v1 VALUES (0);
+RETURN 0;
+END |
+SELECT f1();
+f1()
+0
+CREATE ALGORITHM=TEMPTABLE VIEW v2 AS SELECT * FROM t1;
+CREATE FUNCTION f2() RETURNS INT
+BEGIN
+INSERT INTO v2 VALUES (0);
+RETURN 0;
+END |
+SELECT f2();
+ERROR HY000: The target table v2 of the INSERT is not updatable
+DROP FUNCTION f1;
+DROP FUNCTION f2;
+DROP VIEW v1, v2;
+DROP TABLE t1;
+End of 5.0 tests.
diff --git a/mysql-test/std_data/14897.frm b/mysql-test/std_data/14897.frm
new file mode 100644
index 00000000000..aff11b467b0
--- /dev/null
+++ b/mysql-test/std_data/14897.frm
Binary files differ
diff --git a/mysql-test/t/grant.test b/mysql-test/t/grant.test
index cc66615039f..5bacbf7d50f 100644
--- a/mysql-test/t/grant.test
+++ b/mysql-test/t/grant.test
@@ -812,7 +812,56 @@ DROP USER 'mysqltest_1'@'localhost';
#
# Bug #10668: CREATE USER does not enforce username length limit
#
---error ER_CANNOT_USER
+--error ER_WRONG_STRING_LENGTH
create user mysqltest1_thisisreallytoolong;
+#
+# Test for BUG#16899: Possible buffer overflow in handling of DEFINER-clause.
+#
+# These checks are intended to ensure that appropriate errors are risen when
+# illegal user name or hostname is specified in user-clause of GRANT/REVOKE
+# statements.
+#
+
+# Working with database-level privileges.
+
+--error ER_WRONG_STRING_LENGTH
+GRANT CREATE ON mysqltest.* TO 1234567890abcdefGHIKL@localhost;
+
+--error ER_WRONG_STRING_LENGTH
+GRANT CREATE ON mysqltest.* TO some_user_name@1234567890abcdefghij1234567890abcdefghij1234567890abcdefghijQWERTY;
+
+--error ER_WRONG_STRING_LENGTH
+REVOKE CREATE ON mysqltest.* FROM 1234567890abcdefGHIKL@localhost;
+
+--error ER_WRONG_STRING_LENGTH
+REVOKE CREATE ON mysqltest.* FROM some_user_name@1234567890abcdefghij1234567890abcdefghij1234567890abcdefghijQWERTY;
+
+# Working with table-level privileges.
+
+--error ER_WRONG_STRING_LENGTH
+GRANT CREATE ON t1 TO 1234567890abcdefGHIKL@localhost;
+
+--error ER_WRONG_STRING_LENGTH
+GRANT CREATE ON t1 TO some_user_name@1234567890abcdefghij1234567890abcdefghij1234567890abcdefghijQWERTY;
+
+--error ER_WRONG_STRING_LENGTH
+REVOKE CREATE ON t1 FROM 1234567890abcdefGHIKL@localhost;
+
+--error ER_WRONG_STRING_LENGTH
+REVOKE CREATE ON t1 FROM some_user_name@1234567890abcdefghij1234567890abcdefghij1234567890abcdefghijQWERTY;
+
+# Working with routine-level privileges.
+
+--error ER_WRONG_STRING_LENGTH
+GRANT EXECUTE ON PROCEDURE p1 TO 1234567890abcdefGHIKL@localhost;
+
+--error ER_WRONG_STRING_LENGTH
+GRANT EXECUTE ON PROCEDURE p1 TO some_user_name@1234567890abcdefghij1234567890abcdefghij1234567890abcdefghijQWERTY;
+
+--error ER_WRONG_STRING_LENGTH
+REVOKE EXECUTE ON PROCEDURE p1 FROM 1234567890abcdefGHIKL@localhost;
+
+--error ER_WRONG_STRING_LENGTH
+REVOKE EXECUTE ON PROCEDURE t1 FROM some_user_name@1234567890abcdefghij1234567890abcdefghij1234567890abcdefghijQWERTY;
--echo End of 5.0 tests
diff --git a/mysql-test/t/im_daemon_life_cycle.imtest b/mysql-test/t/im_daemon_life_cycle.imtest
index fe2345a9987..a07da161279 100644
--- a/mysql-test/t/im_daemon_life_cycle.imtest
+++ b/mysql-test/t/im_daemon_life_cycle.imtest
@@ -6,7 +6,6 @@
#
###########################################################################
---source include/im_check_os.inc
--source include/im_check_env.inc
###########################################################################
diff --git a/mysql-test/t/im_life_cycle.imtest b/mysql-test/t/im_life_cycle.imtest
index 16cf25a8f35..ddfb62d312e 100644
--- a/mysql-test/t/im_life_cycle.imtest
+++ b/mysql-test/t/im_life_cycle.imtest
@@ -6,7 +6,6 @@
#
###########################################################################
---source include/im_check_os.inc
--source include/im_check_env.inc
###########################################################################
diff --git a/mysql-test/t/im_utils.imtest b/mysql-test/t/im_utils.imtest
index 4c05b342af5..52878f6c2b5 100644
--- a/mysql-test/t/im_utils.imtest
+++ b/mysql-test/t/im_utils.imtest
@@ -6,7 +6,6 @@
#
###########################################################################
---source include/im_check_os.inc
--source include/im_check_env.inc
###########################################################################
diff --git a/mysql-test/t/ps.test b/mysql-test/t/ps.test
index 94d6fb3af55..e86420f4364 100644
--- a/mysql-test/t/ps.test
+++ b/mysql-test/t/ps.test
@@ -1329,10 +1329,38 @@ create temporary table t1 (i int);
# Restore the old environemnt
#
use test;
-# End of 5.0 tests
#
+# BUG#21166: Prepared statement causes signal 11 on second execution
+#
+# Changes in an item tree done by optimizer weren't properly
+# registered and went unnoticed, which resulted in preliminary freeing
+# of used memory.
+#
+--disable_warnings
+DROP TABLE IF EXISTS t1, t2, t3;
+--enable_warnings
+
+CREATE TABLE t1 (i BIGINT, j BIGINT);
+CREATE TABLE t2 (i BIGINT);
+CREATE TABLE t3 (i BIGINT, j BIGINT);
+
+PREPARE stmt FROM "SELECT * FROM t1 JOIN t2 ON (t2.i = t1.i)
+ LEFT JOIN t3 ON ((t3.i, t3.j) = (t1.i, t1.j))
+ WHERE t1.i = ?";
+
+SET @a= 1;
+EXECUTE stmt USING @a;
+EXECUTE stmt USING @a;
+
+DEALLOCATE PREPARE stmt;
+DROP TABLE IF EXISTS t1, t2, t3;
+
+
+--echo End of 5.0 tests.
+
+#
# Bug #20665: All commands supported in Stored Procedures should work in
# Prepared Statements
#
@@ -2200,4 +2228,3 @@ execute abc;
drop table if exists t1, t2;
execute abc;
deallocate prepare abc;
-
diff --git a/mysql-test/t/sp-error.test b/mysql-test/t/sp-error.test
index e1179f36dad..e829a71c45a 100644
--- a/mysql-test/t/sp-error.test
+++ b/mysql-test/t/sp-error.test
@@ -899,6 +899,45 @@ begin
flush tables;
return 5;
end|
+--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
+create function bug8409() returns int begin reset query cache;
+return 1; end|
+--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
+create function bug8409() returns int begin reset master;
+return 1; end|
+--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
+create function bug8409() returns int begin reset slave;
+return 1; end|
+--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
+create function bug8409() returns int begin flush hosts;
+return 1; end|
+--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
+create function bug8409() returns int begin flush privileges;
+return 1; end|
+--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
+create function bug8409() returns int begin flush tables with read lock;
+return 1; end|
+--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
+create function bug8409() returns int begin flush tables;
+return 1; end|
+--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
+create function bug8409() returns int begin flush logs;
+return 1; end|
+--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
+create function bug8409() returns int begin flush status;
+return 1; end|
+--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
+create function bug8409() returns int begin flush slave;
+return 1; end|
+--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
+create function bug8409() returns int begin flush master;
+return 1; end|
+--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
+create function bug8409() returns int begin flush des_key_file;
+return 1; end|
+--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
+create function bug8409() returns int begin flush user_resources;
+return 1; end|
#
diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test
index 8f83767b883..bd480436d8c 100644
--- a/mysql-test/t/sp.test
+++ b/mysql-test/t/sp.test
@@ -6145,30 +6145,103 @@ CALL bug16676_p2('a', @v2, @v3)|
use test|
DROP DATABASE mysqltest1|
-# Bug#21002 "Derived table not selecting from a "real" table fails in JOINs"
-#
-# A regression caused by the fix for Bug#18444: for derived tables we should
-# set an empty string as the current database. They do not belong to any
-# database and must be usable even if there is no database
-# selected.
+#
+# BUG#8153: Stored procedure with subquery and continue handler, wrong result
+#
+
--disable_warnings
drop table if exists t3|
-drop database if exists mysqltest1|
+drop table if exists t4|
+drop procedure if exists bug8153_subselect|
+drop procedure if exists bug8153_subselect_a|
+drop procedure if exists bug8153_subselect_b|
+drop procedure if exists bug8153_proc_a|
+drop procedure if exists bug8153_proc_b|
--enable_warnings
+
create table t3 (a int)|
-insert into t3 (a) values (1), (2)|
+create table t4 (a int)|
+insert into t3 values (1), (1), (2), (3)|
+insert into t4 values (1), (1)|
-create database mysqltest1|
-use mysqltest1|
-drop database mysqltest1|
+## Testing the use case reported in Bug#8153
-# No current database
-select database()|
+create procedure bug8153_subselect()
+begin
+ declare continue handler for sqlexception
+ begin
+ select 'statement failed';
+ end;
+ update t3 set a=a+1 where (select a from t4 where a=1) is null;
+ select 'statement after update';
+end|
-select * from (select 1 as a) as t1 natural join (select * from test.t3) as t2|
-use test|
-drop table t3|
+call bug8153_subselect()|
+select * from t3|
+
+call bug8153_subselect()|
+select * from t3|
+
+drop procedure bug8153_subselect|
+
+## Testing a subselect with a non local handler
+
+create procedure bug8153_subselect_a()
+begin
+ declare continue handler for sqlexception
+ begin
+ select 'in continue handler';
+ end;
+
+ select 'reachable code a1';
+ call bug8153_subselect_b();
+ select 'reachable code a2';
+end|
+
+create procedure bug8153_subselect_b()
+begin
+ select 'reachable code b1';
+ update t3 set a=a+1 where (select a from t4 where a=1) is null;
+ select 'unreachable code b2';
+end|
+
+call bug8153_subselect_a()|
+select * from t3|
+
+call bug8153_subselect_a()|
+select * from t3|
+drop procedure bug8153_subselect_a|
+drop procedure bug8153_subselect_b|
+
+## Testing extra use cases, found while investigating
+## This is related to BUG#18787, with a non local handler
+
+create procedure bug8153_proc_a()
+begin
+ declare continue handler for sqlexception
+ begin
+ select 'in continue handler';
+ end;
+
+ select 'reachable code a1';
+ call bug8153_proc_b();
+ select 'reachable code a2';
+end|
+
+create procedure bug8153_proc_b()
+begin
+ select 'reachable code b1';
+ select no_such_function();
+ select 'unreachable code b2';
+end|
+
+call bug8153_proc_a()|
+
+drop procedure bug8153_proc_a|
+drop procedure bug8153_proc_b|
+drop table t3|
+drop table t4|
#
# BUG#19862: Sort with filesort by function evaluates function twice
@@ -6189,6 +6262,39 @@ SELECT * FROM t11|
DROP TABLE t11, t12|
DROP FUNCTION bug19862|
+# Test for BUG#16899: Possible buffer overflow in handling of DEFINER-clause.
+#
+# Prepare.
+
+--disable_warnings
+DROP PROCEDURE IF EXISTS bug16899_p1|
+DROP FUNCTION IF EXISTS bug16899_f1|
+--enable_warnings
+
+--error ER_WRONG_STRING_LENGTH
+CREATE DEFINER=1234567890abcdefGHIKL@localhost PROCEDURE bug16899_p1()
+BEGIN
+ SET @a = 1;
+END|
+
+--error ER_WRONG_STRING_LENGTH
+CREATE DEFINER=some_user_name@1234567890abcdefghij1234567890abcdefghij1234567890abcdefghijQWERTY
+ FUNCTION bug16899_f1() RETURNS INT
+BEGIN
+ RETURN 1;
+END|
+
+
+#
+# BUG#21416: SP: Recursion level higher than zero needed for non-recursive call
+#
+--disable_warnings
+drop procedure if exists bug21416|
+--enable_warnings
+create procedure bug21416() show create procedure bug21416|
+call bug21416()|
+drop procedure bug21416|
+
--echo End of 5.0 tests
#
# BUG#NNNN: New bug synopsis
diff --git a/mysql-test/t/trigger.test b/mysql-test/t/trigger.test
index 735a2ad78b8..cd6de45a7ce 100644
--- a/mysql-test/t/trigger.test
+++ b/mysql-test/t/trigger.test
@@ -651,17 +651,105 @@ drop table t1;
# of functions and triggers.
create table t1 (id int);
--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
+create trigger t1_ai after insert on t1 for each row reset query cache;
+--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
+create trigger t1_ai after insert on t1 for each row reset master;
+--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
+create trigger t1_ai after insert on t1 for each row reset slave;
+--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
+create trigger t1_ai after insert on t1 for each row flush hosts;
+--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
+create trigger t1_ai after insert on t1 for each row flush tables with read lock;
+--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
+create trigger t1_ai after insert on t1 for each row flush logs;
+--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
+create trigger t1_ai after insert on t1 for each row flush status;
+--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
+create trigger t1_ai after insert on t1 for each row flush slave;
+--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
+create trigger t1_ai after insert on t1 for each row flush master;
+--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
+create trigger t1_ai after insert on t1 for each row flush des_key_file;
+--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
+create trigger t1_ai after insert on t1 for each row flush user_resources;
+--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
create trigger t1_ai after insert on t1 for each row flush tables;
--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
create trigger t1_ai after insert on t1 for each row flush privileges;
-create procedure p1() flush tables;
+--disable_warnings
+drop procedure if exists p1;
+--enable_warnings
+
create trigger t1_ai after insert on t1 for each row call p1();
+create procedure p1() flush tables;
+--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
+insert into t1 values (0);
+
+drop procedure p1;
+create procedure p1() reset query cache;
--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
insert into t1 values (0);
+
+drop procedure p1;
+create procedure p1() reset master;
+--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
+insert into t1 values (0);
+
+drop procedure p1;
+create procedure p1() reset slave;
+--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
+insert into t1 values (0);
+
+drop procedure p1;
+create procedure p1() flush hosts;
+--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
+insert into t1 values (0);
+
drop procedure p1;
create procedure p1() flush privileges;
--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
insert into t1 values (0);
+
+drop procedure p1;
+create procedure p1() flush tables with read lock;
+--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
+insert into t1 values (0);
+
+drop procedure p1;
+create procedure p1() flush tables;
+--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
+insert into t1 values (0);
+
+drop procedure p1;
+create procedure p1() flush logs;
+--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
+insert into t1 values (0);
+
+drop procedure p1;
+create procedure p1() flush status;
+--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
+insert into t1 values (0);
+
+drop procedure p1;
+create procedure p1() flush slave;
+--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
+insert into t1 values (0);
+
+drop procedure p1;
+create procedure p1() flush master;
+--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
+insert into t1 values (0);
+
+drop procedure p1;
+create procedure p1() flush des_key_file;
+--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
+insert into t1 values (0);
+
+drop procedure p1;
+create procedure p1() flush user_resources;
+--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
+insert into t1 values (0);
+
drop procedure p1;
drop table t1;
@@ -1301,6 +1389,36 @@ create trigger wont_work after update on event for each row
begin
set @a:= 1;
end|
+use test|
delimiter ;|
+
+#
+# Test for BUG#16899: Possible buffer overflow in handling of DEFINER-clause.
+#
+
+# Prepare.
+
+--disable_warnings
+DROP TABLE IF EXISTS t1;
+DROP TABLE IF EXISTS t2;
+--enable_warnings
+
+CREATE TABLE t1(c INT);
+CREATE TABLE t2(c INT);
+
+--error ER_WRONG_STRING_LENGTH
+CREATE DEFINER=1234567890abcdefGHIKL@localhost
+ TRIGGER t1_bi BEFORE INSERT ON t1 FOR EACH ROW SET @a = 1;
+
+--error ER_WRONG_STRING_LENGTH
+CREATE DEFINER=some_user_name@1234567890abcdefghij1234567890abcdefghij1234567890abcdefghijQWERTY
+ TRIGGER t2_bi BEFORE INSERT ON t2 FOR EACH ROW SET @a = 2;
+
+# Cleanup.
+
+DROP TABLE t1;
+DROP TABLE t2;
+
+
--echo End of 5.0 tests
diff --git a/mysql-test/t/type_varchar.test b/mysql-test/t/type_varchar.test
index e5614afe4f6..439e98471b2 100644
--- a/mysql-test/t/type_varchar.test
+++ b/mysql-test/t/type_varchar.test
@@ -146,3 +146,44 @@ DROP TABLE IF EXISTS t1;
CREATE TABLE t1(f1 CHAR(100) DEFAULT 'test');
INSERT INTO t1 VALUES(SUBSTR(f1, 1, 3));
DROP TABLE IF EXISTS t1;
+
+#
+# Bug#14897 "ResultSet.getString("table.column") sometimes doesn't find the
+# column"
+# Test that after upgrading an old 4.1 VARCHAR column to 5.0 VARCHAR we preserve
+# the original column metadata.
+#
+--disable_warnings
+drop table if exists t1, t2, t3;
+--enable_warnings
+
+create table t3 (
+ id int(11),
+ en varchar(255) character set utf8,
+ cz varchar(255) character set utf8
+);
+system cp $MYSQL_TEST_DIR/std_data/14897.frm $MYSQLTEST_VARDIR/master-data/test/t3.frm;
+truncate table t3;
+insert into t3 (id, en, cz) values
+(1,'en string 1','cz string 1'),
+(2,'en string 2','cz string 2'),
+(3,'en string 3','cz string 3');
+
+create table t1 (
+ id int(11),
+ name_id int(11)
+);
+insert into t1 (id, name_id) values (1,1), (2,3), (3,3);
+
+create table t2 (id int(11));
+insert into t2 (id) values (1), (2), (3);
+
+# max_length is different for varchar fields in ps-protocol and we can't
+# replace a single metadata column, disable PS protocol
+--disable_ps_protocol
+--enable_metadata
+select t1.*, t2.id, t3.en, t3.cz from t1 left join t2 on t1.id=t2.id
+left join t3 on t1.id=t3.id order by t3.id;
+--disable_metadata
+--enable_ps_protocol
+drop table t1, t2, t3;
diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test
index ee37429d694..65607d677cd 100644
--- a/mysql-test/t/view.test
+++ b/mysql-test/t/view.test
@@ -2720,6 +2720,80 @@ DROP VIEW t1,v1;
SHOW TABLES;
DROP TABLE t1;
+
+
+#
+# Test for BUG#16899: Possible buffer overflow in handling of DEFINER-clause.
+#
+
+# Prepare.
+
--disable_warnings
+DROP TABLE IF EXISTS t1;
DROP VIEW IF EXISTS v1;
+DROP VIEW IF EXISTS v2;
+--enable_warnings
+
+CREATE TABLE t1(a INT, b INT);
+
+--error ER_WRONG_STRING_LENGTH
+CREATE DEFINER=1234567890abcdefGHIKL@localhost
+ VIEW v1 AS SELECT a FROM t1;
+
+--error ER_WRONG_STRING_LENGTH
+CREATE DEFINER=some_user_name@1234567890abcdefghij1234567890abcdefghij1234567890abcdefghijQWERTY
+ VIEW v2 AS SELECT b FROM t1;
+
+# Cleanup.
+
+DROP TABLE t1;
+
+
+#
+# BUG#17591: Updatable view not possible with trigger or stored
+# function
+#
+# During prelocking phase we didn't update lock type of view tables,
+# hence READ lock was always requested.
+#
+--disable_warnings
+DROP FUNCTION IF EXISTS f1;
+DROP FUNCTION IF EXISTS f2;
+DROP VIEW IF EXISTS v1, v2;
+DROP TABLE IF EXISTS t1;
--enable_warnings
+
+CREATE TABLE t1 (i INT);
+
+CREATE VIEW v1 AS SELECT * FROM t1;
+
+delimiter |;
+CREATE FUNCTION f1() RETURNS INT
+BEGIN
+ INSERT INTO v1 VALUES (0);
+ RETURN 0;
+END |
+delimiter ;|
+
+SELECT f1();
+
+CREATE ALGORITHM=TEMPTABLE VIEW v2 AS SELECT * FROM t1;
+
+delimiter |;
+CREATE FUNCTION f2() RETURNS INT
+BEGIN
+ INSERT INTO v2 VALUES (0);
+ RETURN 0;
+END |
+delimiter ;|
+
+--error ER_NON_UPDATABLE_TABLE
+SELECT f2();
+
+DROP FUNCTION f1;
+DROP FUNCTION f2;
+DROP VIEW v1, v2;
+DROP TABLE t1;
+
+
+--echo End of 5.0 tests.
diff --git a/sql/Makefile.am b/sql/Makefile.am
index 7470abfeb34..dfb625d4747 100644
--- a/sql/Makefile.am
+++ b/sql/Makefile.am
@@ -122,8 +122,8 @@ DEFS = -DMYSQL_SERVER \
BUILT_SOURCES = sql_yacc.cc sql_yacc.h lex_hash.h
EXTRA_DIST = udf_example.c $(BUILT_SOURCES) \
nt_servc.cc nt_servc.h message.mc CMakeLists.txt
-CLEANFILES = lex_hash.h sql_yacc.cc sql_yacc.h
-AM_YFLAGS = -d
+CLEANFILES = lex_hash.h sql_yacc.cc sql_yacc.h sql_yacc.output
+AM_YFLAGS = -d --debug --verbose
mysql_tzinfo_to_sql.cc:
rm -f mysql_tzinfo_to_sql.cc
diff --git a/sql/field.cc b/sql/field.cc
index f2fe9d38917..3b48aa5ad24 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -6302,16 +6302,24 @@ Field *Field_string::new_field(MEM_ROOT *root, struct st_table *new_table,
{
Field *field;
if (type() != MYSQL_TYPE_VAR_STRING || keep_type)
- return Field::new_field(root, new_table, keep_type);
-
- /*
- Old VARCHAR field which should be modified to a VARCHAR on copy
- This is done to ensure that ALTER TABLE will convert old VARCHAR fields
- to now VARCHAR fields.
- */
- if ((field= new Field_varstring(field_length, maybe_null(), field_name,
- new_table->s, charset())))
+ new_field= Field::new_field(root, new_table, keep_type);
+ else if ((field= new Field_varstring(field_length, maybe_null(), field_name,
+ new_table->s, charset())))
+ {
+ /*
+ Old VARCHAR field which should be modified to a VARCHAR on copy
+ This is done to ensure that ALTER TABLE will convert old VARCHAR fields
+ to now VARCHAR fields.
+ */
field->init(new_table);
+ /*
+ Normally orig_table is different from table only if field was created
+ via ::new_field. Here we alter the type of field, so ::new_field is
+ not applicable. But we still need to preserve the original field
+ metadata for the client-server protocol.
+ */
+ new_field->orig_table= orig_table;
+ }
return field;
}
diff --git a/sql/item.cc b/sql/item.cc
index be15bd528bb..29c99a17944 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -417,6 +417,49 @@ void Item::rename(char *new_name)
}
+/*
+ Traverse item tree possibly transforming it (replacing items).
+
+ SYNOPSIS
+ Item::transform()
+ transformer functor that performs transformation of a subtree
+ arg opaque argument passed to the functor
+
+ DESCRIPTION
+ This function is designed to ease transformation of Item trees.
+
+ Re-execution note: every such transformation is registered for
+ rollback by THD::change_item_tree() and is rolled back at the end
+ of execution by THD::rollback_item_tree_changes().
+
+ Therefore:
+
+ - this function can not be used at prepared statement prepare
+ (in particular, in fix_fields!), as only permanent
+ transformation of Item trees are allowed at prepare.
+
+ - the transformer function shall allocate new Items in execution
+ memory root (thd->mem_root) and not anywhere else: allocated
+ items will be gone in the end of execution.
+
+ If you don't need to transform an item tree, but only traverse
+ it, please use Item::walk() instead.
+
+
+ RETURN VALUE
+ Returns pointer to the new subtree root. THD::change_item_tree()
+ should be called for it if transformation took place, i.e. if a
+ pointer to newly allocated item is returned.
+*/
+
+Item* Item::transform(Item_transformer transformer, byte *arg)
+{
+ DBUG_ASSERT(!current_thd->is_stmt_prepare());
+
+ return (this->*transformer)(arg);
+}
+
+
Item_ident::Item_ident(Name_resolution_context *context_arg,
const char *db_name_arg,const char *table_name_arg,
const char *field_name_arg)
@@ -3839,11 +3882,11 @@ Item *Item_field::equal_fields_propagator(byte *arg)
See comments in Arg_comparator::set_compare_func() for details
*/
-Item *Item_field::set_no_const_sub(byte *arg)
+bool Item_field::set_no_const_sub(byte *arg)
{
if (field->charset() != &my_charset_bin)
no_const_subst=1;
- return this;
+ return FALSE;
}
@@ -5376,6 +5419,31 @@ int Item_default_value::save_in_field(Field *field_arg, bool no_conversions)
}
+/*
+ This method like the walk method traverses the item tree, but at the
+ same time it can replace some nodes in the tree
+*/
+
+Item *Item_default_value::transform(Item_transformer transformer, byte *args)
+{
+ DBUG_ASSERT(!current_thd->is_stmt_prepare());
+
+ Item *new_item= arg->transform(transformer, args);
+ if (!new_item)
+ return 0;
+
+ /*
+ THD::change_item_tree() should be called only if the tree was
+ really transformed, i.e. when a new item has been created.
+ Otherwise we'll be allocating a lot of unnecessary memory for
+ change records at each execution.
+ */
+ if (arg != new_item)
+ current_thd->change_item_tree(&arg, new_item);
+ return (this->*transformer)(args);
+}
+
+
bool Item_insert_value::eq(const Item *item, bool binary_cmp) const
{
return item->type() == INSERT_VALUE_ITEM &&
diff --git a/sql/item.h b/sql/item.h
index 0e62d2aa9f0..81480ce8146 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -774,10 +774,7 @@ public:
return (this->*processor)(arg);
}
- virtual Item* transform(Item_transformer transformer, byte *arg)
- {
- return (this->*transformer)(arg);
- }
+ virtual Item* transform(Item_transformer transformer, byte *arg);
virtual void traverse_cond(Cond_traverser traverser,
void *arg, traverse_order order)
@@ -818,7 +815,7 @@ public:
{ *(bool *)bool_arg= FALSE; return 0; }
virtual Item *equal_fields_propagator(byte * arg) { return this; }
- virtual Item *set_no_const_sub(byte *arg) { return this; }
+ virtual bool set_no_const_sub(byte *arg) { return FALSE; }
virtual Item *replace_equal_field(byte * arg) { return this; }
/*
@@ -1319,7 +1316,7 @@ public:
}
Item_equal *find_item_equal(COND_EQUAL *cond_equal);
Item *equal_fields_propagator(byte *arg);
- Item *set_no_const_sub(byte *arg);
+ bool set_no_const_sub(byte *arg);
Item *replace_equal_field(byte *arg);
inline uint32 max_disp_length() { return field->max_length(); }
Item_field *filed_for_view_update() { return this; }
@@ -2197,18 +2194,7 @@ public:
(this->*processor)(args);
}
- /*
- This method like the walk method traverses the item tree, but
- at the same time it can replace some nodes in the tree
- */
- Item *transform(Item_transformer transformer, byte *args)
- {
- Item *new_item= arg->transform(transformer, args);
- if (!new_item)
- return 0;
- arg= new_item;
- return (this->*transformer)(args);
- }
+ Item *transform(Item_transformer transformer, byte *args);
};
/*
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index d3272fae4ed..88e8890c50e 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -515,8 +515,8 @@ int Arg_comparator::set_compare_func(Item_bool_func2 *item, Item_result type)
which would be transformed to:
WHERE col= 'j'
*/
- (*a)->transform(&Item::set_no_const_sub, (byte*) 0);
- (*b)->transform(&Item::set_no_const_sub, (byte*) 0);
+ (*a)->walk(&Item::set_no_const_sub, (byte*) 0);
+ (*b)->walk(&Item::set_no_const_sub, (byte*) 0);
}
break;
}
@@ -2768,6 +2768,8 @@ bool Item_cond::walk(Item_processor processor, bool walk_subquery, byte *arg)
Item *Item_cond::transform(Item_transformer transformer, byte *arg)
{
+ DBUG_ASSERT(!current_thd->is_stmt_prepare());
+
List_iterator<Item> li(list);
Item *item;
while ((item= li++))
@@ -2775,8 +2777,15 @@ Item *Item_cond::transform(Item_transformer transformer, byte *arg)
Item *new_item= item->transform(transformer, arg);
if (!new_item)
return 0;
+
+ /*
+ THD::change_item_tree() should be called only if the tree was
+ really transformed, i.e. when a new item has been created.
+ Otherwise we'll be allocating a lot of unnecessary memory for
+ change records at each execution.
+ */
if (new_item != item)
- li.replace(new_item);
+ current_thd->change_item_tree(li.ref(), new_item);
}
return Item_func::transform(transformer, arg);
}
@@ -4023,6 +4032,8 @@ bool Item_equal::walk(Item_processor processor, bool walk_subquery, byte *arg)
Item *Item_equal::transform(Item_transformer transformer, byte *arg)
{
+ DBUG_ASSERT(!current_thd->is_stmt_prepare());
+
List_iterator<Item_field> it(fields);
Item *item;
while ((item= it++))
@@ -4030,8 +4041,15 @@ Item *Item_equal::transform(Item_transformer transformer, byte *arg)
Item *new_item= item->transform(transformer, arg);
if (!new_item)
return 0;
+
+ /*
+ THD::change_item_tree() should be called only if the tree was
+ really transformed, i.e. when a new item has been created.
+ Otherwise we'll be allocating a lot of unnecessary memory for
+ change records at each execution.
+ */
if (new_item != item)
- it.replace((Item_field *) new_item);
+ current_thd->change_item_tree((Item **) it.ref(), new_item);
}
return Item_func::transform(transformer, arg);
}
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 390c4482591..ace4acc5245 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -260,6 +260,8 @@ void Item_func::traverse_cond(Cond_traverser traverser,
Item *Item_func::transform(Item_transformer transformer, byte *argument)
{
+ DBUG_ASSERT(!current_thd->is_stmt_prepare());
+
if (arg_count)
{
Item **arg,**arg_end;
@@ -268,6 +270,13 @@ Item *Item_func::transform(Item_transformer transformer, byte *argument)
Item *new_item= (*arg)->transform(transformer, argument);
if (!new_item)
return 0;
+
+ /*
+ THD::change_item_tree() should be called only if the tree was
+ really transformed, i.e. when a new item has been created.
+ Otherwise we'll be allocating a lot of unnecessary memory for
+ change records at each execution.
+ */
if (*arg != new_item)
current_thd->change_item_tree(arg, new_item);
}
diff --git a/sql/item_row.cc b/sql/item_row.cc
index c7b678323a8..6e71ae0d4db 100644
--- a/sql/item_row.cc
+++ b/sql/item_row.cc
@@ -156,12 +156,22 @@ bool Item_row::walk(Item_processor processor, bool walk_subquery, byte *arg)
Item *Item_row::transform(Item_transformer transformer, byte *arg)
{
+ DBUG_ASSERT(!current_thd->is_stmt_prepare());
+
for (uint i= 0; i < arg_count; i++)
{
Item *new_item= items[i]->transform(transformer, arg);
if (!new_item)
return 0;
- items[i]= new_item;
+
+ /*
+ THD::change_item_tree() should be called only if the tree was
+ really transformed, i.e. when a new item has been created.
+ Otherwise we'll be allocating a lot of unnecessary memory for
+ change records at each execution.
+ */
+ if (items[i] != new_item)
+ current_thd->change_item_tree(&items[i], new_item);
}
return (this->*transformer)(arg);
}
diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc
index 23c7cb4c3d8..73a56fb34e9 100644
--- a/sql/item_strfunc.cc
+++ b/sql/item_strfunc.cc
@@ -2051,6 +2051,26 @@ String *Item_func_make_set::val_str(String *str)
}
+Item *Item_func_make_set::transform(Item_transformer transformer, byte *arg)
+{
+ DBUG_ASSERT(!current_thd->is_stmt_prepare());
+
+ Item *new_item= item->transform(transformer, arg);
+ if (!new_item)
+ return 0;
+
+ /*
+ THD::change_item_tree() should be called only if the tree was
+ really transformed, i.e. when a new item has been created.
+ Otherwise we'll be allocating a lot of unnecessary memory for
+ change records at each execution.
+ */
+ if (item != new_item)
+ current_thd->change_item_tree(&item, new_item);
+ return Item_str_func::transform(transformer, arg);
+}
+
+
void Item_func_make_set::print(String *str)
{
str->append(STRING_WITH_LEN("make_set("));
diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h
index 1b843b27203..0a9ca50a028 100644
--- a/sql/item_strfunc.h
+++ b/sql/item_strfunc.h
@@ -484,14 +484,7 @@ public:
return item->walk(processor, walk_subquery, arg) ||
Item_str_func::walk(processor, walk_subquery, arg);
}
- Item *transform(Item_transformer transformer, byte *arg)
- {
- Item *new_item= item->transform(transformer, arg);
- if (!new_item)
- return 0;
- item= new_item;
- return Item_str_func::transform(transformer, arg);
- }
+ Item *transform(Item_transformer transformer, byte *arg);
void print(String *str);
};
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index 5253326797f..3bbe38adf78 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -575,6 +575,7 @@ void get_default_definer(THD *thd, LEX_USER *definer);
LEX_USER *create_default_definer(THD *thd);
LEX_USER *create_definer(THD *thd, LEX_STRING *user_name, LEX_STRING *host_name);
LEX_USER *get_current_user(THD *thd, LEX_USER *user);
+bool check_string_length(LEX_STRING *str, const char *err_msg, uint max_length);
enum enum_mysql_completiontype {
ROLLBACK_RELEASE=-2, ROLLBACK=1, ROLLBACK_AND_CHAIN=7,
@@ -1876,6 +1877,9 @@ void free_list(I_List <i_string> *list);
/* sql_yacc.cc */
extern int MYSQLparse(void *thd);
+#ifndef DBUG_OFF
+extern void turn_parser_debug_on();
+#endif
/* frm_crypt.cc */
#ifdef HAVE_CRYPTED_FRM
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 46f5e0ae4e9..86a7a0a3b58 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -2432,10 +2432,8 @@ static int my_message_sql(uint error, const char *str, myf MyFlags)
if ((thd= current_thd))
{
if (thd->spcont &&
- thd->spcont->find_handler(error, MYSQL_ERROR::WARN_LEVEL_ERROR))
+ thd->spcont->handle_error(error, MYSQL_ERROR::WARN_LEVEL_ERROR, thd))
{
- if (! thd->spcont->found_handler_here())
- thd->net.report_error= 1; /* Make "select" abort correctly */
DBUG_RETURN(0);
}
diff --git a/sql/net_serv.cc b/sql/net_serv.cc
index 03164827e8f..a44e07bbb86 100644
--- a/sql/net_serv.cc
+++ b/sql/net_serv.cc
@@ -95,8 +95,11 @@ extern uint test_flags;
extern ulong bytes_sent, bytes_received, net_big_packet_count;
extern pthread_mutex_t LOCK_bytes_sent , LOCK_bytes_received;
#ifndef MYSQL_INSTANCE_MANAGER
-extern void query_cache_insert(NET *net, const char *packet, ulong length);
+#ifdef HAVE_QUERY_CACHE
#define USE_QUERY_CACHE
+extern void query_cache_init_query(NET *net);
+extern void query_cache_insert(NET *net, const char *packet, ulong length);
+#endif // HAVE_QUERY_CACHE
#define update_statistics(A) A
#endif /* MYSQL_INSTANCE_MANGER */
#endif /* defined(MYSQL_SERVER) && !defined(MYSQL_INSTANCE_MANAGER) */
@@ -132,7 +135,11 @@ my_bool my_net_init(NET *net, Vio* vio)
net->compress=0; net->reading_or_writing=0;
net->where_b = net->remain_in_buf=0;
net->last_errno=0;
- net->query_cache_query=0;
+#ifdef USE_QUERY_CACHE
+ query_cache_init_query(net);
+#else
+ net->query_cache_query= 0;
+#endif
net->report_error= 0;
if (vio != 0) /* If real connection */
@@ -551,10 +558,8 @@ net_real_write(NET *net,const char *packet,ulong len)
my_bool net_blocking = vio_is_blocking(net->vio);
DBUG_ENTER("net_real_write");
-#if defined(MYSQL_SERVER) && defined(HAVE_QUERY_CACHE) \
- && !defined(MYSQL_INSTANCE_MANAGER)
- if (net->query_cache_query != 0)
- query_cache_insert(net, packet, len);
+#if defined(MYSQL_SERVER) && defined(USE_QUERY_CACHE)
+ query_cache_insert(net, packet, len);
#endif
if (net->error == 2)
diff --git a/sql/protocol.cc b/sql/protocol.cc
index 46d28a16c58..e00a70cf3a2 100644
--- a/sql/protocol.cc
+++ b/sql/protocol.cc
@@ -53,8 +53,18 @@ bool Protocol_prep::net_store_data(const char *from, uint length)
}
- /* Send a error string to client */
+/*
+ Send a error string to client
+
+ Design note:
+ net_printf_error and net_send_error are low-level functions
+ that shall be used only when a new connection is being
+ established or at server startup.
+ For SIGNAL/RESIGNAL and GET DIAGNOSTICS functionality it's
+ critical that every error that can be intercepted is issued in one
+ place only, my_message_sql.
+*/
void net_send_error(THD *thd, uint sql_errno, const char *err)
{
NET *net= &thd->net;
@@ -64,19 +74,15 @@ void net_send_error(THD *thd, uint sql_errno, const char *err)
err ? err : net->last_error[0] ?
net->last_error : "NULL"));
+ DBUG_ASSERT(!thd->spcont);
+
if (net && net->no_send_error)
{
thd->clear_error();
DBUG_PRINT("info", ("sending error messages prohibited"));
DBUG_VOID_RETURN;
}
- if (thd->spcont && thd->spcont->find_handler(sql_errno,
- MYSQL_ERROR::WARN_LEVEL_ERROR))
- {
- if (! thd->spcont->found_handler_here())
- thd->net.report_error= 1; /* Make "select" abort correctly */
- DBUG_VOID_RETURN;
- }
+
thd->query_error= 1; // needed to catch query errors during replication
if (!err)
{
@@ -117,6 +123,15 @@ void net_send_error(THD *thd, uint sql_errno, const char *err)
Write error package and flush to client
It's a little too low level, but I don't want to use another buffer for
this
+
+ Design note:
+
+ net_printf_error and net_send_error are low-level functions
+ that shall be used only when a new connection is being
+ established or at server startup.
+ For SIGNAL/RESIGNAL and GET DIAGNOSTICS functionality it's
+ critical that every error that can be intercepted is issued in one
+ place only, my_message_sql.
*/
void
@@ -136,6 +151,8 @@ net_printf_error(THD *thd, uint errcode, ...)
DBUG_ENTER("net_printf_error");
DBUG_PRINT("enter",("message: %u",errcode));
+ DBUG_ASSERT(!thd->spcont);
+
if (net && net->no_send_error)
{
thd->clear_error();
@@ -143,13 +160,6 @@ net_printf_error(THD *thd, uint errcode, ...)
DBUG_VOID_RETURN;
}
- if (thd->spcont && thd->spcont->find_handler(errcode,
- MYSQL_ERROR::WARN_LEVEL_ERROR))
- {
- if (! thd->spcont->found_handler_here())
- thd->net.report_error= 1; /* Make "select" abort correctly */
- DBUG_VOID_RETURN;
- }
thd->query_error= 1; // needed to catch query errors during replication
#ifndef EMBEDDED_LIBRARY
query_cache_abort(net); // Safety
diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt
index 297e4c5c374..24306f38c7d 100644
--- a/sql/share/errmsg.txt
+++ b/sql/share/errmsg.txt
@@ -5954,3 +5954,9 @@ ER_BAD_LOG_ENGINE
eng "One can use only CSV and MyISAM engines for the log tables"
ER_CANT_DROP_LOG_TABLE
eng "Cannot drop log table if log is enabled"
+ER_USERNAME
+ eng "user name"
+ER_HOSTNAME
+ eng "host name"
+ER_WRONG_STRING_LENGTH
+ eng "String '%-.70s' is too long for %s (should be no longer than %d)"
diff --git a/sql/sp.cc b/sql/sp.cc
index b7671803cf5..b799cde14a2 100644
--- a/sql/sp.cc
+++ b/sql/sp.cc
@@ -1007,6 +1007,11 @@ sp_find_routine(THD *thd, int type, sp_name *name, sp_cache **cp,
}
DBUG_RETURN(sp->m_first_free_instance);
}
+ /*
+ Actually depth could be +1 than the actual value in case a SP calls
+ SHOW CREATE PROCEDURE. Hence, the linked list could hold up to one more
+ instance.
+ */
level= sp->m_last_cached_sp->m_recursion_level + 1;
if (level > depth)
@@ -1182,10 +1187,16 @@ sp_show_create_procedure(THD *thd, sp_name *name)
DBUG_ENTER("sp_show_create_procedure");
DBUG_PRINT("enter", ("name: %.*s", name->m_name.length, name->m_name.str));
+ /*
+ Increase the recursion limit for this statement. SHOW CREATE PROCEDURE
+ does not do actual recursion.
+ */
+ thd->variables.max_sp_recursion_depth++;
if ((sp= sp_find_routine(thd, TYPE_ENUM_PROCEDURE, name,
&thd->sp_proc_cache, FALSE)))
ret= sp->show_create_procedure(thd);
+ thd->variables.max_sp_recursion_depth--;
DBUG_RETURN(ret);
}
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index 475419631f7..d55b9ff71de 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -235,6 +235,12 @@ sp_get_flags_for_command(LEX *lex)
else
flags= sp_head::HAS_COMMIT_OR_ROLLBACK;
break;
+ case SQLCOM_FLUSH:
+ flags= sp_head::HAS_SQLCOM_FLUSH;
+ break;
+ case SQLCOM_RESET:
+ flags= sp_head::HAS_SQLCOM_RESET;
+ break;
case SQLCOM_CREATE_INDEX:
case SQLCOM_CREATE_DB:
case SQLCOM_CREATE_VIEW:
diff --git a/sql/sp_head.h b/sql/sp_head.h
index 3ad1e1b85f0..3de23903c47 100644
--- a/sql/sp_head.h
+++ b/sql/sp_head.h
@@ -119,6 +119,8 @@ public:
LOG_SLOW_STATEMENTS= 256, // Used by events
LOG_GENERAL_LOG= 512, // Used by events
BINLOG_ROW_BASED_IF_MIXED= 1024
+ HAS_SQLCOM_RESET= 2048,
+ HAS_SQLCOM_FLUSH= 4096
};
/* TYPE_ENUM_FUNCTION, TYPE_ENUM_PROCEDURE or TYPE_ENUM_TRIGGER */
@@ -337,14 +339,16 @@ public:
my_error(ER_SP_NO_RETSET, MYF(0), where);
else if (m_flags & HAS_SET_AUTOCOMMIT_STMT)
my_error(ER_SP_CANT_SET_AUTOCOMMIT, MYF(0));
- else if (m_type != TYPE_ENUM_PROCEDURE &&
- (m_flags & sp_head::HAS_COMMIT_OR_ROLLBACK))
- {
+ else if (m_flags & HAS_COMMIT_OR_ROLLBACK)
my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0));
- return TRUE;
- }
+ else if (m_flags & HAS_SQLCOM_RESET)
+ my_error(ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0), "RESET");
+ else if (m_flags & HAS_SQLCOM_FLUSH)
+ my_error(ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0), "FLUSH");
+
return test(m_flags &
- (CONTAINS_DYNAMIC_SQL|MULTI_RESULTS|HAS_SET_AUTOCOMMIT_STMT));
+ (CONTAINS_DYNAMIC_SQL|MULTI_RESULTS|HAS_SET_AUTOCOMMIT_STMT|
+ HAS_COMMIT_OR_ROLLBACK|HAS_SQLCOM_RESET|HAS_SQLCOM_FLUSH));
}
#ifndef DBUG_OFF
diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc
index 3bc27a029d0..67ee5459bb4 100644
--- a/sql/sp_rcontext.cc
+++ b/sql/sp_rcontext.cc
@@ -260,6 +260,65 @@ sp_rcontext::find_handler(uint sql_errno,
return TRUE;
}
+/*
+ Handle the error for a given errno.
+ The severity of the error is adjusted depending of the current sql_mode.
+ If an handler is present for the error (see find_handler()),
+ this function will return true.
+ If a handler is found and if the severity of the error indicate
+ that the current instruction executed should abort,
+ the flag thd->net.report_error is also set.
+ This will cause the execution of the current instruction in a
+ sp_instr* to fail, and give control to the handler code itself
+ in the sp_head::execute() loop.
+
+ SYNOPSIS
+ sql_errno The error code
+ level Warning level
+ thd The current thread
+ - thd->net.report_error is an optional output.
+
+ RETURN
+ TRUE if a handler was found.
+ FALSE if no handler was found.
+*/
+bool
+sp_rcontext::handle_error(uint sql_errno,
+ MYSQL_ERROR::enum_warning_level level,
+ THD *thd)
+{
+ bool handled= FALSE;
+ MYSQL_ERROR::enum_warning_level elevated_level= level;
+
+
+ /* Depending on the sql_mode of execution,
+ warnings may be considered errors */
+ if ((level == MYSQL_ERROR::WARN_LEVEL_WARN) &&
+ thd->really_abort_on_warning())
+ {
+ elevated_level= MYSQL_ERROR::WARN_LEVEL_ERROR;
+ }
+
+ if (find_handler(sql_errno, elevated_level))
+ {
+ if (elevated_level == MYSQL_ERROR::WARN_LEVEL_ERROR)
+ {
+ /*
+ Forces to abort the current instruction execution.
+ NOTE: This code is altering the original meaning of
+ the net.report_error flag (send an error to the client).
+ In the context of stored procedures with error handlers,
+ the flag is reused to cause error propagation,
+ until the error handler is reached.
+ No messages will be sent to the client in that context.
+ */
+ thd->net.report_error= 1;
+ }
+ handled= TRUE;
+ }
+
+ return handled;
+}
void
sp_rcontext::push_cursor(sp_lex_keeper *lex_keeper, sp_instr_cpush *i)
diff --git a/sql/sp_rcontext.h b/sql/sp_rcontext.h
index 30521f6da84..5e03aa60d23 100644
--- a/sql/sp_rcontext.h
+++ b/sql/sp_rcontext.h
@@ -128,6 +128,12 @@ class sp_rcontext : public Sql_alloc
bool
find_handler(uint sql_errno,MYSQL_ERROR::enum_warning_level level);
+ // If there is an error handler for this error, handle it and return TRUE.
+ bool
+ handle_error(uint sql_errno,
+ MYSQL_ERROR::enum_warning_level level,
+ THD *thd);
+
// Returns handler type and sets *ip to location if one was found
inline int
found_handler(uint *ip, uint *fp)
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index a6679f99ba2..c7776db324b 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -3041,14 +3041,6 @@ bool mysql_table_grant(THD *thd, TABLE_LIST *table_list,
result= TRUE;
continue;
}
- if (Str->host.length > HOSTNAME_LENGTH ||
- Str->user.length > USERNAME_LENGTH)
- {
- my_message(ER_GRANT_WRONG_HOST_OR_USER, ER(ER_GRANT_WRONG_HOST_OR_USER),
- MYF(0));
- result= TRUE;
- continue;
- }
/* Create user if needed */
error=replace_user_table(thd, tables[0].table, *Str,
0, revoke_grant, create_new_users,
@@ -3253,15 +3245,6 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
result= TRUE;
continue;
}
- if (Str->host.length > HOSTNAME_LENGTH ||
- Str->user.length > USERNAME_LENGTH)
- {
- if (!no_error)
- my_message(ER_GRANT_WRONG_HOST_OR_USER, ER(ER_GRANT_WRONG_HOST_OR_USER),
- MYF(0));
- result= TRUE;
- continue;
- }
/* Create user if needed */
error=replace_user_table(thd, tables[0].table, *Str,
0, revoke_grant, create_new_users,
@@ -3387,14 +3370,6 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
result= TRUE;
continue;
}
- if (Str->host.length > HOSTNAME_LENGTH ||
- Str->user.length > USERNAME_LENGTH)
- {
- my_message(ER_GRANT_WRONG_HOST_OR_USER, ER(ER_GRANT_WRONG_HOST_OR_USER),
- MYF(0));
- result= -1;
- continue;
- }
if (replace_user_table(thd, tables[0].table, *Str,
(!db ? rights : 0), revoke_grant, create_new_users,
test(thd->variables.sql_mode &
@@ -4304,14 +4279,6 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user)
DBUG_RETURN(TRUE);
}
- if (lex_user->host.length > HOSTNAME_LENGTH ||
- lex_user->user.length > USERNAME_LENGTH)
- {
- my_message(ER_GRANT_WRONG_HOST_OR_USER, ER(ER_GRANT_WRONG_HOST_OR_USER),
- MYF(0));
- DBUG_RETURN(TRUE);
- }
-
rw_rdlock(&LOCK_grant);
VOID(pthread_mutex_lock(&acl_cache->lock));
diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc
index 1d35bfd59ad..75c2422edf9 100644
--- a/sql/sql_cache.cc
+++ b/sql/sql_cache.cc
@@ -561,21 +561,62 @@ byte *query_cache_query_get_key(const byte *record, uint *length,
*****************************************************************************/
/*
+ Note on double-check locking (DCL) usage.
+
+ Below, in query_cache_insert(), query_cache_abort() and
+ query_cache_end_of_result() we use what is called double-check
+ locking (DCL) for NET::query_cache_query. I.e. we test it first
+ without a lock, and, if positive, test again under the lock.
+
+ This means that if we see 'NET::query_cache_query == 0' without a
+ lock we will skip the operation. But this is safe here: when we
+ started to cache a query, we called Query_cache::store_query(), and
+ NET::query_cache_query was set to non-zero in this thread (and the
+ thread always sees results of its memory operations, mutex or not).
+ If later we see 'NET::query_cache_query == 0' without locking a
+ mutex, that may only mean that some other thread have reset it by
+ invalidating the query. Skipping the operation in this case is the
+ right thing to do, as NET::query_cache_query won't get non-zero for
+ this query again.
+
+ See also comments in Query_cache::store_query() and
+ Query_cache::send_result_to_client().
+
+ NOTE, however, that double-check locking is not applicable in
+ 'invalidate' functions, as we may erroneously skip invalidation,
+ because the thread doing invalidation may never see non-zero
+ NET::query_cache_query.
+*/
+
+
+void query_cache_init_query(NET *net)
+{
+ /*
+ It is safe to initialize 'NET::query_cache_query' without a lock
+ here, because before it will be accessed from different threads it
+ will be set in this thread under a lock, and access from the same
+ thread is always safe.
+ */
+ net->query_cache_query= 0;
+}
+
+
+/*
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");
+ /* See the comment on double-check locking usage above. */
+ if (net->query_cache_query == 0)
+ DBUG_VOID_RETURN;
+
STRUCT_LOCK(&query_cache.structure_guard_mutex);
- /*
- It is very unlikely that following condition is TRUE (it is possible
- only if other thread is resizing cache), so we check it only after guard
- mutex lock
- */
- if (unlikely(query_cache.query_cache_size == 0))
+
+ if (unlikely(query_cache.query_cache_size == 0 ||
+ query_cache.flush_in_progress))
{
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
DBUG_VOID_RETURN;
@@ -612,10 +653,10 @@ void query_cache_insert(NET *net, const char *packet, ulong length)
header->result(result);
header->last_pkt_nr= net->pkt_nr;
BLOCK_UNLOCK_WR(query_block);
+ DBUG_EXECUTE("check_querycache",query_cache.check_integrity(0););
}
else
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
- DBUG_EXECUTE("check_querycache",query_cache.check_integrity(0););
DBUG_VOID_RETURN;
}
@@ -624,33 +665,33 @@ void query_cache_abort(NET *net)
{
DBUG_ENTER("query_cache_abort");
- if (net->query_cache_query != 0) // Quick check on unlocked structure
+ /* See the comment on double-check locking usage above. */
+ if (net->query_cache_query == 0)
+ DBUG_VOID_RETURN;
+
+ STRUCT_LOCK(&query_cache.structure_guard_mutex);
+
+ if (unlikely(query_cache.query_cache_size == 0 ||
+ query_cache.flush_in_progress))
{
- STRUCT_LOCK(&query_cache.structure_guard_mutex);
- /*
- It is very unlikely that following condition is TRUE (it is possible
- only if other thread is resizing cache), so we check it only after guard
- mutex lock
- */
- if (unlikely(query_cache.query_cache_size == 0))
- {
- STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
- DBUG_VOID_RETURN;
- }
+ STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
+ DBUG_VOID_RETURN;
+ }
- Query_cache_block *query_block = ((Query_cache_block*)
- net->query_cache_query);
- if (query_block) // Test if changed by other thread
- {
- DUMP(&query_cache);
- 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;
+ Query_cache_block *query_block= ((Query_cache_block*)
+ net->query_cache_query);
+ if (query_block) // Test if changed by other thread
+ {
+ DUMP(&query_cache);
+ 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;
DBUG_EXECUTE("check_querycache",query_cache.check_integrity(1););
- STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
}
+
+ STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
+
DBUG_VOID_RETURN;
}
@@ -659,60 +700,65 @@ void query_cache_end_of_result(THD *thd)
{
DBUG_ENTER("query_cache_end_of_result");
- if (thd->net.query_cache_query != 0) // Quick check on unlocked structure
- {
+ /* See the comment on double-check locking usage above. */
+ if (thd->net.query_cache_query == 0)
+ DBUG_VOID_RETURN;
+
#ifdef EMBEDDED_LIBRARY
- query_cache_insert(&thd->net, (char*)thd,
- emb_count_querycache_size(thd));
+ query_cache_insert(&thd->net, (char*)thd,
+ emb_count_querycache_size(thd));
#endif
- STRUCT_LOCK(&query_cache.structure_guard_mutex);
- /*
- It is very unlikely that following condition is TRUE (it is possible
- only if other thread is resizing cache), so we check it only after guard
- mutex lock
- */
- if (unlikely(query_cache.query_cache_size == 0))
- {
- STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
- DBUG_VOID_RETURN;
- }
- Query_cache_block *query_block = ((Query_cache_block*)
- thd->net.query_cache_query);
- if (query_block)
- {
- DUMP(&query_cache);
- BLOCK_LOCK_WR(query_block);
- Query_cache_query *header = query_block->query();
- Query_cache_block *last_result_block = header->result()->prev;
- ulong allign_size = ALIGN_SIZE(last_result_block->used);
- ulong len = max(query_cache.min_allocation_unit, allign_size);
- if (last_result_block->length >= query_cache.min_allocation_unit + len)
- query_cache.split_block(last_result_block,len);
- STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
+ STRUCT_LOCK(&query_cache.structure_guard_mutex);
+
+ if (unlikely(query_cache.query_cache_size == 0 ||
+ query_cache.flush_in_progress))
+ {
+ STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
+ DBUG_VOID_RETURN;
+ }
+
+ Query_cache_block *query_block= ((Query_cache_block*)
+ thd->net.query_cache_query);
+ if (query_block)
+ {
+ DUMP(&query_cache);
+ BLOCK_LOCK_WR(query_block);
+ Query_cache_query *header= query_block->query();
+ Query_cache_block *last_result_block= header->result()->prev;
+ ulong allign_size= ALIGN_SIZE(last_result_block->used);
+ ulong len= max(query_cache.min_allocation_unit, allign_size);
+ if (last_result_block->length >= query_cache.min_allocation_unit + len)
+ query_cache.split_block(last_result_block,len);
#ifndef DBUG_OFF
- if (header->result() == 0)
- {
- DBUG_PRINT("error", ("end of data whith no result. query '%s'",
- header->query()));
- query_cache.wreck(__LINE__, "");
- DBUG_VOID_RETURN;
- }
-#endif
- header->found_rows(current_thd->limit_found_rows);
- header->result()->type = Query_cache_block::RESULT;
- header->writer(0);
- BLOCK_UNLOCK_WR(query_block);
- }
- else
+ if (header->result() == 0)
{
- // Cache was flushed or resized and query was deleted => do nothing
+ DBUG_PRINT("error", ("end of data whith no result. query '%s'",
+ header->query()));
+ query_cache.wreck(__LINE__, "");
+
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
+
+ DBUG_VOID_RETURN;
}
- thd->net.query_cache_query=0;
- DBUG_EXECUTE("check_querycache",query_cache.check_integrity(0););
+#endif
+ header->found_rows(current_thd->limit_found_rows);
+ header->result()->type= Query_cache_block::RESULT;
+ header->writer(0);
+ thd->net.query_cache_query= 0;
+ DBUG_EXECUTE("check_querycache",query_cache.check_integrity(1););
+
+ STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
+
+ BLOCK_UNLOCK_WR(query_block);
+ }
+ else
+ {
+ // Cache was flushed or resized and query was deleted => do nothing
+ STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
}
+
DBUG_VOID_RETURN;
}
@@ -758,8 +804,7 @@ ulong Query_cache::resize(ulong query_cache_size_arg)
query_cache_size_arg));
DBUG_ASSERT(initialized);
STRUCT_LOCK(&structure_guard_mutex);
- if (query_cache_size > 0)
- free_cache();
+ free_cache();
query_cache_size= query_cache_size_arg;
::query_cache_size= init_cache();
STRUCT_UNLOCK(&structure_guard_mutex);
@@ -780,7 +825,15 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used)
TABLE_COUNTER_TYPE local_tables;
ulong tot_length;
DBUG_ENTER("Query_cache::store_query");
- if (query_cache_size == 0 || thd->locked_tables)
+ /*
+ Testing 'query_cache_size' without a lock here is safe: the thing
+ we may loose is that the query won't be cached, but we save on
+ mutex locking in the case when query cache is disabled or the
+ query is uncachable.
+
+ See also a note on double-check locking usage above.
+ */
+ if (thd->locked_tables || query_cache_size == 0)
DBUG_VOID_RETURN;
uint8 tables_type= 0;
@@ -832,9 +885,9 @@ sql mode: 0x%lx, sort len: %lu, conncat len: %lu",
acquiring the query cache mutex.
*/
ha_release_temporary_latches(thd);
- STRUCT_LOCK(&structure_guard_mutex);
- if (query_cache_size == 0)
+ STRUCT_LOCK(&structure_guard_mutex);
+ if (query_cache_size == 0 || flush_in_progress)
{
STRUCT_UNLOCK(&structure_guard_mutex);
DBUG_VOID_RETURN;
@@ -908,11 +961,12 @@ sql mode: 0x%lx, sort len: %lu, conncat len: %lu",
double_linked_list_simple_include(query_block, &queries_blocks);
inserts++;
queries_in_cache++;
- STRUCT_UNLOCK(&structure_guard_mutex);
-
net->query_cache_query= (gptr) query_block;
header->writer(net);
header->tables_type(tables_type);
+
+ STRUCT_UNLOCK(&structure_guard_mutex);
+
// init_n_lock make query block locked
BLOCK_UNLOCK_WR(query_block);
}
@@ -966,12 +1020,16 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
Query_cache_query_flags flags;
DBUG_ENTER("Query_cache::send_result_to_client");
- if (query_cache_size == 0 || thd->locked_tables ||
- thd->variables.query_cache_type == 0)
- goto err;
+ /*
+ Testing 'query_cache_size' without a lock here is safe: the thing
+ we may loose is that the query won't be served from cache, but we
+ save on mutex locking in the case when query cache is disabled.
- /* Check that we haven't forgot to reset the query cache variables */
- DBUG_ASSERT(thd->net.query_cache_query == 0);
+ See also a note on double-check locking usage above.
+ */
+ if (thd->locked_tables || thd->variables.query_cache_type == 0 ||
+ query_cache_size == 0)
+ goto err;
if (!thd->lex->safe_to_cache_query)
{
@@ -1008,11 +1066,15 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
}
STRUCT_LOCK(&structure_guard_mutex);
- if (query_cache_size == 0)
+ if (query_cache_size == 0 || flush_in_progress)
{
DBUG_PRINT("qcache", ("query cache disabled"));
goto err_unlock;
}
+
+ /* Check that we haven't forgot to reset the query cache variables */
+ DBUG_ASSERT(thd->net.query_cache_query == 0);
+
Query_cache_block *query_block;
tot_length= query_length + thd->db_length + 1 + QUERY_CACHE_FLAGS_SIZE;
@@ -1238,45 +1300,43 @@ void Query_cache::invalidate(THD *thd, TABLE_LIST *tables_used,
my_bool using_transactions)
{
DBUG_ENTER("Query_cache::invalidate (table list)");
- if (query_cache_size > 0)
+ STRUCT_LOCK(&structure_guard_mutex);
+ if (query_cache_size > 0 && !flush_in_progress)
{
- STRUCT_LOCK(&structure_guard_mutex);
- if (query_cache_size > 0)
- {
- DUMP(this);
+ DUMP(this);
- using_transactions = using_transactions &&
- (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
- for (; tables_used; tables_used= tables_used->next_local)
- {
- DBUG_ASSERT(!using_transactions || tables_used->table!=0);
- if (tables_used->derived)
- continue;
- if (using_transactions &&
- (tables_used->table->file->table_cache_type() ==
- HA_CACHE_TBL_TRANSACT))
- /*
- Tables_used->table can't be 0 in transaction.
- Only 'drop' invalidate not opened table, but 'drop'
- force transaction finish.
- */
- thd->add_changed_table(tables_used->table);
- else
- invalidate_table(tables_used);
- }
+ using_transactions= using_transactions &&
+ (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
+ for (; tables_used; tables_used= tables_used->next_local)
+ {
+ DBUG_ASSERT(!using_transactions || tables_used->table!=0);
+ if (tables_used->derived)
+ continue;
+ if (using_transactions &&
+ (tables_used->table->file->table_cache_type() ==
+ HA_CACHE_TBL_TRANSACT))
+ /*
+ Tables_used->table can't be 0 in transaction.
+ Only 'drop' invalidate not opened table, but 'drop'
+ force transaction finish.
+ */
+ thd->add_changed_table(tables_used->table);
+ else
+ invalidate_table(tables_used);
}
- STRUCT_UNLOCK(&structure_guard_mutex);
}
+ STRUCT_UNLOCK(&structure_guard_mutex);
+
DBUG_VOID_RETURN;
}
void Query_cache::invalidate(CHANGED_TABLE_LIST *tables_used)
{
DBUG_ENTER("Query_cache::invalidate (changed table list)");
- if (query_cache_size > 0 && tables_used)
+ if (tables_used)
{
STRUCT_LOCK(&structure_guard_mutex);
- if (query_cache_size > 0)
+ if (query_cache_size > 0 && !flush_in_progress)
{
DUMP(this);
for (; tables_used; tables_used= tables_used->next)
@@ -1306,10 +1366,10 @@ void Query_cache::invalidate(CHANGED_TABLE_LIST *tables_used)
void Query_cache::invalidate_locked_for_write(TABLE_LIST *tables_used)
{
DBUG_ENTER("Query_cache::invalidate_locked_for_write");
- if (query_cache_size > 0 && tables_used)
+ if (tables_used)
{
STRUCT_LOCK(&structure_guard_mutex);
- if (query_cache_size > 0)
+ if (query_cache_size > 0 && !flush_in_progress)
{
DUMP(this);
for (; tables_used; tables_used= tables_used->next_local)
@@ -1333,21 +1393,19 @@ void Query_cache::invalidate(THD *thd, TABLE *table,
{
DBUG_ENTER("Query_cache::invalidate (table)");
- if (query_cache_size > 0)
+ STRUCT_LOCK(&structure_guard_mutex);
+ if (query_cache_size > 0 && !flush_in_progress)
{
- STRUCT_LOCK(&structure_guard_mutex);
- if (query_cache_size > 0)
- {
- using_transactions = using_transactions &&
- (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
- if (using_transactions &&
- (table->file->table_cache_type() == HA_CACHE_TBL_TRANSACT))
- thd->add_changed_table(table);
- else
- invalidate_table(table);
- }
- STRUCT_UNLOCK(&structure_guard_mutex);
+ using_transactions= using_transactions &&
+ (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
+ if (using_transactions &&
+ (table->file->table_cache_type() == HA_CACHE_TBL_TRANSACT))
+ thd->add_changed_table(table);
+ else
+ invalidate_table(table);
}
+ STRUCT_UNLOCK(&structure_guard_mutex);
+
DBUG_VOID_RETURN;
}
@@ -1356,20 +1414,18 @@ void Query_cache::invalidate(THD *thd, const char *key, uint32 key_length,
{
DBUG_ENTER("Query_cache::invalidate (key)");
- if (query_cache_size > 0)
+ STRUCT_LOCK(&structure_guard_mutex);
+ if (query_cache_size > 0 && !flush_in_progress)
{
- using_transactions = using_transactions &&
+ using_transactions= using_transactions &&
(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
if (using_transactions) // used for innodb => has_transactions() is TRUE
thd->add_changed_table(key, key_length);
else
- {
- STRUCT_LOCK(&structure_guard_mutex);
- if (query_cache_size > 0)
- invalidate_table((byte*)key, key_length);
- STRUCT_UNLOCK(&structure_guard_mutex);
- }
+ invalidate_table((byte*)key, key_length);
}
+ STRUCT_UNLOCK(&structure_guard_mutex);
+
DBUG_VOID_RETURN;
}
@@ -1380,38 +1436,36 @@ void Query_cache::invalidate(THD *thd, const char *key, uint32 key_length,
void Query_cache::invalidate(char *db)
{
DBUG_ENTER("Query_cache::invalidate (db)");
- if (query_cache_size > 0)
+ STRUCT_LOCK(&structure_guard_mutex);
+ if (query_cache_size > 0 && !flush_in_progress)
{
- STRUCT_LOCK(&structure_guard_mutex);
- if (query_cache_size > 0)
- {
- DUMP(this);
+ DUMP(this);
restart_search:
- if (tables_blocks)
+ if (tables_blocks)
+ {
+ Query_cache_block *curr= tables_blocks;
+ Query_cache_block *next;
+ do
{
- Query_cache_block *curr= tables_blocks;
- Query_cache_block *next;
- do
- {
- next= curr->next;
- if (strcmp(db, (char*)(curr->table()->db())) == 0)
- invalidate_table(curr);
- /*
- invalidate_table can freed block on which point 'next' (if
- table of this block used only in queries which was deleted
- by invalidate_table). As far as we do not allocate new blocks
- and mark all headers of freed blocks as 'FREE' (even if they are
- merged with other blocks) we can just test type of block
- to be sure that block is not deleted
- */
- if (next->type == Query_cache_block::FREE)
- goto restart_search;
- curr= next;
- } while (curr != tables_blocks);
- }
+ next= curr->next;
+ if (strcmp(db, (char*)(curr->table()->db())) == 0)
+ invalidate_table(curr);
+ /*
+ invalidate_table can freed block on which point 'next' (if
+ table of this block used only in queries which was deleted
+ by invalidate_table). As far as we do not allocate new blocks
+ and mark all headers of freed blocks as 'FREE' (even if they are
+ merged with other blocks) we can just test type of block
+ to be sure that block is not deleted
+ */
+ if (next->type == Query_cache_block::FREE)
+ goto restart_search;
+ curr= next;
+ } while (curr != tables_blocks);
}
- STRUCT_UNLOCK(&structure_guard_mutex);
}
+ STRUCT_UNLOCK(&structure_guard_mutex);
+
DBUG_VOID_RETURN;
}
@@ -1419,23 +1473,22 @@ void Query_cache::invalidate(char *db)
void Query_cache::invalidate_by_MyISAM_filename(const char *filename)
{
DBUG_ENTER("Query_cache::invalidate_by_MyISAM_filename");
- if (query_cache_size > 0)
+
+ STRUCT_LOCK(&structure_guard_mutex);
+ if (query_cache_size > 0 && !flush_in_progress)
{
/* Calculate the key outside the lock to make the lock shorter */
char key[MAX_DBKEY_LENGTH];
uint32 db_length;
uint key_length= filename_2_table_key(key, filename, &db_length);
- STRUCT_LOCK(&structure_guard_mutex);
- if (query_cache_size > 0) // Safety if cache removed
- {
- Query_cache_block *table_block;
- if ((table_block = (Query_cache_block*) hash_search(&tables,
- (byte*) key,
- key_length)))
- invalidate_table(table_block);
- }
- STRUCT_UNLOCK(&structure_guard_mutex);
+ Query_cache_block *table_block;
+ if ((table_block = (Query_cache_block*) hash_search(&tables,
+ (byte*) key,
+ key_length)))
+ invalidate_table(table_block);
}
+ STRUCT_UNLOCK(&structure_guard_mutex);
+
DBUG_VOID_RETURN;
}
@@ -1480,7 +1533,12 @@ void Query_cache::destroy()
}
else
{
+ /* Underlying code expects the lock. */
+ STRUCT_LOCK(&structure_guard_mutex);
free_cache();
+ STRUCT_UNLOCK(&structure_guard_mutex);
+
+ pthread_cond_destroy(&COND_flush_finished);
pthread_mutex_destroy(&structure_guard_mutex);
initialized = 0;
}
@@ -1496,6 +1554,8 @@ void Query_cache::init()
{
DBUG_ENTER("Query_cache::init");
pthread_mutex_init(&structure_guard_mutex,MY_MUTEX_INIT_FAST);
+ pthread_cond_init(&COND_flush_finished, NULL);
+ flush_in_progress= FALSE;
initialized = 1;
DBUG_VOID_RETURN;
}
@@ -1691,6 +1751,17 @@ void Query_cache::make_disabled()
}
+/*
+ free_cache() - free all resources allocated by the cache.
+
+ SYNOPSIS
+ free_cache()
+
+ DESCRIPTION
+ This function frees all resources allocated by the cache. You
+ have to call init_cache() before using the cache again.
+*/
+
void Query_cache::free_cache()
{
DBUG_ENTER("Query_cache::free_cache");
@@ -1725,17 +1796,51 @@ void Query_cache::free_cache()
Free block data
*****************************************************************************/
+
/*
- The following assumes we have a lock on the cache
+ flush_cache() - flush the cache.
+
+ SYNOPSIS
+ flush_cache()
+
+ DESCRIPTION
+ This function will flush cache contents. It assumes we have
+ 'structure_guard_mutex' locked. The function sets the
+ flush_in_progress flag and releases the lock, so other threads may
+ proceed skipping the cache as if it is disabled. Concurrent
+ flushes are performed in turn.
*/
void Query_cache::flush_cache()
{
+ /*
+ If there is flush in progress, wait for it to finish, and then do
+ our flush. This is necessary because something could be added to
+ the cache before we acquire the lock again, and some code (like
+ Query_cache::free_cache()) depends on the fact that after the
+ flush the cache is empty.
+ */
+ while (flush_in_progress)
+ pthread_cond_wait(&COND_flush_finished, &structure_guard_mutex);
+
+ /*
+ Setting 'flush_in_progress' will prevent other threads from using
+ the cache while we are in the middle of the flush, and we release
+ the lock so that other threads won't block.
+ */
+ flush_in_progress= TRUE;
+ STRUCT_UNLOCK(&structure_guard_mutex);
+
+ my_hash_reset(&queries);
while (queries_blocks != 0)
{
BLOCK_LOCK_WR(queries_blocks);
- free_query(queries_blocks);
+ free_query_internal(queries_blocks);
}
+
+ STRUCT_LOCK(&structure_guard_mutex);
+ flush_in_progress= FALSE;
+ pthread_cond_signal(&COND_flush_finished);
}
/*
@@ -1781,36 +1886,48 @@ my_bool Query_cache::free_old_query()
DBUG_RETURN(1); // Nothing to remove
}
+
/*
- Free query from query cache.
- query_block must be locked for writing.
- This function will remove (and destroy) the lock for the query.
+ free_query_internal() - free query from query cache.
+
+ SYNOPSIS
+ free_query_internal()
+ query_block Query_cache_block representing the query
+
+ DESCRIPTION
+ This function will remove the query from a cache, and place its
+ memory blocks to the list of free blocks. 'query_block' must be
+ locked for writing, this function will release (and destroy) this
+ lock.
+
+ NOTE
+ 'query_block' should be removed from 'queries' hash _before_
+ calling this method, as the lock will be destroyed here.
*/
-void Query_cache::free_query(Query_cache_block *query_block)
+void Query_cache::free_query_internal(Query_cache_block *query_block)
{
- DBUG_ENTER("Query_cache::free_query");
+ DBUG_ENTER("Query_cache::free_query_internal");
DBUG_PRINT("qcache", ("free query 0x%lx %lu bytes result",
(ulong) query_block,
query_block->query()->length() ));
queries_in_cache--;
- hash_delete(&queries,(byte *) query_block);
- Query_cache_query *query = query_block->query();
+ Query_cache_query *query= query_block->query();
if (query->writer() != 0)
{
/* Tell MySQL that this query should not be cached anymore */
- query->writer()->query_cache_query = 0;
+ query->writer()->query_cache_query= 0;
query->writer(0);
}
double_linked_list_exclude(query_block, &queries_blocks);
- Query_cache_block_table *table=query_block->table(0);
+ Query_cache_block_table *table= query_block->table(0);
- for (TABLE_COUNTER_TYPE i=0; i < query_block->n_tables; i++)
+ for (TABLE_COUNTER_TYPE i= 0; i < query_block->n_tables; i++)
unlink_table(table++);
- Query_cache_block *result_block = query->result();
+ Query_cache_block *result_block= query->result();
/*
The following is true when query destruction was called and no results
@@ -1824,11 +1941,11 @@ void Query_cache::free_query(Query_cache_block *query_block)
refused++;
inserts--;
}
- Query_cache_block *block = result_block;
+ Query_cache_block *block= result_block;
do
{
- Query_cache_block *current = block;
- block = block->next;
+ Query_cache_block *current= block;
+ block= block->next;
free_memory_block(current);
} while (block != result_block);
}
@@ -1845,6 +1962,32 @@ void Query_cache::free_query(Query_cache_block *query_block)
DBUG_VOID_RETURN;
}
+
+/*
+ free_query() - free query from query cache.
+
+ SYNOPSIS
+ free_query()
+ query_block Query_cache_block representing the query
+
+ DESCRIPTION
+ This function will remove 'query_block' from 'queries' hash, and
+ then call free_query_internal(), which see.
+*/
+
+void Query_cache::free_query(Query_cache_block *query_block)
+{
+ DBUG_ENTER("Query_cache::free_query");
+ DBUG_PRINT("qcache", ("free query 0x%lx %lu bytes result",
+ (ulong) query_block,
+ query_block->query()->length() ));
+
+ hash_delete(&queries,(byte *) query_block);
+ free_query_internal(query_block);
+
+ DBUG_VOID_RETURN;
+}
+
/*****************************************************************************
Query data creation
*****************************************************************************/
@@ -2430,12 +2573,8 @@ Query_cache::allocate_block(ulong len, my_bool not_less, ulong min,
if (!under_guard)
{
STRUCT_LOCK(&structure_guard_mutex);
- /*
- It is very unlikely that following condition is TRUE (it is possible
- only if other thread is resizing cache), so we check it only after
- guard mutex lock
- */
- if (unlikely(query_cache.query_cache_size == 0))
+
+ if (unlikely(query_cache.query_cache_size == 0 || flush_in_progress))
{
STRUCT_UNLOCK(&structure_guard_mutex);
DBUG_RETURN(0);
@@ -2891,11 +3030,9 @@ static TABLE_COUNTER_TYPE process_and_count_tables(TABLE_LIST *tables_used,
(query without tables are not cached)
*/
-TABLE_COUNTER_TYPE Query_cache::is_cacheable(THD *thd, uint32 query_len,
- char *query,
- LEX *lex,
- TABLE_LIST *tables_used,
- uint8 *tables_type)
+TABLE_COUNTER_TYPE
+Query_cache::is_cacheable(THD *thd, uint32 query_len, char *query, LEX *lex,
+ TABLE_LIST *tables_used, uint8 *tables_type)
{
TABLE_COUNTER_TYPE table_count;
DBUG_ENTER("Query_cache::is_cacheable");
@@ -2980,13 +3117,10 @@ my_bool Query_cache::ask_handler_allowance(THD *thd,
void Query_cache::pack_cache()
{
DBUG_ENTER("Query_cache::pack_cache");
+
STRUCT_LOCK(&structure_guard_mutex);
- /*
- It is very unlikely that following condition is TRUE (it is possible
- only if other thread is resizing cache), so we check it only after
- guard mutex lock
- */
- if (unlikely(query_cache_size == 0))
+
+ if (unlikely(query_cache_size == 0 || flush_in_progress))
{
STRUCT_UNLOCK(&structure_guard_mutex);
DBUG_VOID_RETURN;
@@ -3301,7 +3435,7 @@ my_bool Query_cache::join_results(ulong join_limit)
DBUG_ENTER("Query_cache::join_results");
STRUCT_LOCK(&structure_guard_mutex);
- if (queries_blocks != 0)
+ if (queries_blocks != 0 && !flush_in_progress)
{
DBUG_ASSERT(query_cache_size > 0);
Query_cache_block *block = queries_blocks;
@@ -3588,31 +3722,23 @@ void Query_cache::tables_dump()
}
-my_bool Query_cache::check_integrity(bool not_locked)
+my_bool Query_cache::check_integrity(bool locked)
{
my_bool result = 0;
uint i;
DBUG_ENTER("check_integrity");
- if (query_cache_size == 0)
+ if (!locked)
+ STRUCT_LOCK(&structure_guard_mutex);
+
+ if (unlikely(query_cache_size == 0 || flush_in_progress))
{
+ if (!locked)
+ STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
+
DBUG_PRINT("qcache", ("Query Cache not initialized"));
DBUG_RETURN(0);
}
- if (!not_locked)
- {
- STRUCT_LOCK(&structure_guard_mutex);
- /*
- It is very unlikely that following condition is TRUE (it is possible
- only if other thread is resizing cache), so we check it only after
- guard mutex lock
- */
- if (unlikely(query_cache_size == 0))
- {
- STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
- DBUG_RETURN(0);
- }
- }
if (hash_check(&queries))
{
@@ -3857,7 +3983,7 @@ my_bool Query_cache::check_integrity(bool not_locked)
}
}
DBUG_ASSERT(result == 0);
- if (!not_locked)
+ if (!locked)
STRUCT_UNLOCK(&structure_guard_mutex);
DBUG_RETURN(result);
}
diff --git a/sql/sql_cache.h b/sql/sql_cache.h
index 29d314d3c44..a66067159e2 100644
--- a/sql/sql_cache.h
+++ b/sql/sql_cache.h
@@ -195,7 +195,6 @@ extern "C"
byte *query_cache_table_get_key(const byte *record, uint *length,
my_bool not_used);
}
-void query_cache_insert(NET *thd, const char *packet, ulong length);
extern "C" void query_cache_invalidate_by_MyISAM_filename(const char* filename);
@@ -241,6 +240,12 @@ public:
ulong free_memory, queries_in_cache, hits, inserts, refused,
free_memory_blocks, total_blocks, lowmem_prunes;
+private:
+ pthread_cond_t COND_flush_finished;
+ bool flush_in_progress;
+
+ void free_query_internal(Query_cache_block *point);
+
protected:
/*
The following mutex is locked when searching or changing global
@@ -249,6 +254,12 @@ protected:
LOCK SEQUENCE (to prevent deadlocks):
1. structure_guard_mutex
2. query block (for operation inside query (query block/results))
+
+ Thread doing cache flush releases the mutex once it sets
+ flush_in_progress flag, so other threads may bypass the cache as
+ if it is disabled, not waiting for reset to finish. The exception
+ is other threads that were going to do cache flush---they'll wait
+ till the end of a flush operation.
*/
pthread_mutex_t structure_guard_mutex;
byte *cache; // cache memory
@@ -358,6 +369,7 @@ protected:
If query is cacheable return number tables in query
(query without tables not cached)
*/
+ static
TABLE_COUNTER_TYPE is_cacheable(THD *thd, uint32 query_len, char *query,
LEX *lex, TABLE_LIST *tables_used,
uint8 *tables_type);
@@ -410,6 +422,7 @@ protected:
void destroy();
+ friend void query_cache_init_query(NET *net);
friend void query_cache_insert(NET *net, const char *packet, ulong length);
friend void query_cache_end_of_result(THD *thd);
friend void query_cache_abort(NET *net);
@@ -435,6 +448,8 @@ protected:
extern Query_cache query_cache;
extern TYPELIB query_cache_type_typelib;
+void query_cache_init_query(NET *net);
+void query_cache_insert(NET *net, const char *packet, ulong length);
void query_cache_end_of_result(THD *thd);
void query_cache_abort(NET *net);
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 827a12e4ebd..72051b2b29d 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -261,7 +261,7 @@ THD::THD()
#endif
client_capabilities= 0; // minimalistic client
net.last_error[0]=0; // If error on boot
- net.query_cache_query=0; // If error on boot
+ query_cache_init_query(&net); // If error on boot
ull=0;
system_thread= NON_SYSTEM_THREAD;
cleanup_done= abort_on_warning= no_warnings_for_error= 0;
diff --git a/sql/sql_error.cc b/sql/sql_error.cc
index 9a9a9784df3..dc7a437d7d1 100644
--- a/sql/sql_error.cc
+++ b/sql/sql_error.cc
@@ -139,14 +139,8 @@ MYSQL_ERROR *push_warning(THD *thd, MYSQL_ERROR::enum_warning_level level,
}
if (thd->spcont &&
- thd->spcont->find_handler(code,
- ((int) level >=
- (int) MYSQL_ERROR::WARN_LEVEL_WARN &&
- thd->really_abort_on_warning()) ?
- MYSQL_ERROR::WARN_LEVEL_ERROR : level))
+ thd->spcont->handle_error(code, level, thd))
{
- if (! thd->spcont->found_handler_here())
- thd->net.report_error= 1; /* Make "select" abort correctly */
DBUG_RETURN(NULL);
}
query_cache_abort(&thd->net);
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 63de7bb1930..eafd0e93cdb 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -6036,6 +6036,9 @@ void mysql_init_multi_delete(LEX *lex)
void mysql_parse(THD *thd, char *inBuf, uint length)
{
DBUG_ENTER("mysql_parse");
+
+ DBUG_EXECUTE_IF("parser_debug", turn_parser_debug_on(););
+
mysql_init_query(thd, (uchar*) inBuf, length);
if (query_cache_send_result_to_client(thd, inBuf, length) <= 0)
{
@@ -6943,11 +6946,7 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
select_errors=0; /* Write if more errors */
bool tmp_write_to_binlog= 1;
- if (thd && thd->in_sub_stmt)
- {
- my_error(ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0), "FLUSH");
- return 1;
- }
+ DBUG_ASSERT(!thd || !thd->in_sub_stmt);
#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (options & REFRESH_GRANT)
@@ -7817,16 +7816,34 @@ LEX_USER *create_definer(THD *thd, LEX_STRING *user_name, LEX_STRING *host_name)
LEX_USER *get_current_user(THD *thd, LEX_USER *user)
{
- LEX_USER *curr_user;
if (!user->user.str) // current_user
- {
- if (!(curr_user= (LEX_USER*) thd->alloc(sizeof(LEX_USER))))
- {
- my_error(ER_OUTOFMEMORY, MYF(0), sizeof(LEX_USER));
- return 0;
- }
- get_default_definer(thd, curr_user);
- return curr_user;
- }
+ return create_default_definer(thd);
+
return user;
}
+
+
+/*
+ Check that length of a string does not exceed some limit.
+
+ SYNOPSIS
+ check_string_length()
+ str string to be checked
+ err_msg error message to be displayed if the string is too long
+ max_length max length
+
+ RETURN
+ FALSE the passed string is not longer than max_length
+ TRUE the passed string is longer than max_length
+*/
+
+bool check_string_length(LEX_STRING *str, const char *err_msg,
+ uint max_length)
+{
+ if (str->length <= max_length)
+ return FALSE;
+
+ my_error(ER_WRONG_STRING_LENGTH, MYF(0), str->str, err_msg, max_length);
+
+ return TRUE;
+}
diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc
index a49e9d67a00..acb7d5b61df 100644
--- a/sql/sql_trigger.cc
+++ b/sql/sql_trigger.cc
@@ -158,11 +158,13 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
{
TABLE *table;
bool result= TRUE;
- LEX_STRING definer_user;
- LEX_STRING definer_host;
+ String stmt_query;
DBUG_ENTER("mysql_create_or_drop_trigger");
+ /* Charset of the buffer for statement must be system one. */
+ stmt_query.set_charset(system_charset_info);
+
/*
QQ: This function could be merged in mysql_alter_table() function
But do we want this ?
@@ -270,8 +272,8 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
}
result= (create ?
- table->triggers->create_trigger(thd, tables, &definer_user, &definer_host):
- table->triggers->drop_trigger(thd, tables));
+ table->triggers->create_trigger(thd, tables, &stmt_query):
+ table->triggers->drop_trigger(thd, tables, &stmt_query));
end:
VOID(pthread_mutex_unlock(&LOCK_open));
@@ -283,32 +285,9 @@ end:
{
thd->clear_error();
- String log_query(thd->query, thd->query_length, system_charset_info);
-
- if (create)
- {
- log_query.set((char *) 0, 0, system_charset_info); /* reset log_query */
-
- log_query.append(STRING_WITH_LEN("CREATE "));
-
- if (definer_user.str && definer_host.str)
- {
- /*
- Append definer-clause if the trigger is SUID (a usual trigger in
- new MySQL versions).
- */
-
- append_definer(thd, &log_query, &definer_user, &definer_host);
- }
-
- log_query.append(thd->lex->stmt_definition_begin,
- (char *)thd->lex->sphead->m_body_begin -
- thd->lex->stmt_definition_begin +
- thd->lex->sphead->m_body.length);
- }
-
/* Such a statement can always go directly to binlog, no trans cache. */
- Query_log_event qinfo(thd, log_query.ptr(), log_query.length(), 0, FALSE);
+ Query_log_event qinfo(thd, stmt_query.ptr(), stmt_query.length(), 0,
+ FALSE);
mysql_bin_log.write(&qinfo);
}
@@ -328,22 +307,8 @@ end:
LEX)
tables - table list containing one open table for which the
trigger is created.
- definer_user - [out] after a call it points to 0-terminated string or
- contains the NULL-string:
- - 0-terminated is returned if the trigger is SUID. The
- string contains user name part of the actual trigger
- definer.
- - NULL-string is returned if the trigger is non-SUID.
- Anyway, the caller is responsible to provide memory for
- storing LEX_STRING object.
- definer_host - [out] after a call it points to 0-terminated string or
- contains the NULL-string:
- - 0-terminated string is returned if the trigger is
- SUID. The string contains host name part of the
- actual trigger definer.
- - NULL-string is returned if the trigger is non-SUID.
- Anyway, the caller is responsible to provide memory for
- storing LEX_STRING object.
+ stmt_query - [OUT] after successful return, this string contains
+ well-formed statement for creation this trigger.
NOTE
- Assumes that trigger name is fully qualified.
@@ -358,14 +323,15 @@ end:
True - error
*/
bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
- LEX_STRING *definer_user,
- LEX_STRING *definer_host)
+ String *stmt_query)
{
LEX *lex= thd->lex;
TABLE *table= tables->table;
char file_buff[FN_REFLEN], trigname_buff[FN_REFLEN];
LEX_STRING file, trigname_file;
LEX_STRING *trg_def, *name;
+ LEX_STRING definer_user;
+ LEX_STRING definer_host;
ulonglong *trg_sql_mode;
char trg_definer_holder[USER_HOST_BUFF_SIZE];
LEX_STRING *trg_definer;
@@ -512,8 +478,6 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
definers_list.push_back(trg_definer, &table->mem_root))
goto err_with_cleanup;
- trg_def->str= thd->query;
- trg_def->length= thd->query_length;
*trg_sql_mode= thd->variables.sql_mode;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
@@ -533,27 +497,54 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
{
/* SUID trigger. */
- *definer_user= lex->definer->user;
- *definer_host= lex->definer->host;
+ definer_user= lex->definer->user;
+ definer_host= lex->definer->host;
trg_definer->str= trg_definer_holder;
- trg_definer->length= strxmov(trg_definer->str, definer_user->str, "@",
- definer_host->str, NullS) - trg_definer->str;
+ trg_definer->length= strxmov(trg_definer->str, definer_user.str, "@",
+ definer_host.str, NullS) - trg_definer->str;
}
else
{
/* non-SUID trigger. */
- definer_user->str= 0;
- definer_user->length= 0;
+ definer_user.str= 0;
+ definer_user.length= 0;
- definer_host->str= 0;
- definer_host->length= 0;
+ definer_host.str= 0;
+ definer_host.length= 0;
trg_definer->str= (char*) "";
trg_definer->length= 0;
}
+ /*
+ Create well-formed trigger definition query. Original query is not
+ appropriated, because definer-clause can be not truncated.
+ */
+
+ stmt_query->append(STRING_WITH_LEN("CREATE "));
+
+ if (trg_definer)
+ {
+ /*
+ Append definer-clause if the trigger is SUID (a usual trigger in
+ new MySQL versions).
+ */
+
+ append_definer(thd, stmt_query, &definer_user, &definer_host);
+ }
+
+ stmt_query->append(thd->lex->stmt_definition_begin,
+ (char *) thd->lex->sphead->m_body_begin -
+ thd->lex->stmt_definition_begin +
+ thd->lex->sphead->m_body.length);
+
+ trg_def->str= stmt_query->c_ptr();
+ trg_def->length= stmt_query->length();
+
+ /* Create trigger definition file. */
+
if (!sql_create_definition_file(NULL, &file, &triggers_file_type,
(gptr)this, triggers_file_parameters, 0))
return 0;
@@ -645,15 +636,19 @@ static bool save_trigger_file(Table_triggers_list *triggers, const char *db,
SYNOPSIS
drop_trigger()
- thd - current thread context (including trigger definition in LEX)
- tables - table list containing one open table for which trigger is
- dropped.
+ thd - current thread context
+ (including trigger definition in LEX)
+ tables - table list containing one open table for which trigger
+ is dropped.
+ stmt_query - [OUT] after successful return, this string contains
+ well-formed statement for creation this trigger.
RETURN VALUE
False - success
True - error
*/
-bool Table_triggers_list::drop_trigger(THD *thd, TABLE_LIST *tables)
+bool Table_triggers_list::drop_trigger(THD *thd, TABLE_LIST *tables,
+ String *stmt_query)
{
LEX *lex= thd->lex;
LEX_STRING *name;
@@ -663,6 +658,8 @@ bool Table_triggers_list::drop_trigger(THD *thd, TABLE_LIST *tables)
List_iterator<LEX_STRING> it_definer(definers_list);
char path[FN_REFLEN];
+ stmt_query->append(thd->query, thd->query_length);
+
while ((name= it_name++))
{
it_def++;
diff --git a/sql/sql_trigger.h b/sql/sql_trigger.h
index 13a919c09ca..b2464745f7c 100644
--- a/sql/sql_trigger.h
+++ b/sql/sql_trigger.h
@@ -92,10 +92,8 @@ public:
}
~Table_triggers_list();
- bool create_trigger(THD *thd, TABLE_LIST *table,
- LEX_STRING *definer_user,
- LEX_STRING *definer_host);
- bool drop_trigger(THD *thd, TABLE_LIST *table);
+ bool create_trigger(THD *thd, TABLE_LIST *table, String *stmt_query);
+ bool drop_trigger(THD *thd, TABLE_LIST *table, String *stmt_query);
bool process_triggers(THD *thd, trg_event_type event,
trg_action_time_type time_type,
bool old_row_is_record1);
diff --git a/sql/sql_view.cc b/sql/sql_view.cc
index 7bbd6c553a0..187d289cb16 100644
--- a/sql/sql_view.cc
+++ b/sql/sql_view.cc
@@ -1070,6 +1070,30 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
if (lex->binlog_row_based_if_mixed)
old_lex->binlog_row_based_if_mixed= TRUE;
#endif
+ bool view_is_mergeable= (table->algorithm != VIEW_ALGORITHM_TMPTABLE &&
+ lex->can_be_merged());
+ TABLE_LIST *view_main_select_tables;
+ if (view_is_mergeable)
+ {
+ /*
+ Currently 'view_main_select_tables' differs from 'view_tables'
+ only then view has CONVERT_TZ() function in its select list.
+ This may change in future, for example if we enable merging of
+ views with subqueries in select list.
+ */
+ view_main_select_tables=
+ (TABLE_LIST*)lex->select_lex.table_list.first;
+
+ /*
+ Let us set proper lock type for tables of the view's main
+ select since we may want to perform update or insert on
+ view. This won't work for view containing union. But this is
+ ok since we don't allow insert and update on such views
+ anyway.
+ */
+ for (tbl= view_main_select_tables; tbl; tbl= tbl->next_local)
+ tbl->lock_type= table->lock_type;
+ }
/*
If we are opening this view as part of implicit LOCK TABLES, then
@@ -1125,43 +1149,26 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
- VIEW SELECT allow merging
- VIEW used in subquery or command support MERGE algorithm
*/
- if (table->algorithm != VIEW_ALGORITHM_TMPTABLE &&
- lex->can_be_merged() &&
+ if (view_is_mergeable &&
(table->select_lex->master_unit() != &old_lex->unit ||
old_lex->can_use_merged()) &&
!old_lex->can_not_use_merged())
{
- List_iterator_fast<TABLE_LIST> ti(view_select->top_join_list);
- /*
- Currently 'view_main_select_tables' differs from 'view_tables'
- only then view has CONVERT_TZ() function in its select list.
- This may change in future, for example if we enable merging
- of views with subqueries in select list.
- */
- TABLE_LIST *view_main_select_tables=
- (TABLE_LIST*)lex->select_lex.table_list.first;
/* lex should contain at least one table */
DBUG_ASSERT(view_main_select_tables != 0);
+ List_iterator_fast<TABLE_LIST> ti(view_select->top_join_list);
+
table->effective_algorithm= VIEW_ALGORITHM_MERGE;
DBUG_PRINT("info", ("algorithm: MERGE"));
table->updatable= (table->updatable_view != 0);
table->effective_with_check=
old_lex->get_effective_with_check(table);
table->merge_underlying_list= view_main_select_tables;
- /*
- Let us set proper lock type for tables of the view's main select
- since we may want to perform update or insert on view. This won't
- work for view containing union. But this is ok since we don't
- allow insert and update on such views anyway.
- Also we fill correct wanted privileges.
- */
- for (tbl= table->merge_underlying_list; tbl; tbl= tbl->next_local)
- {
- tbl->lock_type= table->lock_type;
+ /* Fill correct wanted privileges. */
+ for (tbl= view_main_select_tables; tbl; tbl= tbl->next_local)
tbl->grant.want_privilege= top_view->grant.orig_want_privilege;
- }
/* prepare view context */
lex->select_lex.context.resolve_in_table_list_only(view_main_select_tables);
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 8b60eefe4ea..3c01c608843 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -64,6 +64,34 @@ inline Item *is_truth_value(Item *A, bool v1, bool v2)
new Item_int((char *) (v1 ? "FALSE" : "TRUE"),!v1, 1));
}
+#ifndef DBUG_OFF
+#define YYDEBUG 1
+#else
+#define YYDEBUG 0
+#endif
+
+#ifndef DBUG_OFF
+void turn_parser_debug_on()
+{
+ /*
+ MYSQLdebug is in sql/sql_yacc.cc, in bison generated code.
+ Turning this option on is **VERY** verbose, and should be
+ used when investigating a syntax error problem only.
+
+ The syntax to run with bison traces is as follows :
+ - Starting a server manually :
+ mysqld --debug="d,parser_debug" ...
+ - Running a test :
+ mysql-test-run.pl --mysqld="--debug=d,parser_debug" ...
+
+ The result will be in the process stderr (var/log/master.err)
+ */
+
+ extern int yydebug;
+ yydebug= 1;
+}
+#endif
+
%}
%union {
int num;
@@ -8565,17 +8593,8 @@ flush:
FLUSH_SYM opt_no_write_to_binlog
{
LEX *lex=Lex;
- if (lex->sphead && lex->sphead->m_type != TYPE_ENUM_PROCEDURE)
- {
- /*
- Note that both FLUSH TABLES and FLUSH PRIVILEGES will break
- execution in prelocked mode. So it is better to disable
- FLUSH in stored functions and triggers completely.
- */
- my_error(ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0), "FLUSH");
- YYABORT;
- }
- lex->sql_command= SQLCOM_FLUSH; lex->type=0;
+ lex->sql_command= SQLCOM_FLUSH;
+ lex->type= 0;
lex->no_write_to_binlog= $2;
}
flush_options
@@ -9301,6 +9320,9 @@ user:
$$->user = $1;
$$->host.str= (char *) "%";
$$->host.length= 1;
+
+ if (check_string_length(&$$->user, ER(ER_USERNAME), USERNAME_LENGTH))
+ YYABORT;
}
| ident_or_text '@' ident_or_text
{
@@ -9308,6 +9330,11 @@ user:
if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user))))
YYABORT;
$$->user = $1; $$->host=$3;
+
+ if (check_string_length(&$$->user, ER(ER_USERNAME), USERNAME_LENGTH) ||
+ check_string_length(&$$->host, ER(ER_HOSTNAME),
+ HOSTNAME_LENGTH))
+ YYABORT;
}
| CURRENT_USER optional_braces
{
@@ -10826,15 +10853,9 @@ definer:
*/
YYTHD->lex->definer= 0;
}
- | DEFINER_SYM EQ CURRENT_USER optional_braces
+ | DEFINER_SYM EQ user
{
- if (! (YYTHD->lex->definer= create_default_definer(YYTHD)))
- YYABORT;
- }
- | DEFINER_SYM EQ ident_or_text '@' ident_or_text
- {
- if (!(YYTHD->lex->definer= create_definer(YYTHD, &$3, &$5)))
- YYABORT;
+ YYTHD->lex->definer= get_current_user(YYTHD, $3);
}
;