diff --git a/include/my_sys.h b/include/my_sys.h index a8633982f84..d1a253e4a7f 100644 --- a/include/my_sys.h +++ b/include/my_sys.h @@ -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); diff --git a/mysql-test/r/comments.result b/mysql-test/r/comments.result index a9106ce0538..98921c561d1 100644 --- a/mysql-test/r/comments.result +++ b/mysql-test/r/comments.result @@ -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 diff --git a/mysql-test/r/log_tables.result b/mysql-test/r/log_tables.result index 5a90c22fa06..ce3dabe3a56 100644 --- a/mysql-test/r/log_tables.result +++ b/mysql-test/r/log_tables.result @@ -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; diff --git a/mysql-test/r/query_cache.result b/mysql-test/r/query_cache.result index fadd99ab27c..680577c495e 100644 --- a/mysql-test/r/query_cache.result +++ b/mysql-test/r/query_cache.result @@ -1495,3 +1495,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; diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index 1c3fd53b0b2..98d73f7536c 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -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; diff --git a/mysql-test/r/trigger.result b/mysql-test/r/trigger.result index 59ed4511aed..ecaef458e92 100644 --- a/mysql-test/r/trigger.result +++ b/mysql-test/r/trigger.result @@ -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; diff --git a/mysql-test/r/varbinary.result b/mysql-test/r/varbinary.result index f30af2ea1a3..6d39d8301c5 100644 --- a/mysql-test/r/varbinary.result +++ b/mysql-test/r/varbinary.result @@ -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; diff --git a/mysql-test/t/comments.test b/mysql-test/t/comments.test index 52273ec9523..8ae6ba5779e 100644 --- a/mysql-test/t/comments.test +++ b/mysql-test/t/comments.test @@ -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; + diff --git a/mysql-test/t/disabled.def b/mysql-test/t/disabled.def index 27e3b9bc614..29336e1050d 100644 --- a/mysql-test/t/disabled.def +++ b/mysql-test/t/disabled.def @@ -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 diff --git a/mysql-test/t/events_restart_phase3.test b/mysql-test/t/events_restart_phase3.test index f5eeb1af2d9..04d879e50ec 100644 --- a/mysql-test/t/events_restart_phase3.test +++ b/mysql-test/t/events_restart_phase3.test @@ -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 diff --git a/mysql-test/t/greedy_optimizer.test b/mysql-test/t/greedy_optimizer.test index 4feca43ae1a..b73f70c6a3e 100644 --- a/mysql-test/t/greedy_optimizer.test +++ b/mysql-test/t/greedy_optimizer.test @@ -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 diff --git a/mysql-test/t/join.test b/mysql-test/t/join.test index 4d35989aa9a..14c98431970 100644 --- a/mysql-test/t/join.test +++ b/mysql-test/t/join.test @@ -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 diff --git a/mysql-test/t/log_tables.test b/mysql-test/t/log_tables.test index b02a47dde6b..e8eff7cba0a 100644 --- a/mysql-test/t/log_tables.test +++ b/mysql-test/t/log_tables.test @@ -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; diff --git a/mysql-test/t/query_cache.test b/mysql-test/t/query_cache.test index e046f21815a..14ccea0fdc8 100644 --- a/mysql-test/t/query_cache.test +++ b/mysql-test/t/query_cache.test @@ -1058,3 +1058,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 diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index 8972e8bae4a..29e759d7881 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -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; + + diff --git a/mysql-test/t/trigger.test b/mysql-test/t/trigger.test index 6dc8c71176b..3df88f60a33 100644 --- a/mysql-test/t/trigger.test +++ b/mysql-test/t/trigger.test @@ -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; + diff --git a/mysql-test/t/varbinary.test b/mysql-test/t/varbinary.test index 2d055920c22..9ccbac7cdda 100644 --- a/mysql-test/t/varbinary.test +++ b/mysql-test/t/varbinary.test @@ -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; + diff --git a/mysys/charset.c b/mysys/charset.c index c6065f87df3..5f9521eeab8 100644 --- a/mysys/charset.c +++ b/mysys/charset.c @@ -573,6 +573,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 (\) diff --git a/sql/event_data_objects.cc b/sql/event_data_objects.cc index 138752d5ce5..77dc33e6265 100644 --- a/sql/event_data_objects.cc +++ b/sql/event_data_objects.cc @@ -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)); diff --git a/sql/event_data_objects.h b/sql/event_data_objects.h index 8e03ab19602..d79732780ff 100644 --- a/sql/event_data_objects.h +++ b/sql/event_data_objects.h @@ -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 diff --git a/sql/event_db_repository.cc b/sql/event_db_repository.cc index fa17ee8a380..703c4160216 100644 --- a/sql/event_db_repository.cc +++ b/sql/event_db_repository.cc @@ -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) diff --git a/sql/event_scheduler.cc b/sql/event_scheduler.cc index 478ae0098da..c552b22e942 100644 --- a/sql/event_scheduler.cc +++ b/sql/event_scheduler.cc @@ -326,6 +326,8 @@ end: delete event; deinit_event_thread(thd); + + DBUG_VOID_RETURN; } diff --git a/sql/field.cc b/sql/field.cc index e6713e548ab..242d1351bbd 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -38,8 +38,8 @@ *****************************************************************************/ #ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION -template class List; -template class List_iterator; +template class List; +template class List_iterator; #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); @@ -6069,7 +6070,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()); } @@ -6077,7 +6078,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))) || @@ -6086,7 +6087,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; @@ -6939,7 +6940,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) @@ -7621,7 +7622,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; @@ -8159,7 +8160,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 & @@ -8264,7 +8265,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()); @@ -8595,20 +8596,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: @@ -8655,7 +8656,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) { @@ -8696,7 +8697,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, @@ -8706,7 +8707,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; @@ -8890,15 +8891,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) @@ -8951,7 +8955,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: { @@ -9279,7 +9283,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; diff --git a/sql/field.h b/sql/field.h index 3b4784adb2b..a0fe0f2e57e 100644 --- a/sql/field.h +++ b/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. */ diff --git a/sql/handler.cc b/sql/handler.cc index 1a468f306e3..a0f27824533 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -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); diff --git a/sql/item.h b/sql/item.h index 91f116b985e..c79107e04bd 100644 --- a/sql/item.h +++ b/sql/item.h @@ -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; diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 08ce47573e7..10bd24dbb66 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -912,8 +912,8 @@ void Item_sum_distinct::fix_length_and_dec() bool Item_sum_distinct::setup(THD *thd) { - List field_list; - create_field field_def; /* field definition */ + List field_list; + Create_field field_def; /* field definition */ DBUG_ENTER("Item_sum_distinct::setup"); DBUG_ASSERT(tree == 0); diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 6f38a1acc67..95ff7dfd336 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -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)); diff --git a/sql/log.cc b/sql/log.cc index ac6ea92c61a..6dc204265b0 100644 --- a/sql/log.cc +++ b/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); diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 83244d3aeff..2b427e62c8b 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -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 &field_list); +TABLE *create_virtual_tmp_table(THD *thd, List &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 *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, @@ -1588,6 +1590,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 @@ -1609,8 +1612,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, + List &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, + List &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 *list); void free_list(I_List *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 */ diff --git a/sql/sp.cc b/sql/sp.cc index 49e86f9d07e..a8e6c2844a2 100644 --- a/sql/sp.cc +++ b/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, diff --git a/sql/sp_head.cc b/sql/sp_head.cc index bbf281caee0..feceb2fb960 100644 --- a/sql/sp_head.cc +++ b/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 *src) +create_typelib(MEM_ROOT *mem_root, Create_field *field_def, List *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) { diff --git a/sql/sp_head.h b/sql/sp_head.h index eacc2ccdcf6..2d3bc1307d9 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -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); diff --git a/sql/sp_pcontext.cc b/sql/sp_pcontext.cc index 9c96485b8ec..8babbd52ff6 100644 --- a/sql/sp_pcontext.cc +++ b/sql/sp_pcontext.cc @@ -422,7 +422,7 @@ sp_pcontext::find_cursor(LEX_STRING *name, uint *poff, my_bool scoped) void -sp_pcontext::retrieve_field_definitions(List *field_def_lst) +sp_pcontext::retrieve_field_definitions(List *field_def_lst) { /* Put local/context fields in the result list. */ diff --git a/sql/sp_pcontext.h b/sql/sp_pcontext.h index 3b8b165a6e0..aefc501b3b0 100644 --- a/sql/sp_pcontext.h +++ b/sql/sp_pcontext.h @@ -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 *field_def_lst); + retrieve_field_definitions(List *field_def_lst); // Find by name sp_variable_t * diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc index e49c4eb1240..b94c733cb0a 100644 --- a/sql/sp_rcontext.cc +++ b/sql/sp_rcontext.cc @@ -102,7 +102,7 @@ bool sp_rcontext::init(THD *thd) bool sp_rcontext::init_var_table(THD *thd) { - List field_def_lst; + List field_def_lst; if (!m_root_parsing_ctx->max_var_index()) return FALSE; diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index 04f142c5dfb..719c67aff6a 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -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")); diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 9dbc79344a9..62f6706d717 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -59,8 +59,8 @@ const char * const THD::DEFAULT_WHERE= "field list"; /* Used templates */ template class List; template class List_iterator; -template class List; -template class List_iterator; +template class List; +template class List_iterator; template class List; template class List_iterator; template class List; @@ -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 col_it1(a->columns); - List_iterator col_it2(b->columns); - const key_part_spec *col1, *col2; + List_iterator col_it1(a->columns); + List_iterator 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; diff --git a/sql/sql_class.h b/sql/sql_class.h index de7aaafbe3b..dd03225b501 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -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 columns; + List 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 &cols) + bool generated_arg, List &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 ref_columns; + List ref_columns; uint delete_opt, update_opt, match_opt; - foreign_key(const char *name_arg, List &cols, - Table_ident *table, List &ref_cols, + Foreign_key(const char *name_arg, List &cols, + Table_ident *table, List &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 diff --git a/sql/sql_db.cc b/sql/sql_db.cc index 82938184245..43d84740f0b 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -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); } diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 0ce52779ce1..a498a88ce4b 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -3213,7 +3213,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; @@ -3257,7 +3257,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); @@ -3266,7 +3266,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); diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 44768dd6d79..1b8d90d51b6 100644 --- a/sql/sql_lex.cc +++ b/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 */ diff --git a/sql/sql_lex.h b/sql/sql_lex.h index f798fb535b3..7057bf4b5ef 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -850,14 +850,14 @@ public: List drop_list; List alter_list; List key_list; - List create_list; + List create_list; uint flags; enum enum_enable_or_disable keys_onoff; enum tablespace_op_type tablespace_op; List 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; @@ -1048,9 +1048,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 { @@ -1058,6 +1090,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; @@ -1070,37 +1314,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; }; @@ -1138,8 +1419,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 ', in trigger statements. */ + const char* raw_trg_on_table_name_begin; + /** End of 'ON
', 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; @@ -1149,8 +1439,8 @@ typedef struct st_lex : public Query_tables_list */ LEX_USER *definer; - List col_list; - List ref_list; + List col_list; + List ref_list; List interval_list; List users_list; List columns; @@ -1176,7 +1466,7 @@ typedef struct st_lex : public Query_tables_list List 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 @@ -1238,7 +1528,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. @@ -1302,10 +1594,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). @@ -1434,8 +1728,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); diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index e7ed02f333e..66296377100 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -5353,12 +5353,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 && @@ -5381,8 +5380,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); @@ -5436,12 +5435,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(); @@ -5466,7 +5463,7 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type, List *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"); @@ -5479,7 +5476,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); @@ -5489,7 +5486,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); @@ -5547,7 +5544,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)) @@ -7133,3 +7130,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; +} diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index 04e7645a8aa..407a0b3dcf2 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -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; diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 9d739102b6f..7badccd55d9 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -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) diff --git a/sql/sql_select.cc b/sql/sql_select.cc index dc5e33518d2..98620800434 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -10083,12 +10083,12 @@ err: 0 if out of memory, TABLE object in case of success */ -TABLE *create_virtual_tmp_table(THD *thd, List &field_list) +TABLE *create_virtual_tmp_table(THD *thd, List &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 &field_list) setup_tmp_table_column_bitmaps(table, bitmaps); /* Create all fields and calculate the total length of record */ - List_iterator_fast it(field_list); + List_iterator_fast it(field_list); while ((cdef= it++)) { *field= make_field(share, 0, cdef->length, diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 9527bd57922..047c210d6a5 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -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, bool ignore, + List &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 it(alter_info->create_list); - List_iterator it2(alter_info->create_list); + List_iterator it(alter_info->create_list); + List_iterator 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 cols(key->columns), cols2(key->columns); + List_iterator 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 new_field_it(alter_info->create_list); - create_field *new_field; + List_iterator_fast 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 new_create_list; + List new_create_list; /* New key definitions are added here */ List new_key_list; List_iterator drop_it(alter_info->drop_list); - List_iterator def_it(alter_info->create_list); + List_iterator def_it(alter_info->create_list); List_iterator alter_it(alter_info->alter_list); List_iterator key_it(alter_info->key_list); - List_iterator find_it(new_create_list); - List_iterator field_it(new_create_list); - List key_parts; + List_iterator find_it(new_create_list); + List_iterator field_it(new_create_list); + List 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, + List &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 it(create); - create_field *def; + List_iterator it(create); + Create_field *def; copy_end=copy; for (Field **ptr=to->field ; *ptr ; ptr++) { diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index e15003ab243..7433a3f45cd 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -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); +} diff --git a/sql/sql_trigger.h b/sql/sql_trigger.h index 7d99dd811cd..c305efb5029 100644 --- a/sql/sql_trigger.h +++ b/sql/sql_trigger.h @@ -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); + diff --git a/sql/sql_view.cc b/sql/sql_view.cc index c1e4006555f..dd17024aee0 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -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; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 1c00ac98c03..3babaa2aa7a 100644 --- a/sql/sql_yacc.yy +++ b/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_list; List *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: */ $$= 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: @@ -6229,14 +6230,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: @@ -8003,16 +8004,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()); } ; @@ -9112,7 +9111,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 {} @@ -9149,7 +9148,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 { @@ -9338,7 +9337,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)); @@ -9471,7 +9470,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) @@ -10147,7 +10146,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 @@ -10180,9 +10179,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))) @@ -11364,7 +11363,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 {} ; @@ -11389,40 +11388,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 */ @@ -11442,9 +11433,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; @@ -11463,7 +11476,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; @@ -11478,12 +11491,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; @@ -11491,7 +11502,7 @@ trigger_tail: sp->init_strings(YYTHD, lex); /* Restore flag if it was cleared above */ - YYTHD->client_capabilities |= $13; + YYTHD->client_capabilities |= $15; sp->restore_thd_mem_root(YYTHD); if (sp->is_not_allowed_in_function("trigger")) @@ -11502,7 +11513,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)) @@ -11560,8 +11571,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 ')' @@ -11570,7 +11584,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 @@ -11580,7 +11594,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 { diff --git a/sql/unireg.cc b/sql/unireg.cc index a02d24d8ae5..57847bc70c6 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -29,21 +29,21 @@ #define FCOMP 17 /* Bytes for a packed field */ -static uchar * pack_screens(List &create_fields, +static uchar * pack_screens(List &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_fields, + List &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_fields, - create_field *last_field); -static bool pack_fields(File file, List &create_fields, +static uint get_interval_id(uint *int_count,List &create_fields, + Create_field *last_field); +static bool pack_fields(File file, List &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_fields, + List &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_fields, + List &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 it(create_fields); - create_field *field; + List_iterator 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_fields, + List &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_fields, +static uchar *pack_screens(List &create_fields, uint *info_length, uint *screens, bool small_file) { @@ -380,7 +380,7 @@ static uchar *pack_screens(List &create_fields, uint length,cols; uchar *info,*pos,*start_screen; uint fields=create_fields.elements; - List_iterator it(create_fields); + List_iterator 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_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_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_fields, + List &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 it(create_fields); - create_field *field; + List_iterator 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_fields, - create_field *last_field) +static uint get_interval_id(uint *int_count,List &create_fields, + Create_field *last_field) { - List_iterator it(create_fields); - create_field *field; + List_iterator 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_fields, /* Save fields, fieldnames and intervals */ -static bool pack_fields(File file, List &create_fields, +static bool pack_fields(File file, List &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 it(create_fields); + List_iterator it(create_fields); int_count=0; while ((field=it++)) @@ -856,7 +856,7 @@ static bool pack_fields(File file, List &create_fields, static bool make_empty_rec(THD *thd, File file,enum legacy_db_type table_type, uint table_options, - List &create_fields, + List &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 it(create_fields); + List_iterator it(create_fields); thd->count_cuted_fields= CHECK_FIELD_WARN; // To find wrong default values while ((field=it++)) { diff --git a/sql/unireg.h b/sql/unireg.h index d67fa372083..5b73c6e9caa 100644 --- a/sql/unireg.h +++ b/sql/unireg.h @@ -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)) diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index 3f0cb1bf4fe..eefaa6ce071 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -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 } };