mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 12:02:42 +01:00
Merge bk-internal.mysql.com:/home/bk/mysql-5.1
into vajra.(none):/opt/local/work/mysql-5.1-runtime mysql-test/include/mix1.inc: Auto merged mysql-test/r/innodb_mysql.result: Auto merged mysql-test/r/ps_1general.result: Auto merged mysql-test/t/disabled.def: Auto merged mysql-test/t/ps_1general.test: Auto merged sql/ha_ndbcluster.cc: Auto merged sql/ha_ndbcluster_binlog.cc: Auto merged sql/item.cc: Auto merged sql/item_func.cc: Auto merged sql/mysql_priv.h: Auto merged sql/set_var.cc: Auto merged sql/sql_base.cc: Auto merged sql/sql_cache.cc: Auto merged sql/sql_class.cc: Auto merged sql/sql_class.h: Auto merged sql/sql_lex.cc: Auto merged sql/sql_lex.h: Auto merged sql/sql_parse.cc: Auto merged sql/sql_partition.cc: Auto merged sql/sql_prepare.cc: Auto merged sql/sql_table.cc: Auto merged sql/sql_yacc.yy: Auto merged sql/table.h: Auto merged sql/sql_insert.cc: SCCS merged
This commit is contained in:
commit
fc7ef4be71
47 changed files with 2635 additions and 699 deletions
|
@ -742,4 +742,24 @@ select * from t2;
|
|||
|
||||
drop table t2;
|
||||
|
||||
|
||||
#
|
||||
# Tests for bug #28415 "Some ALTER TABLE statements no longer work
|
||||
# under LOCK TABLES" and some aspects of fast ALTER TABLE behaviour
|
||||
# for transactional tables.
|
||||
#
|
||||
--disable_warnings
|
||||
drop table if exists t1, t2;
|
||||
--enable_warnings
|
||||
create table t1 (i int);
|
||||
alter table t1 modify i int default 1;
|
||||
alter table t1 modify i int default 2, rename t2;
|
||||
lock table t2 write;
|
||||
alter table t2 modify i int default 3;
|
||||
unlock tables;
|
||||
lock table t2 write;
|
||||
alter table t2 modify i int default 4, rename t1;
|
||||
unlock tables;
|
||||
drop table t1;
|
||||
|
||||
--echo End of 5.1 tests
|
||||
|
|
|
@ -782,6 +782,100 @@ t1 CREATE TABLE `t1` (
|
|||
`i` int(11) DEFAULT NULL
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=latin1 MAX_ROWS=4294967295
|
||||
drop table t1;
|
||||
create table t1 select * from t2;
|
||||
ERROR 42S02: Table 'test.t2' doesn't exist
|
||||
create table t1 select * from t1;
|
||||
ERROR HY000: You can't specify target table 't1' for update in FROM clause
|
||||
create table t1 select coalesce('a' collate latin1_swedish_ci,'b' collate latin1_bin);
|
||||
ERROR HY000: Illegal mix of collations (latin1_swedish_ci,EXPLICIT) and (latin1_bin,EXPLICIT) for operation 'coalesce'
|
||||
create table t1 (primary key(a)) select "b" as b;
|
||||
ERROR 42000: Key column 'a' doesn't exist in table
|
||||
create table t1 (a int);
|
||||
create table if not exists t1 select 1 as a, 2 as b;
|
||||
ERROR 21S01: Column count doesn't match value count at row 1
|
||||
drop table t1;
|
||||
create table t1 (primary key (a)) (select 1 as a) union all (select 1 as a);
|
||||
ERROR 23000: Duplicate entry '1' for key 'PRIMARY'
|
||||
create table t1 (i int);
|
||||
create table t1 select 1 as i;
|
||||
ERROR 42S01: Table 't1' already exists
|
||||
create table if not exists t1 select 1 as i;
|
||||
Warnings:
|
||||
Note 1050 Table 't1' already exists
|
||||
select * from t1;
|
||||
i
|
||||
1
|
||||
create table t1 select coalesce('a' collate latin1_swedish_ci,'b' collate latin1_bin);
|
||||
ERROR HY000: Illegal mix of collations (latin1_swedish_ci,EXPLICIT) and (latin1_bin,EXPLICIT) for operation 'coalesce'
|
||||
select * from t1;
|
||||
i
|
||||
1
|
||||
alter table t1 add primary key (i);
|
||||
create table if not exists t1 (select 2 as i) union all (select 2 as i);
|
||||
ERROR 23000: Duplicate entry '2' for key 'PRIMARY'
|
||||
select * from t1;
|
||||
i
|
||||
1
|
||||
2
|
||||
drop table t1;
|
||||
create temporary table t1 (j int);
|
||||
create table if not exists t1 select 1;
|
||||
Warnings:
|
||||
Note 1050 Table 't1' already exists
|
||||
select * from t1;
|
||||
j
|
||||
1
|
||||
drop temporary table t1;
|
||||
select * from t1;
|
||||
ERROR 42S02: Table 'test.t1' doesn't exist
|
||||
drop table t1;
|
||||
ERROR 42S02: Unknown table 't1'
|
||||
create table t1 (i int);
|
||||
insert into t1 values (1), (2);
|
||||
lock tables t1 read;
|
||||
create table t2 select * from t1;
|
||||
ERROR HY000: Table 't2' was not locked with LOCK TABLES
|
||||
create table if not exists t2 select * from t1;
|
||||
ERROR HY000: Table 't2' was not locked with LOCK TABLES
|
||||
unlock tables;
|
||||
create table t2 (j int);
|
||||
lock tables t1 read;
|
||||
create table t2 select * from t1;
|
||||
ERROR HY000: Table 't2' was not locked with LOCK TABLES
|
||||
create table if not exists t2 select * from t1;
|
||||
ERROR HY000: Table 't2' was not locked with LOCK TABLES
|
||||
unlock tables;
|
||||
lock table t1 read, t2 read;
|
||||
create table t2 select * from t1;
|
||||
ERROR HY000: Table 't2' was locked with a READ lock and can't be updated
|
||||
create table if not exists t2 select * from t1;
|
||||
ERROR HY000: Table 't2' was locked with a READ lock and can't be updated
|
||||
unlock tables;
|
||||
lock table t1 read, t2 write;
|
||||
create table t2 select * from t1;
|
||||
ERROR 42S01: Table 't2' already exists
|
||||
create table if not exists t2 select * from t1;
|
||||
Warnings:
|
||||
Note 1050 Table 't2' already exists
|
||||
select * from t1;
|
||||
i
|
||||
1
|
||||
2
|
||||
unlock tables;
|
||||
drop table t2;
|
||||
lock tables t1 read;
|
||||
create temporary table t2 select * from t1;
|
||||
create temporary table if not exists t2 select * from t1;
|
||||
Warnings:
|
||||
Note 1050 Table 't2' already exists
|
||||
select * from t2;
|
||||
i
|
||||
1
|
||||
2
|
||||
1
|
||||
2
|
||||
unlock tables;
|
||||
drop table t1, t2;
|
||||
create table t1 (upgrade int);
|
||||
drop table t1;
|
||||
End of 5.0 tests
|
||||
|
|
164
mysql-test/r/create_select-big.result
Normal file
164
mysql-test/r/create_select-big.result
Normal file
|
@ -0,0 +1,164 @@
|
|||
drop table if exists t1,t2,t3,t4,t5;
|
||||
set session debug="+d,sleep_create_select_before_create";
|
||||
create table t1 select 1 as i;;
|
||||
create table t1 (j char(5));
|
||||
ERROR 42S01: Table 't1' already exists
|
||||
show create table t1;
|
||||
Table Create Table
|
||||
t1 CREATE TABLE `t1` (
|
||||
`i` int(1) NOT NULL DEFAULT '0'
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=latin1
|
||||
drop table t1;
|
||||
create table t1 select 1 as i;;
|
||||
create table t1 select "Test" as j;
|
||||
ERROR 42S01: Table 't1' already exists
|
||||
show create table t1;
|
||||
Table Create Table
|
||||
t1 CREATE TABLE `t1` (
|
||||
`i` int(1) NOT NULL DEFAULT '0'
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=latin1
|
||||
drop table t1;
|
||||
create table t3 (j char(5));
|
||||
create table t1 select 1 as i;;
|
||||
create table t1 like t3;
|
||||
ERROR 42S01: Table 't1' already exists
|
||||
show create table t1;
|
||||
Table Create Table
|
||||
t1 CREATE TABLE `t1` (
|
||||
`i` int(1) NOT NULL DEFAULT '0'
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=latin1
|
||||
drop table t1;
|
||||
create table t1 select 1 as i;;
|
||||
rename table t3 to t1;
|
||||
ERROR 42S01: Table 't1' already exists
|
||||
show create table t1;
|
||||
Table Create Table
|
||||
t1 CREATE TABLE `t1` (
|
||||
`i` int(1) NOT NULL DEFAULT '0'
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=latin1
|
||||
drop table t1;
|
||||
create table t1 select 1 as i;;
|
||||
alter table t3 rename to t1;
|
||||
ERROR 42S01: Table 't1' already exists
|
||||
show create table t1;
|
||||
Table Create Table
|
||||
t1 CREATE TABLE `t1` (
|
||||
`i` int(1) NOT NULL DEFAULT '0'
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=latin1
|
||||
drop table t1;
|
||||
create table t1 select 1 as i;;
|
||||
alter table t3 rename to t1, add k int;
|
||||
ERROR 42S01: Table 't1' already exists
|
||||
show create table t1;
|
||||
Table Create Table
|
||||
t1 CREATE TABLE `t1` (
|
||||
`i` int(1) NOT NULL DEFAULT '0'
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=latin1
|
||||
drop table t1, t3;
|
||||
set session debug="-d,sleep_create_select_before_create:+d,sleep_create_select_before_open";
|
||||
create table t1 select 1 as i;;
|
||||
drop table t1;
|
||||
create table t1 select 1 as i;;
|
||||
rename table t1 to t2;
|
||||
drop table t2;
|
||||
create table t1 select 1 as i;;
|
||||
select * from t1;
|
||||
i
|
||||
1
|
||||
drop table t1;
|
||||
create table t1 select 1 as i;;
|
||||
insert into t1 values (2);
|
||||
select * from t1;
|
||||
i
|
||||
1
|
||||
2
|
||||
drop table t1;
|
||||
set @a:=0;
|
||||
create table t1 select 1 as i;;
|
||||
create trigger t1_bi before insert on t1 for each row set @a:=1;
|
||||
select @a;
|
||||
@a
|
||||
0
|
||||
drop table t1;
|
||||
set session debug="-d,sleep_create_select_before_open:+d,sleep_create_select_before_lock";
|
||||
create table t1 select 1 as i;;
|
||||
drop table t1;
|
||||
create table t1 select 1 as i;;
|
||||
rename table t1 to t2;
|
||||
drop table t2;
|
||||
create table t1 select 1 as i;;
|
||||
select * from t1;
|
||||
i
|
||||
1
|
||||
drop table t1;
|
||||
create table t1 select 1 as i;;
|
||||
insert into t1 values (2);
|
||||
select * from t1;
|
||||
i
|
||||
1
|
||||
2
|
||||
drop table t1;
|
||||
set @a:=0;
|
||||
create table t1 select 1 as i;;
|
||||
create trigger t1_bi before insert on t1 for each row set @a:=1;
|
||||
select @a;
|
||||
@a
|
||||
0
|
||||
drop table t1;
|
||||
set session debug="-d,sleep_create_select_before_lock:+d,sleep_create_select_before_check_if_exists";
|
||||
create table t1 (i int);
|
||||
create table if not exists t1 select 1 as i;;
|
||||
drop table t1;
|
||||
Warnings:
|
||||
Note 1050 Table 't1' already exists
|
||||
create table t1 (i int);
|
||||
set @a:=0;
|
||||
create table if not exists t1 select 1 as i;;
|
||||
create trigger t1_bi before insert on t1 for each row set @a:=1;
|
||||
Warnings:
|
||||
Note 1050 Table 't1' already exists
|
||||
select @a;
|
||||
@a
|
||||
0
|
||||
select * from t1;
|
||||
i
|
||||
1
|
||||
drop table t1;
|
||||
set session debug="-d,sleep_create_select_before_check_if_exists";
|
||||
create table t2 (a int);
|
||||
create table t4 (b int);
|
||||
lock table t4 write;
|
||||
select 1;
|
||||
1
|
||||
1
|
||||
create table t3 as select * from t4;;
|
||||
create table t1 select * from t2, t3;;
|
||||
unlock tables;
|
||||
select * from t1;
|
||||
a b
|
||||
show create table t1;
|
||||
Table Create Table
|
||||
t1 CREATE TABLE `t1` (
|
||||
`a` int(11) DEFAULT NULL,
|
||||
`b` int(11) DEFAULT NULL
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=latin1
|
||||
drop table t1, t3;
|
||||
lock table t4 read;
|
||||
select 1;
|
||||
1
|
||||
1
|
||||
rename table t4 to t3;;
|
||||
create table if not exists t1 select 1 as i from t2, t3;;
|
||||
create table t5 (j int);
|
||||
rename table t5 to t1;
|
||||
unlock tables;
|
||||
Warnings:
|
||||
Note 1050 Table 't1' already exists
|
||||
select * from t1;
|
||||
j
|
||||
show create table t1;
|
||||
Table Create Table
|
||||
t1 CREATE TABLE `t1` (
|
||||
`j` int(11) DEFAULT NULL
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=latin1
|
||||
drop table t1, t2, t3;
|
|
@ -785,4 +785,15 @@ k a c
|
|||
11 15 1
|
||||
12 20 1
|
||||
drop table t2;
|
||||
drop table if exists t1, t2;
|
||||
create table t1 (i int);
|
||||
alter table t1 modify i int default 1;
|
||||
alter table t1 modify i int default 2, rename t2;
|
||||
lock table t2 write;
|
||||
alter table t2 modify i int default 3;
|
||||
unlock tables;
|
||||
lock table t2 write;
|
||||
alter table t2 modify i int default 4, rename t1;
|
||||
unlock tables;
|
||||
drop table t1;
|
||||
End of 5.1 tests
|
||||
|
|
|
@ -26,11 +26,11 @@ ERROR HY000: Unknown prepared statement handler (no_such_statement) given to DEA
|
|||
execute stmt1;
|
||||
ERROR HY000: Incorrect arguments to EXECUTE
|
||||
prepare stmt2 from 'prepare nested_stmt from "select 1"';
|
||||
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '"select 1"' at line 1
|
||||
ERROR HY000: This command is not supported in the prepared statement protocol yet
|
||||
prepare stmt2 from 'execute stmt1';
|
||||
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'stmt1' at line 1
|
||||
ERROR HY000: This command is not supported in the prepared statement protocol yet
|
||||
prepare stmt2 from 'deallocate prepare z';
|
||||
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'z' at line 1
|
||||
ERROR HY000: This command is not supported in the prepared statement protocol yet
|
||||
prepare stmt3 from 'insert into t1 values (?,?)';
|
||||
set @arg1=5, @arg2='five';
|
||||
execute stmt3 using @arg1, @arg2;
|
||||
|
|
|
@ -373,11 +373,11 @@ drop table t5 ;
|
|||
deallocate prepare stmt_do ;
|
||||
deallocate prepare stmt_set ;
|
||||
prepare stmt1 from ' prepare stmt2 from '' select 1 '' ' ;
|
||||
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' select 1 '' at line 1
|
||||
ERROR HY000: This command is not supported in the prepared statement protocol yet
|
||||
prepare stmt1 from ' execute stmt2 ' ;
|
||||
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'stmt2' at line 1
|
||||
ERROR HY000: This command is not supported in the prepared statement protocol yet
|
||||
prepare stmt1 from ' deallocate prepare never_prepared ' ;
|
||||
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'never_prepared' at line 1
|
||||
ERROR HY000: This command is not supported in the prepared statement protocol yet
|
||||
prepare stmt4 from ' use test ' ;
|
||||
ERROR HY000: This command is not supported in the prepared statement protocol yet
|
||||
prepare stmt3 from ' create database mysqltest ';
|
||||
|
|
|
@ -1343,3 +1343,97 @@ c1
|
|||
100
|
||||
drop table t1;
|
||||
set global query_cache_size=0;
|
||||
create table t1 (a int);
|
||||
insert into t1 values (1),(2),(3);
|
||||
set GLOBAL query_cache_type=1;
|
||||
set GLOBAL query_cache_limit=10000;
|
||||
set GLOBAL query_cache_min_res_unit=0;
|
||||
set GLOBAL query_cache_size= 100000;
|
||||
reset query cache;
|
||||
set LOCAL default_week_format = 0;
|
||||
select week('2007-01-04');
|
||||
week('2007-01-04')
|
||||
0
|
||||
select week('2007-01-04') from t1;
|
||||
week('2007-01-04')
|
||||
0
|
||||
0
|
||||
0
|
||||
select extract(WEEK FROM '2007-01-04') from t1;
|
||||
extract(WEEK FROM '2007-01-04')
|
||||
0
|
||||
0
|
||||
0
|
||||
set LOCAL default_week_format = 2;
|
||||
select week('2007-01-04');
|
||||
week('2007-01-04')
|
||||
53
|
||||
select week('2007-01-04') from t1;
|
||||
week('2007-01-04')
|
||||
53
|
||||
53
|
||||
53
|
||||
select extract(WEEK FROM '2007-01-04') from t1;
|
||||
extract(WEEK FROM '2007-01-04')
|
||||
53
|
||||
53
|
||||
53
|
||||
reset query cache;
|
||||
set LOCAL div_precision_increment=2;
|
||||
select 1/7;
|
||||
1/7
|
||||
0.14
|
||||
select 1/7 from t1;
|
||||
1/7
|
||||
0.14
|
||||
0.14
|
||||
0.14
|
||||
set LOCAL div_precision_increment=4;
|
||||
select 1/7;
|
||||
1/7
|
||||
0.1429
|
||||
select 1/7 from t1;
|
||||
1/7
|
||||
0.1429
|
||||
0.1429
|
||||
0.1429
|
||||
drop table t1;
|
||||
CREATE TABLE t1 (a VARCHAR(200), b TEXT, FULLTEXT (a,b));
|
||||
INSERT INTO t1 VALUES('MySQL has now support', 'for full-text search'),
|
||||
('Full-text indexes', 'are called collections'),
|
||||
('Only MyISAM tables','support collections'),
|
||||
('Function MATCH ... AGAINST()','is used to do a search'),
|
||||
('Full-text search in MySQL', 'implements vector space model');
|
||||
set GLOBAL ft_boolean_syntax='+ -><()~*:""&|';
|
||||
select *, MATCH(a,b) AGAINST("+called +collections" IN BOOLEAN MODE) as x from t1;
|
||||
a b x
|
||||
MySQL has now support for full-text search 0
|
||||
Full-text indexes are called collections 1
|
||||
Only MyISAM tables support collections 0
|
||||
Function MATCH ... AGAINST() is used to do a search 0
|
||||
Full-text search in MySQL implements vector space model 0
|
||||
set GLOBAL ft_boolean_syntax='- +><()~*:""&|';
|
||||
select *, MATCH(a,b) AGAINST("+called +collections" IN BOOLEAN MODE) as x from t1;
|
||||
a b x
|
||||
MySQL has now support for full-text search 0
|
||||
Full-text indexes are called collections 0
|
||||
Only MyISAM tables support collections 0
|
||||
Function MATCH ... AGAINST() is used to do a search 0
|
||||
Full-text search in MySQL implements vector space model 0
|
||||
create function change_global() returns integer deterministic
|
||||
begin
|
||||
set global ft_boolean_syntax='+ -><()~*:""&|';
|
||||
return 1;
|
||||
end|
|
||||
select *, change_global() from t1;
|
||||
a b change_global()
|
||||
MySQL has now support for full-text search 1
|
||||
Full-text indexes are called collections 1
|
||||
Only MyISAM tables support collections 1
|
||||
Function MATCH ... AGAINST() is used to do a search 1
|
||||
Full-text search in MySQL implements vector space model 1
|
||||
drop function change_global;
|
||||
set GLOBAL query_cache_type=default;
|
||||
set GLOBAL query_cache_limit=default;
|
||||
set GLOBAL query_cache_min_res_unit=default;
|
||||
set GLOBAL query_cache_size= default;
|
||||
|
|
|
@ -620,6 +620,119 @@ SHOW PROCEDURE CODE p1;
|
|||
Pos Instruction
|
||||
0 stmt 2 "CREATE INDEX idx ON t1 (c1)"
|
||||
DROP PROCEDURE p1;
|
||||
drop table if exists t1;
|
||||
drop procedure if exists proc_26977_broken;
|
||||
drop procedure if exists proc_26977_works;
|
||||
create table t1(a int unique);
|
||||
create procedure proc_26977_broken(v int)
|
||||
begin
|
||||
declare i int default 5;
|
||||
declare continue handler for sqlexception
|
||||
begin
|
||||
select 'caught something';
|
||||
retry:
|
||||
while i > 0 do
|
||||
begin
|
||||
set i = i - 1;
|
||||
select 'looping', i;
|
||||
end;
|
||||
end while retry;
|
||||
end;
|
||||
select 'do something';
|
||||
insert into t1 values (v);
|
||||
select 'do something again';
|
||||
insert into t1 values (v);
|
||||
end//
|
||||
create procedure proc_26977_works(v int)
|
||||
begin
|
||||
declare i int default 5;
|
||||
declare continue handler for sqlexception
|
||||
begin
|
||||
select 'caught something';
|
||||
retry:
|
||||
while i > 0 do
|
||||
begin
|
||||
set i = i - 1;
|
||||
select 'looping', i;
|
||||
end;
|
||||
end while retry;
|
||||
select 'optimizer: keep hreturn';
|
||||
end;
|
||||
select 'do something';
|
||||
insert into t1 values (v);
|
||||
select 'do something again';
|
||||
insert into t1 values (v);
|
||||
end//
|
||||
show procedure code proc_26977_broken;
|
||||
Pos Instruction
|
||||
0 set i@1 5
|
||||
1 hpush_jump 8 2 CONTINUE
|
||||
2 stmt 0 "select 'caught something'"
|
||||
3 jump_if_not 7(7) (i@1 > 0)
|
||||
4 set i@1 (i@1 - 1)
|
||||
5 stmt 0 "select 'looping', i"
|
||||
6 jump 3
|
||||
7 hreturn 2
|
||||
8 stmt 0 "select 'do something'"
|
||||
9 stmt 5 "insert into t1 values (v)"
|
||||
10 stmt 0 "select 'do something again'"
|
||||
11 stmt 5 "insert into t1 values (v)"
|
||||
12 hpop 1
|
||||
show procedure code proc_26977_works;
|
||||
Pos Instruction
|
||||
0 set i@1 5
|
||||
1 hpush_jump 9 2 CONTINUE
|
||||
2 stmt 0 "select 'caught something'"
|
||||
3 jump_if_not 7(7) (i@1 > 0)
|
||||
4 set i@1 (i@1 - 1)
|
||||
5 stmt 0 "select 'looping', i"
|
||||
6 jump 3
|
||||
7 stmt 0 "select 'optimizer: keep hreturn'"
|
||||
8 hreturn 2
|
||||
9 stmt 0 "select 'do something'"
|
||||
10 stmt 5 "insert into t1 values (v)"
|
||||
11 stmt 0 "select 'do something again'"
|
||||
12 stmt 5 "insert into t1 values (v)"
|
||||
13 hpop 1
|
||||
call proc_26977_broken(1);
|
||||
do something
|
||||
do something
|
||||
do something again
|
||||
do something again
|
||||
caught something
|
||||
caught something
|
||||
looping i
|
||||
looping 4
|
||||
looping i
|
||||
looping 3
|
||||
looping i
|
||||
looping 2
|
||||
looping i
|
||||
looping 1
|
||||
looping i
|
||||
looping 0
|
||||
call proc_26977_works(2);
|
||||
do something
|
||||
do something
|
||||
do something again
|
||||
do something again
|
||||
caught something
|
||||
caught something
|
||||
looping i
|
||||
looping 4
|
||||
looping i
|
||||
looping 3
|
||||
looping i
|
||||
looping 2
|
||||
looping i
|
||||
looping 1
|
||||
looping i
|
||||
looping 0
|
||||
optimizer: keep hreturn
|
||||
optimizer: keep hreturn
|
||||
drop table t1;
|
||||
drop procedure proc_26977_broken;
|
||||
drop procedure proc_26977_works;
|
||||
End of 5.0 tests.
|
||||
CREATE PROCEDURE p1()
|
||||
BEGIN
|
||||
|
|
|
@ -6210,6 +6210,13 @@ Warning 1265 Data truncated for column 'bug5274_f1' at row 1
|
|||
Warning 1265 Data truncated for column 'bug5274_f1' at row 1
|
||||
DROP FUNCTION bug5274_f1|
|
||||
DROP FUNCTION bug5274_f2|
|
||||
drop procedure if exists proc_21513|
|
||||
create procedure proc_21513()`my_label`:BEGIN END|
|
||||
show create procedure proc_21513|
|
||||
Procedure sql_mode Create Procedure
|
||||
proc_21513 CREATE DEFINER=`root`@`localhost` PROCEDURE `proc_21513`()
|
||||
`my_label`:BEGIN END
|
||||
drop procedure proc_21513|
|
||||
End of 5.0 tests.
|
||||
drop table t1,t2;
|
||||
CREATE TABLE t1 (a int auto_increment primary key) engine=MyISAM;
|
||||
|
|
|
@ -1414,4 +1414,39 @@ id val
|
|||
DROP TRIGGER trg27006_a_insert;
|
||||
DROP TRIGGER trg27006_a_update;
|
||||
drop table t1,t2;
|
||||
drop table if exists t1, t2, t3;
|
||||
create table t1 (i int);
|
||||
create trigger t1_bi before insert on t1 for each row set new.i = 7;
|
||||
create trigger t1_ai after insert on t1 for each row set @a := 7;
|
||||
create table t2 (j int);
|
||||
insert into t2 values (1), (2);
|
||||
set @a:="";
|
||||
create table if not exists t1 select * from t2;
|
||||
Warnings:
|
||||
Note 1050 Table 't1' already exists
|
||||
select * from t1;
|
||||
i
|
||||
7
|
||||
7
|
||||
select @a;
|
||||
@a
|
||||
7
|
||||
drop trigger t1_bi;
|
||||
drop trigger t1_ai;
|
||||
create table t3 (isave int);
|
||||
create trigger t1_bi before insert on t1 for each row insert into t3 values (new.i);
|
||||
create table if not exists t1 select * from t2;
|
||||
Warnings:
|
||||
Note 1050 Table 't1' already exists
|
||||
select * from t1;
|
||||
i
|
||||
7
|
||||
7
|
||||
1
|
||||
2
|
||||
select * from t3;
|
||||
isave
|
||||
1
|
||||
2
|
||||
drop table t1, t2, t3;
|
||||
End of 5.0 tests
|
||||
|
|
|
@ -673,6 +673,117 @@ alter table t1 max_rows=100000000000;
|
|||
show create table t1;
|
||||
drop table t1;
|
||||
|
||||
|
||||
#
|
||||
# Tests for errors happening at various stages of CREATE TABLES ... SELECT
|
||||
#
|
||||
# (Also checks that it behaves atomically in the sense that in case
|
||||
# of error it is automatically dropped if it has not existed before.)
|
||||
#
|
||||
# Error during open_and_lock_tables() of tables
|
||||
--error ER_NO_SUCH_TABLE
|
||||
create table t1 select * from t2;
|
||||
# Rather special error which also caught during open tables pahse
|
||||
--error ER_UPDATE_TABLE_USED
|
||||
create table t1 select * from t1;
|
||||
# Error which happens before select_create::prepare()
|
||||
--error ER_CANT_AGGREGATE_2COLLATIONS
|
||||
create table t1 select coalesce('a' collate latin1_swedish_ci,'b' collate latin1_bin);
|
||||
# Error during table creation
|
||||
--error ER_KEY_COLUMN_DOES_NOT_EXITS
|
||||
create table t1 (primary key(a)) select "b" as b;
|
||||
# Error in select_create::prepare() which is not related to table creation
|
||||
create table t1 (a int);
|
||||
--error ER_WRONG_VALUE_COUNT_ON_ROW
|
||||
create table if not exists t1 select 1 as a, 2 as b;
|
||||
drop table t1;
|
||||
# Finally error which happens during insert
|
||||
--error ER_DUP_ENTRY_WITH_KEY_NAME
|
||||
create table t1 (primary key (a)) (select 1 as a) union all (select 1 as a);
|
||||
# What happens if table already exists ?
|
||||
create table t1 (i int);
|
||||
--error ER_TABLE_EXISTS_ERROR
|
||||
create table t1 select 1 as i;
|
||||
create table if not exists t1 select 1 as i;
|
||||
select * from t1;
|
||||
# Error before select_create::prepare()
|
||||
--error ER_CANT_AGGREGATE_2COLLATIONS
|
||||
create table t1 select coalesce('a' collate latin1_swedish_ci,'b' collate latin1_bin);
|
||||
select * from t1;
|
||||
# Error which happens during insertion of rows
|
||||
alter table t1 add primary key (i);
|
||||
--error ER_DUP_ENTRY_WITH_KEY_NAME
|
||||
create table if not exists t1 (select 2 as i) union all (select 2 as i);
|
||||
select * from t1;
|
||||
drop table t1;
|
||||
|
||||
|
||||
# Base vs temporary tables dillema (a.k.a. bug#24508 "Inconsistent
|
||||
# results of CREATE TABLE ... SELECT when temporary table exists").
|
||||
# In this situation we either have to create non-temporary table and
|
||||
# insert data in it or insert data in temporary table without creation
|
||||
# of permanent table. Since currently temporary tables always shadow
|
||||
# permanent tables we adopt second approach.
|
||||
create temporary table t1 (j int);
|
||||
create table if not exists t1 select 1;
|
||||
select * from t1;
|
||||
drop temporary table t1;
|
||||
--error ER_NO_SUCH_TABLE
|
||||
select * from t1;
|
||||
--error ER_BAD_TABLE_ERROR
|
||||
drop table t1;
|
||||
|
||||
|
||||
#
|
||||
# CREATE TABLE ... SELECT and LOCK TABLES
|
||||
#
|
||||
# There is little sense in using CREATE TABLE ... SELECT under
|
||||
# LOCK TABLES as it mostly does not work. At least we check that
|
||||
# the server doesn't crash, hang and produces sensible errors.
|
||||
# Includes test for bug #20662 "Infinite loop in CREATE TABLE
|
||||
# IF NOT EXISTS ... SELECT with locked tables".
|
||||
create table t1 (i int);
|
||||
insert into t1 values (1), (2);
|
||||
lock tables t1 read;
|
||||
--error ER_TABLE_NOT_LOCKED
|
||||
create table t2 select * from t1;
|
||||
--error ER_TABLE_NOT_LOCKED
|
||||
create table if not exists t2 select * from t1;
|
||||
unlock tables;
|
||||
create table t2 (j int);
|
||||
lock tables t1 read;
|
||||
--error ER_TABLE_NOT_LOCKED
|
||||
create table t2 select * from t1;
|
||||
# This should not be ever allowed as it will undermine
|
||||
# lock-all-at-once approach
|
||||
--error ER_TABLE_NOT_LOCKED
|
||||
create table if not exists t2 select * from t1;
|
||||
unlock tables;
|
||||
lock table t1 read, t2 read;
|
||||
--error ER_TABLE_NOT_LOCKED_FOR_WRITE
|
||||
create table t2 select * from t1;
|
||||
--error ER_TABLE_NOT_LOCKED_FOR_WRITE
|
||||
create table if not exists t2 select * from t1;
|
||||
unlock tables;
|
||||
lock table t1 read, t2 write;
|
||||
--error ER_TABLE_EXISTS_ERROR
|
||||
create table t2 select * from t1;
|
||||
# This is the only case which really works.
|
||||
create table if not exists t2 select * from t1;
|
||||
select * from t1;
|
||||
unlock tables;
|
||||
drop table t2;
|
||||
|
||||
# OTOH CREATE TEMPORARY TABLE ... SELECT should work
|
||||
# well under LOCK TABLES.
|
||||
lock tables t1 read;
|
||||
create temporary table t2 select * from t1;
|
||||
create temporary table if not exists t2 select * from t1;
|
||||
select * from t2;
|
||||
unlock tables;
|
||||
drop table t1, t2;
|
||||
|
||||
|
||||
#
|
||||
# Bug#21772: can not name a column 'upgrade' when create a table
|
||||
#
|
||||
|
@ -715,6 +826,7 @@ INSERT INTO t2 select * from t1;
|
|||
SELECT * from t2;
|
||||
drop table t1,t2;
|
||||
|
||||
|
||||
#
|
||||
# Test incorrect database names
|
||||
#
|
||||
|
|
268
mysql-test/t/create_select-big.test
Normal file
268
mysql-test/t/create_select-big.test
Normal file
|
@ -0,0 +1,268 @@
|
|||
# Tests for various aspects of CREATE TABLE ... SELECT implementation
|
||||
#
|
||||
# Note that we don't test general CREATE TABLE ... SELECT functionality here as
|
||||
# it is already covered by create.test. We are more interested in extreme cases.
|
||||
#
|
||||
# This test takes rather long time so let us run it only in --big-test mode
|
||||
--source include/big_test.inc
|
||||
# We are using some debug-only features in this test
|
||||
--source include/have_debug.inc
|
||||
|
||||
# Create auxilliary connections
|
||||
connect (addconroot1, localhost, root,,);
|
||||
connect (addconroot2, localhost, root,,);
|
||||
connect (addconroot3, localhost, root,,);
|
||||
connection default;
|
||||
|
||||
--disable_warnings
|
||||
drop table if exists t1,t2,t3,t4,t5;
|
||||
--enable_warnings
|
||||
|
||||
|
||||
#
|
||||
# Tests for concurrency problems.
|
||||
#
|
||||
# We introduce delays between various stages of table creation
|
||||
# and check that other statements dealing with this table cannot
|
||||
# interfere during those delays.
|
||||
#
|
||||
# What happens in situation when other statement messes with
|
||||
# table to be created before it is created ?
|
||||
# Concurrent CREATE TABLE
|
||||
set session debug="+d,sleep_create_select_before_create";
|
||||
--send create table t1 select 1 as i;
|
||||
connection addconroot1;
|
||||
--sleep 2
|
||||
--error ER_TABLE_EXISTS_ERROR
|
||||
create table t1 (j char(5));
|
||||
connection default;
|
||||
--reap
|
||||
show create table t1;
|
||||
drop table t1;
|
||||
# Concurrent CREATE TABLE ... SELECT
|
||||
--send create table t1 select 1 as i;
|
||||
connection addconroot1;
|
||||
--sleep 2
|
||||
--error ER_TABLE_EXISTS_ERROR
|
||||
create table t1 select "Test" as j;
|
||||
connection default;
|
||||
--reap
|
||||
show create table t1;
|
||||
drop table t1;
|
||||
# Concurrent CREATE TABLE LIKE
|
||||
create table t3 (j char(5));
|
||||
--send create table t1 select 1 as i;
|
||||
connection addconroot1;
|
||||
--sleep 2
|
||||
--error ER_TABLE_EXISTS_ERROR
|
||||
create table t1 like t3;
|
||||
connection default;
|
||||
--reap
|
||||
show create table t1;
|
||||
drop table t1;
|
||||
# Concurrent RENAME TABLE
|
||||
--send create table t1 select 1 as i;
|
||||
connection addconroot1;
|
||||
--sleep 2
|
||||
--error ER_TABLE_EXISTS_ERROR
|
||||
rename table t3 to t1;
|
||||
connection default;
|
||||
--reap
|
||||
show create table t1;
|
||||
drop table t1;
|
||||
# Concurrent ALTER TABLE RENAME
|
||||
--send create table t1 select 1 as i;
|
||||
connection addconroot1;
|
||||
--sleep 2
|
||||
--error ER_TABLE_EXISTS_ERROR
|
||||
alter table t3 rename to t1;
|
||||
connection default;
|
||||
--reap
|
||||
show create table t1;
|
||||
drop table t1;
|
||||
# Concurrent ALTER TABLE RENAME which also adds column
|
||||
--send create table t1 select 1 as i;
|
||||
connection addconroot1;
|
||||
--sleep 2
|
||||
--error ER_TABLE_EXISTS_ERROR
|
||||
alter table t3 rename to t1, add k int;
|
||||
connection default;
|
||||
--reap
|
||||
show create table t1;
|
||||
drop table t1, t3;
|
||||
# What happens if other statement sneaks in after the table
|
||||
# creation but before its opening ?
|
||||
set session debug="-d,sleep_create_select_before_create:+d,sleep_create_select_before_open";
|
||||
# Concurrent DROP TABLE
|
||||
--send create table t1 select 1 as i;
|
||||
connection addconroot1;
|
||||
--sleep 2
|
||||
drop table t1;
|
||||
connection default;
|
||||
--reap
|
||||
# Concurrent RENAME TABLE
|
||||
--send create table t1 select 1 as i;
|
||||
connection addconroot1;
|
||||
--sleep 2
|
||||
rename table t1 to t2;
|
||||
connection default;
|
||||
--reap
|
||||
drop table t2;
|
||||
# Concurrent SELECT
|
||||
--send create table t1 select 1 as i;
|
||||
connection addconroot1;
|
||||
--sleep 2
|
||||
select * from t1;
|
||||
connection default;
|
||||
--reap
|
||||
drop table t1;
|
||||
# Concurrent INSERT
|
||||
--send create table t1 select 1 as i;
|
||||
connection addconroot1;
|
||||
--sleep 2
|
||||
insert into t1 values (2);
|
||||
connection default;
|
||||
--reap
|
||||
select * from t1;
|
||||
drop table t1;
|
||||
# Concurrent CREATE TRIGGER
|
||||
set @a:=0;
|
||||
--send create table t1 select 1 as i;
|
||||
connection addconroot1;
|
||||
--sleep 2
|
||||
create trigger t1_bi before insert on t1 for each row set @a:=1;
|
||||
connection default;
|
||||
--reap
|
||||
select @a;
|
||||
drop table t1;
|
||||
# Okay, now the same tests for the potential gap between open and lock
|
||||
set session debug="-d,sleep_create_select_before_open:+d,sleep_create_select_before_lock";
|
||||
# Concurrent DROP TABLE
|
||||
--send create table t1 select 1 as i;
|
||||
connection addconroot1;
|
||||
--sleep 2
|
||||
drop table t1;
|
||||
connection default;
|
||||
--reap
|
||||
# Concurrent RENAME TABLE
|
||||
--send create table t1 select 1 as i;
|
||||
connection addconroot1;
|
||||
--sleep 2
|
||||
rename table t1 to t2;
|
||||
connection default;
|
||||
--reap
|
||||
drop table t2;
|
||||
# Concurrent SELECT
|
||||
--send create table t1 select 1 as i;
|
||||
connection addconroot1;
|
||||
--sleep 2
|
||||
select * from t1;
|
||||
connection default;
|
||||
--reap
|
||||
drop table t1;
|
||||
# Concurrent INSERT
|
||||
--send create table t1 select 1 as i;
|
||||
connection addconroot1;
|
||||
--sleep 2
|
||||
insert into t1 values (2);
|
||||
connection default;
|
||||
--reap
|
||||
select * from t1;
|
||||
drop table t1;
|
||||
# Concurrent CREATE TRIGGER
|
||||
set @a:=0;
|
||||
--send create table t1 select 1 as i;
|
||||
connection addconroot1;
|
||||
--sleep 2
|
||||
create trigger t1_bi before insert on t1 for each row set @a:=1;
|
||||
connection default;
|
||||
--reap
|
||||
select @a;
|
||||
drop table t1;
|
||||
# Some tests for case with existing table
|
||||
set session debug="-d,sleep_create_select_before_lock:+d,sleep_create_select_before_check_if_exists";
|
||||
create table t1 (i int);
|
||||
# Concurrent DROP TABLE
|
||||
--send create table if not exists t1 select 1 as i;
|
||||
connection addconroot1;
|
||||
--sleep 2
|
||||
drop table t1;
|
||||
connection default;
|
||||
--reap
|
||||
# Concurrent CREATE TRIGGER
|
||||
create table t1 (i int);
|
||||
set @a:=0;
|
||||
--send create table if not exists t1 select 1 as i;
|
||||
connection addconroot1;
|
||||
--sleep 2
|
||||
create trigger t1_bi before insert on t1 for each row set @a:=1;
|
||||
connection default;
|
||||
--reap
|
||||
select @a;
|
||||
select * from t1;
|
||||
drop table t1;
|
||||
set session debug="-d,sleep_create_select_before_check_if_exists";
|
||||
|
||||
|
||||
# Test for some details of CREATE TABLE ... SELECT implementation.
|
||||
#
|
||||
# We check that create placeholder is handled properly if we have
|
||||
# to reopen tables in open_tables().
|
||||
# This test heavily relies on current implementation of name-locking/
|
||||
# table cache so it may stop working if it changes. OTOH it such problem
|
||||
# will serve as warning that such changes should not be done lightly.
|
||||
create table t2 (a int);
|
||||
create table t4 (b int);
|
||||
connection addconroot2;
|
||||
lock table t4 write;
|
||||
select 1;
|
||||
connection addconroot1;
|
||||
# Create placeholder/name-lock for t3
|
||||
--send create table t3 as select * from t4;
|
||||
--sleep 2
|
||||
connection default;
|
||||
# This statement creates placeholder for t1, then opens t2,
|
||||
# then meets name-lock for t3 and then reopens all tables
|
||||
--send create table t1 select * from t2, t3;
|
||||
--sleep 2
|
||||
connection addconroot2;
|
||||
unlock tables;
|
||||
connection addconroot1;
|
||||
--reap
|
||||
connection default;
|
||||
--reap
|
||||
select * from t1;
|
||||
show create table t1;
|
||||
drop table t1, t3;
|
||||
# Now similar test which proves that we really temporarily
|
||||
# remove placeholder when we reopen tables.
|
||||
connection addconroot2;
|
||||
lock table t4 read;
|
||||
select 1;
|
||||
connection addconroot1;
|
||||
# Create name-lock for t3
|
||||
--send rename table t4 to t3;
|
||||
--sleep 2
|
||||
connection default;
|
||||
# This statement creates placeholder for t1, then opens t2,
|
||||
# then meets name-lock for t3 and then reopens all tables
|
||||
--send create table if not exists t1 select 1 as i from t2, t3;
|
||||
--sleep 2
|
||||
connection addconroot3;
|
||||
# We should be able to take name-lock on table t1 as we should not have
|
||||
# open placeholder for it at this point (otherwise it is possible to
|
||||
# come-up with situation which will lead to deadlock, e.g. think of
|
||||
# concurrent CREATE TABLE t1 SELECT * FROM t2 and RENAME TABLE t2 TO t1)
|
||||
create table t5 (j int);
|
||||
# This statement takes name-lock on t1 and therefore proves
|
||||
# that there is no active open placeholder for it.
|
||||
rename table t5 to t1;
|
||||
connection addconroot2;
|
||||
unlock tables;
|
||||
connection addconroot1;
|
||||
--reap
|
||||
connection default;
|
||||
--reap
|
||||
select * from t1;
|
||||
show create table t1;
|
||||
drop table t1, t2, t3;
|
|
@ -11,7 +11,9 @@
|
|||
##############################################################################
|
||||
user_limits : Bug#23921 random failure of user_limits.test
|
||||
|
||||
im_options : Bug#20294 2006-07-24 stewart Instance manager test im_options fails randomly
|
||||
im_options : Bug#20294 2006-07-24 stewart Instance manager test im_options fails randomly
|
||||
im_daemon_life_cycle : Bug#20294 2007-05-14 alik Instance manager tests fail randomly
|
||||
im_cmd_line : Bug#20294 2007-05-14 alik Instance manager tests fail randomly
|
||||
im_life_cycle : BUG#27851 Instance manager dies on ASSERT in ~Thread_registry() or from not being able to close a mysqld instance.
|
||||
concurrent_innodb : BUG#21579 2006-08-11 mleich innodb_concurrent random failures with varying differences
|
||||
ndb_autodiscover : BUG#18952 2006-02-16 jmiller Needs to be fixed w.r.t binlog
|
||||
|
|
|
@ -34,13 +34,13 @@ deallocate prepare no_such_statement;
|
|||
execute stmt1;
|
||||
|
||||
# Nesting ps commands is not allowed:
|
||||
--error 1064
|
||||
--error ER_UNSUPPORTED_PS
|
||||
prepare stmt2 from 'prepare nested_stmt from "select 1"';
|
||||
|
||||
--error 1064
|
||||
--error ER_UNSUPPORTED_PS
|
||||
prepare stmt2 from 'execute stmt1';
|
||||
|
||||
--error 1064
|
||||
--error ER_UNSUPPORTED_PS
|
||||
prepare stmt2 from 'deallocate prepare z';
|
||||
|
||||
# PS insert
|
||||
|
|
|
@ -407,11 +407,11 @@ deallocate prepare stmt_do ;
|
|||
deallocate prepare stmt_set ;
|
||||
|
||||
## nonsense like prepare of prepare,execute or deallocate
|
||||
--error 1064
|
||||
--error ER_UNSUPPORTED_PS
|
||||
prepare stmt1 from ' prepare stmt2 from '' select 1 '' ' ;
|
||||
--error 1064
|
||||
--error ER_UNSUPPORTED_PS
|
||||
prepare stmt1 from ' execute stmt2 ' ;
|
||||
--error 1064
|
||||
--error ER_UNSUPPORTED_PS
|
||||
prepare stmt1 from ' deallocate prepare never_prepared ' ;
|
||||
|
||||
## switch the database connection
|
||||
|
|
|
@ -928,3 +928,75 @@ select * from t1;
|
|||
drop table t1;
|
||||
|
||||
set global query_cache_size=0;
|
||||
|
||||
#
|
||||
# Query cache and changes to system variables
|
||||
#
|
||||
|
||||
create table t1 (a int);
|
||||
insert into t1 values (1),(2),(3);
|
||||
set GLOBAL query_cache_type=1;
|
||||
set GLOBAL query_cache_limit=10000;
|
||||
set GLOBAL query_cache_min_res_unit=0;
|
||||
set GLOBAL query_cache_size= 100000;
|
||||
|
||||
# default_week_format
|
||||
reset query cache;
|
||||
set LOCAL default_week_format = 0;
|
||||
select week('2007-01-04');
|
||||
select week('2007-01-04') from t1;
|
||||
select extract(WEEK FROM '2007-01-04') from t1;
|
||||
|
||||
set LOCAL default_week_format = 2;
|
||||
select week('2007-01-04');
|
||||
select week('2007-01-04') from t1;
|
||||
select extract(WEEK FROM '2007-01-04') from t1;
|
||||
|
||||
# div_precision_increment
|
||||
reset query cache;
|
||||
set LOCAL div_precision_increment=2;
|
||||
select 1/7;
|
||||
select 1/7 from t1;
|
||||
|
||||
set LOCAL div_precision_increment=4;
|
||||
select 1/7;
|
||||
select 1/7 from t1;
|
||||
|
||||
drop table t1;
|
||||
|
||||
CREATE TABLE t1 (a VARCHAR(200), b TEXT, FULLTEXT (a,b));
|
||||
INSERT INTO t1 VALUES('MySQL has now support', 'for full-text search'),
|
||||
('Full-text indexes', 'are called collections'),
|
||||
('Only MyISAM tables','support collections'),
|
||||
('Function MATCH ... AGAINST()','is used to do a search'),
|
||||
('Full-text search in MySQL', 'implements vector space model');
|
||||
|
||||
|
||||
set GLOBAL ft_boolean_syntax='+ -><()~*:""&|';
|
||||
|
||||
select *, MATCH(a,b) AGAINST("+called +collections" IN BOOLEAN MODE) as x from t1;
|
||||
|
||||
# swap +/-
|
||||
set GLOBAL ft_boolean_syntax='- +><()~*:""&|';
|
||||
|
||||
select *, MATCH(a,b) AGAINST("+called +collections" IN BOOLEAN MODE) as x from t1;
|
||||
|
||||
# If in the future we need to cache queries with functions
|
||||
# be sure not to cause dead lock if the query cache is flushed
|
||||
# while inserting a query in the query cache.
|
||||
delimiter |;
|
||||
create function change_global() returns integer deterministic
|
||||
begin
|
||||
set global ft_boolean_syntax='+ -><()~*:""&|';
|
||||
return 1;
|
||||
end|
|
||||
delimiter ;|
|
||||
select *, change_global() from t1;
|
||||
drop function change_global;
|
||||
|
||||
set GLOBAL query_cache_type=default;
|
||||
set GLOBAL query_cache_limit=default;
|
||||
set GLOBAL query_cache_min_res_unit=default;
|
||||
set GLOBAL query_cache_size= default;
|
||||
|
||||
# End of 5.0 tests
|
||||
|
|
|
@ -446,6 +446,81 @@ SHOW PROCEDURE CODE p1;
|
|||
DROP PROCEDURE p1;
|
||||
|
||||
|
||||
#
|
||||
# Bug#26977 exception handlers never hreturn
|
||||
#
|
||||
--disable_warnings
|
||||
drop table if exists t1;
|
||||
drop procedure if exists proc_26977_broken;
|
||||
drop procedure if exists proc_26977_works;
|
||||
--enable_warnings
|
||||
|
||||
create table t1(a int unique);
|
||||
|
||||
delimiter //;
|
||||
|
||||
create procedure proc_26977_broken(v int)
|
||||
begin
|
||||
declare i int default 5;
|
||||
|
||||
declare continue handler for sqlexception
|
||||
begin
|
||||
select 'caught something';
|
||||
retry:
|
||||
while i > 0 do
|
||||
begin
|
||||
set i = i - 1;
|
||||
select 'looping', i;
|
||||
end;
|
||||
end while retry;
|
||||
end;
|
||||
|
||||
select 'do something';
|
||||
insert into t1 values (v);
|
||||
select 'do something again';
|
||||
insert into t1 values (v);
|
||||
end//
|
||||
|
||||
create procedure proc_26977_works(v int)
|
||||
begin
|
||||
declare i int default 5;
|
||||
|
||||
declare continue handler for sqlexception
|
||||
begin
|
||||
select 'caught something';
|
||||
retry:
|
||||
while i > 0 do
|
||||
begin
|
||||
set i = i - 1;
|
||||
select 'looping', i;
|
||||
end;
|
||||
end while retry;
|
||||
select 'optimizer: keep hreturn';
|
||||
end;
|
||||
|
||||
select 'do something';
|
||||
insert into t1 values (v);
|
||||
select 'do something again';
|
||||
insert into t1 values (v);
|
||||
end//
|
||||
delimiter ;//
|
||||
|
||||
show procedure code proc_26977_broken;
|
||||
|
||||
show procedure code proc_26977_works;
|
||||
|
||||
## This caust an error because of jump short cut
|
||||
## optimization.
|
||||
call proc_26977_broken(1);
|
||||
|
||||
## This works
|
||||
call proc_26977_works(2);
|
||||
|
||||
drop table t1;
|
||||
drop procedure proc_26977_broken;
|
||||
drop procedure proc_26977_works;
|
||||
|
||||
|
||||
--echo End of 5.0 tests.
|
||||
|
||||
#
|
||||
|
|
|
@ -7146,6 +7146,17 @@ SELECT bug5274_f2()|
|
|||
DROP FUNCTION bug5274_f1|
|
||||
DROP FUNCTION bug5274_f2|
|
||||
|
||||
#
|
||||
# Bug#21513 (SP having body starting with quoted label rendered unusable)
|
||||
#
|
||||
--disable_warnings
|
||||
drop procedure if exists proc_21513|
|
||||
--enable_warnings
|
||||
|
||||
create procedure proc_21513()`my_label`:BEGIN END|
|
||||
show create procedure proc_21513|
|
||||
|
||||
drop procedure proc_21513|
|
||||
|
||||
###
|
||||
--echo End of 5.0 tests.
|
||||
|
|
|
@ -1737,4 +1737,30 @@ DROP TRIGGER trg27006_a_insert;
|
|||
DROP TRIGGER trg27006_a_update;
|
||||
drop table t1,t2;
|
||||
|
||||
#
|
||||
# Bug #20903 "Crash when using CREATE TABLE .. SELECT and triggers"
|
||||
#
|
||||
|
||||
--disable_warnings
|
||||
drop table if exists t1, t2, t3;
|
||||
--enable_warnings
|
||||
create table t1 (i int);
|
||||
create trigger t1_bi before insert on t1 for each row set new.i = 7;
|
||||
create trigger t1_ai after insert on t1 for each row set @a := 7;
|
||||
create table t2 (j int);
|
||||
insert into t2 values (1), (2);
|
||||
set @a:="";
|
||||
create table if not exists t1 select * from t2;
|
||||
select * from t1;
|
||||
select @a;
|
||||
# Let us check that trigger that involves table also works ok.
|
||||
drop trigger t1_bi;
|
||||
drop trigger t1_ai;
|
||||
create table t3 (isave int);
|
||||
create trigger t1_bi before insert on t1 for each row insert into t3 values (new.i);
|
||||
create table if not exists t1 select * from t2;
|
||||
select * from t1;
|
||||
select * from t3;
|
||||
drop table t1, t2, t3;
|
||||
|
||||
--echo End of 5.0 tests
|
||||
|
|
|
@ -156,10 +156,14 @@ void
|
|||
Event_parse_data::init_body(THD *thd)
|
||||
{
|
||||
DBUG_ENTER("Event_parse_data::init_body");
|
||||
DBUG_PRINT("info", ("body: '%s' body_begin: 0x%lx end: 0x%lx", body_begin,
|
||||
(long) body_begin, (long) thd->lex->ptr));
|
||||
|
||||
body.length= thd->lex->ptr - body_begin;
|
||||
/* This method is called from within the parser, from sql_yacc.yy */
|
||||
DBUG_ASSERT(thd->m_lip != NULL);
|
||||
|
||||
DBUG_PRINT("info", ("body: '%s' body_begin: 0x%lx end: 0x%lx", body_begin,
|
||||
(long) body_begin, (long) thd->m_lip->ptr));
|
||||
|
||||
body.length= thd->m_lip->ptr - body_begin;
|
||||
const char *body_end= body_begin + body.length - 1;
|
||||
|
||||
/* Trim nuls or close-comments ('*'+'/') or spaces at the end */
|
||||
|
@ -1912,15 +1916,20 @@ Event_job_data::execute(THD *thd, bool drop)
|
|||
thd->query= sp_sql.c_ptr_safe();
|
||||
thd->query_length= sp_sql.length();
|
||||
|
||||
lex_start(thd, thd->query, thd->query_length);
|
||||
|
||||
if (MYSQLparse(thd) || thd->is_fatal_error)
|
||||
{
|
||||
sql_print_error("Event Scheduler: "
|
||||
"%serror during compilation of %s.%s",
|
||||
thd->is_fatal_error ? "fatal " : "",
|
||||
(const char *) dbname.str, (const char *) name.str);
|
||||
goto end;
|
||||
Lex_input_stream lip(thd, thd->query, thd->query_length);
|
||||
thd->m_lip= &lip;
|
||||
lex_start(thd);
|
||||
int err= MYSQLparse(thd);
|
||||
|
||||
if (err || thd->is_fatal_error)
|
||||
{
|
||||
sql_print_error("Event Scheduler: "
|
||||
"%serror during compilation of %s.%s",
|
||||
thd->is_fatal_error ? "fatal " : "",
|
||||
(const char *) dbname.str, (const char *) name.str);
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
|
|
|
@ -6674,7 +6674,7 @@ int ndb_create_table_from_engine(THD *thd, const char *db,
|
|||
LEX *old_lex= thd->lex, newlex;
|
||||
thd->lex= &newlex;
|
||||
newlex.current_select= NULL;
|
||||
lex_start(thd, "", 0);
|
||||
lex_start(thd);
|
||||
int res= ha_create_table_from_engine(thd, db, table_name);
|
||||
thd->lex= old_lex;
|
||||
return res;
|
||||
|
|
|
@ -230,6 +230,7 @@ static void run_query(THD *thd, char *buf, char *end,
|
|||
ulonglong save_thd_options= thd->options;
|
||||
DBUG_ASSERT(sizeof(save_thd_options) == sizeof(thd->options));
|
||||
NET save_net= thd->net;
|
||||
const char* found_semicolon= NULL;
|
||||
|
||||
bzero((char*) &thd->net, sizeof(NET));
|
||||
thd->query_length= end - buf;
|
||||
|
@ -239,7 +240,7 @@ static void run_query(THD *thd, char *buf, char *end,
|
|||
thd->options&= ~OPTION_BIN_LOG;
|
||||
|
||||
DBUG_PRINT("query", ("%s", thd->query));
|
||||
mysql_parse(thd, thd->query, thd->query_length);
|
||||
mysql_parse(thd, thd->query, thd->query_length, &found_semicolon);
|
||||
|
||||
if (print_error && thd->query_error)
|
||||
{
|
||||
|
|
|
@ -4731,7 +4731,6 @@ inline uint char_val(char X)
|
|||
|
||||
Item_hex_string::Item_hex_string(const char *str, uint str_length)
|
||||
{
|
||||
name=(char*) str-2; // Lex makes this start with 0x
|
||||
max_length=(str_length+1)/2;
|
||||
char *ptr=(char*) sql_alloc(max_length+1);
|
||||
if (!ptr)
|
||||
|
@ -4842,7 +4841,6 @@ Item_bin_string::Item_bin_string(const char *str, uint str_length)
|
|||
uchar bits= 0;
|
||||
uint power= 1;
|
||||
|
||||
name= (char*) str - 2;
|
||||
max_length= (str_length + 7) >> 3;
|
||||
char *ptr= (char*) sql_alloc(max_length + 1);
|
||||
if (!ptr)
|
||||
|
|
|
@ -4419,7 +4419,7 @@ int get_var_with_binlog(THD *thd, enum_sql_command sql_command,
|
|||
List<set_var_base> tmp_var_list;
|
||||
LEX *sav_lex= thd->lex, lex_tmp;
|
||||
thd->lex= &lex_tmp;
|
||||
lex_start(thd, NULL, 0);
|
||||
lex_start(thd);
|
||||
tmp_var_list.push_back(new set_var_user(new Item_func_set_user_var(name,
|
||||
new Item_null())));
|
||||
/* Create the variable */
|
||||
|
|
26
sql/lock.cc
26
sql/lock.cc
|
@ -892,8 +892,6 @@ end:
|
|||
int lock_table_name(THD *thd, TABLE_LIST *table_list, bool check_in_use)
|
||||
{
|
||||
TABLE *table;
|
||||
TABLE_SHARE *share;
|
||||
char *key_buff;
|
||||
char key[MAX_DBKEY_LENGTH];
|
||||
char *db= table_list->db;
|
||||
uint key_length;
|
||||
|
@ -921,29 +919,11 @@ int lock_table_name(THD *thd, TABLE_LIST *table_list, bool check_in_use)
|
|||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
Create a table entry with the right key and with an old refresh version
|
||||
Note that we must use my_multi_malloc() here as this is freed by the
|
||||
table cache
|
||||
*/
|
||||
if (!my_multi_malloc(MYF(MY_WME | MY_ZEROFILL),
|
||||
&table, sizeof(*table),
|
||||
&share, sizeof(*share),
|
||||
&key_buff, key_length,
|
||||
NULL))
|
||||
DBUG_RETURN(-1);
|
||||
table->s= share;
|
||||
share->set_table_cache_key(key_buff, key, key_length);
|
||||
share->tmp_table= INTERNAL_TMP_TABLE; // for intern_close_table
|
||||
table->in_use= thd;
|
||||
table->locked_by_name=1;
|
||||
table_list->table=table;
|
||||
|
||||
if (my_hash_insert(&open_cache, (byte*) table))
|
||||
{
|
||||
my_free((gptr) table,MYF(0));
|
||||
if (!(table= table_cache_insert_placeholder(thd, key, key_length)))
|
||||
DBUG_RETURN(-1);
|
||||
}
|
||||
|
||||
table_list->table=table;
|
||||
|
||||
/* Return 1 if table is in use */
|
||||
DBUG_RETURN(test(remove_table_from_cache(thd, db, table_list->table_name,
|
||||
|
|
|
@ -2053,7 +2053,8 @@ int Query_log_event::do_apply_event(RELAY_LOG_INFO const *rli,
|
|||
thd->variables.collation_database= thd->db_charset;
|
||||
|
||||
/* Execute the query (note that we bypass dispatch_command()) */
|
||||
mysql_parse(thd, thd->query, thd->query_length);
|
||||
const char* found_semicolon= NULL;
|
||||
mysql_parse(thd, thd->query, thd->query_length, &found_semicolon);
|
||||
|
||||
}
|
||||
else
|
||||
|
@ -3224,10 +3225,12 @@ int Load_log_event::do_apply_event(NET* net, RELAY_LOG_INFO const *rli,
|
|||
/* see Query_log_event::do_apply_event() and BUG#13360 */
|
||||
DBUG_ASSERT(!rli->m_table_map.count());
|
||||
/*
|
||||
Usually mysql_init_query() is called by mysql_parse(), but we need it here
|
||||
Usually lex_start() is called by mysql_parse(), but we need it here
|
||||
as the present method does not call mysql_parse().
|
||||
*/
|
||||
mysql_init_query(thd, 0, 0);
|
||||
lex_start(thd);
|
||||
mysql_reset_thd_for_next_command(thd);
|
||||
|
||||
if (!use_rli_only_for_errors)
|
||||
{
|
||||
/*
|
||||
|
@ -5860,7 +5863,7 @@ int Rows_log_event::do_apply_event(RELAY_LOG_INFO const *rli)
|
|||
Table_map_log_event::do_apply_event() we don't call
|
||||
mysql_init_query() as that may reset the binlog format.
|
||||
*/
|
||||
lex_start(thd, NULL, 0);
|
||||
lex_start(thd);
|
||||
|
||||
while ((error= lock_tables(thd, rli->tables_to_lock,
|
||||
rli->tables_to_lock_count, &need_reopen)))
|
||||
|
@ -6510,7 +6513,8 @@ int Table_map_log_event::do_apply_event(RELAY_LOG_INFO const *rli)
|
|||
initialized, so we should call lex_start(); to be even safer, we
|
||||
call mysql_init_query() which does a more complete set of inits.
|
||||
*/
|
||||
mysql_init_query(thd, NULL, 0);
|
||||
lex_start(thd);
|
||||
mysql_reset_thd_for_next_command(thd);
|
||||
/*
|
||||
Check if the slave is set to use SBR. If so, it should switch
|
||||
to using RBR until the end of the "statement", i.e., next
|
||||
|
|
|
@ -652,6 +652,8 @@ struct Query_cache_query_flags
|
|||
ulong sql_mode;
|
||||
ulong max_sort_length;
|
||||
ulong group_concat_max_len;
|
||||
ulong default_week_format;
|
||||
ulong div_precision_increment;
|
||||
MY_LOCALE *lc_time_names;
|
||||
};
|
||||
#define QUERY_CACHE_FLAGS_SIZE sizeof(Query_cache_query_flags)
|
||||
|
@ -787,8 +789,6 @@ check_and_unset_inject_value(int value)
|
|||
|
||||
#endif
|
||||
|
||||
uint build_table_path(char *buff, size_t bufflen, const char *db,
|
||||
const char *table, const char *ext);
|
||||
void write_bin_log(THD *thd, bool clear_error,
|
||||
char const *query, ulong query_length);
|
||||
|
||||
|
@ -831,13 +831,15 @@ bool do_rename(THD *thd, TABLE_LIST *ren_table, char *new_db,
|
|||
bool skip_error);
|
||||
bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name,
|
||||
bool force_switch);
|
||||
void mysql_parse(THD *thd,char *inBuf,uint length);
|
||||
|
||||
void mysql_parse(THD *thd, const char *inBuf, uint length,
|
||||
const char ** semicolon);
|
||||
|
||||
bool mysql_test_parse_for_slave(THD *thd,char *inBuf,uint length);
|
||||
bool is_update_query(enum enum_sql_command command);
|
||||
bool alloc_query(THD *thd, const char *packet, uint packet_length);
|
||||
void mysql_init_select(LEX *lex);
|
||||
void mysql_reset_thd_for_next_command(THD *thd);
|
||||
void mysql_init_query(THD *thd, const char *buf, uint length);
|
||||
bool mysql_new_select(LEX *lex, bool move_down);
|
||||
void create_select_for_variable(const char *var_name);
|
||||
void mysql_init_multi_delete(LEX *lex);
|
||||
|
@ -961,6 +963,12 @@ bool mysql_create_table(THD *thd,const char *db, const char *table_name,
|
|||
List<create_field> &fields, List<Key> &keys,
|
||||
bool tmp_table, uint select_field_count,
|
||||
bool use_copy_create_info);
|
||||
bool mysql_create_table_no_lock(THD *thd, const char *db,
|
||||
const char *table_name,
|
||||
HA_CREATE_INFO *create_info,
|
||||
List<create_field> &fields, List<Key> &keys,
|
||||
bool tmp_table, uint select_field_count,
|
||||
bool use_copy_create_info);
|
||||
|
||||
bool mysql_alter_table(THD *thd, char *new_db, char *new_name,
|
||||
HA_CREATE_INFO *create_info,
|
||||
|
@ -1018,7 +1026,11 @@ TABLE_SHARE *get_cached_table_share(const char *db, const char *table_name);
|
|||
TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type update);
|
||||
TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT* mem,
|
||||
bool *refresh, uint flags);
|
||||
bool reopen_name_locked_table(THD* thd, TABLE_LIST* table);
|
||||
bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list, bool link_in);
|
||||
TABLE *table_cache_insert_placeholder(THD *thd, const char *key,
|
||||
uint key_length);
|
||||
bool lock_table_name_if_not_cached(THD *thd, const char *db,
|
||||
const char *table_name, TABLE **table);
|
||||
TABLE *find_locked_table(THD *thd, const char *db,const char *table_name);
|
||||
bool reopen_tables(THD *thd,bool get_locks,bool in_refresh);
|
||||
bool close_data_tables(THD *thd,const char *db, const char *table_name);
|
||||
|
@ -1187,7 +1199,9 @@ void add_join_on(TABLE_LIST *b,Item *expr);
|
|||
void add_join_natural(TABLE_LIST *a,TABLE_LIST *b,List<String> *using_fields,
|
||||
SELECT_LEX *lex);
|
||||
bool add_proc_to_list(THD *thd, Item *item);
|
||||
TABLE *unlink_open_table(THD *thd,TABLE *list,TABLE *find);
|
||||
void unlink_open_table(THD *thd, TABLE *find, bool unlock);
|
||||
void drop_open_table(THD *thd, TABLE *table, const char *db_name,
|
||||
const char *table_name);
|
||||
void update_non_unique_table_error(TABLE_LIST *update,
|
||||
const char *operation,
|
||||
TABLE_LIST *duplicate);
|
||||
|
@ -1622,7 +1636,7 @@ extern double log_01[32];
|
|||
extern ulonglong log_10_int[20];
|
||||
extern ulonglong keybuff_size;
|
||||
extern ulonglong thd_startup_options;
|
||||
extern ulong refresh_version, thread_id;
|
||||
extern ulong thread_id;
|
||||
extern ulong binlog_cache_use, binlog_cache_disk_use;
|
||||
extern ulong aborted_threads,aborted_connects;
|
||||
extern ulong delayed_insert_timeout;
|
||||
|
@ -1761,7 +1775,7 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **table, uint count,
|
|||
#define MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK 0x0001
|
||||
#define MYSQL_LOCK_IGNORE_FLUSH 0x0002
|
||||
#define MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN 0x0004
|
||||
#define MYSQL_OPEN_IGNORE_LOCKED_TABLES 0x0008
|
||||
#define MYSQL_OPEN_TEMPORARY_ONLY 0x0008
|
||||
|
||||
void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock);
|
||||
void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock);
|
||||
|
|
|
@ -802,6 +802,11 @@ static bool sys_update_ftb_syntax(THD *thd, set_var * var)
|
|||
{
|
||||
strmake(ft_boolean_syntax, var->value->str_value.c_ptr(),
|
||||
sizeof(ft_boolean_syntax)-1);
|
||||
|
||||
#ifdef HAVE_QUERY_CACHE
|
||||
query_cache.flush();
|
||||
#endif /* HAVE_QUERY_CACHE */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -923,6 +923,7 @@ static int create_table_from_dump(THD* thd, MYSQL *mysql, const char* db,
|
|||
handler *file;
|
||||
ulonglong save_options;
|
||||
NET *net= &mysql->net;
|
||||
const char *found_semicolon= NULL;
|
||||
DBUG_ENTER("create_table_from_dump");
|
||||
|
||||
packet_len= my_net_read(net); // read create table statement
|
||||
|
@ -974,7 +975,7 @@ static int create_table_from_dump(THD* thd, MYSQL *mysql, const char* db,
|
|||
thd->db = (char*)db;
|
||||
DBUG_ASSERT(thd->db != 0);
|
||||
thd->db_length= strlen(thd->db);
|
||||
mysql_parse(thd, thd->query, packet_len); // run create table
|
||||
mysql_parse(thd, thd->query, packet_len, &found_semicolon); // run create table
|
||||
thd->db = save_db; // leave things the way the were before
|
||||
thd->db_length= save_db_length;
|
||||
thd->options = save_options;
|
||||
|
|
|
@ -384,10 +384,15 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp,
|
|||
if ((ret= sp_use_new_db(thd, name->m_db, &old_db, 1, &dbchanged)))
|
||||
goto end;
|
||||
|
||||
lex_start(thd, defstr.c_ptr(), defstr.length());
|
||||
{
|
||||
Lex_input_stream lip(thd, defstr.c_ptr(), defstr.length());
|
||||
thd->m_lip= &lip;
|
||||
lex_start(thd);
|
||||
ret= MYSQLparse(thd);
|
||||
}
|
||||
|
||||
thd->spcont= 0;
|
||||
if (MYSQLparse(thd) || thd->is_fatal_error || newlex.sphead == NULL)
|
||||
if (ret || thd->is_fatal_error || newlex.sphead == NULL)
|
||||
{
|
||||
sp_head *sp= newlex.sphead;
|
||||
|
||||
|
|
|
@ -557,6 +557,7 @@ sp_head::init_strings(THD *thd, LEX *lex)
|
|||
const char *endp; /* Used to trim the end */
|
||||
/* During parsing, we must use thd->mem_root */
|
||||
MEM_ROOT *root= thd->mem_root;
|
||||
Lex_input_stream *lip=thd->m_lip;
|
||||
|
||||
if (m_param_begin && m_param_end)
|
||||
{
|
||||
|
@ -565,7 +566,7 @@ sp_head::init_strings(THD *thd, LEX *lex)
|
|||
}
|
||||
|
||||
/* If ptr has overrun end_of_query then end_of_query is the end */
|
||||
endp= (lex->ptr > lex->end_of_query ? lex->end_of_query : lex->ptr);
|
||||
endp= (lip->ptr > lip->end_of_query ? lip->end_of_query : lip->ptr);
|
||||
/*
|
||||
Trim "garbage" at the end. This is sometimes needed with the
|
||||
"/ * ! VERSION... * /" wrapper in dump files.
|
||||
|
@ -574,8 +575,8 @@ sp_head::init_strings(THD *thd, LEX *lex)
|
|||
|
||||
m_body.length= endp - m_body_begin;
|
||||
m_body.str= strmake_root(root, m_body_begin, m_body.length);
|
||||
m_defstr.length= endp - lex->buf;
|
||||
m_defstr.str= strmake_root(root, lex->buf, m_defstr.length);
|
||||
m_defstr.length= endp - lip->buf;
|
||||
m_defstr.str= strmake_root(root, lip->buf, m_defstr.length);
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
|
@ -1815,25 +1816,13 @@ sp_head::reset_lex(THD *thd)
|
|||
DBUG_ENTER("sp_head::reset_lex");
|
||||
LEX *sublex;
|
||||
LEX *oldlex= thd->lex;
|
||||
my_lex_states org_next_state= oldlex->next_state;
|
||||
|
||||
(void)m_lex.push_front(oldlex);
|
||||
thd->lex= sublex= new st_lex;
|
||||
|
||||
/* Reset most stuff. The length arguments doesn't matter here. */
|
||||
lex_start(thd, oldlex->buf, (ulong) (oldlex->end_of_query - oldlex->ptr));
|
||||
/* Reset most stuff. */
|
||||
lex_start(thd);
|
||||
|
||||
/*
|
||||
next_state is normally the same (0), but it happens that we swap lex in
|
||||
"mid-sentence", so we must restore it.
|
||||
*/
|
||||
sublex->next_state= org_next_state;
|
||||
/* We must reset ptr and end_of_query again */
|
||||
sublex->ptr= oldlex->ptr;
|
||||
sublex->end_of_query= oldlex->end_of_query;
|
||||
sublex->tok_start= oldlex->tok_start;
|
||||
sublex->tok_end= oldlex->tok_end;
|
||||
sublex->yylineno= oldlex->yylineno;
|
||||
/* And keep the SP stuff too */
|
||||
sublex->sphead= oldlex->sphead;
|
||||
sublex->spcont= oldlex->spcont;
|
||||
|
@ -1866,10 +1855,6 @@ sp_head::restore_lex(THD *thd)
|
|||
if (! oldlex)
|
||||
return; // Nothing to restore
|
||||
|
||||
// Update some state in the old one first
|
||||
oldlex->ptr= sublex->ptr;
|
||||
oldlex->tok_end= sublex->tok_end;
|
||||
oldlex->next_state= sublex->next_state;
|
||||
oldlex->trg_table_fields.push_back(&sublex->trg_table_fields);
|
||||
|
||||
/*
|
||||
|
@ -3062,10 +3047,20 @@ sp_instr_hreturn::print(String *str)
|
|||
uint
|
||||
sp_instr_hreturn::opt_mark(sp_head *sp, List<sp_instr> *leads)
|
||||
{
|
||||
if (m_dest)
|
||||
return sp_instr_jump::opt_mark(sp, leads);
|
||||
|
||||
marked= 1;
|
||||
|
||||
if (m_dest)
|
||||
{
|
||||
/*
|
||||
This is an EXIT handler; next instruction step is in m_dest.
|
||||
*/
|
||||
return m_dest;
|
||||
}
|
||||
|
||||
/*
|
||||
This is a CONTINUE handler; next instruction step will come from
|
||||
the handler stack and not from opt_mark.
|
||||
*/
|
||||
return UINT_MAX;
|
||||
}
|
||||
|
||||
|
|
|
@ -176,7 +176,9 @@ public:
|
|||
*/
|
||||
HASH m_sroutines;
|
||||
// Pointers set during parsing
|
||||
const char *m_param_begin, *m_param_end, *m_body_begin;
|
||||
const char *m_param_begin;
|
||||
const char *m_param_end;
|
||||
const char *m_body_begin;
|
||||
|
||||
/*
|
||||
Security context for stored routine which should be run under
|
||||
|
@ -990,6 +992,12 @@ public:
|
|||
|
||||
virtual void print(String *str);
|
||||
|
||||
/* This instruction will not be short cut optimized. */
|
||||
virtual uint opt_shortcut_jump(sp_head *sp, sp_instr *start)
|
||||
{
|
||||
return m_ip;
|
||||
}
|
||||
|
||||
virtual uint opt_mark(sp_head *sp, List<sp_instr> *leads);
|
||||
|
||||
private:
|
||||
|
|
449
sql/sql_base.cc
449
sql/sql_base.cc
|
@ -97,7 +97,7 @@ static bool open_new_frm(THD *thd, TABLE_SHARE *share, const char *alias,
|
|||
uint db_stat, uint prgflag,
|
||||
uint ha_open_flags, TABLE *outparam,
|
||||
TABLE_LIST *table_desc, MEM_ROOT *mem_root);
|
||||
static void close_old_data_files(THD *thd, TABLE *table, bool abort_locks,
|
||||
static void close_old_data_files(THD *thd, TABLE *table, bool morph_locks,
|
||||
bool send_refresh);
|
||||
static bool reopen_table(TABLE *table);
|
||||
static bool
|
||||
|
@ -688,6 +688,8 @@ static void close_handle_and_leave_table_as_lock(TABLE *table)
|
|||
MEM_ROOT *mem_root= &table->mem_root;
|
||||
DBUG_ENTER("close_handle_and_leave_table_as_lock");
|
||||
|
||||
DBUG_ASSERT(table->db_stat);
|
||||
|
||||
/*
|
||||
Make a local copy of the table share and free the current one.
|
||||
This has to be done to ensure that the table share is removed from
|
||||
|
@ -934,8 +936,22 @@ bool close_cached_tables(THD *thd, bool if_wait_for_refresh,
|
|||
for (uint idx=0 ; idx < open_cache.records ; idx++)
|
||||
{
|
||||
TABLE *table=(TABLE*) hash_element(&open_cache,idx);
|
||||
/*
|
||||
Note that we wait here only for tables which are actually open, and
|
||||
not for placeholders with TABLE::open_placeholder set. Waiting for
|
||||
latter will cause deadlock in the following scenario, for example:
|
||||
|
||||
conn1: lock table t1 write;
|
||||
conn2: lock table t2 write;
|
||||
conn1: flush tables;
|
||||
conn2: flush tables;
|
||||
|
||||
It also does not make sense to wait for those of placeholders that
|
||||
are employed by CREATE TABLE as in this case table simply does not
|
||||
exist yet.
|
||||
*/
|
||||
if (!table->s->log_table &&
|
||||
((table->s->version) < refresh_version && table->db_stat))
|
||||
(table->needs_reopen_or_name_lock() && table->db_stat))
|
||||
{
|
||||
found=1;
|
||||
DBUG_PRINT("signal", ("Waiting for COND_refresh"));
|
||||
|
@ -1249,10 +1265,10 @@ bool close_thread_table(THD *thd, TABLE **table_ptr)
|
|||
TABLE *table= *table_ptr;
|
||||
DBUG_ENTER("close_thread_table");
|
||||
DBUG_ASSERT(table->key_read == 0);
|
||||
DBUG_ASSERT(table->file->inited == handler::NONE);
|
||||
DBUG_ASSERT(!table->file || table->file->inited == handler::NONE);
|
||||
|
||||
*table_ptr=table->next;
|
||||
if (table->s->version != refresh_version ||
|
||||
if (table->needs_reopen_or_name_lock() ||
|
||||
thd->version != refresh_version || !table->db_stat)
|
||||
{
|
||||
VOID(hash_delete(&open_cache,(byte*) table));
|
||||
|
@ -1260,6 +1276,12 @@ bool close_thread_table(THD *thd, TABLE **table_ptr)
|
|||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
Open placeholders have TABLE::db_stat set to 0, so they should be
|
||||
handled by the first alternative.
|
||||
*/
|
||||
DBUG_ASSERT(!table->open_placeholder);
|
||||
|
||||
/* Free memory and reset for next loop */
|
||||
table->file->ha_reset();
|
||||
table->in_use=0;
|
||||
|
@ -1778,18 +1800,32 @@ static void relink_unused(TABLE *table)
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
Remove all instances of table from the current open list
|
||||
Free all locks on tables that are done with LOCK TABLES
|
||||
*/
|
||||
/**
|
||||
@brief Remove all instances of table from thread's open list and
|
||||
table cache.
|
||||
|
||||
TABLE *unlink_open_table(THD *thd, TABLE *list, TABLE *find)
|
||||
@param thd Thread context
|
||||
@param find Table to remove
|
||||
@param unlock TRUE - free all locks on tables removed that are
|
||||
done with LOCK TABLES
|
||||
FALSE - otherwise
|
||||
|
||||
@note When unlock parameter is FALSE or current thread doesn't have
|
||||
any tables locked with LOCK TABLES tables are assumed to be
|
||||
not locked (for example already unlocked).
|
||||
*/
|
||||
|
||||
void unlink_open_table(THD *thd, TABLE *find, bool unlock)
|
||||
{
|
||||
char key[MAX_DBKEY_LENGTH];
|
||||
uint key_length= find->s->table_cache_key.length;
|
||||
TABLE *start=list,**prev,*next;
|
||||
prev= &start;
|
||||
TABLE *list, **prev, *next;
|
||||
DBUG_ENTER("unlink_open_table");
|
||||
|
||||
safe_mutex_assert_owner(&LOCK_open);
|
||||
|
||||
list= thd->open_tables;
|
||||
prev= &thd->open_tables;
|
||||
memcpy(key, find->s->table_cache_key.str, key_length);
|
||||
for (; list ; list=next)
|
||||
{
|
||||
|
@ -1797,7 +1833,7 @@ TABLE *unlink_open_table(THD *thd, TABLE *list, TABLE *find)
|
|||
if (list->s->table_cache_key.length == key_length &&
|
||||
!memcmp(list->s->table_cache_key.str, key, key_length))
|
||||
{
|
||||
if (thd->locked_tables)
|
||||
if (unlock && thd->locked_tables)
|
||||
mysql_lock_remove(thd, thd->locked_tables,list);
|
||||
VOID(hash_delete(&open_cache,(byte*) list)); // Close table
|
||||
}
|
||||
|
@ -1810,7 +1846,41 @@ TABLE *unlink_open_table(THD *thd, TABLE *list, TABLE *find)
|
|||
*prev=0;
|
||||
// Notify any 'refresh' threads
|
||||
broadcast_refresh();
|
||||
return start;
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@brief Auxiliary routine which closes and drops open table.
|
||||
|
||||
@param thd Thread handle
|
||||
@param table TABLE object for table to be dropped
|
||||
@param db_name Name of database for this table
|
||||
@param table_name Name of this table
|
||||
|
||||
@note This routine assumes that table to be closed is open only
|
||||
by calling thread so we needn't wait until other threads
|
||||
will close the table. It also assumes that table to be
|
||||
dropped is already unlocked.
|
||||
*/
|
||||
|
||||
void drop_open_table(THD *thd, TABLE *table, const char *db_name,
|
||||
const char *table_name)
|
||||
{
|
||||
if (table->s->tmp_table)
|
||||
close_temporary_table(thd, table, 1, 1);
|
||||
else
|
||||
{
|
||||
handlerton *table_type= table->s->db_type;
|
||||
VOID(pthread_mutex_lock(&LOCK_open));
|
||||
/*
|
||||
unlink_open_table() also tells threads waiting for refresh or close
|
||||
that something has happened.
|
||||
*/
|
||||
unlink_open_table(thd, table, FALSE);
|
||||
quick_rm_table(table_type, db_name, table_name, 0);
|
||||
VOID(pthread_mutex_unlock(&LOCK_open));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -1867,6 +1937,11 @@ void wait_for_condition(THD *thd, pthread_mutex_t *mutex, pthread_cond_t *cond)
|
|||
table_list TABLE_LIST object for table to be open, TABLE_LIST::table
|
||||
member should point to TABLE object which was used for
|
||||
name-locking.
|
||||
link_in TRUE - if TABLE object for table to be opened should be
|
||||
linked into THD::open_tables list.
|
||||
FALSE - placeholder used for name-locking is already in
|
||||
this list so we only need to preserve TABLE::next
|
||||
pointer.
|
||||
|
||||
NOTE
|
||||
This function assumes that its caller already acquired LOCK_open mutex.
|
||||
|
@ -1876,7 +1951,7 @@ void wait_for_condition(THD *thd, pthread_mutex_t *mutex, pthread_cond_t *cond)
|
|||
TRUE - Error
|
||||
*/
|
||||
|
||||
bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list)
|
||||
bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list, bool link_in)
|
||||
{
|
||||
TABLE *table= table_list->table;
|
||||
TABLE_SHARE *share;
|
||||
|
@ -1907,11 +1982,32 @@ bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list)
|
|||
}
|
||||
|
||||
share= table->s;
|
||||
/*
|
||||
We want to prevent other connections from opening this table until end
|
||||
of statement as it is likely that modifications of table's metadata are
|
||||
not yet finished (for example CREATE TRIGGER have to change .TRG file,
|
||||
or we might want to drop table if CREATE TABLE ... SELECT fails).
|
||||
This also allows us to assume that no other connection will sneak in
|
||||
before we will get table-level lock on this table.
|
||||
*/
|
||||
share->version=0;
|
||||
table->in_use = thd;
|
||||
check_unused();
|
||||
table->next = thd->open_tables;
|
||||
thd->open_tables = table;
|
||||
|
||||
if (link_in)
|
||||
{
|
||||
table->next= thd->open_tables;
|
||||
thd->open_tables= table;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
TABLE object should be already in THD::open_tables list so we just
|
||||
need to set TABLE::next correctly.
|
||||
*/
|
||||
table->next= orig_table.next;
|
||||
}
|
||||
|
||||
table->tablenr=thd->current_tablenr++;
|
||||
table->used_fields=0;
|
||||
table->const_table=0;
|
||||
|
@ -1921,6 +2017,173 @@ bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list)
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
@brief Create and insert into table cache placeholder for table
|
||||
which will prevent its opening (or creation) (a.k.a lock
|
||||
table name).
|
||||
|
||||
@param thd Thread context
|
||||
@param key Table cache key for name to be locked
|
||||
@param key_length Table cache key length
|
||||
|
||||
@return Pointer to TABLE object used for name locking or 0 in
|
||||
case of failure.
|
||||
*/
|
||||
|
||||
TABLE *table_cache_insert_placeholder(THD *thd, const char *key,
|
||||
uint key_length)
|
||||
{
|
||||
TABLE *table;
|
||||
TABLE_SHARE *share;
|
||||
char *key_buff;
|
||||
DBUG_ENTER("table_cache_insert_placeholder");
|
||||
|
||||
safe_mutex_assert_owner(&LOCK_open);
|
||||
|
||||
/*
|
||||
Create a table entry with the right key and with an old refresh version
|
||||
Note that we must use my_multi_malloc() here as this is freed by the
|
||||
table cache
|
||||
*/
|
||||
if (!my_multi_malloc(MYF(MY_WME | MY_ZEROFILL),
|
||||
&table, sizeof(*table),
|
||||
&share, sizeof(*share),
|
||||
&key_buff, key_length,
|
||||
NULL))
|
||||
DBUG_RETURN(NULL);
|
||||
|
||||
table->s= share;
|
||||
share->set_table_cache_key(key_buff, key, key_length);
|
||||
share->tmp_table= INTERNAL_TMP_TABLE; // for intern_close_table
|
||||
table->in_use= thd;
|
||||
table->locked_by_name=1;
|
||||
|
||||
if (my_hash_insert(&open_cache, (byte*)table))
|
||||
{
|
||||
my_free((gptr) table, MYF(0));
|
||||
DBUG_RETURN(NULL);
|
||||
}
|
||||
|
||||
DBUG_RETURN(table);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@brief Obtain an exclusive name lock on the table if it is not cached
|
||||
in the table cache.
|
||||
|
||||
@param thd Thread context
|
||||
@param db Name of database
|
||||
@param table_name Name of table
|
||||
@param[out] table Out parameter which is either:
|
||||
- set to NULL if table cache contains record for
|
||||
the table or
|
||||
- set to point to the TABLE instance used for
|
||||
name-locking.
|
||||
|
||||
@note This function takes into account all records for table in table
|
||||
cache, even placeholders used for name-locking. This means that
|
||||
'table' parameter can be set to NULL for some situations when
|
||||
table does not really exist.
|
||||
|
||||
@retval TRUE Error occured (OOM)
|
||||
@retval FALSE Success. 'table' parameter set according to above rules.
|
||||
*/
|
||||
|
||||
bool lock_table_name_if_not_cached(THD *thd, const char *db,
|
||||
const char *table_name, TABLE **table)
|
||||
{
|
||||
char key[MAX_DBKEY_LENGTH];
|
||||
uint key_length;
|
||||
DBUG_ENTER("lock_table_name_if_not_cached");
|
||||
|
||||
key_length= (uint)(strmov(strmov(key, db) + 1, table_name) - key) + 1;
|
||||
VOID(pthread_mutex_lock(&LOCK_open));
|
||||
|
||||
if (hash_search(&open_cache, (byte *)key, key_length))
|
||||
{
|
||||
VOID(pthread_mutex_unlock(&LOCK_open));
|
||||
DBUG_PRINT("info", ("Table is cached, name-lock is not obtained"));
|
||||
*table= 0;
|
||||
DBUG_RETURN(FALSE);
|
||||
}
|
||||
if (!(*table= table_cache_insert_placeholder(thd, key, key_length)))
|
||||
{
|
||||
VOID(pthread_mutex_unlock(&LOCK_open));
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
(*table)->open_placeholder= 1;
|
||||
(*table)->next= thd->open_tables;
|
||||
thd->open_tables= *table;
|
||||
VOID(pthread_mutex_unlock(&LOCK_open));
|
||||
DBUG_RETURN(FALSE);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@brief Check that table exists in table definition cache, on disk
|
||||
or in some storage engine.
|
||||
|
||||
@param thd Thread context
|
||||
@param table Table list element
|
||||
@param exists[out] Out parameter which is set to TRUE if table
|
||||
exists and to FALSE otherwise.
|
||||
|
||||
@note This function assumes that caller owns LOCK_open mutex.
|
||||
It also assumes that the fact that there are no name-locks
|
||||
on the table was checked beforehand.
|
||||
|
||||
@note If there is no .FRM file for the table but it exists in one
|
||||
of engines (e.g. it was created on another node of NDB cluster)
|
||||
this function will fetch and create proper .FRM file for it.
|
||||
|
||||
@retval TRUE Some error occured
|
||||
@retval FALSE No error. 'exists' out parameter set accordingly.
|
||||
*/
|
||||
|
||||
bool check_if_table_exists(THD *thd, TABLE_LIST *table, bool *exists)
|
||||
{
|
||||
char path[FN_REFLEN];
|
||||
int rc;
|
||||
DBUG_ENTER("check_if_table_exists");
|
||||
|
||||
safe_mutex_assert_owner(&LOCK_open);
|
||||
|
||||
*exists= TRUE;
|
||||
|
||||
if (get_cached_table_share(table->db, table->table_name))
|
||||
DBUG_RETURN(FALSE);
|
||||
|
||||
build_table_filename(path, sizeof(path) - 1, table->db, table->table_name,
|
||||
reg_ext, 0);
|
||||
|
||||
if (!access(path, F_OK))
|
||||
DBUG_RETURN(FALSE);
|
||||
|
||||
/* .FRM file doesn't exist. Check if some engine can provide it. */
|
||||
|
||||
rc= ha_create_table_from_engine(thd, table->db, table->table_name);
|
||||
|
||||
if (rc < 0)
|
||||
{
|
||||
/* Table does not exists in engines as well. */
|
||||
*exists= FALSE;
|
||||
DBUG_RETURN(FALSE);
|
||||
}
|
||||
else if (!rc)
|
||||
{
|
||||
/* Table exists in some engine and .FRM for it was created. */
|
||||
DBUG_RETURN(FALSE);
|
||||
}
|
||||
else /* (rc > 0) */
|
||||
{
|
||||
my_printf_error(ER_UNKNOWN_ERROR, "Failed to open '%-.64s', error while "
|
||||
"unpacking from engine", MYF(0), table->table_name);
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Open a table.
|
||||
|
||||
|
@ -1936,12 +2199,17 @@ bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list)
|
|||
MYSQL_LOCK_IGNORE_FLUSH - Open table even if
|
||||
someone has done a flush or namelock on it.
|
||||
No version number checking is done.
|
||||
MYSQL_OPEN_IGNORE_LOCKED_TABLES - Open table
|
||||
ignoring set of locked tables and prelocked mode.
|
||||
MYSQL_OPEN_TEMPORARY_ONLY - Open only temporary
|
||||
table not the base table or view.
|
||||
|
||||
IMPLEMENTATION
|
||||
Uses a cache of open tables to find a table not in use.
|
||||
|
||||
If table list element for the table to be opened has "create" flag
|
||||
set and table does not exist, this function will automatically insert
|
||||
a placeholder for exclusive name lock into the open tables cache and
|
||||
will return the TABLE instance that corresponds to this placeholder.
|
||||
|
||||
RETURN
|
||||
NULL Open failed. If refresh is set then one should close
|
||||
all other tables and retry the open.
|
||||
|
@ -2014,6 +2282,12 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
|
|||
}
|
||||
}
|
||||
|
||||
if (flags & MYSQL_OPEN_TEMPORARY_ONLY)
|
||||
{
|
||||
my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db, table_list->table_name);
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
/*
|
||||
The table is not temporary - if we're in pre-locked or LOCK TABLES
|
||||
mode, let's try to find the requested table in the list of pre-opened
|
||||
|
@ -2021,8 +2295,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
|
|||
open not pre-opened tables in pre-locked/LOCK TABLES mode.
|
||||
TODO: move this block into a separate function.
|
||||
*/
|
||||
if (!(flags & MYSQL_OPEN_IGNORE_LOCKED_TABLES) &&
|
||||
(thd->locked_tables || thd->prelocked_mode))
|
||||
if (thd->locked_tables || thd->prelocked_mode)
|
||||
{ // Using table locks
|
||||
TABLE *best_table= 0;
|
||||
int best_distance= INT_MIN;
|
||||
|
@ -2204,7 +2477,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
|
|||
c1: name lock t2; -- blocks
|
||||
c2: open t1; -- blocks
|
||||
*/
|
||||
if (table->s->version != refresh_version && !table->s->log_table)
|
||||
if (table->needs_reopen_or_name_lock() && !table->s->log_table)
|
||||
{
|
||||
DBUG_PRINT("note",
|
||||
("Found table '%s.%s' with different refresh version",
|
||||
|
@ -2217,6 +2490,14 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
|
|||
continue;
|
||||
}
|
||||
|
||||
/* Avoid self-deadlocks by detecting self-dependencies. */
|
||||
if (table->open_placeholder && table->in_use == thd)
|
||||
{
|
||||
VOID(pthread_mutex_unlock(&LOCK_open));
|
||||
my_error(ER_UPDATE_TABLE_USED, MYF(0), table->s->table_name.str);
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
/*
|
||||
Back off, part 1: mark the table as "unused" for the
|
||||
purpose of name-locking by setting table->db_stat to 0. Do
|
||||
|
@ -2233,6 +2514,14 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
|
|||
and wait till the operation is complete: when any
|
||||
operation that juggles with table->s->version completes,
|
||||
it broadcasts COND_refresh condition variable.
|
||||
If 'old' table we met is in use by current thread we return
|
||||
without waiting since in this situation it's this thread
|
||||
which is responsible for broadcasting on COND_refresh
|
||||
(and this was done already in close_old_data_files()).
|
||||
Good example of such situation is when we have statement
|
||||
that needs two instances of table and FLUSH TABLES comes
|
||||
after we open first instance but before we open second
|
||||
instance.
|
||||
*/
|
||||
if (table->in_use != thd)
|
||||
{
|
||||
|
@ -2273,6 +2562,40 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
|
|||
while (open_cache.records > table_cache_size && unused_tables)
|
||||
VOID(hash_delete(&open_cache,(byte*) unused_tables)); /* purecov: tested */
|
||||
|
||||
if (table_list->create)
|
||||
{
|
||||
bool exists;
|
||||
|
||||
if (check_if_table_exists(thd, table_list, &exists))
|
||||
{
|
||||
VOID(pthread_mutex_unlock(&LOCK_open));
|
||||
DBUG_RETURN(NULL);
|
||||
}
|
||||
|
||||
if (!exists)
|
||||
{
|
||||
/*
|
||||
Table to be created, so we need to create placeholder in table-cache.
|
||||
*/
|
||||
if (!(table= table_cache_insert_placeholder(thd, key, key_length)))
|
||||
{
|
||||
VOID(pthread_mutex_unlock(&LOCK_open));
|
||||
DBUG_RETURN(NULL);
|
||||
}
|
||||
/*
|
||||
Link placeholder to the open tables list so it will be automatically
|
||||
removed once tables are closed. Also mark it so it won't be ignored
|
||||
by other trying to take name-lock.
|
||||
*/
|
||||
table->open_placeholder= 1;
|
||||
table->next= thd->open_tables;
|
||||
thd->open_tables= table;
|
||||
VOID(pthread_mutex_unlock(&LOCK_open));
|
||||
DBUG_RETURN(table);
|
||||
}
|
||||
/* Table exists. Let us try to open it. */
|
||||
}
|
||||
|
||||
/* make a new table */
|
||||
if (!(table=(TABLE*) my_malloc(sizeof(*table),MYF(MY_WME))))
|
||||
{
|
||||
|
@ -2489,9 +2812,24 @@ bool close_data_tables(THD *thd,const char *db, const char *table_name)
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
Reopen all tables with closed data files
|
||||
One should have lock on LOCK_open when calling this
|
||||
/**
|
||||
@brief Reopen all tables with closed data files.
|
||||
|
||||
@param thd Thread context
|
||||
@param get_locks Should we get locks after reopening tables ?
|
||||
@param in_refresh Are we in FLUSH TABLES ? TODO: It seems that
|
||||
we can remove this parameter.
|
||||
|
||||
@note Since this function can't properly handle prelocking and
|
||||
create placeholders it should be used in very special
|
||||
situations like FLUSH TABLES or ALTER TABLE. In general
|
||||
case one should just repeat open_tables()/lock_tables()
|
||||
combination when one needs tables to be reopened (for
|
||||
example see open_and_lock_tables()).
|
||||
|
||||
@note One should have lock on LOCK_open when calling this.
|
||||
|
||||
@return FALSE in case of success, TRUE - otherwise.
|
||||
*/
|
||||
|
||||
bool reopen_tables(THD *thd,bool get_locks,bool in_refresh)
|
||||
|
@ -2537,7 +2875,7 @@ bool reopen_tables(THD *thd,bool get_locks,bool in_refresh)
|
|||
if (in_refresh)
|
||||
{
|
||||
table->s->version=0;
|
||||
table->locked_by_flush=0;
|
||||
table->open_placeholder= 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2564,13 +2902,21 @@ bool reopen_tables(THD *thd,bool get_locks,bool in_refresh)
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
Close handlers for tables in list, but leave the TABLE structure
|
||||
intact so that we can re-open these quickly
|
||||
abort_locks is set if called from flush_tables.
|
||||
/**
|
||||
@brief Close handlers for tables in list, but leave the TABLE structure
|
||||
intact so that we can re-open these quickly.
|
||||
|
||||
@param thd Thread context
|
||||
@param table Head of the list of TABLE objects
|
||||
@param morph_locks TRUE - remove locks which we have on tables being closed
|
||||
but ensure that no DML or DDL will sneak in before
|
||||
we will re-open the table (i.e. temporarily morph
|
||||
our table-level locks into name-locks).
|
||||
FALSE - otherwise
|
||||
@param send_refresh Should we awake waiters even if we didn't close any tables?
|
||||
*/
|
||||
|
||||
void close_old_data_files(THD *thd, TABLE *table, bool abort_locks,
|
||||
void close_old_data_files(THD *thd, TABLE *table, bool morph_locks,
|
||||
bool send_refresh)
|
||||
{
|
||||
bool found= send_refresh;
|
||||
|
@ -2582,19 +2928,41 @@ void close_old_data_files(THD *thd, TABLE *table, bool abort_locks,
|
|||
Reopen marked for flush. But close log tables. They are flushed only
|
||||
explicitly on FLUSH LOGS
|
||||
*/
|
||||
if (table->s->version != refresh_version && !table->s->log_table)
|
||||
if (table->needs_reopen_or_name_lock() && !table->s->log_table)
|
||||
{
|
||||
found=1;
|
||||
if (table->db_stat)
|
||||
{
|
||||
if (abort_locks)
|
||||
if (morph_locks)
|
||||
{
|
||||
mysql_lock_abort(thd,table, TRUE); // Close waiting threads
|
||||
mysql_lock_remove(thd, thd->locked_tables,table);
|
||||
table->locked_by_flush=1; // Will be reopened with locks
|
||||
/*
|
||||
Wake up threads waiting for table-level lock on this table
|
||||
so they won't sneak in when we will temporarily remove our
|
||||
lock on it. This will also give them a chance to close their
|
||||
instances of this table.
|
||||
*/
|
||||
mysql_lock_abort(thd, table, TRUE);
|
||||
mysql_lock_remove(thd, thd->locked_tables, table);
|
||||
/*
|
||||
We want to protect the table from concurrent DDL operations
|
||||
(like RENAME TABLE) until we will re-open and re-lock it.
|
||||
*/
|
||||
table->open_placeholder= 1;
|
||||
}
|
||||
close_handle_and_leave_table_as_lock(table);
|
||||
}
|
||||
else if (table->open_placeholder)
|
||||
{
|
||||
/*
|
||||
We come here only in close-for-back-off scenario. So we have to
|
||||
"close" create placeholder here to avoid deadlocks (for example,
|
||||
in case of concurrent execution of CREATE TABLE t1 SELECT * FROM t2
|
||||
and RENAME TABLE t2 TO t1). In close-for-re-open scenario we will
|
||||
probably want to let it stay.
|
||||
*/
|
||||
DBUG_ASSERT(!morph_locks);
|
||||
table->open_placeholder= 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (found)
|
||||
|
@ -2630,10 +2998,10 @@ bool table_is_used(TABLE *table, bool wait_for_name_lock)
|
|||
key_length, &state))
|
||||
{
|
||||
DBUG_PRINT("info", ("share: 0x%lx locked_by_logger: %d "
|
||||
"locked_by_flush: %d locked_by_name: %d "
|
||||
"open_placeholder: %d locked_by_name: %d "
|
||||
"db_stat: %u version: %lu",
|
||||
(ulong) search->s, search->locked_by_logger,
|
||||
search->locked_by_flush, search->locked_by_name,
|
||||
search->open_placeholder, search->locked_by_name,
|
||||
search->db_stat,
|
||||
search->s->version));
|
||||
if (search->in_use == table->in_use)
|
||||
|
@ -2649,8 +3017,7 @@ bool table_is_used(TABLE *table, bool wait_for_name_lock)
|
|||
*/
|
||||
if (!search->locked_by_logger &&
|
||||
(search->locked_by_name && wait_for_name_lock ||
|
||||
search->locked_by_flush ||
|
||||
(search->db_stat && search->s->version < refresh_version)))
|
||||
(search->is_name_opened() && search->needs_reopen_or_name_lock())))
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
} while ((table=table->next));
|
||||
|
@ -6637,7 +7004,7 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name,
|
|||
{
|
||||
DBUG_PRINT("info", ("Table was in use by other thread"));
|
||||
in_use->some_tables_deleted=1;
|
||||
if (table->db_stat)
|
||||
if (table->is_name_opened())
|
||||
{
|
||||
DBUG_PRINT("info", ("Found another active instance of the table"));
|
||||
result=1;
|
||||
|
@ -7012,7 +7379,7 @@ has_two_write_locked_tables_with_auto_increment(TABLE_LIST *tables)
|
|||
for (TABLE_LIST *table= tables; table; table= table->next_global)
|
||||
{
|
||||
/* we must do preliminary checks as table->table may be NULL */
|
||||
if (!table->placeholder() && !table->schema_table &&
|
||||
if (!table->placeholder() &&
|
||||
table->table->found_next_number_field &&
|
||||
(table->lock_type >= TL_WRITE_ALLOW_WRITE))
|
||||
{
|
||||
|
|
|
@ -867,10 +867,13 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used)
|
|||
flags.max_sort_length= thd->variables.max_sort_length;
|
||||
flags.lc_time_names= thd->variables.lc_time_names;
|
||||
flags.group_concat_max_len= thd->variables.group_concat_max_len;
|
||||
flags.div_precision_increment= thd->variables.div_precincrement;
|
||||
flags.default_week_format= thd->variables.default_week_format;
|
||||
DBUG_PRINT("qcache", ("\
|
||||
long %d, 4.1: %d, bin_proto: %d, more results %d, pkt_nr: %d, \
|
||||
CS client: %u, CS result: %u, CS conn: %u, limit: %lu, TZ: 0x%lx, \
|
||||
sql mode: 0x%lx, sort len: %lu, conncat len: %lu",
|
||||
sql mode: 0x%lx, sort len: %lu, conncat len: %lu, div_precision: %lu, \
|
||||
def_week_frmt: %lu",
|
||||
(int)flags.client_long_flag,
|
||||
(int)flags.client_protocol_41,
|
||||
(int)flags.result_in_binary_protocol,
|
||||
|
@ -883,7 +886,9 @@ sql mode: 0x%lx, sort len: %lu, conncat len: %lu",
|
|||
(ulong) flags.time_zone,
|
||||
flags.sql_mode,
|
||||
flags.max_sort_length,
|
||||
flags.group_concat_max_len));
|
||||
flags.group_concat_max_len,
|
||||
flags.div_precision_increment,
|
||||
flags.default_week_format));
|
||||
/*
|
||||
Make InnoDB to release the adaptive hash index latch before
|
||||
acquiring the query cache mutex.
|
||||
|
@ -1112,11 +1117,14 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
|
|||
flags.sql_mode= thd->variables.sql_mode;
|
||||
flags.max_sort_length= thd->variables.max_sort_length;
|
||||
flags.group_concat_max_len= thd->variables.group_concat_max_len;
|
||||
flags.div_precision_increment= thd->variables.div_precincrement;
|
||||
flags.default_week_format= thd->variables.default_week_format;
|
||||
flags.lc_time_names= thd->variables.lc_time_names;
|
||||
DBUG_PRINT("qcache", ("\
|
||||
long %d, 4.1: %d, bin_proto: %d, more results %d, pkt_nr: %d, \
|
||||
CS client: %u, CS result: %u, CS conn: %u, limit: %lu, TZ: 0x%lx, \
|
||||
sql mode: 0x%lx, sort len: %lu, conncat len: %lu",
|
||||
sql mode: 0x%lx, sort len: %lu, conncat len: %lu, div_precision: %lu, \
|
||||
def_week_frmt: %lu",
|
||||
(int)flags.client_long_flag,
|
||||
(int)flags.client_protocol_41,
|
||||
(int)flags.result_in_binary_protocol,
|
||||
|
@ -1129,7 +1137,9 @@ sql mode: 0x%lx, sort len: %lu, conncat len: %lu",
|
|||
(ulong) flags.time_zone,
|
||||
flags.sql_mode,
|
||||
flags.max_sort_length,
|
||||
flags.group_concat_max_len));
|
||||
flags.group_concat_max_len,
|
||||
flags.div_precision_increment,
|
||||
flags.default_week_format));
|
||||
memcpy((void *)(sql + (tot_length - QUERY_CACHE_FLAGS_SIZE)),
|
||||
&flags, QUERY_CACHE_FLAGS_SIZE);
|
||||
query_block = (Query_cache_block *) hash_search(&queries, (byte*) sql,
|
||||
|
|
|
@ -31,6 +31,7 @@ class Load_log_event;
|
|||
class Slave_log_event;
|
||||
class sp_rcontext;
|
||||
class sp_cache;
|
||||
class Lex_input_stream;
|
||||
class Rows_log_event;
|
||||
|
||||
enum enum_enable_or_disable { LEAVE_AS_IS, ENABLE, DISABLE };
|
||||
|
@ -171,7 +172,7 @@ public:
|
|||
|
||||
#include "sql_lex.h" /* Must be here */
|
||||
|
||||
class delayed_insert;
|
||||
class Delayed_insert;
|
||||
class select_result;
|
||||
class Time_zone;
|
||||
|
||||
|
@ -1013,7 +1014,7 @@ public:
|
|||
time_t start_time,time_after_lock,user_time;
|
||||
time_t connect_time,thr_create_time; // track down slow pthread_create
|
||||
thr_lock_type update_lock_default;
|
||||
delayed_insert *di;
|
||||
Delayed_insert *di;
|
||||
|
||||
/* <> 0 if we are inside of trigger or stored function. */
|
||||
uint in_sub_stmt;
|
||||
|
@ -1435,6 +1436,16 @@ public:
|
|||
*/
|
||||
query_id_t first_query_id;
|
||||
} binlog_evt_union;
|
||||
|
||||
/**
|
||||
Character input stream consumed by the lexical analyser,
|
||||
used during parsing.
|
||||
Note that since the parser is not re-entrant, we keep only one input
|
||||
stream here. This member is valid only when executing code during parsing,
|
||||
and may point to invalid memory after that.
|
||||
*/
|
||||
Lex_input_stream *m_lip;
|
||||
|
||||
#ifdef WITH_PARTITION_STORAGE_ENGINE
|
||||
partition_info *work_part_info;
|
||||
#endif
|
||||
|
|
|
@ -682,7 +682,7 @@ int mysql_ha_flush(THD *thd, TABLE_LIST *tables, uint mode_flags,
|
|||
while (*table_ptr)
|
||||
{
|
||||
if ((mode_flags & MYSQL_HA_FLUSH_ALL) ||
|
||||
((*table_ptr)->s->version != refresh_version))
|
||||
(*table_ptr)->needs_reopen_or_name_lock())
|
||||
{
|
||||
/* The first time it is required, lock for close_thread_table(). */
|
||||
if (! did_lock && ! is_locked)
|
||||
|
@ -783,15 +783,22 @@ void mysql_ha_mark_tables_for_reopen(THD *thd, TABLE *table)
|
|||
safe_mutex_assert_owner(&LOCK_open);
|
||||
for (; table; table= table->next)
|
||||
{
|
||||
TABLE_LIST *hash_tables;
|
||||
if ((hash_tables= (TABLE_LIST*) hash_search(&thd->handler_tables_hash,
|
||||
(byte*) table->alias,
|
||||
strlen(table->alias) + 1)))
|
||||
/*
|
||||
Some elements in open table list, for example placeholders used for
|
||||
name-locking, can have alias set to 0.
|
||||
*/
|
||||
if (table->alias)
|
||||
{
|
||||
/* Mark table as ready for reopen. */
|
||||
hash_tables->table= NULL;
|
||||
/* End open index/table scans. */
|
||||
table->file->ha_index_or_rnd_end();
|
||||
TABLE_LIST *hash_tables;
|
||||
if ((hash_tables= (TABLE_LIST*) hash_search(&thd->handler_tables_hash,
|
||||
(byte*) table->alias,
|
||||
strlen(table->alias) + 1)))
|
||||
{
|
||||
/* Mark table as ready for reopen. */
|
||||
hash_tables->table= NULL;
|
||||
/* End open index/table scans. */
|
||||
table->file->ha_index_or_rnd_end();
|
||||
}
|
||||
}
|
||||
}
|
||||
DBUG_VOID_RETURN;
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
waited for to open and lock the table.
|
||||
|
||||
If accessing the thread succeeded, in
|
||||
delayed_insert::get_local_table() the table of the thread is copied
|
||||
Delayed_insert::get_local_table() the table of the thread is copied
|
||||
for local use. A copy is required because the normal insert logic
|
||||
works on a target table, but the other threads table object must not
|
||||
be used. The insert logic uses the record buffer to create a record.
|
||||
|
@ -384,6 +384,88 @@ void prepare_triggers_for_insert_stmt(TABLE *table)
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
Upgrade table-level lock of INSERT statement to TL_WRITE if
|
||||
a more concurrent lock is infeasible for some reason. This is
|
||||
necessary for engines without internal locking support (MyISAM).
|
||||
An engine with internal locking implementation might later
|
||||
downgrade the lock in handler::store_lock() method.
|
||||
*/
|
||||
|
||||
void upgrade_lock_type(THD *thd, thr_lock_type *lock_type,
|
||||
enum_duplicates duplic,
|
||||
bool is_multi_insert)
|
||||
{
|
||||
if (duplic == DUP_UPDATE ||
|
||||
duplic == DUP_REPLACE && *lock_type == TL_WRITE_CONCURRENT_INSERT)
|
||||
{
|
||||
*lock_type= TL_WRITE;
|
||||
return;
|
||||
}
|
||||
|
||||
if (*lock_type == TL_WRITE_DELAYED)
|
||||
{
|
||||
#ifdef EMBEDDED_LIBRARY
|
||||
/* No auxiliary threads in the embedded server. */
|
||||
*lock_type= TL_WRITE;
|
||||
return;
|
||||
#else
|
||||
/*
|
||||
We do not use delayed threads if:
|
||||
- we're running in the safe mode or skip-new - the feature
|
||||
is disabled in these modes
|
||||
- we're running this query in statement level replication,
|
||||
on a replication slave - because we must ensure serial
|
||||
execution of queries on the slave
|
||||
- it is INSERT .. ON DUPLICATE KEY UPDATE - in this case the
|
||||
insert cannot be concurrent
|
||||
*/
|
||||
if (specialflag & (SPECIAL_NO_NEW_FUNC | SPECIAL_SAFE_MODE) ||
|
||||
thd->slave_thread ||
|
||||
thd->variables.max_insert_delayed_threads == 0)
|
||||
{
|
||||
*lock_type= TL_WRITE;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
bool log_on= (thd->options & OPTION_BIN_LOG ||
|
||||
! (thd->security_ctx->master_access & SUPER_ACL));
|
||||
if (global_system_variables.binlog_format == BINLOG_FORMAT_STMT &&
|
||||
log_on && mysql_bin_log.is_open() && is_multi_insert)
|
||||
{
|
||||
/*
|
||||
Statement-based binary logging does not work in this case, because:
|
||||
a) two concurrent statements may have their rows intermixed in the
|
||||
queue, leading to autoincrement replication problems on slave (because
|
||||
the values generated used for one statement don't depend only on the
|
||||
value generated for the first row of this statement, so are not
|
||||
replicable)
|
||||
b) if first row of the statement has an error the full statement is
|
||||
not binlogged, while next rows of the statement may be inserted.
|
||||
c) if first row succeeds, statement is binlogged immediately with a
|
||||
zero error code (i.e. "no error"), if then second row fails, query
|
||||
will fail on slave too and slave will stop (wrongly believing that the
|
||||
master got no error).
|
||||
So we fallback to non-delayed INSERT.
|
||||
Note that to be fully correct, we should test the "binlog format which
|
||||
the delayed thread is going to use for this row". But in the common case
|
||||
where the global binlog format is not changed and the session binlog
|
||||
format may be changed, that is equal to the global binlog format.
|
||||
We test it without mutex for speed reasons (condition rarely true), and
|
||||
in the common case (global not changed) it is as good as without mutex;
|
||||
if global value is changed, anyway there is uncertainty as the delayed
|
||||
thread may be old and use the before-the-change value.
|
||||
*/
|
||||
*lock_type= TL_WRITE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
INSERT statement implementation
|
||||
*/
|
||||
|
||||
bool mysql_insert(THD *thd,TABLE_LIST *table_list,
|
||||
List<Item> &fields,
|
||||
List<List_item> &values_list,
|
||||
|
@ -406,7 +488,6 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
|
|||
Name_resolution_context_state ctx_state;
|
||||
#ifndef EMBEDDED_LIBRARY
|
||||
char *query= thd->query;
|
||||
#endif
|
||||
/*
|
||||
log_on is about delayed inserts only.
|
||||
By default, both logs are enabled (this won't cause problems if the server
|
||||
|
@ -414,72 +495,37 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
|
|||
*/
|
||||
bool log_on= ((thd->options & OPTION_BIN_LOG) ||
|
||||
(!(thd->security_ctx->master_access & SUPER_ACL)));
|
||||
#endif
|
||||
thr_lock_type lock_type = table_list->lock_type;
|
||||
Item *unused_conds= 0;
|
||||
DBUG_ENTER("mysql_insert");
|
||||
|
||||
/*
|
||||
in safe mode or with skip-new change delayed insert to be regular
|
||||
if we are told to replace duplicates, the insert cannot be concurrent
|
||||
delayed insert changed to regular in slave thread
|
||||
*/
|
||||
#ifdef EMBEDDED_LIBRARY
|
||||
if (lock_type == TL_WRITE_DELAYED)
|
||||
lock_type=TL_WRITE;
|
||||
#else
|
||||
if ((lock_type == TL_WRITE_DELAYED &&
|
||||
((specialflag & (SPECIAL_NO_NEW_FUNC | SPECIAL_SAFE_MODE)) ||
|
||||
thd->slave_thread || !thd->variables.max_insert_delayed_threads)) ||
|
||||
(lock_type == TL_WRITE_CONCURRENT_INSERT && duplic == DUP_REPLACE) ||
|
||||
(duplic == DUP_UPDATE))
|
||||
lock_type=TL_WRITE;
|
||||
#endif
|
||||
if ((lock_type == TL_WRITE_DELAYED) &&
|
||||
(global_system_variables.binlog_format == BINLOG_FORMAT_STMT) &&
|
||||
log_on && mysql_bin_log.is_open() &&
|
||||
(values_list.elements > 1))
|
||||
Upgrade lock type if the requested lock is incompatible with
|
||||
the current connection mode or table operation.
|
||||
*/
|
||||
upgrade_lock_type(thd, &table_list->lock_type, duplic,
|
||||
values_list.elements > 1);
|
||||
lock_type= table_list->lock_type;
|
||||
|
||||
/*
|
||||
We can't write-delayed into a table locked with LOCK TABLES:
|
||||
this will lead to a deadlock, since the delayed thread will
|
||||
never be able to get a lock on the table. QQQ: why not
|
||||
upgrade the lock here instead?
|
||||
*/
|
||||
if (lock_type == TL_WRITE_DELAYED && thd->locked_tables &&
|
||||
find_locked_table(thd, table_list->db, table_list->table_name))
|
||||
{
|
||||
/*
|
||||
Statement-based binary logging does not work in this case, because:
|
||||
a) two concurrent statements may have their rows intermixed in the
|
||||
queue, leading to autoincrement replication problems on slave (because
|
||||
the values generated used for one statement don't depend only on the
|
||||
value generated for the first row of this statement, so are not
|
||||
replicable)
|
||||
b) if first row of the statement has an error the full statement is
|
||||
not binlogged, while next rows of the statement may be inserted.
|
||||
c) if first row succeeds, statement is binlogged immediately with a
|
||||
zero error code (i.e. "no error"), if then second row fails, query
|
||||
will fail on slave too and slave will stop (wrongly believing that the
|
||||
master got no error).
|
||||
So we fallback to non-delayed INSERT.
|
||||
Note that to be fully correct, we should test the "binlog format which
|
||||
the delayed thread is going to use for this row". But in the common case
|
||||
where the global binlog format is not changed and the session binlog
|
||||
format may be changed, that is equal to the global binlog format.
|
||||
We test it without mutex for speed reasons (condition rarely true), and
|
||||
in the common case (global not changed) it is as good as without mutex;
|
||||
if global value is changed, anyway there is uncertainty as the delayed
|
||||
thread may be old and use the before-the-change value.
|
||||
*/
|
||||
lock_type= TL_WRITE;
|
||||
my_error(ER_DELAYED_INSERT_TABLE_LOCKED, MYF(0),
|
||||
table_list->table_name);
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
table_list->lock_type= lock_type;
|
||||
|
||||
#ifndef EMBEDDED_LIBRARY
|
||||
if (lock_type == TL_WRITE_DELAYED)
|
||||
{
|
||||
res= 1;
|
||||
if (thd->locked_tables)
|
||||
{
|
||||
DBUG_ASSERT(table_list->db); /* Must be set in the parser */
|
||||
if (find_locked_table(thd, table_list->db, table_list->table_name))
|
||||
{
|
||||
my_error(ER_DELAYED_INSERT_TABLE_LOCKED, MYF(0),
|
||||
table_list->table_name);
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
}
|
||||
if ((table= delayed_get_table(thd,table_list)) && !thd->is_fatal_error)
|
||||
{
|
||||
/*
|
||||
|
@ -1503,8 +1549,14 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
Delayed_insert - context of a thread responsible for delayed insert
|
||||
into one table. When processing delayed inserts, we create an own
|
||||
thread for every distinct table. Later on all delayed inserts directed
|
||||
into that table are handled by a dedicated thread.
|
||||
*/
|
||||
|
||||
class delayed_insert :public ilink {
|
||||
class Delayed_insert :public ilink {
|
||||
uint locks_in_memory;
|
||||
public:
|
||||
THD thd;
|
||||
|
@ -1518,7 +1570,7 @@ public:
|
|||
ulong group_count;
|
||||
TABLE_LIST table_list; // Argument
|
||||
|
||||
delayed_insert()
|
||||
Delayed_insert()
|
||||
:locks_in_memory(0),
|
||||
table(0),tables_in_use(0),stacked_inserts(0), status(0), dead(0),
|
||||
group_count(0)
|
||||
|
@ -1548,7 +1600,7 @@ public:
|
|||
delayed_insert_threads++;
|
||||
VOID(pthread_mutex_unlock(&LOCK_thread_count));
|
||||
}
|
||||
~delayed_insert()
|
||||
~Delayed_insert()
|
||||
{
|
||||
/* The following is not really needed, but just for safety */
|
||||
delayed_row *row;
|
||||
|
@ -1596,15 +1648,21 @@ public:
|
|||
};
|
||||
|
||||
|
||||
I_List<delayed_insert> delayed_threads;
|
||||
I_List<Delayed_insert> delayed_threads;
|
||||
|
||||
|
||||
delayed_insert *find_handler(THD *thd, TABLE_LIST *table_list)
|
||||
/**
|
||||
Return an instance of delayed insert thread that can handle
|
||||
inserts into a given table, if it exists. Otherwise return NULL.
|
||||
*/
|
||||
|
||||
static
|
||||
Delayed_insert *find_handler(THD *thd, TABLE_LIST *table_list)
|
||||
{
|
||||
thd->proc_info="waiting for delay_list";
|
||||
pthread_mutex_lock(&LOCK_delayed_insert); // Protect master list
|
||||
I_List_iterator<delayed_insert> it(delayed_threads);
|
||||
delayed_insert *tmp;
|
||||
I_List_iterator<Delayed_insert> it(delayed_threads);
|
||||
Delayed_insert *tmp;
|
||||
while ((tmp=it++))
|
||||
{
|
||||
if (!strcmp(tmp->thd.db, table_list->db) &&
|
||||
|
@ -1619,10 +1677,22 @@ delayed_insert *find_handler(THD *thd, TABLE_LIST *table_list)
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
Attempt to find or create a delayed insert thread to handle inserts
|
||||
into this table.
|
||||
|
||||
@return Return a local copy of the table in the delayed thread
|
||||
@retval NULL too many delayed threads OR
|
||||
this thread ran out of resources OR
|
||||
a newly created delayed insert thread ran out of resources OR
|
||||
the delayed insert thread failed to open the table.
|
||||
In the last three cases an error is set in THD.
|
||||
*/
|
||||
|
||||
static TABLE *delayed_get_table(THD *thd,TABLE_LIST *table_list)
|
||||
{
|
||||
int error;
|
||||
delayed_insert *tmp;
|
||||
Delayed_insert *tmp;
|
||||
TABLE *table;
|
||||
DBUG_ENTER("delayed_get_table");
|
||||
|
||||
|
@ -1646,9 +1716,9 @@ static TABLE *delayed_get_table(THD *thd,TABLE_LIST *table_list)
|
|||
*/
|
||||
if (! (tmp= find_handler(thd, table_list)))
|
||||
{
|
||||
if (!(tmp=new delayed_insert()))
|
||||
if (!(tmp=new Delayed_insert()))
|
||||
{
|
||||
my_error(ER_OUTOFMEMORY,MYF(0),sizeof(delayed_insert));
|
||||
my_error(ER_OUTOFMEMORY,MYF(0),sizeof(Delayed_insert));
|
||||
goto err1;
|
||||
}
|
||||
pthread_mutex_lock(&LOCK_thread_count);
|
||||
|
@ -1727,21 +1797,27 @@ static TABLE *delayed_get_table(THD *thd,TABLE_LIST *table_list)
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
As we can't let many threads modify the same TABLE structure, we create
|
||||
an own structure for each tread. This includes a row buffer to save the
|
||||
column values and new fields that points to the new row buffer.
|
||||
The memory is allocated in the client thread and is freed automaticly.
|
||||
/**
|
||||
As we can't let many client threads modify the same TABLE
|
||||
structure of the dedicated delayed insert thread, we create an
|
||||
own structure for each client thread. This includes a row
|
||||
buffer to save the column values and new fields that point to
|
||||
the new row buffer. The memory is allocated in the client
|
||||
thread and is freed automatically.
|
||||
|
||||
@pre This function is called from the client thread. Delayed
|
||||
insert thread mutex must be acquired before invoking this
|
||||
function.
|
||||
*/
|
||||
|
||||
TABLE *delayed_insert::get_local_table(THD* client_thd)
|
||||
TABLE *Delayed_insert::get_local_table(THD* client_thd)
|
||||
{
|
||||
my_ptrdiff_t adjust_ptrs;
|
||||
Field **field,**org_field, *found_next_number_field;
|
||||
TABLE *copy;
|
||||
TABLE_SHARE *share= table->s;
|
||||
byte *bitmap;
|
||||
DBUG_ENTER("delayed_insert::get_local_table");
|
||||
DBUG_ENTER("Delayed_insert::get_local_table");
|
||||
|
||||
/* First request insert thread to get a lock */
|
||||
status=1;
|
||||
|
@ -1851,7 +1927,7 @@ write_delayed(THD *thd,TABLE *table, enum_duplicates duplic,
|
|||
LEX_STRING query, bool ignore, bool log_on)
|
||||
{
|
||||
delayed_row *row= 0;
|
||||
delayed_insert *di=thd->di;
|
||||
Delayed_insert *di=thd->di;
|
||||
const Discrete_interval *forced_auto_inc;
|
||||
DBUG_ENTER("write_delayed");
|
||||
DBUG_PRINT("enter", ("query = '%s' length %u", query.str, query.length));
|
||||
|
@ -1934,7 +2010,7 @@ write_delayed(THD *thd,TABLE *table, enum_duplicates duplic,
|
|||
static void end_delayed_insert(THD *thd)
|
||||
{
|
||||
DBUG_ENTER("end_delayed_insert");
|
||||
delayed_insert *di=thd->di;
|
||||
Delayed_insert *di=thd->di;
|
||||
pthread_mutex_lock(&di->mutex);
|
||||
DBUG_PRINT("info",("tables in use: %d",di->tables_in_use));
|
||||
if (!--di->tables_in_use || di->thd.killed)
|
||||
|
@ -1953,8 +2029,8 @@ void kill_delayed_threads(void)
|
|||
{
|
||||
VOID(pthread_mutex_lock(&LOCK_delayed_insert)); // For unlink from list
|
||||
|
||||
I_List_iterator<delayed_insert> it(delayed_threads);
|
||||
delayed_insert *tmp;
|
||||
I_List_iterator<Delayed_insert> it(delayed_threads);
|
||||
Delayed_insert *tmp;
|
||||
while ((tmp=it++))
|
||||
{
|
||||
tmp->thd.killed= THD::KILL_CONNECTION;
|
||||
|
@ -1986,7 +2062,7 @@ void kill_delayed_threads(void)
|
|||
|
||||
pthread_handler_t handle_delayed_insert(void *arg)
|
||||
{
|
||||
delayed_insert *di=(delayed_insert*) arg;
|
||||
Delayed_insert *di=(Delayed_insert*) arg;
|
||||
THD *thd= &di->thd;
|
||||
|
||||
pthread_detach_this_thread();
|
||||
|
@ -2237,7 +2313,7 @@ static void free_delayed_insert_blobs(register TABLE *table)
|
|||
}
|
||||
|
||||
|
||||
bool delayed_insert::handle_inserts(void)
|
||||
bool Delayed_insert::handle_inserts(void)
|
||||
{
|
||||
int error;
|
||||
ulong max_rows;
|
||||
|
@ -2262,7 +2338,7 @@ bool delayed_insert::handle_inserts(void)
|
|||
|
||||
thd.proc_info="insert";
|
||||
max_rows= delayed_insert_limit;
|
||||
if (thd.killed || table->s->version != refresh_version)
|
||||
if (thd.killed || table->needs_reopen_or_name_lock())
|
||||
{
|
||||
thd.killed= THD::KILL_CONNECTION;
|
||||
max_rows= ULONG_MAX; // Do as much as possible
|
||||
|
@ -2963,8 +3039,8 @@ bool select_insert::send_eof()
|
|||
***************************************************************************/
|
||||
|
||||
/*
|
||||
Create table from lists of fields and items (or open existing table
|
||||
with same name).
|
||||
Create table from lists of fields and items (or just return TABLE
|
||||
object for pre-opened existing table).
|
||||
|
||||
SYNOPSIS
|
||||
create_table_from_items()
|
||||
|
@ -2979,19 +3055,25 @@ bool select_insert::send_eof()
|
|||
of fields for the table (corresponding fields will
|
||||
be added to the end of 'extra_fields' list)
|
||||
lock out Pointer to the MYSQL_LOCK object for table created
|
||||
(open) will be returned in this parameter. Since
|
||||
this table is not included in THD::lock caller is
|
||||
responsible for explicitly unlocking this table.
|
||||
(or open temporary table) will be returned in this
|
||||
parameter. Since this table is not included in
|
||||
THD::lock caller is responsible for explicitly
|
||||
unlocking this table.
|
||||
hooks
|
||||
|
||||
NOTES
|
||||
If 'create_info->options' bitmask has HA_LEX_CREATE_IF_NOT_EXISTS
|
||||
flag and table with name provided already exists then this function will
|
||||
simply open existing table.
|
||||
Also note that create, open and lock sequence in this function is not
|
||||
atomic and thus contains gap for deadlock and can cause other troubles.
|
||||
Since this function contains some logic specific to CREATE TABLE ... SELECT
|
||||
it should be changed before it can be used in other contexts.
|
||||
This function behaves differently for base and temporary tables:
|
||||
- For base table we assume that either table exists and was pre-opened
|
||||
and locked at open_and_lock_tables() stage (and in this case we just
|
||||
emit error or warning and return pre-opened TABLE object) or special
|
||||
placeholder was put in table cache that guarantees that this table
|
||||
won't be created or opened until the placeholder will be removed
|
||||
(so there is an exclusive lock on this table).
|
||||
- We don't pre-open existing temporary table, instead we either open
|
||||
or create and then open table in this function.
|
||||
|
||||
Since this function contains some logic specific to CREATE TABLE ...
|
||||
SELECT it should be changed before it can be used in other contexts.
|
||||
|
||||
RETURN VALUES
|
||||
non-zero Pointer to TABLE object for table created or opened
|
||||
|
@ -3017,6 +3099,25 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
|
|||
bool not_used;
|
||||
DBUG_ENTER("create_table_from_items");
|
||||
|
||||
DBUG_EXECUTE_IF("sleep_create_select_before_check_if_exists", my_sleep(6000000););
|
||||
|
||||
if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE) &&
|
||||
create_table->table->db_stat)
|
||||
{
|
||||
/* Table already exists and was open at open_and_lock_tables() stage. */
|
||||
if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
|
||||
{
|
||||
create_info->table_existed= 1; // Mark that table existed
|
||||
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
|
||||
ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR),
|
||||
create_table->table_name);
|
||||
DBUG_RETURN(create_table->table);
|
||||
}
|
||||
|
||||
my_error(ER_TABLE_EXISTS_ERROR, MYF(0), create_table->table_name);
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
tmp_table.alias= 0;
|
||||
tmp_table.timestamp_field= 0;
|
||||
tmp_table.s= &share;
|
||||
|
@ -3048,8 +3149,15 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
|
|||
cr_field->flags &= ~NOT_NULL_FLAG;
|
||||
extra_fields->push_back(cr_field);
|
||||
}
|
||||
|
||||
DBUG_EXECUTE_IF("sleep_create_select_before_create", my_sleep(6000000););
|
||||
|
||||
/*
|
||||
create and lock table
|
||||
Create and lock table.
|
||||
|
||||
Note that we either creating (or opening existing) temporary table or
|
||||
creating base table on which name we have exclusive lock. So code below
|
||||
should not cause deadlocks or races.
|
||||
|
||||
We don't log the statement, it will be logged later.
|
||||
|
||||
|
@ -3059,63 +3167,74 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
|
|||
don't want to delete from it) 2) it would be written before the CREATE
|
||||
TABLE, which is a wrong order. So we keep binary logging disabled when we
|
||||
open_table().
|
||||
NOTE: By locking table which we just have created (or for which we just
|
||||
have have found that it already exists) separately from other tables used
|
||||
by the statement we create potential window for deadlock.
|
||||
TODO: create and open should be done atomic !
|
||||
*/
|
||||
{
|
||||
tmp_disable_binlog(thd);
|
||||
if (!mysql_create_table(thd, create_table->db, create_table->table_name,
|
||||
create_info, *extra_fields, *keys, 0,
|
||||
select_field_count, 0))
|
||||
if (!mysql_create_table_no_lock(thd, create_table->db,
|
||||
create_table->table_name,
|
||||
create_info, *extra_fields, *keys, 0,
|
||||
select_field_count, 0))
|
||||
{
|
||||
/*
|
||||
If we are here in prelocked mode we either create temporary table
|
||||
or prelocked mode is caused by the SELECT part of this statement.
|
||||
*/
|
||||
DBUG_ASSERT(!thd->prelocked_mode ||
|
||||
create_info->options & HA_LEX_CREATE_TMP_TABLE ||
|
||||
thd->lex->requires_prelocking());
|
||||
|
||||
/*
|
||||
NOTE: We don't want to ignore set of locked tables here if we are
|
||||
under explicit LOCK TABLES since it will open gap for deadlock
|
||||
too wide (and also is not backward compatible).
|
||||
*/
|
||||
if (create_info->table_existed &&
|
||||
!(create_info->options & HA_LEX_CREATE_TMP_TABLE))
|
||||
{
|
||||
/*
|
||||
This means that someone created table underneath server
|
||||
or it was created via different mysqld front-end to the
|
||||
cluster. We don't have much options but throw an error.
|
||||
*/
|
||||
my_error(ER_TABLE_EXISTS_ERROR, MYF(0), create_table->table_name);
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
DBUG_EXECUTE_IF("sleep_create_select_before_open", my_sleep(6000000););
|
||||
|
||||
if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE))
|
||||
{
|
||||
VOID(pthread_mutex_lock(&LOCK_open));
|
||||
if (reopen_name_locked_table(thd, create_table, FALSE))
|
||||
{
|
||||
quick_rm_table(create_info->db_type, create_table->db,
|
||||
table_case_name(create_info, create_table->table_name),
|
||||
0);
|
||||
}
|
||||
else
|
||||
table= create_table->table;
|
||||
VOID(pthread_mutex_unlock(&LOCK_open));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!(table= open_table(thd, create_table, thd->mem_root, (bool*) 0,
|
||||
MYSQL_OPEN_TEMPORARY_ONLY)) &&
|
||||
!create_info->table_existed)
|
||||
{
|
||||
/*
|
||||
This shouldn't happen as creation of temporary table should make
|
||||
it preparable for open. But let us do close_temporary_table() here
|
||||
just in case.
|
||||
*/
|
||||
close_temporary_table(thd, create_table);
|
||||
}
|
||||
}
|
||||
|
||||
if (! (table= open_table(thd, create_table, thd->mem_root, (bool*) 0,
|
||||
(MYSQL_LOCK_IGNORE_FLUSH |
|
||||
((thd->prelocked_mode == PRELOCKED) ?
|
||||
MYSQL_OPEN_IGNORE_LOCKED_TABLES:0)))))
|
||||
quick_rm_table(create_info->db_type, create_table->db,
|
||||
table_case_name(create_info, create_table->table_name),
|
||||
0);
|
||||
}
|
||||
reenable_binlog(thd);
|
||||
if (!table) // open failed
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
/*
|
||||
FIXME: What happens if trigger manages to be created while we are
|
||||
obtaining this lock ? May be it is sensible just to disable
|
||||
trigger execution in this case ? Or will MYSQL_LOCK_IGNORE_FLUSH
|
||||
save us from that ?
|
||||
*/
|
||||
DBUG_EXECUTE_IF("sleep_create_select_before_lock", my_sleep(6000000););
|
||||
|
||||
table->reginfo.lock_type=TL_WRITE;
|
||||
hooks->prelock(&table, 1); // Call prelock hooks
|
||||
if (! ((*lock)= mysql_lock_tables(thd, &table, 1,
|
||||
MYSQL_LOCK_IGNORE_FLUSH, ¬_used)))
|
||||
{
|
||||
VOID(pthread_mutex_lock(&LOCK_open));
|
||||
hash_delete(&open_cache,(byte*) table);
|
||||
VOID(pthread_mutex_unlock(&LOCK_open));
|
||||
quick_rm_table(create_info->db_type, create_table->db,
|
||||
table_case_name(create_info, create_table->table_name), 0);
|
||||
if (!create_info->table_existed)
|
||||
drop_open_table(thd, table, create_table->db, create_table->table_name);
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
table->file->extra(HA_EXTRA_WRITE_CACHE);
|
||||
DBUG_RETURN(table);
|
||||
}
|
||||
|
||||
|
@ -3218,6 +3337,7 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
|
|||
if (check_that_all_fields_are_given_values(thd, table, table_list))
|
||||
DBUG_RETURN(1);
|
||||
table->mark_columns_needed_for_insert();
|
||||
table->file->extra(HA_EXTRA_WRITE_CACHE);
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
|
@ -3320,24 +3440,19 @@ bool select_create::send_eof()
|
|||
|
||||
table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
|
||||
table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE);
|
||||
VOID(pthread_mutex_lock(&LOCK_open));
|
||||
mysql_unlock_tables(thd, thd->extra_lock);
|
||||
if (!table->s->tmp_table)
|
||||
if (thd->extra_lock)
|
||||
{
|
||||
if (close_thread_table(thd, &table))
|
||||
broadcast_refresh();
|
||||
mysql_unlock_tables(thd, thd->extra_lock);
|
||||
thd->extra_lock=0;
|
||||
}
|
||||
thd->extra_lock=0;
|
||||
table=0;
|
||||
VOID(pthread_mutex_unlock(&LOCK_open));
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
|
||||
void select_create::abort()
|
||||
{
|
||||
DBUG_ENTER("select_create::abort");
|
||||
VOID(pthread_mutex_lock(&LOCK_open));
|
||||
|
||||
/*
|
||||
We roll back the statement, including truncating the transaction
|
||||
|
@ -3365,24 +3480,10 @@ void select_create::abort()
|
|||
{
|
||||
table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
|
||||
table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE);
|
||||
handlerton *table_type=table->s->db_type();
|
||||
if (!table->s->tmp_table)
|
||||
{
|
||||
ulong version= table->s->version;
|
||||
table->s->version= 0;
|
||||
hash_delete(&open_cache,(byte*) table);
|
||||
if (!create_info->table_existed)
|
||||
quick_rm_table(table_type, create_table->db,
|
||||
create_table->table_name, 0);
|
||||
/* Tell threads waiting for refresh that something has happened */
|
||||
if (version != refresh_version)
|
||||
broadcast_refresh();
|
||||
}
|
||||
else if (!create_info->table_existed)
|
||||
close_temporary_table(thd, table, 1, 1);
|
||||
if (!create_info->table_existed)
|
||||
drop_open_table(thd, table, create_table->db, create_table->table_name);
|
||||
table=0; // Safety
|
||||
}
|
||||
VOID(pthread_mutex_unlock(&LOCK_open));
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
|
@ -3394,8 +3495,8 @@ void select_create::abort()
|
|||
#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
|
||||
template class List_iterator_fast<List_item>;
|
||||
#ifndef EMBEDDED_LIBRARY
|
||||
template class I_List<delayed_insert>;
|
||||
template class I_List_iterator<delayed_insert>;
|
||||
template class I_List<Delayed_insert>;
|
||||
template class I_List_iterator<Delayed_insert>;
|
||||
template class I_List<delayed_row>;
|
||||
#endif /* EMBEDDED_LIBRARY */
|
||||
#endif /* HAVE_EXPLICIT_TEMPLATE_INSTANTIATION */
|
||||
|
|
310
sql/sql_lex.cc
310
sql/sql_lex.cc
|
@ -33,13 +33,13 @@ sys_var *trg_new_row_fake_var= (sys_var*) 0x01;
|
|||
|
||||
/* Macros to look like lex */
|
||||
|
||||
#define yyGet() ((uchar) *(lex->ptr++))
|
||||
#define yyGetLast() ((uchar) lex->ptr[-1])
|
||||
#define yyPeek() ((uchar) lex->ptr[0])
|
||||
#define yyPeek2() ((uchar) lex->ptr[1])
|
||||
#define yyUnget() lex->ptr--
|
||||
#define yySkip() lex->ptr++
|
||||
#define yyLength() ((uint) (lex->ptr - lex->tok_start)-1)
|
||||
#define yyGet() ((uchar) *(lip->ptr++))
|
||||
#define yyGetLast() ((uchar) lip->ptr[-1])
|
||||
#define yyPeek() ((uchar) lip->ptr[0])
|
||||
#define yyPeek2() ((uchar) lip->ptr[1])
|
||||
#define yyUnget() lip->ptr--
|
||||
#define yySkip() lip->ptr++
|
||||
#define yyLength() ((uint) (lip->ptr - lip->tok_start)-1)
|
||||
|
||||
/* Longest standard keyword name */
|
||||
#define TOCK_NAME_LENGTH 24
|
||||
|
@ -120,6 +120,29 @@ st_parsing_options::reset()
|
|||
allows_derived= TRUE;
|
||||
}
|
||||
|
||||
Lex_input_stream::Lex_input_stream(THD *thd,
|
||||
const char* buffer,
|
||||
unsigned int length)
|
||||
: m_thd(thd),
|
||||
yylineno(1),
|
||||
yytoklen(0),
|
||||
yylval(NULL),
|
||||
ptr(buffer),
|
||||
tok_start(NULL),
|
||||
tok_end(NULL),
|
||||
end_of_query(buffer + length),
|
||||
tok_start_prev(NULL),
|
||||
buf(buffer),
|
||||
next_state(MY_LEX_START),
|
||||
found_semicolon(NULL),
|
||||
ignore_space(test(thd->variables.sql_mode & MODE_IGNORE_SPACE)),
|
||||
stmt_prepare_mode(FALSE)
|
||||
{
|
||||
}
|
||||
|
||||
Lex_input_stream::~Lex_input_stream()
|
||||
{}
|
||||
|
||||
|
||||
/*
|
||||
This is called before every query that is to be parsed.
|
||||
|
@ -127,14 +150,12 @@ st_parsing_options::reset()
|
|||
(We already do too much here)
|
||||
*/
|
||||
|
||||
void lex_start(THD *thd, const char *buf, uint length)
|
||||
void lex_start(THD *thd)
|
||||
{
|
||||
LEX *lex= thd->lex;
|
||||
DBUG_ENTER("lex_start");
|
||||
|
||||
lex->thd= lex->unit.thd= thd;
|
||||
lex->buf= lex->ptr= buf;
|
||||
lex->end_of_query= buf+length;
|
||||
|
||||
lex->context_stack.empty();
|
||||
lex->unit.init_query();
|
||||
|
@ -164,17 +185,13 @@ void lex_start(THD *thd, const char *buf, uint length)
|
|||
lex->describe= 0;
|
||||
lex->subqueries= FALSE;
|
||||
lex->view_prepare_mode= FALSE;
|
||||
lex->stmt_prepare_mode= FALSE;
|
||||
lex->derived_tables= 0;
|
||||
lex->lock_option= TL_READ;
|
||||
lex->found_semicolon= 0;
|
||||
lex->safe_to_cache_query= 1;
|
||||
lex->leaf_tables_insert= 0;
|
||||
lex->parsing_options.reset();
|
||||
lex->empty_field_list_on_rset= 0;
|
||||
lex->select_lex.select_number= 1;
|
||||
lex->next_state=MY_LEX_START;
|
||||
lex->yylineno = 1;
|
||||
lex->in_comment=0;
|
||||
lex->length=0;
|
||||
lex->part_info= 0;
|
||||
|
@ -184,7 +201,6 @@ void lex_start(THD *thd, const char *buf, uint length)
|
|||
lex->select_lex.ftfunc_list= &lex->select_lex.ftfunc_list_alloc;
|
||||
lex->select_lex.group_list.empty();
|
||||
lex->select_lex.order_list.empty();
|
||||
lex->ignore_space=test(thd->variables.sql_mode & MODE_IGNORE_SPACE);
|
||||
lex->sql_command= SQLCOM_END;
|
||||
lex->duplicates= DUP_ERROR;
|
||||
lex->ignore= 0;
|
||||
|
@ -243,24 +259,24 @@ void lex_end(LEX *lex)
|
|||
}
|
||||
|
||||
|
||||
static int find_keyword(LEX *lex, uint len, bool function)
|
||||
static int find_keyword(Lex_input_stream *lip, uint len, bool function)
|
||||
{
|
||||
const char *tok= lex->tok_start;
|
||||
const char *tok= lip->tok_start;
|
||||
|
||||
SYMBOL *symbol= get_hash_symbol(tok, len, function);
|
||||
if (symbol)
|
||||
{
|
||||
lex->yylval->symbol.symbol=symbol;
|
||||
lex->yylval->symbol.str= (char*) tok;
|
||||
lex->yylval->symbol.length=len;
|
||||
|
||||
lip->yylval->symbol.symbol=symbol;
|
||||
lip->yylval->symbol.str= (char*) tok;
|
||||
lip->yylval->symbol.length=len;
|
||||
|
||||
if ((symbol->tok == NOT_SYM) &&
|
||||
(lex->thd->variables.sql_mode & MODE_HIGH_NOT_PRECEDENCE))
|
||||
(lip->m_thd->variables.sql_mode & MODE_HIGH_NOT_PRECEDENCE))
|
||||
return NOT2_SYM;
|
||||
if ((symbol->tok == OR_OR_SYM) &&
|
||||
!(lex->thd->variables.sql_mode & MODE_PIPES_AS_CONCAT))
|
||||
!(lip->m_thd->variables.sql_mode & MODE_PIPES_AS_CONCAT))
|
||||
return OR2_SYM;
|
||||
|
||||
|
||||
return symbol->tok;
|
||||
}
|
||||
return 0;
|
||||
|
@ -293,12 +309,12 @@ bool is_lex_native_function(const LEX_STRING *name)
|
|||
|
||||
/* make a copy of token before ptr and set yytoklen */
|
||||
|
||||
static LEX_STRING get_token(LEX *lex,uint length)
|
||||
static LEX_STRING get_token(Lex_input_stream *lip, uint skip, uint length)
|
||||
{
|
||||
LEX_STRING tmp;
|
||||
yyUnget(); // ptr points now after last token char
|
||||
tmp.length=lex->yytoklen=length;
|
||||
tmp.str=(char*) lex->thd->strmake((char*) lex->tok_start,tmp.length);
|
||||
tmp.length=lip->yytoklen=length;
|
||||
tmp.str= lip->m_thd->strmake(lip->tok_start + skip, tmp.length);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
|
@ -309,17 +325,20 @@ static LEX_STRING get_token(LEX *lex,uint length)
|
|||
future to operate multichar strings (like ucs2)
|
||||
*/
|
||||
|
||||
static LEX_STRING get_quoted_token(LEX *lex,uint length, char quote)
|
||||
static LEX_STRING get_quoted_token(Lex_input_stream *lip,
|
||||
uint skip,
|
||||
uint length, char quote)
|
||||
{
|
||||
LEX_STRING tmp;
|
||||
const char *from, *end;
|
||||
char *to;
|
||||
yyUnget(); // ptr points now after last token char
|
||||
tmp.length=lex->yytoklen=length;
|
||||
tmp.str=(char*) lex->thd->alloc(tmp.length+1);
|
||||
for (from= lex->tok_start, to= tmp.str, end= to+length ;
|
||||
to != end ;
|
||||
)
|
||||
tmp.length= lip->yytoklen=length;
|
||||
tmp.str=(char*) lip->m_thd->alloc(tmp.length+1);
|
||||
from= lip->tok_start + skip;
|
||||
to= tmp.str;
|
||||
end= to+length;
|
||||
for ( ; to != end; )
|
||||
{
|
||||
if ((*to++= *from++) == quote)
|
||||
from++; // Skip double quotes
|
||||
|
@ -334,31 +353,31 @@ static LEX_STRING get_quoted_token(LEX *lex,uint length, char quote)
|
|||
Fix sometimes to do only one scan of the string
|
||||
*/
|
||||
|
||||
static char *get_text(LEX *lex)
|
||||
static char *get_text(Lex_input_stream *lip)
|
||||
{
|
||||
reg1 uchar c,sep;
|
||||
uint found_escape=0;
|
||||
CHARSET_INFO *cs= lex->thd->charset();
|
||||
CHARSET_INFO *cs= lip->m_thd->charset();
|
||||
|
||||
sep= yyGetLast(); // String should end with this
|
||||
while (lex->ptr != lex->end_of_query)
|
||||
while (lip->ptr != lip->end_of_query)
|
||||
{
|
||||
c = yyGet();
|
||||
#ifdef USE_MB
|
||||
{
|
||||
int l;
|
||||
if (use_mb(cs) &&
|
||||
(l = my_ismbchar(cs, lex->ptr-1, lex->end_of_query))) {
|
||||
lex->ptr += l-1;
|
||||
(l = my_ismbchar(cs, lip->ptr-1, lip->end_of_query))) {
|
||||
lip->ptr += l-1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (c == '\\' &&
|
||||
!(lex->thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES))
|
||||
!(lip->m_thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES))
|
||||
{ // Escaped character
|
||||
found_escape=1;
|
||||
if (lex->ptr == lex->end_of_query)
|
||||
if (lip->ptr == lip->end_of_query)
|
||||
return 0;
|
||||
yySkip();
|
||||
}
|
||||
|
@ -376,15 +395,15 @@ static char *get_text(LEX *lex)
|
|||
const char *str, *end;
|
||||
char *start;
|
||||
|
||||
str=lex->tok_start+1;
|
||||
end=lex->ptr-1;
|
||||
if (!(start= (char*) lex->thd->alloc((uint) (end-str)+1)))
|
||||
str=lip->tok_start+1;
|
||||
end=lip->ptr-1;
|
||||
if (!(start= (char*) lip->m_thd->alloc((uint) (end-str)+1)))
|
||||
return (char*) ""; // Sql_alloc has set error flag
|
||||
if (!found_escape)
|
||||
{
|
||||
lex->yytoklen=(uint) (end-str);
|
||||
memcpy(start,str,lex->yytoklen);
|
||||
start[lex->yytoklen]=0;
|
||||
lip->yytoklen=(uint) (end-str);
|
||||
memcpy(start,str,lip->yytoklen);
|
||||
start[lip->yytoklen]=0;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -402,7 +421,7 @@ static char *get_text(LEX *lex)
|
|||
continue;
|
||||
}
|
||||
#endif
|
||||
if (!(lex->thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES) &&
|
||||
if (!(lip->m_thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES) &&
|
||||
*str == '\\' && str+1 != end)
|
||||
{
|
||||
switch(*++str) {
|
||||
|
@ -439,7 +458,7 @@ static char *get_text(LEX *lex)
|
|||
*to++ = *str;
|
||||
}
|
||||
*to=0;
|
||||
lex->yytoklen=(uint) (to-start);
|
||||
lip->yytoklen=(uint) (to-start);
|
||||
}
|
||||
return start;
|
||||
}
|
||||
|
@ -552,19 +571,21 @@ int MYSQLlex(void *arg, void *yythd)
|
|||
int tokval, result_state;
|
||||
uint length;
|
||||
enum my_lex_states state;
|
||||
LEX *lex= ((THD *)yythd)->lex;
|
||||
THD *thd= (THD *)yythd;
|
||||
Lex_input_stream *lip= thd->m_lip;
|
||||
LEX *lex= thd->lex;
|
||||
YYSTYPE *yylval=(YYSTYPE*) arg;
|
||||
CHARSET_INFO *cs= ((THD *) yythd)->charset();
|
||||
CHARSET_INFO *cs= thd->charset();
|
||||
uchar *state_map= cs->state_map;
|
||||
uchar *ident_map= cs->ident_map;
|
||||
|
||||
lex->yylval=yylval; // The global state
|
||||
lip->yylval=yylval; // The global state
|
||||
|
||||
lex->tok_start_prev= lex->tok_start;
|
||||
lip->tok_start_prev= lip->tok_start;
|
||||
|
||||
lex->tok_start=lex->tok_end=lex->ptr;
|
||||
state=lex->next_state;
|
||||
lex->next_state=MY_LEX_OPERATOR_OR_IDENT;
|
||||
lip->tok_start=lip->tok_end=lip->ptr;
|
||||
state=lip->next_state;
|
||||
lip->next_state=MY_LEX_OPERATOR_OR_IDENT;
|
||||
LINT_INIT(c);
|
||||
for (;;)
|
||||
{
|
||||
|
@ -575,9 +596,9 @@ int MYSQLlex(void *arg, void *yythd)
|
|||
for (c=yyGet() ; (state_map[c] == MY_LEX_SKIP) ; c= yyGet())
|
||||
{
|
||||
if (c == '\n')
|
||||
lex->yylineno++;
|
||||
lip->yylineno++;
|
||||
}
|
||||
lex->tok_start=lex->ptr-1; // Start of real token
|
||||
lip->tok_start=lip->ptr-1; // Start of real token
|
||||
state= (enum my_lex_states) state_map[c];
|
||||
break;
|
||||
case MY_LEX_ESCAPE:
|
||||
|
@ -596,20 +617,20 @@ int MYSQLlex(void *arg, void *yythd)
|
|||
state=MY_LEX_COMMENT;
|
||||
break;
|
||||
}
|
||||
yylval->lex_str.str=(char*) (lex->ptr=lex->tok_start);// Set to first chr
|
||||
yylval->lex_str.str=(char*) (lip->ptr=lip->tok_start);// Set to first chr
|
||||
yylval->lex_str.length=1;
|
||||
c=yyGet();
|
||||
if (c != ')')
|
||||
lex->next_state= MY_LEX_START; // Allow signed numbers
|
||||
lip->next_state= MY_LEX_START; // Allow signed numbers
|
||||
if (c == ',')
|
||||
lex->tok_start=lex->ptr; // Let tok_start point at next item
|
||||
lip->tok_start=lip->ptr; // Let tok_start point at next item
|
||||
/*
|
||||
Check for a placeholder: it should not precede a possible identifier
|
||||
because of binlogging: when a placeholder is replaced with
|
||||
its value in a query for the binlog, the query must stay
|
||||
grammatically correct.
|
||||
*/
|
||||
else if (c == '?' && lex->stmt_prepare_mode && !ident_map[yyPeek()])
|
||||
else if (c == '?' && lip->stmt_prepare_mode && !ident_map[yyPeek()])
|
||||
return(PARAM_MARKER);
|
||||
return((int) c);
|
||||
|
||||
|
@ -620,14 +641,14 @@ int MYSQLlex(void *arg, void *yythd)
|
|||
break;
|
||||
}
|
||||
/* Found N'string' */
|
||||
lex->tok_start++; // Skip N
|
||||
lip->tok_start++; // Skip N
|
||||
yySkip(); // Skip '
|
||||
if (!(yylval->lex_str.str = get_text(lex)))
|
||||
if (!(yylval->lex_str.str = get_text(lip)))
|
||||
{
|
||||
state= MY_LEX_CHAR; // Read char by char
|
||||
break;
|
||||
}
|
||||
yylval->lex_str.length= lex->yytoklen;
|
||||
yylval->lex_str.length= lip->yytoklen;
|
||||
return(NCHAR_STRING);
|
||||
|
||||
case MY_LEX_IDENT_OR_HEX:
|
||||
|
@ -650,21 +671,21 @@ int MYSQLlex(void *arg, void *yythd)
|
|||
result_state= IDENT_QUOTED;
|
||||
if (my_mbcharlen(cs, yyGetLast()) > 1)
|
||||
{
|
||||
int l = my_ismbchar(cs, lex->ptr-1, lex->end_of_query);
|
||||
int l = my_ismbchar(cs, lip->ptr-1, lip->end_of_query);
|
||||
if (l == 0) {
|
||||
state = MY_LEX_CHAR;
|
||||
continue;
|
||||
}
|
||||
lex->ptr += l - 1;
|
||||
lip->ptr += l - 1;
|
||||
}
|
||||
while (ident_map[c=yyGet()])
|
||||
{
|
||||
if (my_mbcharlen(cs, c) > 1)
|
||||
{
|
||||
int l;
|
||||
if ((l = my_ismbchar(cs, lex->ptr-1, lex->end_of_query)) == 0)
|
||||
if ((l = my_ismbchar(cs, lip->ptr-1, lip->end_of_query)) == 0)
|
||||
break;
|
||||
lex->ptr += l-1;
|
||||
lip->ptr += l-1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -675,9 +696,9 @@ int MYSQLlex(void *arg, void *yythd)
|
|||
/* If there were non-ASCII characters, mark that we must convert */
|
||||
result_state= result_state & 0x80 ? IDENT_QUOTED : IDENT;
|
||||
}
|
||||
length= (uint) (lex->ptr - lex->tok_start)-1;
|
||||
start= lex->ptr;
|
||||
if (lex->ignore_space)
|
||||
length= (uint) (lip->ptr - lip->tok_start)-1;
|
||||
start= lip->ptr;
|
||||
if (lip->ignore_space)
|
||||
{
|
||||
/*
|
||||
If we find a space then this can't be an identifier. We notice this
|
||||
|
@ -685,19 +706,19 @@ int MYSQLlex(void *arg, void *yythd)
|
|||
*/
|
||||
for (; state_map[c] == MY_LEX_SKIP ; c= yyGet());
|
||||
}
|
||||
if (start == lex->ptr && c == '.' && ident_map[yyPeek()])
|
||||
lex->next_state=MY_LEX_IDENT_SEP;
|
||||
if (start == lip->ptr && c == '.' && ident_map[yyPeek()])
|
||||
lip->next_state=MY_LEX_IDENT_SEP;
|
||||
else
|
||||
{ // '(' must follow directly if function
|
||||
yyUnget();
|
||||
if ((tokval = find_keyword(lex,length,c == '(')))
|
||||
if ((tokval = find_keyword(lip, length,c == '(')))
|
||||
{
|
||||
lex->next_state= MY_LEX_START; // Allow signed numbers
|
||||
lip->next_state= MY_LEX_START; // Allow signed numbers
|
||||
return(tokval); // Was keyword
|
||||
}
|
||||
yySkip(); // next state does a unget
|
||||
}
|
||||
yylval->lex_str=get_token(lex,length);
|
||||
yylval->lex_str=get_token(lip, 0, length);
|
||||
|
||||
/*
|
||||
Note: "SELECT _bla AS 'alias'"
|
||||
|
@ -714,12 +735,12 @@ int MYSQLlex(void *arg, void *yythd)
|
|||
return(result_state); // IDENT or IDENT_QUOTED
|
||||
|
||||
case MY_LEX_IDENT_SEP: // Found ident and now '.'
|
||||
yylval->lex_str.str=(char*) lex->ptr;
|
||||
yylval->lex_str.str=(char*) lip->ptr;
|
||||
yylval->lex_str.length=1;
|
||||
c=yyGet(); // should be '.'
|
||||
lex->next_state= MY_LEX_IDENT_START;// Next is an ident (not a keyword)
|
||||
lip->next_state= MY_LEX_IDENT_START;// Next is an ident (not a keyword)
|
||||
if (!ident_map[yyPeek()]) // Probably ` or "
|
||||
lex->next_state= MY_LEX_START;
|
||||
lip->next_state= MY_LEX_START;
|
||||
return((int) c);
|
||||
|
||||
case MY_LEX_NUMBER_IDENT: // number or ident which num-start
|
||||
|
@ -739,36 +760,32 @@ int MYSQLlex(void *arg, void *yythd)
|
|||
{
|
||||
yySkip();
|
||||
while (my_isdigit(cs,yyGet())) ;
|
||||
yylval->lex_str=get_token(lex,yyLength());
|
||||
yylval->lex_str=get_token(lip, 0, yyLength());
|
||||
return(FLOAT_NUM);
|
||||
}
|
||||
}
|
||||
yyUnget(); /* purecov: inspected */
|
||||
}
|
||||
else if (c == 'x' && (lex->ptr - lex->tok_start) == 2 &&
|
||||
lex->tok_start[0] == '0' )
|
||||
else if (c == 'x' && (lip->ptr - lip->tok_start) == 2 &&
|
||||
lip->tok_start[0] == '0' )
|
||||
{ // Varbinary
|
||||
while (my_isxdigit(cs,(c = yyGet()))) ;
|
||||
if ((lex->ptr - lex->tok_start) >= 4 && !ident_map[c])
|
||||
if ((lip->ptr - lip->tok_start) >= 4 && !ident_map[c])
|
||||
{
|
||||
yylval->lex_str=get_token(lex,yyLength());
|
||||
yylval->lex_str.str+=2; // Skip 0x
|
||||
yylval->lex_str.length-=2;
|
||||
lex->yytoklen-=2;
|
||||
/* skip '0x' */
|
||||
yylval->lex_str=get_token(lip, 2, yyLength()-2);
|
||||
return (HEX_NUM);
|
||||
}
|
||||
yyUnget();
|
||||
}
|
||||
else if (c == 'b' && (lex->ptr - lex->tok_start) == 2 &&
|
||||
lex->tok_start[0] == '0' )
|
||||
else if (c == 'b' && (lip->ptr - lip->tok_start) == 2 &&
|
||||
lip->tok_start[0] == '0' )
|
||||
{ // b'bin-number'
|
||||
while (my_isxdigit(cs,(c = yyGet()))) ;
|
||||
if ((lex->ptr - lex->tok_start) >= 4 && !ident_map[c])
|
||||
if ((lip->ptr - lip->tok_start) >= 4 && !ident_map[c])
|
||||
{
|
||||
yylval->lex_str= get_token(lex, yyLength());
|
||||
yylval->lex_str.str+= 2; // Skip 0x
|
||||
yylval->lex_str.length-= 2;
|
||||
lex->yytoklen-= 2;
|
||||
/* Skip '0b' */
|
||||
yylval->lex_str= get_token(lip, 2, yyLength()-2);
|
||||
return (BIN_NUM);
|
||||
}
|
||||
yyUnget();
|
||||
|
@ -785,9 +802,9 @@ int MYSQLlex(void *arg, void *yythd)
|
|||
if (my_mbcharlen(cs, c) > 1)
|
||||
{
|
||||
int l;
|
||||
if ((l = my_ismbchar(cs, lex->ptr-1, lex->end_of_query)) == 0)
|
||||
if ((l = my_ismbchar(cs, lip->ptr-1, lip->end_of_query)) == 0)
|
||||
break;
|
||||
lex->ptr += l-1;
|
||||
lip->ptr += l-1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -799,16 +816,15 @@ int MYSQLlex(void *arg, void *yythd)
|
|||
result_state= result_state & 0x80 ? IDENT_QUOTED : IDENT;
|
||||
}
|
||||
if (c == '.' && ident_map[yyPeek()])
|
||||
lex->next_state=MY_LEX_IDENT_SEP;// Next is '.'
|
||||
lip->next_state=MY_LEX_IDENT_SEP;// Next is '.'
|
||||
|
||||
yylval->lex_str= get_token(lex,yyLength());
|
||||
yylval->lex_str= get_token(lip, 0, yyLength());
|
||||
return(result_state);
|
||||
|
||||
case MY_LEX_USER_VARIABLE_DELIMITER: // Found quote char
|
||||
{
|
||||
uint double_quotes= 0;
|
||||
char quote_char= c; // Used char
|
||||
lex->tok_start=lex->ptr; // Skip first `
|
||||
while ((c=yyGet()))
|
||||
{
|
||||
int var_length;
|
||||
|
@ -826,23 +842,24 @@ int MYSQLlex(void *arg, void *yythd)
|
|||
#ifdef USE_MB
|
||||
else if (var_length < 1)
|
||||
break; // Error
|
||||
lex->ptr+= var_length-1;
|
||||
lip->ptr+= var_length-1;
|
||||
#endif
|
||||
}
|
||||
if (double_quotes)
|
||||
yylval->lex_str=get_quoted_token(lex,yyLength() - double_quotes,
|
||||
yylval->lex_str=get_quoted_token(lip, 1,
|
||||
yyLength() - double_quotes -1,
|
||||
quote_char);
|
||||
else
|
||||
yylval->lex_str=get_token(lex,yyLength());
|
||||
yylval->lex_str=get_token(lip, 1, yyLength() -1);
|
||||
if (c == quote_char)
|
||||
yySkip(); // Skip end `
|
||||
lex->next_state= MY_LEX_START;
|
||||
lip->next_state= MY_LEX_START;
|
||||
return(IDENT_QUOTED);
|
||||
}
|
||||
case MY_LEX_INT_OR_REAL: // Compleat int or incompleat real
|
||||
case MY_LEX_INT_OR_REAL: // Complete int or incomplete real
|
||||
if (c != '.')
|
||||
{ // Found complete integer number.
|
||||
yylval->lex_str=get_token(lex,yyLength());
|
||||
yylval->lex_str=get_token(lip, 0, yyLength());
|
||||
return int_token(yylval->lex_str.str,yylval->lex_str.length);
|
||||
}
|
||||
// fall through
|
||||
|
@ -860,47 +877,45 @@ int MYSQLlex(void *arg, void *yythd)
|
|||
break;
|
||||
}
|
||||
while (my_isdigit(cs,yyGet())) ;
|
||||
yylval->lex_str=get_token(lex,yyLength());
|
||||
yylval->lex_str=get_token(lip, 0, yyLength());
|
||||
return(FLOAT_NUM);
|
||||
}
|
||||
yylval->lex_str=get_token(lex,yyLength());
|
||||
yylval->lex_str=get_token(lip, 0, yyLength());
|
||||
return(DECIMAL_NUM);
|
||||
|
||||
case MY_LEX_HEX_NUMBER: // Found x'hexstring'
|
||||
yyGet(); // Skip '
|
||||
while (my_isxdigit(cs,(c = yyGet()))) ;
|
||||
length=(lex->ptr - lex->tok_start); // Length of hexnum+3
|
||||
length=(lip->ptr - lip->tok_start); // Length of hexnum+3
|
||||
if (!(length & 1) || c != '\'')
|
||||
{
|
||||
return(ABORT_SYM); // Illegal hex constant
|
||||
}
|
||||
yyGet(); // get_token makes an unget
|
||||
yylval->lex_str=get_token(lex,length);
|
||||
yylval->lex_str.str+=2; // Skip x'
|
||||
yylval->lex_str.length-=3; // Don't count x' and last '
|
||||
lex->yytoklen-=3;
|
||||
yylval->lex_str=get_token(lip,
|
||||
2, // skip x'
|
||||
length-3); // don't count x' and last '
|
||||
return (HEX_NUM);
|
||||
|
||||
case MY_LEX_BIN_NUMBER: // Found b'bin-string'
|
||||
yyGet(); // Skip '
|
||||
while ((c= yyGet()) == '0' || c == '1');
|
||||
length= (lex->ptr - lex->tok_start); // Length of bin-num + 3
|
||||
length= (lip->ptr - lip->tok_start); // Length of bin-num + 3
|
||||
if (c != '\'')
|
||||
return(ABORT_SYM); // Illegal hex constant
|
||||
yyGet(); // get_token makes an unget
|
||||
yylval->lex_str= get_token(lex, length);
|
||||
yylval->lex_str.str+= 2; // Skip b'
|
||||
yylval->lex_str.length-= 3; // Don't count b' and last '
|
||||
lex->yytoklen-= 3;
|
||||
return (BIN_NUM);
|
||||
yylval->lex_str= get_token(lip,
|
||||
2, // skip b'
|
||||
length-3); // don't count b' and last '
|
||||
return (BIN_NUM);
|
||||
|
||||
case MY_LEX_CMP_OP: // Incomplete comparison operator
|
||||
if (state_map[yyPeek()] == MY_LEX_CMP_OP ||
|
||||
state_map[yyPeek()] == MY_LEX_LONG_CMP_OP)
|
||||
yySkip();
|
||||
if ((tokval = find_keyword(lex,(uint) (lex->ptr - lex->tok_start),0)))
|
||||
if ((tokval = find_keyword(lip, (uint) (lip->ptr - lip->tok_start),0)))
|
||||
{
|
||||
lex->next_state= MY_LEX_START; // Allow signed numbers
|
||||
lip->next_state= MY_LEX_START; // Allow signed numbers
|
||||
return(tokval);
|
||||
}
|
||||
state = MY_LEX_CHAR; // Something fishy found
|
||||
|
@ -914,9 +929,9 @@ int MYSQLlex(void *arg, void *yythd)
|
|||
if (state_map[yyPeek()] == MY_LEX_CMP_OP)
|
||||
yySkip();
|
||||
}
|
||||
if ((tokval = find_keyword(lex,(uint) (lex->ptr - lex->tok_start),0)))
|
||||
if ((tokval = find_keyword(lip, (uint) (lip->ptr - lip->tok_start),0)))
|
||||
{
|
||||
lex->next_state= MY_LEX_START; // Found long op
|
||||
lip->next_state= MY_LEX_START; // Found long op
|
||||
return(tokval);
|
||||
}
|
||||
state = MY_LEX_CHAR; // Something fishy found
|
||||
|
@ -929,24 +944,24 @@ int MYSQLlex(void *arg, void *yythd)
|
|||
break;
|
||||
}
|
||||
yySkip();
|
||||
tokval = find_keyword(lex,2,0); // Is a bool operator
|
||||
lex->next_state= MY_LEX_START; // Allow signed numbers
|
||||
tokval = find_keyword(lip,2,0); // Is a bool operator
|
||||
lip->next_state= MY_LEX_START; // Allow signed numbers
|
||||
return(tokval);
|
||||
|
||||
case MY_LEX_STRING_OR_DELIMITER:
|
||||
if (((THD *) yythd)->variables.sql_mode & MODE_ANSI_QUOTES)
|
||||
if (thd->variables.sql_mode & MODE_ANSI_QUOTES)
|
||||
{
|
||||
state= MY_LEX_USER_VARIABLE_DELIMITER;
|
||||
break;
|
||||
}
|
||||
/* " used for strings */
|
||||
case MY_LEX_STRING: // Incomplete text string
|
||||
if (!(yylval->lex_str.str = get_text(lex)))
|
||||
if (!(yylval->lex_str.str = get_text(lip)))
|
||||
{
|
||||
state= MY_LEX_CHAR; // Read char by char
|
||||
break;
|
||||
}
|
||||
yylval->lex_str.length=lex->yytoklen;
|
||||
yylval->lex_str.length=lip->yytoklen;
|
||||
return(TEXT_STRING);
|
||||
|
||||
case MY_LEX_COMMENT: // Comment
|
||||
|
@ -970,7 +985,7 @@ int MYSQLlex(void *arg, void *yythd)
|
|||
state=MY_LEX_START;
|
||||
if (my_isdigit(cs,yyPeek()))
|
||||
{ // Version number
|
||||
version=strtol((char*) lex->ptr,(char**) &lex->ptr,10);
|
||||
version=strtol((char*) lip->ptr,(char**) &lip->ptr,10);
|
||||
}
|
||||
if (version <= MYSQL_VERSION_ID)
|
||||
{
|
||||
|
@ -978,13 +993,13 @@ int MYSQLlex(void *arg, void *yythd)
|
|||
break;
|
||||
}
|
||||
}
|
||||
while (lex->ptr != lex->end_of_query &&
|
||||
while (lip->ptr != lip->end_of_query &&
|
||||
((c=yyGet()) != '*' || yyPeek() != '/'))
|
||||
{
|
||||
if (c == '\n')
|
||||
lex->yylineno++;
|
||||
lip->yylineno++;
|
||||
}
|
||||
if (lex->ptr != lex->end_of_query)
|
||||
if (lip->ptr != lip->end_of_query)
|
||||
yySkip(); // remove last '/'
|
||||
state = MY_LEX_START; // Try again
|
||||
break;
|
||||
|
@ -1009,14 +1024,13 @@ int MYSQLlex(void *arg, void *yythd)
|
|||
case MY_LEX_SEMICOLON: // optional line terminator
|
||||
if (yyPeek())
|
||||
{
|
||||
THD* thd= (THD*)yythd;
|
||||
if ((thd->client_capabilities & CLIENT_MULTI_STATEMENTS) &&
|
||||
!lex->stmt_prepare_mode)
|
||||
!lip->stmt_prepare_mode)
|
||||
{
|
||||
lex->safe_to_cache_query= 0;
|
||||
lex->found_semicolon=(char*) lex->ptr;
|
||||
lip->found_semicolon= lip->ptr;
|
||||
thd->server_status|= SERVER_MORE_RESULTS_EXISTS;
|
||||
lex->next_state= MY_LEX_END;
|
||||
lip->next_state= MY_LEX_END;
|
||||
return (END_OF_INPUT);
|
||||
}
|
||||
state= MY_LEX_CHAR; // Return ';'
|
||||
|
@ -1024,15 +1038,15 @@ int MYSQLlex(void *arg, void *yythd)
|
|||
}
|
||||
/* fall true */
|
||||
case MY_LEX_EOL:
|
||||
if (lex->ptr >= lex->end_of_query)
|
||||
if (lip->ptr >= lip->end_of_query)
|
||||
{
|
||||
lex->next_state=MY_LEX_END; // Mark for next loop
|
||||
lip->next_state=MY_LEX_END; // Mark for next loop
|
||||
return(END_OF_INPUT);
|
||||
}
|
||||
state=MY_LEX_CHAR;
|
||||
break;
|
||||
case MY_LEX_END:
|
||||
lex->next_state=MY_LEX_END;
|
||||
lip->next_state=MY_LEX_END;
|
||||
return(0); // We found end of input last time
|
||||
|
||||
/* Actually real shouldn't start with . but allow them anyhow */
|
||||
|
@ -1052,26 +1066,26 @@ int MYSQLlex(void *arg, void *yythd)
|
|||
case MY_LEX_STRING_OR_DELIMITER:
|
||||
break;
|
||||
case MY_LEX_USER_END:
|
||||
lex->next_state=MY_LEX_SYSTEM_VAR;
|
||||
lip->next_state=MY_LEX_SYSTEM_VAR;
|
||||
break;
|
||||
default:
|
||||
lex->next_state=MY_LEX_HOSTNAME;
|
||||
lip->next_state=MY_LEX_HOSTNAME;
|
||||
break;
|
||||
}
|
||||
yylval->lex_str.str=(char*) lex->ptr;
|
||||
yylval->lex_str.str=(char*) lip->ptr;
|
||||
yylval->lex_str.length=1;
|
||||
return((int) '@');
|
||||
case MY_LEX_HOSTNAME: // end '@' of user@hostname
|
||||
for (c=yyGet() ;
|
||||
my_isalnum(cs,c) || c == '.' || c == '_' || c == '$';
|
||||
c= yyGet()) ;
|
||||
yylval->lex_str=get_token(lex,yyLength());
|
||||
yylval->lex_str=get_token(lip, 0, yyLength());
|
||||
return(LEX_HOSTNAME);
|
||||
case MY_LEX_SYSTEM_VAR:
|
||||
yylval->lex_str.str=(char*) lex->ptr;
|
||||
yylval->lex_str.str=(char*) lip->ptr;
|
||||
yylval->lex_str.length=1;
|
||||
yySkip(); // Skip '@'
|
||||
lex->next_state= (state_map[yyPeek()] ==
|
||||
lip->next_state= (state_map[yyPeek()] ==
|
||||
MY_LEX_USER_VARIABLE_DELIMITER ?
|
||||
MY_LEX_OPERATOR_OR_IDENT :
|
||||
MY_LEX_IDENT_OR_KEYWORD);
|
||||
|
@ -1088,16 +1102,16 @@ int MYSQLlex(void *arg, void *yythd)
|
|||
result_state= result_state & 0x80 ? IDENT_QUOTED : IDENT;
|
||||
|
||||
if (c == '.')
|
||||
lex->next_state=MY_LEX_IDENT_SEP;
|
||||
length= (uint) (lex->ptr - lex->tok_start)-1;
|
||||
lip->next_state=MY_LEX_IDENT_SEP;
|
||||
length= (uint) (lip->ptr - lip->tok_start)-1;
|
||||
if (length == 0)
|
||||
return(ABORT_SYM); // Names must be nonempty.
|
||||
if ((tokval= find_keyword(lex,length,0)))
|
||||
if ((tokval= find_keyword(lip, length,0)))
|
||||
{
|
||||
yyUnget(); // Put back 'c'
|
||||
return(tokval); // Was keyword
|
||||
}
|
||||
yylval->lex_str=get_token(lex,length);
|
||||
yylval->lex_str=get_token(lip, 0, length);
|
||||
return(result_state);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -543,7 +543,7 @@ public:
|
|||
void set_thd(THD *thd_arg) { thd= thd_arg; }
|
||||
inline bool is_union ();
|
||||
|
||||
friend void lex_start(THD *thd, const char *buf, uint length);
|
||||
friend void lex_start(THD *thd);
|
||||
friend int subselect_union_engine::exec();
|
||||
|
||||
List<Item> *get_unit_column_types();
|
||||
|
@ -744,7 +744,7 @@ public:
|
|||
void cut_subtree() { slave= 0; }
|
||||
bool test_limit();
|
||||
|
||||
friend void lex_start(THD *thd, const char *buf, uint length);
|
||||
friend void lex_start(THD *thd);
|
||||
st_select_lex() : n_sum_items(0), n_child_sum_items(0) {}
|
||||
void make_empty_select()
|
||||
{
|
||||
|
@ -991,23 +991,72 @@ struct st_parsing_options
|
|||
};
|
||||
|
||||
|
||||
/**
|
||||
This class represents the character input stream consumed during
|
||||
lexical analysis.
|
||||
*/
|
||||
class Lex_input_stream
|
||||
{
|
||||
public:
|
||||
Lex_input_stream(THD *thd, const char* buff, unsigned int length);
|
||||
~Lex_input_stream();
|
||||
|
||||
/** Current thread. */
|
||||
THD *m_thd;
|
||||
|
||||
/** Current line number. */
|
||||
uint yylineno;
|
||||
|
||||
/** Length of the last token parsed. */
|
||||
uint yytoklen;
|
||||
|
||||
/** Interface with bison, value of the last token parsed. */
|
||||
LEX_YYSTYPE yylval;
|
||||
|
||||
/** Pointer to the current position in the input stream. */
|
||||
const char* ptr;
|
||||
|
||||
/** Starting position of the last token parsed. */
|
||||
const char* tok_start;
|
||||
|
||||
/** Ending position of the last token parsed. */
|
||||
const char* tok_end;
|
||||
|
||||
/** End of the query text in the input stream. */
|
||||
const char* end_of_query;
|
||||
|
||||
/** Starting position of the previous token parsed. */
|
||||
const char* tok_start_prev;
|
||||
|
||||
/** Begining of the query text in the input stream. */
|
||||
const char* buf;
|
||||
|
||||
/** Current state of the lexical analyser. */
|
||||
enum my_lex_states next_state;
|
||||
|
||||
/** Position of ';' in the stream, to delimit multiple queries. */
|
||||
const char* found_semicolon;
|
||||
|
||||
/** SQL_MODE = IGNORE_SPACE. */
|
||||
bool ignore_space;
|
||||
/*
|
||||
TRUE if we're parsing a prepared statement: in this mode
|
||||
we should allow placeholders and disallow multi-statements.
|
||||
*/
|
||||
bool stmt_prepare_mode;
|
||||
};
|
||||
|
||||
|
||||
/* The state of the lex parsing. This is saved in the THD struct */
|
||||
|
||||
typedef struct st_lex : public Query_tables_list
|
||||
{
|
||||
uint yylineno,yytoklen; /* Simulate lex */
|
||||
LEX_YYSTYPE yylval;
|
||||
SELECT_LEX_UNIT unit; /* most upper unit */
|
||||
SELECT_LEX select_lex; /* first SELECT_LEX */
|
||||
/* current SELECT_LEX in parsing */
|
||||
SELECT_LEX *current_select;
|
||||
/* list of all SELECT_LEX */
|
||||
SELECT_LEX *all_selects_list;
|
||||
const char *buf; /* The beginning of string, used by SPs */
|
||||
const char *ptr,*tok_start,*tok_end,*end_of_query;
|
||||
|
||||
/* The value of tok_start as they were one call of MYSQLlex before */
|
||||
const char *tok_start_prev;
|
||||
|
||||
char *length,*dec,*change;
|
||||
LEX_STRING name;
|
||||
|
@ -1016,7 +1065,6 @@ typedef struct st_lex : public Query_tables_list
|
|||
char *backup_dir; /* For RESTORE/BACKUP */
|
||||
char* to_log; /* For PURGE MASTER LOGS TO */
|
||||
char* x509_subject,*x509_issuer,*ssl_cipher;
|
||||
char* found_semicolon; /* For multi queries - next query */
|
||||
String *wild;
|
||||
sql_exchange *exchange;
|
||||
select_result *result;
|
||||
|
@ -1105,7 +1153,6 @@ typedef struct st_lex : public Query_tables_list
|
|||
|
||||
thr_lock_type lock_option;
|
||||
enum SSL_type ssl_type; /* defined in violite.h */
|
||||
enum my_lex_states next_state;
|
||||
enum enum_duplicates duplicates;
|
||||
enum enum_tx_isolation tx_isolation;
|
||||
enum enum_ha_read_modes ha_read_mode;
|
||||
|
@ -1137,7 +1184,7 @@ typedef struct st_lex : public Query_tables_list
|
|||
uint8 create_view_algorithm;
|
||||
uint8 create_view_check;
|
||||
bool drop_if_exists, drop_temporary, local_file, one_shot_set;
|
||||
bool in_comment, ignore_space, verbose, no_write_to_binlog;
|
||||
bool in_comment, verbose, no_write_to_binlog;
|
||||
bool tx_chain, tx_release;
|
||||
/*
|
||||
Special JOIN::prepare mode: changing of query is prohibited.
|
||||
|
@ -1147,11 +1194,6 @@ typedef struct st_lex : public Query_tables_list
|
|||
to an .frm file. We need this definition to stay untouched.
|
||||
*/
|
||||
bool view_prepare_mode;
|
||||
/*
|
||||
TRUE if we're parsing a prepared statement: in this mode
|
||||
we should allow placeholders and disallow multistatements.
|
||||
*/
|
||||
bool stmt_prepare_mode;
|
||||
bool safe_to_cache_query;
|
||||
bool subqueries, ignore;
|
||||
st_parsing_options parsing_options;
|
||||
|
@ -1214,7 +1256,8 @@ typedef struct st_lex : public Query_tables_list
|
|||
Pointers to part of LOAD DATA statement that should be rewritten
|
||||
during replication ("LOCAL 'filename' REPLACE INTO" part).
|
||||
*/
|
||||
const char *fname_start, *fname_end;
|
||||
const char *fname_start;
|
||||
const char *fname_end;
|
||||
|
||||
/*
|
||||
Reference to a struct that contains information in various commands
|
||||
|
@ -1333,7 +1376,7 @@ struct st_lex_local: public st_lex
|
|||
|
||||
extern void lex_init(void);
|
||||
extern void lex_free(void);
|
||||
extern void lex_start(THD *thd, const char *buf, uint length);
|
||||
extern void lex_start(THD *thd);
|
||||
extern void lex_end(LEX *lex);
|
||||
extern int MYSQLlex(void *arg, void *yythd);
|
||||
extern const char *skip_rear_comments(const char *ubegin, const char *uend);
|
||||
|
|
121
sql/sql_parse.cc
121
sql/sql_parse.cc
|
@ -300,6 +300,7 @@ pthread_handler_t handle_bootstrap(void *arg)
|
|||
THD *thd=(THD*) arg;
|
||||
FILE *file=bootstrap_file;
|
||||
char *buff;
|
||||
const char* found_semicolon= NULL;
|
||||
|
||||
/* The following must be called before DBUG_ENTER */
|
||||
thd->thread_stack= (char*) &thd;
|
||||
|
@ -380,7 +381,7 @@ pthread_handler_t handle_bootstrap(void *arg)
|
|||
*/
|
||||
thd->query_id=next_query_id();
|
||||
thd->set_time();
|
||||
mysql_parse(thd,thd->query,length);
|
||||
mysql_parse(thd, thd->query, length, & found_semicolon);
|
||||
close_thread_tables(thd); // Free tables
|
||||
|
||||
if (thd->is_fatal_error)
|
||||
|
@ -894,17 +895,19 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
|
|||
char *packet_end= thd->query + thd->query_length;
|
||||
/* 'b' stands for 'buffer' parameter', special for 'my_snprintf' */
|
||||
const char *format= "%.*b";
|
||||
const char* found_semicolon= NULL;
|
||||
|
||||
general_log_print(thd, command, format, thd->query_length, thd->query);
|
||||
DBUG_PRINT("query",("%-.4096s",thd->query));
|
||||
|
||||
if (!(specialflag & SPECIAL_NO_PRIOR))
|
||||
my_pthread_setprio(pthread_self(),QUERY_PRIOR);
|
||||
|
||||
mysql_parse(thd,thd->query, thd->query_length);
|
||||
mysql_parse(thd, thd->query, thd->query_length, & found_semicolon);
|
||||
|
||||
while (!thd->killed && thd->lex->found_semicolon && !thd->net.report_error)
|
||||
while (!thd->killed && found_semicolon && !thd->net.report_error)
|
||||
{
|
||||
char *next_packet= thd->lex->found_semicolon;
|
||||
char *next_packet= (char*) found_semicolon;
|
||||
net->no_send_error= 0;
|
||||
/*
|
||||
Multiple queries exits, execute them individually
|
||||
|
@ -929,7 +932,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
|
|||
thd->set_time(); /* Reset the query start time. */
|
||||
/* TODO: set thd->lex->sql_command to SQLCOM_END here */
|
||||
VOID(pthread_mutex_unlock(&LOCK_thread_count));
|
||||
mysql_parse(thd, next_packet, length);
|
||||
mysql_parse(thd, next_packet, length, & found_semicolon);
|
||||
}
|
||||
|
||||
if (!(specialflag & SPECIAL_NO_PRIOR))
|
||||
|
@ -951,7 +954,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
|
|||
uint dummy;
|
||||
|
||||
/* used as fields initializator */
|
||||
lex_start(thd, 0, 0);
|
||||
lex_start(thd);
|
||||
|
||||
statistic_increment(thd->status_var.com_stat[SQLCOM_SHOW_FIELDS],
|
||||
&LOCK_status);
|
||||
|
@ -990,7 +993,10 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
|
|||
break;
|
||||
/* init structures for VIEW processing */
|
||||
table_list.select_lex= &(thd->lex->select_lex);
|
||||
mysql_init_query(thd, "", 0);
|
||||
|
||||
lex_start(thd);
|
||||
mysql_reset_thd_for_next_command(thd);
|
||||
|
||||
thd->lex->
|
||||
select_lex.table_list.link_in_list((byte*) &table_list,
|
||||
(byte**) &table_list.next_local);
|
||||
|
@ -2158,7 +2164,13 @@ mysql_execute_command(THD *thd)
|
|||
select_lex->options|= SELECT_NO_UNLOCK;
|
||||
unit->set_limit(select_lex);
|
||||
|
||||
if (!(res= open_and_lock_tables(thd, select_tables)))
|
||||
if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE))
|
||||
{
|
||||
lex->link_first_table_back(create_table, link_to_local);
|
||||
create_table->create= TRUE;
|
||||
}
|
||||
|
||||
if (!(res= open_and_lock_tables(thd, lex->query_tables)))
|
||||
{
|
||||
/*
|
||||
Is table which we are changing used somewhere in other parts
|
||||
|
@ -2167,6 +2179,7 @@ mysql_execute_command(THD *thd)
|
|||
if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE))
|
||||
{
|
||||
TABLE_LIST *duplicate;
|
||||
create_table= lex->unlink_first_table(&link_to_local);
|
||||
if ((duplicate= unique_table(thd, create_table, select_tables, 0)))
|
||||
{
|
||||
update_non_unique_table_error(create_table, "CREATE", duplicate);
|
||||
|
@ -2192,6 +2205,12 @@ mysql_execute_command(THD *thd)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
FIXME Temporary hack which will go away once Kostja pushes
|
||||
his uber-fix for ALTER/CREATE TABLE.
|
||||
*/
|
||||
lex->create_info.table_existed= 0;
|
||||
|
||||
if ((result= new select_create(create_table,
|
||||
&lex->create_info,
|
||||
lex->create_list,
|
||||
|
@ -2211,6 +2230,9 @@ mysql_execute_command(THD *thd)
|
|||
lex->create_list.empty();
|
||||
lex->key_list.empty();
|
||||
}
|
||||
else if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE))
|
||||
create_table= lex->unlink_first_table(&link_to_local);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -5044,20 +5066,6 @@ bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, ulong *yystacksize)
|
|||
}
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
Initialize global thd variables needed for query
|
||||
****************************************************************************/
|
||||
|
||||
void
|
||||
mysql_init_query(THD *thd, const char *buf, uint length)
|
||||
{
|
||||
DBUG_ENTER("mysql_init_query");
|
||||
lex_start(thd, buf, length);
|
||||
mysql_reset_thd_for_next_command(thd);
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Reset THD part responsible for command processing state.
|
||||
|
||||
|
@ -5269,22 +5277,55 @@ void mysql_init_multi_delete(LEX *lex)
|
|||
mysql_test_parse_for_slave() in this same file.
|
||||
*/
|
||||
|
||||
void mysql_parse(THD *thd, char *inBuf, uint length)
|
||||
/**
|
||||
Parse a query.
|
||||
@param thd Current thread
|
||||
@param inBuf Begining of the query text
|
||||
@param length Length of the query text
|
||||
@param [out] semicolon For multi queries, position of the character of
|
||||
the next query in the query text.
|
||||
*/
|
||||
|
||||
void mysql_parse(THD *thd, const char *inBuf, uint length,
|
||||
const char ** found_semicolon)
|
||||
{
|
||||
DBUG_ENTER("mysql_parse");
|
||||
|
||||
DBUG_EXECUTE_IF("parser_debug", turn_parser_debug_on(););
|
||||
|
||||
mysql_init_query(thd, inBuf, length);
|
||||
/*
|
||||
Warning.
|
||||
The purpose of query_cache_send_result_to_client() is to lookup the
|
||||
query in the query cache first, to avoid parsing and executing it.
|
||||
So, the natural implementation would be to:
|
||||
- first, call query_cache_send_result_to_client,
|
||||
- second, if caching failed, initialise the lexical and syntactic parser.
|
||||
The problem is that the query cache depends on a clean initialization
|
||||
of (among others) lex->safe_to_cache_query and thd->server_status,
|
||||
which are reset respectively in
|
||||
- lex_start()
|
||||
- mysql_reset_thd_for_next_command()
|
||||
So, initializing the lexical analyser *before* using the query cache
|
||||
is required for the cache to work properly.
|
||||
FIXME: cleanup the dependencies in the code to simplify this.
|
||||
*/
|
||||
lex_start(thd);
|
||||
mysql_reset_thd_for_next_command(thd);
|
||||
|
||||
if (query_cache_send_result_to_client(thd, inBuf, length) <= 0)
|
||||
if (query_cache_send_result_to_client(thd, (char*) inBuf, length) <= 0)
|
||||
{
|
||||
LEX *lex= thd->lex;
|
||||
|
||||
|
||||
sp_cache_flush_obsolete(&thd->sp_proc_cache);
|
||||
sp_cache_flush_obsolete(&thd->sp_func_cache);
|
||||
|
||||
if (!MYSQLparse((void *)thd) && ! thd->is_fatal_error)
|
||||
|
||||
Lex_input_stream lip(thd, inBuf, length);
|
||||
thd->m_lip= &lip;
|
||||
|
||||
int err= MYSQLparse(thd);
|
||||
*found_semicolon= lip.found_semicolon;
|
||||
|
||||
if (!err && ! thd->is_fatal_error)
|
||||
{
|
||||
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
||||
if (mqh_used && thd->user_connect &&
|
||||
|
@ -5307,8 +5348,8 @@ void mysql_parse(THD *thd, char *inBuf, uint length)
|
|||
PROCESSLIST.
|
||||
Note that we don't need LOCK_thread_count to modify query_length.
|
||||
*/
|
||||
if (lex->found_semicolon &&
|
||||
(thd->query_length= (ulong)(lex->found_semicolon - thd->query)))
|
||||
if (lip.found_semicolon &&
|
||||
(thd->query_length= (ulong)(lip.found_semicolon - thd->query)))
|
||||
thd->query_length--;
|
||||
/* Actually execute the query */
|
||||
mysql_execute_command(thd);
|
||||
|
@ -5335,6 +5376,12 @@ void mysql_parse(THD *thd, char *inBuf, uint length)
|
|||
thd->cleanup_after_query();
|
||||
DBUG_ASSERT(thd->change_list.is_empty());
|
||||
}
|
||||
else
|
||||
{
|
||||
/* There are no multi queries in the cache. */
|
||||
*found_semicolon= NULL;
|
||||
}
|
||||
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
|
@ -5355,8 +5402,13 @@ bool mysql_test_parse_for_slave(THD *thd, char *inBuf, uint length)
|
|||
bool error= 0;
|
||||
DBUG_ENTER("mysql_test_parse_for_slave");
|
||||
|
||||
mysql_init_query(thd, inBuf, length);
|
||||
if (!MYSQLparse((void*) thd) && ! thd->is_fatal_error &&
|
||||
Lex_input_stream lip(thd, inBuf, length);
|
||||
thd->m_lip= &lip;
|
||||
lex_start(thd);
|
||||
mysql_reset_thd_for_next_command(thd);
|
||||
int err= MYSQLparse((void*) thd);
|
||||
|
||||
if (!err && ! thd->is_fatal_error &&
|
||||
all_tables_not_ok(thd,(TABLE_LIST*) lex->select_lex.table_list.first))
|
||||
error= 1; /* Ignore question */
|
||||
thd->end_statement();
|
||||
|
@ -6407,8 +6459,9 @@ bool check_simple_select()
|
|||
if (lex->current_select != &lex->select_lex)
|
||||
{
|
||||
char command[80];
|
||||
strmake(command, lex->yylval->symbol.str,
|
||||
min(lex->yylval->symbol.length, sizeof(command)-1));
|
||||
Lex_input_stream *lip= thd->m_lip;
|
||||
strmake(command, lip->yylval->symbol.str,
|
||||
min(lip->yylval->symbol.length, sizeof(command)-1));
|
||||
my_error(ER_CANT_USE_OPTION_HERE, MYF(0), command);
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -3718,7 +3718,11 @@ bool mysql_unpack_partition(THD *thd,
|
|||
|
||||
thd->lex= &lex;
|
||||
thd->variables.character_set_client= system_charset_info;
|
||||
lex_start(thd, part_buf, part_info_len);
|
||||
|
||||
Lex_input_stream lip(thd, part_buf, part_info_len);
|
||||
thd->m_lip= &lip;
|
||||
|
||||
lex_start(thd);
|
||||
/*
|
||||
We need to use the current SELECT_LEX since I need to keep the
|
||||
Name_resolution_context object which is referenced from the
|
||||
|
|
|
@ -1493,8 +1493,21 @@ static bool mysql_test_create_table(Prepared_statement *stmt)
|
|||
|
||||
if (select_lex->item_list.elements)
|
||||
{
|
||||
if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE))
|
||||
{
|
||||
lex->link_first_table_back(create_table, link_to_local);
|
||||
create_table->create= TRUE;
|
||||
}
|
||||
|
||||
if (open_normal_and_derived_tables(stmt->thd, lex->query_tables, 0))
|
||||
DBUG_RETURN(TRUE);
|
||||
|
||||
if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE))
|
||||
create_table= lex->unlink_first_table(&link_to_local);
|
||||
|
||||
select_lex->context.resolve_in_select_list= TRUE;
|
||||
res= select_like_stmt_test_with_open(stmt, tables, 0, 0);
|
||||
|
||||
res= select_like_stmt_test(stmt, 0, 0);
|
||||
}
|
||||
|
||||
/* put tables back for PS rexecuting */
|
||||
|
@ -1795,6 +1808,9 @@ static bool check_prepared_statement(Prepared_statement *stmt,
|
|||
case SQLCOM_KILL:
|
||||
break;
|
||||
|
||||
case SQLCOM_PREPARE:
|
||||
case SQLCOM_EXECUTE:
|
||||
case SQLCOM_DEALLOCATE_PREPARE:
|
||||
default:
|
||||
/*
|
||||
Trivial check of all status commands. This is easier than having
|
||||
|
@ -2851,10 +2867,14 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
|
|||
|
||||
old_stmt_arena= thd->stmt_arena;
|
||||
thd->stmt_arena= this;
|
||||
lex_start(thd, thd->query, thd->query_length);
|
||||
lex->stmt_prepare_mode= TRUE;
|
||||
|
||||
error= MYSQLparse((void *)thd) || thd->is_fatal_error ||
|
||||
Lex_input_stream lip(thd, thd->query, thd->query_length);
|
||||
lip.stmt_prepare_mode= TRUE;
|
||||
thd->m_lip= &lip;
|
||||
lex_start(thd);
|
||||
int err= MYSQLparse((void *)thd);
|
||||
|
||||
error= err || thd->is_fatal_error ||
|
||||
thd->net.report_error || init_param_array(this);
|
||||
|
||||
/*
|
||||
|
|
272
sql/sql_table.cc
272
sql/sql_table.cc
|
@ -3193,7 +3193,7 @@ static HA_CREATE_INFO *copy_create_info(HA_CREATE_INFO *lex_create_info)
|
|||
Create a table
|
||||
|
||||
SYNOPSIS
|
||||
mysql_create_table_internal()
|
||||
mysql_create_table_no_lock()
|
||||
thd Thread object
|
||||
db Database
|
||||
table_name Table name
|
||||
|
@ -3210,6 +3210,11 @@ static HA_CREATE_INFO *copy_create_info(HA_CREATE_INFO *lex_create_info)
|
|||
DESCRIPTION
|
||||
If one creates a temporary table, this is automatically opened
|
||||
|
||||
Note that this function assumes that caller already have taken
|
||||
name-lock on table being created or used some other way to ensure
|
||||
that concurrent operations won't intervene. mysql_create_table()
|
||||
is a wrapper that can be used for this.
|
||||
|
||||
no_log is needed for the case of CREATE ... SELECT,
|
||||
as the logging will be done later in sql_insert.cc
|
||||
select_field_count is also used for CREATE ... SELECT,
|
||||
|
@ -3220,7 +3225,7 @@ static HA_CREATE_INFO *copy_create_info(HA_CREATE_INFO *lex_create_info)
|
|||
TRUE error
|
||||
*/
|
||||
|
||||
bool mysql_create_table_internal(THD *thd,
|
||||
bool mysql_create_table_no_lock(THD *thd,
|
||||
const char *db, const char *table_name,
|
||||
HA_CREATE_INFO *lex_create_info,
|
||||
List<create_field> &fields,
|
||||
|
@ -3236,7 +3241,7 @@ bool mysql_create_table_internal(THD *thd,
|
|||
HA_CREATE_INFO *create_info;
|
||||
handler *file;
|
||||
bool error= TRUE;
|
||||
DBUG_ENTER("mysql_create_table_internal");
|
||||
DBUG_ENTER("mysql_create_table_no_lock");
|
||||
DBUG_PRINT("enter", ("db: '%s' table: '%s' tmp: %d",
|
||||
db, table_name, internal_tmp_table));
|
||||
|
||||
|
@ -3588,7 +3593,7 @@ warn:
|
|||
|
||||
|
||||
/*
|
||||
Database locking aware wrapper for mysql_create_table_internal(),
|
||||
Database and name-locking aware wrapper for mysql_create_table_no_lock(),
|
||||
*/
|
||||
|
||||
bool mysql_create_table(THD *thd, const char *db, const char *table_name,
|
||||
|
@ -3598,6 +3603,7 @@ bool mysql_create_table(THD *thd, const char *db, const char *table_name,
|
|||
uint select_field_count,
|
||||
bool use_copy_create_info)
|
||||
{
|
||||
TABLE *name_lock= 0;
|
||||
bool result;
|
||||
DBUG_ENTER("mysql_create_table");
|
||||
|
||||
|
@ -3618,11 +3624,44 @@ bool mysql_create_table(THD *thd, const char *db, const char *table_name,
|
|||
creating_table++;
|
||||
pthread_mutex_unlock(&LOCK_lock_db);
|
||||
|
||||
result= mysql_create_table_internal(thd, db, table_name, create_info,
|
||||
fields, keys, internal_tmp_table,
|
||||
select_field_count,
|
||||
use_copy_create_info);
|
||||
if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE))
|
||||
{
|
||||
if (lock_table_name_if_not_cached(thd, db, table_name, &name_lock))
|
||||
{
|
||||
result= TRUE;
|
||||
goto unlock;
|
||||
}
|
||||
if (!name_lock)
|
||||
{
|
||||
if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
|
||||
{
|
||||
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
|
||||
ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR),
|
||||
table_name);
|
||||
create_info->table_existed= 1;
|
||||
result= FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name);
|
||||
result= TRUE;
|
||||
}
|
||||
goto unlock;
|
||||
}
|
||||
}
|
||||
|
||||
result= mysql_create_table_no_lock(thd, db, table_name, create_info,
|
||||
fields, keys, internal_tmp_table,
|
||||
select_field_count,
|
||||
use_copy_create_info);
|
||||
|
||||
unlock:
|
||||
if (name_lock)
|
||||
{
|
||||
pthread_mutex_lock(&LOCK_open);
|
||||
unlink_open_table(thd, name_lock, FALSE);
|
||||
pthread_mutex_unlock(&LOCK_open);
|
||||
}
|
||||
pthread_mutex_lock(&LOCK_lock_db);
|
||||
if (!--creating_table && creating_database)
|
||||
pthread_cond_signal(&COND_refresh);
|
||||
|
@ -3825,7 +3864,7 @@ void close_cached_table(THD *thd, TABLE *table)
|
|||
thd->lock=0; // Start locked threads
|
||||
}
|
||||
/* Close all copies of 'table'. This also frees all LOCK TABLES lock */
|
||||
thd->open_tables=unlink_open_table(thd,thd->open_tables,table);
|
||||
unlink_open_table(thd, table, TRUE);
|
||||
|
||||
/* When lock on LOCK_open is freed other threads can continue */
|
||||
broadcast_refresh();
|
||||
|
@ -3901,7 +3940,7 @@ static int prepare_for_restore(THD* thd, TABLE_LIST* table,
|
|||
to finish the restore in the handler later on
|
||||
*/
|
||||
pthread_mutex_lock(&LOCK_open);
|
||||
if (reopen_name_locked_table(thd, table))
|
||||
if (reopen_name_locked_table(thd, table, TRUE))
|
||||
{
|
||||
unlock_table_name(thd, table);
|
||||
pthread_mutex_unlock(&LOCK_open);
|
||||
|
@ -4033,7 +4072,7 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
|
|||
to finish the repair in the handler later on.
|
||||
*/
|
||||
pthread_mutex_lock(&LOCK_open);
|
||||
if (reopen_name_locked_table(thd, table_list))
|
||||
if (reopen_name_locked_table(thd, table_list, TRUE))
|
||||
{
|
||||
unlock_table_name(thd, table_list);
|
||||
pthread_mutex_unlock(&LOCK_open);
|
||||
|
@ -4639,7 +4678,7 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table,
|
|||
HA_CREATE_INFO *lex_create_info,
|
||||
Table_ident *table_ident)
|
||||
{
|
||||
TABLE *tmp_table;
|
||||
TABLE *tmp_table, *name_lock= 0;
|
||||
char src_path[FN_REFLEN], dst_path[FN_REFLEN];
|
||||
char src_table_name_buff[FN_REFLEN], src_db_name_buff[FN_REFLEN];
|
||||
uint dst_path_length;
|
||||
|
@ -4648,14 +4687,14 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table,
|
|||
char *src_db;
|
||||
char *src_table= table_ident->table.str;
|
||||
int err;
|
||||
bool res= TRUE, unlock_dst_table= FALSE;
|
||||
bool res= TRUE;
|
||||
enum legacy_db_type not_used;
|
||||
HA_CREATE_INFO *create_info;
|
||||
#ifdef WITH_PARTITION_STORAGE_ENGINE
|
||||
char tmp_path[FN_REFLEN];
|
||||
#endif
|
||||
char ts_name[FN_LEN];
|
||||
TABLE_LIST src_tables_list, dst_tables_list;
|
||||
TABLE_LIST src_tables_list;
|
||||
DBUG_ENTER("mysql_create_like_table");
|
||||
|
||||
if (!(create_info= copy_create_info(lex_create_info)))
|
||||
|
@ -4763,6 +4802,10 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table,
|
|||
}
|
||||
else
|
||||
{
|
||||
if (lock_table_name_if_not_cached(thd, db, table_name, &name_lock))
|
||||
goto err;
|
||||
if (!name_lock)
|
||||
goto table_exists;
|
||||
dst_path_length= build_table_filename(dst_path, sizeof(dst_path),
|
||||
db, table_name, reg_ext, 0);
|
||||
if (!access(dst_path, F_OK))
|
||||
|
@ -4850,28 +4893,21 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table,
|
|||
char buf[2048];
|
||||
String query(buf, sizeof(buf), system_charset_info);
|
||||
query.length(0); // Have to zero it since constructor doesn't
|
||||
uint counter;
|
||||
|
||||
/*
|
||||
Here we open the destination table. This is needed for
|
||||
store_create_info() to work. The table will be closed
|
||||
by close_thread_tables() at the end of the statement.
|
||||
Here we open the destination table, on which we already have
|
||||
name-lock. This is needed for store_create_info() to work.
|
||||
The table will be closed by unlink_open_table() at the end
|
||||
of this function.
|
||||
*/
|
||||
if (open_tables(thd, &table, &counter, 0))
|
||||
table->table= name_lock;
|
||||
VOID(pthread_mutex_lock(&LOCK_open));
|
||||
if (reopen_name_locked_table(thd, table, FALSE))
|
||||
{
|
||||
VOID(pthread_mutex_unlock(&LOCK_open));
|
||||
goto err;
|
||||
|
||||
bzero((gptr)&dst_tables_list, sizeof(dst_tables_list));
|
||||
dst_tables_list.db= table->db;
|
||||
dst_tables_list.table_name= table->table_name;
|
||||
|
||||
/*
|
||||
lock destination table name, to make sure that nobody
|
||||
can drop/alter the table while we execute store_create_info()
|
||||
*/
|
||||
if (lock_and_wait_for_table_name(thd, &dst_tables_list))
|
||||
goto err;
|
||||
else
|
||||
unlock_dst_table= TRUE;
|
||||
}
|
||||
VOID(pthread_mutex_unlock(&LOCK_open));
|
||||
|
||||
IF_DBUG(int result=) store_create_info(thd, table, &query,
|
||||
create_info);
|
||||
|
@ -4906,10 +4942,10 @@ table_exists:
|
|||
my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name);
|
||||
|
||||
err:
|
||||
if (unlock_dst_table)
|
||||
if (name_lock)
|
||||
{
|
||||
pthread_mutex_lock(&LOCK_open);
|
||||
unlock_table_name(thd, &dst_tables_list);
|
||||
unlink_open_table(thd, name_lock, FALSE);
|
||||
pthread_mutex_unlock(&LOCK_open);
|
||||
}
|
||||
DBUG_RETURN(res);
|
||||
|
@ -5356,7 +5392,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
|
|||
uint order_num, ORDER *order, bool ignore,
|
||||
ALTER_INFO *alter_info, bool do_send_ok)
|
||||
{
|
||||
TABLE *table,*new_table=0;
|
||||
TABLE *table, *new_table= 0, *name_lock= 0;
|
||||
int error= 0;
|
||||
char tmp_name[80],old_name[32],new_name_buff[FN_REFLEN];
|
||||
char new_alias_buff[FN_REFLEN], *table_name, *db, *new_alias, *alias;
|
||||
|
@ -5493,6 +5529,18 @@ view_err:
|
|||
DBUG_RETURN(TRUE);
|
||||
table->use_all_columns();
|
||||
|
||||
List_iterator<Alter_drop> drop_it(alter_info->drop_list);
|
||||
List_iterator<create_field> def_it(fields);
|
||||
List_iterator<Alter_column> alter_it(alter_info->alter_list);
|
||||
List<create_field> create_list; // Add new fields here
|
||||
List<Key> key_list; // Add new keys here
|
||||
List_iterator<create_field> find_it(create_list);
|
||||
List_iterator<Key> key_it(keys);
|
||||
List_iterator<create_field> field_it(create_list);
|
||||
List<key_part_spec> key_parts;
|
||||
|
||||
KEY *key_info=table->key_info;
|
||||
|
||||
/* Check that we are not trying to rename to an existing table */
|
||||
if (new_name)
|
||||
{
|
||||
|
@ -5529,13 +5577,21 @@ view_err:
|
|||
}
|
||||
else
|
||||
{
|
||||
if (lock_table_name_if_not_cached(thd, new_db, new_name, &name_lock))
|
||||
DBUG_RETURN(TRUE);
|
||||
if (!name_lock)
|
||||
{
|
||||
my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias);
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
|
||||
build_table_filename(new_name_buff, sizeof(new_name_buff),
|
||||
new_db, new_name_buff, reg_ext, 0);
|
||||
if (!access(new_name_buff, F_OK))
|
||||
{
|
||||
/* Table will be closed in do_command() */
|
||||
my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias);
|
||||
DBUG_RETURN(TRUE);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5570,12 +5626,10 @@ view_err:
|
|||
#ifdef WITH_PARTITION_STORAGE_ENGINE
|
||||
if (prep_alter_part_table(thd, table, alter_info, create_info, old_db_type,
|
||||
&partition_changed, &fast_alter_partition))
|
||||
{
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
goto err;
|
||||
#endif
|
||||
if (check_engine(thd, new_name, create_info))
|
||||
DBUG_RETURN(TRUE);
|
||||
goto err;
|
||||
new_db_type= create_info->db_type;
|
||||
if (create_info->row_type == ROW_TYPE_NOT_USED)
|
||||
create_info->row_type= table->s->row_type;
|
||||
|
@ -5588,7 +5642,7 @@ view_err:
|
|||
{
|
||||
DBUG_PRINT("info", ("doesn't support alter"));
|
||||
my_error(ER_ILLEGAL_HA, MYF(0), table_name);
|
||||
DBUG_RETURN(TRUE);
|
||||
goto err;
|
||||
}
|
||||
|
||||
thd->proc_info="setup";
|
||||
|
@ -5647,7 +5701,19 @@ view_err:
|
|||
if (!error && (new_name != table_name || new_db != db))
|
||||
{
|
||||
thd->proc_info="rename";
|
||||
/* Then do a 'simple' rename of the table */
|
||||
/*
|
||||
Then do a 'simple' rename of the table. First we need to close all
|
||||
instances of 'source' table.
|
||||
*/
|
||||
close_cached_table(thd, table);
|
||||
/*
|
||||
Then, we want check once again that target table does not exist.
|
||||
Actually the order of these two steps does not matter since
|
||||
earlier we took name-lock on the target table, so we do them
|
||||
in this particular order only to be consistent with 5.0, in which
|
||||
we don't take this name-lock and where this order really matters.
|
||||
TODO: Investigate if we need this access() check at all.
|
||||
*/
|
||||
if (!access(new_name_buff,F_OK))
|
||||
{
|
||||
my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name);
|
||||
|
@ -5656,8 +5722,6 @@ view_err:
|
|||
else
|
||||
{
|
||||
*fn_ext(new_name)=0;
|
||||
table->s->version= 0; // Force removal of table def
|
||||
close_cached_table(thd, table);
|
||||
if (mysql_rename_table(old_db_type,db,table_name,new_db,new_alias, 0))
|
||||
error= -1;
|
||||
else if (Table_triggers_list::change_table_name(thd, db, table_name,
|
||||
|
@ -5689,6 +5753,8 @@ view_err:
|
|||
table->file->print_error(error, MYF(0));
|
||||
error= -1;
|
||||
}
|
||||
if (name_lock)
|
||||
unlink_open_table(thd, name_lock, FALSE);
|
||||
VOID(pthread_mutex_unlock(&LOCK_open));
|
||||
table_list->table= NULL; // For query cache
|
||||
query_cache_invalidate3(thd, table_list, 0);
|
||||
|
@ -5725,11 +5791,6 @@ view_err:
|
|||
create_info->tablespace= tablespace;
|
||||
}
|
||||
restore_record(table, s->default_values); // Empty record for DEFAULT
|
||||
List_iterator<Alter_drop> drop_it(alter_info->drop_list);
|
||||
List_iterator<create_field> def_it(fields);
|
||||
List_iterator<Alter_column> alter_it(alter_info->alter_list);
|
||||
List<create_field> create_list; // Add new fields here
|
||||
List<Key> key_list; // Add new keys here
|
||||
create_field *def;
|
||||
|
||||
/*
|
||||
|
@ -5800,7 +5861,7 @@ view_err:
|
|||
if (def->sql_type == MYSQL_TYPE_BLOB)
|
||||
{
|
||||
my_error(ER_BLOB_CANT_HAVE_DEFAULT, MYF(0), def->change);
|
||||
DBUG_RETURN(TRUE);
|
||||
goto err;
|
||||
}
|
||||
if ((def->def=alter->def)) // Use new default
|
||||
def->flags&= ~NO_DEFAULT_VALUE_FLAG;
|
||||
|
@ -5811,13 +5872,12 @@ view_err:
|
|||
}
|
||||
}
|
||||
def_it.rewind();
|
||||
List_iterator<create_field> find_it(create_list);
|
||||
while ((def=def_it++)) // Add new columns
|
||||
{
|
||||
if (def->change && ! def->field)
|
||||
{
|
||||
my_error(ER_BAD_FIELD_ERROR, MYF(0), def->change, table_name);
|
||||
DBUG_RETURN(TRUE);
|
||||
goto err;
|
||||
}
|
||||
if (!def->after)
|
||||
create_list.push_back(def);
|
||||
|
@ -5835,7 +5895,7 @@ view_err:
|
|||
if (!find)
|
||||
{
|
||||
my_error(ER_BAD_FIELD_ERROR, MYF(0), def->after, table_name);
|
||||
DBUG_RETURN(TRUE);
|
||||
goto err;
|
||||
}
|
||||
find_it.after(def); // Put element after this
|
||||
}
|
||||
|
@ -5844,13 +5904,13 @@ view_err:
|
|||
{
|
||||
my_error(ER_BAD_FIELD_ERROR, MYF(0),
|
||||
alter_info->alter_list.head()->name, table_name);
|
||||
DBUG_RETURN(TRUE);
|
||||
goto err;
|
||||
}
|
||||
if (!create_list.elements)
|
||||
{
|
||||
my_message(ER_CANT_REMOVE_ALL_FIELDS, ER(ER_CANT_REMOVE_ALL_FIELDS),
|
||||
MYF(0));
|
||||
DBUG_RETURN(TRUE);
|
||||
goto err;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -5858,11 +5918,6 @@ view_err:
|
|||
for which some fields exists.
|
||||
*/
|
||||
|
||||
List_iterator<Key> key_it(keys);
|
||||
List_iterator<create_field> field_it(create_list);
|
||||
List<key_part_spec> key_parts;
|
||||
|
||||
KEY *key_info=table->key_info;
|
||||
for (uint i=0 ; i < table->s->keys ; i++,key_info++)
|
||||
{
|
||||
char *key_name= key_info->name;
|
||||
|
@ -5966,7 +6021,7 @@ view_err:
|
|||
!my_strcasecmp(system_charset_info,key->name,primary_key_name))
|
||||
{
|
||||
my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key->name);
|
||||
DBUG_RETURN(TRUE);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6208,6 +6263,7 @@ view_err:
|
|||
#ifdef WITH_PARTITION_STORAGE_ENGINE
|
||||
if (fast_alter_partition)
|
||||
{
|
||||
DBUG_ASSERT(!name_lock);
|
||||
DBUG_RETURN(fast_alter_partition_table(thd, table, alter_info,
|
||||
create_info, table_list,
|
||||
&create_list, &key_list,
|
||||
|
@ -6268,11 +6324,12 @@ view_err:
|
|||
We don't log the statement, it will be logged later.
|
||||
*/
|
||||
tmp_disable_binlog(thd);
|
||||
error= mysql_create_table(thd, new_db, tmp_name,
|
||||
create_info,create_list,key_list,1,0,0);
|
||||
error= mysql_create_table_no_lock(thd, new_db, tmp_name,
|
||||
create_info, create_list,
|
||||
key_list, 1, 0, 0);
|
||||
reenable_binlog(thd);
|
||||
if (error)
|
||||
DBUG_RETURN(error);
|
||||
goto err;
|
||||
|
||||
/* Open the table if we need to copy the data. */
|
||||
if (need_copy_table)
|
||||
|
@ -6533,17 +6590,6 @@ view_err:
|
|||
current_pid, thd->thread_id);
|
||||
if (lower_case_table_names)
|
||||
my_casedn_str(files_charset_info, old_name);
|
||||
if (new_name != table_name || new_db != db)
|
||||
{
|
||||
if (!access(new_name_buff,F_OK))
|
||||
{
|
||||
error=1;
|
||||
my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name_buff);
|
||||
VOID(quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP));
|
||||
VOID(pthread_mutex_unlock(&LOCK_open));
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
#if !defined( __WIN__)
|
||||
if (table->file->has_transactions())
|
||||
|
@ -6553,7 +6599,6 @@ view_err:
|
|||
Win32 and InnoDB can't drop a table that is in use, so we must
|
||||
close the original table before doing the rename
|
||||
*/
|
||||
table->s->version= 0; // Force removal of table def
|
||||
close_cached_table(thd, table);
|
||||
table=0; // Marker that table is closed
|
||||
no_table_reopen= TRUE;
|
||||
|
@ -6563,6 +6608,21 @@ view_err:
|
|||
table->file->extra(HA_EXTRA_FORCE_REOPEN); // Don't use this file anymore
|
||||
#endif
|
||||
|
||||
if (new_name != table_name || new_db != db)
|
||||
{
|
||||
/*
|
||||
Check that there is no table with target name. See the
|
||||
comment describing code for 'simple' ALTER TABLE ... RENAME.
|
||||
*/
|
||||
if (!access(new_name_buff,F_OK))
|
||||
{
|
||||
error=1;
|
||||
my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name_buff);
|
||||
VOID(quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP));
|
||||
VOID(pthread_mutex_unlock(&LOCK_open));
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
error=0;
|
||||
save_old_db_type= old_db_type;
|
||||
|
@ -6613,7 +6673,6 @@ view_err:
|
|||
*/
|
||||
if (table)
|
||||
{
|
||||
table->s->version= 0; // Force removal of table def
|
||||
close_cached_table(thd,table);
|
||||
}
|
||||
VOID(pthread_mutex_unlock(&LOCK_open));
|
||||
|
@ -6621,6 +6680,7 @@ view_err:
|
|||
}
|
||||
if (! need_copy_table)
|
||||
{
|
||||
bool needs_unlink= FALSE;
|
||||
if (! table)
|
||||
{
|
||||
if (new_name != table_name || new_db != db)
|
||||
|
@ -6631,11 +6691,41 @@ view_err:
|
|||
table_list->db= new_db;
|
||||
table_list->db_length= strlen(new_db);
|
||||
}
|
||||
|
||||
VOID(pthread_mutex_unlock(&LOCK_open));
|
||||
if (! (table= open_ltable(thd, table_list, TL_WRITE_ALLOW_READ)))
|
||||
else
|
||||
{
|
||||
/*
|
||||
TODO: Creation of name-lock placeholder here is a temporary
|
||||
work-around. Long term we should change close_cached_table() call
|
||||
which we invoke before table renaming operation in such way that
|
||||
it will leave placeholders for table in table cache/THD::open_tables
|
||||
list. By doing this we will be able easily reopen and relock these
|
||||
tables later and therefore behave under LOCK TABLES in the same way
|
||||
on all platforms.
|
||||
*/
|
||||
char key[MAX_DBKEY_LENGTH];
|
||||
uint key_length;
|
||||
key_length= create_table_def_key(thd, key, table_list, 0);
|
||||
if (!(name_lock= table_cache_insert_placeholder(thd, key,
|
||||
key_length)))
|
||||
{
|
||||
VOID(pthread_mutex_unlock(&LOCK_open));
|
||||
goto err;
|
||||
}
|
||||
name_lock->next= thd->open_tables;
|
||||
thd->open_tables= name_lock;
|
||||
}
|
||||
table_list->table= name_lock;
|
||||
if (reopen_name_locked_table(thd, table_list, FALSE))
|
||||
{
|
||||
VOID(pthread_mutex_unlock(&LOCK_open));
|
||||
goto err;
|
||||
VOID(pthread_mutex_lock(&LOCK_open));
|
||||
}
|
||||
table= table_list->table;
|
||||
/*
|
||||
We can't rely on later close_cached_table() calls to close
|
||||
this instance of the table since it was not properly locked.
|
||||
*/
|
||||
needs_unlink= TRUE;
|
||||
}
|
||||
/* Tell the handler that a new frm file is in place. */
|
||||
if (table->file->create_handler_files(path, NULL, CHF_INDEX_FLAG,
|
||||
|
@ -6644,6 +6734,11 @@ view_err:
|
|||
VOID(pthread_mutex_unlock(&LOCK_open));
|
||||
goto err;
|
||||
}
|
||||
if (needs_unlink)
|
||||
{
|
||||
unlink_open_table(thd, table, FALSE);
|
||||
table= name_lock= 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (thd->lock || new_name != table_name || no_table_reopen) // True if WIN32
|
||||
|
@ -6654,7 +6749,6 @@ view_err:
|
|||
*/
|
||||
if (table)
|
||||
{
|
||||
table->s->version= 0; // Force removal of table def
|
||||
close_cached_table(thd,table);
|
||||
}
|
||||
VOID(quick_rm_table(old_db_type, db, old_name, FN_IS_TMP));
|
||||
|
@ -6680,7 +6774,6 @@ view_err:
|
|||
{ // This shouldn't happen
|
||||
if (table)
|
||||
{
|
||||
table->s->version= 0; // Force removal of table def
|
||||
close_cached_table(thd,table); // Remove lock for table
|
||||
}
|
||||
VOID(pthread_mutex_unlock(&LOCK_open));
|
||||
|
@ -6737,6 +6830,13 @@ view_err:
|
|||
table_list->table=0; // For query cache
|
||||
query_cache_invalidate3(thd, table_list, 0);
|
||||
|
||||
if (name_lock)
|
||||
{
|
||||
pthread_mutex_lock(&LOCK_open);
|
||||
unlink_open_table(thd, name_lock, FALSE);
|
||||
pthread_mutex_unlock(&LOCK_open);
|
||||
}
|
||||
|
||||
end_temporary:
|
||||
my_snprintf(tmp_name, sizeof(tmp_name), ER(ER_INSERT_INFO),
|
||||
(ulong) (copied + deleted), (ulong) deleted,
|
||||
|
@ -6756,6 +6856,12 @@ err1:
|
|||
VOID(quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP));
|
||||
|
||||
err:
|
||||
if (name_lock)
|
||||
{
|
||||
pthread_mutex_lock(&LOCK_open);
|
||||
unlink_open_table(thd, name_lock, FALSE);
|
||||
pthread_mutex_unlock(&LOCK_open);
|
||||
}
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
/* mysql_alter_table */
|
||||
|
|
|
@ -279,7 +279,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
|
|||
/* We also don't allow creation of triggers on views. */
|
||||
tables->required_type= FRMTYPE_TABLE;
|
||||
|
||||
if (reopen_name_locked_table(thd, tables))
|
||||
if (reopen_name_locked_table(thd, tables, TRUE))
|
||||
{
|
||||
unlock_table_name(thd, tables);
|
||||
goto end;
|
||||
|
@ -976,10 +976,14 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
|
|||
LEX_STRING *trg_definer= it_definer++;
|
||||
|
||||
thd->variables.sql_mode= (ulong)*trg_sql_mode;
|
||||
lex_start(thd, trg_create_str->str, trg_create_str->length);
|
||||
|
||||
thd->spcont= 0;
|
||||
if (MYSQLparse((void *)thd) || thd->is_fatal_error)
|
||||
Lex_input_stream lip(thd, trg_create_str->str, trg_create_str->length);
|
||||
thd->m_lip= &lip;
|
||||
lex_start(thd);
|
||||
thd->spcont= 0;
|
||||
int err= MYSQLparse((void *)thd);
|
||||
|
||||
if (err || thd->is_fatal_error)
|
||||
{
|
||||
/* Currently sphead is always deleted in case of a parse error */
|
||||
DBUG_ASSERT(lex.sphead == 0);
|
||||
|
|
|
@ -985,10 +985,14 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
|
|||
now Lex placed in statement memory
|
||||
*/
|
||||
table->view= lex= thd->lex= (LEX*) new(thd->mem_root) st_lex_local;
|
||||
lex_start(thd, table->query.str, table->query.length);
|
||||
view_select= &lex->select_lex;
|
||||
view_select->select_number= ++thd->select_number;
|
||||
|
||||
{
|
||||
Lex_input_stream lip(thd, table->query.str, table->query.length);
|
||||
thd->m_lip= &lip;
|
||||
lex_start(thd);
|
||||
view_select= &lex->select_lex;
|
||||
view_select->select_number= ++thd->select_number;
|
||||
|
||||
ulong save_mode= thd->variables.sql_mode;
|
||||
/* switch off modes which can prevent normal parsing of VIEW
|
||||
- MODE_REAL_AS_FLOAT affect only CREATE TABLE parsing
|
||||
|
|
217
sql/sql_yacc.yy
217
sql/sql_yacc.yy
|
@ -104,12 +104,13 @@ const LEX_STRING null_lex_str={0,0};
|
|||
void my_parse_error(const char *s)
|
||||
{
|
||||
THD *thd= current_thd;
|
||||
Lex_input_stream *lip= thd->m_lip;
|
||||
|
||||
char *yytext= (char*) thd->lex->tok_start;
|
||||
const char *yytext= lip->tok_start;
|
||||
/* Push an error into the error stack */
|
||||
my_printf_error(ER_PARSE_ERROR, ER(ER_PARSE_ERROR), MYF(0), s,
|
||||
(yytext ? (char*) yytext : ""),
|
||||
thd->lex->yylineno);
|
||||
(yytext ? yytext : ""),
|
||||
lip->yylineno);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1363,11 +1364,6 @@ deallocate:
|
|||
{
|
||||
THD *thd= YYTHD;
|
||||
LEX *lex= thd->lex;
|
||||
if (lex->stmt_prepare_mode)
|
||||
{
|
||||
my_parse_error(ER(ER_SYNTAX_ERROR));
|
||||
MYSQL_YYABORT;
|
||||
}
|
||||
lex->sql_command= SQLCOM_DEALLOCATE_PREPARE;
|
||||
lex->prepared_stmt_name= $3;
|
||||
};
|
||||
|
@ -1383,11 +1379,6 @@ prepare:
|
|||
{
|
||||
THD *thd= YYTHD;
|
||||
LEX *lex= thd->lex;
|
||||
if (lex->stmt_prepare_mode)
|
||||
{
|
||||
my_parse_error(ER(ER_SYNTAX_ERROR));
|
||||
MYSQL_YYABORT;
|
||||
}
|
||||
lex->sql_command= SQLCOM_PREPARE;
|
||||
lex->prepared_stmt_name= $2;
|
||||
};
|
||||
|
@ -1413,11 +1404,6 @@ execute:
|
|||
{
|
||||
THD *thd= YYTHD;
|
||||
LEX *lex= thd->lex;
|
||||
if (lex->stmt_prepare_mode)
|
||||
{
|
||||
my_parse_error(ER(ER_SYNTAX_ERROR));
|
||||
MYSQL_YYABORT;
|
||||
}
|
||||
lex->sql_command= SQLCOM_EXECUTE;
|
||||
lex->prepared_stmt_name= $2;
|
||||
}
|
||||
|
@ -1581,9 +1567,7 @@ create:
|
|||
lex->sql_command= SQLCOM_CREATE_TABLE;
|
||||
if (!lex->select_lex.add_table_to_list(thd, $5, NULL,
|
||||
TL_OPTION_UPDATING,
|
||||
(using_update_log ?
|
||||
TL_READ_NO_INSERT:
|
||||
TL_READ)))
|
||||
TL_WRITE))
|
||||
MYSQL_YYABORT;
|
||||
lex->create_list.empty();
|
||||
lex->key_list.empty();
|
||||
|
@ -1849,7 +1833,9 @@ opt_ev_comment: /* empty */ { $$= 0; }
|
|||
|
||||
ev_sql_stmt:
|
||||
{
|
||||
LEX *lex= Lex;
|
||||
THD *thd= YYTHD;
|
||||
LEX *lex= thd->lex;
|
||||
Lex_input_stream *lip= thd->m_lip;
|
||||
|
||||
/*
|
||||
This stops the following :
|
||||
|
@ -1886,22 +1872,23 @@ ev_sql_stmt:
|
|||
bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics));
|
||||
lex->sphead->m_chistics= &lex->sp_chistics;
|
||||
|
||||
lex->sphead->m_body_begin= lex->ptr;
|
||||
lex->sphead->m_body_begin= lip->ptr;
|
||||
|
||||
Lex->event_parse_data->body_begin= lex->ptr;
|
||||
lex->event_parse_data->body_begin= lip->ptr;
|
||||
|
||||
}
|
||||
ev_sql_stmt_inner
|
||||
{
|
||||
LEX *lex=Lex;
|
||||
THD *thd= YYTHD;
|
||||
LEX *lex= thd->lex;
|
||||
|
||||
/* return back to the original memory root ASAP */
|
||||
lex->sphead->init_strings(YYTHD, lex);
|
||||
lex->sphead->restore_thd_mem_root(YYTHD);
|
||||
lex->sphead->init_strings(thd, lex);
|
||||
lex->sphead->restore_thd_mem_root(thd);
|
||||
|
||||
lex->sp_chistics.suid= SP_IS_SUID; //always the definer!
|
||||
|
||||
Lex->event_parse_data->init_body(YYTHD);
|
||||
lex->event_parse_data->init_body(thd);
|
||||
}
|
||||
;
|
||||
|
||||
|
@ -1995,7 +1982,9 @@ create_function_tail:
|
|||
}
|
||||
| '('
|
||||
{
|
||||
LEX *lex= Lex;
|
||||
THD *thd= YYTHD;
|
||||
LEX *lex= thd->lex;
|
||||
Lex_input_stream *lip= thd->m_lip;
|
||||
sp_head *sp;
|
||||
|
||||
/*
|
||||
|
@ -2015,9 +2004,9 @@ create_function_tail:
|
|||
}
|
||||
/* Order is important here: new - reset - init */
|
||||
sp= new sp_head();
|
||||
sp->reset_thd_mem_root(YYTHD);
|
||||
sp->reset_thd_mem_root(thd);
|
||||
sp->init(lex);
|
||||
sp->init_sp_name(YYTHD, lex->spname);
|
||||
sp->init_sp_name(thd, lex->spname);
|
||||
|
||||
sp->m_type= TYPE_ENUM_FUNCTION;
|
||||
lex->sphead= sp;
|
||||
|
@ -2026,15 +2015,17 @@ create_function_tail:
|
|||
stored procedure, otherwise yylex will chop it into pieces
|
||||
at each ';'.
|
||||
*/
|
||||
$<ulong_num>$= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES;
|
||||
YYTHD->client_capabilities &= ~CLIENT_MULTI_QUERIES;
|
||||
lex->sphead->m_param_begin= lex->tok_start+1;
|
||||
$<ulong_num>$= thd->client_capabilities & CLIENT_MULTI_QUERIES;
|
||||
thd->client_capabilities &= ~CLIENT_MULTI_QUERIES;
|
||||
lex->sphead->m_param_begin= lip->tok_start+1;
|
||||
}
|
||||
sp_fdparam_list ')'
|
||||
{
|
||||
LEX *lex= Lex;
|
||||
THD *thd= YYTHD;
|
||||
LEX *lex= thd->lex;
|
||||
Lex_input_stream *lip= thd->m_lip;
|
||||
|
||||
lex->sphead->m_param_end= lex->tok_start;
|
||||
lex->sphead->m_param_end= lip->tok_start;
|
||||
}
|
||||
RETURNS_SYM
|
||||
{
|
||||
|
@ -2069,10 +2060,12 @@ create_function_tail:
|
|||
}
|
||||
sp_c_chistics
|
||||
{
|
||||
LEX *lex= Lex;
|
||||
THD *thd= YYTHD;
|
||||
LEX *lex= thd->lex;
|
||||
Lex_input_stream *lip= thd->m_lip;
|
||||
|
||||
lex->sphead->m_chistics= &lex->sp_chistics;
|
||||
lex->sphead->m_body_begin= lex->tok_start;
|
||||
lex->sphead->m_body_begin= lip->tok_start;
|
||||
}
|
||||
sp_proc_stmt
|
||||
{
|
||||
|
@ -2678,14 +2671,18 @@ sp_proc_stmt_if:
|
|||
|
||||
sp_proc_stmt_statement:
|
||||
{
|
||||
LEX *lex= Lex;
|
||||
THD *thd= YYTHD;
|
||||
LEX *lex= thd->lex;
|
||||
Lex_input_stream *lip= thd->m_lip;
|
||||
|
||||
lex->sphead->reset_lex(YYTHD);
|
||||
lex->sphead->m_tmp_query= lex->tok_start;
|
||||
lex->sphead->reset_lex(thd);
|
||||
lex->sphead->m_tmp_query= lip->tok_start;
|
||||
}
|
||||
statement
|
||||
{
|
||||
LEX *lex= Lex;
|
||||
THD *thd= YYTHD;
|
||||
LEX *lex= thd->lex;
|
||||
Lex_input_stream *lip= thd->m_lip;
|
||||
sp_head *sp= lex->sphead;
|
||||
|
||||
sp->m_flags|= sp_get_flags_for_command(lex);
|
||||
|
@ -2712,15 +2709,15 @@ sp_proc_stmt_statement:
|
|||
lex->tok_end otherwise.
|
||||
*/
|
||||
if (yychar == YYEMPTY)
|
||||
i->m_query.length= lex->ptr - sp->m_tmp_query;
|
||||
i->m_query.length= lip->ptr - sp->m_tmp_query;
|
||||
else
|
||||
i->m_query.length= lex->tok_end - sp->m_tmp_query;
|
||||
i->m_query.str= strmake_root(YYTHD->mem_root,
|
||||
i->m_query.length= lip->tok_end - sp->m_tmp_query;
|
||||
i->m_query.str= strmake_root(thd->mem_root,
|
||||
sp->m_tmp_query,
|
||||
i->m_query.length);
|
||||
sp->add_instr(i);
|
||||
}
|
||||
sp->restore_lex(YYTHD);
|
||||
sp->restore_lex(thd);
|
||||
}
|
||||
;
|
||||
|
||||
|
@ -6222,26 +6219,36 @@ select_item_list:
|
|||
select_item:
|
||||
remember_name select_item2 remember_end select_alias
|
||||
{
|
||||
if (add_item_to_list(YYTHD, $2))
|
||||
THD *thd= YYTHD;
|
||||
DBUG_ASSERT($1 < $3);
|
||||
|
||||
if (add_item_to_list(thd, $2))
|
||||
MYSQL_YYABORT;
|
||||
if ($4.str)
|
||||
{
|
||||
$2->is_autogenerated_name= FALSE;
|
||||
$2->set_name($4.str, $4.length, system_charset_info);
|
||||
}
|
||||
else if (!$2->name) {
|
||||
char *str = $1;
|
||||
if (str[-1] == '`')
|
||||
str--;
|
||||
$2->set_name(str,(uint) ($3 - str), YYTHD->charset());
|
||||
else if (!$2->name)
|
||||
{
|
||||
$2->set_name($1, (uint) ($3 - $1), thd->charset());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
remember_name:
|
||||
{ $$=(char*) Lex->tok_start; };
|
||||
{
|
||||
THD *thd= YYTHD;
|
||||
Lex_input_stream *lip= thd->m_lip;
|
||||
$$= (char*) lip->tok_start;
|
||||
};
|
||||
|
||||
remember_end:
|
||||
{ $$=(char*) Lex->tok_end; };
|
||||
{
|
||||
THD *thd= YYTHD;
|
||||
Lex_input_stream *lip= thd->m_lip;
|
||||
$$=(char*) lip->tok_end;
|
||||
};
|
||||
|
||||
select_item2:
|
||||
table_wild { $$=$1; } /* table.* */
|
||||
|
@ -8005,12 +8012,14 @@ procedure_list2:
|
|||
procedure_item:
|
||||
remember_name expr
|
||||
{
|
||||
LEX *lex= Lex;
|
||||
if (add_proc_to_list(lex->thd, $2))
|
||||
THD *thd= YYTHD;
|
||||
Lex_input_stream *lip= thd->m_lip;
|
||||
|
||||
if (add_proc_to_list(thd, $2))
|
||||
MYSQL_YYABORT;
|
||||
if (!$2->name)
|
||||
$2->set_name($1,(uint) ((char*) lex->tok_end - $1),
|
||||
YYTHD->charset());
|
||||
$2->set_name($1,(uint) ((char*) lip->tok_end - $1),
|
||||
thd->charset());
|
||||
}
|
||||
;
|
||||
|
||||
|
@ -9100,13 +9109,16 @@ use: USE_SYM ident
|
|||
|
||||
load: LOAD DATA_SYM
|
||||
{
|
||||
LEX *lex=Lex;
|
||||
THD *thd= YYTHD;
|
||||
LEX *lex= thd->lex;
|
||||
Lex_input_stream *lip= thd->m_lip;
|
||||
|
||||
if (lex->sphead)
|
||||
{
|
||||
my_error(ER_SP_BADSTATEMENT, MYF(0), "LOAD DATA");
|
||||
MYSQL_YYABORT;
|
||||
}
|
||||
lex->fname_start= lex->ptr;
|
||||
lex->fname_start= lip->ptr;
|
||||
}
|
||||
load_data
|
||||
{}
|
||||
|
@ -9140,8 +9152,10 @@ load_data:
|
|||
}
|
||||
opt_duplicate INTO
|
||||
{
|
||||
LEX *lex=Lex;
|
||||
lex->fname_end= lex->ptr;
|
||||
THD *thd= YYTHD;
|
||||
LEX *lex= thd->lex;
|
||||
Lex_input_stream *lip= thd->m_lip;
|
||||
lex->fname_end= lip->ptr;
|
||||
}
|
||||
TABLE_SYM table_ident
|
||||
{
|
||||
|
@ -9322,14 +9336,15 @@ param_marker:
|
|||
PARAM_MARKER
|
||||
{
|
||||
THD *thd= YYTHD;
|
||||
LEX *lex= thd->lex;
|
||||
LEX *lex= thd->lex;
|
||||
Lex_input_stream *lip= thd->m_lip;
|
||||
Item_param *item;
|
||||
if (! lex->parsing_options.allows_variable)
|
||||
{
|
||||
my_error(ER_VIEW_SELECT_VARIABLE, MYF(0));
|
||||
MYSQL_YYABORT;
|
||||
}
|
||||
item= new Item_param((uint) (lex->tok_start - thd->query));
|
||||
item= new Item_param((uint) (lip->tok_start - thd->query));
|
||||
if (!($$= item) || lex->param_list.push_back(item))
|
||||
{
|
||||
my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
|
||||
|
@ -9352,8 +9367,11 @@ signed_literal:
|
|||
literal:
|
||||
text_literal { $$ = $1; }
|
||||
| NUM_literal { $$ = $1; }
|
||||
| NULL_SYM { $$ = new Item_null();
|
||||
Lex->next_state=MY_LEX_OPERATOR_OR_IDENT;}
|
||||
| NULL_SYM
|
||||
{
|
||||
$$ = new Item_null();
|
||||
YYTHD->m_lip->next_state=MY_LEX_OPERATOR_OR_IDENT;
|
||||
}
|
||||
| FALSE_SYM { $$= new Item_int((char*) "FALSE",0,1); }
|
||||
| TRUE_SYM { $$= new Item_int((char*) "TRUE",1,1); }
|
||||
| HEX_NUM { $$ = new Item_hex_string($1.str, $1.length);}
|
||||
|
@ -9443,8 +9461,10 @@ order_ident:
|
|||
simple_ident:
|
||||
ident
|
||||
{
|
||||
THD *thd= YYTHD;
|
||||
LEX *lex= thd->lex;
|
||||
Lex_input_stream *lip= thd->m_lip;
|
||||
sp_variable_t *spv;
|
||||
LEX *lex = Lex;
|
||||
sp_pcontext *spc = lex->spcont;
|
||||
if (spc && (spv = spc->find_variable(&$1)))
|
||||
{
|
||||
|
@ -9457,7 +9477,7 @@ simple_ident:
|
|||
|
||||
Item_splocal *splocal;
|
||||
splocal= new Item_splocal($1, spv->offset, spv->type,
|
||||
lex->tok_start_prev -
|
||||
lip->tok_start_prev -
|
||||
lex->sphead->m_tmp_query);
|
||||
#ifndef DBUG_OFF
|
||||
if (splocal)
|
||||
|
@ -10107,7 +10127,11 @@ option_value_list:
|
|||
|
||||
option_type_value:
|
||||
{
|
||||
if (Lex->sphead)
|
||||
THD *thd= YYTHD;
|
||||
LEX *lex= thd->lex;
|
||||
Lex_input_stream *lip= thd->m_lip;
|
||||
|
||||
if (lex->sphead)
|
||||
{
|
||||
/*
|
||||
If we are in SP we want have own LEX for each assignment.
|
||||
|
@ -10119,9 +10143,8 @@ option_type_value:
|
|||
|
||||
QQ: May be we should simply prohibit group assignments in SP?
|
||||
*/
|
||||
LEX *lex;
|
||||
Lex->sphead->reset_lex(YYTHD);
|
||||
lex= Lex;
|
||||
lex->sphead->reset_lex(thd);
|
||||
lex= thd->lex;
|
||||
|
||||
/* Set new LEX as if we at start of set rule. */
|
||||
lex->sql_command= SQLCOM_SET_OPTION;
|
||||
|
@ -10129,12 +10152,14 @@ option_type_value:
|
|||
lex->option_type=OPT_SESSION;
|
||||
lex->var_list.empty();
|
||||
lex->one_shot_set= 0;
|
||||
lex->sphead->m_tmp_query= lex->tok_start;
|
||||
lex->sphead->m_tmp_query= lip->tok_start;
|
||||
}
|
||||
}
|
||||
ext_option_value
|
||||
{
|
||||
LEX *lex= Lex;
|
||||
THD *thd= YYTHD;
|
||||
LEX *lex= thd->lex;
|
||||
Lex_input_stream *lip= thd->m_lip;
|
||||
|
||||
if (lex->sphead)
|
||||
{
|
||||
|
@ -10156,15 +10181,15 @@ option_type_value:
|
|||
|
||||
/*
|
||||
Extract the query statement from the tokenizer. The
|
||||
end is either lex->ptr, if there was no lookahead,
|
||||
lex->tok_end otherwise.
|
||||
end is either lip->ptr, if there was no lookahead,
|
||||
lip->tok_end otherwise.
|
||||
*/
|
||||
if (yychar == YYEMPTY)
|
||||
qbuff.length= lex->ptr - sp->m_tmp_query;
|
||||
qbuff.length= lip->ptr - sp->m_tmp_query;
|
||||
else
|
||||
qbuff.length= lex->tok_end - sp->m_tmp_query;
|
||||
qbuff.length= lip->tok_end - sp->m_tmp_query;
|
||||
|
||||
if (!(qbuff.str= alloc_root(YYTHD->mem_root, qbuff.length + 5)))
|
||||
if (!(qbuff.str= alloc_root(thd->mem_root, qbuff.length + 5)))
|
||||
MYSQL_YYABORT;
|
||||
|
||||
strmake(strmake(qbuff.str, "SET ", 4), sp->m_tmp_query,
|
||||
|
@ -10173,7 +10198,7 @@ option_type_value:
|
|||
i->m_query= qbuff;
|
||||
sp->add_instr(i);
|
||||
}
|
||||
lex->sphead->restore_lex(YYTHD);
|
||||
lex->sphead->restore_lex(thd);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -11422,7 +11447,9 @@ trigger_tail:
|
|||
TRIGGER_SYM remember_name sp_name trg_action_time trg_event
|
||||
ON remember_name table_ident FOR_SYM remember_name EACH_SYM ROW_SYM
|
||||
{
|
||||
LEX *lex= Lex;
|
||||
THD *thd= YYTHD;
|
||||
LEX *lex= thd->lex;
|
||||
Lex_input_stream *lip= thd->m_lip;
|
||||
sp_head *sp;
|
||||
|
||||
if (lex->sphead)
|
||||
|
@ -11433,9 +11460,9 @@ trigger_tail:
|
|||
|
||||
if (!(sp= new sp_head()))
|
||||
MYSQL_YYABORT;
|
||||
sp->reset_thd_mem_root(YYTHD);
|
||||
sp->reset_thd_mem_root(thd);
|
||||
sp->init(lex);
|
||||
sp->init_sp_name(YYTHD, $3);
|
||||
sp->init_sp_name(thd, $3);
|
||||
lex->stmt_definition_begin= $2;
|
||||
lex->ident.str= $7;
|
||||
lex->ident.length= $10 - $7;
|
||||
|
@ -11448,12 +11475,12 @@ trigger_tail:
|
|||
stored procedure, otherwise yylex will chop it into pieces
|
||||
at each ';'.
|
||||
*/
|
||||
$<ulong_num>$= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES;
|
||||
YYTHD->client_capabilities &= ~CLIENT_MULTI_QUERIES;
|
||||
$<ulong_num>$= thd->client_capabilities & CLIENT_MULTI_QUERIES;
|
||||
thd->client_capabilities &= ~CLIENT_MULTI_QUERIES;
|
||||
|
||||
bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics));
|
||||
lex->sphead->m_chistics= &lex->sp_chistics;
|
||||
lex->sphead->m_body_begin= lex->ptr;
|
||||
lex->sphead->m_body_begin= lip->ptr;
|
||||
while (my_isspace(system_charset_info, lex->sphead->m_body_begin[0]))
|
||||
++lex->sphead->m_body_begin;
|
||||
}
|
||||
|
@ -11532,24 +11559,30 @@ sp_tail:
|
|||
}
|
||||
'('
|
||||
{
|
||||
LEX *lex= Lex;
|
||||
THD *thd= YYTHD;
|
||||
LEX *lex= thd->lex;
|
||||
Lex_input_stream *lip= thd->m_lip;
|
||||
|
||||
lex->sphead->m_param_begin= lex->tok_start+1;
|
||||
lex->sphead->m_param_begin= lip->tok_start+1;
|
||||
}
|
||||
sp_pdparam_list
|
||||
')'
|
||||
{
|
||||
LEX *lex= Lex;
|
||||
THD *thd= YYTHD;
|
||||
LEX *lex= thd->lex;
|
||||
Lex_input_stream *lip= thd->m_lip;
|
||||
|
||||
lex->sphead->m_param_end= lex->tok_start;
|
||||
lex->sphead->m_param_end= lip->tok_start;
|
||||
bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics));
|
||||
}
|
||||
sp_c_chistics
|
||||
{
|
||||
LEX *lex= Lex;
|
||||
THD *thd= YYTHD;
|
||||
LEX *lex= thd->lex;
|
||||
Lex_input_stream *lip= thd->m_lip;
|
||||
|
||||
lex->sphead->m_chistics= &lex->sp_chistics;
|
||||
lex->sphead->m_body_begin= lex->tok_start;
|
||||
lex->sphead->m_body_begin= lip->tok_start;
|
||||
}
|
||||
sp_proc_stmt
|
||||
{
|
||||
|
|
43
sql/table.h
43
sql/table.h
|
@ -304,6 +304,8 @@ typedef struct st_table_share
|
|||
} TABLE_SHARE;
|
||||
|
||||
|
||||
extern ulong refresh_version;
|
||||
|
||||
/* Information for one open table */
|
||||
enum index_hint_type
|
||||
{
|
||||
|
@ -319,8 +321,8 @@ struct st_table {
|
|||
handler *file;
|
||||
#ifdef NOT_YET
|
||||
struct st_table *used_next, **used_prev; /* Link to used tables */
|
||||
#endif
|
||||
struct st_table *open_next, **open_prev; /* Link to open tables */
|
||||
#endif
|
||||
struct st_table *next, *prev;
|
||||
|
||||
THD *in_use; /* Which thread uses this */
|
||||
|
@ -436,7 +438,24 @@ struct st_table {
|
|||
my_bool force_index;
|
||||
my_bool distinct,const_table,no_rows;
|
||||
my_bool key_read, no_keyread;
|
||||
my_bool locked_by_flush;
|
||||
/*
|
||||
Placeholder for an open table which prevents other connections
|
||||
from taking name-locks on this table. Typically used with
|
||||
TABLE_SHARE::version member to take an exclusive name-lock on
|
||||
this table name -- a name lock that not only prevents other
|
||||
threads from opening the table, but also blocks other name
|
||||
locks. This is achieved by:
|
||||
- setting open_placeholder to 1 - this will block other name
|
||||
locks, as wait_for_locked_table_name will be forced to wait,
|
||||
see table_is_used for details.
|
||||
- setting version to 0 - this will force other threads to close
|
||||
the instance of this table and wait (this is the same approach
|
||||
as used for usual name locks).
|
||||
An exclusively name-locked table currently can have no handler
|
||||
object associated with it (db_stat is always 0), but please do
|
||||
not rely on that.
|
||||
*/
|
||||
my_bool open_placeholder;
|
||||
my_bool locked_by_logger;
|
||||
my_bool no_replicate;
|
||||
my_bool locked_by_name;
|
||||
|
@ -497,7 +516,13 @@ struct st_table {
|
|||
read_set= &def_read_set;
|
||||
write_set= &def_write_set;
|
||||
}
|
||||
|
||||
/* Is table open or should be treated as such by name-locking? */
|
||||
inline bool is_name_opened() { return db_stat || open_placeholder; }
|
||||
/*
|
||||
Is this instance of the table should be reopen or represents a name-lock?
|
||||
*/
|
||||
inline bool needs_reopen_or_name_lock()
|
||||
{ return s->version != refresh_version; }
|
||||
};
|
||||
|
||||
enum enum_schema_table_state
|
||||
|
@ -893,6 +918,12 @@ typedef struct st_table_list
|
|||
used for implicit LOCK TABLES only and won't be used in real statement.
|
||||
*/
|
||||
bool prelocking_placeholder;
|
||||
/*
|
||||
This TABLE_LIST object corresponds to the table to be created
|
||||
so it is possible that it does not exist (used in CREATE TABLE
|
||||
... SELECT implementation).
|
||||
*/
|
||||
bool create;
|
||||
|
||||
enum enum_schema_table_state schema_table_state;
|
||||
void calc_md5(char *buffer);
|
||||
|
@ -900,7 +931,11 @@ typedef struct st_table_list
|
|||
int view_check_option(THD *thd, bool ignore_failure);
|
||||
bool setup_underlying(THD *thd);
|
||||
void cleanup_items();
|
||||
bool placeholder() {return derived || view || schema_table || !table; }
|
||||
bool placeholder()
|
||||
{
|
||||
return derived || view || schema_table || create && !table->db_stat ||
|
||||
!table;
|
||||
}
|
||||
void print(THD *thd, String *str);
|
||||
bool check_single_table(st_table_list **table, table_map map,
|
||||
st_table_list *view);
|
||||
|
|
Loading…
Reference in a new issue