mirror of
https://github.com/MariaDB/server.git
synced 2025-01-17 04:22:27 +01:00
Merge mysql.com:/nfsdisk1/lars/bk/mysql-5.1
into mysql.com:/nfsdisk1/lars/bk/mysql-5.1-new-rpl mysql-test/t/disabled.def: Auto merged mysql-test/t/ndb_index_ordered.test: Auto merged mysys/charset.c: Auto merged sql/field.cc: Auto merged sql/handler.cc: Auto merged sql/mysql_priv.h: Auto merged sql/sp_head.cc: Auto merged sql/sp_head.h: Auto merged sql/sql_class.cc: Auto merged sql/sql_class.h: Auto merged sql/sql_insert.cc: Auto merged sql/sql_lex.cc: Auto merged sql/sql_lex.h: Auto merged sql/sql_parse.cc: Auto merged sql/sql_view.cc: Auto merged
This commit is contained in:
commit
63d7c4a8c8
55 changed files with 2180 additions and 955 deletions
|
@ -904,6 +904,14 @@ extern CHARSET_INFO *get_charset(uint cs_number, myf flags);
|
|||
extern CHARSET_INFO *get_charset_by_name(const char *cs_name, myf flags);
|
||||
extern CHARSET_INFO *get_charset_by_csname(const char *cs_name,
|
||||
uint cs_flags, myf my_flags);
|
||||
|
||||
extern bool resolve_charset(CHARSET_INFO **cs,
|
||||
const char *cs_name,
|
||||
CHARSET_INFO *default_cs);
|
||||
extern bool resolve_collation(CHARSET_INFO **cl,
|
||||
const char *cl_name,
|
||||
CHARSET_INFO *default_cl);
|
||||
|
||||
extern void free_charsets(void);
|
||||
extern char *get_charsets_dir(char *buf);
|
||||
extern my_bool my_charset_same(CHARSET_INFO *cs1, CHARSET_INFO *cs2);
|
||||
|
|
|
@ -8,7 +8,7 @@ multi line comment */;
|
|||
;
|
||||
ERROR 42000: Query was empty
|
||||
select 1 /*!32301 +1 */;
|
||||
1 /*!32301 +1
|
||||
1 +1
|
||||
2
|
||||
select 1 /*!52301 +1 */;
|
||||
1
|
||||
|
@ -26,3 +26,13 @@ select 1 # The rest of the row will be ignored
|
|||
1
|
||||
1
|
||||
/* line with only comment */;
|
||||
select 1/*!2*/;
|
||||
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 '2*/' at line 1
|
||||
select 1/*!000002*/;
|
||||
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 '2*/' at line 1
|
||||
select 1/*!999992*/;
|
||||
1
|
||||
1
|
||||
select 1 + /*!00000 2 */ + 3 /*!99999 noise*/ + 4;
|
||||
1 + 2 + 3 + 4
|
||||
10
|
||||
|
|
|
@ -287,3 +287,71 @@ slow_log
|
|||
slow_log_new
|
||||
drop table slow_log_new, general_log_new;
|
||||
use test;
|
||||
SET GLOBAL LOG_OUTPUT = 'TABLE';
|
||||
SET GLOBAL general_log = 0;
|
||||
FLUSH LOGS;
|
||||
TRUNCATE TABLE mysql.general_log;
|
||||
ALTER TABLE mysql.general_log ENGINE = MyISAM;
|
||||
ALTER TABLE mysql.general_log
|
||||
ADD COLUMN seq BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY;
|
||||
SET GLOBAL general_log = 1;
|
||||
FLUSH LOGS;
|
||||
SELECT * FROM mysql.general_log;
|
||||
event_time user_host thread_id server_id command_type argument seq
|
||||
EVENT_TIME USER_HOST THREAD_ID SERVER_ID Query FLUSH LOGS 1
|
||||
EVENT_TIME USER_HOST THREAD_ID SERVER_ID Query SELECT * FROM mysql.general_log 2
|
||||
SELECT * FROM mysql.general_log;
|
||||
event_time user_host thread_id server_id command_type argument seq
|
||||
EVENT_TIME USER_HOST THREAD_ID SERVER_ID Query FLUSH LOGS 1
|
||||
EVENT_TIME USER_HOST THREAD_ID SERVER_ID Query SELECT * FROM mysql.general_log 2
|
||||
EVENT_TIME USER_HOST THREAD_ID SERVER_ID Query SELECT * FROM mysql.general_log 3
|
||||
SELECT "My own query 1";
|
||||
My own query 1
|
||||
My own query 1
|
||||
SELECT "My own query 2";
|
||||
My own query 2
|
||||
My own query 2
|
||||
SELECT * FROM mysql.general_log;
|
||||
event_time user_host thread_id server_id command_type argument seq
|
||||
EVENT_TIME USER_HOST THREAD_ID SERVER_ID Query FLUSH LOGS 1
|
||||
EVENT_TIME USER_HOST THREAD_ID SERVER_ID Query SELECT * FROM mysql.general_log 2
|
||||
EVENT_TIME USER_HOST THREAD_ID SERVER_ID Query SELECT * FROM mysql.general_log 3
|
||||
EVENT_TIME USER_HOST THREAD_ID SERVER_ID Query SELECT "My own query 1" 4
|
||||
EVENT_TIME USER_HOST THREAD_ID SERVER_ID Query SELECT "My own query 2" 5
|
||||
EVENT_TIME USER_HOST THREAD_ID SERVER_ID Query SELECT * FROM mysql.general_log 6
|
||||
SET GLOBAL general_log = 0;
|
||||
FLUSH LOGS;
|
||||
ALTER TABLE mysql.general_log DROP COLUMN seq;
|
||||
ALTER TABLE mysql.general_log ENGINE = CSV;
|
||||
SET @old_long_query_time:=@@long_query_time;
|
||||
SET GLOBAL slow_query_log = 0;
|
||||
FLUSH LOGS;
|
||||
TRUNCATE TABLE mysql.slow_log;
|
||||
ALTER TABLE mysql.slow_log ENGINE = MyISAM;
|
||||
ALTER TABLE mysql.slow_log
|
||||
ADD COLUMN seq BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY;
|
||||
SET SESSION long_query_time = 1;
|
||||
SET GLOBAL slow_query_log = 1;
|
||||
FLUSH LOGS;
|
||||
SELECT "My own slow query", sleep(2);
|
||||
My own slow query sleep(2)
|
||||
My own slow query 0
|
||||
SELECT "My own slow query", sleep(2);
|
||||
My own slow query sleep(2)
|
||||
My own slow query 0
|
||||
SELECT "My own slow query", sleep(2);
|
||||
My own slow query sleep(2)
|
||||
My own slow query 0
|
||||
SELECT "My own slow query", sleep(2);
|
||||
My own slow query sleep(2)
|
||||
My own slow query 0
|
||||
SELECT * FROM mysql.slow_log WHERE seq >= 2 LIMIT 3;
|
||||
start_time user_host query_time lock_time rows_sent rows_examined db last_insert_id insert_id server_id sql_text seq
|
||||
START_TIME USER_HOST QUERY_TIME 00:00:00 1 0 test NULL NULL 1 SELECT "My own slow query", sleep(2) 2
|
||||
START_TIME USER_HOST QUERY_TIME 00:00:00 1 0 test NULL NULL 1 SELECT "My own slow query", sleep(2) 3
|
||||
START_TIME USER_HOST QUERY_TIME 00:00:00 1 0 test NULL NULL 1 SELECT "My own slow query", sleep(2) 4
|
||||
SET GLOBAL slow_query_log = 0;
|
||||
SET SESSION long_query_time =@old_long_query_time;
|
||||
FLUSH LOGS;
|
||||
ALTER TABLE mysql.slow_log DROP COLUMN seq;
|
||||
ALTER TABLE mysql.slow_log ENGINE = CSV;
|
||||
|
|
|
@ -1444,3 +1444,51 @@ insert into t1 values ('c');
|
|||
a
|
||||
drop table t1;
|
||||
set GLOBAL query_cache_size= default;
|
||||
drop database if exists db1;
|
||||
drop database if exists db2;
|
||||
set GLOBAL query_cache_size=15*1024*1024;
|
||||
create database db1;
|
||||
use db1;
|
||||
create table t1(c1 int)engine=myisam;
|
||||
insert into t1(c1) values (1);
|
||||
select * from db1.t1 f;
|
||||
c1
|
||||
1
|
||||
show status like 'Qcache_queries_in_cache';
|
||||
Variable_name Value
|
||||
Qcache_queries_in_cache 1
|
||||
rename schema db1 to db2;
|
||||
show status like 'Qcache_queries_in_cache';
|
||||
Variable_name Value
|
||||
Qcache_queries_in_cache 0
|
||||
drop database db2;
|
||||
set global query_cache_size=default;
|
||||
drop database if exists db1;
|
||||
drop database if exists db3;
|
||||
set GLOBAL query_cache_size=15*1024*1024;
|
||||
create database db1;
|
||||
create database db3;
|
||||
use db1;
|
||||
create table t1(c1 int) engine=myisam;
|
||||
use db3;
|
||||
create table t1(c1 int) engine=myisam;
|
||||
use db1;
|
||||
insert into t1(c1) values (1);
|
||||
use mysql;
|
||||
select * from db1.t1;
|
||||
c1
|
||||
1
|
||||
select c1+1 from db1.t1;
|
||||
c1+1
|
||||
2
|
||||
select * from db3.t1;
|
||||
c1
|
||||
show status like 'Qcache_queries_in_cache';
|
||||
Variable_name Value
|
||||
Qcache_queries_in_cache 3
|
||||
rename schema db1 to db2;
|
||||
show status like 'Qcache_queries_in_cache';
|
||||
Variable_name Value
|
||||
Qcache_queries_in_cache 1
|
||||
drop database db2;
|
||||
drop database db3;
|
||||
|
|
|
@ -6281,4 +6281,130 @@ v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VI
|
|||
DROP VIEW v1;
|
||||
DROP FUNCTION metered;
|
||||
DROP TABLE t1;
|
||||
End of 5.0 tests
|
||||
drop procedure if exists proc_25411_a;
|
||||
drop procedure if exists proc_25411_b;
|
||||
drop procedure if exists proc_25411_c;
|
||||
create procedure proc_25411_a()
|
||||
begin
|
||||
/* real comment */
|
||||
select 1;
|
||||
/*! select 2; */
|
||||
select 3;
|
||||
/*!00000 select 4; */
|
||||
/*!99999 select 5; */
|
||||
end
|
||||
$$
|
||||
create procedure proc_25411_b(
|
||||
/* real comment */
|
||||
/*! p1 int, */
|
||||
/*!00000 p2 int */
|
||||
/*!99999 ,p3 int */
|
||||
)
|
||||
begin
|
||||
select p1, p2;
|
||||
end
|
||||
$$
|
||||
create procedure proc_25411_c()
|
||||
begin
|
||||
select 1/*!,2*//*!00000,3*//*!99999,4*/;
|
||||
select 1/*! ,2*//*!00000 ,3*//*!99999 ,4*/;
|
||||
select 1/*!,2 *//*!00000,3 *//*!99999,4 */;
|
||||
select 1/*! ,2 *//*!00000 ,3 *//*!99999 ,4 */;
|
||||
select 1 /*!,2*/ /*!00000,3*/ /*!99999,4*/ ;
|
||||
end
|
||||
$$
|
||||
show create procedure proc_25411_a;
|
||||
Procedure sql_mode Create Procedure
|
||||
proc_25411_a CREATE DEFINER=`root`@`localhost` PROCEDURE `proc_25411_a`()
|
||||
begin
|
||||
/* real comment */
|
||||
select 1;
|
||||
select 2;
|
||||
select 3;
|
||||
select 4;
|
||||
|
||||
end
|
||||
call proc_25411_a();
|
||||
1
|
||||
1
|
||||
2
|
||||
2
|
||||
3
|
||||
3
|
||||
4
|
||||
4
|
||||
show create procedure proc_25411_b;
|
||||
Procedure sql_mode Create Procedure
|
||||
proc_25411_b CREATE DEFINER=`root`@`localhost` PROCEDURE `proc_25411_b`(
|
||||
/* real comment */
|
||||
p1 int,
|
||||
p2 int
|
||||
|
||||
)
|
||||
begin
|
||||
select p1, p2;
|
||||
end
|
||||
select name, param_list, body from mysql.proc where name like "%25411%";
|
||||
name param_list body
|
||||
proc_25411_a begin
|
||||
/* real comment */
|
||||
select 1;
|
||||
select 2;
|
||||
select 3;
|
||||
select 4;
|
||||
|
||||
end
|
||||
proc_25411_b
|
||||
/* real comment */
|
||||
p1 int,
|
||||
p2 int
|
||||
|
||||
begin
|
||||
select p1, p2;
|
||||
end
|
||||
proc_25411_c begin
|
||||
select 1,2,3;
|
||||
select 1 ,2 ,3;
|
||||
select 1,2 ,3 ;
|
||||
select 1 ,2 ,3 ;
|
||||
select 1 ,2 ,3 ;
|
||||
end
|
||||
call proc_25411_b(10, 20);
|
||||
p1 p2
|
||||
10 20
|
||||
show create procedure proc_25411_c;
|
||||
Procedure sql_mode Create Procedure
|
||||
proc_25411_c CREATE DEFINER=`root`@`localhost` PROCEDURE `proc_25411_c`()
|
||||
begin
|
||||
select 1,2,3;
|
||||
select 1 ,2 ,3;
|
||||
select 1,2 ,3 ;
|
||||
select 1 ,2 ,3 ;
|
||||
select 1 ,2 ,3 ;
|
||||
end
|
||||
call proc_25411_c();
|
||||
1 2 3
|
||||
1 2 3
|
||||
1 2 3
|
||||
1 2 3
|
||||
1 2 3
|
||||
1 2 3
|
||||
1 2 3
|
||||
1 2 3
|
||||
1 2 3
|
||||
1 2 3
|
||||
drop procedure proc_25411_a;
|
||||
drop procedure proc_25411_b;
|
||||
drop procedure proc_25411_c;
|
||||
drop procedure if exists proc_26302;
|
||||
create procedure proc_26302()
|
||||
select 1 /* testing */;
|
||||
show create procedure proc_26302;
|
||||
Procedure sql_mode Create Procedure
|
||||
proc_26302 CREATE DEFINER=`root`@`localhost` PROCEDURE `proc_26302`()
|
||||
select 1 /* testing */
|
||||
select ROUTINE_NAME, ROUTINE_DEFINITION from information_schema.ROUTINES
|
||||
where ROUTINE_NAME = "proc_26302";
|
||||
ROUTINE_NAME ROUTINE_DEFINITION
|
||||
proc_26302 select 1 /* testing */
|
||||
drop procedure proc_26302;
|
||||
|
|
|
@ -1477,3 +1477,19 @@ DROP TABLE t1,t2;
|
|||
SET SESSION LOW_PRIORITY_UPDATES=DEFAULT;
|
||||
SET GLOBAL LOW_PRIORITY_UPDATES=DEFAULT;
|
||||
End of 5.0 tests
|
||||
drop table if exists table_25411_a;
|
||||
drop table if exists table_25411_b;
|
||||
create table table_25411_a(a int);
|
||||
create table table_25411_b(b int);
|
||||
create trigger trg_25411a_ai after insert on table_25411_a
|
||||
for each row
|
||||
insert into table_25411_b select new.*;
|
||||
select * from table_25411_a;
|
||||
a
|
||||
insert into table_25411_a values (1);
|
||||
ERROR 42S02: Unknown table 'new'
|
||||
select * from table_25411_a;
|
||||
a
|
||||
1
|
||||
drop table table_25411_a;
|
||||
drop table table_25411_b;
|
||||
|
|
|
@ -79,3 +79,19 @@ select length(a) from t1;
|
|||
length(a)
|
||||
6
|
||||
drop table t1;
|
||||
drop table if exists table_28127_a;
|
||||
drop table if exists table_28127_b;
|
||||
create table table_28127_a(0b02 int);
|
||||
show create table table_28127_a;
|
||||
Table Create Table
|
||||
table_28127_a CREATE TABLE `table_28127_a` (
|
||||
`0b02` int(11) DEFAULT NULL
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=latin1
|
||||
create table table_28127_b(0b2 int);
|
||||
show create table table_28127_b;
|
||||
Table Create Table
|
||||
table_28127_b CREATE TABLE `table_28127_b` (
|
||||
`0b2` int(11) DEFAULT NULL
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=latin1
|
||||
drop table table_28127_a;
|
||||
drop table table_28127_b;
|
||||
|
|
|
@ -19,3 +19,18 @@ select 1 # The rest of the row will be ignored
|
|||
/* line with only comment */;
|
||||
|
||||
# End of 4.1 tests
|
||||
|
||||
#
|
||||
# Bug#25411 (trigger code truncated)
|
||||
#
|
||||
|
||||
--error ER_PARSE_ERROR
|
||||
select 1/*!2*/;
|
||||
|
||||
--error ER_PARSE_ERROR
|
||||
select 1/*!000002*/;
|
||||
|
||||
select 1/*!999992*/;
|
||||
|
||||
select 1 + /*!00000 2 */ + 3 /*!99999 noise*/ + 4;
|
||||
|
||||
|
|
|
@ -48,3 +48,6 @@ ndb_partition_error2 : HF is not sure if the test can work as internded on all
|
|||
im_options_set : Bug#20294: Instance manager tests fail randomly
|
||||
im_options_unset : Bug#20294: Instance manager tests fail randomly
|
||||
mysql_upgrade : Bug#28560 test links to /usr/local/mysql/lib libraries, causes non-determinism and failures on ABI breakage
|
||||
rpl_udf : Bug#28993 rpl_udf test causes server crash and valgrind warning in pushbuild
|
||||
rpl_ndb_circular : Bug#29233 rpl_ndb_circular fails randomly
|
||||
ndb_dd_sql_features : Bug#29102 ndb_dd_sql_features fails in pushbuild
|
||||
|
|
|
@ -18,3 +18,4 @@ drop database events_test;
|
|||
let $wait_condition=
|
||||
select count(*) = 0 from information_schema.processlist
|
||||
where db='events_test' and command = 'Connect' and user=current_user();
|
||||
--source include/wait_condition.inc
|
||||
|
|
|
@ -145,11 +145,11 @@ select @@optimizer_prune_level;
|
|||
#
|
||||
# These are the values for the parameters that control the greedy optimizer
|
||||
# (total 6 combinations - 3 for optimizer_search_depth, 2 for optimizer_prune_level):
|
||||
#
|
||||
# 3:
|
||||
# set optimizer_search_depth=0; - automatic
|
||||
# set optimizer_search_depth=1; - min
|
||||
# set optimizer_search_depth=62; - max (default)
|
||||
#
|
||||
# 2:
|
||||
# set optimizer_prune_level=0 - exhaustive;
|
||||
# set optimizer_prune_level=1 - heuristic; # default
|
||||
|
||||
|
|
|
@ -512,10 +512,12 @@ select * from v1a join (t3 natural join t4) on a = y;
|
|||
#--------------------------------------------------------------------
|
||||
# Negative tests (tests for errors)
|
||||
#--------------------------------------------------------------------
|
||||
# works in Oracle - bug
|
||||
-- error 1052
|
||||
select * from t1 natural join (t3 cross join t4); # works in Oracle - bug
|
||||
select * from t1 natural join (t3 cross join t4);
|
||||
# works in Oracle - bug
|
||||
-- error 1052
|
||||
select * from (t3 cross join t4) natural join t1; # works in Oracle - bug
|
||||
select * from (t3 cross join t4) natural join t1;
|
||||
-- error 1052
|
||||
select * from t1 join (t2, t3) using (b);
|
||||
-- error 1052
|
||||
|
|
|
@ -400,6 +400,76 @@ show tables like "%log%";
|
|||
drop table slow_log_new, general_log_new;
|
||||
use test;
|
||||
|
||||
#
|
||||
# Bug#27857 (Log tables supplies the wrong value for generating
|
||||
# AUTO_INCREMENT numbers)
|
||||
#
|
||||
|
||||
SET GLOBAL LOG_OUTPUT = 'TABLE';
|
||||
|
||||
## test the general log
|
||||
|
||||
SET GLOBAL general_log = 0;
|
||||
FLUSH LOGS;
|
||||
|
||||
TRUNCATE TABLE mysql.general_log;
|
||||
ALTER TABLE mysql.general_log ENGINE = MyISAM;
|
||||
ALTER TABLE mysql.general_log
|
||||
ADD COLUMN seq BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY;
|
||||
|
||||
SET GLOBAL general_log = 1;
|
||||
FLUSH LOGS;
|
||||
|
||||
--replace_column 1 EVENT_TIME 2 USER_HOST 3 THREAD_ID 4 SERVER_ID
|
||||
SELECT * FROM mysql.general_log;
|
||||
--replace_column 1 EVENT_TIME 2 USER_HOST 3 THREAD_ID 4 SERVER_ID
|
||||
SELECT * FROM mysql.general_log;
|
||||
SELECT "My own query 1";
|
||||
SELECT "My own query 2";
|
||||
--replace_column 1 EVENT_TIME 2 USER_HOST 3 THREAD_ID 4 SERVER_ID
|
||||
SELECT * FROM mysql.general_log;
|
||||
|
||||
SET GLOBAL general_log = 0;
|
||||
FLUSH LOGS;
|
||||
|
||||
ALTER TABLE mysql.general_log DROP COLUMN seq;
|
||||
ALTER TABLE mysql.general_log ENGINE = CSV;
|
||||
|
||||
## test the slow query log
|
||||
|
||||
SET @old_long_query_time:=@@long_query_time;
|
||||
|
||||
SET GLOBAL slow_query_log = 0;
|
||||
FLUSH LOGS;
|
||||
|
||||
TRUNCATE TABLE mysql.slow_log;
|
||||
ALTER TABLE mysql.slow_log ENGINE = MyISAM;
|
||||
|
||||
ALTER TABLE mysql.slow_log
|
||||
ADD COLUMN seq BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY;
|
||||
|
||||
SET SESSION long_query_time = 1;
|
||||
SET GLOBAL slow_query_log = 1;
|
||||
FLUSH LOGS;
|
||||
|
||||
## FLUSH LOGS above might be slow, so the following is
|
||||
## logged as either seq 1-4 or seq 2-5
|
||||
SELECT "My own slow query", sleep(2);
|
||||
SELECT "My own slow query", sleep(2);
|
||||
SELECT "My own slow query", sleep(2);
|
||||
SELECT "My own slow query", sleep(2);
|
||||
|
||||
## So we look for seq 2-4
|
||||
--replace_column 1 START_TIME 2 USER_HOST 3 QUERY_TIME
|
||||
SELECT * FROM mysql.slow_log WHERE seq >= 2 LIMIT 3;
|
||||
|
||||
SET GLOBAL slow_query_log = 0;
|
||||
SET SESSION long_query_time =@old_long_query_time;
|
||||
FLUSH LOGS;
|
||||
|
||||
ALTER TABLE mysql.slow_log DROP COLUMN seq;
|
||||
ALTER TABLE mysql.slow_log ENGINE = CSV;
|
||||
|
||||
# kill all connections
|
||||
disconnect con1;
|
||||
disconnect con2;
|
||||
|
|
|
@ -1024,3 +1024,48 @@ drop table t1;
|
|||
set GLOBAL query_cache_size= default;
|
||||
|
||||
# End of 5.0 tests
|
||||
|
||||
|
||||
#
|
||||
# Bug #28211 RENAME DATABASE and query cache don't play nicely together
|
||||
#
|
||||
--disable_warnings
|
||||
drop database if exists db1;
|
||||
drop database if exists db2;
|
||||
--enable_warnings
|
||||
set GLOBAL query_cache_size=15*1024*1024;
|
||||
create database db1;
|
||||
use db1;
|
||||
create table t1(c1 int)engine=myisam;
|
||||
insert into t1(c1) values (1);
|
||||
select * from db1.t1 f;
|
||||
show status like 'Qcache_queries_in_cache';
|
||||
rename schema db1 to db2;
|
||||
show status like 'Qcache_queries_in_cache';
|
||||
drop database db2;
|
||||
set global query_cache_size=default;
|
||||
|
||||
--disable_warnings
|
||||
drop database if exists db1;
|
||||
drop database if exists db3;
|
||||
--enable_warnings
|
||||
set GLOBAL query_cache_size=15*1024*1024;
|
||||
create database db1;
|
||||
create database db3;
|
||||
use db1;
|
||||
create table t1(c1 int) engine=myisam;
|
||||
use db3;
|
||||
create table t1(c1 int) engine=myisam;
|
||||
use db1;
|
||||
insert into t1(c1) values (1);
|
||||
use mysql;
|
||||
select * from db1.t1;
|
||||
select c1+1 from db1.t1;
|
||||
select * from db3.t1;
|
||||
show status like 'Qcache_queries_in_cache';
|
||||
rename schema db1 to db2;
|
||||
show status like 'Qcache_queries_in_cache';
|
||||
drop database db2;
|
||||
drop database db3;
|
||||
|
||||
# End of 5.1 tests
|
||||
|
|
|
@ -7251,4 +7251,83 @@ DROP FUNCTION metered;
|
|||
DROP TABLE t1;
|
||||
|
||||
|
||||
--echo End of 5.0 tests
|
||||
#
|
||||
# Bug#25411 (trigger code truncated)
|
||||
#
|
||||
|
||||
--disable_warnings
|
||||
drop procedure if exists proc_25411_a;
|
||||
drop procedure if exists proc_25411_b;
|
||||
drop procedure if exists proc_25411_c;
|
||||
--enable_warnings
|
||||
|
||||
delimiter $$;
|
||||
|
||||
create procedure proc_25411_a()
|
||||
begin
|
||||
/* real comment */
|
||||
select 1;
|
||||
/*! select 2; */
|
||||
select 3;
|
||||
/*!00000 select 4; */
|
||||
/*!99999 select 5; */
|
||||
end
|
||||
$$
|
||||
|
||||
create procedure proc_25411_b(
|
||||
/* real comment */
|
||||
/*! p1 int, */
|
||||
/*!00000 p2 int */
|
||||
/*!99999 ,p3 int */
|
||||
)
|
||||
begin
|
||||
select p1, p2;
|
||||
end
|
||||
$$
|
||||
|
||||
create procedure proc_25411_c()
|
||||
begin
|
||||
select 1/*!,2*//*!00000,3*//*!99999,4*/;
|
||||
select 1/*! ,2*//*!00000 ,3*//*!99999 ,4*/;
|
||||
select 1/*!,2 *//*!00000,3 *//*!99999,4 */;
|
||||
select 1/*! ,2 *//*!00000 ,3 *//*!99999 ,4 */;
|
||||
select 1 /*!,2*/ /*!00000,3*/ /*!99999,4*/ ;
|
||||
end
|
||||
$$
|
||||
|
||||
delimiter ;$$
|
||||
|
||||
show create procedure proc_25411_a;
|
||||
call proc_25411_a();
|
||||
|
||||
show create procedure proc_25411_b;
|
||||
select name, param_list, body from mysql.proc where name like "%25411%";
|
||||
call proc_25411_b(10, 20);
|
||||
|
||||
show create procedure proc_25411_c;
|
||||
call proc_25411_c();
|
||||
|
||||
drop procedure proc_25411_a;
|
||||
drop procedure proc_25411_b;
|
||||
drop procedure proc_25411_c;
|
||||
|
||||
|
||||
#
|
||||
# Bug#26302 (MySQL server cuts off trailing "*/" from comments in SP/func)
|
||||
#
|
||||
|
||||
--disable_warnings
|
||||
drop procedure if exists proc_26302;
|
||||
--enable_warnings
|
||||
|
||||
create procedure proc_26302()
|
||||
select 1 /* testing */;
|
||||
|
||||
show create procedure proc_26302;
|
||||
|
||||
select ROUTINE_NAME, ROUTINE_DEFINITION from information_schema.ROUTINES
|
||||
where ROUTINE_NAME = "proc_26302";
|
||||
|
||||
drop procedure proc_26302;
|
||||
|
||||
|
||||
|
|
|
@ -1830,3 +1830,30 @@ SET SESSION LOW_PRIORITY_UPDATES=DEFAULT;
|
|||
SET GLOBAL LOW_PRIORITY_UPDATES=DEFAULT;
|
||||
|
||||
--echo End of 5.0 tests
|
||||
|
||||
#
|
||||
# Bug#25411 (trigger code truncated)
|
||||
#
|
||||
|
||||
--disable_warnings
|
||||
drop table if exists table_25411_a;
|
||||
drop table if exists table_25411_b;
|
||||
--enable_warnings
|
||||
|
||||
create table table_25411_a(a int);
|
||||
create table table_25411_b(b int);
|
||||
|
||||
create trigger trg_25411a_ai after insert on table_25411_a
|
||||
for each row
|
||||
insert into table_25411_b select new.*;
|
||||
|
||||
select * from table_25411_a;
|
||||
|
||||
--error ER_BAD_TABLE_ERROR
|
||||
insert into table_25411_a values (1);
|
||||
|
||||
select * from table_25411_a;
|
||||
|
||||
drop table table_25411_a;
|
||||
drop table table_25411_b;
|
||||
|
||||
|
|
|
@ -85,3 +85,22 @@ alter table t1 modify a varchar(255);
|
|||
select length(a) from t1;
|
||||
drop table t1;
|
||||
|
||||
|
||||
#
|
||||
# Bug#28127 (Some valid identifiers names are not parsed correctly)
|
||||
#
|
||||
|
||||
--disable_warnings
|
||||
drop table if exists table_28127_a;
|
||||
drop table if exists table_28127_b;
|
||||
--enable_warnings
|
||||
|
||||
create table table_28127_a(0b02 int);
|
||||
show create table table_28127_a;
|
||||
|
||||
create table table_28127_b(0b2 int);
|
||||
show create table table_28127_b;
|
||||
|
||||
drop table table_28127_a;
|
||||
drop table table_28127_b;
|
||||
|
||||
|
|
|
@ -587,6 +587,70 @@ CHARSET_INFO *get_charset_by_csname(const char *cs_name,
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
Resolve character set by the character set name (utf8, latin1, ...).
|
||||
|
||||
The function tries to resolve character set by the specified name. If
|
||||
there is character set with the given name, it is assigned to the "cs"
|
||||
parameter and FALSE is returned. If there is no such character set,
|
||||
"default_cs" is assigned to the "cs" and TRUE is returned.
|
||||
|
||||
@param[out] cs Variable to store character set.
|
||||
@param[in] cs_name Character set name.
|
||||
@param[in] default_cs Default character set.
|
||||
|
||||
@return FALSE if character set was resolved successfully; TRUE if there
|
||||
is no character set with given name.
|
||||
*/
|
||||
|
||||
bool resolve_charset(CHARSET_INFO **cs,
|
||||
const char *cs_name,
|
||||
CHARSET_INFO *default_cs)
|
||||
{
|
||||
*cs= get_charset_by_csname(cs_name, MY_CS_PRIMARY, MYF(0));
|
||||
|
||||
if (*cs == NULL)
|
||||
{
|
||||
*cs= default_cs;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Resolve collation by the collation name (utf8_general_ci, ...).
|
||||
|
||||
The function tries to resolve collation by the specified name. If there
|
||||
is collation with the given name, it is assigned to the "cl" parameter
|
||||
and FALSE is returned. If there is no such collation, "default_cl" is
|
||||
assigned to the "cl" and TRUE is returned.
|
||||
|
||||
@param[out] cl Variable to store collation.
|
||||
@param[in] cl_name Collation name.
|
||||
@param[in] default_cl Default collation.
|
||||
|
||||
@return FALSE if collation was resolved successfully; TRUE if there is no
|
||||
collation with given name.
|
||||
*/
|
||||
|
||||
bool resolve_collation(CHARSET_INFO **cl,
|
||||
const char *cl_name,
|
||||
CHARSET_INFO *default_cl)
|
||||
{
|
||||
*cl= get_charset_by_name(cl_name, MYF(0));
|
||||
|
||||
if (*cl == NULL)
|
||||
{
|
||||
*cl= default_cl;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Escape string with backslashes (\)
|
||||
|
||||
|
|
|
@ -94,17 +94,18 @@ Event_parse_data::Event_parse_data()
|
|||
:on_completion(Event_basic::ON_COMPLETION_DROP),
|
||||
status(Event_basic::ENABLED),
|
||||
do_not_create(FALSE),
|
||||
item_starts(NULL), item_ends(NULL), item_execute_at(NULL),
|
||||
starts_null(TRUE), ends_null(TRUE), execute_at_null(TRUE),
|
||||
item_expression(NULL), expression(0)
|
||||
body_changed(FALSE),
|
||||
item_starts(NULL), item_ends(NULL), item_execute_at(NULL),
|
||||
starts_null(TRUE), ends_null(TRUE), execute_at_null(TRUE),
|
||||
item_expression(NULL), expression(0)
|
||||
{
|
||||
DBUG_ENTER("Event_parse_data::Event_parse_data");
|
||||
|
||||
/* Actually in the parser STARTS is always set */
|
||||
starts= ends= execute_at= 0;
|
||||
|
||||
body.str= comment.str= NULL;
|
||||
body.length= comment.length= 0;
|
||||
comment.str= NULL;
|
||||
comment.length= 0;
|
||||
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
@ -137,86 +138,6 @@ Event_parse_data::init_name(THD *thd, sp_name *spn)
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
Set body of the event - what should be executed.
|
||||
|
||||
SYNOPSIS
|
||||
Event_parse_data::init_body()
|
||||
thd THD
|
||||
|
||||
NOTE
|
||||
The body is extracted by copying all data between the
|
||||
start of the body set by another method and the current pointer in Lex.
|
||||
|
||||
Some questionable removal of characters is done in here, and that part
|
||||
should be refactored when the parser is smarter.
|
||||
*/
|
||||
|
||||
void
|
||||
Event_parse_data::init_body(THD *thd)
|
||||
{
|
||||
DBUG_ENTER("Event_parse_data::init_body");
|
||||
|
||||
/* 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 */
|
||||
while (body_begin < body_end)
|
||||
{
|
||||
|
||||
if ((*body_end == '\0') ||
|
||||
(my_isspace(thd->variables.character_set_client, *body_end)))
|
||||
{ /* consume NULs and meaningless whitespace */
|
||||
--body.length;
|
||||
--body_end;
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
consume closing comments
|
||||
|
||||
This is arguably wrong, but it's the best we have until the parser is
|
||||
changed to be smarter. FIXME PARSER
|
||||
|
||||
See also the sp_head code, where something like this is done also.
|
||||
|
||||
One idea is to keep in the lexer structure the count of the number of
|
||||
open-comments we've entered, and scan left-to-right looking for a
|
||||
closing comment IFF the count is greater than zero.
|
||||
|
||||
Another idea is to remove the closing comment-characters wholly in the
|
||||
parser, since that's where it "removes" the opening characters.
|
||||
*/
|
||||
if ((*(body_end - 1) == '*') && (*body_end == '/'))
|
||||
{
|
||||
DBUG_PRINT("info", ("consumend one '*" "/' comment in the query '%s'",
|
||||
body_begin));
|
||||
body.length-= 2;
|
||||
body_end-= 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
break; /* none were found, so we have excised all we can. */
|
||||
}
|
||||
|
||||
/* the first is always whitespace which I cannot skip in the parser */
|
||||
while (my_isspace(thd->variables.character_set_client, *body_begin))
|
||||
{
|
||||
++body_begin;
|
||||
--body.length;
|
||||
}
|
||||
body.str= thd->strmake(body_begin, body.length);
|
||||
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
This function is called on CREATE EVENT or ALTER EVENT. When either
|
||||
ENDS or AT is in the past, we are trying to create an event that
|
||||
|
@ -838,36 +759,32 @@ Event_timed::init()
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
Loads an event's body from a row from mysql.event
|
||||
/**
|
||||
Load an event's body from a row from mysql.event.
|
||||
@details This method is silent on errors and should behave like that.
|
||||
Callers should handle throwing of error messages. The reason is that the
|
||||
class should not know about how to deal with communication.
|
||||
|
||||
SYNOPSIS
|
||||
Event_job_data::load_from_row(THD *thd, TABLE *table)
|
||||
|
||||
RETURN VALUE
|
||||
0 OK
|
||||
EVEX_GET_FIELD_FAILED Error
|
||||
|
||||
NOTES
|
||||
This method is silent on errors and should behave like that. Callers
|
||||
should handle throwing of error messages. The reason is that the class
|
||||
should not know about how to deal with communication.
|
||||
@return Operation status
|
||||
@retval FALSE OK
|
||||
@retval TRUE Error
|
||||
*/
|
||||
|
||||
int
|
||||
bool
|
||||
Event_job_data::load_from_row(THD *thd, TABLE *table)
|
||||
{
|
||||
char *ptr;
|
||||
uint len;
|
||||
LEX_STRING tz_name;
|
||||
|
||||
DBUG_ENTER("Event_job_data::load_from_row");
|
||||
|
||||
if (!table)
|
||||
goto error;
|
||||
DBUG_RETURN(TRUE);
|
||||
|
||||
if (table->s->fields < ET_FIELD_COUNT)
|
||||
goto error;
|
||||
DBUG_RETURN(TRUE);
|
||||
|
||||
LEX_STRING tz_name;
|
||||
if (load_string_fields(table->field,
|
||||
ET_FIELD_DB, &dbname,
|
||||
ET_FIELD_NAME, &name,
|
||||
|
@ -875,10 +792,10 @@ Event_job_data::load_from_row(THD *thd, TABLE *table)
|
|||
ET_FIELD_DEFINER, &definer,
|
||||
ET_FIELD_TIME_ZONE, &tz_name,
|
||||
ET_FIELD_COUNT))
|
||||
goto error;
|
||||
DBUG_RETURN(TRUE);
|
||||
|
||||
if (load_time_zone(thd, tz_name))
|
||||
goto error;
|
||||
DBUG_RETURN(TRUE);
|
||||
|
||||
ptr= strchr(definer.str, '@');
|
||||
|
||||
|
@ -895,29 +812,23 @@ Event_job_data::load_from_row(THD *thd, TABLE *table)
|
|||
|
||||
sql_mode= (ulong) table->field[ET_FIELD_SQL_MODE]->val_int();
|
||||
|
||||
DBUG_RETURN(0);
|
||||
error:
|
||||
DBUG_RETURN(EVEX_GET_FIELD_FAILED);
|
||||
DBUG_RETURN(FALSE);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Loads an event from a row from mysql.event
|
||||
/**
|
||||
Load an event's body from a row from mysql.event.
|
||||
|
||||
SYNOPSIS
|
||||
Event_queue_element::load_from_row(THD *thd, TABLE *table)
|
||||
@details This method is silent on errors and should behave like that.
|
||||
Callers should handle throwing of error messages. The reason is that the
|
||||
class should not know about how to deal with communication.
|
||||
|
||||
RETURN VALUE
|
||||
0 OK
|
||||
EVEX_GET_FIELD_FAILED Error
|
||||
|
||||
NOTES
|
||||
This method is silent on errors and should behave like that. Callers
|
||||
should handle throwing of error messages. The reason is that the class
|
||||
should not know about how to deal with communication.
|
||||
@return Operation status
|
||||
@retval FALSE OK
|
||||
@retval TRUE Error
|
||||
*/
|
||||
|
||||
int
|
||||
bool
|
||||
Event_queue_element::load_from_row(THD *thd, TABLE *table)
|
||||
{
|
||||
char *ptr;
|
||||
|
@ -927,10 +838,10 @@ Event_queue_element::load_from_row(THD *thd, TABLE *table)
|
|||
DBUG_ENTER("Event_queue_element::load_from_row");
|
||||
|
||||
if (!table)
|
||||
goto error;
|
||||
DBUG_RETURN(TRUE);
|
||||
|
||||
if (table->s->fields < ET_FIELD_COUNT)
|
||||
goto error;
|
||||
DBUG_RETURN(TRUE);
|
||||
|
||||
if (load_string_fields(table->field,
|
||||
ET_FIELD_DB, &dbname,
|
||||
|
@ -938,10 +849,10 @@ Event_queue_element::load_from_row(THD *thd, TABLE *table)
|
|||
ET_FIELD_DEFINER, &definer,
|
||||
ET_FIELD_TIME_ZONE, &tz_name,
|
||||
ET_FIELD_COUNT))
|
||||
goto error;
|
||||
DBUG_RETURN(TRUE);
|
||||
|
||||
if (load_time_zone(thd, tz_name))
|
||||
goto error;
|
||||
DBUG_RETURN(TRUE);
|
||||
|
||||
starts_null= table->field[ET_FIELD_STARTS]->is_null();
|
||||
if (!starts_null)
|
||||
|
@ -971,7 +882,7 @@ Event_queue_element::load_from_row(THD *thd, TABLE *table)
|
|||
{
|
||||
if (table->field[ET_FIELD_EXECUTE_AT]->get_date(&time,
|
||||
TIME_NO_ZERO_DATE))
|
||||
goto error;
|
||||
DBUG_RETURN(TRUE);
|
||||
execute_at= sec_since_epoch_TIME(&time);
|
||||
}
|
||||
|
||||
|
@ -990,13 +901,13 @@ Event_queue_element::load_from_row(THD *thd, TABLE *table)
|
|||
|
||||
table->field[ET_FIELD_TRANSIENT_INTERVAL]->val_str(&str);
|
||||
if (!(tmp.length= str.length()))
|
||||
goto error;
|
||||
DBUG_RETURN(TRUE);
|
||||
|
||||
tmp.str= str.c_ptr_safe();
|
||||
|
||||
i= find_string_in_array(interval_type_to_name, &tmp, system_charset_info);
|
||||
if (i < 0)
|
||||
goto error;
|
||||
DBUG_RETURN(TRUE);
|
||||
interval= (interval_type) i;
|
||||
}
|
||||
|
||||
|
@ -1009,7 +920,7 @@ Event_queue_element::load_from_row(THD *thd, TABLE *table)
|
|||
last_executed_changed= FALSE;
|
||||
|
||||
if ((ptr= get_field(&mem_root, table->field[ET_FIELD_STATUS])) == NullS)
|
||||
goto error;
|
||||
DBUG_RETURN(TRUE);
|
||||
|
||||
DBUG_PRINT("load_from_row", ("Event [%s] is [%s]", name.str, ptr));
|
||||
|
||||
|
@ -1028,40 +939,34 @@ Event_queue_element::load_from_row(THD *thd, TABLE *table)
|
|||
break;
|
||||
}
|
||||
if ((ptr= get_field(&mem_root, table->field[ET_FIELD_ORIGINATOR])) == NullS)
|
||||
goto error;
|
||||
DBUG_RETURN(TRUE);
|
||||
originator = table->field[ET_FIELD_ORIGINATOR]->val_int();
|
||||
|
||||
/* ToDo : Andrey . Find a way not to allocate ptr on event_mem_root */
|
||||
if ((ptr= get_field(&mem_root,
|
||||
table->field[ET_FIELD_ON_COMPLETION])) == NullS)
|
||||
goto error;
|
||||
DBUG_RETURN(TRUE);
|
||||
|
||||
on_completion= (ptr[0]=='D'? Event_queue_element::ON_COMPLETION_DROP:
|
||||
Event_queue_element::ON_COMPLETION_PRESERVE);
|
||||
|
||||
DBUG_RETURN(0);
|
||||
error:
|
||||
DBUG_RETURN(EVEX_GET_FIELD_FAILED);
|
||||
DBUG_RETURN(FALSE);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Loads an event from a row from mysql.event
|
||||
/**
|
||||
Load an event's body from a row from mysql.event.
|
||||
|
||||
SYNOPSIS
|
||||
Event_timed::load_from_row(THD *thd, TABLE *table)
|
||||
@details This method is silent on errors and should behave like that.
|
||||
Callers should handle throwing of error messages. The reason is that the
|
||||
class should not know about how to deal with communication.
|
||||
|
||||
RETURN VALUE
|
||||
0 OK
|
||||
EVEX_GET_FIELD_FAILED Error
|
||||
|
||||
NOTES
|
||||
This method is silent on errors and should behave like that. Callers
|
||||
should handle throwing of error messages. The reason is that the class
|
||||
should not know about how to deal with communication.
|
||||
@return Operation status
|
||||
@retval FALSE OK
|
||||
@retval TRUE Error
|
||||
*/
|
||||
|
||||
int
|
||||
bool
|
||||
Event_timed::load_from_row(THD *thd, TABLE *table)
|
||||
{
|
||||
char *ptr;
|
||||
|
@ -1070,12 +975,12 @@ Event_timed::load_from_row(THD *thd, TABLE *table)
|
|||
DBUG_ENTER("Event_timed::load_from_row");
|
||||
|
||||
if (Event_queue_element::load_from_row(thd, table))
|
||||
goto error;
|
||||
DBUG_RETURN(TRUE);
|
||||
|
||||
if (load_string_fields(table->field,
|
||||
ET_FIELD_BODY, &body,
|
||||
ET_FIELD_COUNT))
|
||||
goto error;
|
||||
DBUG_RETURN(TRUE);
|
||||
|
||||
|
||||
ptr= strchr(definer.str, '@');
|
||||
|
@ -1102,9 +1007,7 @@ Event_timed::load_from_row(THD *thd, TABLE *table)
|
|||
|
||||
sql_mode= (ulong) table->field[ET_FIELD_SQL_MODE]->val_int();
|
||||
|
||||
DBUG_RETURN(0);
|
||||
error:
|
||||
DBUG_RETURN(EVEX_GET_FIELD_FAILED);
|
||||
DBUG_RETURN(FALSE);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1925,11 +1828,9 @@ Event_job_data::execute(THD *thd, bool drop)
|
|||
|
||||
{
|
||||
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)
|
||||
if (parse_sql(thd, &lip))
|
||||
{
|
||||
sql_print_error("Event Scheduler: "
|
||||
"%serror during compilation of %s.%s",
|
||||
|
@ -1999,6 +1900,9 @@ end:
|
|||
thd->lex->unit.cleanup();
|
||||
thd->end_statement();
|
||||
thd->cleanup_after_query();
|
||||
/* Avoid races with SHOW PROCESSLIST */
|
||||
thd->query_length= 0;
|
||||
thd->query= NULL;
|
||||
|
||||
DBUG_PRINT("info", ("EXECUTED %s.%s ret: %d", dbname.str, name.str, ret));
|
||||
|
||||
|
|
|
@ -77,7 +77,7 @@ public:
|
|||
Event_basic();
|
||||
virtual ~Event_basic();
|
||||
|
||||
virtual int
|
||||
virtual bool
|
||||
load_from_row(THD *thd, TABLE *table) = 0;
|
||||
|
||||
protected:
|
||||
|
@ -119,7 +119,7 @@ public:
|
|||
Event_queue_element();
|
||||
virtual ~Event_queue_element();
|
||||
|
||||
virtual int
|
||||
virtual bool
|
||||
load_from_row(THD *thd, TABLE *table);
|
||||
|
||||
bool
|
||||
|
@ -157,7 +157,7 @@ public:
|
|||
void
|
||||
init();
|
||||
|
||||
virtual int
|
||||
virtual bool
|
||||
load_from_row(THD *thd, TABLE *table);
|
||||
|
||||
int
|
||||
|
@ -176,7 +176,7 @@ public:
|
|||
|
||||
Event_job_data();
|
||||
|
||||
virtual int
|
||||
virtual bool
|
||||
load_from_row(THD *thd, TABLE *table);
|
||||
|
||||
bool
|
||||
|
@ -205,12 +205,11 @@ public:
|
|||
*/
|
||||
bool do_not_create;
|
||||
|
||||
const char *body_begin;
|
||||
bool body_changed;
|
||||
|
||||
LEX_STRING dbname;
|
||||
LEX_STRING name;
|
||||
LEX_STRING definer;// combination of user and host
|
||||
LEX_STRING body;
|
||||
LEX_STRING comment;
|
||||
|
||||
Item* item_starts;
|
||||
|
@ -235,9 +234,6 @@ public:
|
|||
bool
|
||||
check_parse_data(THD *thd);
|
||||
|
||||
void
|
||||
init_body(THD *thd);
|
||||
|
||||
private:
|
||||
|
||||
void
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
#include "mysql_priv.h"
|
||||
#include "event_db_repository.h"
|
||||
#include "sp_head.h"
|
||||
#include "event_data_objects.h"
|
||||
#include "events.h"
|
||||
#include "sql_show.h"
|
||||
|
@ -141,7 +142,10 @@ const TABLE_FIELD_W_TYPE event_table_fields[ET_FIELD_COUNT] =
|
|||
*/
|
||||
|
||||
static bool
|
||||
mysql_event_fill_row(THD *thd, TABLE *table, Event_parse_data *et,
|
||||
mysql_event_fill_row(THD *thd,
|
||||
TABLE *table,
|
||||
Event_parse_data *et,
|
||||
sp_head *sp,
|
||||
my_bool is_update)
|
||||
{
|
||||
CHARSET_INFO *scs= system_charset_info;
|
||||
|
@ -152,7 +156,6 @@ mysql_event_fill_row(THD *thd, TABLE *table, Event_parse_data *et,
|
|||
|
||||
DBUG_PRINT("info", ("dbname=[%s]", et->dbname.str));
|
||||
DBUG_PRINT("info", ("name =[%s]", et->name.str));
|
||||
DBUG_PRINT("info", ("body =[%s]", et->body.str));
|
||||
|
||||
if (table->s->fields < ET_FIELD_COUNT)
|
||||
{
|
||||
|
@ -187,11 +190,18 @@ mysql_event_fill_row(THD *thd, TABLE *table, Event_parse_data *et,
|
|||
Change the SQL_MODE only if body was present in an ALTER EVENT and of course
|
||||
always during CREATE EVENT.
|
||||
*/
|
||||
if (et->body.str)
|
||||
if (et->body_changed)
|
||||
{
|
||||
DBUG_ASSERT(sp->m_body.str);
|
||||
|
||||
fields[ET_FIELD_SQL_MODE]->store((longlong)thd->variables.sql_mode, TRUE);
|
||||
if (fields[f_num= ET_FIELD_BODY]->store(et->body.str, et->body.length, scs))
|
||||
|
||||
if (fields[f_num= ET_FIELD_BODY]->store(sp->m_body.str,
|
||||
sp->m_body.length,
|
||||
scs))
|
||||
{
|
||||
goto err_truncate;
|
||||
}
|
||||
}
|
||||
|
||||
if (et->expression)
|
||||
|
@ -513,10 +523,12 @@ Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data,
|
|||
{
|
||||
int ret= 1;
|
||||
TABLE *table= NULL;
|
||||
sp_head *sp= thd->lex->sphead;
|
||||
|
||||
DBUG_ENTER("Event_db_repository::create_event");
|
||||
|
||||
DBUG_PRINT("info", ("open mysql.event for update"));
|
||||
DBUG_ASSERT(sp);
|
||||
|
||||
if (open_event_table(thd, TL_WRITE, &table))
|
||||
goto end;
|
||||
|
@ -561,7 +573,7 @@ Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data,
|
|||
goto end;
|
||||
}
|
||||
|
||||
if (parse_data->body.length > table->field[ET_FIELD_BODY]->field_length)
|
||||
if (sp->m_body.length > table->field[ET_FIELD_BODY]->field_length)
|
||||
{
|
||||
my_error(ER_TOO_LONG_BODY, MYF(0), parse_data->name.str);
|
||||
goto end;
|
||||
|
@ -573,7 +585,7 @@ Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data,
|
|||
mysql_event_fill_row() calls my_error() in case of error so no need to
|
||||
handle it here
|
||||
*/
|
||||
if (mysql_event_fill_row(thd, table, parse_data, FALSE))
|
||||
if (mysql_event_fill_row(thd, table, parse_data, sp, FALSE))
|
||||
goto end;
|
||||
|
||||
table->field[ET_FIELD_STATUS]->store((longlong)parse_data->status, TRUE);
|
||||
|
@ -617,7 +629,9 @@ Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data,
|
|||
{
|
||||
CHARSET_INFO *scs= system_charset_info;
|
||||
TABLE *table= NULL;
|
||||
sp_head *sp= thd->lex->sphead;
|
||||
int ret= 1;
|
||||
|
||||
DBUG_ENTER("Event_db_repository::update_event");
|
||||
|
||||
/* None or both must be set */
|
||||
|
@ -661,7 +675,7 @@ Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data,
|
|||
mysql_event_fill_row() calls my_error() in case of error so no need to
|
||||
handle it here
|
||||
*/
|
||||
if (mysql_event_fill_row(thd, table, parse_data, TRUE))
|
||||
if (mysql_event_fill_row(thd, table, parse_data, sp, TRUE))
|
||||
goto end;
|
||||
|
||||
if (new_dbname)
|
||||
|
|
|
@ -326,6 +326,8 @@ end:
|
|||
|
||||
delete event;
|
||||
deinit_event_thread(thd);
|
||||
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
|
||||
|
|
62
sql/field.cc
62
sql/field.cc
|
@ -38,8 +38,8 @@
|
|||
*****************************************************************************/
|
||||
|
||||
#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
|
||||
template class List<create_field>;
|
||||
template class List_iterator<create_field>;
|
||||
template class List<Create_field>;
|
||||
template class List_iterator<Create_field>;
|
||||
#endif
|
||||
|
||||
uchar Field_null::null[1]={1};
|
||||
|
@ -2631,7 +2631,7 @@ void Field_new_decimal::sql_type(String &str) const
|
|||
}
|
||||
|
||||
|
||||
uint Field_new_decimal::is_equal(create_field *new_field)
|
||||
uint Field_new_decimal::is_equal(Create_field *new_field)
|
||||
{
|
||||
return ((new_field->sql_type == real_type()) &&
|
||||
((new_field->flags & UNSIGNED_FLAG) ==
|
||||
|
@ -4366,7 +4366,7 @@ Field_timestamp::Field_timestamp(uchar *ptr_arg, uint32 len_arg,
|
|||
const char *field_name_arg,
|
||||
TABLE_SHARE *share,
|
||||
CHARSET_INFO *cs)
|
||||
:Field_str(ptr_arg, 19, null_ptr_arg, null_bit_arg,
|
||||
:Field_str(ptr_arg, MAX_DATETIME_WIDTH, null_ptr_arg, null_bit_arg,
|
||||
unireg_check_arg, field_name_arg, cs)
|
||||
{
|
||||
/* For 4.0 MYD and 4.0 InnoDB compatibility */
|
||||
|
@ -4383,7 +4383,8 @@ Field_timestamp::Field_timestamp(uchar *ptr_arg, uint32 len_arg,
|
|||
Field_timestamp::Field_timestamp(bool maybe_null_arg,
|
||||
const char *field_name_arg,
|
||||
CHARSET_INFO *cs)
|
||||
:Field_str((uchar*) 0, 19, maybe_null_arg ? (uchar*) "": 0, 0,
|
||||
:Field_str((uchar*) 0, MAX_DATETIME_WIDTH,
|
||||
maybe_null_arg ? (uchar*) "": 0, 0,
|
||||
NONE, field_name_arg, cs)
|
||||
{
|
||||
/* For 4.0 MYD and 4.0 InnoDB compatibility */
|
||||
|
@ -4916,7 +4917,7 @@ String *Field_time::val_str(String *val_buffer,
|
|||
{
|
||||
ASSERT_COLUMN_MARKED_FOR_READ;
|
||||
MYSQL_TIME ltime;
|
||||
val_buffer->alloc(19);
|
||||
val_buffer->alloc(MAX_DATE_STRING_REP_LENGTH);
|
||||
long tmp=(long) sint3korr(ptr);
|
||||
ltime.neg= 0;
|
||||
if (tmp < 0)
|
||||
|
@ -5464,7 +5465,7 @@ int Field_newdate::store_time(MYSQL_TIME *ltime,timestamp_type time_type)
|
|||
(MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE |
|
||||
MODE_INVALID_DATES))), &error))
|
||||
{
|
||||
char buff[12];
|
||||
char buff[MAX_DATE_STRING_REP_LENGTH];
|
||||
String str(buff, sizeof(buff), &my_charset_latin1);
|
||||
make_date((DATE_TIME_FORMAT *) 0, ltime, &str);
|
||||
set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED,
|
||||
|
@ -5695,7 +5696,7 @@ int Field_datetime::store_time(MYSQL_TIME *ltime,timestamp_type time_type)
|
|||
(MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE |
|
||||
MODE_INVALID_DATES))), &error))
|
||||
{
|
||||
char buff[19];
|
||||
char buff[MAX_DATE_STRING_REP_LENGTH];
|
||||
String str(buff, sizeof(buff), &my_charset_latin1);
|
||||
make_datetime((DATE_TIME_FORMAT *) 0, ltime, &str);
|
||||
set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED,
|
||||
|
@ -5771,7 +5772,7 @@ String *Field_datetime::val_str(String *val_buffer,
|
|||
part1=(long) (tmp/LL(1000000));
|
||||
part2=(long) (tmp - (ulonglong) part1*LL(1000000));
|
||||
|
||||
pos=(char*) val_buffer->ptr()+19;
|
||||
pos=(char*) val_buffer->ptr() + MAX_DATETIME_WIDTH;
|
||||
*pos--=0;
|
||||
*pos--= (char) ('0'+(char) (part2%10)); part2/=10;
|
||||
*pos--= (char) ('0'+(char) (part2%10)); part3= (int) (part2 / 10);
|
||||
|
@ -6081,7 +6082,7 @@ int Field_str::store(double nr)
|
|||
}
|
||||
|
||||
|
||||
uint Field::is_equal(create_field *new_field)
|
||||
uint Field::is_equal(Create_field *new_field)
|
||||
{
|
||||
return (new_field->sql_type == real_type());
|
||||
}
|
||||
|
@ -6089,7 +6090,7 @@ uint Field::is_equal(create_field *new_field)
|
|||
|
||||
/* If one of the fields is binary and the other one isn't return 1 else 0 */
|
||||
|
||||
bool Field_str::compare_str_field_flags(create_field *new_field, uint32 flags)
|
||||
bool Field_str::compare_str_field_flags(Create_field *new_field, uint32 flags)
|
||||
{
|
||||
return (((new_field->flags & (BINCMP_FLAG | BINARY_FLAG)) &&
|
||||
!(flags & (BINCMP_FLAG | BINARY_FLAG))) ||
|
||||
|
@ -6098,7 +6099,7 @@ bool Field_str::compare_str_field_flags(create_field *new_field, uint32 flags)
|
|||
}
|
||||
|
||||
|
||||
uint Field_str::is_equal(create_field *new_field)
|
||||
uint Field_str::is_equal(Create_field *new_field)
|
||||
{
|
||||
if (compare_str_field_flags(new_field, flags))
|
||||
return 0;
|
||||
|
@ -6951,7 +6952,7 @@ Field *Field_varstring::new_key_field(MEM_ROOT *root,
|
|||
}
|
||||
|
||||
|
||||
uint Field_varstring::is_equal(create_field *new_field)
|
||||
uint Field_varstring::is_equal(Create_field *new_field)
|
||||
{
|
||||
if (new_field->sql_type == real_type() &&
|
||||
new_field->charset == field_charset)
|
||||
|
@ -7633,7 +7634,7 @@ uint Field_blob::max_packed_col_length(uint max_length)
|
|||
}
|
||||
|
||||
|
||||
uint Field_blob::is_equal(create_field *new_field)
|
||||
uint Field_blob::is_equal(Create_field *new_field)
|
||||
{
|
||||
if (compare_str_field_flags(new_field, flags))
|
||||
return 0;
|
||||
|
@ -8171,7 +8172,7 @@ bool Field_num::eq_def(Field *field)
|
|||
}
|
||||
|
||||
|
||||
uint Field_num::is_equal(create_field *new_field)
|
||||
uint Field_num::is_equal(Create_field *new_field)
|
||||
{
|
||||
return ((new_field->sql_type == real_type()) &&
|
||||
((new_field->flags & UNSIGNED_FLAG) == (uint) (flags &
|
||||
|
@ -8276,7 +8277,7 @@ Field *Field_bit::new_key_field(MEM_ROOT *root,
|
|||
}
|
||||
|
||||
|
||||
uint Field_bit::is_equal(create_field *new_field)
|
||||
uint Field_bit::is_equal(Create_field *new_field)
|
||||
{
|
||||
return (new_field->sql_type == real_type() &&
|
||||
new_field->length == max_display_length());
|
||||
|
@ -8607,20 +8608,20 @@ void Field_bit_as_char::sql_type(String &res) const
|
|||
|
||||
|
||||
/*****************************************************************************
|
||||
Handling of field and create_field
|
||||
Handling of field and Create_field
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
Convert create_field::length from number of characters to number of bytes
|
||||
Convert Create_field::length from number of characters to number of bytes
|
||||
|
||||
SYNOPSIS
|
||||
create_field::create_length_to_internal_length()
|
||||
Create_field::create_length_to_internal_length()
|
||||
|
||||
DESCRIPTION
|
||||
Convert create_field::length from number of characters to number of bytes.
|
||||
Convert Create_field::length from number of characters to number of bytes.
|
||||
*/
|
||||
|
||||
void create_field::create_length_to_internal_length(void)
|
||||
void Create_field::create_length_to_internal_length(void)
|
||||
{
|
||||
switch (sql_type) {
|
||||
case MYSQL_TYPE_TINY_BLOB:
|
||||
|
@ -8667,7 +8668,7 @@ void create_field::create_length_to_internal_length(void)
|
|||
}
|
||||
|
||||
|
||||
void create_field::init_for_tmp_table(enum_field_types sql_type_arg,
|
||||
void Create_field::init_for_tmp_table(enum_field_types sql_type_arg,
|
||||
uint32 length_arg, uint32 decimals_arg,
|
||||
bool maybe_null, bool is_unsigned)
|
||||
{
|
||||
|
@ -8708,7 +8709,7 @@ void create_field::init_for_tmp_table(enum_field_types sql_type_arg,
|
|||
TRUE on error
|
||||
*/
|
||||
|
||||
bool create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
|
||||
bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
|
||||
char *fld_length, char *fld_decimals,
|
||||
uint fld_type_modifier, Item *fld_default_value,
|
||||
Item *fld_on_update_value, LEX_STRING *fld_comment,
|
||||
|
@ -8718,7 +8719,7 @@ bool create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
|
|||
uint sign_len, allowed_type_modifier= 0;
|
||||
ulong max_field_charlength= MAX_FIELD_CHARLENGTH;
|
||||
|
||||
DBUG_ENTER("create_field::init()");
|
||||
DBUG_ENTER("Create_field::init()");
|
||||
|
||||
field= 0;
|
||||
field_name= fld_name;
|
||||
|
@ -8902,15 +8903,18 @@ bool create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
|
|||
break;
|
||||
case MYSQL_TYPE_TIMESTAMP:
|
||||
if (!fld_length)
|
||||
length= 14; /* Full date YYYYMMDDHHMMSS */
|
||||
else if (length != 19)
|
||||
{
|
||||
/* Compressed date YYYYMMDDHHMMSS */
|
||||
length= MAX_DATETIME_COMPRESSED_WIDTH;
|
||||
}
|
||||
else if (length != MAX_DATETIME_WIDTH)
|
||||
{
|
||||
/*
|
||||
We support only even TIMESTAMP lengths less or equal than 14
|
||||
and 19 as length of 4.1 compatible representation.
|
||||
*/
|
||||
length= ((length+1)/2)*2; /* purecov: inspected */
|
||||
length= min(length,14); /* purecov: inspected */
|
||||
length= min(length, MAX_DATETIME_COMPRESSED_WIDTH); /* purecov: inspected */
|
||||
}
|
||||
flags|= ZEROFILL_FLAG | UNSIGNED_FLAG;
|
||||
if (fld_default_value)
|
||||
|
@ -8963,7 +8967,7 @@ bool create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
|
|||
length= 10;
|
||||
break;
|
||||
case MYSQL_TYPE_DATETIME:
|
||||
length= 19;
|
||||
length= MAX_DATETIME_WIDTH;
|
||||
break;
|
||||
case MYSQL_TYPE_SET:
|
||||
{
|
||||
|
@ -9291,7 +9295,7 @@ Field *make_field(TABLE_SHARE *share, uchar *ptr, uint32 field_length,
|
|||
|
||||
/* Create a field suitable for create of table */
|
||||
|
||||
create_field::create_field(Field *old_field,Field *orig_field)
|
||||
Create_field::Create_field(Field *old_field,Field *orig_field)
|
||||
{
|
||||
field= old_field;
|
||||
field_name=change=old_field->field_name;
|
||||
|
|
32
sql/field.h
32
sql/field.h
|
@ -28,7 +28,7 @@
|
|||
|
||||
class Send_field;
|
||||
class Protocol;
|
||||
class create_field;
|
||||
class Create_field;
|
||||
struct st_cache_field;
|
||||
int field_conv(Field *to,Field *from);
|
||||
|
||||
|
@ -413,7 +413,7 @@ public:
|
|||
/* maximum possible display length */
|
||||
virtual uint32 max_display_length()= 0;
|
||||
|
||||
virtual uint is_equal(create_field *new_field);
|
||||
virtual uint is_equal(Create_field *new_field);
|
||||
/* convert decimal to longlong with overflow check */
|
||||
longlong convert_decimal2longlong(const my_decimal *val, bool unsigned_flag,
|
||||
int *err);
|
||||
|
@ -474,14 +474,14 @@ public:
|
|||
Item_result result_type () const { return REAL_RESULT; }
|
||||
void prepend_zeros(String *value);
|
||||
void add_zerofill_and_unsigned(String &res) const;
|
||||
friend class create_field;
|
||||
friend class Create_field;
|
||||
void make_field(Send_field *);
|
||||
uint decimals() const { return (uint) dec; }
|
||||
uint size_of() const { return sizeof(*this); }
|
||||
bool eq_def(Field *field);
|
||||
int store_decimal(const my_decimal *);
|
||||
my_decimal *val_decimal(my_decimal *);
|
||||
uint is_equal(create_field *new_field);
|
||||
uint is_equal(Create_field *new_field);
|
||||
int check_int(CHARSET_INFO *cs, const char *str, int length,
|
||||
const char *int_end, int error);
|
||||
bool get_int(CHARSET_INFO *cs, const char *from, uint len,
|
||||
|
@ -512,11 +512,11 @@ public:
|
|||
{ field_derivation= derivation_arg; }
|
||||
bool binary() const { return field_charset == &my_charset_bin; }
|
||||
uint32 max_display_length() { return field_length; }
|
||||
friend class create_field;
|
||||
friend class Create_field;
|
||||
my_decimal *val_decimal(my_decimal *);
|
||||
virtual bool str_needs_quotes() { return TRUE; }
|
||||
bool compare_str_field_flags(create_field *new_field, uint32 flags);
|
||||
uint is_equal(create_field *new_field);
|
||||
bool compare_str_field_flags(Create_field *new_field, uint32 flags);
|
||||
uint is_equal(Create_field *new_field);
|
||||
};
|
||||
|
||||
|
||||
|
@ -625,7 +625,7 @@ public:
|
|||
uint32 max_display_length() { return field_length; }
|
||||
uint size_of() const { return sizeof(*this); }
|
||||
uint32 pack_length() const { return (uint32) bin_size; }
|
||||
uint is_equal(create_field *new_field);
|
||||
uint is_equal(Create_field *new_field);
|
||||
};
|
||||
|
||||
|
||||
|
@ -1257,7 +1257,7 @@ public:
|
|||
Field *new_key_field(MEM_ROOT *root, struct st_table *new_table,
|
||||
uchar *new_ptr, uchar *new_null_ptr,
|
||||
uint new_null_bit);
|
||||
uint is_equal(create_field *new_field);
|
||||
uint is_equal(Create_field *new_field);
|
||||
void hash(ulong *nr, ulong *nr2);
|
||||
};
|
||||
|
||||
|
@ -1391,7 +1391,7 @@ public:
|
|||
bool has_charset(void) const
|
||||
{ return charset() == &my_charset_bin ? FALSE : TRUE; }
|
||||
uint32 max_display_length();
|
||||
uint is_equal(create_field *new_field);
|
||||
uint is_equal(Create_field *new_field);
|
||||
};
|
||||
|
||||
|
||||
|
@ -1570,7 +1570,7 @@ public:
|
|||
bit_ptr == ((Field_bit *)field)->bit_ptr &&
|
||||
bit_ofs == ((Field_bit *)field)->bit_ofs);
|
||||
}
|
||||
uint is_equal(create_field *new_field);
|
||||
uint is_equal(Create_field *new_field);
|
||||
void move_field_offset(my_ptrdiff_t ptr_diff)
|
||||
{
|
||||
Field::move_field_offset(ptr_diff);
|
||||
|
@ -1608,7 +1608,7 @@ public:
|
|||
Create field class for CREATE TABLE
|
||||
*/
|
||||
|
||||
class create_field :public Sql_alloc
|
||||
class Create_field :public Sql_alloc
|
||||
{
|
||||
public:
|
||||
const char *field_name;
|
||||
|
@ -1639,11 +1639,11 @@ public:
|
|||
|
||||
uint8 row,col,sc_length,interval_id; // For rea_create_table
|
||||
uint offset,pack_flag;
|
||||
create_field() :after(0) {}
|
||||
create_field(Field *field, Field *orig_field);
|
||||
Create_field() :after(0) {}
|
||||
Create_field(Field *field, Field *orig_field);
|
||||
/* Used to make a clone of this object for ALTER/CREATE TABLE */
|
||||
create_field *clone(MEM_ROOT *mem_root) const
|
||||
{ return new (mem_root) create_field(*this); }
|
||||
Create_field *clone(MEM_ROOT *mem_root) const
|
||||
{ return new (mem_root) Create_field(*this); }
|
||||
void create_length_to_internal_length(void);
|
||||
|
||||
/* Init for a tmp table field. To be extended if need be. */
|
||||
|
|
|
@ -3376,8 +3376,8 @@ TYPELIB *ha_known_exts(void)
|
|||
const char **ext, *old_ext;
|
||||
|
||||
known_extensions_id= mysys_usage_id;
|
||||
found_exts.push_back((char*) triggers_file_ext);
|
||||
found_exts.push_back((char*) trigname_file_ext);
|
||||
found_exts.push_back((char*) TRG_EXT);
|
||||
found_exts.push_back((char*) TRN_EXT);
|
||||
|
||||
plugin_foreach(NULL, exts_handlerton,
|
||||
MYSQL_STORAGE_ENGINE_PLUGIN, &found_exts);
|
||||
|
|
|
@ -1768,7 +1768,7 @@ public:
|
|||
We have to have a different max_length than 'length' here to
|
||||
ensure that we get the right length if we do use the item
|
||||
to create a new table. In this case max_length must be the maximum
|
||||
number of chars for a string of this type because we in create_field::
|
||||
number of chars for a string of this type because we in Create_field::
|
||||
divide the max_length with mbmaxlen).
|
||||
*/
|
||||
max_length= str_value.numchars()*cs->mbmaxlen;
|
||||
|
|
|
@ -912,8 +912,8 @@ void Item_sum_distinct::fix_length_and_dec()
|
|||
|
||||
bool Item_sum_distinct::setup(THD *thd)
|
||||
{
|
||||
List<create_field> field_list;
|
||||
create_field field_def; /* field definition */
|
||||
List<Create_field> field_list;
|
||||
Create_field field_def; /* field definition */
|
||||
DBUG_ENTER("Item_sum_distinct::setup");
|
||||
DBUG_ASSERT(tree == 0);
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ static bool make_datetime(date_time_format_types format, MYSQL_TIME *ltime,
|
|||
{
|
||||
char *buff;
|
||||
CHARSET_INFO *cs= &my_charset_bin;
|
||||
uint length= 30;
|
||||
uint length= MAX_DATE_STRING_REP_LENGTH;
|
||||
|
||||
if (str->alloc(length))
|
||||
return 1;
|
||||
|
@ -1379,7 +1379,7 @@ String *Item_date::val_str(String *str)
|
|||
MYSQL_TIME ltime;
|
||||
if (get_date(<ime, TIME_FUZZY_DATE))
|
||||
return (String *) 0;
|
||||
if (str->alloc(11))
|
||||
if (str->alloc(MAX_DATE_STRING_REP_LENGTH))
|
||||
{
|
||||
null_value= 1;
|
||||
return (String *) 0;
|
||||
|
@ -1428,7 +1428,7 @@ void Item_func_curdate::fix_length_and_dec()
|
|||
String *Item_func_curdate::val_str(String *str)
|
||||
{
|
||||
DBUG_ASSERT(fixed == 1);
|
||||
if (str->alloc(11))
|
||||
if (str->alloc(MAX_DATE_STRING_REP_LENGTH))
|
||||
{
|
||||
null_value= 1;
|
||||
return (String *) 0;
|
||||
|
@ -1657,7 +1657,8 @@ String *Item_func_sec_to_time::val_str(String *str)
|
|||
MYSQL_TIME ltime;
|
||||
longlong arg_val= args[0]->val_int();
|
||||
|
||||
if ((null_value=args[0]->null_value) || str->alloc(19))
|
||||
if ((null_value=args[0]->null_value) ||
|
||||
str->alloc(MAX_DATE_STRING_REP_LENGTH))
|
||||
{
|
||||
null_value= 1;
|
||||
return (String*) 0;
|
||||
|
@ -1842,6 +1843,10 @@ String *Item_func_date_format::val_str(String *str)
|
|||
size=max_length;
|
||||
else
|
||||
size=format_length(format);
|
||||
|
||||
if (size < MAX_DATE_STRING_REP_LENGTH)
|
||||
size= MAX_DATE_STRING_REP_LENGTH;
|
||||
|
||||
if (format == str)
|
||||
str= &value; // Save result here
|
||||
if (str->alloc(size))
|
||||
|
@ -1885,13 +1890,14 @@ String *Item_func_from_unixtime::val_str(String *str)
|
|||
if (get_date(&time_tmp, 0))
|
||||
return 0;
|
||||
|
||||
if (str->alloc(20*MY_CHARSET_BIN_MB_MAXLEN))
|
||||
if (str->alloc(MAX_DATE_STRING_REP_LENGTH))
|
||||
{
|
||||
null_value= 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
make_datetime((DATE_TIME_FORMAT *) 0, &time_tmp, str);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
|
@ -1940,14 +1946,15 @@ String *Item_func_convert_tz::val_str(String *str)
|
|||
|
||||
if (get_date(&time_tmp, 0))
|
||||
return 0;
|
||||
|
||||
if (str->alloc(20*MY_CHARSET_BIN_MB_MAXLEN))
|
||||
|
||||
if (str->alloc(MAX_DATE_STRING_REP_LENGTH))
|
||||
{
|
||||
null_value= 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
make_datetime((DATE_TIME_FORMAT *) 0, &time_tmp, str);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
|
@ -2433,6 +2440,7 @@ String *Item_datetime_typecast::val_str(String *str)
|
|||
{
|
||||
DBUG_ASSERT(fixed == 1);
|
||||
MYSQL_TIME ltime;
|
||||
|
||||
if (!get_arg0_date(<ime, TIME_FUZZY_DATE) &&
|
||||
!make_datetime(ltime.second_part ? DATE_TIME_MICROSECOND : DATE_TIME,
|
||||
<ime, str))
|
||||
|
@ -2511,7 +2519,8 @@ String *Item_date_typecast::val_str(String *str)
|
|||
DBUG_ASSERT(fixed == 1);
|
||||
MYSQL_TIME ltime;
|
||||
|
||||
if (!get_arg0_date(<ime, TIME_FUZZY_DATE) && !str->alloc(11))
|
||||
if (!get_arg0_date(<ime, TIME_FUZZY_DATE) &&
|
||||
!str->alloc(MAX_DATE_STRING_REP_LENGTH))
|
||||
{
|
||||
make_date((DATE_TIME_FORMAT *) 0, <ime, str);
|
||||
return str;
|
||||
|
@ -2564,7 +2573,7 @@ String *Item_func_makedate::val_str(String *str)
|
|||
{
|
||||
null_value=0;
|
||||
get_date_from_daynr(days,&l_time.year,&l_time.month,&l_time.day);
|
||||
if (str->alloc(11))
|
||||
if (str->alloc(MAX_DATE_STRING_REP_LENGTH))
|
||||
goto err;
|
||||
make_date((DATE_TIME_FORMAT *) 0, &l_time, str);
|
||||
return str;
|
||||
|
@ -2700,6 +2709,7 @@ String *Item_func_add_time::val_str(String *str)
|
|||
days= (long)(seconds/86400L);
|
||||
|
||||
calc_time_from_sec(&l_time3, (long)(seconds%86400L), microseconds);
|
||||
|
||||
if (!is_time)
|
||||
{
|
||||
get_date_from_daynr(days,&l_time3.year,&l_time3.month,&l_time3.day);
|
||||
|
@ -2815,7 +2825,7 @@ String *Item_func_maketime::val_str(String *str)
|
|||
args[2]->null_value ||
|
||||
minute < 0 || minute > 59 ||
|
||||
second < 0 || second > 59 ||
|
||||
str->alloc(19))))
|
||||
str->alloc(MAX_DATE_STRING_REP_LENGTH))))
|
||||
return 0;
|
||||
|
||||
bzero((char *)<ime, sizeof(ltime));
|
||||
|
|
11
sql/log.cc
11
sql/log.cc
|
@ -307,6 +307,9 @@ bool Log_to_csv_event_handler::open_log_table(uint log_table_type)
|
|||
table->table->use_all_columns();
|
||||
table->table->locked_by_logger= TRUE;
|
||||
table->table->no_replicate= TRUE;
|
||||
|
||||
/* Honor next number columns if present */
|
||||
table->table->next_number_field= table->table->found_next_number_field;
|
||||
}
|
||||
/* restore thread settings */
|
||||
if (curr)
|
||||
|
@ -440,6 +443,7 @@ bool Log_to_csv_event_handler::
|
|||
CHARSET_INFO *client_cs)
|
||||
{
|
||||
TABLE *table= general_log.table;
|
||||
uint field_index;
|
||||
|
||||
/*
|
||||
"INSERT INTO general_log" can generate warning sometimes.
|
||||
|
@ -490,6 +494,12 @@ bool Log_to_csv_event_handler::
|
|||
table->field[4]->set_notnull();
|
||||
table->field[5]->set_notnull();
|
||||
|
||||
/* Set any extra columns to their default values */
|
||||
for (field_index= 6 ; field_index < table->s->fields ; field_index++)
|
||||
{
|
||||
table->field[field_index]->set_default();
|
||||
}
|
||||
|
||||
/* log table entries are not replicated at the moment */
|
||||
tmp_disable_binlog(current_thd);
|
||||
|
||||
|
@ -1331,6 +1341,7 @@ void Log_to_csv_event_handler::
|
|||
/* close the table */
|
||||
log_thd->store_globals();
|
||||
table->table->file->ha_rnd_end();
|
||||
table->table->file->ha_release_auto_increment();
|
||||
/* discard logger mark before unlock*/
|
||||
table->table->locked_by_logger= FALSE;
|
||||
close_thread_tables(log_thd, lock_in_use);
|
||||
|
|
|
@ -101,7 +101,7 @@ char* query_table_status(THD *thd,const char *db,const char *table_name);
|
|||
ER_WARN_DEPRECATED_SYNTAX, ER(ER_WARN_DEPRECATED_SYNTAX), \
|
||||
(Old), (Ver), (New)); \
|
||||
else \
|
||||
sql_print_warning("The syntax %s is deprecated and will be removed " \
|
||||
sql_print_warning("The syntax '%s' is deprecated and will be removed " \
|
||||
"in MySQL %s. Please use %s instead.", (Old), (Ver), (New)); \
|
||||
} while(0)
|
||||
|
||||
|
@ -618,6 +618,8 @@ bool check_string_char_length(LEX_STRING *str, const char *err_msg,
|
|||
uint max_char_length, CHARSET_INFO *cs,
|
||||
bool no_error);
|
||||
|
||||
bool parse_sql(THD *thd, class Lex_input_stream *lip);
|
||||
|
||||
enum enum_mysql_completiontype {
|
||||
ROLLBACK_RELEASE=-2, ROLLBACK=1, ROLLBACK_AND_CHAIN=7,
|
||||
COMMIT_RELEASE=-1, COMMIT=0, COMMIT_AND_CHAIN=6
|
||||
|
@ -908,7 +910,7 @@ bool mysql_assign_to_keycache(THD* thd, TABLE_LIST* table_list,
|
|||
bool mysql_preload_keys(THD* thd, TABLE_LIST* table_list);
|
||||
int reassign_keycache_tables(THD* thd, KEY_CACHE *src_cache,
|
||||
KEY_CACHE *dst_cache);
|
||||
TABLE *create_virtual_tmp_table(THD *thd, List<create_field> &field_list);
|
||||
TABLE *create_virtual_tmp_table(THD *thd, List<Create_field> &field_list);
|
||||
|
||||
bool mysql_xa_recover(THD *thd);
|
||||
|
||||
|
@ -952,8 +954,8 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
|
|||
bool table_cant_handle_bit_fields,
|
||||
bool make_copy_field,
|
||||
uint convert_blob_length);
|
||||
void sp_prepare_create_field(THD *thd, create_field *sql_field);
|
||||
int prepare_create_field(create_field *sql_field,
|
||||
void sp_prepare_create_field(THD *thd, Create_field *sql_field);
|
||||
int prepare_create_field(Create_field *sql_field,
|
||||
uint *blob_columns,
|
||||
int *timestamps, int *timestamps_with_niladic,
|
||||
longlong table_flags);
|
||||
|
@ -1178,7 +1180,7 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum enum_field_types t
|
|||
char *change, List<String> *interval_list,
|
||||
CHARSET_INFO *cs,
|
||||
uint uint_geom_type);
|
||||
create_field * new_create_field(THD *thd, char *field_name, enum_field_types type,
|
||||
Create_field * new_create_field(THD *thd, char *field_name, enum_field_types type,
|
||||
char *length, char *decimals,
|
||||
uint type_modifier,
|
||||
Item *default_value, Item *on_update_value,
|
||||
|
@ -1589,6 +1591,7 @@ bool check_db_dir_existence(const char *db_name);
|
|||
bool load_db_opt(THD *thd, const char *path, HA_CREATE_INFO *create);
|
||||
bool load_db_opt_by_name(THD *thd, const char *db_name,
|
||||
HA_CREATE_INFO *db_create_info);
|
||||
CHARSET_INFO *get_default_db_collation(THD *thd, const char *db_name);
|
||||
bool my_dbopt_init(void);
|
||||
void my_dbopt_cleanup(void);
|
||||
extern int creating_database; // How many database locks are made
|
||||
|
@ -1610,8 +1613,8 @@ extern const char *first_keyword, *my_localhost, *delayed_user, *binary_keyword;
|
|||
extern const char **errmesg; /* Error messages */
|
||||
extern const char *myisam_recover_options_str;
|
||||
extern const char *in_left_expr_name, *in_additional_cond, *in_having_cond;
|
||||
extern const char * const triggers_file_ext;
|
||||
extern const char * const trigname_file_ext;
|
||||
extern const char * const TRG_EXT;
|
||||
extern const char * const TRN_EXT;
|
||||
extern Eq_creator eq_creator;
|
||||
extern Ne_creator ne_creator;
|
||||
extern Gt_creator gt_creator;
|
||||
|
@ -1805,12 +1808,12 @@ void unireg_end(void) __attribute__((noreturn));
|
|||
bool mysql_create_frm(THD *thd, const char *file_name,
|
||||
const char *db, const char *table,
|
||||
HA_CREATE_INFO *create_info,
|
||||
List<create_field> &create_field,
|
||||
List<Create_field> &create_field,
|
||||
uint key_count,KEY *key_info,handler *db_type);
|
||||
int rea_create_table(THD *thd, const char *path,
|
||||
const char *db, const char *table_name,
|
||||
HA_CREATE_INFO *create_info,
|
||||
List<create_field> &create_field,
|
||||
List<Create_field> &create_field,
|
||||
uint key_count,KEY *key_info,
|
||||
handler *file);
|
||||
int format_number(uint inputflag,uint max_length,char * pos,uint length,
|
||||
|
@ -1961,7 +1964,6 @@ void free_list(I_List <i_string_pair> *list);
|
|||
void free_list(I_List <i_string> *list);
|
||||
|
||||
/* sql_yacc.cc */
|
||||
extern int MYSQLparse(void *thd);
|
||||
#ifndef DBUG_OFF
|
||||
extern void turn_parser_debug_on();
|
||||
#endif
|
||||
|
@ -2153,6 +2155,12 @@ bool schema_table_store_record(THD *thd, TABLE *table);
|
|||
int item_create_init();
|
||||
void item_create_cleanup();
|
||||
|
||||
inline void lex_string_set(LEX_STRING *lex_str, const char *c_str)
|
||||
{
|
||||
lex_str->str= (char *) c_str;
|
||||
lex_str->length= strlen(c_str);
|
||||
}
|
||||
|
||||
#endif /* MYSQL_SERVER */
|
||||
#endif /* MYSQL_CLIENT */
|
||||
|
||||
|
|
54
sql/sp.cc
54
sql/sp.cc
|
@ -384,32 +384,32 @@ 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;
|
||||
|
||||
thd->spcont= NULL;
|
||||
|
||||
{
|
||||
Lex_input_stream lip(thd, defstr.c_ptr(), defstr.length());
|
||||
thd->m_lip= &lip;
|
||||
lex_start(thd);
|
||||
ret= MYSQLparse(thd);
|
||||
|
||||
if (parse_sql(thd, &lip) || newlex.sphead == NULL)
|
||||
{
|
||||
sp_head *sp= newlex.sphead;
|
||||
|
||||
if (dbchanged && (ret= mysql_change_db(thd, &old_db, TRUE)))
|
||||
goto end;
|
||||
delete sp;
|
||||
ret= SP_PARSE_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (dbchanged && (ret= mysql_change_db(thd, &old_db, TRUE)))
|
||||
goto end;
|
||||
*sphp= newlex.sphead;
|
||||
(*sphp)->set_definer(&definer_user_name, &definer_host_name);
|
||||
(*sphp)->set_info(created, modified, &chistics, sql_mode);
|
||||
(*sphp)->optimize();
|
||||
}
|
||||
}
|
||||
|
||||
thd->spcont= 0;
|
||||
if (ret || thd->is_fatal_error || newlex.sphead == NULL)
|
||||
{
|
||||
sp_head *sp= newlex.sphead;
|
||||
|
||||
if (dbchanged && (ret= mysql_change_db(thd, &old_db, TRUE)))
|
||||
goto end;
|
||||
delete sp;
|
||||
ret= SP_PARSE_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (dbchanged && (ret= mysql_change_db(thd, &old_db, TRUE)))
|
||||
goto end;
|
||||
*sphp= newlex.sphead;
|
||||
(*sphp)->set_definer(&definer_user_name, &definer_host_name);
|
||||
(*sphp)->set_info(created, modified, &chistics, sql_mode);
|
||||
(*sphp)->optimize();
|
||||
}
|
||||
end:
|
||||
lex_end(thd->lex);
|
||||
thd->spcont= old_spcont;
|
||||
|
@ -588,10 +588,14 @@ sp_create_routine(THD *thd, int type, sp_head *sp)
|
|||
log_query.append(STRING_WITH_LEN("CREATE "));
|
||||
append_definer(thd, &log_query, &thd->lex->definer->user,
|
||||
&thd->lex->definer->host);
|
||||
log_query.append(thd->lex->stmt_definition_begin,
|
||||
(char *)sp->m_body_begin -
|
||||
thd->lex->stmt_definition_begin +
|
||||
sp->m_body.length);
|
||||
|
||||
LEX_STRING stmt_definition;
|
||||
stmt_definition.str= (char*) thd->lex->stmt_definition_begin;
|
||||
stmt_definition.length= thd->lex->stmt_definition_end
|
||||
- thd->lex->stmt_definition_begin;
|
||||
trim_whitespace(thd->charset(), & stmt_definition);
|
||||
|
||||
log_query.append(stmt_definition.str, stmt_definition.length);
|
||||
|
||||
/* Such a statement can always go directly to binlog, no trans cache */
|
||||
thd->binlog_query(THD::MYSQL_QUERY_TYPE,
|
||||
|
|
120
sql/sp_head.cc
120
sql/sp_head.cc
|
@ -564,24 +564,23 @@ sp_head::init_strings(THD *thd, LEX *lex)
|
|||
m_params.str= strmake_root(root, m_param_begin, m_params.length);
|
||||
}
|
||||
|
||||
/* If ptr has overrun end_of_query then end_of_query is the end */
|
||||
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.
|
||||
*/
|
||||
endp= skip_rear_comments(thd->charset(), m_body_begin, endp);
|
||||
endp= lip->get_cpp_ptr();
|
||||
lex->stmt_definition_end= endp;
|
||||
|
||||
m_body.length= endp - m_body_begin;
|
||||
m_body.str= strmake_root(root, m_body_begin, m_body.length);
|
||||
m_defstr.length= endp - lip->buf;
|
||||
m_defstr.str= strmake_root(root, lip->buf, m_defstr.length);
|
||||
trim_whitespace(thd->charset(), & m_body);
|
||||
|
||||
m_defstr.length= endp - lip->get_cpp_buf();
|
||||
m_defstr.str= strmake_root(root, lip->get_cpp_buf(), m_defstr.length);
|
||||
trim_whitespace(thd->charset(), & m_defstr);
|
||||
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
|
||||
static TYPELIB *
|
||||
create_typelib(MEM_ROOT *mem_root, create_field *field_def, List<String> *src)
|
||||
create_typelib(MEM_ROOT *mem_root, Create_field *field_def, List<String> *src)
|
||||
{
|
||||
TYPELIB *result= NULL;
|
||||
CHARSET_INFO *cs= field_def->charset;
|
||||
|
@ -1269,30 +1268,31 @@ set_routine_security_ctx(THD *thd, sp_head *sp, bool is_proc,
|
|||
#endif // ! NO_EMBEDDED_ACCESS_CHECKS
|
||||
|
||||
|
||||
/*
|
||||
Execute a trigger:
|
||||
- changes security context for triggers
|
||||
- switch to new memroot
|
||||
- call sp_head::execute
|
||||
- restore old memroot
|
||||
- restores security context
|
||||
/**
|
||||
Execute trigger stored program.
|
||||
|
||||
SYNOPSIS
|
||||
sp_head::execute_trigger()
|
||||
thd Thread handle
|
||||
db database name
|
||||
table table name
|
||||
grant_info GRANT_INFO structure to be filled with
|
||||
information about definer's privileges
|
||||
on subject table
|
||||
|
||||
RETURN
|
||||
FALSE on success
|
||||
TRUE on error
|
||||
Execute a trigger:
|
||||
- changes security context for triggers;
|
||||
- switch to new memroot;
|
||||
- call sp_head::execute;
|
||||
- restore old memroot;
|
||||
- restores security context.
|
||||
|
||||
@param thd Thread context.
|
||||
@param db_name Database name.
|
||||
@param table_name Table name.
|
||||
@param grant_info GRANT_INFO structure to be filled with information
|
||||
about definer's privileges on subject table.
|
||||
|
||||
@return Error status.
|
||||
@retval FALSE on success.
|
||||
@retval TRUE on error.
|
||||
*/
|
||||
|
||||
bool
|
||||
sp_head::execute_trigger(THD *thd, const char *db, const char *table,
|
||||
sp_head::execute_trigger(THD *thd,
|
||||
const LEX_STRING *db_name,
|
||||
const LEX_STRING *table_name,
|
||||
GRANT_INFO *grant_info)
|
||||
{
|
||||
sp_rcontext *octx = thd->spcont;
|
||||
|
@ -1305,6 +1305,46 @@ sp_head::execute_trigger(THD *thd, const char *db, const char *table,
|
|||
DBUG_ENTER("sp_head::execute_trigger");
|
||||
DBUG_PRINT("info", ("trigger %s", m_name.str));
|
||||
|
||||
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
||||
Security_context *save_ctx= NULL;
|
||||
|
||||
|
||||
if (m_chistics->suid != SP_IS_NOT_SUID &&
|
||||
m_security_ctx.change_security_context(thd,
|
||||
&m_definer_user,
|
||||
&m_definer_host,
|
||||
&m_db,
|
||||
&save_ctx))
|
||||
DBUG_RETURN(TRUE);
|
||||
|
||||
/*
|
||||
Fetch information about table-level privileges for subject table into
|
||||
GRANT_INFO instance. The access check itself will happen in
|
||||
Item_trigger_field, where this information will be used along with
|
||||
information about column-level privileges.
|
||||
*/
|
||||
|
||||
fill_effective_table_privileges(thd,
|
||||
grant_info,
|
||||
db_name->str,
|
||||
table_name->str);
|
||||
|
||||
/* Check that the definer has TRIGGER privilege on the subject table. */
|
||||
|
||||
if (!(grant_info->privilege & TRIGGER_ACL))
|
||||
{
|
||||
char priv_desc[128];
|
||||
get_privilege_desc(priv_desc, sizeof(priv_desc), TRIGGER_ACL);
|
||||
|
||||
my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0), priv_desc,
|
||||
thd->security_ctx->priv_user, thd->security_ctx->host_or_ip,
|
||||
table_name->str);
|
||||
|
||||
m_security_ctx.restore_security_context(thd, save_ctx);
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
#endif // NO_EMBEDDED_ACCESS_CHECKS
|
||||
|
||||
/*
|
||||
Prepare arena and memroot for objects which lifetime is whole
|
||||
duration of trigger call (sp_rcontext, it's tables and items,
|
||||
|
@ -1337,6 +1377,11 @@ sp_head::execute_trigger(THD *thd, const char *db, const char *table,
|
|||
|
||||
err_with_cleanup:
|
||||
thd->restore_active_arena(&call_arena, &backup_arena);
|
||||
|
||||
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
||||
m_security_ctx.restore_security_context(thd, save_ctx);
|
||||
#endif // NO_EMBEDDED_ACCESS_CHECKS
|
||||
|
||||
delete nctx;
|
||||
call_arena.free_items();
|
||||
free_root(&call_mem_root, MYF(0));
|
||||
|
@ -1827,8 +1872,6 @@ sp_head::reset_lex(THD *thd)
|
|||
sublex->trg_table_fields.empty();
|
||||
sublex->sp_lex_in_use= FALSE;
|
||||
|
||||
sublex->in_comment= oldlex->in_comment;
|
||||
|
||||
/* Reset type info. */
|
||||
|
||||
sublex->charset= NULL;
|
||||
|
@ -1908,7 +1951,7 @@ sp_head::backpatch(sp_label_t *lab)
|
|||
}
|
||||
|
||||
/*
|
||||
Prepare an instance of create_field for field creation (fill all necessary
|
||||
Prepare an instance of Create_field for field creation (fill all necessary
|
||||
attributes).
|
||||
|
||||
SYNOPSIS
|
||||
|
@ -1916,7 +1959,7 @@ sp_head::backpatch(sp_label_t *lab)
|
|||
thd [IN] Thread handle
|
||||
lex [IN] Yacc parsing context
|
||||
field_type [IN] Field type
|
||||
field_def [OUT] An instance of create_field to be filled
|
||||
field_def [OUT] An instance of Create_field to be filled
|
||||
|
||||
RETURN
|
||||
FALSE on success
|
||||
|
@ -1926,7 +1969,7 @@ sp_head::backpatch(sp_label_t *lab)
|
|||
bool
|
||||
sp_head::fill_field_definition(THD *thd, LEX *lex,
|
||||
enum enum_field_types field_type,
|
||||
create_field *field_def)
|
||||
Create_field *field_def)
|
||||
{
|
||||
HA_CREATE_INFO sp_db_info;
|
||||
LEX_STRING cmt = { 0, 0 };
|
||||
|
@ -2010,6 +2053,13 @@ sp_head::set_info(longlong created, longlong modified,
|
|||
}
|
||||
|
||||
|
||||
void
|
||||
sp_head::set_body_begin_ptr(Lex_input_stream *lip, const char *begin_ptr)
|
||||
{
|
||||
m_body_begin= begin_ptr;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
sp_head::set_definer(const char *definer, uint definerlen)
|
||||
{
|
||||
|
|
|
@ -126,7 +126,7 @@ public:
|
|||
int m_type;
|
||||
uint m_flags; // Boolean attributes of a stored routine
|
||||
|
||||
create_field m_return_field_def; /* This is used for FUNCTIONs only. */
|
||||
Create_field m_return_field_def; /* This is used for FUNCTIONs only. */
|
||||
|
||||
const char *m_tmp_query; // Temporary pointer to sub query string
|
||||
st_sp_chistics *m_chistics;
|
||||
|
@ -178,8 +178,11 @@ public:
|
|||
// Pointers set during parsing
|
||||
const char *m_param_begin;
|
||||
const char *m_param_end;
|
||||
|
||||
private:
|
||||
const char *m_body_begin;
|
||||
|
||||
public:
|
||||
/*
|
||||
Security context for stored routine which should be run under
|
||||
definer privileges.
|
||||
|
@ -216,8 +219,10 @@ public:
|
|||
destroy();
|
||||
|
||||
bool
|
||||
execute_trigger(THD *thd, const char *db, const char *table,
|
||||
GRANT_INFO *grant_onfo);
|
||||
execute_trigger(THD *thd,
|
||||
const LEX_STRING *db_name,
|
||||
const LEX_STRING *table_name,
|
||||
GRANT_INFO *grant_info);
|
||||
|
||||
bool
|
||||
execute_function(THD *thd, Item **args, uint argcount, Field *return_fld);
|
||||
|
@ -290,11 +295,13 @@ public:
|
|||
|
||||
bool fill_field_definition(THD *thd, LEX *lex,
|
||||
enum enum_field_types field_type,
|
||||
create_field *field_def);
|
||||
Create_field *field_def);
|
||||
|
||||
void set_info(longlong created, longlong modified,
|
||||
st_sp_chistics *chistics, ulong sql_mode);
|
||||
|
||||
void set_body_begin_ptr(Lex_input_stream *lip, const char *begin_ptr);
|
||||
|
||||
void set_definer(const char *definer, uint definerlen);
|
||||
void set_definer(const LEX_STRING *user_name, const LEX_STRING *host_name);
|
||||
|
||||
|
|
|
@ -422,7 +422,7 @@ sp_pcontext::find_cursor(LEX_STRING *name, uint *poff, my_bool scoped)
|
|||
|
||||
|
||||
void
|
||||
sp_pcontext::retrieve_field_definitions(List<create_field> *field_def_lst)
|
||||
sp_pcontext::retrieve_field_definitions(List<Create_field> *field_def_lst)
|
||||
{
|
||||
/* Put local/context fields in the result list. */
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ typedef struct sp_variable
|
|||
uint offset;
|
||||
|
||||
Item *dflt;
|
||||
create_field field_def;
|
||||
Create_field field_def;
|
||||
} sp_variable_t;
|
||||
|
||||
|
||||
|
@ -234,7 +234,7 @@ public:
|
|||
children.
|
||||
*/
|
||||
void
|
||||
retrieve_field_definitions(List<create_field> *field_def_lst);
|
||||
retrieve_field_definitions(List<Create_field> *field_def_lst);
|
||||
|
||||
// Find by name
|
||||
sp_variable_t *
|
||||
|
|
|
@ -102,7 +102,7 @@ bool sp_rcontext::init(THD *thd)
|
|||
bool
|
||||
sp_rcontext::init_var_table(THD *thd)
|
||||
{
|
||||
List<create_field> field_def_lst;
|
||||
List<Create_field> field_def_lst;
|
||||
|
||||
if (!m_root_parsing_ctx->max_var_index())
|
||||
return FALSE;
|
||||
|
|
|
@ -1459,40 +1459,62 @@ void Query_cache::invalidate(THD *thd, const char *key, uint32 key_length,
|
|||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
/*
|
||||
Remove all cached queries that uses the given database
|
||||
/**
|
||||
@brief Remove all cached queries that uses the given database
|
||||
*/
|
||||
|
||||
void Query_cache::invalidate(char *db)
|
||||
{
|
||||
bool restart= FALSE;
|
||||
DBUG_ENTER("Query_cache::invalidate (db)");
|
||||
STRUCT_LOCK(&structure_guard_mutex);
|
||||
if (query_cache_size > 0 && !flush_in_progress)
|
||||
{
|
||||
DUMP(this);
|
||||
restart_search:
|
||||
if (tables_blocks)
|
||||
{
|
||||
Query_cache_block *curr= tables_blocks;
|
||||
Query_cache_block *next;
|
||||
do
|
||||
{
|
||||
next= curr->next;
|
||||
if (strcmp(db, (char*)(curr->table()->db())) == 0)
|
||||
invalidate_table(curr);
|
||||
Query_cache_block *table_block = tables_blocks;
|
||||
do {
|
||||
restart= FALSE;
|
||||
do
|
||||
{
|
||||
Query_cache_block *next= table_block->next;
|
||||
Query_cache_table *table = table_block->table();
|
||||
if (strcmp(table->db(),db) == 0)
|
||||
invalidate_table(table_block);
|
||||
|
||||
table_block= next;
|
||||
|
||||
/*
|
||||
If our root node to used tables became null then the last element
|
||||
in the table list was removed when a query was invalidated;
|
||||
Terminate the search.
|
||||
*/
|
||||
if (tables_blocks == 0)
|
||||
{
|
||||
table_block= tables_blocks;
|
||||
}
|
||||
/*
|
||||
If the iterated list has changed underlying structure;
|
||||
we need to restart the search.
|
||||
*/
|
||||
else if (table_block->type == Query_cache_block::FREE)
|
||||
{
|
||||
restart= TRUE;
|
||||
table_block= tables_blocks;
|
||||
}
|
||||
/*
|
||||
The used tables are linked in a circular list;
|
||||
loop until we return to the begining.
|
||||
*/
|
||||
} while (table_block != tables_blocks);
|
||||
/*
|
||||
invalidate_table can freed block on which point 'next' (if
|
||||
table of this block used only in queries which was deleted
|
||||
by invalidate_table). As far as we do not allocate new blocks
|
||||
and mark all headers of freed blocks as 'FREE' (even if they are
|
||||
merged with other blocks) we can just test type of block
|
||||
to be sure that block is not deleted
|
||||
Invalidating a table will also mean that all cached queries using
|
||||
this table also will be invalidated. This will in turn change the
|
||||
list of tables associated with these queries and the linked list of
|
||||
used table will be changed. Because of this we might need to restart
|
||||
the search when a table has been invalidated.
|
||||
*/
|
||||
if (next->type == Query_cache_block::FREE)
|
||||
goto restart_search;
|
||||
curr= next;
|
||||
} while (curr != tables_blocks);
|
||||
}
|
||||
} while (restart);
|
||||
} // end if( tables_blocks )
|
||||
}
|
||||
STRUCT_UNLOCK(&structure_guard_mutex);
|
||||
|
||||
|
@ -2395,6 +2417,7 @@ Query_cache::register_tables_from_list(TABLE_LIST *tables_used,
|
|||
(ulong) tables_used->table,
|
||||
(ulong) tables_used->table->s->table_cache_key.length,
|
||||
(ulong) tables_used->table->s->table_cache_key.str));
|
||||
|
||||
if (!insert_table(tables_used->table->s->table_cache_key.length,
|
||||
tables_used->table->s->table_cache_key.str,
|
||||
block_table,
|
||||
|
@ -2461,9 +2484,8 @@ my_bool Query_cache::register_all_tables(Query_cache_block *block,
|
|||
|
||||
n= register_tables_from_list(tables_used, 0, block_table);
|
||||
|
||||
if (n)
|
||||
if (n==0)
|
||||
{
|
||||
DBUG_PRINT("qcache", ("failed at table %d", (int) n));
|
||||
/* Unlink the tables we allocated above */
|
||||
for (Query_cache_block_table *tmp = block->table(0) ;
|
||||
tmp != block_table;
|
||||
|
@ -2953,8 +2975,11 @@ Query_cache::double_linked_list_exclude(Query_cache_block *point,
|
|||
{
|
||||
point->next->prev = point->prev;
|
||||
point->prev->next = point->next;
|
||||
/*
|
||||
If the root is removed; select a new root
|
||||
*/
|
||||
if (point == *list_pointer)
|
||||
*list_pointer = point->next;
|
||||
*list_pointer= point->next;
|
||||
}
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
@ -3752,7 +3777,7 @@ void Query_cache::tables_dump()
|
|||
Query_cache_table *table = table_block->table();
|
||||
DBUG_PRINT("qcache", ("'%s' '%s'", table->db(), table->table()));
|
||||
table_block = table_block->next;
|
||||
} while ( table_block != tables_blocks);
|
||||
} while (table_block != tables_blocks);
|
||||
}
|
||||
else
|
||||
DBUG_PRINT("qcache", ("no tables in list"));
|
||||
|
|
|
@ -59,8 +59,8 @@ const char * const THD::DEFAULT_WHERE= "field list";
|
|||
/* Used templates */
|
||||
template class List<Key>;
|
||||
template class List_iterator<Key>;
|
||||
template class List<key_part_spec>;
|
||||
template class List_iterator<key_part_spec>;
|
||||
template class List<Key_part_spec>;
|
||||
template class List_iterator<Key_part_spec>;
|
||||
template class List<Alter_drop>;
|
||||
template class List_iterator<Alter_drop>;
|
||||
template class List<Alter_column>;
|
||||
|
@ -86,7 +86,7 @@ extern "C" void free_user_var(user_var_entry *entry)
|
|||
my_free((char*) entry,MYF(0));
|
||||
}
|
||||
|
||||
bool key_part_spec::operator==(const key_part_spec& other) const
|
||||
bool Key_part_spec::operator==(const Key_part_spec& other) const
|
||||
{
|
||||
return length == other.length && !strcmp(field_name, other.field_name);
|
||||
}
|
||||
|
@ -115,7 +115,7 @@ Key::Key(const Key &rhs, MEM_ROOT *mem_root)
|
|||
in THD.
|
||||
*/
|
||||
|
||||
foreign_key::foreign_key(const foreign_key &rhs, MEM_ROOT *mem_root)
|
||||
Foreign_key::Foreign_key(const Foreign_key &rhs, MEM_ROOT *mem_root)
|
||||
:Key(rhs),
|
||||
ref_table(rhs.ref_table),
|
||||
ref_columns(rhs.ref_columns),
|
||||
|
@ -160,9 +160,9 @@ bool foreign_key_prefix(Key *a, Key *b)
|
|||
if (a->columns.elements > b->columns.elements)
|
||||
return TRUE; // Can't be prefix
|
||||
|
||||
List_iterator<key_part_spec> col_it1(a->columns);
|
||||
List_iterator<key_part_spec> col_it2(b->columns);
|
||||
const key_part_spec *col1, *col2;
|
||||
List_iterator<Key_part_spec> col_it1(a->columns);
|
||||
List_iterator<Key_part_spec> col_it2(b->columns);
|
||||
const Key_part_spec *col1, *col2;
|
||||
|
||||
#ifdef ENABLE_WHEN_INNODB_CAN_HANDLE_SWAPED_FOREIGN_KEY_COLUMNS
|
||||
while ((col1= col_it1++))
|
||||
|
@ -342,7 +342,8 @@ THD::THD()
|
|||
in_lock_tables(0),
|
||||
bootstrap(0),
|
||||
derived_tables_processing(FALSE),
|
||||
spcont(NULL)
|
||||
spcont(NULL),
|
||||
m_lip(NULL)
|
||||
{
|
||||
ulong tmp;
|
||||
|
||||
|
|
|
@ -85,14 +85,14 @@ typedef struct st_copy_info {
|
|||
} COPY_INFO;
|
||||
|
||||
|
||||
class key_part_spec :public Sql_alloc {
|
||||
class Key_part_spec :public Sql_alloc {
|
||||
public:
|
||||
const char *field_name;
|
||||
uint length;
|
||||
key_part_spec(const char *name,uint len=0) :field_name(name), length(len) {}
|
||||
bool operator==(const key_part_spec& other) const;
|
||||
Key_part_spec(const char *name,uint len=0) :field_name(name), length(len) {}
|
||||
bool operator==(const Key_part_spec& other) const;
|
||||
/**
|
||||
Construct a copy of this key_part_spec. field_name is copied
|
||||
Construct a copy of this Key_part_spec. field_name is copied
|
||||
by-pointer as it is known to never change. At the same time
|
||||
'length' may be reset in mysql_prepare_create_table, and this
|
||||
is why we supply it with a copy.
|
||||
|
@ -100,8 +100,8 @@ public:
|
|||
@return If out of memory, 0 is returned and an error is set in
|
||||
THD.
|
||||
*/
|
||||
key_part_spec *clone(MEM_ROOT *mem_root) const
|
||||
{ return new (mem_root) key_part_spec(*this); }
|
||||
Key_part_spec *clone(MEM_ROOT *mem_root) const
|
||||
{ return new (mem_root) Key_part_spec(*this); }
|
||||
};
|
||||
|
||||
|
||||
|
@ -114,7 +114,7 @@ public:
|
|||
:name(par_name), type(par_type) {}
|
||||
/**
|
||||
Used to make a clone of this object for ALTER/CREATE TABLE
|
||||
@sa comment for key_part_spec::clone
|
||||
@sa comment for Key_part_spec::clone
|
||||
*/
|
||||
Alter_drop *clone(MEM_ROOT *mem_root) const
|
||||
{ return new (mem_root) Alter_drop(*this); }
|
||||
|
@ -129,7 +129,7 @@ public:
|
|||
:name(par_name), def(literal) {}
|
||||
/**
|
||||
Used to make a clone of this object for ALTER/CREATE TABLE
|
||||
@sa comment for key_part_spec::clone
|
||||
@sa comment for Key_part_spec::clone
|
||||
*/
|
||||
Alter_column *clone(MEM_ROOT *mem_root) const
|
||||
{ return new (mem_root) Alter_column(*this); }
|
||||
|
@ -141,13 +141,13 @@ public:
|
|||
enum Keytype { PRIMARY, UNIQUE, MULTIPLE, FULLTEXT, SPATIAL, FOREIGN_KEY};
|
||||
enum Keytype type;
|
||||
KEY_CREATE_INFO key_create_info;
|
||||
List<key_part_spec> columns;
|
||||
List<Key_part_spec> columns;
|
||||
const char *name;
|
||||
bool generated;
|
||||
|
||||
Key(enum Keytype type_par, const char *name_arg,
|
||||
KEY_CREATE_INFO *key_info_arg,
|
||||
bool generated_arg, List<key_part_spec> &cols)
|
||||
bool generated_arg, List<Key_part_spec> &cols)
|
||||
:type(type_par), key_create_info(*key_info_arg), columns(cols),
|
||||
name(name_arg), generated(generated_arg)
|
||||
{}
|
||||
|
@ -157,7 +157,7 @@ public:
|
|||
friend bool foreign_key_prefix(Key *a, Key *b);
|
||||
/**
|
||||
Used to make a clone of this object for ALTER/CREATE TABLE
|
||||
@sa comment for key_part_spec::clone
|
||||
@sa comment for Key_part_spec::clone
|
||||
*/
|
||||
virtual Key *clone(MEM_ROOT *mem_root) const
|
||||
{ return new (mem_root) Key(*this, mem_root); }
|
||||
|
@ -165,7 +165,7 @@ public:
|
|||
|
||||
class Table_ident;
|
||||
|
||||
class foreign_key: public Key {
|
||||
class Foreign_key: public Key {
|
||||
public:
|
||||
enum fk_match_opt { FK_MATCH_UNDEF, FK_MATCH_FULL,
|
||||
FK_MATCH_PARTIAL, FK_MATCH_SIMPLE};
|
||||
|
@ -173,23 +173,23 @@ public:
|
|||
FK_OPTION_SET_NULL, FK_OPTION_NO_ACTION, FK_OPTION_DEFAULT};
|
||||
|
||||
Table_ident *ref_table;
|
||||
List<key_part_spec> ref_columns;
|
||||
List<Key_part_spec> ref_columns;
|
||||
uint delete_opt, update_opt, match_opt;
|
||||
foreign_key(const char *name_arg, List<key_part_spec> &cols,
|
||||
Table_ident *table, List<key_part_spec> &ref_cols,
|
||||
Foreign_key(const char *name_arg, List<Key_part_spec> &cols,
|
||||
Table_ident *table, List<Key_part_spec> &ref_cols,
|
||||
uint delete_opt_arg, uint update_opt_arg, uint match_opt_arg)
|
||||
:Key(FOREIGN_KEY, name_arg, &default_key_create_info, 0, cols),
|
||||
ref_table(table), ref_columns(cols),
|
||||
delete_opt(delete_opt_arg), update_opt(update_opt_arg),
|
||||
match_opt(match_opt_arg)
|
||||
{}
|
||||
foreign_key(const foreign_key &rhs, MEM_ROOT *mem_root);
|
||||
Foreign_key(const Foreign_key &rhs, MEM_ROOT *mem_root);
|
||||
/**
|
||||
Used to make a clone of this object for ALTER/CREATE TABLE
|
||||
@sa comment for key_part_spec::clone
|
||||
@sa comment for Key_part_spec::clone
|
||||
*/
|
||||
virtual Key *clone(MEM_ROOT *mem_root) const
|
||||
{ return new (mem_root) foreign_key(*this, mem_root); }
|
||||
{ return new (mem_root) Foreign_key(*this, mem_root); }
|
||||
};
|
||||
|
||||
typedef struct st_mysql_lock
|
||||
|
|
|
@ -538,6 +538,37 @@ bool load_db_opt_by_name(THD *thd, const char *db_name,
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
Return default database collation.
|
||||
|
||||
@param thd Thread context.
|
||||
@param db_name Database name.
|
||||
|
||||
@return CHARSET_INFO object. The operation always return valid character
|
||||
set, even if the database does not exist.
|
||||
*/
|
||||
|
||||
CHARSET_INFO *get_default_db_collation(THD *thd, const char *db_name)
|
||||
{
|
||||
HA_CREATE_INFO db_info;
|
||||
|
||||
if (thd->db != NULL && strcmp(db_name, thd->db) == 0)
|
||||
return thd->db_charset;
|
||||
|
||||
load_db_opt_by_name(thd, db_name, &db_info);
|
||||
|
||||
/*
|
||||
NOTE: even if load_db_opt_by_name() fails,
|
||||
db_info.default_table_charset contains valid character set
|
||||
(collation_server). We should not fail if load_db_opt_by_name() fails,
|
||||
because it is valid case. If a database has been created just by
|
||||
"mkdir", it does not contain db.opt file, but it is valid database.
|
||||
*/
|
||||
|
||||
return db_info.default_table_charset;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Create a database
|
||||
|
||||
|
@ -751,10 +782,8 @@ bool mysql_alter_db(THD *thd, const char *db, HA_CREATE_INFO *create_info)
|
|||
if ((error=write_db_opt(thd, path, create_info)))
|
||||
goto exit;
|
||||
|
||||
/*
|
||||
Change options if current database is being altered
|
||||
TODO: Delete this code
|
||||
*/
|
||||
/* Change options if current database is being altered. */
|
||||
|
||||
if (thd->db && !strcmp(thd->db,db))
|
||||
{
|
||||
thd->db_charset= create_info->default_table_charset ?
|
||||
|
@ -1358,6 +1387,7 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch)
|
|||
|
||||
Security_context *sctx= thd->security_ctx;
|
||||
ulong db_access= sctx->db_access;
|
||||
CHARSET_INFO *db_default_cl;
|
||||
|
||||
DBUG_ENTER("mysql_change_db");
|
||||
DBUG_PRINT("enter",("name: '%s'", new_db_name->str));
|
||||
|
@ -1487,16 +1517,9 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch)
|
|||
attributes and will be freed in THD::~THD().
|
||||
*/
|
||||
|
||||
{
|
||||
HA_CREATE_INFO db_options;
|
||||
db_default_cl= get_default_db_collation(thd, new_db_file_name.str);
|
||||
|
||||
load_db_opt_by_name(thd, new_db_name->str, &db_options);
|
||||
|
||||
mysql_change_db_impl(thd, &new_db_file_name, db_access,
|
||||
db_options.default_table_charset ?
|
||||
db_options.default_table_charset :
|
||||
thd->variables.collation_server);
|
||||
}
|
||||
mysql_change_db_impl(thd, &new_db_file_name, db_access, db_default_cl);
|
||||
|
||||
DBUG_RETURN(FALSE);
|
||||
}
|
||||
|
|
|
@ -3214,7 +3214,7 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
|
|||
MYSQL_LOCK **lock,
|
||||
TABLEOP_HOOKS *hooks)
|
||||
{
|
||||
TABLE tmp_table; // Used during 'create_field()'
|
||||
TABLE tmp_table; // Used during 'Create_field()'
|
||||
TABLE_SHARE share;
|
||||
TABLE *table= 0;
|
||||
uint select_field_count= items->elements;
|
||||
|
@ -3258,7 +3258,7 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
|
|||
|
||||
while ((item=it++))
|
||||
{
|
||||
create_field *cr_field;
|
||||
Create_field *cr_field;
|
||||
Field *field, *def_field;
|
||||
if (item->type() == Item::FUNC_ITEM)
|
||||
field= item->tmp_table_field(&tmp_table);
|
||||
|
@ -3267,7 +3267,7 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
|
|||
(Item ***) 0, &tmp_field, &def_field, 0, 0, 0, 0,
|
||||
0);
|
||||
if (!field ||
|
||||
!(cr_field=new create_field(field,(item->type() == Item::FIELD_ITEM ?
|
||||
!(cr_field=new Create_field(field,(item->type() == Item::FIELD_ITEM ?
|
||||
((Item_field *)item)->field :
|
||||
(Field*) 0))))
|
||||
DBUG_RETURN(0);
|
||||
|
|
562
sql/sql_lex.cc
562
sql/sql_lex.cc
|
@ -31,16 +31,6 @@
|
|||
|
||||
sys_var *trg_new_row_fake_var= (sys_var*) 0x01;
|
||||
|
||||
/* Macros to look like lex */
|
||||
|
||||
#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
|
||||
|
||||
|
@ -127,17 +117,24 @@ Lex_input_stream::Lex_input_stream(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),
|
||||
m_ptr(buffer),
|
||||
m_tok_start(NULL),
|
||||
m_tok_end(NULL),
|
||||
m_end_of_query(buffer + length),
|
||||
m_tok_start_prev(NULL),
|
||||
m_buf(buffer),
|
||||
m_echo(true),
|
||||
m_cpp_tok_start(NULL),
|
||||
m_cpp_tok_start_prev(NULL),
|
||||
m_cpp_tok_end(NULL),
|
||||
next_state(MY_LEX_START),
|
||||
found_semicolon(NULL),
|
||||
ignore_space(test(thd->variables.sql_mode & MODE_IGNORE_SPACE)),
|
||||
stmt_prepare_mode(FALSE)
|
||||
stmt_prepare_mode(FALSE),
|
||||
in_comment(NO_COMMENT)
|
||||
{
|
||||
m_cpp_buf= (char*) thd->alloc(length + 1);
|
||||
m_cpp_ptr= m_cpp_buf;
|
||||
}
|
||||
|
||||
Lex_input_stream::~Lex_input_stream()
|
||||
|
@ -192,7 +189,6 @@ void lex_start(THD *thd)
|
|||
lex->parsing_options.reset();
|
||||
lex->empty_field_list_on_rset= 0;
|
||||
lex->select_lex.select_number= 1;
|
||||
lex->in_comment=0;
|
||||
lex->length=0;
|
||||
lex->part_info= 0;
|
||||
lex->select_lex.in_sum_expr=0;
|
||||
|
@ -261,7 +257,7 @@ void lex_end(LEX *lex)
|
|||
|
||||
static int find_keyword(Lex_input_stream *lip, uint len, bool function)
|
||||
{
|
||||
const char *tok= lip->tok_start;
|
||||
const char *tok= lip->get_tok_start();
|
||||
|
||||
SYMBOL *symbol= get_hash_symbol(tok, len, function);
|
||||
if (symbol)
|
||||
|
@ -312,9 +308,9 @@ bool is_lex_native_function(const LEX_STRING *name)
|
|||
static LEX_STRING get_token(Lex_input_stream *lip, uint skip, uint length)
|
||||
{
|
||||
LEX_STRING tmp;
|
||||
yyUnget(); // ptr points now after last token char
|
||||
lip->yyUnget(); // ptr points now after last token char
|
||||
tmp.length=lip->yytoklen=length;
|
||||
tmp.str= lip->m_thd->strmake(lip->tok_start + skip, tmp.length);
|
||||
tmp.str= lip->m_thd->strmake(lip->get_tok_start() + skip, tmp.length);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
|
@ -332,10 +328,10 @@ static LEX_STRING get_quoted_token(Lex_input_stream *lip,
|
|||
LEX_STRING tmp;
|
||||
const char *from, *end;
|
||||
char *to;
|
||||
yyUnget(); // ptr points now after last token char
|
||||
lip->yyUnget(); // ptr points now after last token char
|
||||
tmp.length= lip->yytoklen=length;
|
||||
tmp.str=(char*) lip->m_thd->alloc(tmp.length+1);
|
||||
from= lip->tok_start + skip;
|
||||
from= lip->get_tok_start() + skip;
|
||||
to= tmp.str;
|
||||
end= to+length;
|
||||
for ( ; to != end; )
|
||||
|
@ -353,23 +349,25 @@ static LEX_STRING get_quoted_token(Lex_input_stream *lip,
|
|||
Fix sometimes to do only one scan of the string
|
||||
*/
|
||||
|
||||
static char *get_text(Lex_input_stream *lip)
|
||||
static char *get_text(Lex_input_stream *lip, int pre_skip, int post_skip)
|
||||
{
|
||||
reg1 uchar c,sep;
|
||||
uint found_escape=0;
|
||||
CHARSET_INFO *cs= lip->m_thd->charset();
|
||||
|
||||
sep= yyGetLast(); // String should end with this
|
||||
while (lip->ptr != lip->end_of_query)
|
||||
sep= lip->yyGetLast(); // String should end with this
|
||||
while (! lip->eof())
|
||||
{
|
||||
c = yyGet();
|
||||
c= lip->yyGet();
|
||||
#ifdef USE_MB
|
||||
{
|
||||
int l;
|
||||
if (use_mb(cs) &&
|
||||
(l = my_ismbchar(cs, lip->ptr-1, lip->end_of_query))) {
|
||||
lip->ptr += l-1;
|
||||
continue;
|
||||
(l = my_ismbchar(cs,
|
||||
lip->get_ptr() -1,
|
||||
lip->get_end_of_query()))) {
|
||||
lip->skip_binary(l-1);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -377,26 +375,31 @@ static char *get_text(Lex_input_stream *lip)
|
|||
!(lip->m_thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES))
|
||||
{ // Escaped character
|
||||
found_escape=1;
|
||||
if (lip->ptr == lip->end_of_query)
|
||||
if (lip->eof())
|
||||
return 0;
|
||||
yySkip();
|
||||
lip->yySkip();
|
||||
}
|
||||
else if (c == sep)
|
||||
{
|
||||
if (c == yyGet()) // Check if two separators in a row
|
||||
if (c == lip->yyGet()) // Check if two separators in a row
|
||||
{
|
||||
found_escape=1; // dupplicate. Remember for delete
|
||||
found_escape=1; // duplicate. Remember for delete
|
||||
continue;
|
||||
}
|
||||
else
|
||||
yyUnget();
|
||||
lip->yyUnget();
|
||||
|
||||
/* Found end. Unescape and return string */
|
||||
const char *str, *end;
|
||||
char *start;
|
||||
|
||||
str=lip->tok_start+1;
|
||||
end=lip->ptr-1;
|
||||
str= lip->get_tok_start();
|
||||
end= lip->get_ptr();
|
||||
/* Extract the text from the token */
|
||||
str += pre_skip;
|
||||
end -= post_skip;
|
||||
DBUG_ASSERT(end >= str);
|
||||
|
||||
if (!(start= (char*) lip->m_thd->alloc((uint) (end-str)+1)))
|
||||
return (char*) ""; // Sql_alloc has set error flag
|
||||
if (!found_escape)
|
||||
|
@ -581,9 +584,7 @@ int MYSQLlex(void *arg, void *yythd)
|
|||
|
||||
lip->yylval=yylval; // The global state
|
||||
|
||||
lip->tok_start_prev= lip->tok_start;
|
||||
|
||||
lip->tok_start=lip->tok_end=lip->ptr;
|
||||
lip->start_token();
|
||||
state=lip->next_state;
|
||||
lip->next_state=MY_LEX_OPERATOR_OR_IDENT;
|
||||
LINT_INIT(c);
|
||||
|
@ -592,17 +593,22 @@ int MYSQLlex(void *arg, void *yythd)
|
|||
switch (state) {
|
||||
case MY_LEX_OPERATOR_OR_IDENT: // Next is operator or keyword
|
||||
case MY_LEX_START: // Start of token
|
||||
// Skip startspace
|
||||
for (c=yyGet() ; (state_map[c] == MY_LEX_SKIP) ; c= yyGet())
|
||||
// Skip starting whitespace
|
||||
while(state_map[c= lip->yyPeek()] == MY_LEX_SKIP)
|
||||
{
|
||||
if (c == '\n')
|
||||
lip->yylineno++;
|
||||
|
||||
lip->yySkip();
|
||||
}
|
||||
lip->tok_start=lip->ptr-1; // Start of real token
|
||||
|
||||
/* Start of real token */
|
||||
lip->restart_token();
|
||||
c= lip->yyGet();
|
||||
state= (enum my_lex_states) state_map[c];
|
||||
break;
|
||||
case MY_LEX_ESCAPE:
|
||||
if (yyGet() == 'N')
|
||||
if (lip->yyGet() == 'N')
|
||||
{ // Allow \N as shortcut for NULL
|
||||
yylval->lex_str.str=(char*) "\\N";
|
||||
yylval->lex_str.length=2;
|
||||
|
@ -610,40 +616,53 @@ int MYSQLlex(void *arg, void *yythd)
|
|||
}
|
||||
case MY_LEX_CHAR: // Unknown or single char token
|
||||
case MY_LEX_SKIP: // This should not happen
|
||||
if (c == '-' && yyPeek() == '-' &&
|
||||
(my_isspace(cs,yyPeek2()) ||
|
||||
my_iscntrl(cs,yyPeek2())))
|
||||
if (c == '-' && lip->yyPeek() == '-' &&
|
||||
(my_isspace(cs,lip->yyPeekn(1)) ||
|
||||
my_iscntrl(cs,lip->yyPeekn(1))))
|
||||
{
|
||||
state=MY_LEX_COMMENT;
|
||||
break;
|
||||
}
|
||||
yylval->lex_str.str=(char*) (lip->ptr=lip->tok_start);// Set to first chr
|
||||
yylval->lex_str.length=1;
|
||||
c=yyGet();
|
||||
|
||||
if (c != ')')
|
||||
lip->next_state= MY_LEX_START; // Allow signed numbers
|
||||
|
||||
if (c == ',')
|
||||
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 == '?' && lip->stmt_prepare_mode && !ident_map[yyPeek()])
|
||||
{
|
||||
/*
|
||||
Warning:
|
||||
This is a work around, to make the "remember_name" rule in
|
||||
sql/sql_yacc.yy work properly.
|
||||
The problem is that, when parsing "select expr1, expr2",
|
||||
the code generated by bison executes the *pre* action
|
||||
remember_name (see select_item) *before* actually parsing the
|
||||
first token of expr2.
|
||||
*/
|
||||
lip->restart_token();
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
if (c == '?' && lip->stmt_prepare_mode && !ident_map[lip->yyPeek()])
|
||||
return(PARAM_MARKER);
|
||||
}
|
||||
|
||||
return((int) c);
|
||||
|
||||
case MY_LEX_IDENT_OR_NCHAR:
|
||||
if (yyPeek() != '\'')
|
||||
{
|
||||
if (lip->yyPeek() != '\'')
|
||||
{
|
||||
state= MY_LEX_IDENT;
|
||||
break;
|
||||
}
|
||||
/* Found N'string' */
|
||||
lip->tok_start++; // Skip N
|
||||
yySkip(); // Skip '
|
||||
if (!(yylval->lex_str.str = get_text(lip)))
|
||||
lip->yySkip(); // Skip '
|
||||
if (!(yylval->lex_str.str = get_text(lip, 2, 1)))
|
||||
{
|
||||
state= MY_LEX_CHAR; // Read char by char
|
||||
break;
|
||||
|
@ -652,13 +671,13 @@ int MYSQLlex(void *arg, void *yythd)
|
|||
return(NCHAR_STRING);
|
||||
|
||||
case MY_LEX_IDENT_OR_HEX:
|
||||
if (yyPeek() == '\'')
|
||||
if (lip->yyPeek() == '\'')
|
||||
{ // Found x'hex-number'
|
||||
state= MY_LEX_HEX_NUMBER;
|
||||
break;
|
||||
}
|
||||
case MY_LEX_IDENT_OR_BIN:
|
||||
if (yyPeek() == '\'')
|
||||
if (lip->yyPeek() == '\'')
|
||||
{ // Found b'bin-number'
|
||||
state= MY_LEX_BIN_NUMBER;
|
||||
break;
|
||||
|
@ -669,54 +688,58 @@ int MYSQLlex(void *arg, void *yythd)
|
|||
if (use_mb(cs))
|
||||
{
|
||||
result_state= IDENT_QUOTED;
|
||||
if (my_mbcharlen(cs, yyGetLast()) > 1)
|
||||
if (my_mbcharlen(cs, lip->yyGetLast()) > 1)
|
||||
{
|
||||
int l = my_ismbchar(cs, lip->ptr-1, lip->end_of_query);
|
||||
int l = my_ismbchar(cs,
|
||||
lip->get_ptr() -1,
|
||||
lip->get_end_of_query());
|
||||
if (l == 0) {
|
||||
state = MY_LEX_CHAR;
|
||||
continue;
|
||||
}
|
||||
lip->ptr += l - 1;
|
||||
lip->skip_binary(l - 1);
|
||||
}
|
||||
while (ident_map[c=yyGet()])
|
||||
while (ident_map[c=lip->yyGet()])
|
||||
{
|
||||
if (my_mbcharlen(cs, c) > 1)
|
||||
{
|
||||
int l;
|
||||
if ((l = my_ismbchar(cs, lip->ptr-1, lip->end_of_query)) == 0)
|
||||
if ((l = my_ismbchar(cs,
|
||||
lip->get_ptr() -1,
|
||||
lip->get_end_of_query())) == 0)
|
||||
break;
|
||||
lip->ptr += l-1;
|
||||
lip->skip_binary(l-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
for (result_state= c; ident_map[c= yyGet()]; result_state|= c);
|
||||
for (result_state= c; ident_map[c= lip->yyGet()]; result_state|= c);
|
||||
/* If there were non-ASCII characters, mark that we must convert */
|
||||
result_state= result_state & 0x80 ? IDENT_QUOTED : IDENT;
|
||||
}
|
||||
length= (uint) (lip->ptr - lip->tok_start)-1;
|
||||
start= lip->ptr;
|
||||
length= lip->yyLength();
|
||||
start= lip->get_ptr();
|
||||
if (lip->ignore_space)
|
||||
{
|
||||
/*
|
||||
If we find a space then this can't be an identifier. We notice this
|
||||
below by checking start != lex->ptr.
|
||||
*/
|
||||
for (; state_map[c] == MY_LEX_SKIP ; c= yyGet());
|
||||
for (; state_map[c] == MY_LEX_SKIP ; c= lip->yyGet());
|
||||
}
|
||||
if (start == lip->ptr && c == '.' && ident_map[yyPeek()])
|
||||
if (start == lip->get_ptr() && c == '.' && ident_map[lip->yyPeek()])
|
||||
lip->next_state=MY_LEX_IDENT_SEP;
|
||||
else
|
||||
{ // '(' must follow directly if function
|
||||
yyUnget();
|
||||
if ((tokval = find_keyword(lip, length,c == '(')))
|
||||
lip->yyUnget();
|
||||
if ((tokval = find_keyword(lip, length, c == '(')))
|
||||
{
|
||||
lip->next_state= MY_LEX_START; // Allow signed numbers
|
||||
return(tokval); // Was keyword
|
||||
}
|
||||
yySkip(); // next state does a unget
|
||||
lip->yySkip(); // next state does a unget
|
||||
}
|
||||
yylval->lex_str=get_token(lip, 0, length);
|
||||
|
||||
|
@ -735,16 +758,48 @@ 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*) lip->ptr;
|
||||
yylval->lex_str.length=1;
|
||||
c=yyGet(); // should be '.'
|
||||
yylval->lex_str.str= (char*) lip->get_ptr();
|
||||
yylval->lex_str.length= 1;
|
||||
c= lip->yyGet(); // should be '.'
|
||||
lip->next_state= MY_LEX_IDENT_START;// Next is an ident (not a keyword)
|
||||
if (!ident_map[yyPeek()]) // Probably ` or "
|
||||
if (!ident_map[lip->yyPeek()]) // Probably ` or "
|
||||
lip->next_state= MY_LEX_START;
|
||||
return((int) c);
|
||||
|
||||
case MY_LEX_NUMBER_IDENT: // number or ident which num-start
|
||||
while (my_isdigit(cs,(c = yyGet()))) ;
|
||||
if (lip->yyGetLast() == '0')
|
||||
{
|
||||
c= lip->yyGet();
|
||||
if (c == 'x')
|
||||
{
|
||||
while (my_isxdigit(cs,(c = lip->yyGet()))) ;
|
||||
if ((lip->yyLength() >= 3) && !ident_map[c])
|
||||
{
|
||||
/* skip '0x' */
|
||||
yylval->lex_str=get_token(lip, 2, lip->yyLength()-2);
|
||||
return (HEX_NUM);
|
||||
}
|
||||
lip->yyUnget();
|
||||
state= MY_LEX_IDENT_START;
|
||||
break;
|
||||
}
|
||||
else if (c == 'b')
|
||||
{
|
||||
while ((c= lip->yyGet()) == '0' || c == '1');
|
||||
if ((lip->yyLength() >= 3) && !ident_map[c])
|
||||
{
|
||||
/* Skip '0b' */
|
||||
yylval->lex_str= get_token(lip, 2, lip->yyLength()-2);
|
||||
return (BIN_NUM);
|
||||
}
|
||||
lip->yyUnget();
|
||||
state= MY_LEX_IDENT_START;
|
||||
break;
|
||||
}
|
||||
lip->yyUnget();
|
||||
}
|
||||
|
||||
while (my_isdigit(cs, (c = lip->yyGet()))) ;
|
||||
if (!ident_map[c])
|
||||
{ // Can't be identifier
|
||||
state=MY_LEX_INT_OR_REAL;
|
||||
|
@ -753,42 +808,18 @@ int MYSQLlex(void *arg, void *yythd)
|
|||
if (c == 'e' || c == 'E')
|
||||
{
|
||||
// The following test is written this way to allow numbers of type 1e1
|
||||
if (my_isdigit(cs,yyPeek()) ||
|
||||
(c=(yyGet())) == '+' || c == '-')
|
||||
if (my_isdigit(cs,lip->yyPeek()) ||
|
||||
(c=(lip->yyGet())) == '+' || c == '-')
|
||||
{ // Allow 1E+10
|
||||
if (my_isdigit(cs,yyPeek())) // Number must have digit after sign
|
||||
if (my_isdigit(cs,lip->yyPeek())) // Number must have digit after sign
|
||||
{
|
||||
yySkip();
|
||||
while (my_isdigit(cs,yyGet())) ;
|
||||
yylval->lex_str=get_token(lip, 0, yyLength());
|
||||
lip->yySkip();
|
||||
while (my_isdigit(cs,lip->yyGet())) ;
|
||||
yylval->lex_str=get_token(lip, 0, lip->yyLength());
|
||||
return(FLOAT_NUM);
|
||||
}
|
||||
}
|
||||
yyUnget(); /* purecov: inspected */
|
||||
}
|
||||
else if (c == 'x' && (lip->ptr - lip->tok_start) == 2 &&
|
||||
lip->tok_start[0] == '0' )
|
||||
{ // Varbinary
|
||||
while (my_isxdigit(cs,(c = yyGet()))) ;
|
||||
if ((lip->ptr - lip->tok_start) >= 4 && !ident_map[c])
|
||||
{
|
||||
/* skip '0x' */
|
||||
yylval->lex_str=get_token(lip, 2, yyLength()-2);
|
||||
return (HEX_NUM);
|
||||
}
|
||||
yyUnget();
|
||||
}
|
||||
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 ((lip->ptr - lip->tok_start) >= 4 && !ident_map[c])
|
||||
{
|
||||
/* Skip '0b' */
|
||||
yylval->lex_str= get_token(lip, 2, yyLength()-2);
|
||||
return (BIN_NUM);
|
||||
}
|
||||
yyUnget();
|
||||
lip->yyUnget();
|
||||
}
|
||||
// fall through
|
||||
case MY_LEX_IDENT_START: // We come here after '.'
|
||||
|
@ -797,44 +828,46 @@ int MYSQLlex(void *arg, void *yythd)
|
|||
if (use_mb(cs))
|
||||
{
|
||||
result_state= IDENT_QUOTED;
|
||||
while (ident_map[c=yyGet()])
|
||||
while (ident_map[c=lip->yyGet()])
|
||||
{
|
||||
if (my_mbcharlen(cs, c) > 1)
|
||||
{
|
||||
int l;
|
||||
if ((l = my_ismbchar(cs, lip->ptr-1, lip->end_of_query)) == 0)
|
||||
if ((l = my_ismbchar(cs,
|
||||
lip->get_ptr() -1,
|
||||
lip->get_end_of_query())) == 0)
|
||||
break;
|
||||
lip->ptr += l-1;
|
||||
lip->skip_binary(l-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
for (result_state=0; ident_map[c= yyGet()]; result_state|= c);
|
||||
for (result_state=0; ident_map[c= lip->yyGet()]; result_state|= c);
|
||||
/* If there were non-ASCII characters, mark that we must convert */
|
||||
result_state= result_state & 0x80 ? IDENT_QUOTED : IDENT;
|
||||
}
|
||||
if (c == '.' && ident_map[yyPeek()])
|
||||
if (c == '.' && ident_map[lip->yyPeek()])
|
||||
lip->next_state=MY_LEX_IDENT_SEP;// Next is '.'
|
||||
|
||||
yylval->lex_str= get_token(lip, 0, yyLength());
|
||||
yylval->lex_str= get_token(lip, 0, lip->yyLength());
|
||||
return(result_state);
|
||||
|
||||
case MY_LEX_USER_VARIABLE_DELIMITER: // Found quote char
|
||||
{
|
||||
uint double_quotes= 0;
|
||||
char quote_char= c; // Used char
|
||||
while ((c=yyGet()))
|
||||
while ((c=lip->yyGet()))
|
||||
{
|
||||
int var_length;
|
||||
if ((var_length= my_mbcharlen(cs, c)) == 1)
|
||||
{
|
||||
if (c == quote_char)
|
||||
{
|
||||
if (yyPeek() != quote_char)
|
||||
if (lip->yyPeek() != quote_char)
|
||||
break;
|
||||
c=yyGet();
|
||||
c=lip->yyGet();
|
||||
double_quotes++;
|
||||
continue;
|
||||
}
|
||||
|
@ -842,78 +875,78 @@ int MYSQLlex(void *arg, void *yythd)
|
|||
#ifdef USE_MB
|
||||
else if (var_length < 1)
|
||||
break; // Error
|
||||
lip->ptr+= var_length-1;
|
||||
lip->skip_binary(var_length-1);
|
||||
#endif
|
||||
}
|
||||
if (double_quotes)
|
||||
yylval->lex_str=get_quoted_token(lip, 1,
|
||||
yyLength() - double_quotes -1,
|
||||
lip->yyLength() - double_quotes -1,
|
||||
quote_char);
|
||||
else
|
||||
yylval->lex_str=get_token(lip, 1, yyLength() -1);
|
||||
yylval->lex_str=get_token(lip, 1, lip->yyLength() -1);
|
||||
if (c == quote_char)
|
||||
yySkip(); // Skip end `
|
||||
lip->yySkip(); // Skip end `
|
||||
lip->next_state= MY_LEX_START;
|
||||
return(IDENT_QUOTED);
|
||||
}
|
||||
case MY_LEX_INT_OR_REAL: // Complete int or incomplete real
|
||||
if (c != '.')
|
||||
{ // Found complete integer number.
|
||||
yylval->lex_str=get_token(lip, 0, yyLength());
|
||||
yylval->lex_str=get_token(lip, 0, lip->yyLength());
|
||||
return int_token(yylval->lex_str.str,yylval->lex_str.length);
|
||||
}
|
||||
// fall through
|
||||
case MY_LEX_REAL: // Incomplete real number
|
||||
while (my_isdigit(cs,c = yyGet())) ;
|
||||
while (my_isdigit(cs,c = lip->yyGet())) ;
|
||||
|
||||
if (c == 'e' || c == 'E')
|
||||
{
|
||||
c = yyGet();
|
||||
c = lip->yyGet();
|
||||
if (c == '-' || c == '+')
|
||||
c = yyGet(); // Skip sign
|
||||
c = lip->yyGet(); // Skip sign
|
||||
if (!my_isdigit(cs,c))
|
||||
{ // No digit after sign
|
||||
state= MY_LEX_CHAR;
|
||||
break;
|
||||
}
|
||||
while (my_isdigit(cs,yyGet())) ;
|
||||
yylval->lex_str=get_token(lip, 0, yyLength());
|
||||
while (my_isdigit(cs,lip->yyGet())) ;
|
||||
yylval->lex_str=get_token(lip, 0, lip->yyLength());
|
||||
return(FLOAT_NUM);
|
||||
}
|
||||
yylval->lex_str=get_token(lip, 0, yyLength());
|
||||
yylval->lex_str=get_token(lip, 0, lip->yyLength());
|
||||
return(DECIMAL_NUM);
|
||||
|
||||
case MY_LEX_HEX_NUMBER: // Found x'hexstring'
|
||||
yyGet(); // Skip '
|
||||
while (my_isxdigit(cs,(c = yyGet()))) ;
|
||||
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
|
||||
lip->yySkip(); // Accept opening '
|
||||
while (my_isxdigit(cs, (c= lip->yyGet()))) ;
|
||||
if (c != '\'')
|
||||
return(ABORT_SYM); // Illegal hex constant
|
||||
lip->yySkip(); // Accept closing '
|
||||
length= lip->yyLength(); // Length of hexnum+3
|
||||
if ((length % 2) == 0)
|
||||
return(ABORT_SYM); // odd number of hex digits
|
||||
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= (lip->ptr - lip->tok_start); // Length of bin-num + 3
|
||||
lip->yySkip(); // Accept opening '
|
||||
while ((c= lip->yyGet()) == '0' || c == '1');
|
||||
if (c != '\'')
|
||||
return(ABORT_SYM); // Illegal hex constant
|
||||
yyGet(); // get_token makes an unget
|
||||
return(ABORT_SYM); // Illegal hex constant
|
||||
lip->yySkip(); // Accept closing '
|
||||
length= lip->yyLength(); // Length of bin-num + 3
|
||||
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(lip, (uint) (lip->ptr - lip->tok_start),0)))
|
||||
if (state_map[lip->yyPeek()] == MY_LEX_CMP_OP ||
|
||||
state_map[lip->yyPeek()] == MY_LEX_LONG_CMP_OP)
|
||||
lip->yySkip();
|
||||
if ((tokval = find_keyword(lip, lip->yyLength() + 1, 0)))
|
||||
{
|
||||
lip->next_state= MY_LEX_START; // Allow signed numbers
|
||||
return(tokval);
|
||||
|
@ -922,14 +955,14 @@ int MYSQLlex(void *arg, void *yythd)
|
|||
break;
|
||||
|
||||
case MY_LEX_LONG_CMP_OP: // Incomplete comparison operator
|
||||
if (state_map[yyPeek()] == MY_LEX_CMP_OP ||
|
||||
state_map[yyPeek()] == MY_LEX_LONG_CMP_OP)
|
||||
if (state_map[lip->yyPeek()] == MY_LEX_CMP_OP ||
|
||||
state_map[lip->yyPeek()] == MY_LEX_LONG_CMP_OP)
|
||||
{
|
||||
yySkip();
|
||||
if (state_map[yyPeek()] == MY_LEX_CMP_OP)
|
||||
yySkip();
|
||||
lip->yySkip();
|
||||
if (state_map[lip->yyPeek()] == MY_LEX_CMP_OP)
|
||||
lip->yySkip();
|
||||
}
|
||||
if ((tokval = find_keyword(lip, (uint) (lip->ptr - lip->tok_start),0)))
|
||||
if ((tokval = find_keyword(lip, lip->yyLength() + 1, 0)))
|
||||
{
|
||||
lip->next_state= MY_LEX_START; // Found long op
|
||||
return(tokval);
|
||||
|
@ -938,12 +971,12 @@ int MYSQLlex(void *arg, void *yythd)
|
|||
break;
|
||||
|
||||
case MY_LEX_BOOL:
|
||||
if (c != yyPeek())
|
||||
if (c != lip->yyPeek())
|
||||
{
|
||||
state=MY_LEX_CHAR;
|
||||
break;
|
||||
}
|
||||
yySkip();
|
||||
lip->yySkip();
|
||||
tokval = find_keyword(lip,2,0); // Is a bool operator
|
||||
lip->next_state= MY_LEX_START; // Allow signed numbers
|
||||
return(tokval);
|
||||
|
@ -956,7 +989,7 @@ int MYSQLlex(void *arg, void *yythd)
|
|||
}
|
||||
/* " used for strings */
|
||||
case MY_LEX_STRING: // Incomplete text string
|
||||
if (!(yylval->lex_str.str = get_text(lip)))
|
||||
if (!(yylval->lex_str.str = get_text(lip, 1, 1)))
|
||||
{
|
||||
state= MY_LEX_CHAR; // Read char by char
|
||||
break;
|
||||
|
@ -966,82 +999,138 @@ int MYSQLlex(void *arg, void *yythd)
|
|||
|
||||
case MY_LEX_COMMENT: // Comment
|
||||
lex->select_lex.options|= OPTION_FOUND_COMMENT;
|
||||
while ((c = yyGet()) != '\n' && c) ;
|
||||
yyUnget(); // Safety against eof
|
||||
while ((c = lip->yyGet()) != '\n' && c) ;
|
||||
lip->yyUnget(); // Safety against eof
|
||||
state = MY_LEX_START; // Try again
|
||||
break;
|
||||
case MY_LEX_LONG_COMMENT: /* Long C comment? */
|
||||
if (yyPeek() != '*')
|
||||
if (lip->yyPeek() != '*')
|
||||
{
|
||||
state=MY_LEX_CHAR; // Probable division
|
||||
break;
|
||||
}
|
||||
yySkip(); // Skip '*'
|
||||
lex->select_lex.options|= OPTION_FOUND_COMMENT;
|
||||
if (yyPeek() == '!') // MySQL command in comment
|
||||
/* Reject '/' '*', since we might need to turn off the echo */
|
||||
lip->yyUnget();
|
||||
|
||||
if (lip->yyPeekn(2) == '!')
|
||||
{
|
||||
ulong version=MYSQL_VERSION_ID;
|
||||
yySkip();
|
||||
state=MY_LEX_START;
|
||||
if (my_isdigit(cs,yyPeek()))
|
||||
{ // Version number
|
||||
version=strtol((char*) lip->ptr,(char**) &lip->ptr,10);
|
||||
}
|
||||
if (version <= MYSQL_VERSION_ID)
|
||||
{
|
||||
lex->in_comment=1;
|
||||
break;
|
||||
}
|
||||
lip->in_comment= DISCARD_COMMENT;
|
||||
/* Accept '/' '*' '!', but do not keep this marker. */
|
||||
lip->set_echo(false);
|
||||
lip->yySkip();
|
||||
lip->yySkip();
|
||||
lip->yySkip();
|
||||
|
||||
/*
|
||||
The special comment format is very strict:
|
||||
'/' '*' '!', followed by exactly
|
||||
2 digits (major), then 3 digits (minor).
|
||||
*/
|
||||
char version_str[6];
|
||||
version_str[0]= lip->yyPeekn(0);
|
||||
version_str[1]= lip->yyPeekn(1);
|
||||
version_str[2]= lip->yyPeekn(2);
|
||||
version_str[3]= lip->yyPeekn(3);
|
||||
version_str[4]= lip->yyPeekn(4);
|
||||
version_str[5]= 0;
|
||||
if ( my_isdigit(cs, version_str[0])
|
||||
&& my_isdigit(cs, version_str[1])
|
||||
&& my_isdigit(cs, version_str[2])
|
||||
&& my_isdigit(cs, version_str[3])
|
||||
&& my_isdigit(cs, version_str[4])
|
||||
)
|
||||
{
|
||||
ulong version;
|
||||
version=strtol(version_str, NULL, 10);
|
||||
|
||||
/* Accept 'M' 'M' 'm' 'm' 'm' */
|
||||
lip->yySkipn(5);
|
||||
|
||||
if (version <= MYSQL_VERSION_ID)
|
||||
{
|
||||
/* Expand the content of the special comment as real code */
|
||||
lip->set_echo(true);
|
||||
state=MY_LEX_START;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
state=MY_LEX_START;
|
||||
lip->set_echo(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (lip->ptr != lip->end_of_query &&
|
||||
((c=yyGet()) != '*' || yyPeek() != '/'))
|
||||
else
|
||||
{
|
||||
if (c == '\n')
|
||||
lip->yylineno++;
|
||||
lip->in_comment= PRESERVE_COMMENT;
|
||||
lip->yySkip(); // Accept /
|
||||
lip->yySkip(); // Accept *
|
||||
}
|
||||
if (lip->ptr != lip->end_of_query)
|
||||
yySkip(); // remove last '/'
|
||||
state = MY_LEX_START; // Try again
|
||||
|
||||
while (! lip->eof() &&
|
||||
((c=lip->yyGet()) != '*' || lip->yyPeek() != '/'))
|
||||
{
|
||||
if (c == '\n')
|
||||
lip->yylineno++;
|
||||
}
|
||||
if (! lip->eof())
|
||||
lip->yySkip(); // remove last '/'
|
||||
state = MY_LEX_START; // Try again
|
||||
lip->set_echo(true);
|
||||
break;
|
||||
case MY_LEX_END_LONG_COMMENT:
|
||||
if (lex->in_comment && yyPeek() == '/')
|
||||
if ((lip->in_comment != NO_COMMENT) && lip->yyPeek() == '/')
|
||||
{
|
||||
yySkip();
|
||||
lex->in_comment=0;
|
||||
state=MY_LEX_START;
|
||||
/* Reject '*' '/' */
|
||||
lip->yyUnget();
|
||||
/* Accept '*' '/', with the proper echo */
|
||||
lip->set_echo(lip->in_comment == PRESERVE_COMMENT);
|
||||
lip->yySkipn(2);
|
||||
/* And start recording the tokens again */
|
||||
lip->set_echo(true);
|
||||
lip->in_comment=NO_COMMENT;
|
||||
state=MY_LEX_START;
|
||||
}
|
||||
else
|
||||
state=MY_LEX_CHAR; // Return '*'
|
||||
break;
|
||||
case MY_LEX_SET_VAR: // Check if ':='
|
||||
if (yyPeek() != '=')
|
||||
if (lip->yyPeek() != '=')
|
||||
{
|
||||
state=MY_LEX_CHAR; // Return ':'
|
||||
break;
|
||||
}
|
||||
yySkip();
|
||||
lip->yySkip();
|
||||
return (SET_VAR);
|
||||
case MY_LEX_SEMICOLON: // optional line terminator
|
||||
if (yyPeek())
|
||||
if (lip->yyPeek())
|
||||
{
|
||||
if ((thd->client_capabilities & CLIENT_MULTI_STATEMENTS) &&
|
||||
!lip->stmt_prepare_mode)
|
||||
{
|
||||
lex->safe_to_cache_query= 0;
|
||||
lip->found_semicolon= lip->ptr;
|
||||
lip->found_semicolon= lip->get_ptr();
|
||||
thd->server_status|= SERVER_MORE_RESULTS_EXISTS;
|
||||
lip->next_state= MY_LEX_END;
|
||||
lip->set_echo(true);
|
||||
return (END_OF_INPUT);
|
||||
}
|
||||
state= MY_LEX_CHAR; // Return ';'
|
||||
break;
|
||||
}
|
||||
/* fall true */
|
||||
lip->next_state=MY_LEX_END; // Mark for next loop
|
||||
return(END_OF_INPUT);
|
||||
case MY_LEX_EOL:
|
||||
if (lip->ptr >= lip->end_of_query)
|
||||
if (lip->eof())
|
||||
{
|
||||
lip->next_state=MY_LEX_END; // Mark for next loop
|
||||
return(END_OF_INPUT);
|
||||
lip->yyUnget(); // Reject the last '\0'
|
||||
lip->set_echo(false);
|
||||
lip->yySkip();
|
||||
lip->set_echo(true);
|
||||
lip->next_state=MY_LEX_END; // Mark for next loop
|
||||
return(END_OF_INPUT);
|
||||
}
|
||||
state=MY_LEX_CHAR;
|
||||
break;
|
||||
|
@ -1051,16 +1140,16 @@ int MYSQLlex(void *arg, void *yythd)
|
|||
|
||||
/* Actually real shouldn't start with . but allow them anyhow */
|
||||
case MY_LEX_REAL_OR_POINT:
|
||||
if (my_isdigit(cs,yyPeek()))
|
||||
if (my_isdigit(cs,lip->yyPeek()))
|
||||
state = MY_LEX_REAL; // Real
|
||||
else
|
||||
{
|
||||
state= MY_LEX_IDENT_SEP; // return '.'
|
||||
yyUnget(); // Put back '.'
|
||||
lip->yyUnget(); // Put back '.'
|
||||
}
|
||||
break;
|
||||
case MY_LEX_USER_END: // end '@' of user@hostname
|
||||
switch (state_map[yyPeek()]) {
|
||||
switch (state_map[lip->yyPeek()]) {
|
||||
case MY_LEX_STRING:
|
||||
case MY_LEX_USER_VARIABLE_DELIMITER:
|
||||
case MY_LEX_STRING_OR_DELIMITER:
|
||||
|
@ -1072,20 +1161,20 @@ int MYSQLlex(void *arg, void *yythd)
|
|||
lip->next_state=MY_LEX_HOSTNAME;
|
||||
break;
|
||||
}
|
||||
yylval->lex_str.str=(char*) lip->ptr;
|
||||
yylval->lex_str.str=(char*) lip->get_ptr();
|
||||
yylval->lex_str.length=1;
|
||||
return((int) '@');
|
||||
case MY_LEX_HOSTNAME: // end '@' of user@hostname
|
||||
for (c=yyGet() ;
|
||||
for (c=lip->yyGet() ;
|
||||
my_isalnum(cs,c) || c == '.' || c == '_' || c == '$';
|
||||
c= yyGet()) ;
|
||||
yylval->lex_str=get_token(lip, 0, yyLength());
|
||||
c= lip->yyGet()) ;
|
||||
yylval->lex_str=get_token(lip, 0, lip->yyLength());
|
||||
return(LEX_HOSTNAME);
|
||||
case MY_LEX_SYSTEM_VAR:
|
||||
yylval->lex_str.str=(char*) lip->ptr;
|
||||
yylval->lex_str.str=(char*) lip->get_ptr();
|
||||
yylval->lex_str.length=1;
|
||||
yySkip(); // Skip '@'
|
||||
lip->next_state= (state_map[yyPeek()] ==
|
||||
lip->yySkip(); // Skip '@'
|
||||
lip->next_state= (state_map[lip->yyPeek()] ==
|
||||
MY_LEX_USER_VARIABLE_DELIMITER ?
|
||||
MY_LEX_OPERATOR_OR_IDENT :
|
||||
MY_LEX_IDENT_OR_KEYWORD);
|
||||
|
@ -1096,19 +1185,19 @@ int MYSQLlex(void *arg, void *yythd)
|
|||
We should now be able to handle:
|
||||
[(global | local | session) .]variable_name
|
||||
*/
|
||||
|
||||
for (result_state= 0; ident_map[c= yyGet()]; result_state|= c);
|
||||
|
||||
for (result_state= 0; ident_map[c= lip->yyGet()]; result_state|= c);
|
||||
/* If there were non-ASCII characters, mark that we must convert */
|
||||
result_state= result_state & 0x80 ? IDENT_QUOTED : IDENT;
|
||||
|
||||
|
||||
if (c == '.')
|
||||
lip->next_state=MY_LEX_IDENT_SEP;
|
||||
length= (uint) (lip->ptr - lip->tok_start)-1;
|
||||
if (length == 0)
|
||||
length= lip->yyLength();
|
||||
if (length == 0)
|
||||
return(ABORT_SYM); // Names must be nonempty.
|
||||
if ((tokval= find_keyword(lip, length,0)))
|
||||
{
|
||||
yyUnget(); // Put back 'c'
|
||||
lip->yyUnget(); // Put back 'c'
|
||||
return(tokval); // Was keyword
|
||||
}
|
||||
yylval->lex_str=get_token(lip, 0, length);
|
||||
|
@ -1135,7 +1224,7 @@ Alter_info::Alter_info(const Alter_info &rhs, MEM_ROOT *mem_root)
|
|||
/*
|
||||
Make deep copies of used objects.
|
||||
This is not a fully deep copy - clone() implementations
|
||||
of Alter_drop, Alter_column, Key, foreign_key, key_part_spec
|
||||
of Alter_drop, Alter_column, Key, foreign_key, Key_part_spec
|
||||
do not copy string constants. At the same length the only
|
||||
reason we make a copy currently is that ALTER/CREATE TABLE
|
||||
code changes input Alter_info definitions, but string
|
||||
|
@ -1149,32 +1238,31 @@ Alter_info::Alter_info(const Alter_info &rhs, MEM_ROOT *mem_root)
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
Skip comment in the end of statement.
|
||||
|
||||
SYNOPSIS
|
||||
skip_rear_comments()
|
||||
cs character set
|
||||
begin pointer to the beginning of statement
|
||||
end pointer to the end of statement
|
||||
|
||||
DESCRIPTION
|
||||
The function is intended to trim comments at the end of the statement.
|
||||
|
||||
RETURN
|
||||
Pointer to the last non-comment symbol of the statement.
|
||||
*/
|
||||
|
||||
const char *skip_rear_comments(CHARSET_INFO *cs, const char *begin,
|
||||
const char *end)
|
||||
void trim_whitespace(CHARSET_INFO *cs, LEX_STRING *str)
|
||||
{
|
||||
while (begin < end && (end[-1] == '*' ||
|
||||
end[-1] == '/' || end[-1] == ';' ||
|
||||
my_isspace(cs, end[-1])))
|
||||
end-= 1;
|
||||
return end;
|
||||
/*
|
||||
TODO:
|
||||
This code assumes that there are no multi-bytes characters
|
||||
that can be considered white-space.
|
||||
*/
|
||||
|
||||
while ((str->length > 0) && (my_isspace(cs, str->str[0])))
|
||||
{
|
||||
str->length --;
|
||||
str->str ++;
|
||||
}
|
||||
|
||||
/*
|
||||
FIXME:
|
||||
Also, parsing backward is not safe with multi bytes characters
|
||||
*/
|
||||
while ((str->length > 0) && (my_isspace(cs, str->str[str->length-1])))
|
||||
{
|
||||
str->length --;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
st_select_lex structures initialisations
|
||||
*/
|
||||
|
|
344
sql/sql_lex.h
344
sql/sql_lex.h
|
@ -850,14 +850,14 @@ public:
|
|||
List<Alter_drop> drop_list;
|
||||
List<Alter_column> alter_list;
|
||||
List<Key> key_list;
|
||||
List<create_field> create_list;
|
||||
List<Create_field> create_list;
|
||||
uint flags;
|
||||
enum enum_enable_or_disable keys_onoff;
|
||||
enum tablespace_op_type tablespace_op;
|
||||
List<char> partition_names;
|
||||
uint no_parts;
|
||||
enum_alter_table_change_level change_level;
|
||||
create_field *datetime_field;
|
||||
Create_field *datetime_field;
|
||||
bool error_if_not_empty;
|
||||
|
||||
|
||||
|
@ -1076,9 +1076,41 @@ struct st_parsing_options
|
|||
};
|
||||
|
||||
|
||||
/**
|
||||
The state of the lexical parser, when parsing comments.
|
||||
*/
|
||||
enum enum_comment_state
|
||||
{
|
||||
/**
|
||||
Not parsing comments.
|
||||
*/
|
||||
NO_COMMENT,
|
||||
/**
|
||||
Parsing comments that need to be preserved.
|
||||
Typically, these are user comments '/' '*' ... '*' '/'.
|
||||
*/
|
||||
PRESERVE_COMMENT,
|
||||
/**
|
||||
Parsing comments that need to be discarded.
|
||||
Typically, these are special comments '/' '*' '!' ... '*' '/',
|
||||
or '/' '*' '!' 'M' 'M' 'm' 'm' 'm' ... '*' '/', where the comment
|
||||
markers should not be expanded.
|
||||
*/
|
||||
DISCARD_COMMENT
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
This class represents the character input stream consumed during
|
||||
lexical analysis.
|
||||
In addition to consuming the input stream, this class performs some
|
||||
comment pre processing, by filtering out out of bound special text
|
||||
from the query input stream.
|
||||
Two buffers, with pointers inside each buffers, are maintained in
|
||||
parallel. The 'raw' buffer is the original query text, which may
|
||||
contain out-of-bound comments. The 'cpp' (for comments pre processor)
|
||||
is the pre-processed buffer that contains only the query text that
|
||||
should be seen once out-of-bound data is removed.
|
||||
*/
|
||||
class Lex_input_stream
|
||||
{
|
||||
|
@ -1086,6 +1118,218 @@ public:
|
|||
Lex_input_stream(THD *thd, const char* buff, unsigned int length);
|
||||
~Lex_input_stream();
|
||||
|
||||
/**
|
||||
Set the echo mode.
|
||||
When echo is true, characters parsed from the raw input stream are
|
||||
preserved. When false, characters parsed are silently ignored.
|
||||
@param echo the echo mode.
|
||||
*/
|
||||
void set_echo(bool echo)
|
||||
{
|
||||
m_echo= echo;
|
||||
}
|
||||
|
||||
/**
|
||||
Skip binary from the input stream.
|
||||
@param n number of bytes to accept.
|
||||
*/
|
||||
void skip_binary(int n)
|
||||
{
|
||||
if (m_echo)
|
||||
{
|
||||
memcpy(m_cpp_ptr, m_ptr, n);
|
||||
m_cpp_ptr += n;
|
||||
}
|
||||
m_ptr += n;
|
||||
}
|
||||
|
||||
/**
|
||||
Get a character, and advance in the stream.
|
||||
@return the next character to parse.
|
||||
*/
|
||||
char yyGet()
|
||||
{
|
||||
char c= *m_ptr++;
|
||||
if (m_echo)
|
||||
*m_cpp_ptr++ = c;
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
Get the last character accepted.
|
||||
@return the last character accepted.
|
||||
*/
|
||||
char yyGetLast()
|
||||
{
|
||||
return m_ptr[-1];
|
||||
}
|
||||
|
||||
/**
|
||||
Look at the next character to parse, but do not accept it.
|
||||
*/
|
||||
char yyPeek()
|
||||
{
|
||||
return m_ptr[0];
|
||||
}
|
||||
|
||||
/**
|
||||
Look ahead at some character to parse.
|
||||
@param n offset of the character to look up
|
||||
*/
|
||||
char yyPeekn(int n)
|
||||
{
|
||||
return m_ptr[n];
|
||||
}
|
||||
|
||||
/**
|
||||
Cancel the effect of the last yyGet() or yySkip().
|
||||
Note that the echo mode should not change between calls to yyGet / yySkip
|
||||
and yyUnget. The caller is responsible for ensuring that.
|
||||
*/
|
||||
void yyUnget()
|
||||
{
|
||||
m_ptr--;
|
||||
if (m_echo)
|
||||
m_cpp_ptr--;
|
||||
}
|
||||
|
||||
/**
|
||||
Accept a character, by advancing the input stream.
|
||||
*/
|
||||
void yySkip()
|
||||
{
|
||||
if (m_echo)
|
||||
*m_cpp_ptr++ = *m_ptr++;
|
||||
else
|
||||
m_ptr++;
|
||||
}
|
||||
|
||||
/**
|
||||
Accept multiple characters at once.
|
||||
@param n the number of characters to accept.
|
||||
*/
|
||||
void yySkipn(int n)
|
||||
{
|
||||
if (m_echo)
|
||||
{
|
||||
memcpy(m_cpp_ptr, m_ptr, n);
|
||||
m_cpp_ptr += n;
|
||||
}
|
||||
m_ptr += n;
|
||||
}
|
||||
|
||||
/**
|
||||
End of file indicator for the query text to parse.
|
||||
@return true if there are no more characters to parse
|
||||
*/
|
||||
bool eof()
|
||||
{
|
||||
return (m_ptr >= m_end_of_query);
|
||||
}
|
||||
|
||||
/**
|
||||
End of file indicator for the query text to parse.
|
||||
@param n number of characters expected
|
||||
@return true if there are less than n characters to parse
|
||||
*/
|
||||
bool eof(int n)
|
||||
{
|
||||
return ((m_ptr + n) >= m_end_of_query);
|
||||
}
|
||||
|
||||
/** Get the raw query buffer. */
|
||||
const char* get_buf()
|
||||
{
|
||||
return m_buf;
|
||||
}
|
||||
|
||||
/** Get the pre-processed query buffer. */
|
||||
const char* get_cpp_buf()
|
||||
{
|
||||
return m_cpp_buf;
|
||||
}
|
||||
|
||||
/** Get the end of the raw query buffer. */
|
||||
const char* get_end_of_query()
|
||||
{
|
||||
return m_end_of_query;
|
||||
}
|
||||
|
||||
/** Mark the stream position as the start of a new token. */
|
||||
void start_token()
|
||||
{
|
||||
m_tok_start_prev= m_tok_start;
|
||||
m_tok_start= m_ptr;
|
||||
m_tok_end= m_ptr;
|
||||
|
||||
m_cpp_tok_start_prev= m_cpp_tok_start;
|
||||
m_cpp_tok_start= m_cpp_ptr;
|
||||
m_cpp_tok_end= m_cpp_ptr;
|
||||
}
|
||||
|
||||
/**
|
||||
Adjust the starting position of the current token.
|
||||
This is used to compensate for starting whitespace.
|
||||
*/
|
||||
void restart_token()
|
||||
{
|
||||
m_tok_start= m_ptr;
|
||||
m_cpp_tok_start= m_cpp_ptr;
|
||||
}
|
||||
|
||||
/** Get the token start position, in the raw buffer. */
|
||||
const char* get_tok_start()
|
||||
{
|
||||
return m_tok_start;
|
||||
}
|
||||
|
||||
/** Get the token start position, in the pre-processed buffer. */
|
||||
const char* get_cpp_tok_start()
|
||||
{
|
||||
return m_cpp_tok_start;
|
||||
}
|
||||
|
||||
/** Get the token end position, in the raw buffer. */
|
||||
const char* get_tok_end()
|
||||
{
|
||||
return m_tok_end;
|
||||
}
|
||||
|
||||
/** Get the token end position, in the pre-processed buffer. */
|
||||
const char* get_cpp_tok_end()
|
||||
{
|
||||
return m_cpp_tok_end;
|
||||
}
|
||||
|
||||
/** Get the previous token start position, in the raw buffer. */
|
||||
const char* get_tok_start_prev()
|
||||
{
|
||||
return m_tok_start_prev;
|
||||
}
|
||||
|
||||
/** Get the current stream pointer, in the raw buffer. */
|
||||
const char* get_ptr()
|
||||
{
|
||||
return m_ptr;
|
||||
}
|
||||
|
||||
/** Get the current stream pointer, in the pre-processed buffer. */
|
||||
const char* get_cpp_ptr()
|
||||
{
|
||||
return m_cpp_ptr;
|
||||
}
|
||||
|
||||
/** Get the length of the current token, in the raw buffer. */
|
||||
uint yyLength()
|
||||
{
|
||||
/*
|
||||
The assumption is that the lexical analyser is always 1 character ahead,
|
||||
which the -1 account for.
|
||||
*/
|
||||
DBUG_ASSERT(m_ptr > m_tok_start);
|
||||
return (uint) ((m_ptr - m_tok_start) - 1);
|
||||
}
|
||||
|
||||
/** Current thread. */
|
||||
THD *m_thd;
|
||||
|
||||
|
@ -1098,37 +1342,74 @@ public:
|
|||
/** Interface with bison, value of the last token parsed. */
|
||||
LEX_YYSTYPE yylval;
|
||||
|
||||
/** Pointer to the current position in the input stream. */
|
||||
const char* ptr;
|
||||
private:
|
||||
/** Pointer to the current position in the raw input stream. */
|
||||
const char* m_ptr;
|
||||
|
||||
/** Starting position of the last token parsed. */
|
||||
const char* tok_start;
|
||||
/** Starting position of the last token parsed, in the raw buffer. */
|
||||
const char* m_tok_start;
|
||||
|
||||
/** Ending position of the last token parsed. */
|
||||
const char* tok_end;
|
||||
/** Ending position of the previous token parsed, in the raw buffer. */
|
||||
const char* m_tok_end;
|
||||
|
||||
/** End of the query text in the input stream. */
|
||||
const char* end_of_query;
|
||||
/** End of the query text in the input stream, in the raw buffer. */
|
||||
const char* m_end_of_query;
|
||||
|
||||
/** Starting position of the previous token parsed. */
|
||||
const char* tok_start_prev;
|
||||
/** Starting position of the previous token parsed, in the raw buffer. */
|
||||
const char* m_tok_start_prev;
|
||||
|
||||
/** Begining of the query text in the input stream. */
|
||||
const char* buf;
|
||||
/** Begining of the query text in the input stream, in the raw buffer. */
|
||||
const char* m_buf;
|
||||
|
||||
/** Echo the parsed stream to the pre-processed buffer. */
|
||||
bool m_echo;
|
||||
|
||||
/** Pre-processed buffer. */
|
||||
char* m_cpp_buf;
|
||||
|
||||
/** Pointer to the current position in the pre-processed input stream. */
|
||||
char* m_cpp_ptr;
|
||||
|
||||
/**
|
||||
Starting position of the last token parsed,
|
||||
in the pre-processed buffer.
|
||||
*/
|
||||
const char* m_cpp_tok_start;
|
||||
|
||||
/**
|
||||
Starting position of the previous token parsed,
|
||||
in the pre-procedded buffer.
|
||||
*/
|
||||
const char* m_cpp_tok_start_prev;
|
||||
|
||||
/**
|
||||
Ending position of the previous token parsed,
|
||||
in the pre-processed buffer.
|
||||
*/
|
||||
const char* m_cpp_tok_end;
|
||||
|
||||
public:
|
||||
|
||||
/** Current state of the lexical analyser. */
|
||||
enum my_lex_states next_state;
|
||||
|
||||
/** Position of ';' in the stream, to delimit multiple queries. */
|
||||
/**
|
||||
Position of ';' in the stream, to delimit multiple queries.
|
||||
This delimiter is in the raw buffer.
|
||||
*/
|
||||
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;
|
||||
|
||||
/** State of the lexical analyser for comments. */
|
||||
enum_comment_state in_comment;
|
||||
};
|
||||
|
||||
|
||||
|
@ -1166,8 +1447,17 @@ typedef struct st_lex : public Query_tables_list
|
|||
CHARSET_INFO *charset, *underscore_charset;
|
||||
/* store original leaf_tables for INSERT SELECT and PS/SP */
|
||||
TABLE_LIST *leaf_tables_insert;
|
||||
/* Position (first character index) of SELECT of CREATE VIEW statement */
|
||||
uint create_view_select_start;
|
||||
|
||||
/** Start of SELECT of CREATE VIEW statement */
|
||||
const char* create_view_select_start;
|
||||
/** End of SELECT of CREATE VIEW statement */
|
||||
const char* create_view_select_end;
|
||||
|
||||
/** Start of 'ON <table>', in trigger statements. */
|
||||
const char* raw_trg_on_table_name_begin;
|
||||
/** End of 'ON <table>', in trigger statements. */
|
||||
const char* raw_trg_on_table_name_end;
|
||||
|
||||
/* Partition info structure filled in by PARTITION BY parse part */
|
||||
partition_info *part_info;
|
||||
|
||||
|
@ -1177,8 +1467,8 @@ typedef struct st_lex : public Query_tables_list
|
|||
*/
|
||||
LEX_USER *definer;
|
||||
|
||||
List<key_part_spec> col_list;
|
||||
List<key_part_spec> ref_list;
|
||||
List<Key_part_spec> col_list;
|
||||
List<Key_part_spec> ref_list;
|
||||
List<String> interval_list;
|
||||
List<LEX_USER> users_list;
|
||||
List<LEX_COLUMN> columns;
|
||||
|
@ -1204,7 +1494,7 @@ typedef struct st_lex : public Query_tables_list
|
|||
List<LEX_STRING> db_list;
|
||||
|
||||
SQL_LIST proc_list, auxiliary_table_list, save_list;
|
||||
create_field *last_field;
|
||||
Create_field *last_field;
|
||||
Item_sum *in_sum_func;
|
||||
udf_func udf;
|
||||
HA_CHECK_OPT check_opt; // check/repair options
|
||||
|
@ -1266,7 +1556,9 @@ 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, verbose, no_write_to_binlog;
|
||||
|
||||
bool verbose, no_write_to_binlog;
|
||||
|
||||
bool tx_chain, tx_release;
|
||||
/*
|
||||
Special JOIN::prepare mode: changing of query is prohibited.
|
||||
|
@ -1330,10 +1622,12 @@ typedef struct st_lex : public Query_tables_list
|
|||
- CREATE FUNCTION (points to "FUNCTION" or "AGGREGATE");
|
||||
|
||||
This pointer is required to add possibly omitted DEFINER-clause to the
|
||||
DDL-statement before dumping it to the binlog.
|
||||
DDL-statement before dumping it to the binlog.
|
||||
*/
|
||||
const char *stmt_definition_begin;
|
||||
|
||||
const char *stmt_definition_end;
|
||||
|
||||
/*
|
||||
Pointers to part of LOAD DATA statement that should be rewritten
|
||||
during replication ("LOCAL 'filename' REPLACE INTO" part).
|
||||
|
@ -1462,8 +1756,8 @@ extern void lex_free(void);
|
|||
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(CHARSET_INFO *cs, const char *ubegin,
|
||||
const char *uend);
|
||||
|
||||
extern void trim_whitespace(CHARSET_INFO *cs, LEX_STRING *str);
|
||||
|
||||
extern bool is_lex_native_function(const LEX_STRING *name);
|
||||
|
||||
|
|
|
@ -5354,12 +5354,11 @@ void mysql_parse(THD *thd, const char *inBuf, uint length,
|
|||
sp_cache_flush_obsolete(&thd->sp_func_cache);
|
||||
|
||||
Lex_input_stream lip(thd, inBuf, length);
|
||||
thd->m_lip= &lip;
|
||||
|
||||
int err= MYSQLparse(thd);
|
||||
bool err= parse_sql(thd, &lip);
|
||||
*found_semicolon= lip.found_semicolon;
|
||||
|
||||
if (!err && ! thd->is_fatal_error)
|
||||
if (!err)
|
||||
{
|
||||
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
||||
if (mqh_used && thd->user_connect &&
|
||||
|
@ -5382,8 +5381,8 @@ void mysql_parse(THD *thd, const char *inBuf, uint length,
|
|||
PROCESSLIST.
|
||||
Note that we don't need LOCK_thread_count to modify query_length.
|
||||
*/
|
||||
if (lip.found_semicolon &&
|
||||
(thd->query_length= (ulong)(lip.found_semicolon - thd->query)))
|
||||
if (*found_semicolon &&
|
||||
(thd->query_length= (ulong)(*found_semicolon - thd->query)))
|
||||
thd->query_length--;
|
||||
/* Actually execute the query */
|
||||
mysql_execute_command(thd);
|
||||
|
@ -5437,12 +5436,10 @@ bool mysql_test_parse_for_slave(THD *thd, char *inBuf, uint length)
|
|||
DBUG_ENTER("mysql_test_parse_for_slave");
|
||||
|
||||
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 &&
|
||||
if (!parse_sql(thd, &lip) &&
|
||||
all_tables_not_ok(thd,(TABLE_LIST*) lex->select_lex.table_list.first))
|
||||
error= 1; /* Ignore question */
|
||||
thd->end_statement();
|
||||
|
@ -5467,7 +5464,7 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type,
|
|||
List<String> *interval_list, CHARSET_INFO *cs,
|
||||
uint uint_geom_type)
|
||||
{
|
||||
register create_field *new_field;
|
||||
register Create_field *new_field;
|
||||
LEX *lex= thd->lex;
|
||||
DBUG_ENTER("add_field_to_list");
|
||||
|
||||
|
@ -5480,7 +5477,7 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type,
|
|||
if (type_modifier & PRI_KEY_FLAG)
|
||||
{
|
||||
Key *key;
|
||||
lex->col_list.push_back(new key_part_spec(field_name->str, 0));
|
||||
lex->col_list.push_back(new Key_part_spec(field_name->str, 0));
|
||||
key= new Key(Key::PRIMARY, NullS,
|
||||
&default_key_create_info,
|
||||
0, lex->col_list);
|
||||
|
@ -5490,7 +5487,7 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type,
|
|||
if (type_modifier & (UNIQUE_FLAG | UNIQUE_KEY_FLAG))
|
||||
{
|
||||
Key *key;
|
||||
lex->col_list.push_back(new key_part_spec(field_name->str, 0));
|
||||
lex->col_list.push_back(new Key_part_spec(field_name->str, 0));
|
||||
key= new Key(Key::UNIQUE, NullS,
|
||||
&default_key_create_info, 0,
|
||||
lex->col_list);
|
||||
|
@ -5548,7 +5545,7 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type,
|
|||
WARN_DEPRECATED(thd, "5.2", buf, "'TIMESTAMP'");
|
||||
}
|
||||
|
||||
if (!(new_field= new create_field()) ||
|
||||
if (!(new_field= new Create_field()) ||
|
||||
new_field->init(thd, field_name->str, type, length, decimals, type_modifier,
|
||||
default_value, on_update_value, comment, change,
|
||||
interval_list, cs, uint_geom_type))
|
||||
|
@ -7134,3 +7131,34 @@ bool check_string_char_length(LEX_STRING *str, const char *err_msg,
|
|||
my_error(ER_WRONG_STRING_LENGTH, MYF(0), str->str, err_msg, max_char_length);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
extern int MYSQLparse(void *thd); // from sql_yacc.cc
|
||||
|
||||
|
||||
/**
|
||||
This is a wrapper of MYSQLparse(). All the code should call parse_sql()
|
||||
instead of MYSQLparse().
|
||||
|
||||
@param thd Thread context.
|
||||
@param lip Lexer context.
|
||||
|
||||
@return Error status.
|
||||
@retval FALSE on success.
|
||||
@retval TRUE on parsing error.
|
||||
*/
|
||||
|
||||
bool parse_sql(THD *thd, Lex_input_stream *lip)
|
||||
{
|
||||
bool err_status;
|
||||
|
||||
DBUG_ASSERT(thd->m_lip == NULL);
|
||||
|
||||
thd->m_lip= lip;
|
||||
|
||||
err_status= MYSQLparse(thd) != 0 || thd->is_fatal_error;
|
||||
|
||||
thd->m_lip= NULL;
|
||||
|
||||
return err_status;
|
||||
}
|
||||
|
|
|
@ -3696,7 +3696,6 @@ bool mysql_unpack_partition(THD *thd,
|
|||
thd->variables.character_set_client= system_charset_info;
|
||||
|
||||
Lex_input_stream lip(thd, part_buf, part_info_len);
|
||||
thd->m_lip= &lip;
|
||||
|
||||
lex_start(thd);
|
||||
/*
|
||||
|
@ -3725,7 +3724,7 @@ bool mysql_unpack_partition(THD *thd,
|
|||
lex.part_info->part_state= part_state;
|
||||
lex.part_info->part_state_len= part_state_len;
|
||||
DBUG_PRINT("info", ("Parse: %s", part_buf));
|
||||
if (MYSQLparse((void*)thd) || thd->is_fatal_error)
|
||||
if (parse_sql(thd, &lip))
|
||||
{
|
||||
thd->free_items();
|
||||
goto end;
|
||||
|
|
|
@ -2868,12 +2868,11 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
|
|||
|
||||
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);
|
||||
error= parse_sql(thd, &lip) ||
|
||||
thd->net.report_error ||
|
||||
init_param_array(this);
|
||||
|
||||
/*
|
||||
While doing context analysis of the query (in check_prepared_statement)
|
||||
|
|
|
@ -10083,12 +10083,12 @@ err:
|
|||
0 if out of memory, TABLE object in case of success
|
||||
*/
|
||||
|
||||
TABLE *create_virtual_tmp_table(THD *thd, List<create_field> &field_list)
|
||||
TABLE *create_virtual_tmp_table(THD *thd, List<Create_field> &field_list)
|
||||
{
|
||||
uint field_count= field_list.elements;
|
||||
uint blob_count= 0;
|
||||
Field **field;
|
||||
create_field *cdef; /* column definition */
|
||||
Create_field *cdef; /* column definition */
|
||||
uint record_length= 0;
|
||||
uint null_count= 0; /* number of columns which may be null */
|
||||
uint null_pack_length; /* NULL representation array length */
|
||||
|
@ -10116,7 +10116,7 @@ TABLE *create_virtual_tmp_table(THD *thd, List<create_field> &field_list)
|
|||
setup_tmp_table_column_bitmaps(table, bitmaps);
|
||||
|
||||
/* Create all fields and calculate the total length of record */
|
||||
List_iterator_fast<create_field> it(field_list);
|
||||
List_iterator_fast<Create_field> it(field_list);
|
||||
while ((cdef= it++))
|
||||
{
|
||||
*field= make_field(share, 0, cdef->length,
|
||||
|
|
|
@ -34,13 +34,13 @@ const char *primary_key_name="PRIMARY";
|
|||
static bool check_if_keyname_exists(const char *name,KEY *start, KEY *end);
|
||||
static char *make_unique_key_name(const char *field_name,KEY *start,KEY *end);
|
||||
static int copy_data_between_tables(TABLE *from,TABLE *to,
|
||||
List<create_field> &create, bool ignore,
|
||||
List<Create_field> &create, bool ignore,
|
||||
uint order_num, ORDER *order,
|
||||
ha_rows *copied,ha_rows *deleted,
|
||||
enum enum_enable_or_disable keys_onoff,
|
||||
bool error_if_not_empty);
|
||||
|
||||
static bool prepare_blob_field(THD *thd, create_field *sql_field);
|
||||
static bool prepare_blob_field(THD *thd, Create_field *sql_field);
|
||||
static bool check_engine(THD *, const char *, HA_CREATE_INFO *);
|
||||
static bool
|
||||
mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
|
||||
|
@ -1939,7 +1939,7 @@ void calculate_interval_lengths(CHARSET_INFO *cs, TYPELIB *interval,
|
|||
table_flags table flags
|
||||
|
||||
DESCRIPTION
|
||||
This function prepares a create_field instance.
|
||||
This function prepares a Create_field instance.
|
||||
Fields such as pack_flag are valid after this call.
|
||||
|
||||
RETURN VALUES
|
||||
|
@ -1947,7 +1947,7 @@ void calculate_interval_lengths(CHARSET_INFO *cs, TYPELIB *interval,
|
|||
1 Error
|
||||
*/
|
||||
|
||||
int prepare_create_field(create_field *sql_field,
|
||||
int prepare_create_field(Create_field *sql_field,
|
||||
uint *blob_columns,
|
||||
int *timestamps, int *timestamps_with_niladic,
|
||||
longlong table_flags)
|
||||
|
@ -2138,7 +2138,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
|
|||
uint *key_count, int select_field_count)
|
||||
{
|
||||
const char *key_name;
|
||||
create_field *sql_field,*dup_field;
|
||||
Create_field *sql_field,*dup_field;
|
||||
uint field,null_fields,blob_columns,max_key_length;
|
||||
ulong record_offset= 0;
|
||||
KEY *key_info;
|
||||
|
@ -2146,8 +2146,8 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
|
|||
int timestamps= 0, timestamps_with_niladic= 0;
|
||||
int field_no,dup_no;
|
||||
int select_field_pos,auto_increment=0;
|
||||
List_iterator<create_field> it(alter_info->create_list);
|
||||
List_iterator<create_field> it2(alter_info->create_list);
|
||||
List_iterator<Create_field> it(alter_info->create_list);
|
||||
List_iterator<Create_field> it2(alter_info->create_list);
|
||||
uint total_uneven_bit_length= 0;
|
||||
DBUG_ENTER("mysql_prepare_create_table");
|
||||
|
||||
|
@ -2201,7 +2201,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
|
|||
sql_field->sql_type == MYSQL_TYPE_ENUM))
|
||||
{
|
||||
/*
|
||||
Starting from 5.1 we work here with a copy of create_field
|
||||
Starting from 5.1 we work here with a copy of Create_field
|
||||
created by the caller, not with the instance that was
|
||||
originally created during parsing. It's OK to create
|
||||
a temporary item and initialize with it a member of the
|
||||
|
@ -2492,7 +2492,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
|
|||
if (key->type == Key::FOREIGN_KEY)
|
||||
{
|
||||
fk_key_count++;
|
||||
foreign_key *fk_key= (foreign_key*) key;
|
||||
Foreign_key *fk_key= (Foreign_key*) key;
|
||||
if (fk_key->ref_columns.elements &&
|
||||
fk_key->ref_columns.elements != fk_key->columns.elements)
|
||||
{
|
||||
|
@ -2576,7 +2576,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
|
|||
for (; (key=key_iterator++) ; key_number++)
|
||||
{
|
||||
uint key_length=0;
|
||||
key_part_spec *column;
|
||||
Key_part_spec *column;
|
||||
|
||||
if (key->name == ignore_key)
|
||||
{
|
||||
|
@ -2685,12 +2685,12 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
|
|||
if (key_info->block_size)
|
||||
key_info->flags|= HA_USES_BLOCK_SIZE;
|
||||
|
||||
List_iterator<key_part_spec> cols(key->columns), cols2(key->columns);
|
||||
List_iterator<Key_part_spec> cols(key->columns), cols2(key->columns);
|
||||
CHARSET_INFO *ft_key_charset=0; // for FULLTEXT
|
||||
for (uint column_nr=0 ; (column=cols++) ; column_nr++)
|
||||
{
|
||||
uint length;
|
||||
key_part_spec *dup_column;
|
||||
Key_part_spec *dup_column;
|
||||
|
||||
it.rewind();
|
||||
field=0;
|
||||
|
@ -3000,7 +3000,7 @@ static void set_table_default_charset(THD *thd,
|
|||
In this case the error is given
|
||||
*/
|
||||
|
||||
static bool prepare_blob_field(THD *thd, create_field *sql_field)
|
||||
static bool prepare_blob_field(THD *thd, Create_field *sql_field)
|
||||
{
|
||||
DBUG_ENTER("prepare_blob_field");
|
||||
|
||||
|
@ -3041,7 +3041,7 @@ static bool prepare_blob_field(THD *thd, create_field *sql_field)
|
|||
|
||||
|
||||
/*
|
||||
Preparation of create_field for SP function return values.
|
||||
Preparation of Create_field for SP function return values.
|
||||
Based on code used in the inner loop of mysql_prepare_create_table()
|
||||
above.
|
||||
|
||||
|
@ -3055,7 +3055,7 @@ static bool prepare_blob_field(THD *thd, create_field *sql_field)
|
|||
|
||||
*/
|
||||
|
||||
void sp_prepare_create_field(THD *thd, create_field *sql_field)
|
||||
void sp_prepare_create_field(THD *thd, Create_field *sql_field)
|
||||
{
|
||||
if (sql_field->sql_type == MYSQL_TYPE_SET ||
|
||||
sql_field->sql_type == MYSQL_TYPE_ENUM)
|
||||
|
@ -4950,8 +4950,8 @@ compare_tables(TABLE *table,
|
|||
Field **f_ptr, *field;
|
||||
uint changes= 0, tmp;
|
||||
uint key_count;
|
||||
List_iterator_fast<create_field> new_field_it(alter_info->create_list);
|
||||
create_field *new_field;
|
||||
List_iterator_fast<Create_field> new_field_it(alter_info->create_list);
|
||||
Create_field *new_field;
|
||||
KEY_PART_INFO *key_part;
|
||||
KEY_PART_INFO *end;
|
||||
/*
|
||||
|
@ -5298,16 +5298,16 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
|
|||
Alter_info *alter_info)
|
||||
{
|
||||
/* New column definitions are added here */
|
||||
List<create_field> new_create_list;
|
||||
List<Create_field> new_create_list;
|
||||
/* New key definitions are added here */
|
||||
List<Key> new_key_list;
|
||||
List_iterator<Alter_drop> drop_it(alter_info->drop_list);
|
||||
List_iterator<create_field> def_it(alter_info->create_list);
|
||||
List_iterator<Create_field> def_it(alter_info->create_list);
|
||||
List_iterator<Alter_column> alter_it(alter_info->alter_list);
|
||||
List_iterator<Key> key_it(alter_info->key_list);
|
||||
List_iterator<create_field> find_it(new_create_list);
|
||||
List_iterator<create_field> field_it(new_create_list);
|
||||
List<key_part_spec> key_parts;
|
||||
List_iterator<Create_field> find_it(new_create_list);
|
||||
List_iterator<Create_field> field_it(new_create_list);
|
||||
List<Key_part_spec> key_parts;
|
||||
uint db_create_options= (table->s->db_create_options
|
||||
& ~(HA_OPTION_PACK_RECORD));
|
||||
uint used_fields= create_info->used_fields;
|
||||
|
@ -5347,7 +5347,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
|
|||
create_info->tablespace= tablespace;
|
||||
}
|
||||
restore_record(table, s->default_values); // Empty record for DEFAULT
|
||||
create_field *def;
|
||||
Create_field *def;
|
||||
|
||||
/*
|
||||
First collect all fields from table which isn't in drop_list
|
||||
|
@ -5403,7 +5403,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
|
|||
This field was not dropped and not changed, add it to the list
|
||||
for the new table.
|
||||
*/
|
||||
def= new create_field(field, field);
|
||||
def= new Create_field(field, field);
|
||||
new_create_list.push_back(def);
|
||||
alter_it.rewind(); // Change default if ALTER
|
||||
Alter_column *alter;
|
||||
|
@ -5458,7 +5458,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
|
|||
new_create_list.push_front(def);
|
||||
else
|
||||
{
|
||||
create_field *find;
|
||||
Create_field *find;
|
||||
find_it.rewind();
|
||||
while ((find=find_it++)) // Add new columns
|
||||
{
|
||||
|
@ -5516,7 +5516,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
|
|||
if (!key_part->field)
|
||||
continue; // Wrong field (from UNIREG)
|
||||
const char *key_part_name=key_part->field->field_name;
|
||||
create_field *cfield;
|
||||
Create_field *cfield;
|
||||
field_it.rewind();
|
||||
while ((cfield=field_it++))
|
||||
{
|
||||
|
@ -5558,7 +5558,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
|
|||
key_part_length= 0; // Use whole field
|
||||
}
|
||||
key_part_length /= key_part->field->charset()->mbmaxlen;
|
||||
key_parts.push_back(new key_part_spec(cfield->field_name,
|
||||
key_parts.push_back(new Key_part_spec(cfield->field_name,
|
||||
key_part_length));
|
||||
}
|
||||
if (key_parts.elements)
|
||||
|
@ -6763,7 +6763,7 @@ err_with_placeholders:
|
|||
|
||||
static int
|
||||
copy_data_between_tables(TABLE *from,TABLE *to,
|
||||
List<create_field> &create,
|
||||
List<Create_field> &create,
|
||||
bool ignore,
|
||||
uint order_num, ORDER *order,
|
||||
ha_rows *copied,
|
||||
|
@ -6817,8 +6817,8 @@ copy_data_between_tables(TABLE *from,TABLE *to,
|
|||
|
||||
save_sql_mode= thd->variables.sql_mode;
|
||||
|
||||
List_iterator<create_field> it(create);
|
||||
create_field *def;
|
||||
List_iterator<Create_field> it(create);
|
||||
Create_field *def;
|
||||
copy_end=copy;
|
||||
for (Field **ptr=to->field ; *ptr ; ptr++)
|
||||
{
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
static const LEX_STRING triggers_file_type=
|
||||
{ C_STRING_WITH_LEN("TRIGGERS") };
|
||||
|
||||
const char * const triggers_file_ext= ".TRG";
|
||||
const char * const TRG_EXT= ".TRG";
|
||||
|
||||
/*
|
||||
Table of .TRG file field descriptors.
|
||||
|
@ -79,7 +79,7 @@ struct st_trigname
|
|||
static const LEX_STRING trigname_file_type=
|
||||
{ C_STRING_WITH_LEN("TRIGGERNAME") };
|
||||
|
||||
const char * const trigname_file_ext= ".TRN";
|
||||
const char * const TRN_EXT= ".TRN";
|
||||
|
||||
static File_option trigname_file_parameters[]=
|
||||
{
|
||||
|
@ -132,6 +132,7 @@ private:
|
|||
LEX_STRING *trigger_table_value;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
Create or drop trigger for table.
|
||||
|
||||
|
@ -463,14 +464,14 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
|
|||
sql_create_definition_file() files handles renaming and backup of older
|
||||
versions
|
||||
*/
|
||||
file.length= build_table_filename(file_buff, FN_REFLEN-1,
|
||||
file.length= build_table_filename(file_buff, FN_REFLEN - 1,
|
||||
tables->db, tables->table_name,
|
||||
triggers_file_ext, 0);
|
||||
TRG_EXT, 0);
|
||||
file.str= file_buff;
|
||||
trigname_file.length= build_table_filename(trigname_buff, FN_REFLEN-1,
|
||||
tables->db,
|
||||
lex->spname->m_name.str,
|
||||
trigname_file_ext, 0);
|
||||
TRN_EXT, 0);
|
||||
trigname_file.str= trigname_buff;
|
||||
|
||||
/* Use the filesystem to enforce trigger namespace constraints. */
|
||||
|
@ -563,10 +564,13 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
|
|||
append_definer(thd, stmt_query, &definer_user, &definer_host);
|
||||
}
|
||||
|
||||
stmt_query->append(thd->lex->stmt_definition_begin,
|
||||
(char *) thd->lex->sphead->m_body_begin -
|
||||
thd->lex->stmt_definition_begin +
|
||||
thd->lex->sphead->m_body.length);
|
||||
LEX_STRING stmt_definition;
|
||||
stmt_definition.str= (char*) thd->lex->stmt_definition_begin;
|
||||
stmt_definition.length= thd->lex->stmt_definition_end
|
||||
- thd->lex->stmt_definition_begin;
|
||||
trim_whitespace(thd->charset(), & stmt_definition);
|
||||
|
||||
stmt_query->append(stmt_definition.str, stmt_definition.length);
|
||||
|
||||
trg_def->str= stmt_query->c_ptr();
|
||||
trg_def->length= stmt_query->length();
|
||||
|
@ -601,7 +605,7 @@ err_with_cleanup:
|
|||
static bool rm_trigger_file(char *path, const char *db,
|
||||
const char *table_name)
|
||||
{
|
||||
build_table_filename(path, FN_REFLEN-1, db, table_name, triggers_file_ext, 0);
|
||||
build_table_filename(path, FN_REFLEN-1, db, table_name, TRG_EXT, 0);
|
||||
return my_delete(path, MYF(MY_WME));
|
||||
}
|
||||
|
||||
|
@ -624,8 +628,7 @@ static bool rm_trigger_file(char *path, const char *db,
|
|||
static bool rm_trigname_file(char *path, const char *db,
|
||||
const char *trigger_name)
|
||||
{
|
||||
build_table_filename(path, FN_REFLEN-1,
|
||||
db, trigger_name, trigname_file_ext, 0);
|
||||
build_table_filename(path, FN_REFLEN - 1, db, trigger_name, TRN_EXT, 0);
|
||||
return my_delete(path, MYF(MY_WME));
|
||||
}
|
||||
|
||||
|
@ -650,8 +653,8 @@ static bool save_trigger_file(Table_triggers_list *triggers, const char *db,
|
|||
char file_buff[FN_REFLEN];
|
||||
LEX_STRING file;
|
||||
|
||||
file.length= build_table_filename(file_buff, FN_REFLEN-1, db, table_name,
|
||||
triggers_file_ext, 0);
|
||||
file.length= build_table_filename(file_buff, FN_REFLEN - 1, db, table_name,
|
||||
TRG_EXT, 0);
|
||||
file.str= file_buff;
|
||||
return sql_create_definition_file(NULL, &file, &triggers_file_type,
|
||||
(uchar*)triggers, triggers_file_parameters,
|
||||
|
@ -831,8 +834,8 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
|
|||
|
||||
DBUG_ENTER("Table_triggers_list::check_n_load");
|
||||
|
||||
path.length= build_table_filename(path_buff, FN_REFLEN-1,
|
||||
db, table_name, triggers_file_ext, 0);
|
||||
path.length= build_table_filename(path_buff, FN_REFLEN - 1,
|
||||
db, table_name, TRG_EXT, 0);
|
||||
path.str= path_buff;
|
||||
|
||||
// QQ: should we analyze errno somehow ?
|
||||
|
@ -978,12 +981,10 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
|
|||
thd->variables.sql_mode= (ulong)*trg_sql_mode;
|
||||
|
||||
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)
|
||||
if (parse_sql(thd, &lip))
|
||||
{
|
||||
/* Currently sphead is always deleted in case of a parse error */
|
||||
DBUG_ASSERT(lex.sphead == 0);
|
||||
|
@ -1032,7 +1033,11 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
|
|||
if (!(on_table_name= (LEX_STRING*) alloc_root(&table->mem_root,
|
||||
sizeof(LEX_STRING))))
|
||||
goto err_with_lex_cleanup;
|
||||
*on_table_name= lex.ident;
|
||||
|
||||
on_table_name->str= (char*) lex.raw_trg_on_table_name_begin;
|
||||
on_table_name->length= lex.raw_trg_on_table_name_end
|
||||
- lex.raw_trg_on_table_name_begin;
|
||||
|
||||
if (triggers->on_table_names_list.push_back(on_table_name, &table->mem_root))
|
||||
goto err_with_lex_cleanup;
|
||||
|
||||
|
@ -1101,7 +1106,7 @@ err_with_lex_cleanup:
|
|||
be merged into .FRM anyway.
|
||||
*/
|
||||
my_error(ER_WRONG_OBJECT, MYF(0),
|
||||
table_name, triggers_file_ext+1, "TRIGGER");
|
||||
table_name, TRG_EXT + 1, "TRIGGER");
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
|
||||
|
@ -1161,83 +1166,66 @@ bool Table_triggers_list::get_trigger_info(THD *thd, trg_event_type event,
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
/**
|
||||
Find trigger's table from trigger identifier and add it to
|
||||
the statement table list.
|
||||
|
||||
SYNOPSIS
|
||||
mysql_table_for_trigger()
|
||||
thd - current thread context
|
||||
trig - identifier for trigger
|
||||
if_exists - treat a not existing trigger as a warning if TRUE
|
||||
table - pointer to TABLE_LIST object for the table trigger (output)
|
||||
@param[in] thd Thread context.
|
||||
@param[in] trg_name Trigger name.
|
||||
@param[in] if_exists TRUE if SQL statement contains "IF EXISTS" clause.
|
||||
That means a warning instead of error should be
|
||||
thrown if trigger with given name does not exist.
|
||||
@param[out] table Pointer to TABLE_LIST object for the
|
||||
table trigger.
|
||||
|
||||
RETURN VALUE
|
||||
0 Success
|
||||
1 Error
|
||||
@return Operation status
|
||||
@retval FALSE On success.
|
||||
@retval TRUE Otherwise.
|
||||
*/
|
||||
|
||||
int
|
||||
add_table_for_trigger(THD *thd, sp_name *trig, bool if_exists,
|
||||
TABLE_LIST **table)
|
||||
bool add_table_for_trigger(THD *thd,
|
||||
sp_name *trg_name,
|
||||
bool if_exists,
|
||||
TABLE_LIST **table)
|
||||
{
|
||||
LEX *lex= thd->lex;
|
||||
char path_buff[FN_REFLEN];
|
||||
LEX_STRING path;
|
||||
File_parser *parser;
|
||||
struct st_trigname trigname;
|
||||
Handle_old_incorrect_trigger_table_hook trigger_table_hook(
|
||||
path_buff, &trigname.trigger_table);
|
||||
|
||||
char trn_path_buff[FN_REFLEN];
|
||||
LEX_STRING trn_path= { trn_path_buff, 0 };
|
||||
LEX_STRING tbl_name;
|
||||
|
||||
DBUG_ENTER("add_table_for_trigger");
|
||||
DBUG_ASSERT(table != NULL);
|
||||
|
||||
path.length= build_table_filename(path_buff, FN_REFLEN-1,
|
||||
trig->m_db.str, trig->m_name.str,
|
||||
trigname_file_ext, 0);
|
||||
path.str= path_buff;
|
||||
build_trn_path(thd, trg_name, &trn_path);
|
||||
|
||||
if (access(path_buff, F_OK))
|
||||
if (check_trn_exists(&trn_path))
|
||||
{
|
||||
if (if_exists)
|
||||
{
|
||||
push_warning_printf(thd,
|
||||
MYSQL_ERROR::WARN_LEVEL_NOTE,
|
||||
ER_TRG_DOES_NOT_EXIST,
|
||||
ER(ER_TRG_DOES_NOT_EXIST));
|
||||
MYSQL_ERROR::WARN_LEVEL_NOTE,
|
||||
ER_TRG_DOES_NOT_EXIST,
|
||||
ER(ER_TRG_DOES_NOT_EXIST));
|
||||
|
||||
*table= NULL;
|
||||
DBUG_RETURN(0);
|
||||
|
||||
DBUG_RETURN(FALSE);
|
||||
}
|
||||
|
||||
my_error(ER_TRG_DOES_NOT_EXIST, MYF(0));
|
||||
DBUG_RETURN(1);
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
|
||||
if (!(parser= sql_parse_prepare(&path, thd->mem_root, 1)))
|
||||
DBUG_RETURN(1);
|
||||
|
||||
if (!is_equal(&trigname_file_type, parser->type()))
|
||||
{
|
||||
my_error(ER_WRONG_OBJECT, MYF(0), trig->m_name.str, trigname_file_ext+1,
|
||||
"TRIGGERNAME");
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
|
||||
if (parser->parse((uchar*)&trigname, thd->mem_root,
|
||||
trigname_file_parameters, 1,
|
||||
&trigger_table_hook))
|
||||
DBUG_RETURN(1);
|
||||
if (load_table_name_for_trigger(thd, trg_name, &trn_path, &tbl_name))
|
||||
DBUG_RETURN(TRUE);
|
||||
|
||||
/* We need to reset statement table list to be PS/SP friendly. */
|
||||
lex->query_tables= 0;
|
||||
lex->query_tables_last= &lex->query_tables;
|
||||
*table= sp_add_to_query_tables(thd, lex, trig->m_db.str,
|
||||
trigname.trigger_table.str, TL_IGNORE);
|
||||
|
||||
if (! *table)
|
||||
DBUG_RETURN(1);
|
||||
*table= sp_add_to_query_tables(thd, lex, trg_name->m_db.str,
|
||||
tbl_name.str, TL_IGNORE);
|
||||
|
||||
DBUG_RETURN(0);
|
||||
DBUG_RETURN(*table ? FALSE : TRUE);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1348,7 +1336,12 @@ Table_triggers_list::change_table_name_in_triggers(THD *thd,
|
|||
|
||||
/* Construct CREATE TRIGGER statement with new table name. */
|
||||
buff.length(0);
|
||||
|
||||
/* WARNING: 'on_table_name' is supposed to point inside 'def' */
|
||||
DBUG_ASSERT(on_table_name->str > def->str);
|
||||
DBUG_ASSERT(on_table_name->str < (def->str + def->length));
|
||||
before_on_len= on_table_name->str - def->str;
|
||||
|
||||
buff.append(def->str, before_on_len);
|
||||
buff.append(STRING_WITH_LEN("ON "));
|
||||
append_identifier(thd, &buff, new_table_name->str, new_table_name->length);
|
||||
|
@ -1416,7 +1409,7 @@ Table_triggers_list::change_table_name_in_trignames(const char *db_name,
|
|||
{
|
||||
trigname_file.length= build_table_filename(trigname_buff, FN_REFLEN-1,
|
||||
db_name, trigger->str,
|
||||
trigname_file_ext, 0);
|
||||
TRN_EXT, 0);
|
||||
trigname_file.str= trigname_buff;
|
||||
|
||||
trigname.trigger_table= *new_table_name;
|
||||
|
@ -1525,77 +1518,54 @@ end:
|
|||
}
|
||||
|
||||
|
||||
bool Table_triggers_list::process_triggers(THD *thd, trg_event_type event,
|
||||
/**
|
||||
Execute trigger for given (event, time) pair.
|
||||
|
||||
The operation executes trigger for the specified event (insert, update,
|
||||
delete) and time (after, before) if it is set.
|
||||
|
||||
@param thd
|
||||
@param event
|
||||
@param time_type,
|
||||
@param old_row_is_record1
|
||||
|
||||
@return Error status.
|
||||
@retval FALSE on success.
|
||||
@retval TRUE on error.
|
||||
*/
|
||||
|
||||
bool Table_triggers_list::process_triggers(THD *thd,
|
||||
trg_event_type event,
|
||||
trg_action_time_type time_type,
|
||||
bool old_row_is_record1)
|
||||
{
|
||||
bool err_status= FALSE;
|
||||
sp_head *sp_trigger= bodies[event][time_type];
|
||||
bool err_status;
|
||||
Sub_statement_state statement_state;
|
||||
|
||||
if (sp_trigger)
|
||||
if (!bodies[event][time_type])
|
||||
return FALSE;
|
||||
|
||||
if (old_row_is_record1)
|
||||
{
|
||||
Sub_statement_state statement_state;
|
||||
|
||||
if (old_row_is_record1)
|
||||
{
|
||||
old_field= record1_field;
|
||||
new_field= trigger_table->field;
|
||||
}
|
||||
else
|
||||
{
|
||||
new_field= record1_field;
|
||||
old_field= trigger_table->field;
|
||||
}
|
||||
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
||||
Security_context *sctx= &sp_trigger->m_security_ctx;
|
||||
Security_context *save_ctx= NULL;
|
||||
|
||||
|
||||
if (sp_trigger->m_chistics->suid != SP_IS_NOT_SUID &&
|
||||
sctx->change_security_context(thd,
|
||||
&sp_trigger->m_definer_user,
|
||||
&sp_trigger->m_definer_host,
|
||||
&sp_trigger->m_db,
|
||||
&save_ctx))
|
||||
return TRUE;
|
||||
|
||||
/*
|
||||
Fetch information about table-level privileges to GRANT_INFO structure for
|
||||
subject table. Check of privileges that will use it and information about
|
||||
column-level privileges will happen in Item_trigger_field::fix_fields().
|
||||
*/
|
||||
|
||||
fill_effective_table_privileges(thd,
|
||||
&subject_table_grants[event][time_type],
|
||||
trigger_table->s->db.str,
|
||||
trigger_table->s->table_name.str);
|
||||
|
||||
/* Check that the definer has TRIGGER privilege on the subject table. */
|
||||
|
||||
if (!(subject_table_grants[event][time_type].privilege & TRIGGER_ACL))
|
||||
{
|
||||
char priv_desc[128];
|
||||
get_privilege_desc(priv_desc, sizeof(priv_desc), TRIGGER_ACL);
|
||||
|
||||
my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0), priv_desc,
|
||||
thd->security_ctx->priv_user, thd->security_ctx->host_or_ip,
|
||||
trigger_table->s->table_name.str);
|
||||
|
||||
sctx->restore_security_context(thd, save_ctx);
|
||||
return TRUE;
|
||||
}
|
||||
#endif // NO_EMBEDDED_ACCESS_CHECKS
|
||||
|
||||
thd->reset_sub_statement_state(&statement_state, SUB_STMT_TRIGGER);
|
||||
err_status= sp_trigger->execute_trigger
|
||||
(thd, trigger_table->s->db.str, trigger_table->s->table_name.str,
|
||||
&subject_table_grants[event][time_type]);
|
||||
thd->restore_sub_statement_state(&statement_state);
|
||||
|
||||
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
||||
sctx->restore_security_context(thd, save_ctx);
|
||||
#endif // NO_EMBEDDED_ACCESS_CHECKS
|
||||
old_field= record1_field;
|
||||
new_field= trigger_table->field;
|
||||
}
|
||||
else
|
||||
{
|
||||
new_field= record1_field;
|
||||
old_field= trigger_table->field;
|
||||
}
|
||||
|
||||
thd->reset_sub_statement_state(&statement_state, SUB_STMT_TRIGGER);
|
||||
|
||||
err_status=
|
||||
bodies[event][time_type]->execute_trigger(
|
||||
thd,
|
||||
&trigger_table->s->db,
|
||||
&trigger_table->s->table_name,
|
||||
&subject_table_grants[event][time_type]);
|
||||
|
||||
thd->restore_sub_statement_state(&statement_state);
|
||||
|
||||
return err_status;
|
||||
}
|
||||
|
@ -1737,3 +1707,95 @@ process_unknown_string(char *&unknown_key, uchar* base, MEM_ROOT *mem_root,
|
|||
}
|
||||
DBUG_RETURN(FALSE);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Contruct path to TRN-file.
|
||||
|
||||
@param thd[in] Thread context.
|
||||
@param trg_name[in] Trigger name.
|
||||
@param trn_path[out] Variable to store constructed path
|
||||
*/
|
||||
|
||||
void build_trn_path(THD *thd, const sp_name *trg_name, LEX_STRING *trn_path)
|
||||
{
|
||||
/* Construct path to the TRN-file. */
|
||||
|
||||
trn_path->length= build_table_filename(trn_path->str,
|
||||
FN_REFLEN - 1,
|
||||
trg_name->m_db.str,
|
||||
trg_name->m_name.str,
|
||||
TRN_EXT,
|
||||
0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Check if TRN-file exists.
|
||||
|
||||
@return
|
||||
@retval TRUE if TRN-file does not exist.
|
||||
@retval FALSE if TRN-file exists.
|
||||
*/
|
||||
|
||||
bool check_trn_exists(const LEX_STRING *trn_path)
|
||||
{
|
||||
return access(trn_path->str, F_OK) != 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Retrieve table name for given trigger.
|
||||
|
||||
@param thd[in] Thread context.
|
||||
@param trg_name[in] Trigger name.
|
||||
@param trn_path[in] Path to the corresponding TRN-file.
|
||||
@param tbl_name[out] Variable to store retrieved table name.
|
||||
|
||||
@return Error status.
|
||||
@retval FALSE on success.
|
||||
@retval TRUE if table name could not be retrieved.
|
||||
*/
|
||||
|
||||
bool load_table_name_for_trigger(THD *thd,
|
||||
const sp_name *trg_name,
|
||||
const LEX_STRING *trn_path,
|
||||
LEX_STRING *tbl_name)
|
||||
{
|
||||
File_parser *parser;
|
||||
struct st_trigname trn_data;
|
||||
|
||||
Handle_old_incorrect_trigger_table_hook trigger_table_hook(
|
||||
trn_path->str,
|
||||
&trn_data.trigger_table);
|
||||
|
||||
DBUG_ENTER("load_table_name_for_trigger");
|
||||
|
||||
/* Parse the TRN-file. */
|
||||
|
||||
if (!(parser= sql_parse_prepare(trn_path, thd->mem_root, TRUE)))
|
||||
DBUG_RETURN(TRUE);
|
||||
|
||||
if (!is_equal(&trigname_file_type, parser->type()))
|
||||
{
|
||||
my_error(ER_WRONG_OBJECT, MYF(0),
|
||||
trg_name->m_name.str,
|
||||
TRN_EXT + 1,
|
||||
"TRIGGERNAME");
|
||||
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
|
||||
if (parser->parse((uchar*) &trn_data, thd->mem_root,
|
||||
trigname_file_parameters, 1,
|
||||
&trigger_table_hook))
|
||||
DBUG_RETURN(TRUE);
|
||||
|
||||
/* Copy trigger table name. */
|
||||
|
||||
*tbl_name= trn_data.trigger_table;
|
||||
|
||||
/* That's all. */
|
||||
|
||||
DBUG_RETURN(FALSE);
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
/*
|
||||
This class holds all information about triggers of table.
|
||||
|
||||
QQ: Will it be merged into TABLE in future ?
|
||||
QQ: Will it be merged into TABLE in the future ?
|
||||
*/
|
||||
|
||||
class Table_triggers_list: public Sql_alloc
|
||||
|
@ -143,6 +143,17 @@ private:
|
|||
extern const LEX_STRING trg_action_time_type_names[];
|
||||
extern const LEX_STRING trg_event_type_names[];
|
||||
|
||||
int
|
||||
add_table_for_trigger(THD *thd, sp_name *trig, bool if_exists,
|
||||
TABLE_LIST **table);
|
||||
bool add_table_for_trigger(THD *thd,
|
||||
sp_name *trg_name,
|
||||
bool continue_if_not_exist,
|
||||
TABLE_LIST **table);
|
||||
|
||||
void build_trn_path(THD *thd, const sp_name *trg_name, LEX_STRING *trn_path);
|
||||
|
||||
bool check_trn_exists(const LEX_STRING *trn_path);
|
||||
|
||||
bool load_table_name_for_trigger(THD *thd,
|
||||
const sp_name *trg_name,
|
||||
const LEX_STRING *trn_path,
|
||||
LEX_STRING *tbl_name);
|
||||
|
||||
|
|
|
@ -690,7 +690,6 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view,
|
|||
char md5[MD5_BUFF_LENGTH];
|
||||
bool can_be_merged;
|
||||
char dir_buff[FN_REFLEN], path_buff[FN_REFLEN];
|
||||
const char *endp;
|
||||
LEX_STRING dir, file, path;
|
||||
int error= 0;
|
||||
DBUG_ENTER("mysql_register_view");
|
||||
|
@ -708,10 +707,12 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view,
|
|||
/* fill structure */
|
||||
view->query.str= str.c_ptr_safe();
|
||||
view->query.length= str.length();
|
||||
view->source.str= thd->query + thd->lex->create_view_select_start;
|
||||
endp= view->source.str;
|
||||
endp= skip_rear_comments(thd->charset(), endp, thd->query + thd->query_length);
|
||||
view->source.length= endp - view->source.str;
|
||||
|
||||
view->source.str= (char*) thd->lex->create_view_select_start;
|
||||
view->source.length= (thd->lex->create_view_select_end
|
||||
- thd->lex->create_view_select_start);
|
||||
trim_whitespace(thd->charset(), & view->source);
|
||||
|
||||
view->file_version= 1;
|
||||
view->calc_md5(md5);
|
||||
view->md5.str= md5;
|
||||
|
@ -892,7 +893,7 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
|
|||
LEX *old_lex, *lex;
|
||||
Query_arena *arena, backup;
|
||||
TABLE_LIST *top_view= table->top_table();
|
||||
int res;
|
||||
bool res;
|
||||
bool result, view_is_mergeable;
|
||||
TABLE_LIST *view_main_select_tables;
|
||||
DBUG_ENTER("mysql_make_view");
|
||||
|
@ -1004,7 +1005,6 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
|
|||
|
||||
{
|
||||
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;
|
||||
|
@ -1038,7 +1038,7 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
|
|||
MODE_IGNORE_SPACE | MODE_NO_BACKSLASH_ESCAPES);
|
||||
CHARSET_INFO *save_cs= thd->variables.character_set_client;
|
||||
thd->variables.character_set_client= system_charset_info;
|
||||
res= MYSQLparse((void *)thd);
|
||||
res= parse_sql(thd, &lip);
|
||||
|
||||
if ((old_lex->sql_command == SQLCOM_SHOW_FIELDS) ||
|
||||
(old_lex->sql_command == SQLCOM_SHOW_CREATE))
|
||||
|
@ -1047,7 +1047,7 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
|
|||
thd->variables.character_set_client= save_cs;
|
||||
thd->variables.sql_mode= save_mode;
|
||||
}
|
||||
if (!res && !thd->is_fatal_error)
|
||||
if (!res)
|
||||
{
|
||||
TABLE_LIST *view_tables= lex->query_tables;
|
||||
TABLE_LIST *view_tables_tail= 0;
|
||||
|
|
160
sql/sql_yacc.yy
160
sql/sql_yacc.yy
|
@ -106,7 +106,7 @@ void my_parse_error(const char *s)
|
|||
THD *thd= current_thd;
|
||||
Lex_input_stream *lip= thd->m_lip;
|
||||
|
||||
const char *yytext= lip->tok_start;
|
||||
const char *yytext= lip->get_tok_start();
|
||||
/* Push an error into the error stack */
|
||||
my_printf_error(ER_PARSE_ERROR, ER(ER_PARSE_ERROR), MYF(0), s,
|
||||
(yytext ? yytext : ""),
|
||||
|
@ -456,7 +456,7 @@ Item* handle_sql2003_note184_exception(THD *thd, Item* left, bool equal,
|
|||
List<Item> *item_list;
|
||||
List<String> *string_list;
|
||||
String *string;
|
||||
key_part_spec *key_part;
|
||||
Key_part_spec *key_part;
|
||||
TABLE_LIST *table_list;
|
||||
udf_func *udf;
|
||||
LEX_USER *lex_user;
|
||||
|
@ -1872,10 +1872,7 @@ ev_sql_stmt:
|
|||
bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics));
|
||||
lex->sphead->m_chistics= &lex->sp_chistics;
|
||||
|
||||
lex->sphead->m_body_begin= lip->ptr;
|
||||
|
||||
lex->event_parse_data->body_begin= lip->ptr;
|
||||
|
||||
lex->sphead->set_body_begin_ptr(lip, lip->get_cpp_ptr());
|
||||
}
|
||||
ev_sql_stmt_inner
|
||||
{
|
||||
|
@ -1888,7 +1885,7 @@ ev_sql_stmt:
|
|||
|
||||
lex->sp_chistics.suid= SP_IS_SUID; //always the definer!
|
||||
|
||||
lex->event_parse_data->init_body(thd);
|
||||
lex->event_parse_data->body_changed= TRUE;
|
||||
}
|
||||
;
|
||||
|
||||
|
@ -1986,6 +1983,7 @@ create_function_tail:
|
|||
LEX *lex= thd->lex;
|
||||
Lex_input_stream *lip= thd->m_lip;
|
||||
sp_head *sp;
|
||||
const char* tmp_param_begin;
|
||||
|
||||
/*
|
||||
First check if AGGREGATE was used, in that case it's a
|
||||
|
@ -2017,7 +2015,10 @@ create_function_tail:
|
|||
*/
|
||||
$<ulong_num>$= thd->client_capabilities & CLIENT_MULTI_QUERIES;
|
||||
thd->client_capabilities &= ~CLIENT_MULTI_QUERIES;
|
||||
lex->sphead->m_param_begin= lip->tok_start+1;
|
||||
|
||||
tmp_param_begin= lip->get_cpp_tok_start();
|
||||
tmp_param_begin++;
|
||||
lex->sphead->m_param_begin= tmp_param_begin;
|
||||
}
|
||||
sp_fdparam_list ')'
|
||||
{
|
||||
|
@ -2025,7 +2026,7 @@ create_function_tail:
|
|||
LEX *lex= thd->lex;
|
||||
Lex_input_stream *lip= thd->m_lip;
|
||||
|
||||
lex->sphead->m_param_end= lip->tok_start;
|
||||
lex->sphead->m_param_end= lip->get_cpp_tok_start();
|
||||
}
|
||||
RETURNS_SYM
|
||||
{
|
||||
|
@ -2065,7 +2066,7 @@ create_function_tail:
|
|||
Lex_input_stream *lip= thd->m_lip;
|
||||
|
||||
lex->sphead->m_chistics= &lex->sp_chistics;
|
||||
lex->sphead->m_body_begin= lip->tok_start;
|
||||
lex->sphead->set_body_begin_ptr(lip, lip->get_cpp_tok_start());
|
||||
}
|
||||
sp_proc_stmt
|
||||
{
|
||||
|
@ -2676,7 +2677,7 @@ sp_proc_stmt_statement:
|
|||
Lex_input_stream *lip= thd->m_lip;
|
||||
|
||||
lex->sphead->reset_lex(thd);
|
||||
lex->sphead->m_tmp_query= lip->tok_start;
|
||||
lex->sphead->m_tmp_query= lip->get_tok_start();
|
||||
}
|
||||
statement
|
||||
{
|
||||
|
@ -2709,9 +2710,9 @@ sp_proc_stmt_statement:
|
|||
lex->tok_end otherwise.
|
||||
*/
|
||||
if (yychar == YYEMPTY)
|
||||
i->m_query.length= lip->ptr - sp->m_tmp_query;
|
||||
i->m_query.length= lip->get_ptr() - sp->m_tmp_query;
|
||||
else
|
||||
i->m_query.length= lip->tok_end - sp->m_tmp_query;
|
||||
i->m_query.length= lip->get_tok_end() - sp->m_tmp_query;
|
||||
i->m_query.str= strmake_root(thd->mem_root,
|
||||
sp->m_tmp_query,
|
||||
i->m_query.length);
|
||||
|
@ -4498,7 +4499,7 @@ key_def:
|
|||
{
|
||||
LEX *lex=Lex;
|
||||
const char *key_name= $4 ? $4 : $1;
|
||||
Key *key= new foreign_key(key_name, lex->col_list,
|
||||
Key *key= new Foreign_key(key_name, lex->col_list,
|
||||
$8,
|
||||
lex->ref_list,
|
||||
lex->fk_delete_opt,
|
||||
|
@ -4925,8 +4926,8 @@ opt_ref_list:
|
|||
| '(' ref_list ')' opt_on_delete {};
|
||||
|
||||
ref_list:
|
||||
ref_list ',' ident { Lex->ref_list.push_back(new key_part_spec($3.str)); }
|
||||
| ident { Lex->ref_list.push_back(new key_part_spec($1.str)); };
|
||||
ref_list ',' ident { Lex->ref_list.push_back(new Key_part_spec($3.str)); }
|
||||
| ident { Lex->ref_list.push_back(new Key_part_spec($1.str)); };
|
||||
|
||||
|
||||
opt_on_delete:
|
||||
|
@ -4940,16 +4941,16 @@ opt_on_delete_list:
|
|||
opt_on_delete_item:
|
||||
ON DELETE_SYM delete_option { Lex->fk_delete_opt= $3; }
|
||||
| ON UPDATE_SYM delete_option { Lex->fk_update_opt= $3; }
|
||||
| MATCH FULL { Lex->fk_match_option= foreign_key::FK_MATCH_FULL; }
|
||||
| MATCH PARTIAL { Lex->fk_match_option= foreign_key::FK_MATCH_PARTIAL; }
|
||||
| MATCH SIMPLE_SYM { Lex->fk_match_option= foreign_key::FK_MATCH_SIMPLE; };
|
||||
| MATCH FULL { Lex->fk_match_option= Foreign_key::FK_MATCH_FULL; }
|
||||
| MATCH PARTIAL { Lex->fk_match_option= Foreign_key::FK_MATCH_PARTIAL; }
|
||||
| MATCH SIMPLE_SYM { Lex->fk_match_option= Foreign_key::FK_MATCH_SIMPLE; };
|
||||
|
||||
delete_option:
|
||||
RESTRICT { $$= (int) foreign_key::FK_OPTION_RESTRICT; }
|
||||
| CASCADE { $$= (int) foreign_key::FK_OPTION_CASCADE; }
|
||||
| SET NULL_SYM { $$= (int) foreign_key::FK_OPTION_SET_NULL; }
|
||||
| NO_SYM ACTION { $$= (int) foreign_key::FK_OPTION_NO_ACTION; }
|
||||
| SET DEFAULT { $$= (int) foreign_key::FK_OPTION_DEFAULT; };
|
||||
RESTRICT { $$= (int) Foreign_key::FK_OPTION_RESTRICT; }
|
||||
| CASCADE { $$= (int) Foreign_key::FK_OPTION_CASCADE; }
|
||||
| SET NULL_SYM { $$= (int) Foreign_key::FK_OPTION_SET_NULL; }
|
||||
| NO_SYM ACTION { $$= (int) Foreign_key::FK_OPTION_NO_ACTION; }
|
||||
| SET DEFAULT { $$= (int) Foreign_key::FK_OPTION_DEFAULT; };
|
||||
|
||||
key_type:
|
||||
key_or_index { $$= Key::MULTIPLE; }
|
||||
|
@ -5061,7 +5062,7 @@ key_list:
|
|||
| key_part order_dir { Lex->col_list.push_back($1); };
|
||||
|
||||
key_part:
|
||||
ident { $$=new key_part_spec($1.str); }
|
||||
ident { $$=new Key_part_spec($1.str); }
|
||||
| ident '(' NUM ')'
|
||||
{
|
||||
int key_part_len= atoi($3.str);
|
||||
|
@ -5069,7 +5070,7 @@ key_part:
|
|||
{
|
||||
my_error(ER_KEY_PART_0, MYF(0), $1.str);
|
||||
}
|
||||
$$=new key_part_spec($1.str,(uint) key_part_len);
|
||||
$$=new Key_part_spec($1.str,(uint) key_part_len);
|
||||
};
|
||||
|
||||
opt_ident:
|
||||
|
@ -6225,14 +6226,14 @@ remember_name:
|
|||
{
|
||||
THD *thd= YYTHD;
|
||||
Lex_input_stream *lip= thd->m_lip;
|
||||
$$= (char*) lip->tok_start;
|
||||
$$= (char*) lip->get_cpp_tok_start();
|
||||
};
|
||||
|
||||
remember_end:
|
||||
{
|
||||
THD *thd= YYTHD;
|
||||
Lex_input_stream *lip= thd->m_lip;
|
||||
$$=(char*) lip->tok_end;
|
||||
$$= (char*) lip->get_cpp_tok_end();
|
||||
};
|
||||
|
||||
select_item2:
|
||||
|
@ -7999,16 +8000,14 @@ procedure_list2:
|
|||
| procedure_item;
|
||||
|
||||
procedure_item:
|
||||
remember_name expr
|
||||
remember_name expr remember_end
|
||||
{
|
||||
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*) lip->tok_end - $1),
|
||||
thd->charset());
|
||||
$2->set_name($1, (uint) ($3 - $1), thd->charset());
|
||||
}
|
||||
;
|
||||
|
||||
|
@ -9108,7 +9107,7 @@ load: LOAD DATA_SYM
|
|||
my_error(ER_SP_BADSTATEMENT, MYF(0), "LOAD DATA");
|
||||
MYSQL_YYABORT;
|
||||
}
|
||||
lex->fname_start= lip->ptr;
|
||||
lex->fname_start= lip->get_ptr();
|
||||
}
|
||||
load_data
|
||||
{}
|
||||
|
@ -9145,7 +9144,7 @@ load_data:
|
|||
THD *thd= YYTHD;
|
||||
LEX *lex= thd->lex;
|
||||
Lex_input_stream *lip= thd->m_lip;
|
||||
lex->fname_end= lip->ptr;
|
||||
lex->fname_end= lip->get_ptr();
|
||||
}
|
||||
TABLE_SYM table_ident
|
||||
{
|
||||
|
@ -9334,7 +9333,7 @@ param_marker:
|
|||
my_error(ER_VIEW_SELECT_VARIABLE, MYF(0));
|
||||
MYSQL_YYABORT;
|
||||
}
|
||||
item= new Item_param((uint) (lip->tok_start - thd->query));
|
||||
item= new Item_param((uint) (lip->get_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));
|
||||
|
@ -9467,7 +9466,7 @@ simple_ident:
|
|||
|
||||
Item_splocal *splocal;
|
||||
splocal= new Item_splocal($1, spv->offset, spv->type,
|
||||
lip->tok_start_prev -
|
||||
lip->get_tok_start_prev() -
|
||||
lex->sphead->m_tmp_query);
|
||||
#ifndef DBUG_OFF
|
||||
if (splocal)
|
||||
|
@ -10143,7 +10142,7 @@ option_type_value:
|
|||
lex->option_type=OPT_SESSION;
|
||||
lex->var_list.empty();
|
||||
lex->one_shot_set= 0;
|
||||
lex->sphead->m_tmp_query= lip->tok_start;
|
||||
lex->sphead->m_tmp_query= lip->get_tok_start();
|
||||
}
|
||||
}
|
||||
ext_option_value
|
||||
|
@ -10176,9 +10175,9 @@ option_type_value:
|
|||
lip->tok_end otherwise.
|
||||
*/
|
||||
if (yychar == YYEMPTY)
|
||||
qbuff.length= lip->ptr - sp->m_tmp_query;
|
||||
qbuff.length= lip->get_ptr() - sp->m_tmp_query;
|
||||
else
|
||||
qbuff.length= lip->tok_end - sp->m_tmp_query;
|
||||
qbuff.length= lip->get_tok_end() - sp->m_tmp_query;
|
||||
|
||||
if (!(qbuff.str= (char*) alloc_root(thd->mem_root,
|
||||
qbuff.length + 5)))
|
||||
|
@ -11360,7 +11359,7 @@ view_tail:
|
|||
if (!lex->select_lex.add_table_to_list(thd, $3, NULL, TL_OPTION_UPDATING))
|
||||
MYSQL_YYABORT;
|
||||
}
|
||||
view_list_opt AS view_select view_check_option
|
||||
view_list_opt AS view_select
|
||||
{}
|
||||
;
|
||||
|
||||
|
@ -11385,40 +11384,32 @@ view_list:
|
|||
|
||||
view_select:
|
||||
{
|
||||
THD *thd= YYTHD;
|
||||
LEX *lex= Lex;
|
||||
Lex_input_stream *lip= thd->m_lip;
|
||||
lex->parsing_options.allows_variable= FALSE;
|
||||
lex->parsing_options.allows_select_into= FALSE;
|
||||
lex->parsing_options.allows_select_procedure= FALSE;
|
||||
lex->parsing_options.allows_derived= FALSE;
|
||||
}
|
||||
view_select_aux
|
||||
lex->create_view_select_start= lip->get_cpp_ptr();
|
||||
}
|
||||
view_select_aux view_check_option
|
||||
{
|
||||
THD *thd= YYTHD;
|
||||
LEX *lex= Lex;
|
||||
Lex_input_stream *lip= thd->m_lip;
|
||||
lex->parsing_options.allows_variable= TRUE;
|
||||
lex->parsing_options.allows_select_into= TRUE;
|
||||
lex->parsing_options.allows_select_procedure= TRUE;
|
||||
lex->parsing_options.allows_derived= TRUE;
|
||||
lex->create_view_select_end= lip->get_cpp_ptr();
|
||||
}
|
||||
;
|
||||
|
||||
view_select_aux:
|
||||
SELECT_SYM remember_name select_init2
|
||||
{
|
||||
THD *thd= YYTHD;
|
||||
LEX *lex= thd->lex;
|
||||
const char *stmt_beg= (lex->sphead ?
|
||||
lex->sphead->m_tmp_query : thd->query);
|
||||
lex->create_view_select_start= $2 - stmt_beg;
|
||||
}
|
||||
| '(' remember_name select_paren ')' union_opt
|
||||
{
|
||||
THD *thd= YYTHD;
|
||||
LEX *lex= thd->lex;
|
||||
const char *stmt_beg= (lex->sphead ?
|
||||
lex->sphead->m_tmp_query : thd->query);
|
||||
lex->create_view_select_start= $2 - stmt_beg;
|
||||
}
|
||||
;
|
||||
SELECT_SYM select_init2
|
||||
| '(' select_paren ')' union_opt
|
||||
;
|
||||
|
||||
view_check_option:
|
||||
/* empty */
|
||||
|
@ -11438,9 +11429,31 @@ view_check_option:
|
|||
**************************************************************************/
|
||||
|
||||
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
|
||||
{
|
||||
TRIGGER_SYM
|
||||
remember_name
|
||||
sp_name
|
||||
trg_action_time
|
||||
trg_event
|
||||
ON
|
||||
remember_name /* $7 */
|
||||
{ /* $8 */
|
||||
THD *thd= YYTHD;
|
||||
LEX *lex= thd->lex;
|
||||
Lex_input_stream *lip= thd->m_lip;
|
||||
lex->raw_trg_on_table_name_begin= lip->get_tok_start();
|
||||
}
|
||||
table_ident /* $9 */
|
||||
FOR_SYM
|
||||
remember_name /* $11 */
|
||||
{ /* $12 */
|
||||
THD *thd= YYTHD;
|
||||
LEX *lex= thd->lex;
|
||||
Lex_input_stream *lip= thd->m_lip;
|
||||
lex->raw_trg_on_table_name_end= lip->get_tok_start();
|
||||
}
|
||||
EACH_SYM
|
||||
ROW_SYM
|
||||
{ /* $15 */
|
||||
THD *thd= YYTHD;
|
||||
LEX *lex= thd->lex;
|
||||
Lex_input_stream *lip= thd->m_lip;
|
||||
|
@ -11459,7 +11472,7 @@ trigger_tail:
|
|||
sp->init_sp_name(thd, $3);
|
||||
lex->stmt_definition_begin= $2;
|
||||
lex->ident.str= $7;
|
||||
lex->ident.length= $10 - $7;
|
||||
lex->ident.length= $11 - $7;
|
||||
|
||||
sp->m_type= TYPE_ENUM_TRIGGER;
|
||||
lex->sphead= sp;
|
||||
|
@ -11474,12 +11487,10 @@ trigger_tail:
|
|||
|
||||
bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics));
|
||||
lex->sphead->m_chistics= &lex->sp_chistics;
|
||||
lex->sphead->m_body_begin= lip->ptr;
|
||||
while (my_isspace(system_charset_info, lex->sphead->m_body_begin[0]))
|
||||
++lex->sphead->m_body_begin;
|
||||
lex->sphead->set_body_begin_ptr(lip, lip->get_cpp_ptr());
|
||||
}
|
||||
sp_proc_stmt
|
||||
{
|
||||
sp_proc_stmt /* $16 */
|
||||
{ /* $17 */
|
||||
LEX *lex= Lex;
|
||||
sp_head *sp= lex->sphead;
|
||||
|
||||
|
@ -11487,7 +11498,7 @@ trigger_tail:
|
|||
sp->init_strings(YYTHD, lex);
|
||||
/* Restore flag if it was cleared above */
|
||||
|
||||
YYTHD->client_capabilities |= $<ulong_num>13;
|
||||
YYTHD->client_capabilities |= $<ulong_num>15;
|
||||
sp->restore_thd_mem_root(YYTHD);
|
||||
|
||||
if (sp->is_not_allowed_in_function("trigger"))
|
||||
|
@ -11498,7 +11509,7 @@ trigger_tail:
|
|||
sp_proc_stmt alternatives are not saving/restoring LEX, so
|
||||
lex->query_tables can be wiped out.
|
||||
*/
|
||||
if (!lex->select_lex.add_table_to_list(YYTHD, $8,
|
||||
if (!lex->select_lex.add_table_to_list(YYTHD, $9,
|
||||
(LEX_STRING*) 0,
|
||||
TL_OPTION_UPDATING,
|
||||
TL_IGNORE))
|
||||
|
@ -11556,8 +11567,11 @@ sp_tail:
|
|||
THD *thd= YYTHD;
|
||||
LEX *lex= thd->lex;
|
||||
Lex_input_stream *lip= thd->m_lip;
|
||||
const char* tmp_param_begin;
|
||||
|
||||
lex->sphead->m_param_begin= lip->tok_start+1;
|
||||
tmp_param_begin= lip->get_cpp_tok_start();
|
||||
tmp_param_begin++;
|
||||
lex->sphead->m_param_begin= tmp_param_begin;
|
||||
}
|
||||
sp_pdparam_list
|
||||
')'
|
||||
|
@ -11566,7 +11580,7 @@ sp_tail:
|
|||
LEX *lex= thd->lex;
|
||||
Lex_input_stream *lip= thd->m_lip;
|
||||
|
||||
lex->sphead->m_param_end= lip->tok_start;
|
||||
lex->sphead->m_param_end= lip->get_cpp_tok_start();
|
||||
bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics));
|
||||
}
|
||||
sp_c_chistics
|
||||
|
@ -11576,7 +11590,7 @@ sp_tail:
|
|||
Lex_input_stream *lip= thd->m_lip;
|
||||
|
||||
lex->sphead->m_chistics= &lex->sp_chistics;
|
||||
lex->sphead->m_body_begin= lip->tok_start;
|
||||
lex->sphead->set_body_begin_ptr(lip, lip->get_cpp_tok_start());
|
||||
}
|
||||
sp_proc_stmt
|
||||
{
|
||||
|
|
|
@ -29,21 +29,21 @@
|
|||
|
||||
#define FCOMP 17 /* Bytes for a packed field */
|
||||
|
||||
static uchar * pack_screens(List<create_field> &create_fields,
|
||||
static uchar * pack_screens(List<Create_field> &create_fields,
|
||||
uint *info_length, uint *screens, bool small_file);
|
||||
static uint pack_keys(uchar *keybuff,uint key_count, KEY *key_info,
|
||||
ulong data_offset);
|
||||
static bool pack_header(uchar *forminfo,enum legacy_db_type table_type,
|
||||
List<create_field> &create_fields,
|
||||
List<Create_field> &create_fields,
|
||||
uint info_length, uint screens, uint table_options,
|
||||
ulong data_offset, handler *file);
|
||||
static uint get_interval_id(uint *int_count,List<create_field> &create_fields,
|
||||
create_field *last_field);
|
||||
static bool pack_fields(File file, List<create_field> &create_fields,
|
||||
static uint get_interval_id(uint *int_count,List<Create_field> &create_fields,
|
||||
Create_field *last_field);
|
||||
static bool pack_fields(File file, List<Create_field> &create_fields,
|
||||
ulong data_offset);
|
||||
static bool make_empty_rec(THD *thd, int file, enum legacy_db_type table_type,
|
||||
uint table_options,
|
||||
List<create_field> &create_fields,
|
||||
List<Create_field> &create_fields,
|
||||
uint reclength, ulong data_offset,
|
||||
handler *handler);
|
||||
|
||||
|
@ -70,7 +70,7 @@ static bool make_empty_rec(THD *thd, int file, enum legacy_db_type table_type,
|
|||
bool mysql_create_frm(THD *thd, const char *file_name,
|
||||
const char *db, const char *table,
|
||||
HA_CREATE_INFO *create_info,
|
||||
List<create_field> &create_fields,
|
||||
List<Create_field> &create_fields,
|
||||
uint keys, KEY *key_info,
|
||||
handler *db_file)
|
||||
{
|
||||
|
@ -294,8 +294,8 @@ bool mysql_create_frm(THD *thd, const char *file_name,
|
|||
Restore all UCS2 intervals.
|
||||
HEX representation of them is not needed anymore.
|
||||
*/
|
||||
List_iterator<create_field> it(create_fields);
|
||||
create_field *field;
|
||||
List_iterator<Create_field> it(create_fields);
|
||||
Create_field *field;
|
||||
while ((field=it++))
|
||||
{
|
||||
if (field->save_interval)
|
||||
|
@ -341,7 +341,7 @@ err3:
|
|||
int rea_create_table(THD *thd, const char *path,
|
||||
const char *db, const char *table_name,
|
||||
HA_CREATE_INFO *create_info,
|
||||
List<create_field> &create_fields,
|
||||
List<Create_field> &create_fields,
|
||||
uint keys, KEY *key_info, handler *file)
|
||||
{
|
||||
DBUG_ENTER("rea_create_table");
|
||||
|
@ -371,7 +371,7 @@ err_handler:
|
|||
|
||||
/* Pack screens to a screen for save in a form-file */
|
||||
|
||||
static uchar *pack_screens(List<create_field> &create_fields,
|
||||
static uchar *pack_screens(List<Create_field> &create_fields,
|
||||
uint *info_length, uint *screens,
|
||||
bool small_file)
|
||||
{
|
||||
|
@ -380,7 +380,7 @@ static uchar *pack_screens(List<create_field> &create_fields,
|
|||
uint length,cols;
|
||||
uchar *info,*pos,*start_screen;
|
||||
uint fields=create_fields.elements;
|
||||
List_iterator<create_field> it(create_fields);
|
||||
List_iterator<Create_field> it(create_fields);
|
||||
DBUG_ENTER("pack_screens");
|
||||
|
||||
start_row=4; end_row=22; cols=80; fields_on_screen=end_row+1-start_row;
|
||||
|
@ -388,7 +388,7 @@ static uchar *pack_screens(List<create_field> &create_fields,
|
|||
*screens=(fields-1)/fields_on_screen+1;
|
||||
length= (*screens) * (SC_INFO_LENGTH+ (cols>> 1)+4);
|
||||
|
||||
create_field *field;
|
||||
Create_field *field;
|
||||
while ((field=it++))
|
||||
length+=(uint) strlen(field->field_name)+1+TE_INFO_LENGTH+cols/2;
|
||||
|
||||
|
@ -401,7 +401,7 @@ static uchar *pack_screens(List<create_field> &create_fields,
|
|||
it.rewind();
|
||||
for (i=0 ; i < fields ; i++)
|
||||
{
|
||||
create_field *cfield=it++;
|
||||
Create_field *cfield=it++;
|
||||
if (row++ == end_row)
|
||||
{
|
||||
if (i)
|
||||
|
@ -521,7 +521,7 @@ static uint pack_keys(uchar *keybuff, uint key_count, KEY *keyinfo,
|
|||
/* Make formheader */
|
||||
|
||||
static bool pack_header(uchar *forminfo, enum legacy_db_type table_type,
|
||||
List<create_field> &create_fields,
|
||||
List<Create_field> &create_fields,
|
||||
uint info_length, uint screens, uint table_options,
|
||||
ulong data_offset, handler *file)
|
||||
{
|
||||
|
@ -544,8 +544,8 @@ static bool pack_header(uchar *forminfo, enum legacy_db_type table_type,
|
|||
|
||||
/* Check fields */
|
||||
|
||||
List_iterator<create_field> it(create_fields);
|
||||
create_field *field;
|
||||
List_iterator<Create_field> it(create_fields);
|
||||
Create_field *field;
|
||||
while ((field=it++))
|
||||
{
|
||||
uint tmp_len= system_charset_info->cset->charpos(system_charset_info,
|
||||
|
@ -687,11 +687,11 @@ static bool pack_header(uchar *forminfo, enum legacy_db_type table_type,
|
|||
|
||||
/* get each unique interval each own id */
|
||||
|
||||
static uint get_interval_id(uint *int_count,List<create_field> &create_fields,
|
||||
create_field *last_field)
|
||||
static uint get_interval_id(uint *int_count,List<Create_field> &create_fields,
|
||||
Create_field *last_field)
|
||||
{
|
||||
List_iterator<create_field> it(create_fields);
|
||||
create_field *field;
|
||||
List_iterator<Create_field> it(create_fields);
|
||||
Create_field *field;
|
||||
TYPELIB *interval=last_field->interval;
|
||||
|
||||
while ((field=it++) != last_field)
|
||||
|
@ -715,18 +715,18 @@ static uint get_interval_id(uint *int_count,List<create_field> &create_fields,
|
|||
|
||||
/* Save fields, fieldnames and intervals */
|
||||
|
||||
static bool pack_fields(File file, List<create_field> &create_fields,
|
||||
static bool pack_fields(File file, List<Create_field> &create_fields,
|
||||
ulong data_offset)
|
||||
{
|
||||
reg2 uint i;
|
||||
uint int_count, comment_length=0;
|
||||
uchar buff[MAX_FIELD_WIDTH];
|
||||
create_field *field;
|
||||
Create_field *field;
|
||||
DBUG_ENTER("pack_fields");
|
||||
|
||||
/* Write field info */
|
||||
|
||||
List_iterator<create_field> it(create_fields);
|
||||
List_iterator<Create_field> it(create_fields);
|
||||
|
||||
int_count=0;
|
||||
while ((field=it++))
|
||||
|
@ -856,7 +856,7 @@ static bool pack_fields(File file, List<create_field> &create_fields,
|
|||
|
||||
static bool make_empty_rec(THD *thd, File file,enum legacy_db_type table_type,
|
||||
uint table_options,
|
||||
List<create_field> &create_fields,
|
||||
List<Create_field> &create_fields,
|
||||
uint reclength,
|
||||
ulong data_offset,
|
||||
handler *handler)
|
||||
|
@ -867,7 +867,7 @@ static bool make_empty_rec(THD *thd, File file,enum legacy_db_type table_type,
|
|||
uchar *buff,*null_pos;
|
||||
TABLE table;
|
||||
TABLE_SHARE share;
|
||||
create_field *field;
|
||||
Create_field *field;
|
||||
enum_check_fields old_count_cuted_fields= thd->count_cuted_fields;
|
||||
DBUG_ENTER("make_empty_rec");
|
||||
|
||||
|
@ -893,7 +893,7 @@ static bool make_empty_rec(THD *thd, File file,enum legacy_db_type table_type,
|
|||
}
|
||||
null_pos= buff;
|
||||
|
||||
List_iterator<create_field> it(create_fields);
|
||||
List_iterator<Create_field> it(create_fields);
|
||||
thd->count_cuted_fields= CHECK_FIELD_WARN; // To find wrong default values
|
||||
while ((field=it++))
|
||||
{
|
||||
|
|
|
@ -74,6 +74,7 @@
|
|||
#define MAX_TIME_WIDTH 23 /* -DDDDDD HH:MM:SS.###### */
|
||||
#define MAX_DATETIME_FULL_WIDTH 29 /* YYYY-MM-DD HH:MM:SS.###### AM */
|
||||
#define MAX_DATETIME_WIDTH 19 /* YYYY-MM-DD HH:MM:SS */
|
||||
#define MAX_DATETIME_COMPRESSED_WIDTH 14 /* YYYYMMDDHHMMSS */
|
||||
|
||||
#define MAX_TABLES (sizeof(table_map)*8-3) /* Max tables in join */
|
||||
#define PARAM_TABLE_BIT (((table_map) 1) << (sizeof(table_map)*8-3))
|
||||
|
|
|
@ -16079,7 +16079,7 @@ static void test_bug24179()
|
|||
Bug#28075 "COM_DEBUG crashes mysqld"
|
||||
Note: Test disabled because of failure in PushBuild.
|
||||
*/
|
||||
#ifdef fix_bug_in_pb_first
|
||||
|
||||
static void test_bug28075()
|
||||
{
|
||||
int rc;
|
||||
|
@ -16089,18 +16089,18 @@ static void test_bug28075()
|
|||
|
||||
rc= mysql_dump_debug_info(mysql);
|
||||
DIE_UNLESS(rc == 0);
|
||||
|
||||
|
||||
rc= mysql_ping(mysql);
|
||||
DIE_UNLESS(rc == 0);
|
||||
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
Bug#27876 (SF with cyrillic variable name fails during execution (regression))
|
||||
*/
|
||||
|
||||
static void test_bug27876()
|
||||
{
|
||||
int rc;
|
||||
|
@ -16165,6 +16165,7 @@ static void test_bug27876()
|
|||
Bug#28505: mysql_affected_rows() returns wrong value if CLIENT_FOUND_ROWS
|
||||
flag is set.
|
||||
*/
|
||||
|
||||
static void test_bug28505()
|
||||
{
|
||||
my_ulonglong res;
|
||||
|
@ -16217,6 +16218,59 @@ static void test_bug28934()
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
Bug#27592 (stack overrun when storing datetime value using prepared statements)
|
||||
*/
|
||||
|
||||
static void test_bug27592()
|
||||
{
|
||||
const int NUM_ITERATIONS= 40;
|
||||
int i;
|
||||
int rc;
|
||||
MYSQL_STMT *stmt= NULL;
|
||||
MYSQL_BIND bind[1];
|
||||
MYSQL_TIME time_val;
|
||||
|
||||
DBUG_ENTER("test_bug27592");
|
||||
myheader("test_bug27592");
|
||||
|
||||
mysql_query(mysql, "DROP TABLE IF EXISTS t1");
|
||||
mysql_query(mysql, "CREATE TABLE t1(c2 DATETIME)");
|
||||
|
||||
stmt= mysql_simple_prepare(mysql, "INSERT INTO t1 VALUES (?)");
|
||||
DIE_UNLESS(stmt);
|
||||
|
||||
memset(bind, 0, sizeof(bind));
|
||||
|
||||
bind[0].buffer_type= MYSQL_TYPE_DATETIME;
|
||||
bind[0].buffer= (char *) &time_val;
|
||||
bind[0].length= NULL;
|
||||
|
||||
for (i= 0; i < NUM_ITERATIONS; i++)
|
||||
{
|
||||
time_val.year= 2007;
|
||||
time_val.month= 6;
|
||||
time_val.day= 7;
|
||||
time_val.hour= 18;
|
||||
time_val.minute= 41;
|
||||
time_val.second= 3;
|
||||
|
||||
time_val.second_part=0;
|
||||
time_val.neg=0;
|
||||
|
||||
rc= mysql_stmt_bind_param(stmt, bind);
|
||||
check_execute(stmt, rc);
|
||||
|
||||
rc= mysql_stmt_execute(stmt);
|
||||
check_execute(stmt, rc);
|
||||
}
|
||||
|
||||
mysql_stmt_close(stmt);
|
||||
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Read and parse arguments and MySQL options from my.cnf
|
||||
*/
|
||||
|
@ -16498,15 +16552,14 @@ static struct my_tests_st my_tests[]= {
|
|||
{ "test_bug15518", test_bug15518 },
|
||||
{ "test_bug23383", test_bug23383 },
|
||||
{ "test_bug21635", test_bug21635 },
|
||||
{ "test_bug28505", test_bug28505 },
|
||||
{ "test_status", test_status },
|
||||
{ "test_bug24179", test_bug24179 },
|
||||
{ "test_ps_query_cache", test_ps_query_cache },
|
||||
#ifdef fix_bug_in_pb_first
|
||||
{ "test_bug28075", test_bug28075 },
|
||||
#endif
|
||||
{ "test_bug28934", test_bug28934 },
|
||||
{ "test_bug27876", test_bug27876 },
|
||||
{ "test_bug28505", test_bug28505 },
|
||||
{ "test_bug28934", test_bug28934 },
|
||||
{ "test_bug27592", test_bug27592 },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue