Merge bodhi.local:/opt/local/work/mysql-5.0-runtime-safemerge
into bodhi.local:/opt/local/work/mysql-5.1-runtime-merge BitKeeper/deleted/.del-im_check_os.inc: Auto merged BitKeeper/deleted/.del-im_options_set.imtest~b53d9d60e5684833: Auto merged BitKeeper/deleted/.del-im_options_set.result~59278f56be61d921: Auto merged BitKeeper/deleted/.del-im_options_unset.imtest~768eb186b51d0048: Auto merged configure.in: Auto merged BitKeeper/deleted/.del-im_options_unset.result~20a4790cd3c70a4f: Auto merged include/mysql_com.h: Auto merged mysql-test/lib/mtr_io.pl: Auto merged mysql-test/r/im_daemon_life_cycle.result: Auto merged mysql-test/r/im_life_cycle.result: Auto merged mysql-test/r/im_utils.result: Auto merged mysql-test/r/sp-error.result: Auto merged mysql-test/r/trigger.result: Auto merged mysql-test/r/type_varchar.result: Auto merged mysql-test/r/view.result: Auto merged mysql-test/t/im_daemon_life_cycle.imtest: Auto merged mysql-test/t/im_life_cycle.imtest: Auto merged mysql-test/t/im_utils.imtest: Auto merged mysql-test/t/sp-error.test: Auto merged mysql-test/t/trigger.test: Auto merged mysql-test/t/type_varchar.test: Auto merged mysql-test/t/view.test: Auto merged sql/item.cc: Auto merged sql/item.h: Auto merged sql/item_cmpfunc.cc: Auto merged sql/item_func.cc: Auto merged sql/item_row.cc: Auto merged sql/item_strfunc.cc: Auto merged sql/item_strfunc.h: Auto merged sql/mysql_priv.h: Auto merged sql/net_serv.cc: Auto merged sql/protocol.cc: Auto merged sql/sp_head.cc: Auto merged sql/sql_acl.cc: Auto merged sql/sql_cache.cc: Auto merged sql/sql_cache.h: Auto merged sql/sql_class.cc: Auto merged sql/sql_error.cc: Auto merged sql/sql_parse.cc: Auto merged sql/sql_trigger.h: Auto merged sql/sql_yacc.yy: Auto merged mysql-test/mysql-test-run.pl: Use local. Alik will merge his changes manually. mysql-test/lib/mtr_process.pl: Use local. mysql-test/r/grant.result: Use local. mysql-test/r/sp.result: Use local. mysql-test/r/ps.result: Manual merge. mysql-test/t/grant.test: Manual merge. mysql-test/t/ps.test: Manual merge. mysql-test/t/sp.test: Manual merge. sql/Makefile.am: Manual merge. sql/field.cc: Manual merge. sql/mysqld.cc: Manual merge. sql/share/errmsg.txt: Manual merge. sql/sp.cc: Manual merge. sql/sp_head.h: Manual merge. sql/sql_trigger.cc: Manual merge. sql/sql_view.cc: Manual merge.
This commit is contained in:
commit
5fab3969af
@ -1 +1,4 @@
|
|||||||
44d03f27qNdqJmARzBoP3Is_cN5e0w
|
44d03f27qNdqJmARzBoP3Is_cN5e0w
|
||||||
|
44ec850ac2k4y2Omgr92GiWPBAVKGQ
|
||||||
|
44edb86b1iE5knJ97MbliK_3lCiAXA
|
||||||
|
44f33f3aj5KW5qweQeekY1LU0E9ZCg
|
||||||
|
@ -213,7 +213,13 @@ typedef struct st_net {
|
|||||||
char last_error[MYSQL_ERRMSG_SIZE], sqlstate[SQLSTATE_LENGTH+1];
|
char last_error[MYSQL_ERRMSG_SIZE], sqlstate[SQLSTATE_LENGTH+1];
|
||||||
unsigned int last_errno;
|
unsigned int last_errno;
|
||||||
unsigned char error;
|
unsigned char error;
|
||||||
|
|
||||||
|
/*
|
||||||
|
'query_cache_query' should be accessed only via query cache
|
||||||
|
functions and methods to maintain proper locking.
|
||||||
|
*/
|
||||||
gptr query_cache_query;
|
gptr query_cache_query;
|
||||||
|
|
||||||
my_bool report_error; /* We should report error (we have unreported error) */
|
my_bool report_error; /* We should report error (we have unreported error) */
|
||||||
my_bool return_errno;
|
my_bool return_errno;
|
||||||
} NET;
|
} NET;
|
||||||
|
@ -2,10 +2,6 @@
|
|||||||
# that ensure that starting conditions (environment) for the IM-test are as
|
# that ensure that starting conditions (environment) for the IM-test are as
|
||||||
# expected.
|
# 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.
|
# Check the running instances.
|
||||||
|
|
||||||
--connect (mysql1_con,localhost,root,,mysql,$IM_MYSQLD1_PORT,$IM_MYSQLD1_SOCK)
|
--connect (mysql1_con,localhost,root,,mysql,$IM_MYSQLD1_PORT,$IM_MYSQLD1_SOCK)
|
||||||
@ -14,6 +10,8 @@
|
|||||||
|
|
||||||
SHOW VARIABLES LIKE 'server_id';
|
SHOW VARIABLES LIKE 'server_id';
|
||||||
|
|
||||||
|
--source include/not_windows.inc
|
||||||
|
|
||||||
--connection default
|
--connection default
|
||||||
|
|
||||||
# Let IM detect that mysqld1 is online. This delay should be longer than
|
# Let IM detect that mysqld1 is online. This delay should be longer than
|
||||||
|
@ -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
|
|
@ -20,13 +20,39 @@ sub mtr_lastlinefromfile($);
|
|||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
sub mtr_get_pid_from_file ($) {
|
sub mtr_get_pid_from_file ($) {
|
||||||
my $file= shift;
|
my $pid_file_path= shift;
|
||||||
|
my $TOTAL_ATTEMPTS= 30;
|
||||||
|
my $timeout= 1;
|
||||||
|
|
||||||
|
# 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\": $!");
|
||||||
|
|
||||||
open(FILE,"<",$file) or mtr_error("can't open file \"$file\": $!");
|
|
||||||
my $pid= <FILE>;
|
my $pid= <FILE>;
|
||||||
chomp($pid);
|
|
||||||
|
chomp($pid) if defined $pid;
|
||||||
|
|
||||||
close FILE;
|
close FILE;
|
||||||
return $pid;
|
|
||||||
|
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 ($) {
|
sub mtr_get_opts_from_file ($) {
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
Success: the process has been started.
|
|
||||||
SHOW VARIABLES LIKE 'server_id';
|
SHOW VARIABLES LIKE 'server_id';
|
||||||
Variable_name Value
|
Variable_name Value
|
||||||
server_id 1
|
server_id 1
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
Success: the process has been started.
|
|
||||||
SHOW VARIABLES LIKE 'server_id';
|
SHOW VARIABLES LIKE 'server_id';
|
||||||
Variable_name Value
|
Variable_name Value
|
||||||
server_id 1
|
server_id 1
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
Success: the process has been started.
|
|
||||||
SHOW VARIABLES LIKE 'server_id';
|
SHOW VARIABLES LIKE 'server_id';
|
||||||
Variable_name Value
|
Variable_name Value
|
||||||
server_id 1
|
server_id 1
|
||||||
|
@ -1277,6 +1277,21 @@ ERROR 3D000: No database selected
|
|||||||
create temporary table t1 (i int);
|
create temporary table t1 (i int);
|
||||||
ERROR 3D000: No database selected
|
ERROR 3D000: No database selected
|
||||||
use test;
|
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;
|
create procedure proc_1() reset query cache;
|
||||||
call proc_1();
|
call proc_1();
|
||||||
call proc_1();
|
call proc_1();
|
||||||
|
@ -634,6 +634,45 @@ flush tables;
|
|||||||
return 5;
|
return 5;
|
||||||
end|
|
end|
|
||||||
ERROR 0A000: FLUSH is not allowed in stored function or trigger
|
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()
|
create procedure bug9529_901234567890123456789012345678901234567890123456789012345()
|
||||||
begin
|
begin
|
||||||
end|
|
end|
|
||||||
|
@ -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
|
t1_bi INSERT t1 set new.a = '2004-01-00' BEFORE # root@localhost
|
||||||
drop table t1;
|
drop table t1;
|
||||||
create table t1 (id int);
|
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;
|
create trigger t1_ai after insert on t1 for each row flush tables;
|
||||||
ERROR 0A000: FLUSH is not allowed in stored function or trigger
|
ERROR 0A000: FLUSH is not allowed in stored function or trigger
|
||||||
create trigger t1_ai after insert on t1 for each row flush privileges;
|
create trigger t1_ai after insert on t1 for each row flush privileges;
|
||||||
ERROR 0A000: FLUSH is not allowed in stored function or trigger
|
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 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);
|
insert into t1 values (0);
|
||||||
ERROR 0A000: FLUSH is not allowed in stored function or trigger
|
ERROR 0A000: FLUSH is not allowed in stored function or trigger
|
||||||
drop procedure p1;
|
drop procedure p1;
|
||||||
@ -639,6 +678,38 @@ create procedure p1() flush privileges;
|
|||||||
insert into t1 values (0);
|
insert into t1 values (0);
|
||||||
ERROR 0A000: FLUSH is not allowed in stored function or trigger
|
ERROR 0A000: FLUSH is not allowed in stored function or trigger
|
||||||
drop procedure p1;
|
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;
|
drop table t1;
|
||||||
create table t1 (id int, data int, username varchar(16));
|
create table t1 (id int, data int, username varchar(16));
|
||||||
insert into t1 (id, data) values (1, 0);
|
insert into t1 (id, data) values (1, 0);
|
||||||
@ -1089,4 +1160,17 @@ begin
|
|||||||
set @a:= 1;
|
set @a:= 1;
|
||||||
end|
|
end|
|
||||||
ERROR HY000: Triggers can not be created on system tables
|
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
|
End of 5.0 tests
|
||||||
|
@ -422,3 +422,34 @@ DROP TABLE IF EXISTS t1;
|
|||||||
CREATE TABLE t1(f1 CHAR(100) DEFAULT 'test');
|
CREATE TABLE t1(f1 CHAR(100) DEFAULT 'test');
|
||||||
INSERT INTO t1 VALUES(SUBSTR(f1, 1, 3));
|
INSERT INTO t1 VALUES(SUBSTR(f1, 1, 3));
|
||||||
DROP TABLE IF EXISTS t1;
|
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;
|
||||||
|
@ -2849,4 +2849,41 @@ SHOW TABLES;
|
|||||||
Tables_in_test
|
Tables_in_test
|
||||||
t1
|
t1
|
||||||
DROP TABLE t1;
|
DROP TABLE t1;
|
||||||
|
DROP TABLE IF EXISTS t1;
|
||||||
DROP VIEW IF EXISTS v1;
|
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.
|
||||||
|
BIN
mysql-test/std_data/14897.frm
Normal file
BIN
mysql-test/std_data/14897.frm
Normal file
Binary file not shown.
@ -812,7 +812,56 @@ DROP USER 'mysqltest_1'@'localhost';
|
|||||||
#
|
#
|
||||||
# Bug #10668: CREATE USER does not enforce username length limit
|
# Bug #10668: CREATE USER does not enforce username length limit
|
||||||
#
|
#
|
||||||
--error ER_CANNOT_USER
|
--error ER_WRONG_STRING_LENGTH
|
||||||
create user mysqltest1_thisisreallytoolong;
|
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
|
--echo End of 5.0 tests
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
#
|
#
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
--source include/im_check_os.inc
|
|
||||||
--source include/im_check_env.inc
|
--source include/im_check_env.inc
|
||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
#
|
#
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
--source include/im_check_os.inc
|
|
||||||
--source include/im_check_env.inc
|
--source include/im_check_env.inc
|
||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
#
|
#
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
--source include/im_check_os.inc
|
|
||||||
--source include/im_check_env.inc
|
--source include/im_check_env.inc
|
||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
@ -1329,9 +1329,37 @@ create temporary table t1 (i int);
|
|||||||
# Restore the old environemnt
|
# Restore the old environemnt
|
||||||
#
|
#
|
||||||
use test;
|
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
|
# Bug #20665: All commands supported in Stored Procedures should work in
|
||||||
# Prepared Statements
|
# Prepared Statements
|
||||||
@ -2200,4 +2228,3 @@ execute abc;
|
|||||||
drop table if exists t1, t2;
|
drop table if exists t1, t2;
|
||||||
execute abc;
|
execute abc;
|
||||||
deallocate prepare abc;
|
deallocate prepare abc;
|
||||||
|
|
||||||
|
@ -899,6 +899,45 @@ begin
|
|||||||
flush tables;
|
flush tables;
|
||||||
return 5;
|
return 5;
|
||||||
end|
|
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|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -6145,30 +6145,103 @@ CALL bug16676_p2('a', @v2, @v3)|
|
|||||||
use test|
|
use test|
|
||||||
|
|
||||||
DROP DATABASE mysqltest1|
|
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
|
# BUG#8153: Stored procedure with subquery and continue handler, wrong result
|
||||||
# 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.
|
|
||||||
--disable_warnings
|
--disable_warnings
|
||||||
drop table if exists t3|
|
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
|
--enable_warnings
|
||||||
|
|
||||||
create table t3 (a int)|
|
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|
|
## Testing the use case reported in Bug#8153
|
||||||
use mysqltest1|
|
|
||||||
drop database mysqltest1|
|
|
||||||
|
|
||||||
# No current database
|
create procedure bug8153_subselect()
|
||||||
select database()|
|
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|
|
call bug8153_subselect()|
|
||||||
use test|
|
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 t3|
|
||||||
|
drop table t4|
|
||||||
|
|
||||||
#
|
#
|
||||||
# BUG#19862: Sort with filesort by function evaluates function twice
|
# BUG#19862: Sort with filesort by function evaluates function twice
|
||||||
@ -6189,6 +6262,39 @@ SELECT * FROM t11|
|
|||||||
DROP TABLE t11, t12|
|
DROP TABLE t11, t12|
|
||||||
DROP FUNCTION bug19862|
|
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
|
--echo End of 5.0 tests
|
||||||
#
|
#
|
||||||
# BUG#NNNN: New bug synopsis
|
# BUG#NNNN: New bug synopsis
|
||||||
|
@ -651,17 +651,105 @@ drop table t1;
|
|||||||
# of functions and triggers.
|
# of functions and triggers.
|
||||||
create table t1 (id int);
|
create table t1 (id int);
|
||||||
--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
|
--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;
|
create trigger t1_ai after insert on t1 for each row flush tables;
|
||||||
--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
|
--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
|
||||||
create trigger t1_ai after insert on t1 for each row flush privileges;
|
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 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
|
--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
|
||||||
insert into t1 values (0);
|
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;
|
drop procedure p1;
|
||||||
create procedure p1() flush privileges;
|
create procedure p1() flush privileges;
|
||||||
--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
|
--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
|
||||||
insert into t1 values (0);
|
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 procedure p1;
|
||||||
drop table t1;
|
drop table t1;
|
||||||
|
|
||||||
@ -1301,6 +1389,36 @@ create trigger wont_work after update on event for each row
|
|||||||
begin
|
begin
|
||||||
set @a:= 1;
|
set @a:= 1;
|
||||||
end|
|
end|
|
||||||
|
use test|
|
||||||
delimiter ;|
|
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
|
--echo End of 5.0 tests
|
||||||
|
@ -146,3 +146,44 @@ DROP TABLE IF EXISTS t1;
|
|||||||
CREATE TABLE t1(f1 CHAR(100) DEFAULT 'test');
|
CREATE TABLE t1(f1 CHAR(100) DEFAULT 'test');
|
||||||
INSERT INTO t1 VALUES(SUBSTR(f1, 1, 3));
|
INSERT INTO t1 VALUES(SUBSTR(f1, 1, 3));
|
||||||
DROP TABLE IF EXISTS t1;
|
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;
|
||||||
|
@ -2720,6 +2720,80 @@ DROP VIEW t1,v1;
|
|||||||
SHOW TABLES;
|
SHOW TABLES;
|
||||||
|
|
||||||
DROP TABLE t1;
|
DROP TABLE t1;
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Test for BUG#16899: Possible buffer overflow in handling of DEFINER-clause.
|
||||||
|
#
|
||||||
|
|
||||||
|
# Prepare.
|
||||||
|
|
||||||
--disable_warnings
|
--disable_warnings
|
||||||
|
DROP TABLE IF EXISTS t1;
|
||||||
DROP VIEW IF EXISTS v1;
|
DROP VIEW IF EXISTS v1;
|
||||||
|
DROP VIEW IF EXISTS v2;
|
||||||
--enable_warnings
|
--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.
|
||||||
|
@ -122,8 +122,8 @@ DEFS = -DMYSQL_SERVER \
|
|||||||
BUILT_SOURCES = sql_yacc.cc sql_yacc.h lex_hash.h
|
BUILT_SOURCES = sql_yacc.cc sql_yacc.h lex_hash.h
|
||||||
EXTRA_DIST = udf_example.c $(BUILT_SOURCES) \
|
EXTRA_DIST = udf_example.c $(BUILT_SOURCES) \
|
||||||
nt_servc.cc nt_servc.h message.mc CMakeLists.txt
|
nt_servc.cc nt_servc.h message.mc CMakeLists.txt
|
||||||
CLEANFILES = lex_hash.h sql_yacc.cc sql_yacc.h
|
CLEANFILES = lex_hash.h sql_yacc.cc sql_yacc.h sql_yacc.output
|
||||||
AM_YFLAGS = -d
|
AM_YFLAGS = -d --debug --verbose
|
||||||
|
|
||||||
mysql_tzinfo_to_sql.cc:
|
mysql_tzinfo_to_sql.cc:
|
||||||
rm -f mysql_tzinfo_to_sql.cc
|
rm -f mysql_tzinfo_to_sql.cc
|
||||||
|
16
sql/field.cc
16
sql/field.cc
@ -6302,16 +6302,24 @@ Field *Field_string::new_field(MEM_ROOT *root, struct st_table *new_table,
|
|||||||
{
|
{
|
||||||
Field *field;
|
Field *field;
|
||||||
if (type() != MYSQL_TYPE_VAR_STRING || keep_type)
|
if (type() != MYSQL_TYPE_VAR_STRING || keep_type)
|
||||||
return Field::new_field(root, new_table, keep_type);
|
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
|
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
|
This is done to ensure that ALTER TABLE will convert old VARCHAR fields
|
||||||
to now VARCHAR fields.
|
to now VARCHAR fields.
|
||||||
*/
|
*/
|
||||||
if ((field= new Field_varstring(field_length, maybe_null(), field_name,
|
|
||||||
new_table->s, charset())))
|
|
||||||
field->init(new_table);
|
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;
|
return field;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
72
sql/item.cc
72
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,
|
Item_ident::Item_ident(Name_resolution_context *context_arg,
|
||||||
const char *db_name_arg,const char *table_name_arg,
|
const char *db_name_arg,const char *table_name_arg,
|
||||||
const char *field_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
|
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)
|
if (field->charset() != &my_charset_bin)
|
||||||
no_const_subst=1;
|
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
|
bool Item_insert_value::eq(const Item *item, bool binary_cmp) const
|
||||||
{
|
{
|
||||||
return item->type() == INSERT_VALUE_ITEM &&
|
return item->type() == INSERT_VALUE_ITEM &&
|
||||||
|
22
sql/item.h
22
sql/item.h
@ -774,10 +774,7 @@ public:
|
|||||||
return (this->*processor)(arg);
|
return (this->*processor)(arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Item* transform(Item_transformer transformer, byte *arg)
|
virtual Item* transform(Item_transformer transformer, byte *arg);
|
||||||
{
|
|
||||||
return (this->*transformer)(arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void traverse_cond(Cond_traverser traverser,
|
virtual void traverse_cond(Cond_traverser traverser,
|
||||||
void *arg, traverse_order order)
|
void *arg, traverse_order order)
|
||||||
@ -818,7 +815,7 @@ public:
|
|||||||
{ *(bool *)bool_arg= FALSE; return 0; }
|
{ *(bool *)bool_arg= FALSE; return 0; }
|
||||||
|
|
||||||
virtual Item *equal_fields_propagator(byte * arg) { return this; }
|
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; }
|
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 *find_item_equal(COND_EQUAL *cond_equal);
|
||||||
Item *equal_fields_propagator(byte *arg);
|
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);
|
Item *replace_equal_field(byte *arg);
|
||||||
inline uint32 max_disp_length() { return field->max_length(); }
|
inline uint32 max_disp_length() { return field->max_length(); }
|
||||||
Item_field *filed_for_view_update() { return this; }
|
Item_field *filed_for_view_update() { return this; }
|
||||||
@ -2197,18 +2194,7 @@ public:
|
|||||||
(this->*processor)(args);
|
(this->*processor)(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
Item *transform(Item_transformer transformer, byte *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);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -515,8 +515,8 @@ int Arg_comparator::set_compare_func(Item_bool_func2 *item, Item_result type)
|
|||||||
which would be transformed to:
|
which would be transformed to:
|
||||||
WHERE col= 'j'
|
WHERE col= 'j'
|
||||||
*/
|
*/
|
||||||
(*a)->transform(&Item::set_no_const_sub, (byte*) 0);
|
(*a)->walk(&Item::set_no_const_sub, (byte*) 0);
|
||||||
(*b)->transform(&Item::set_no_const_sub, (byte*) 0);
|
(*b)->walk(&Item::set_no_const_sub, (byte*) 0);
|
||||||
}
|
}
|
||||||
break;
|
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)
|
Item *Item_cond::transform(Item_transformer transformer, byte *arg)
|
||||||
{
|
{
|
||||||
|
DBUG_ASSERT(!current_thd->is_stmt_prepare());
|
||||||
|
|
||||||
List_iterator<Item> li(list);
|
List_iterator<Item> li(list);
|
||||||
Item *item;
|
Item *item;
|
||||||
while ((item= li++))
|
while ((item= li++))
|
||||||
@ -2775,8 +2777,15 @@ Item *Item_cond::transform(Item_transformer transformer, byte *arg)
|
|||||||
Item *new_item= item->transform(transformer, arg);
|
Item *new_item= item->transform(transformer, arg);
|
||||||
if (!new_item)
|
if (!new_item)
|
||||||
return 0;
|
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)
|
if (new_item != item)
|
||||||
li.replace(new_item);
|
current_thd->change_item_tree(li.ref(), new_item);
|
||||||
}
|
}
|
||||||
return Item_func::transform(transformer, arg);
|
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)
|
Item *Item_equal::transform(Item_transformer transformer, byte *arg)
|
||||||
{
|
{
|
||||||
|
DBUG_ASSERT(!current_thd->is_stmt_prepare());
|
||||||
|
|
||||||
List_iterator<Item_field> it(fields);
|
List_iterator<Item_field> it(fields);
|
||||||
Item *item;
|
Item *item;
|
||||||
while ((item= it++))
|
while ((item= it++))
|
||||||
@ -4030,8 +4041,15 @@ Item *Item_equal::transform(Item_transformer transformer, byte *arg)
|
|||||||
Item *new_item= item->transform(transformer, arg);
|
Item *new_item= item->transform(transformer, arg);
|
||||||
if (!new_item)
|
if (!new_item)
|
||||||
return 0;
|
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)
|
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);
|
return Item_func::transform(transformer, arg);
|
||||||
}
|
}
|
||||||
|
@ -260,6 +260,8 @@ void Item_func::traverse_cond(Cond_traverser traverser,
|
|||||||
|
|
||||||
Item *Item_func::transform(Item_transformer transformer, byte *argument)
|
Item *Item_func::transform(Item_transformer transformer, byte *argument)
|
||||||
{
|
{
|
||||||
|
DBUG_ASSERT(!current_thd->is_stmt_prepare());
|
||||||
|
|
||||||
if (arg_count)
|
if (arg_count)
|
||||||
{
|
{
|
||||||
Item **arg,**arg_end;
|
Item **arg,**arg_end;
|
||||||
@ -268,6 +270,13 @@ Item *Item_func::transform(Item_transformer transformer, byte *argument)
|
|||||||
Item *new_item= (*arg)->transform(transformer, argument);
|
Item *new_item= (*arg)->transform(transformer, argument);
|
||||||
if (!new_item)
|
if (!new_item)
|
||||||
return 0;
|
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)
|
if (*arg != new_item)
|
||||||
current_thd->change_item_tree(arg, new_item);
|
current_thd->change_item_tree(arg, new_item);
|
||||||
}
|
}
|
||||||
|
@ -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)
|
Item *Item_row::transform(Item_transformer transformer, byte *arg)
|
||||||
{
|
{
|
||||||
|
DBUG_ASSERT(!current_thd->is_stmt_prepare());
|
||||||
|
|
||||||
for (uint i= 0; i < arg_count; i++)
|
for (uint i= 0; i < arg_count; i++)
|
||||||
{
|
{
|
||||||
Item *new_item= items[i]->transform(transformer, arg);
|
Item *new_item= items[i]->transform(transformer, arg);
|
||||||
if (!new_item)
|
if (!new_item)
|
||||||
return 0;
|
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);
|
return (this->*transformer)(arg);
|
||||||
}
|
}
|
||||||
|
@ -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)
|
void Item_func_make_set::print(String *str)
|
||||||
{
|
{
|
||||||
str->append(STRING_WITH_LEN("make_set("));
|
str->append(STRING_WITH_LEN("make_set("));
|
||||||
|
@ -484,14 +484,7 @@ public:
|
|||||||
return item->walk(processor, walk_subquery, arg) ||
|
return item->walk(processor, walk_subquery, arg) ||
|
||||||
Item_str_func::walk(processor, walk_subquery, arg);
|
Item_str_func::walk(processor, walk_subquery, arg);
|
||||||
}
|
}
|
||||||
Item *transform(Item_transformer transformer, byte *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);
|
|
||||||
}
|
|
||||||
void print(String *str);
|
void print(String *str);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -575,6 +575,7 @@ void get_default_definer(THD *thd, LEX_USER *definer);
|
|||||||
LEX_USER *create_default_definer(THD *thd);
|
LEX_USER *create_default_definer(THD *thd);
|
||||||
LEX_USER *create_definer(THD *thd, LEX_STRING *user_name, LEX_STRING *host_name);
|
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 *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 {
|
enum enum_mysql_completiontype {
|
||||||
ROLLBACK_RELEASE=-2, ROLLBACK=1, ROLLBACK_AND_CHAIN=7,
|
ROLLBACK_RELEASE=-2, ROLLBACK=1, ROLLBACK_AND_CHAIN=7,
|
||||||
@ -1876,6 +1877,9 @@ void free_list(I_List <i_string> *list);
|
|||||||
|
|
||||||
/* sql_yacc.cc */
|
/* sql_yacc.cc */
|
||||||
extern int MYSQLparse(void *thd);
|
extern int MYSQLparse(void *thd);
|
||||||
|
#ifndef DBUG_OFF
|
||||||
|
extern void turn_parser_debug_on();
|
||||||
|
#endif
|
||||||
|
|
||||||
/* frm_crypt.cc */
|
/* frm_crypt.cc */
|
||||||
#ifdef HAVE_CRYPTED_FRM
|
#ifdef HAVE_CRYPTED_FRM
|
||||||
|
@ -2432,10 +2432,8 @@ static int my_message_sql(uint error, const char *str, myf MyFlags)
|
|||||||
if ((thd= current_thd))
|
if ((thd= current_thd))
|
||||||
{
|
{
|
||||||
if (thd->spcont &&
|
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);
|
DBUG_RETURN(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,8 +95,11 @@ extern uint test_flags;
|
|||||||
extern ulong bytes_sent, bytes_received, net_big_packet_count;
|
extern ulong bytes_sent, bytes_received, net_big_packet_count;
|
||||||
extern pthread_mutex_t LOCK_bytes_sent , LOCK_bytes_received;
|
extern pthread_mutex_t LOCK_bytes_sent , LOCK_bytes_received;
|
||||||
#ifndef MYSQL_INSTANCE_MANAGER
|
#ifndef MYSQL_INSTANCE_MANAGER
|
||||||
extern void query_cache_insert(NET *net, const char *packet, ulong length);
|
#ifdef HAVE_QUERY_CACHE
|
||||||
#define USE_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
|
#define update_statistics(A) A
|
||||||
#endif /* MYSQL_INSTANCE_MANGER */
|
#endif /* MYSQL_INSTANCE_MANGER */
|
||||||
#endif /* defined(MYSQL_SERVER) && !defined(MYSQL_INSTANCE_MANAGER) */
|
#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->compress=0; net->reading_or_writing=0;
|
||||||
net->where_b = net->remain_in_buf=0;
|
net->where_b = net->remain_in_buf=0;
|
||||||
net->last_errno=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;
|
net->report_error= 0;
|
||||||
|
|
||||||
if (vio != 0) /* If real connection */
|
if (vio != 0) /* If real connection */
|
||||||
@ -551,9 +558,7 @@ net_real_write(NET *net,const char *packet,ulong len)
|
|||||||
my_bool net_blocking = vio_is_blocking(net->vio);
|
my_bool net_blocking = vio_is_blocking(net->vio);
|
||||||
DBUG_ENTER("net_real_write");
|
DBUG_ENTER("net_real_write");
|
||||||
|
|
||||||
#if defined(MYSQL_SERVER) && defined(HAVE_QUERY_CACHE) \
|
#if defined(MYSQL_SERVER) && defined(USE_QUERY_CACHE)
|
||||||
&& !defined(MYSQL_INSTANCE_MANAGER)
|
|
||||||
if (net->query_cache_query != 0)
|
|
||||||
query_cache_insert(net, packet, len);
|
query_cache_insert(net, packet, len);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -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)
|
void net_send_error(THD *thd, uint sql_errno, const char *err)
|
||||||
{
|
{
|
||||||
NET *net= &thd->net;
|
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] ?
|
err ? err : net->last_error[0] ?
|
||||||
net->last_error : "NULL"));
|
net->last_error : "NULL"));
|
||||||
|
|
||||||
|
DBUG_ASSERT(!thd->spcont);
|
||||||
|
|
||||||
if (net && net->no_send_error)
|
if (net && net->no_send_error)
|
||||||
{
|
{
|
||||||
thd->clear_error();
|
thd->clear_error();
|
||||||
DBUG_PRINT("info", ("sending error messages prohibited"));
|
DBUG_PRINT("info", ("sending error messages prohibited"));
|
||||||
DBUG_VOID_RETURN;
|
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
|
thd->query_error= 1; // needed to catch query errors during replication
|
||||||
if (!err)
|
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
|
Write error package and flush to client
|
||||||
It's a little too low level, but I don't want to use another buffer for
|
It's a little too low level, but I don't want to use another buffer for
|
||||||
this
|
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
|
void
|
||||||
@ -136,6 +151,8 @@ net_printf_error(THD *thd, uint errcode, ...)
|
|||||||
DBUG_ENTER("net_printf_error");
|
DBUG_ENTER("net_printf_error");
|
||||||
DBUG_PRINT("enter",("message: %u",errcode));
|
DBUG_PRINT("enter",("message: %u",errcode));
|
||||||
|
|
||||||
|
DBUG_ASSERT(!thd->spcont);
|
||||||
|
|
||||||
if (net && net->no_send_error)
|
if (net && net->no_send_error)
|
||||||
{
|
{
|
||||||
thd->clear_error();
|
thd->clear_error();
|
||||||
@ -143,13 +160,6 @@ net_printf_error(THD *thd, uint errcode, ...)
|
|||||||
DBUG_VOID_RETURN;
|
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
|
thd->query_error= 1; // needed to catch query errors during replication
|
||||||
#ifndef EMBEDDED_LIBRARY
|
#ifndef EMBEDDED_LIBRARY
|
||||||
query_cache_abort(net); // Safety
|
query_cache_abort(net); // Safety
|
||||||
|
@ -5954,3 +5954,9 @@ ER_BAD_LOG_ENGINE
|
|||||||
eng "One can use only CSV and MyISAM engines for the log tables"
|
eng "One can use only CSV and MyISAM engines for the log tables"
|
||||||
ER_CANT_DROP_LOG_TABLE
|
ER_CANT_DROP_LOG_TABLE
|
||||||
eng "Cannot drop log table if log is enabled"
|
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)"
|
||||||
|
11
sql/sp.cc
11
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);
|
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;
|
level= sp->m_last_cached_sp->m_recursion_level + 1;
|
||||||
if (level > depth)
|
if (level > depth)
|
||||||
@ -1182,10 +1187,16 @@ sp_show_create_procedure(THD *thd, sp_name *name)
|
|||||||
DBUG_ENTER("sp_show_create_procedure");
|
DBUG_ENTER("sp_show_create_procedure");
|
||||||
DBUG_PRINT("enter", ("name: %.*s", name->m_name.length, name->m_name.str));
|
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,
|
if ((sp= sp_find_routine(thd, TYPE_ENUM_PROCEDURE, name,
|
||||||
&thd->sp_proc_cache, FALSE)))
|
&thd->sp_proc_cache, FALSE)))
|
||||||
ret= sp->show_create_procedure(thd);
|
ret= sp->show_create_procedure(thd);
|
||||||
|
|
||||||
|
thd->variables.max_sp_recursion_depth--;
|
||||||
DBUG_RETURN(ret);
|
DBUG_RETURN(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,6 +235,12 @@ sp_get_flags_for_command(LEX *lex)
|
|||||||
else
|
else
|
||||||
flags= sp_head::HAS_COMMIT_OR_ROLLBACK;
|
flags= sp_head::HAS_COMMIT_OR_ROLLBACK;
|
||||||
break;
|
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_INDEX:
|
||||||
case SQLCOM_CREATE_DB:
|
case SQLCOM_CREATE_DB:
|
||||||
case SQLCOM_CREATE_VIEW:
|
case SQLCOM_CREATE_VIEW:
|
||||||
|
@ -119,6 +119,8 @@ public:
|
|||||||
LOG_SLOW_STATEMENTS= 256, // Used by events
|
LOG_SLOW_STATEMENTS= 256, // Used by events
|
||||||
LOG_GENERAL_LOG= 512, // Used by events
|
LOG_GENERAL_LOG= 512, // Used by events
|
||||||
BINLOG_ROW_BASED_IF_MIXED= 1024
|
BINLOG_ROW_BASED_IF_MIXED= 1024
|
||||||
|
HAS_SQLCOM_RESET= 2048,
|
||||||
|
HAS_SQLCOM_FLUSH= 4096
|
||||||
};
|
};
|
||||||
|
|
||||||
/* TYPE_ENUM_FUNCTION, TYPE_ENUM_PROCEDURE or TYPE_ENUM_TRIGGER */
|
/* TYPE_ENUM_FUNCTION, TYPE_ENUM_PROCEDURE or TYPE_ENUM_TRIGGER */
|
||||||
@ -337,14 +339,16 @@ public:
|
|||||||
my_error(ER_SP_NO_RETSET, MYF(0), where);
|
my_error(ER_SP_NO_RETSET, MYF(0), where);
|
||||||
else if (m_flags & HAS_SET_AUTOCOMMIT_STMT)
|
else if (m_flags & HAS_SET_AUTOCOMMIT_STMT)
|
||||||
my_error(ER_SP_CANT_SET_AUTOCOMMIT, MYF(0));
|
my_error(ER_SP_CANT_SET_AUTOCOMMIT, MYF(0));
|
||||||
else if (m_type != TYPE_ENUM_PROCEDURE &&
|
else if (m_flags & HAS_COMMIT_OR_ROLLBACK)
|
||||||
(m_flags & sp_head::HAS_COMMIT_OR_ROLLBACK))
|
|
||||||
{
|
|
||||||
my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0));
|
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 &
|
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
|
#ifndef DBUG_OFF
|
||||||
|
@ -260,6 +260,65 @@ sp_rcontext::find_handler(uint sql_errno,
|
|||||||
return TRUE;
|
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
|
void
|
||||||
sp_rcontext::push_cursor(sp_lex_keeper *lex_keeper, sp_instr_cpush *i)
|
sp_rcontext::push_cursor(sp_lex_keeper *lex_keeper, sp_instr_cpush *i)
|
||||||
|
@ -128,6 +128,12 @@ class sp_rcontext : public Sql_alloc
|
|||||||
bool
|
bool
|
||||||
find_handler(uint sql_errno,MYSQL_ERROR::enum_warning_level level);
|
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
|
// Returns handler type and sets *ip to location if one was found
|
||||||
inline int
|
inline int
|
||||||
found_handler(uint *ip, uint *fp)
|
found_handler(uint *ip, uint *fp)
|
||||||
|
@ -3041,14 +3041,6 @@ bool mysql_table_grant(THD *thd, TABLE_LIST *table_list,
|
|||||||
result= TRUE;
|
result= TRUE;
|
||||||
continue;
|
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 */
|
/* Create user if needed */
|
||||||
error=replace_user_table(thd, tables[0].table, *Str,
|
error=replace_user_table(thd, tables[0].table, *Str,
|
||||||
0, revoke_grant, create_new_users,
|
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;
|
result= TRUE;
|
||||||
continue;
|
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 */
|
/* Create user if needed */
|
||||||
error=replace_user_table(thd, tables[0].table, *Str,
|
error=replace_user_table(thd, tables[0].table, *Str,
|
||||||
0, revoke_grant, create_new_users,
|
0, revoke_grant, create_new_users,
|
||||||
@ -3387,14 +3370,6 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
|
|||||||
result= TRUE;
|
result= TRUE;
|
||||||
continue;
|
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,
|
if (replace_user_table(thd, tables[0].table, *Str,
|
||||||
(!db ? rights : 0), revoke_grant, create_new_users,
|
(!db ? rights : 0), revoke_grant, create_new_users,
|
||||||
test(thd->variables.sql_mode &
|
test(thd->variables.sql_mode &
|
||||||
@ -4304,14 +4279,6 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user)
|
|||||||
DBUG_RETURN(TRUE);
|
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);
|
rw_rdlock(&LOCK_grant);
|
||||||
VOID(pthread_mutex_lock(&acl_cache->lock));
|
VOID(pthread_mutex_lock(&acl_cache->lock));
|
||||||
|
|
||||||
|
388
sql/sql_cache.cc
388
sql/sql_cache.cc
@ -560,22 +560,63 @@ byte *query_cache_query_get_key(const byte *record, uint *length,
|
|||||||
Functions to store things into the query cache
|
Functions to store things into the query cache
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
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.
|
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)
|
void query_cache_insert(NET *net, const char *packet, ulong length)
|
||||||
{
|
{
|
||||||
DBUG_ENTER("query_cache_insert");
|
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);
|
STRUCT_LOCK(&query_cache.structure_guard_mutex);
|
||||||
/*
|
|
||||||
It is very unlikely that following condition is TRUE (it is possible
|
if (unlikely(query_cache.query_cache_size == 0 ||
|
||||||
only if other thread is resizing cache), so we check it only after guard
|
query_cache.flush_in_progress))
|
||||||
mutex lock
|
|
||||||
*/
|
|
||||||
if (unlikely(query_cache.query_cache_size == 0))
|
|
||||||
{
|
{
|
||||||
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
|
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
|
||||||
DBUG_VOID_RETURN;
|
DBUG_VOID_RETURN;
|
||||||
@ -612,10 +653,10 @@ void query_cache_insert(NET *net, const char *packet, ulong length)
|
|||||||
header->result(result);
|
header->result(result);
|
||||||
header->last_pkt_nr= net->pkt_nr;
|
header->last_pkt_nr= net->pkt_nr;
|
||||||
BLOCK_UNLOCK_WR(query_block);
|
BLOCK_UNLOCK_WR(query_block);
|
||||||
|
DBUG_EXECUTE("check_querycache",query_cache.check_integrity(0););
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
|
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
|
||||||
DBUG_EXECUTE("check_querycache",query_cache.check_integrity(0););
|
|
||||||
DBUG_VOID_RETURN;
|
DBUG_VOID_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -624,21 +665,20 @@ void query_cache_abort(NET *net)
|
|||||||
{
|
{
|
||||||
DBUG_ENTER("query_cache_abort");
|
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);
|
STRUCT_LOCK(&query_cache.structure_guard_mutex);
|
||||||
/*
|
|
||||||
It is very unlikely that following condition is TRUE (it is possible
|
if (unlikely(query_cache.query_cache_size == 0 ||
|
||||||
only if other thread is resizing cache), so we check it only after guard
|
query_cache.flush_in_progress))
|
||||||
mutex lock
|
|
||||||
*/
|
|
||||||
if (unlikely(query_cache.query_cache_size == 0))
|
|
||||||
{
|
{
|
||||||
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
|
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
|
||||||
DBUG_VOID_RETURN;
|
DBUG_VOID_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
Query_cache_block *query_block = ((Query_cache_block*)
|
Query_cache_block *query_block= ((Query_cache_block*)
|
||||||
net->query_cache_query);
|
net->query_cache_query);
|
||||||
if (query_block) // Test if changed by other thread
|
if (query_block) // Test if changed by other thread
|
||||||
{
|
{
|
||||||
@ -646,11 +686,12 @@ void query_cache_abort(NET *net)
|
|||||||
BLOCK_LOCK_WR(query_block);
|
BLOCK_LOCK_WR(query_block);
|
||||||
// The following call will remove the lock on query_block
|
// The following call will remove the lock on query_block
|
||||||
query_cache.free_query(query_block);
|
query_cache.free_query(query_block);
|
||||||
}
|
net->query_cache_query= 0;
|
||||||
net->query_cache_query=0;
|
|
||||||
DBUG_EXECUTE("check_querycache",query_cache.check_integrity(1););
|
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;
|
DBUG_VOID_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -659,37 +700,36 @@ void query_cache_end_of_result(THD *thd)
|
|||||||
{
|
{
|
||||||
DBUG_ENTER("query_cache_end_of_result");
|
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
|
#ifdef EMBEDDED_LIBRARY
|
||||||
query_cache_insert(&thd->net, (char*)thd,
|
query_cache_insert(&thd->net, (char*)thd,
|
||||||
emb_count_querycache_size(thd));
|
emb_count_querycache_size(thd));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
STRUCT_LOCK(&query_cache.structure_guard_mutex);
|
STRUCT_LOCK(&query_cache.structure_guard_mutex);
|
||||||
/*
|
|
||||||
It is very unlikely that following condition is TRUE (it is possible
|
if (unlikely(query_cache.query_cache_size == 0 ||
|
||||||
only if other thread is resizing cache), so we check it only after guard
|
query_cache.flush_in_progress))
|
||||||
mutex lock
|
|
||||||
*/
|
|
||||||
if (unlikely(query_cache.query_cache_size == 0))
|
|
||||||
{
|
{
|
||||||
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
|
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
|
||||||
DBUG_VOID_RETURN;
|
DBUG_VOID_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
Query_cache_block *query_block = ((Query_cache_block*)
|
Query_cache_block *query_block= ((Query_cache_block*)
|
||||||
thd->net.query_cache_query);
|
thd->net.query_cache_query);
|
||||||
if (query_block)
|
if (query_block)
|
||||||
{
|
{
|
||||||
DUMP(&query_cache);
|
DUMP(&query_cache);
|
||||||
BLOCK_LOCK_WR(query_block);
|
BLOCK_LOCK_WR(query_block);
|
||||||
Query_cache_query *header = query_block->query();
|
Query_cache_query *header= query_block->query();
|
||||||
Query_cache_block *last_result_block = header->result()->prev;
|
Query_cache_block *last_result_block= header->result()->prev;
|
||||||
ulong allign_size = ALIGN_SIZE(last_result_block->used);
|
ulong allign_size= ALIGN_SIZE(last_result_block->used);
|
||||||
ulong len = max(query_cache.min_allocation_unit, allign_size);
|
ulong len= max(query_cache.min_allocation_unit, allign_size);
|
||||||
if (last_result_block->length >= query_cache.min_allocation_unit + len)
|
if (last_result_block->length >= query_cache.min_allocation_unit + len)
|
||||||
query_cache.split_block(last_result_block,len);
|
query_cache.split_block(last_result_block,len);
|
||||||
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
|
|
||||||
|
|
||||||
#ifndef DBUG_OFF
|
#ifndef DBUG_OFF
|
||||||
if (header->result() == 0)
|
if (header->result() == 0)
|
||||||
@ -697,12 +737,20 @@ void query_cache_end_of_result(THD *thd)
|
|||||||
DBUG_PRINT("error", ("end of data whith no result. query '%s'",
|
DBUG_PRINT("error", ("end of data whith no result. query '%s'",
|
||||||
header->query()));
|
header->query()));
|
||||||
query_cache.wreck(__LINE__, "");
|
query_cache.wreck(__LINE__, "");
|
||||||
|
|
||||||
|
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
|
||||||
|
|
||||||
DBUG_VOID_RETURN;
|
DBUG_VOID_RETURN;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
header->found_rows(current_thd->limit_found_rows);
|
header->found_rows(current_thd->limit_found_rows);
|
||||||
header->result()->type = Query_cache_block::RESULT;
|
header->result()->type= Query_cache_block::RESULT;
|
||||||
header->writer(0);
|
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);
|
BLOCK_UNLOCK_WR(query_block);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -710,9 +758,7 @@ void query_cache_end_of_result(THD *thd)
|
|||||||
// Cache was flushed or resized and query was deleted => do nothing
|
// Cache was flushed or resized and query was deleted => do nothing
|
||||||
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
|
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
|
||||||
}
|
}
|
||||||
thd->net.query_cache_query=0;
|
|
||||||
DBUG_EXECUTE("check_querycache",query_cache.check_integrity(0););
|
|
||||||
}
|
|
||||||
DBUG_VOID_RETURN;
|
DBUG_VOID_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -758,7 +804,6 @@ ulong Query_cache::resize(ulong query_cache_size_arg)
|
|||||||
query_cache_size_arg));
|
query_cache_size_arg));
|
||||||
DBUG_ASSERT(initialized);
|
DBUG_ASSERT(initialized);
|
||||||
STRUCT_LOCK(&structure_guard_mutex);
|
STRUCT_LOCK(&structure_guard_mutex);
|
||||||
if (query_cache_size > 0)
|
|
||||||
free_cache();
|
free_cache();
|
||||||
query_cache_size= query_cache_size_arg;
|
query_cache_size= query_cache_size_arg;
|
||||||
::query_cache_size= init_cache();
|
::query_cache_size= init_cache();
|
||||||
@ -780,7 +825,15 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used)
|
|||||||
TABLE_COUNTER_TYPE local_tables;
|
TABLE_COUNTER_TYPE local_tables;
|
||||||
ulong tot_length;
|
ulong tot_length;
|
||||||
DBUG_ENTER("Query_cache::store_query");
|
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;
|
DBUG_VOID_RETURN;
|
||||||
uint8 tables_type= 0;
|
uint8 tables_type= 0;
|
||||||
|
|
||||||
@ -832,9 +885,9 @@ sql mode: 0x%lx, sort len: %lu, conncat len: %lu",
|
|||||||
acquiring the query cache mutex.
|
acquiring the query cache mutex.
|
||||||
*/
|
*/
|
||||||
ha_release_temporary_latches(thd);
|
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);
|
STRUCT_UNLOCK(&structure_guard_mutex);
|
||||||
DBUG_VOID_RETURN;
|
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);
|
double_linked_list_simple_include(query_block, &queries_blocks);
|
||||||
inserts++;
|
inserts++;
|
||||||
queries_in_cache++;
|
queries_in_cache++;
|
||||||
STRUCT_UNLOCK(&structure_guard_mutex);
|
|
||||||
|
|
||||||
net->query_cache_query= (gptr) query_block;
|
net->query_cache_query= (gptr) query_block;
|
||||||
header->writer(net);
|
header->writer(net);
|
||||||
header->tables_type(tables_type);
|
header->tables_type(tables_type);
|
||||||
|
|
||||||
|
STRUCT_UNLOCK(&structure_guard_mutex);
|
||||||
|
|
||||||
// init_n_lock make query block locked
|
// init_n_lock make query block locked
|
||||||
BLOCK_UNLOCK_WR(query_block);
|
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;
|
Query_cache_query_flags flags;
|
||||||
DBUG_ENTER("Query_cache::send_result_to_client");
|
DBUG_ENTER("Query_cache::send_result_to_client");
|
||||||
|
|
||||||
if (query_cache_size == 0 || thd->locked_tables ||
|
/*
|
||||||
thd->variables.query_cache_type == 0)
|
Testing 'query_cache_size' without a lock here is safe: the thing
|
||||||
goto err;
|
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 */
|
See also a note on double-check locking usage above.
|
||||||
DBUG_ASSERT(thd->net.query_cache_query == 0);
|
*/
|
||||||
|
if (thd->locked_tables || thd->variables.query_cache_type == 0 ||
|
||||||
|
query_cache_size == 0)
|
||||||
|
goto err;
|
||||||
|
|
||||||
if (!thd->lex->safe_to_cache_query)
|
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);
|
STRUCT_LOCK(&structure_guard_mutex);
|
||||||
if (query_cache_size == 0)
|
if (query_cache_size == 0 || flush_in_progress)
|
||||||
{
|
{
|
||||||
DBUG_PRINT("qcache", ("query cache disabled"));
|
DBUG_PRINT("qcache", ("query cache disabled"));
|
||||||
goto err_unlock;
|
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;
|
Query_cache_block *query_block;
|
||||||
|
|
||||||
tot_length= query_length + thd->db_length + 1 + QUERY_CACHE_FLAGS_SIZE;
|
tot_length= query_length + thd->db_length + 1 + QUERY_CACHE_FLAGS_SIZE;
|
||||||
@ -1238,14 +1300,12 @@ void Query_cache::invalidate(THD *thd, TABLE_LIST *tables_used,
|
|||||||
my_bool using_transactions)
|
my_bool using_transactions)
|
||||||
{
|
{
|
||||||
DBUG_ENTER("Query_cache::invalidate (table list)");
|
DBUG_ENTER("Query_cache::invalidate (table list)");
|
||||||
if (query_cache_size > 0)
|
|
||||||
{
|
|
||||||
STRUCT_LOCK(&structure_guard_mutex);
|
STRUCT_LOCK(&structure_guard_mutex);
|
||||||
if (query_cache_size > 0)
|
if (query_cache_size > 0 && !flush_in_progress)
|
||||||
{
|
{
|
||||||
DUMP(this);
|
DUMP(this);
|
||||||
|
|
||||||
using_transactions = using_transactions &&
|
using_transactions= using_transactions &&
|
||||||
(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
|
(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
|
||||||
for (; tables_used; tables_used= tables_used->next_local)
|
for (; tables_used; tables_used= tables_used->next_local)
|
||||||
{
|
{
|
||||||
@ -1266,17 +1326,17 @@ void Query_cache::invalidate(THD *thd, TABLE_LIST *tables_used,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
STRUCT_UNLOCK(&structure_guard_mutex);
|
STRUCT_UNLOCK(&structure_guard_mutex);
|
||||||
}
|
|
||||||
DBUG_VOID_RETURN;
|
DBUG_VOID_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Query_cache::invalidate(CHANGED_TABLE_LIST *tables_used)
|
void Query_cache::invalidate(CHANGED_TABLE_LIST *tables_used)
|
||||||
{
|
{
|
||||||
DBUG_ENTER("Query_cache::invalidate (changed table list)");
|
DBUG_ENTER("Query_cache::invalidate (changed table list)");
|
||||||
if (query_cache_size > 0 && tables_used)
|
if (tables_used)
|
||||||
{
|
{
|
||||||
STRUCT_LOCK(&structure_guard_mutex);
|
STRUCT_LOCK(&structure_guard_mutex);
|
||||||
if (query_cache_size > 0)
|
if (query_cache_size > 0 && !flush_in_progress)
|
||||||
{
|
{
|
||||||
DUMP(this);
|
DUMP(this);
|
||||||
for (; tables_used; tables_used= tables_used->next)
|
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)
|
void Query_cache::invalidate_locked_for_write(TABLE_LIST *tables_used)
|
||||||
{
|
{
|
||||||
DBUG_ENTER("Query_cache::invalidate_locked_for_write");
|
DBUG_ENTER("Query_cache::invalidate_locked_for_write");
|
||||||
if (query_cache_size > 0 && tables_used)
|
if (tables_used)
|
||||||
{
|
{
|
||||||
STRUCT_LOCK(&structure_guard_mutex);
|
STRUCT_LOCK(&structure_guard_mutex);
|
||||||
if (query_cache_size > 0)
|
if (query_cache_size > 0 && !flush_in_progress)
|
||||||
{
|
{
|
||||||
DUMP(this);
|
DUMP(this);
|
||||||
for (; tables_used; tables_used= tables_used->next_local)
|
for (; tables_used; tables_used= tables_used->next_local)
|
||||||
@ -1333,12 +1393,10 @@ void Query_cache::invalidate(THD *thd, TABLE *table,
|
|||||||
{
|
{
|
||||||
DBUG_ENTER("Query_cache::invalidate (table)");
|
DBUG_ENTER("Query_cache::invalidate (table)");
|
||||||
|
|
||||||
if (query_cache_size > 0)
|
|
||||||
{
|
|
||||||
STRUCT_LOCK(&structure_guard_mutex);
|
STRUCT_LOCK(&structure_guard_mutex);
|
||||||
if (query_cache_size > 0)
|
if (query_cache_size > 0 && !flush_in_progress)
|
||||||
{
|
{
|
||||||
using_transactions = using_transactions &&
|
using_transactions= using_transactions &&
|
||||||
(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
|
(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
|
||||||
if (using_transactions &&
|
if (using_transactions &&
|
||||||
(table->file->table_cache_type() == HA_CACHE_TBL_TRANSACT))
|
(table->file->table_cache_type() == HA_CACHE_TBL_TRANSACT))
|
||||||
@ -1347,7 +1405,7 @@ void Query_cache::invalidate(THD *thd, TABLE *table,
|
|||||||
invalidate_table(table);
|
invalidate_table(table);
|
||||||
}
|
}
|
||||||
STRUCT_UNLOCK(&structure_guard_mutex);
|
STRUCT_UNLOCK(&structure_guard_mutex);
|
||||||
}
|
|
||||||
DBUG_VOID_RETURN;
|
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)");
|
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));
|
(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
|
||||||
if (using_transactions) // used for innodb => has_transactions() is TRUE
|
if (using_transactions) // used for innodb => has_transactions() is TRUE
|
||||||
thd->add_changed_table(key, key_length);
|
thd->add_changed_table(key, key_length);
|
||||||
else
|
else
|
||||||
{
|
|
||||||
STRUCT_LOCK(&structure_guard_mutex);
|
|
||||||
if (query_cache_size > 0)
|
|
||||||
invalidate_table((byte*)key, key_length);
|
invalidate_table((byte*)key, key_length);
|
||||||
|
}
|
||||||
STRUCT_UNLOCK(&structure_guard_mutex);
|
STRUCT_UNLOCK(&structure_guard_mutex);
|
||||||
}
|
|
||||||
}
|
|
||||||
DBUG_VOID_RETURN;
|
DBUG_VOID_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1380,10 +1436,8 @@ void Query_cache::invalidate(THD *thd, const char *key, uint32 key_length,
|
|||||||
void Query_cache::invalidate(char *db)
|
void Query_cache::invalidate(char *db)
|
||||||
{
|
{
|
||||||
DBUG_ENTER("Query_cache::invalidate (db)");
|
DBUG_ENTER("Query_cache::invalidate (db)");
|
||||||
if (query_cache_size > 0)
|
|
||||||
{
|
|
||||||
STRUCT_LOCK(&structure_guard_mutex);
|
STRUCT_LOCK(&structure_guard_mutex);
|
||||||
if (query_cache_size > 0)
|
if (query_cache_size > 0 && !flush_in_progress)
|
||||||
{
|
{
|
||||||
DUMP(this);
|
DUMP(this);
|
||||||
restart_search:
|
restart_search:
|
||||||
@ -1411,7 +1465,7 @@ void Query_cache::invalidate(char *db)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
STRUCT_UNLOCK(&structure_guard_mutex);
|
STRUCT_UNLOCK(&structure_guard_mutex);
|
||||||
}
|
|
||||||
DBUG_VOID_RETURN;
|
DBUG_VOID_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1419,15 +1473,14 @@ void Query_cache::invalidate(char *db)
|
|||||||
void Query_cache::invalidate_by_MyISAM_filename(const char *filename)
|
void Query_cache::invalidate_by_MyISAM_filename(const char *filename)
|
||||||
{
|
{
|
||||||
DBUG_ENTER("Query_cache::invalidate_by_MyISAM_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 */
|
/* Calculate the key outside the lock to make the lock shorter */
|
||||||
char key[MAX_DBKEY_LENGTH];
|
char key[MAX_DBKEY_LENGTH];
|
||||||
uint32 db_length;
|
uint32 db_length;
|
||||||
uint key_length= filename_2_table_key(key, filename, &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;
|
Query_cache_block *table_block;
|
||||||
if ((table_block = (Query_cache_block*) hash_search(&tables,
|
if ((table_block = (Query_cache_block*) hash_search(&tables,
|
||||||
(byte*) key,
|
(byte*) key,
|
||||||
@ -1435,7 +1488,7 @@ void Query_cache::invalidate_by_MyISAM_filename(const char *filename)
|
|||||||
invalidate_table(table_block);
|
invalidate_table(table_block);
|
||||||
}
|
}
|
||||||
STRUCT_UNLOCK(&structure_guard_mutex);
|
STRUCT_UNLOCK(&structure_guard_mutex);
|
||||||
}
|
|
||||||
DBUG_VOID_RETURN;
|
DBUG_VOID_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1480,7 +1533,12 @@ void Query_cache::destroy()
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
/* Underlying code expects the lock. */
|
||||||
|
STRUCT_LOCK(&structure_guard_mutex);
|
||||||
free_cache();
|
free_cache();
|
||||||
|
STRUCT_UNLOCK(&structure_guard_mutex);
|
||||||
|
|
||||||
|
pthread_cond_destroy(&COND_flush_finished);
|
||||||
pthread_mutex_destroy(&structure_guard_mutex);
|
pthread_mutex_destroy(&structure_guard_mutex);
|
||||||
initialized = 0;
|
initialized = 0;
|
||||||
}
|
}
|
||||||
@ -1496,6 +1554,8 @@ void Query_cache::init()
|
|||||||
{
|
{
|
||||||
DBUG_ENTER("Query_cache::init");
|
DBUG_ENTER("Query_cache::init");
|
||||||
pthread_mutex_init(&structure_guard_mutex,MY_MUTEX_INIT_FAST);
|
pthread_mutex_init(&structure_guard_mutex,MY_MUTEX_INIT_FAST);
|
||||||
|
pthread_cond_init(&COND_flush_finished, NULL);
|
||||||
|
flush_in_progress= FALSE;
|
||||||
initialized = 1;
|
initialized = 1;
|
||||||
DBUG_VOID_RETURN;
|
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()
|
void Query_cache::free_cache()
|
||||||
{
|
{
|
||||||
DBUG_ENTER("Query_cache::free_cache");
|
DBUG_ENTER("Query_cache::free_cache");
|
||||||
@ -1725,17 +1796,51 @@ void Query_cache::free_cache()
|
|||||||
Free block data
|
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()
|
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)
|
while (queries_blocks != 0)
|
||||||
{
|
{
|
||||||
BLOCK_LOCK_WR(queries_blocks);
|
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
|
DBUG_RETURN(1); // Nothing to remove
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Free query from query cache.
|
free_query_internal() - free query from query cache.
|
||||||
query_block must be locked for writing.
|
|
||||||
This function will remove (and destroy) the lock for the query.
|
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",
|
DBUG_PRINT("qcache", ("free query 0x%lx %lu bytes result",
|
||||||
(ulong) query_block,
|
(ulong) query_block,
|
||||||
query_block->query()->length() ));
|
query_block->query()->length() ));
|
||||||
|
|
||||||
queries_in_cache--;
|
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)
|
if (query->writer() != 0)
|
||||||
{
|
{
|
||||||
/* Tell MySQL that this query should not be cached anymore */
|
/* 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);
|
query->writer(0);
|
||||||
}
|
}
|
||||||
double_linked_list_exclude(query_block, &queries_blocks);
|
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++);
|
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
|
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++;
|
refused++;
|
||||||
inserts--;
|
inserts--;
|
||||||
}
|
}
|
||||||
Query_cache_block *block = result_block;
|
Query_cache_block *block= result_block;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
Query_cache_block *current = block;
|
Query_cache_block *current= block;
|
||||||
block = block->next;
|
block= block->next;
|
||||||
free_memory_block(current);
|
free_memory_block(current);
|
||||||
} while (block != result_block);
|
} while (block != result_block);
|
||||||
}
|
}
|
||||||
@ -1845,6 +1962,32 @@ void Query_cache::free_query(Query_cache_block *query_block)
|
|||||||
DBUG_VOID_RETURN;
|
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
|
Query data creation
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
@ -2430,12 +2573,8 @@ Query_cache::allocate_block(ulong len, my_bool not_less, ulong min,
|
|||||||
if (!under_guard)
|
if (!under_guard)
|
||||||
{
|
{
|
||||||
STRUCT_LOCK(&structure_guard_mutex);
|
STRUCT_LOCK(&structure_guard_mutex);
|
||||||
/*
|
|
||||||
It is very unlikely that following condition is TRUE (it is possible
|
if (unlikely(query_cache.query_cache_size == 0 || flush_in_progress))
|
||||||
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(&structure_guard_mutex);
|
STRUCT_UNLOCK(&structure_guard_mutex);
|
||||||
DBUG_RETURN(0);
|
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)
|
(query without tables are not cached)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
TABLE_COUNTER_TYPE Query_cache::is_cacheable(THD *thd, uint32 query_len,
|
TABLE_COUNTER_TYPE
|
||||||
char *query,
|
Query_cache::is_cacheable(THD *thd, uint32 query_len, char *query, LEX *lex,
|
||||||
LEX *lex,
|
TABLE_LIST *tables_used, uint8 *tables_type)
|
||||||
TABLE_LIST *tables_used,
|
|
||||||
uint8 *tables_type)
|
|
||||||
{
|
{
|
||||||
TABLE_COUNTER_TYPE table_count;
|
TABLE_COUNTER_TYPE table_count;
|
||||||
DBUG_ENTER("Query_cache::is_cacheable");
|
DBUG_ENTER("Query_cache::is_cacheable");
|
||||||
@ -2980,13 +3117,10 @@ my_bool Query_cache::ask_handler_allowance(THD *thd,
|
|||||||
void Query_cache::pack_cache()
|
void Query_cache::pack_cache()
|
||||||
{
|
{
|
||||||
DBUG_ENTER("Query_cache::pack_cache");
|
DBUG_ENTER("Query_cache::pack_cache");
|
||||||
|
|
||||||
STRUCT_LOCK(&structure_guard_mutex);
|
STRUCT_LOCK(&structure_guard_mutex);
|
||||||
/*
|
|
||||||
It is very unlikely that following condition is TRUE (it is possible
|
if (unlikely(query_cache_size == 0 || flush_in_progress))
|
||||||
only if other thread is resizing cache), so we check it only after
|
|
||||||
guard mutex lock
|
|
||||||
*/
|
|
||||||
if (unlikely(query_cache_size == 0))
|
|
||||||
{
|
{
|
||||||
STRUCT_UNLOCK(&structure_guard_mutex);
|
STRUCT_UNLOCK(&structure_guard_mutex);
|
||||||
DBUG_VOID_RETURN;
|
DBUG_VOID_RETURN;
|
||||||
@ -3301,7 +3435,7 @@ my_bool Query_cache::join_results(ulong join_limit)
|
|||||||
DBUG_ENTER("Query_cache::join_results");
|
DBUG_ENTER("Query_cache::join_results");
|
||||||
|
|
||||||
STRUCT_LOCK(&structure_guard_mutex);
|
STRUCT_LOCK(&structure_guard_mutex);
|
||||||
if (queries_blocks != 0)
|
if (queries_blocks != 0 && !flush_in_progress)
|
||||||
{
|
{
|
||||||
DBUG_ASSERT(query_cache_size > 0);
|
DBUG_ASSERT(query_cache_size > 0);
|
||||||
Query_cache_block *block = queries_blocks;
|
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;
|
my_bool result = 0;
|
||||||
uint i;
|
uint i;
|
||||||
DBUG_ENTER("check_integrity");
|
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_PRINT("qcache", ("Query Cache not initialized"));
|
||||||
DBUG_RETURN(0);
|
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))
|
if (hash_check(&queries))
|
||||||
{
|
{
|
||||||
@ -3857,7 +3983,7 @@ my_bool Query_cache::check_integrity(bool not_locked)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
DBUG_ASSERT(result == 0);
|
DBUG_ASSERT(result == 0);
|
||||||
if (!not_locked)
|
if (!locked)
|
||||||
STRUCT_UNLOCK(&structure_guard_mutex);
|
STRUCT_UNLOCK(&structure_guard_mutex);
|
||||||
DBUG_RETURN(result);
|
DBUG_RETURN(result);
|
||||||
}
|
}
|
||||||
|
@ -195,7 +195,6 @@ extern "C"
|
|||||||
byte *query_cache_table_get_key(const byte *record, uint *length,
|
byte *query_cache_table_get_key(const byte *record, uint *length,
|
||||||
my_bool not_used);
|
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);
|
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,
|
ulong free_memory, queries_in_cache, hits, inserts, refused,
|
||||||
free_memory_blocks, total_blocks, lowmem_prunes;
|
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:
|
protected:
|
||||||
/*
|
/*
|
||||||
The following mutex is locked when searching or changing global
|
The following mutex is locked when searching or changing global
|
||||||
@ -249,6 +254,12 @@ protected:
|
|||||||
LOCK SEQUENCE (to prevent deadlocks):
|
LOCK SEQUENCE (to prevent deadlocks):
|
||||||
1. structure_guard_mutex
|
1. structure_guard_mutex
|
||||||
2. query block (for operation inside query (query block/results))
|
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;
|
pthread_mutex_t structure_guard_mutex;
|
||||||
byte *cache; // cache memory
|
byte *cache; // cache memory
|
||||||
@ -358,6 +369,7 @@ protected:
|
|||||||
If query is cacheable return number tables in query
|
If query is cacheable return number tables in query
|
||||||
(query without tables not cached)
|
(query without tables not cached)
|
||||||
*/
|
*/
|
||||||
|
static
|
||||||
TABLE_COUNTER_TYPE is_cacheable(THD *thd, uint32 query_len, char *query,
|
TABLE_COUNTER_TYPE is_cacheable(THD *thd, uint32 query_len, char *query,
|
||||||
LEX *lex, TABLE_LIST *tables_used,
|
LEX *lex, TABLE_LIST *tables_used,
|
||||||
uint8 *tables_type);
|
uint8 *tables_type);
|
||||||
@ -410,6 +422,7 @@ protected:
|
|||||||
|
|
||||||
void destroy();
|
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_insert(NET *net, const char *packet, ulong length);
|
||||||
friend void query_cache_end_of_result(THD *thd);
|
friend void query_cache_end_of_result(THD *thd);
|
||||||
friend void query_cache_abort(NET *net);
|
friend void query_cache_abort(NET *net);
|
||||||
@ -435,6 +448,8 @@ protected:
|
|||||||
|
|
||||||
extern Query_cache query_cache;
|
extern Query_cache query_cache;
|
||||||
extern TYPELIB query_cache_type_typelib;
|
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_end_of_result(THD *thd);
|
||||||
void query_cache_abort(NET *net);
|
void query_cache_abort(NET *net);
|
||||||
|
|
||||||
|
@ -261,7 +261,7 @@ THD::THD()
|
|||||||
#endif
|
#endif
|
||||||
client_capabilities= 0; // minimalistic client
|
client_capabilities= 0; // minimalistic client
|
||||||
net.last_error[0]=0; // If error on boot
|
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;
|
ull=0;
|
||||||
system_thread= NON_SYSTEM_THREAD;
|
system_thread= NON_SYSTEM_THREAD;
|
||||||
cleanup_done= abort_on_warning= no_warnings_for_error= 0;
|
cleanup_done= abort_on_warning= no_warnings_for_error= 0;
|
||||||
|
@ -139,14 +139,8 @@ MYSQL_ERROR *push_warning(THD *thd, MYSQL_ERROR::enum_warning_level level,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (thd->spcont &&
|
if (thd->spcont &&
|
||||||
thd->spcont->find_handler(code,
|
thd->spcont->handle_error(code, level, thd))
|
||||||
((int) level >=
|
|
||||||
(int) MYSQL_ERROR::WARN_LEVEL_WARN &&
|
|
||||||
thd->really_abort_on_warning()) ?
|
|
||||||
MYSQL_ERROR::WARN_LEVEL_ERROR : level))
|
|
||||||
{
|
{
|
||||||
if (! thd->spcont->found_handler_here())
|
|
||||||
thd->net.report_error= 1; /* Make "select" abort correctly */
|
|
||||||
DBUG_RETURN(NULL);
|
DBUG_RETURN(NULL);
|
||||||
}
|
}
|
||||||
query_cache_abort(&thd->net);
|
query_cache_abort(&thd->net);
|
||||||
|
@ -6036,6 +6036,9 @@ void mysql_init_multi_delete(LEX *lex)
|
|||||||
void mysql_parse(THD *thd, char *inBuf, uint length)
|
void mysql_parse(THD *thd, char *inBuf, uint length)
|
||||||
{
|
{
|
||||||
DBUG_ENTER("mysql_parse");
|
DBUG_ENTER("mysql_parse");
|
||||||
|
|
||||||
|
DBUG_EXECUTE_IF("parser_debug", turn_parser_debug_on(););
|
||||||
|
|
||||||
mysql_init_query(thd, (uchar*) inBuf, length);
|
mysql_init_query(thd, (uchar*) inBuf, length);
|
||||||
if (query_cache_send_result_to_client(thd, inBuf, length) <= 0)
|
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 */
|
select_errors=0; /* Write if more errors */
|
||||||
bool tmp_write_to_binlog= 1;
|
bool tmp_write_to_binlog= 1;
|
||||||
|
|
||||||
if (thd && thd->in_sub_stmt)
|
DBUG_ASSERT(!thd || !thd->in_sub_stmt);
|
||||||
{
|
|
||||||
my_error(ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0), "FLUSH");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
||||||
if (options & REFRESH_GRANT)
|
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 *get_current_user(THD *thd, LEX_USER *user)
|
||||||
{
|
{
|
||||||
LEX_USER *curr_user;
|
|
||||||
if (!user->user.str) // current_user
|
if (!user->user.str) // current_user
|
||||||
{
|
return create_default_definer(thd);
|
||||||
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 user;
|
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;
|
||||||
|
}
|
||||||
|
@ -158,11 +158,13 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
|
|||||||
{
|
{
|
||||||
TABLE *table;
|
TABLE *table;
|
||||||
bool result= TRUE;
|
bool result= TRUE;
|
||||||
LEX_STRING definer_user;
|
String stmt_query;
|
||||||
LEX_STRING definer_host;
|
|
||||||
|
|
||||||
DBUG_ENTER("mysql_create_or_drop_trigger");
|
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
|
QQ: This function could be merged in mysql_alter_table() function
|
||||||
But do we want this ?
|
But do we want this ?
|
||||||
@ -270,8 +272,8 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
|
|||||||
}
|
}
|
||||||
|
|
||||||
result= (create ?
|
result= (create ?
|
||||||
table->triggers->create_trigger(thd, tables, &definer_user, &definer_host):
|
table->triggers->create_trigger(thd, tables, &stmt_query):
|
||||||
table->triggers->drop_trigger(thd, tables));
|
table->triggers->drop_trigger(thd, tables, &stmt_query));
|
||||||
|
|
||||||
end:
|
end:
|
||||||
VOID(pthread_mutex_unlock(&LOCK_open));
|
VOID(pthread_mutex_unlock(&LOCK_open));
|
||||||
@ -283,32 +285,9 @@ end:
|
|||||||
{
|
{
|
||||||
thd->clear_error();
|
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. */
|
/* 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);
|
mysql_bin_log.write(&qinfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -328,22 +307,8 @@ end:
|
|||||||
LEX)
|
LEX)
|
||||||
tables - table list containing one open table for which the
|
tables - table list containing one open table for which the
|
||||||
trigger is created.
|
trigger is created.
|
||||||
definer_user - [out] after a call it points to 0-terminated string or
|
stmt_query - [OUT] after successful return, this string contains
|
||||||
contains the NULL-string:
|
well-formed statement for creation this trigger.
|
||||||
- 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.
|
|
||||||
|
|
||||||
NOTE
|
NOTE
|
||||||
- Assumes that trigger name is fully qualified.
|
- Assumes that trigger name is fully qualified.
|
||||||
@ -358,14 +323,15 @@ end:
|
|||||||
True - error
|
True - error
|
||||||
*/
|
*/
|
||||||
bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
|
bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
|
||||||
LEX_STRING *definer_user,
|
String *stmt_query)
|
||||||
LEX_STRING *definer_host)
|
|
||||||
{
|
{
|
||||||
LEX *lex= thd->lex;
|
LEX *lex= thd->lex;
|
||||||
TABLE *table= tables->table;
|
TABLE *table= tables->table;
|
||||||
char file_buff[FN_REFLEN], trigname_buff[FN_REFLEN];
|
char file_buff[FN_REFLEN], trigname_buff[FN_REFLEN];
|
||||||
LEX_STRING file, trigname_file;
|
LEX_STRING file, trigname_file;
|
||||||
LEX_STRING *trg_def, *name;
|
LEX_STRING *trg_def, *name;
|
||||||
|
LEX_STRING definer_user;
|
||||||
|
LEX_STRING definer_host;
|
||||||
ulonglong *trg_sql_mode;
|
ulonglong *trg_sql_mode;
|
||||||
char trg_definer_holder[USER_HOST_BUFF_SIZE];
|
char trg_definer_holder[USER_HOST_BUFF_SIZE];
|
||||||
LEX_STRING *trg_definer;
|
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))
|
definers_list.push_back(trg_definer, &table->mem_root))
|
||||||
goto err_with_cleanup;
|
goto err_with_cleanup;
|
||||||
|
|
||||||
trg_def->str= thd->query;
|
|
||||||
trg_def->length= thd->query_length;
|
|
||||||
*trg_sql_mode= thd->variables.sql_mode;
|
*trg_sql_mode= thd->variables.sql_mode;
|
||||||
|
|
||||||
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
||||||
@ -533,27 +497,54 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
|
|||||||
{
|
{
|
||||||
/* SUID trigger. */
|
/* SUID trigger. */
|
||||||
|
|
||||||
*definer_user= lex->definer->user;
|
definer_user= lex->definer->user;
|
||||||
*definer_host= lex->definer->host;
|
definer_host= lex->definer->host;
|
||||||
|
|
||||||
trg_definer->str= trg_definer_holder;
|
trg_definer->str= trg_definer_holder;
|
||||||
trg_definer->length= strxmov(trg_definer->str, definer_user->str, "@",
|
trg_definer->length= strxmov(trg_definer->str, definer_user.str, "@",
|
||||||
definer_host->str, NullS) - trg_definer->str;
|
definer_host.str, NullS) - trg_definer->str;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* non-SUID trigger. */
|
/* non-SUID trigger. */
|
||||||
|
|
||||||
definer_user->str= 0;
|
definer_user.str= 0;
|
||||||
definer_user->length= 0;
|
definer_user.length= 0;
|
||||||
|
|
||||||
definer_host->str= 0;
|
definer_host.str= 0;
|
||||||
definer_host->length= 0;
|
definer_host.length= 0;
|
||||||
|
|
||||||
trg_definer->str= (char*) "";
|
trg_definer->str= (char*) "";
|
||||||
trg_definer->length= 0;
|
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,
|
if (!sql_create_definition_file(NULL, &file, &triggers_file_type,
|
||||||
(gptr)this, triggers_file_parameters, 0))
|
(gptr)this, triggers_file_parameters, 0))
|
||||||
return 0;
|
return 0;
|
||||||
@ -645,15 +636,19 @@ static bool save_trigger_file(Table_triggers_list *triggers, const char *db,
|
|||||||
|
|
||||||
SYNOPSIS
|
SYNOPSIS
|
||||||
drop_trigger()
|
drop_trigger()
|
||||||
thd - current thread context (including trigger definition in LEX)
|
thd - current thread context
|
||||||
tables - table list containing one open table for which trigger is
|
(including trigger definition in LEX)
|
||||||
dropped.
|
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
|
RETURN VALUE
|
||||||
False - success
|
False - success
|
||||||
True - error
|
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 *lex= thd->lex;
|
||||||
LEX_STRING *name;
|
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);
|
List_iterator<LEX_STRING> it_definer(definers_list);
|
||||||
char path[FN_REFLEN];
|
char path[FN_REFLEN];
|
||||||
|
|
||||||
|
stmt_query->append(thd->query, thd->query_length);
|
||||||
|
|
||||||
while ((name= it_name++))
|
while ((name= it_name++))
|
||||||
{
|
{
|
||||||
it_def++;
|
it_def++;
|
||||||
|
@ -92,10 +92,8 @@ public:
|
|||||||
}
|
}
|
||||||
~Table_triggers_list();
|
~Table_triggers_list();
|
||||||
|
|
||||||
bool create_trigger(THD *thd, TABLE_LIST *table,
|
bool create_trigger(THD *thd, TABLE_LIST *table, String *stmt_query);
|
||||||
LEX_STRING *definer_user,
|
bool drop_trigger(THD *thd, TABLE_LIST *table, String *stmt_query);
|
||||||
LEX_STRING *definer_host);
|
|
||||||
bool drop_trigger(THD *thd, TABLE_LIST *table);
|
|
||||||
bool process_triggers(THD *thd, trg_event_type event,
|
bool process_triggers(THD *thd, trg_event_type event,
|
||||||
trg_action_time_type time_type,
|
trg_action_time_type time_type,
|
||||||
bool old_row_is_record1);
|
bool old_row_is_record1);
|
||||||
|
@ -1070,6 +1070,30 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
|
|||||||
if (lex->binlog_row_based_if_mixed)
|
if (lex->binlog_row_based_if_mixed)
|
||||||
old_lex->binlog_row_based_if_mixed= TRUE;
|
old_lex->binlog_row_based_if_mixed= TRUE;
|
||||||
#endif
|
#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
|
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 SELECT allow merging
|
||||||
- VIEW used in subquery or command support MERGE algorithm
|
- VIEW used in subquery or command support MERGE algorithm
|
||||||
*/
|
*/
|
||||||
if (table->algorithm != VIEW_ALGORITHM_TMPTABLE &&
|
if (view_is_mergeable &&
|
||||||
lex->can_be_merged() &&
|
|
||||||
(table->select_lex->master_unit() != &old_lex->unit ||
|
(table->select_lex->master_unit() != &old_lex->unit ||
|
||||||
old_lex->can_use_merged()) &&
|
old_lex->can_use_merged()) &&
|
||||||
!old_lex->can_not_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 */
|
/* lex should contain at least one table */
|
||||||
DBUG_ASSERT(view_main_select_tables != 0);
|
DBUG_ASSERT(view_main_select_tables != 0);
|
||||||
|
|
||||||
|
List_iterator_fast<TABLE_LIST> ti(view_select->top_join_list);
|
||||||
|
|
||||||
table->effective_algorithm= VIEW_ALGORITHM_MERGE;
|
table->effective_algorithm= VIEW_ALGORITHM_MERGE;
|
||||||
DBUG_PRINT("info", ("algorithm: MERGE"));
|
DBUG_PRINT("info", ("algorithm: MERGE"));
|
||||||
table->updatable= (table->updatable_view != 0);
|
table->updatable= (table->updatable_view != 0);
|
||||||
table->effective_with_check=
|
table->effective_with_check=
|
||||||
old_lex->get_effective_with_check(table);
|
old_lex->get_effective_with_check(table);
|
||||||
table->merge_underlying_list= view_main_select_tables;
|
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.
|
/* Fill correct wanted privileges. */
|
||||||
*/
|
for (tbl= view_main_select_tables; tbl; tbl= tbl->next_local)
|
||||||
for (tbl= table->merge_underlying_list; tbl; tbl= tbl->next_local)
|
|
||||||
{
|
|
||||||
tbl->lock_type= table->lock_type;
|
|
||||||
tbl->grant.want_privilege= top_view->grant.orig_want_privilege;
|
tbl->grant.want_privilege= top_view->grant.orig_want_privilege;
|
||||||
}
|
|
||||||
|
|
||||||
/* prepare view context */
|
/* prepare view context */
|
||||||
lex->select_lex.context.resolve_in_table_list_only(view_main_select_tables);
|
lex->select_lex.context.resolve_in_table_list_only(view_main_select_tables);
|
||||||
|
@ -64,6 +64,34 @@ inline Item *is_truth_value(Item *A, bool v1, bool v2)
|
|||||||
new Item_int((char *) (v1 ? "FALSE" : "TRUE"),!v1, 1));
|
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 {
|
%union {
|
||||||
int num;
|
int num;
|
||||||
@ -8565,17 +8593,8 @@ flush:
|
|||||||
FLUSH_SYM opt_no_write_to_binlog
|
FLUSH_SYM opt_no_write_to_binlog
|
||||||
{
|
{
|
||||||
LEX *lex=Lex;
|
LEX *lex=Lex;
|
||||||
if (lex->sphead && lex->sphead->m_type != TYPE_ENUM_PROCEDURE)
|
lex->sql_command= SQLCOM_FLUSH;
|
||||||
{
|
lex->type= 0;
|
||||||
/*
|
|
||||||
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->no_write_to_binlog= $2;
|
lex->no_write_to_binlog= $2;
|
||||||
}
|
}
|
||||||
flush_options
|
flush_options
|
||||||
@ -9301,6 +9320,9 @@ user:
|
|||||||
$$->user = $1;
|
$$->user = $1;
|
||||||
$$->host.str= (char *) "%";
|
$$->host.str= (char *) "%";
|
||||||
$$->host.length= 1;
|
$$->host.length= 1;
|
||||||
|
|
||||||
|
if (check_string_length(&$$->user, ER(ER_USERNAME), USERNAME_LENGTH))
|
||||||
|
YYABORT;
|
||||||
}
|
}
|
||||||
| ident_or_text '@' ident_or_text
|
| ident_or_text '@' ident_or_text
|
||||||
{
|
{
|
||||||
@ -9308,6 +9330,11 @@ user:
|
|||||||
if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user))))
|
if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user))))
|
||||||
YYABORT;
|
YYABORT;
|
||||||
$$->user = $1; $$->host=$3;
|
$$->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
|
| CURRENT_USER optional_braces
|
||||||
{
|
{
|
||||||
@ -10826,15 +10853,9 @@ definer:
|
|||||||
*/
|
*/
|
||||||
YYTHD->lex->definer= 0;
|
YYTHD->lex->definer= 0;
|
||||||
}
|
}
|
||||||
| DEFINER_SYM EQ CURRENT_USER optional_braces
|
| DEFINER_SYM EQ user
|
||||||
{
|
{
|
||||||
if (! (YYTHD->lex->definer= create_default_definer(YYTHD)))
|
YYTHD->lex->definer= get_current_user(YYTHD, $3);
|
||||||
YYABORT;
|
|
||||||
}
|
|
||||||
| DEFINER_SYM EQ ident_or_text '@' ident_or_text
|
|
||||||
{
|
|
||||||
if (!(YYTHD->lex->definer= create_definer(YYTHD, &$3, &$5)))
|
|
||||||
YYABORT;
|
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user