Merge mysql.com:/nfsdisk1/lars/bk/mysql-5.1

into  mysql.com:/nfsdisk1/lars/bk/mysql-5.1-new-rpl


mysql-test/t/disabled.def:
  Auto merged
mysql-test/t/ndb_index_ordered.test:
  Auto merged
mysys/charset.c:
  Auto merged
sql/field.cc:
  Auto merged
sql/handler.cc:
  Auto merged
sql/mysql_priv.h:
  Auto merged
sql/sp_head.cc:
  Auto merged
sql/sp_head.h:
  Auto merged
sql/sql_class.cc:
  Auto merged
sql/sql_class.h:
  Auto merged
sql/sql_insert.cc:
  Auto merged
sql/sql_lex.cc:
  Auto merged
sql/sql_lex.h:
  Auto merged
sql/sql_parse.cc:
  Auto merged
sql/sql_view.cc:
  Auto merged
This commit is contained in:
unknown 2007-06-21 17:13:02 +02:00
commit 63d7c4a8c8
55 changed files with 2180 additions and 955 deletions

View file

@ -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);

View file

@ -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

View file

@ -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;

View file

@ -1444,3 +1444,51 @@ insert into t1 values ('c');
a
drop table t1;
set GLOBAL query_cache_size= default;
drop database if exists db1;
drop database if exists db2;
set GLOBAL query_cache_size=15*1024*1024;
create database db1;
use db1;
create table t1(c1 int)engine=myisam;
insert into t1(c1) values (1);
select * from db1.t1 f;
c1
1
show status like 'Qcache_queries_in_cache';
Variable_name Value
Qcache_queries_in_cache 1
rename schema db1 to db2;
show status like 'Qcache_queries_in_cache';
Variable_name Value
Qcache_queries_in_cache 0
drop database db2;
set global query_cache_size=default;
drop database if exists db1;
drop database if exists db3;
set GLOBAL query_cache_size=15*1024*1024;
create database db1;
create database db3;
use db1;
create table t1(c1 int) engine=myisam;
use db3;
create table t1(c1 int) engine=myisam;
use db1;
insert into t1(c1) values (1);
use mysql;
select * from db1.t1;
c1
1
select c1+1 from db1.t1;
c1+1
2
select * from db3.t1;
c1
show status like 'Qcache_queries_in_cache';
Variable_name Value
Qcache_queries_in_cache 3
rename schema db1 to db2;
show status like 'Qcache_queries_in_cache';
Variable_name Value
Qcache_queries_in_cache 1
drop database db2;
drop database db3;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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;

View file

@ -1024,3 +1024,48 @@ drop table t1;
set GLOBAL query_cache_size= default;
# End of 5.0 tests
#
# Bug #28211 RENAME DATABASE and query cache don't play nicely together
#
--disable_warnings
drop database if exists db1;
drop database if exists db2;
--enable_warnings
set GLOBAL query_cache_size=15*1024*1024;
create database db1;
use db1;
create table t1(c1 int)engine=myisam;
insert into t1(c1) values (1);
select * from db1.t1 f;
show status like 'Qcache_queries_in_cache';
rename schema db1 to db2;
show status like 'Qcache_queries_in_cache';
drop database db2;
set global query_cache_size=default;
--disable_warnings
drop database if exists db1;
drop database if exists db3;
--enable_warnings
set GLOBAL query_cache_size=15*1024*1024;
create database db1;
create database db3;
use db1;
create table t1(c1 int) engine=myisam;
use db3;
create table t1(c1 int) engine=myisam;
use db1;
insert into t1(c1) values (1);
use mysql;
select * from db1.t1;
select c1+1 from db1.t1;
select * from db3.t1;
show status like 'Qcache_queries_in_cache';
rename schema db1 to db2;
show status like 'Qcache_queries_in_cache';
drop database db2;
drop database db3;
# End of 5.1 tests

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -587,6 +587,70 @@ CHARSET_INFO *get_charset_by_csname(const char *cs_name,
}
/**
Resolve character set by the character set name (utf8, latin1, ...).
The function tries to resolve character set by the specified name. If
there is character set with the given name, it is assigned to the "cs"
parameter and FALSE is returned. If there is no such character set,
"default_cs" is assigned to the "cs" and TRUE is returned.
@param[out] cs Variable to store character set.
@param[in] cs_name Character set name.
@param[in] default_cs Default character set.
@return FALSE if character set was resolved successfully; TRUE if there
is no character set with given name.
*/
bool resolve_charset(CHARSET_INFO **cs,
const char *cs_name,
CHARSET_INFO *default_cs)
{
*cs= get_charset_by_csname(cs_name, MY_CS_PRIMARY, MYF(0));
if (*cs == NULL)
{
*cs= default_cs;
return TRUE;
}
return FALSE;
}
/**
Resolve collation by the collation name (utf8_general_ci, ...).
The function tries to resolve collation by the specified name. If there
is collation with the given name, it is assigned to the "cl" parameter
and FALSE is returned. If there is no such collation, "default_cl" is
assigned to the "cl" and TRUE is returned.
@param[out] cl Variable to store collation.
@param[in] cl_name Collation name.
@param[in] default_cl Default collation.
@return FALSE if collation was resolved successfully; TRUE if there is no
collation with given name.
*/
bool resolve_collation(CHARSET_INFO **cl,
const char *cl_name,
CHARSET_INFO *default_cl)
{
*cl= get_charset_by_name(cl_name, MYF(0));
if (*cl == NULL)
{
*cl= default_cl;
return TRUE;
}
return FALSE;
}
/*
Escape string with backslashes (\)

View file

@ -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));

View file

@ -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

View file

@ -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)

View file

@ -326,6 +326,8 @@ end:
delete event;
deinit_event_thread(thd);
DBUG_VOID_RETURN;
}

View file

@ -38,8 +38,8 @@
*****************************************************************************/
#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
template class List<create_field>;
template class List_iterator<create_field>;
template class List<Create_field>;
template class List_iterator<Create_field>;
#endif
uchar Field_null::null[1]={1};
@ -2631,7 +2631,7 @@ void Field_new_decimal::sql_type(String &str) const
}
uint Field_new_decimal::is_equal(create_field *new_field)
uint Field_new_decimal::is_equal(Create_field *new_field)
{
return ((new_field->sql_type == real_type()) &&
((new_field->flags & UNSIGNED_FLAG) ==
@ -4366,7 +4366,7 @@ Field_timestamp::Field_timestamp(uchar *ptr_arg, uint32 len_arg,
const char *field_name_arg,
TABLE_SHARE *share,
CHARSET_INFO *cs)
:Field_str(ptr_arg, 19, null_ptr_arg, null_bit_arg,
:Field_str(ptr_arg, MAX_DATETIME_WIDTH, null_ptr_arg, null_bit_arg,
unireg_check_arg, field_name_arg, cs)
{
/* For 4.0 MYD and 4.0 InnoDB compatibility */
@ -4383,7 +4383,8 @@ Field_timestamp::Field_timestamp(uchar *ptr_arg, uint32 len_arg,
Field_timestamp::Field_timestamp(bool maybe_null_arg,
const char *field_name_arg,
CHARSET_INFO *cs)
:Field_str((uchar*) 0, 19, maybe_null_arg ? (uchar*) "": 0, 0,
:Field_str((uchar*) 0, MAX_DATETIME_WIDTH,
maybe_null_arg ? (uchar*) "": 0, 0,
NONE, field_name_arg, cs)
{
/* For 4.0 MYD and 4.0 InnoDB compatibility */
@ -4916,7 +4917,7 @@ String *Field_time::val_str(String *val_buffer,
{
ASSERT_COLUMN_MARKED_FOR_READ;
MYSQL_TIME ltime;
val_buffer->alloc(19);
val_buffer->alloc(MAX_DATE_STRING_REP_LENGTH);
long tmp=(long) sint3korr(ptr);
ltime.neg= 0;
if (tmp < 0)
@ -5464,7 +5465,7 @@ int Field_newdate::store_time(MYSQL_TIME *ltime,timestamp_type time_type)
(MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE |
MODE_INVALID_DATES))), &error))
{
char buff[12];
char buff[MAX_DATE_STRING_REP_LENGTH];
String str(buff, sizeof(buff), &my_charset_latin1);
make_date((DATE_TIME_FORMAT *) 0, ltime, &str);
set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED,
@ -5695,7 +5696,7 @@ int Field_datetime::store_time(MYSQL_TIME *ltime,timestamp_type time_type)
(MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE |
MODE_INVALID_DATES))), &error))
{
char buff[19];
char buff[MAX_DATE_STRING_REP_LENGTH];
String str(buff, sizeof(buff), &my_charset_latin1);
make_datetime((DATE_TIME_FORMAT *) 0, ltime, &str);
set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED,
@ -5771,7 +5772,7 @@ String *Field_datetime::val_str(String *val_buffer,
part1=(long) (tmp/LL(1000000));
part2=(long) (tmp - (ulonglong) part1*LL(1000000));
pos=(char*) val_buffer->ptr()+19;
pos=(char*) val_buffer->ptr() + MAX_DATETIME_WIDTH;
*pos--=0;
*pos--= (char) ('0'+(char) (part2%10)); part2/=10;
*pos--= (char) ('0'+(char) (part2%10)); part3= (int) (part2 / 10);
@ -6081,7 +6082,7 @@ int Field_str::store(double nr)
}
uint Field::is_equal(create_field *new_field)
uint Field::is_equal(Create_field *new_field)
{
return (new_field->sql_type == real_type());
}
@ -6089,7 +6090,7 @@ uint Field::is_equal(create_field *new_field)
/* If one of the fields is binary and the other one isn't return 1 else 0 */
bool Field_str::compare_str_field_flags(create_field *new_field, uint32 flags)
bool Field_str::compare_str_field_flags(Create_field *new_field, uint32 flags)
{
return (((new_field->flags & (BINCMP_FLAG | BINARY_FLAG)) &&
!(flags & (BINCMP_FLAG | BINARY_FLAG))) ||
@ -6098,7 +6099,7 @@ bool Field_str::compare_str_field_flags(create_field *new_field, uint32 flags)
}
uint Field_str::is_equal(create_field *new_field)
uint Field_str::is_equal(Create_field *new_field)
{
if (compare_str_field_flags(new_field, flags))
return 0;
@ -6951,7 +6952,7 @@ Field *Field_varstring::new_key_field(MEM_ROOT *root,
}
uint Field_varstring::is_equal(create_field *new_field)
uint Field_varstring::is_equal(Create_field *new_field)
{
if (new_field->sql_type == real_type() &&
new_field->charset == field_charset)
@ -7633,7 +7634,7 @@ uint Field_blob::max_packed_col_length(uint max_length)
}
uint Field_blob::is_equal(create_field *new_field)
uint Field_blob::is_equal(Create_field *new_field)
{
if (compare_str_field_flags(new_field, flags))
return 0;
@ -8171,7 +8172,7 @@ bool Field_num::eq_def(Field *field)
}
uint Field_num::is_equal(create_field *new_field)
uint Field_num::is_equal(Create_field *new_field)
{
return ((new_field->sql_type == real_type()) &&
((new_field->flags & UNSIGNED_FLAG) == (uint) (flags &
@ -8276,7 +8277,7 @@ Field *Field_bit::new_key_field(MEM_ROOT *root,
}
uint Field_bit::is_equal(create_field *new_field)
uint Field_bit::is_equal(Create_field *new_field)
{
return (new_field->sql_type == real_type() &&
new_field->length == max_display_length());
@ -8607,20 +8608,20 @@ void Field_bit_as_char::sql_type(String &res) const
/*****************************************************************************
Handling of field and create_field
Handling of field and Create_field
*****************************************************************************/
/*
Convert create_field::length from number of characters to number of bytes
Convert Create_field::length from number of characters to number of bytes
SYNOPSIS
create_field::create_length_to_internal_length()
Create_field::create_length_to_internal_length()
DESCRIPTION
Convert create_field::length from number of characters to number of bytes.
Convert Create_field::length from number of characters to number of bytes.
*/
void create_field::create_length_to_internal_length(void)
void Create_field::create_length_to_internal_length(void)
{
switch (sql_type) {
case MYSQL_TYPE_TINY_BLOB:
@ -8667,7 +8668,7 @@ void create_field::create_length_to_internal_length(void)
}
void create_field::init_for_tmp_table(enum_field_types sql_type_arg,
void Create_field::init_for_tmp_table(enum_field_types sql_type_arg,
uint32 length_arg, uint32 decimals_arg,
bool maybe_null, bool is_unsigned)
{
@ -8708,7 +8709,7 @@ void create_field::init_for_tmp_table(enum_field_types sql_type_arg,
TRUE on error
*/
bool create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
char *fld_length, char *fld_decimals,
uint fld_type_modifier, Item *fld_default_value,
Item *fld_on_update_value, LEX_STRING *fld_comment,
@ -8718,7 +8719,7 @@ bool create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
uint sign_len, allowed_type_modifier= 0;
ulong max_field_charlength= MAX_FIELD_CHARLENGTH;
DBUG_ENTER("create_field::init()");
DBUG_ENTER("Create_field::init()");
field= 0;
field_name= fld_name;
@ -8902,15 +8903,18 @@ bool create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
break;
case MYSQL_TYPE_TIMESTAMP:
if (!fld_length)
length= 14; /* Full date YYYYMMDDHHMMSS */
else if (length != 19)
{
/* Compressed date YYYYMMDDHHMMSS */
length= MAX_DATETIME_COMPRESSED_WIDTH;
}
else if (length != MAX_DATETIME_WIDTH)
{
/*
We support only even TIMESTAMP lengths less or equal than 14
and 19 as length of 4.1 compatible representation.
*/
length= ((length+1)/2)*2; /* purecov: inspected */
length= min(length,14); /* purecov: inspected */
length= min(length, MAX_DATETIME_COMPRESSED_WIDTH); /* purecov: inspected */
}
flags|= ZEROFILL_FLAG | UNSIGNED_FLAG;
if (fld_default_value)
@ -8963,7 +8967,7 @@ bool create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
length= 10;
break;
case MYSQL_TYPE_DATETIME:
length= 19;
length= MAX_DATETIME_WIDTH;
break;
case MYSQL_TYPE_SET:
{
@ -9291,7 +9295,7 @@ Field *make_field(TABLE_SHARE *share, uchar *ptr, uint32 field_length,
/* Create a field suitable for create of table */
create_field::create_field(Field *old_field,Field *orig_field)
Create_field::Create_field(Field *old_field,Field *orig_field)
{
field= old_field;
field_name=change=old_field->field_name;

View file

@ -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. */

View file

@ -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);

View file

@ -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;

View file

@ -912,8 +912,8 @@ void Item_sum_distinct::fix_length_and_dec()
bool Item_sum_distinct::setup(THD *thd)
{
List<create_field> field_list;
create_field field_def; /* field definition */
List<Create_field> field_list;
Create_field field_def; /* field definition */
DBUG_ENTER("Item_sum_distinct::setup");
DBUG_ASSERT(tree == 0);

View file

@ -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(&ltime, 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(&ltime, TIME_FUZZY_DATE) &&
!make_datetime(ltime.second_part ? DATE_TIME_MICROSECOND : DATE_TIME,
&ltime, str))
@ -2511,7 +2519,8 @@ String *Item_date_typecast::val_str(String *str)
DBUG_ASSERT(fixed == 1);
MYSQL_TIME ltime;
if (!get_arg0_date(&ltime, TIME_FUZZY_DATE) && !str->alloc(11))
if (!get_arg0_date(&ltime, TIME_FUZZY_DATE) &&
!str->alloc(MAX_DATE_STRING_REP_LENGTH))
{
make_date((DATE_TIME_FORMAT *) 0, &ltime, 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 *)&ltime, sizeof(ltime));

View file

@ -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);

View file

@ -101,7 +101,7 @@ char* query_table_status(THD *thd,const char *db,const char *table_name);
ER_WARN_DEPRECATED_SYNTAX, ER(ER_WARN_DEPRECATED_SYNTAX), \
(Old), (Ver), (New)); \
else \
sql_print_warning("The syntax %s is deprecated and will be removed " \
sql_print_warning("The syntax '%s' is deprecated and will be removed " \
"in MySQL %s. Please use %s instead.", (Old), (Ver), (New)); \
} while(0)
@ -618,6 +618,8 @@ bool check_string_char_length(LEX_STRING *str, const char *err_msg,
uint max_char_length, CHARSET_INFO *cs,
bool no_error);
bool parse_sql(THD *thd, class Lex_input_stream *lip);
enum enum_mysql_completiontype {
ROLLBACK_RELEASE=-2, ROLLBACK=1, ROLLBACK_AND_CHAIN=7,
COMMIT_RELEASE=-1, COMMIT=0, COMMIT_AND_CHAIN=6
@ -908,7 +910,7 @@ bool mysql_assign_to_keycache(THD* thd, TABLE_LIST* table_list,
bool mysql_preload_keys(THD* thd, TABLE_LIST* table_list);
int reassign_keycache_tables(THD* thd, KEY_CACHE *src_cache,
KEY_CACHE *dst_cache);
TABLE *create_virtual_tmp_table(THD *thd, List<create_field> &field_list);
TABLE *create_virtual_tmp_table(THD *thd, List<Create_field> &field_list);
bool mysql_xa_recover(THD *thd);
@ -952,8 +954,8 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
bool table_cant_handle_bit_fields,
bool make_copy_field,
uint convert_blob_length);
void sp_prepare_create_field(THD *thd, create_field *sql_field);
int prepare_create_field(create_field *sql_field,
void sp_prepare_create_field(THD *thd, Create_field *sql_field);
int prepare_create_field(Create_field *sql_field,
uint *blob_columns,
int *timestamps, int *timestamps_with_niladic,
longlong table_flags);
@ -1178,7 +1180,7 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum enum_field_types t
char *change, List<String> *interval_list,
CHARSET_INFO *cs,
uint uint_geom_type);
create_field * new_create_field(THD *thd, char *field_name, enum_field_types type,
Create_field * new_create_field(THD *thd, char *field_name, enum_field_types type,
char *length, char *decimals,
uint type_modifier,
Item *default_value, Item *on_update_value,
@ -1589,6 +1591,7 @@ bool check_db_dir_existence(const char *db_name);
bool load_db_opt(THD *thd, const char *path, HA_CREATE_INFO *create);
bool load_db_opt_by_name(THD *thd, const char *db_name,
HA_CREATE_INFO *db_create_info);
CHARSET_INFO *get_default_db_collation(THD *thd, const char *db_name);
bool my_dbopt_init(void);
void my_dbopt_cleanup(void);
extern int creating_database; // How many database locks are made
@ -1610,8 +1613,8 @@ extern const char *first_keyword, *my_localhost, *delayed_user, *binary_keyword;
extern const char **errmesg; /* Error messages */
extern const char *myisam_recover_options_str;
extern const char *in_left_expr_name, *in_additional_cond, *in_having_cond;
extern const char * const triggers_file_ext;
extern const char * const trigname_file_ext;
extern const char * const TRG_EXT;
extern const char * const TRN_EXT;
extern Eq_creator eq_creator;
extern Ne_creator ne_creator;
extern Gt_creator gt_creator;
@ -1805,12 +1808,12 @@ void unireg_end(void) __attribute__((noreturn));
bool mysql_create_frm(THD *thd, const char *file_name,
const char *db, const char *table,
HA_CREATE_INFO *create_info,
List<create_field> &create_field,
List<Create_field> &create_field,
uint key_count,KEY *key_info,handler *db_type);
int rea_create_table(THD *thd, const char *path,
const char *db, const char *table_name,
HA_CREATE_INFO *create_info,
List<create_field> &create_field,
List<Create_field> &create_field,
uint key_count,KEY *key_info,
handler *file);
int format_number(uint inputflag,uint max_length,char * pos,uint length,
@ -1961,7 +1964,6 @@ void free_list(I_List <i_string_pair> *list);
void free_list(I_List <i_string> *list);
/* sql_yacc.cc */
extern int MYSQLparse(void *thd);
#ifndef DBUG_OFF
extern void turn_parser_debug_on();
#endif
@ -2153,6 +2155,12 @@ bool schema_table_store_record(THD *thd, TABLE *table);
int item_create_init();
void item_create_cleanup();
inline void lex_string_set(LEX_STRING *lex_str, const char *c_str)
{
lex_str->str= (char *) c_str;
lex_str->length= strlen(c_str);
}
#endif /* MYSQL_SERVER */
#endif /* MYSQL_CLIENT */

View file

@ -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,

View file

@ -564,24 +564,23 @@ sp_head::init_strings(THD *thd, LEX *lex)
m_params.str= strmake_root(root, m_param_begin, m_params.length);
}
/* If ptr has overrun end_of_query then end_of_query is the end */
endp= (lip->ptr > lip->end_of_query ? lip->end_of_query : lip->ptr);
/*
Trim "garbage" at the end. This is sometimes needed with the
"/ * ! VERSION... * /" wrapper in dump files.
*/
endp= skip_rear_comments(thd->charset(), m_body_begin, endp);
endp= lip->get_cpp_ptr();
lex->stmt_definition_end= endp;
m_body.length= endp - m_body_begin;
m_body.str= strmake_root(root, m_body_begin, m_body.length);
m_defstr.length= endp - lip->buf;
m_defstr.str= strmake_root(root, lip->buf, m_defstr.length);
trim_whitespace(thd->charset(), & m_body);
m_defstr.length= endp - lip->get_cpp_buf();
m_defstr.str= strmake_root(root, lip->get_cpp_buf(), m_defstr.length);
trim_whitespace(thd->charset(), & m_defstr);
DBUG_VOID_RETURN;
}
static TYPELIB *
create_typelib(MEM_ROOT *mem_root, create_field *field_def, List<String> *src)
create_typelib(MEM_ROOT *mem_root, Create_field *field_def, List<String> *src)
{
TYPELIB *result= NULL;
CHARSET_INFO *cs= field_def->charset;
@ -1269,30 +1268,31 @@ set_routine_security_ctx(THD *thd, sp_head *sp, bool is_proc,
#endif // ! NO_EMBEDDED_ACCESS_CHECKS
/*
Execute a trigger:
- changes security context for triggers
- switch to new memroot
- call sp_head::execute
- restore old memroot
- restores security context
/**
Execute trigger stored program.
SYNOPSIS
sp_head::execute_trigger()
thd Thread handle
db database name
table table name
grant_info GRANT_INFO structure to be filled with
information about definer's privileges
on subject table
RETURN
FALSE on success
TRUE on error
Execute a trigger:
- changes security context for triggers;
- switch to new memroot;
- call sp_head::execute;
- restore old memroot;
- restores security context.
@param thd Thread context.
@param db_name Database name.
@param table_name Table name.
@param grant_info GRANT_INFO structure to be filled with information
about definer's privileges on subject table.
@return Error status.
@retval FALSE on success.
@retval TRUE on error.
*/
bool
sp_head::execute_trigger(THD *thd, const char *db, const char *table,
sp_head::execute_trigger(THD *thd,
const LEX_STRING *db_name,
const LEX_STRING *table_name,
GRANT_INFO *grant_info)
{
sp_rcontext *octx = thd->spcont;
@ -1305,6 +1305,46 @@ sp_head::execute_trigger(THD *thd, const char *db, const char *table,
DBUG_ENTER("sp_head::execute_trigger");
DBUG_PRINT("info", ("trigger %s", m_name.str));
#ifndef NO_EMBEDDED_ACCESS_CHECKS
Security_context *save_ctx= NULL;
if (m_chistics->suid != SP_IS_NOT_SUID &&
m_security_ctx.change_security_context(thd,
&m_definer_user,
&m_definer_host,
&m_db,
&save_ctx))
DBUG_RETURN(TRUE);
/*
Fetch information about table-level privileges for subject table into
GRANT_INFO instance. The access check itself will happen in
Item_trigger_field, where this information will be used along with
information about column-level privileges.
*/
fill_effective_table_privileges(thd,
grant_info,
db_name->str,
table_name->str);
/* Check that the definer has TRIGGER privilege on the subject table. */
if (!(grant_info->privilege & TRIGGER_ACL))
{
char priv_desc[128];
get_privilege_desc(priv_desc, sizeof(priv_desc), TRIGGER_ACL);
my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0), priv_desc,
thd->security_ctx->priv_user, thd->security_ctx->host_or_ip,
table_name->str);
m_security_ctx.restore_security_context(thd, save_ctx);
DBUG_RETURN(TRUE);
}
#endif // NO_EMBEDDED_ACCESS_CHECKS
/*
Prepare arena and memroot for objects which lifetime is whole
duration of trigger call (sp_rcontext, it's tables and items,
@ -1337,6 +1377,11 @@ sp_head::execute_trigger(THD *thd, const char *db, const char *table,
err_with_cleanup:
thd->restore_active_arena(&call_arena, &backup_arena);
#ifndef NO_EMBEDDED_ACCESS_CHECKS
m_security_ctx.restore_security_context(thd, save_ctx);
#endif // NO_EMBEDDED_ACCESS_CHECKS
delete nctx;
call_arena.free_items();
free_root(&call_mem_root, MYF(0));
@ -1827,8 +1872,6 @@ sp_head::reset_lex(THD *thd)
sublex->trg_table_fields.empty();
sublex->sp_lex_in_use= FALSE;
sublex->in_comment= oldlex->in_comment;
/* Reset type info. */
sublex->charset= NULL;
@ -1908,7 +1951,7 @@ sp_head::backpatch(sp_label_t *lab)
}
/*
Prepare an instance of create_field for field creation (fill all necessary
Prepare an instance of Create_field for field creation (fill all necessary
attributes).
SYNOPSIS
@ -1916,7 +1959,7 @@ sp_head::backpatch(sp_label_t *lab)
thd [IN] Thread handle
lex [IN] Yacc parsing context
field_type [IN] Field type
field_def [OUT] An instance of create_field to be filled
field_def [OUT] An instance of Create_field to be filled
RETURN
FALSE on success
@ -1926,7 +1969,7 @@ sp_head::backpatch(sp_label_t *lab)
bool
sp_head::fill_field_definition(THD *thd, LEX *lex,
enum enum_field_types field_type,
create_field *field_def)
Create_field *field_def)
{
HA_CREATE_INFO sp_db_info;
LEX_STRING cmt = { 0, 0 };
@ -2010,6 +2053,13 @@ sp_head::set_info(longlong created, longlong modified,
}
void
sp_head::set_body_begin_ptr(Lex_input_stream *lip, const char *begin_ptr)
{
m_body_begin= begin_ptr;
}
void
sp_head::set_definer(const char *definer, uint definerlen)
{

View file

@ -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);

View file

@ -422,7 +422,7 @@ sp_pcontext::find_cursor(LEX_STRING *name, uint *poff, my_bool scoped)
void
sp_pcontext::retrieve_field_definitions(List<create_field> *field_def_lst)
sp_pcontext::retrieve_field_definitions(List<Create_field> *field_def_lst)
{
/* Put local/context fields in the result list. */

View file

@ -43,7 +43,7 @@ typedef struct sp_variable
uint offset;
Item *dflt;
create_field field_def;
Create_field field_def;
} sp_variable_t;
@ -234,7 +234,7 @@ public:
children.
*/
void
retrieve_field_definitions(List<create_field> *field_def_lst);
retrieve_field_definitions(List<Create_field> *field_def_lst);
// Find by name
sp_variable_t *

View file

@ -102,7 +102,7 @@ bool sp_rcontext::init(THD *thd)
bool
sp_rcontext::init_var_table(THD *thd)
{
List<create_field> field_def_lst;
List<Create_field> field_def_lst;
if (!m_root_parsing_ctx->max_var_index())
return FALSE;

View file

@ -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"));

View file

@ -59,8 +59,8 @@ const char * const THD::DEFAULT_WHERE= "field list";
/* Used templates */
template class List<Key>;
template class List_iterator<Key>;
template class List<key_part_spec>;
template class List_iterator<key_part_spec>;
template class List<Key_part_spec>;
template class List_iterator<Key_part_spec>;
template class List<Alter_drop>;
template class List_iterator<Alter_drop>;
template class List<Alter_column>;
@ -86,7 +86,7 @@ extern "C" void free_user_var(user_var_entry *entry)
my_free((char*) entry,MYF(0));
}
bool key_part_spec::operator==(const key_part_spec& other) const
bool Key_part_spec::operator==(const Key_part_spec& other) const
{
return length == other.length && !strcmp(field_name, other.field_name);
}
@ -115,7 +115,7 @@ Key::Key(const Key &rhs, MEM_ROOT *mem_root)
in THD.
*/
foreign_key::foreign_key(const foreign_key &rhs, MEM_ROOT *mem_root)
Foreign_key::Foreign_key(const Foreign_key &rhs, MEM_ROOT *mem_root)
:Key(rhs),
ref_table(rhs.ref_table),
ref_columns(rhs.ref_columns),
@ -160,9 +160,9 @@ bool foreign_key_prefix(Key *a, Key *b)
if (a->columns.elements > b->columns.elements)
return TRUE; // Can't be prefix
List_iterator<key_part_spec> col_it1(a->columns);
List_iterator<key_part_spec> col_it2(b->columns);
const key_part_spec *col1, *col2;
List_iterator<Key_part_spec> col_it1(a->columns);
List_iterator<Key_part_spec> col_it2(b->columns);
const Key_part_spec *col1, *col2;
#ifdef ENABLE_WHEN_INNODB_CAN_HANDLE_SWAPED_FOREIGN_KEY_COLUMNS
while ((col1= col_it1++))
@ -342,7 +342,8 @@ THD::THD()
in_lock_tables(0),
bootstrap(0),
derived_tables_processing(FALSE),
spcont(NULL)
spcont(NULL),
m_lip(NULL)
{
ulong tmp;

View file

@ -85,14 +85,14 @@ typedef struct st_copy_info {
} COPY_INFO;
class key_part_spec :public Sql_alloc {
class Key_part_spec :public Sql_alloc {
public:
const char *field_name;
uint length;
key_part_spec(const char *name,uint len=0) :field_name(name), length(len) {}
bool operator==(const key_part_spec& other) const;
Key_part_spec(const char *name,uint len=0) :field_name(name), length(len) {}
bool operator==(const Key_part_spec& other) const;
/**
Construct a copy of this key_part_spec. field_name is copied
Construct a copy of this Key_part_spec. field_name is copied
by-pointer as it is known to never change. At the same time
'length' may be reset in mysql_prepare_create_table, and this
is why we supply it with a copy.
@ -100,8 +100,8 @@ public:
@return If out of memory, 0 is returned and an error is set in
THD.
*/
key_part_spec *clone(MEM_ROOT *mem_root) const
{ return new (mem_root) key_part_spec(*this); }
Key_part_spec *clone(MEM_ROOT *mem_root) const
{ return new (mem_root) Key_part_spec(*this); }
};
@ -114,7 +114,7 @@ public:
:name(par_name), type(par_type) {}
/**
Used to make a clone of this object for ALTER/CREATE TABLE
@sa comment for key_part_spec::clone
@sa comment for Key_part_spec::clone
*/
Alter_drop *clone(MEM_ROOT *mem_root) const
{ return new (mem_root) Alter_drop(*this); }
@ -129,7 +129,7 @@ public:
:name(par_name), def(literal) {}
/**
Used to make a clone of this object for ALTER/CREATE TABLE
@sa comment for key_part_spec::clone
@sa comment for Key_part_spec::clone
*/
Alter_column *clone(MEM_ROOT *mem_root) const
{ return new (mem_root) Alter_column(*this); }
@ -141,13 +141,13 @@ public:
enum Keytype { PRIMARY, UNIQUE, MULTIPLE, FULLTEXT, SPATIAL, FOREIGN_KEY};
enum Keytype type;
KEY_CREATE_INFO key_create_info;
List<key_part_spec> columns;
List<Key_part_spec> columns;
const char *name;
bool generated;
Key(enum Keytype type_par, const char *name_arg,
KEY_CREATE_INFO *key_info_arg,
bool generated_arg, List<key_part_spec> &cols)
bool generated_arg, List<Key_part_spec> &cols)
:type(type_par), key_create_info(*key_info_arg), columns(cols),
name(name_arg), generated(generated_arg)
{}
@ -157,7 +157,7 @@ public:
friend bool foreign_key_prefix(Key *a, Key *b);
/**
Used to make a clone of this object for ALTER/CREATE TABLE
@sa comment for key_part_spec::clone
@sa comment for Key_part_spec::clone
*/
virtual Key *clone(MEM_ROOT *mem_root) const
{ return new (mem_root) Key(*this, mem_root); }
@ -165,7 +165,7 @@ public:
class Table_ident;
class foreign_key: public Key {
class Foreign_key: public Key {
public:
enum fk_match_opt { FK_MATCH_UNDEF, FK_MATCH_FULL,
FK_MATCH_PARTIAL, FK_MATCH_SIMPLE};
@ -173,23 +173,23 @@ public:
FK_OPTION_SET_NULL, FK_OPTION_NO_ACTION, FK_OPTION_DEFAULT};
Table_ident *ref_table;
List<key_part_spec> ref_columns;
List<Key_part_spec> ref_columns;
uint delete_opt, update_opt, match_opt;
foreign_key(const char *name_arg, List<key_part_spec> &cols,
Table_ident *table, List<key_part_spec> &ref_cols,
Foreign_key(const char *name_arg, List<Key_part_spec> &cols,
Table_ident *table, List<Key_part_spec> &ref_cols,
uint delete_opt_arg, uint update_opt_arg, uint match_opt_arg)
:Key(FOREIGN_KEY, name_arg, &default_key_create_info, 0, cols),
ref_table(table), ref_columns(cols),
delete_opt(delete_opt_arg), update_opt(update_opt_arg),
match_opt(match_opt_arg)
{}
foreign_key(const foreign_key &rhs, MEM_ROOT *mem_root);
Foreign_key(const Foreign_key &rhs, MEM_ROOT *mem_root);
/**
Used to make a clone of this object for ALTER/CREATE TABLE
@sa comment for key_part_spec::clone
@sa comment for Key_part_spec::clone
*/
virtual Key *clone(MEM_ROOT *mem_root) const
{ return new (mem_root) foreign_key(*this, mem_root); }
{ return new (mem_root) Foreign_key(*this, mem_root); }
};
typedef struct st_mysql_lock

View file

@ -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);
}

View file

@ -3214,7 +3214,7 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
MYSQL_LOCK **lock,
TABLEOP_HOOKS *hooks)
{
TABLE tmp_table; // Used during 'create_field()'
TABLE tmp_table; // Used during 'Create_field()'
TABLE_SHARE share;
TABLE *table= 0;
uint select_field_count= items->elements;
@ -3258,7 +3258,7 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
while ((item=it++))
{
create_field *cr_field;
Create_field *cr_field;
Field *field, *def_field;
if (item->type() == Item::FUNC_ITEM)
field= item->tmp_table_field(&tmp_table);
@ -3267,7 +3267,7 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
(Item ***) 0, &tmp_field, &def_field, 0, 0, 0, 0,
0);
if (!field ||
!(cr_field=new create_field(field,(item->type() == Item::FIELD_ITEM ?
!(cr_field=new Create_field(field,(item->type() == Item::FIELD_ITEM ?
((Item_field *)item)->field :
(Field*) 0))))
DBUG_RETURN(0);

View file

@ -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
*/

View file

@ -850,14 +850,14 @@ public:
List<Alter_drop> drop_list;
List<Alter_column> alter_list;
List<Key> key_list;
List<create_field> create_list;
List<Create_field> create_list;
uint flags;
enum enum_enable_or_disable keys_onoff;
enum tablespace_op_type tablespace_op;
List<char> partition_names;
uint no_parts;
enum_alter_table_change_level change_level;
create_field *datetime_field;
Create_field *datetime_field;
bool error_if_not_empty;
@ -1076,9 +1076,41 @@ struct st_parsing_options
};
/**
The state of the lexical parser, when parsing comments.
*/
enum enum_comment_state
{
/**
Not parsing comments.
*/
NO_COMMENT,
/**
Parsing comments that need to be preserved.
Typically, these are user comments '/' '*' ... '*' '/'.
*/
PRESERVE_COMMENT,
/**
Parsing comments that need to be discarded.
Typically, these are special comments '/' '*' '!' ... '*' '/',
or '/' '*' '!' 'M' 'M' 'm' 'm' 'm' ... '*' '/', where the comment
markers should not be expanded.
*/
DISCARD_COMMENT
};
/**
This class represents the character input stream consumed during
lexical analysis.
In addition to consuming the input stream, this class performs some
comment pre processing, by filtering out out of bound special text
from the query input stream.
Two buffers, with pointers inside each buffers, are maintained in
parallel. The 'raw' buffer is the original query text, which may
contain out-of-bound comments. The 'cpp' (for comments pre processor)
is the pre-processed buffer that contains only the query text that
should be seen once out-of-bound data is removed.
*/
class Lex_input_stream
{
@ -1086,6 +1118,218 @@ public:
Lex_input_stream(THD *thd, const char* buff, unsigned int length);
~Lex_input_stream();
/**
Set the echo mode.
When echo is true, characters parsed from the raw input stream are
preserved. When false, characters parsed are silently ignored.
@param echo the echo mode.
*/
void set_echo(bool echo)
{
m_echo= echo;
}
/**
Skip binary from the input stream.
@param n number of bytes to accept.
*/
void skip_binary(int n)
{
if (m_echo)
{
memcpy(m_cpp_ptr, m_ptr, n);
m_cpp_ptr += n;
}
m_ptr += n;
}
/**
Get a character, and advance in the stream.
@return the next character to parse.
*/
char yyGet()
{
char c= *m_ptr++;
if (m_echo)
*m_cpp_ptr++ = c;
return c;
}
/**
Get the last character accepted.
@return the last character accepted.
*/
char yyGetLast()
{
return m_ptr[-1];
}
/**
Look at the next character to parse, but do not accept it.
*/
char yyPeek()
{
return m_ptr[0];
}
/**
Look ahead at some character to parse.
@param n offset of the character to look up
*/
char yyPeekn(int n)
{
return m_ptr[n];
}
/**
Cancel the effect of the last yyGet() or yySkip().
Note that the echo mode should not change between calls to yyGet / yySkip
and yyUnget. The caller is responsible for ensuring that.
*/
void yyUnget()
{
m_ptr--;
if (m_echo)
m_cpp_ptr--;
}
/**
Accept a character, by advancing the input stream.
*/
void yySkip()
{
if (m_echo)
*m_cpp_ptr++ = *m_ptr++;
else
m_ptr++;
}
/**
Accept multiple characters at once.
@param n the number of characters to accept.
*/
void yySkipn(int n)
{
if (m_echo)
{
memcpy(m_cpp_ptr, m_ptr, n);
m_cpp_ptr += n;
}
m_ptr += n;
}
/**
End of file indicator for the query text to parse.
@return true if there are no more characters to parse
*/
bool eof()
{
return (m_ptr >= m_end_of_query);
}
/**
End of file indicator for the query text to parse.
@param n number of characters expected
@return true if there are less than n characters to parse
*/
bool eof(int n)
{
return ((m_ptr + n) >= m_end_of_query);
}
/** Get the raw query buffer. */
const char* get_buf()
{
return m_buf;
}
/** Get the pre-processed query buffer. */
const char* get_cpp_buf()
{
return m_cpp_buf;
}
/** Get the end of the raw query buffer. */
const char* get_end_of_query()
{
return m_end_of_query;
}
/** Mark the stream position as the start of a new token. */
void start_token()
{
m_tok_start_prev= m_tok_start;
m_tok_start= m_ptr;
m_tok_end= m_ptr;
m_cpp_tok_start_prev= m_cpp_tok_start;
m_cpp_tok_start= m_cpp_ptr;
m_cpp_tok_end= m_cpp_ptr;
}
/**
Adjust the starting position of the current token.
This is used to compensate for starting whitespace.
*/
void restart_token()
{
m_tok_start= m_ptr;
m_cpp_tok_start= m_cpp_ptr;
}
/** Get the token start position, in the raw buffer. */
const char* get_tok_start()
{
return m_tok_start;
}
/** Get the token start position, in the pre-processed buffer. */
const char* get_cpp_tok_start()
{
return m_cpp_tok_start;
}
/** Get the token end position, in the raw buffer. */
const char* get_tok_end()
{
return m_tok_end;
}
/** Get the token end position, in the pre-processed buffer. */
const char* get_cpp_tok_end()
{
return m_cpp_tok_end;
}
/** Get the previous token start position, in the raw buffer. */
const char* get_tok_start_prev()
{
return m_tok_start_prev;
}
/** Get the current stream pointer, in the raw buffer. */
const char* get_ptr()
{
return m_ptr;
}
/** Get the current stream pointer, in the pre-processed buffer. */
const char* get_cpp_ptr()
{
return m_cpp_ptr;
}
/** Get the length of the current token, in the raw buffer. */
uint yyLength()
{
/*
The assumption is that the lexical analyser is always 1 character ahead,
which the -1 account for.
*/
DBUG_ASSERT(m_ptr > m_tok_start);
return (uint) ((m_ptr - m_tok_start) - 1);
}
/** Current thread. */
THD *m_thd;
@ -1098,37 +1342,74 @@ public:
/** Interface with bison, value of the last token parsed. */
LEX_YYSTYPE yylval;
/** Pointer to the current position in the input stream. */
const char* ptr;
private:
/** Pointer to the current position in the raw input stream. */
const char* m_ptr;
/** Starting position of the last token parsed. */
const char* tok_start;
/** Starting position of the last token parsed, in the raw buffer. */
const char* m_tok_start;
/** Ending position of the last token parsed. */
const char* tok_end;
/** Ending position of the previous token parsed, in the raw buffer. */
const char* m_tok_end;
/** End of the query text in the input stream. */
const char* end_of_query;
/** End of the query text in the input stream, in the raw buffer. */
const char* m_end_of_query;
/** Starting position of the previous token parsed. */
const char* tok_start_prev;
/** Starting position of the previous token parsed, in the raw buffer. */
const char* m_tok_start_prev;
/** Begining of the query text in the input stream. */
const char* buf;
/** Begining of the query text in the input stream, in the raw buffer. */
const char* m_buf;
/** Echo the parsed stream to the pre-processed buffer. */
bool m_echo;
/** Pre-processed buffer. */
char* m_cpp_buf;
/** Pointer to the current position in the pre-processed input stream. */
char* m_cpp_ptr;
/**
Starting position of the last token parsed,
in the pre-processed buffer.
*/
const char* m_cpp_tok_start;
/**
Starting position of the previous token parsed,
in the pre-procedded buffer.
*/
const char* m_cpp_tok_start_prev;
/**
Ending position of the previous token parsed,
in the pre-processed buffer.
*/
const char* m_cpp_tok_end;
public:
/** Current state of the lexical analyser. */
enum my_lex_states next_state;
/** Position of ';' in the stream, to delimit multiple queries. */
/**
Position of ';' in the stream, to delimit multiple queries.
This delimiter is in the raw buffer.
*/
const char* found_semicolon;
/** SQL_MODE = IGNORE_SPACE. */
bool ignore_space;
/*
/**
TRUE if we're parsing a prepared statement: in this mode
we should allow placeholders and disallow multi-statements.
*/
bool stmt_prepare_mode;
/** State of the lexical analyser for comments. */
enum_comment_state in_comment;
};
@ -1166,8 +1447,17 @@ typedef struct st_lex : public Query_tables_list
CHARSET_INFO *charset, *underscore_charset;
/* store original leaf_tables for INSERT SELECT and PS/SP */
TABLE_LIST *leaf_tables_insert;
/* Position (first character index) of SELECT of CREATE VIEW statement */
uint create_view_select_start;
/** Start of SELECT of CREATE VIEW statement */
const char* create_view_select_start;
/** End of SELECT of CREATE VIEW statement */
const char* create_view_select_end;
/** Start of 'ON <table>', in trigger statements. */
const char* raw_trg_on_table_name_begin;
/** End of 'ON <table>', in trigger statements. */
const char* raw_trg_on_table_name_end;
/* Partition info structure filled in by PARTITION BY parse part */
partition_info *part_info;
@ -1177,8 +1467,8 @@ typedef struct st_lex : public Query_tables_list
*/
LEX_USER *definer;
List<key_part_spec> col_list;
List<key_part_spec> ref_list;
List<Key_part_spec> col_list;
List<Key_part_spec> ref_list;
List<String> interval_list;
List<LEX_USER> users_list;
List<LEX_COLUMN> columns;
@ -1204,7 +1494,7 @@ typedef struct st_lex : public Query_tables_list
List<LEX_STRING> db_list;
SQL_LIST proc_list, auxiliary_table_list, save_list;
create_field *last_field;
Create_field *last_field;
Item_sum *in_sum_func;
udf_func udf;
HA_CHECK_OPT check_opt; // check/repair options
@ -1266,7 +1556,9 @@ typedef struct st_lex : public Query_tables_list
uint8 create_view_algorithm;
uint8 create_view_check;
bool drop_if_exists, drop_temporary, local_file, one_shot_set;
bool in_comment, verbose, no_write_to_binlog;
bool verbose, no_write_to_binlog;
bool tx_chain, tx_release;
/*
Special JOIN::prepare mode: changing of query is prohibited.
@ -1330,10 +1622,12 @@ typedef struct st_lex : public Query_tables_list
- CREATE FUNCTION (points to "FUNCTION" or "AGGREGATE");
This pointer is required to add possibly omitted DEFINER-clause to the
DDL-statement before dumping it to the binlog.
DDL-statement before dumping it to the binlog.
*/
const char *stmt_definition_begin;
const char *stmt_definition_end;
/*
Pointers to part of LOAD DATA statement that should be rewritten
during replication ("LOCAL 'filename' REPLACE INTO" part).
@ -1462,8 +1756,8 @@ extern void lex_free(void);
extern void lex_start(THD *thd);
extern void lex_end(LEX *lex);
extern int MYSQLlex(void *arg, void *yythd);
extern const char *skip_rear_comments(CHARSET_INFO *cs, const char *ubegin,
const char *uend);
extern void trim_whitespace(CHARSET_INFO *cs, LEX_STRING *str);
extern bool is_lex_native_function(const LEX_STRING *name);

View file

@ -5354,12 +5354,11 @@ void mysql_parse(THD *thd, const char *inBuf, uint length,
sp_cache_flush_obsolete(&thd->sp_func_cache);
Lex_input_stream lip(thd, inBuf, length);
thd->m_lip= &lip;
int err= MYSQLparse(thd);
bool err= parse_sql(thd, &lip);
*found_semicolon= lip.found_semicolon;
if (!err && ! thd->is_fatal_error)
if (!err)
{
#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (mqh_used && thd->user_connect &&
@ -5382,8 +5381,8 @@ void mysql_parse(THD *thd, const char *inBuf, uint length,
PROCESSLIST.
Note that we don't need LOCK_thread_count to modify query_length.
*/
if (lip.found_semicolon &&
(thd->query_length= (ulong)(lip.found_semicolon - thd->query)))
if (*found_semicolon &&
(thd->query_length= (ulong)(*found_semicolon - thd->query)))
thd->query_length--;
/* Actually execute the query */
mysql_execute_command(thd);
@ -5437,12 +5436,10 @@ bool mysql_test_parse_for_slave(THD *thd, char *inBuf, uint length)
DBUG_ENTER("mysql_test_parse_for_slave");
Lex_input_stream lip(thd, inBuf, length);
thd->m_lip= &lip;
lex_start(thd);
mysql_reset_thd_for_next_command(thd);
int err= MYSQLparse((void*) thd);
if (!err && ! thd->is_fatal_error &&
if (!parse_sql(thd, &lip) &&
all_tables_not_ok(thd,(TABLE_LIST*) lex->select_lex.table_list.first))
error= 1; /* Ignore question */
thd->end_statement();
@ -5467,7 +5464,7 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type,
List<String> *interval_list, CHARSET_INFO *cs,
uint uint_geom_type)
{
register create_field *new_field;
register Create_field *new_field;
LEX *lex= thd->lex;
DBUG_ENTER("add_field_to_list");
@ -5480,7 +5477,7 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type,
if (type_modifier & PRI_KEY_FLAG)
{
Key *key;
lex->col_list.push_back(new key_part_spec(field_name->str, 0));
lex->col_list.push_back(new Key_part_spec(field_name->str, 0));
key= new Key(Key::PRIMARY, NullS,
&default_key_create_info,
0, lex->col_list);
@ -5490,7 +5487,7 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type,
if (type_modifier & (UNIQUE_FLAG | UNIQUE_KEY_FLAG))
{
Key *key;
lex->col_list.push_back(new key_part_spec(field_name->str, 0));
lex->col_list.push_back(new Key_part_spec(field_name->str, 0));
key= new Key(Key::UNIQUE, NullS,
&default_key_create_info, 0,
lex->col_list);
@ -5548,7 +5545,7 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type,
WARN_DEPRECATED(thd, "5.2", buf, "'TIMESTAMP'");
}
if (!(new_field= new create_field()) ||
if (!(new_field= new Create_field()) ||
new_field->init(thd, field_name->str, type, length, decimals, type_modifier,
default_value, on_update_value, comment, change,
interval_list, cs, uint_geom_type))
@ -7134,3 +7131,34 @@ bool check_string_char_length(LEX_STRING *str, const char *err_msg,
my_error(ER_WRONG_STRING_LENGTH, MYF(0), str->str, err_msg, max_char_length);
return TRUE;
}
extern int MYSQLparse(void *thd); // from sql_yacc.cc
/**
This is a wrapper of MYSQLparse(). All the code should call parse_sql()
instead of MYSQLparse().
@param thd Thread context.
@param lip Lexer context.
@return Error status.
@retval FALSE on success.
@retval TRUE on parsing error.
*/
bool parse_sql(THD *thd, Lex_input_stream *lip)
{
bool err_status;
DBUG_ASSERT(thd->m_lip == NULL);
thd->m_lip= lip;
err_status= MYSQLparse(thd) != 0 || thd->is_fatal_error;
thd->m_lip= NULL;
return err_status;
}

View file

@ -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;

View file

@ -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)

View file

@ -10083,12 +10083,12 @@ err:
0 if out of memory, TABLE object in case of success
*/
TABLE *create_virtual_tmp_table(THD *thd, List<create_field> &field_list)
TABLE *create_virtual_tmp_table(THD *thd, List<Create_field> &field_list)
{
uint field_count= field_list.elements;
uint blob_count= 0;
Field **field;
create_field *cdef; /* column definition */
Create_field *cdef; /* column definition */
uint record_length= 0;
uint null_count= 0; /* number of columns which may be null */
uint null_pack_length; /* NULL representation array length */
@ -10116,7 +10116,7 @@ TABLE *create_virtual_tmp_table(THD *thd, List<create_field> &field_list)
setup_tmp_table_column_bitmaps(table, bitmaps);
/* Create all fields and calculate the total length of record */
List_iterator_fast<create_field> it(field_list);
List_iterator_fast<Create_field> it(field_list);
while ((cdef= it++))
{
*field= make_field(share, 0, cdef->length,

View file

@ -34,13 +34,13 @@ const char *primary_key_name="PRIMARY";
static bool check_if_keyname_exists(const char *name,KEY *start, KEY *end);
static char *make_unique_key_name(const char *field_name,KEY *start,KEY *end);
static int copy_data_between_tables(TABLE *from,TABLE *to,
List<create_field> &create, bool ignore,
List<Create_field> &create, bool ignore,
uint order_num, ORDER *order,
ha_rows *copied,ha_rows *deleted,
enum enum_enable_or_disable keys_onoff,
bool error_if_not_empty);
static bool prepare_blob_field(THD *thd, create_field *sql_field);
static bool prepare_blob_field(THD *thd, Create_field *sql_field);
static bool check_engine(THD *, const char *, HA_CREATE_INFO *);
static bool
mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
@ -1939,7 +1939,7 @@ void calculate_interval_lengths(CHARSET_INFO *cs, TYPELIB *interval,
table_flags table flags
DESCRIPTION
This function prepares a create_field instance.
This function prepares a Create_field instance.
Fields such as pack_flag are valid after this call.
RETURN VALUES
@ -1947,7 +1947,7 @@ void calculate_interval_lengths(CHARSET_INFO *cs, TYPELIB *interval,
1 Error
*/
int prepare_create_field(create_field *sql_field,
int prepare_create_field(Create_field *sql_field,
uint *blob_columns,
int *timestamps, int *timestamps_with_niladic,
longlong table_flags)
@ -2138,7 +2138,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
uint *key_count, int select_field_count)
{
const char *key_name;
create_field *sql_field,*dup_field;
Create_field *sql_field,*dup_field;
uint field,null_fields,blob_columns,max_key_length;
ulong record_offset= 0;
KEY *key_info;
@ -2146,8 +2146,8 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
int timestamps= 0, timestamps_with_niladic= 0;
int field_no,dup_no;
int select_field_pos,auto_increment=0;
List_iterator<create_field> it(alter_info->create_list);
List_iterator<create_field> it2(alter_info->create_list);
List_iterator<Create_field> it(alter_info->create_list);
List_iterator<Create_field> it2(alter_info->create_list);
uint total_uneven_bit_length= 0;
DBUG_ENTER("mysql_prepare_create_table");
@ -2201,7 +2201,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
sql_field->sql_type == MYSQL_TYPE_ENUM))
{
/*
Starting from 5.1 we work here with a copy of create_field
Starting from 5.1 we work here with a copy of Create_field
created by the caller, not with the instance that was
originally created during parsing. It's OK to create
a temporary item and initialize with it a member of the
@ -2492,7 +2492,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
if (key->type == Key::FOREIGN_KEY)
{
fk_key_count++;
foreign_key *fk_key= (foreign_key*) key;
Foreign_key *fk_key= (Foreign_key*) key;
if (fk_key->ref_columns.elements &&
fk_key->ref_columns.elements != fk_key->columns.elements)
{
@ -2576,7 +2576,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
for (; (key=key_iterator++) ; key_number++)
{
uint key_length=0;
key_part_spec *column;
Key_part_spec *column;
if (key->name == ignore_key)
{
@ -2685,12 +2685,12 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
if (key_info->block_size)
key_info->flags|= HA_USES_BLOCK_SIZE;
List_iterator<key_part_spec> cols(key->columns), cols2(key->columns);
List_iterator<Key_part_spec> cols(key->columns), cols2(key->columns);
CHARSET_INFO *ft_key_charset=0; // for FULLTEXT
for (uint column_nr=0 ; (column=cols++) ; column_nr++)
{
uint length;
key_part_spec *dup_column;
Key_part_spec *dup_column;
it.rewind();
field=0;
@ -3000,7 +3000,7 @@ static void set_table_default_charset(THD *thd,
In this case the error is given
*/
static bool prepare_blob_field(THD *thd, create_field *sql_field)
static bool prepare_blob_field(THD *thd, Create_field *sql_field)
{
DBUG_ENTER("prepare_blob_field");
@ -3041,7 +3041,7 @@ static bool prepare_blob_field(THD *thd, create_field *sql_field)
/*
Preparation of create_field for SP function return values.
Preparation of Create_field for SP function return values.
Based on code used in the inner loop of mysql_prepare_create_table()
above.
@ -3055,7 +3055,7 @@ static bool prepare_blob_field(THD *thd, create_field *sql_field)
*/
void sp_prepare_create_field(THD *thd, create_field *sql_field)
void sp_prepare_create_field(THD *thd, Create_field *sql_field)
{
if (sql_field->sql_type == MYSQL_TYPE_SET ||
sql_field->sql_type == MYSQL_TYPE_ENUM)
@ -4950,8 +4950,8 @@ compare_tables(TABLE *table,
Field **f_ptr, *field;
uint changes= 0, tmp;
uint key_count;
List_iterator_fast<create_field> new_field_it(alter_info->create_list);
create_field *new_field;
List_iterator_fast<Create_field> new_field_it(alter_info->create_list);
Create_field *new_field;
KEY_PART_INFO *key_part;
KEY_PART_INFO *end;
/*
@ -5298,16 +5298,16 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
Alter_info *alter_info)
{
/* New column definitions are added here */
List<create_field> new_create_list;
List<Create_field> new_create_list;
/* New key definitions are added here */
List<Key> new_key_list;
List_iterator<Alter_drop> drop_it(alter_info->drop_list);
List_iterator<create_field> def_it(alter_info->create_list);
List_iterator<Create_field> def_it(alter_info->create_list);
List_iterator<Alter_column> alter_it(alter_info->alter_list);
List_iterator<Key> key_it(alter_info->key_list);
List_iterator<create_field> find_it(new_create_list);
List_iterator<create_field> field_it(new_create_list);
List<key_part_spec> key_parts;
List_iterator<Create_field> find_it(new_create_list);
List_iterator<Create_field> field_it(new_create_list);
List<Key_part_spec> key_parts;
uint db_create_options= (table->s->db_create_options
& ~(HA_OPTION_PACK_RECORD));
uint used_fields= create_info->used_fields;
@ -5347,7 +5347,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
create_info->tablespace= tablespace;
}
restore_record(table, s->default_values); // Empty record for DEFAULT
create_field *def;
Create_field *def;
/*
First collect all fields from table which isn't in drop_list
@ -5403,7 +5403,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
This field was not dropped and not changed, add it to the list
for the new table.
*/
def= new create_field(field, field);
def= new Create_field(field, field);
new_create_list.push_back(def);
alter_it.rewind(); // Change default if ALTER
Alter_column *alter;
@ -5458,7 +5458,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
new_create_list.push_front(def);
else
{
create_field *find;
Create_field *find;
find_it.rewind();
while ((find=find_it++)) // Add new columns
{
@ -5516,7 +5516,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
if (!key_part->field)
continue; // Wrong field (from UNIREG)
const char *key_part_name=key_part->field->field_name;
create_field *cfield;
Create_field *cfield;
field_it.rewind();
while ((cfield=field_it++))
{
@ -5558,7 +5558,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
key_part_length= 0; // Use whole field
}
key_part_length /= key_part->field->charset()->mbmaxlen;
key_parts.push_back(new key_part_spec(cfield->field_name,
key_parts.push_back(new Key_part_spec(cfield->field_name,
key_part_length));
}
if (key_parts.elements)
@ -6763,7 +6763,7 @@ err_with_placeholders:
static int
copy_data_between_tables(TABLE *from,TABLE *to,
List<create_field> &create,
List<Create_field> &create,
bool ignore,
uint order_num, ORDER *order,
ha_rows *copied,
@ -6817,8 +6817,8 @@ copy_data_between_tables(TABLE *from,TABLE *to,
save_sql_mode= thd->variables.sql_mode;
List_iterator<create_field> it(create);
create_field *def;
List_iterator<Create_field> it(create);
Create_field *def;
copy_end=copy;
for (Field **ptr=to->field ; *ptr ; ptr++)
{

View file

@ -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);
}

View file

@ -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);

View file

@ -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;

View file

@ -106,7 +106,7 @@ void my_parse_error(const char *s)
THD *thd= current_thd;
Lex_input_stream *lip= thd->m_lip;
const char *yytext= lip->tok_start;
const char *yytext= lip->get_tok_start();
/* Push an error into the error stack */
my_printf_error(ER_PARSE_ERROR, ER(ER_PARSE_ERROR), MYF(0), s,
(yytext ? yytext : ""),
@ -456,7 +456,7 @@ Item* handle_sql2003_note184_exception(THD *thd, Item* left, bool equal,
List<Item> *item_list;
List<String> *string_list;
String *string;
key_part_spec *key_part;
Key_part_spec *key_part;
TABLE_LIST *table_list;
udf_func *udf;
LEX_USER *lex_user;
@ -1872,10 +1872,7 @@ ev_sql_stmt:
bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics));
lex->sphead->m_chistics= &lex->sp_chistics;
lex->sphead->m_body_begin= lip->ptr;
lex->event_parse_data->body_begin= lip->ptr;
lex->sphead->set_body_begin_ptr(lip, lip->get_cpp_ptr());
}
ev_sql_stmt_inner
{
@ -1888,7 +1885,7 @@ ev_sql_stmt:
lex->sp_chistics.suid= SP_IS_SUID; //always the definer!
lex->event_parse_data->init_body(thd);
lex->event_parse_data->body_changed= TRUE;
}
;
@ -1986,6 +1983,7 @@ create_function_tail:
LEX *lex= thd->lex;
Lex_input_stream *lip= thd->m_lip;
sp_head *sp;
const char* tmp_param_begin;
/*
First check if AGGREGATE was used, in that case it's a
@ -2017,7 +2015,10 @@ create_function_tail:
*/
$<ulong_num>$= thd->client_capabilities & CLIENT_MULTI_QUERIES;
thd->client_capabilities &= ~CLIENT_MULTI_QUERIES;
lex->sphead->m_param_begin= lip->tok_start+1;
tmp_param_begin= lip->get_cpp_tok_start();
tmp_param_begin++;
lex->sphead->m_param_begin= tmp_param_begin;
}
sp_fdparam_list ')'
{
@ -2025,7 +2026,7 @@ create_function_tail:
LEX *lex= thd->lex;
Lex_input_stream *lip= thd->m_lip;
lex->sphead->m_param_end= lip->tok_start;
lex->sphead->m_param_end= lip->get_cpp_tok_start();
}
RETURNS_SYM
{
@ -2065,7 +2066,7 @@ create_function_tail:
Lex_input_stream *lip= thd->m_lip;
lex->sphead->m_chistics= &lex->sp_chistics;
lex->sphead->m_body_begin= lip->tok_start;
lex->sphead->set_body_begin_ptr(lip, lip->get_cpp_tok_start());
}
sp_proc_stmt
{
@ -2676,7 +2677,7 @@ sp_proc_stmt_statement:
Lex_input_stream *lip= thd->m_lip;
lex->sphead->reset_lex(thd);
lex->sphead->m_tmp_query= lip->tok_start;
lex->sphead->m_tmp_query= lip->get_tok_start();
}
statement
{
@ -2709,9 +2710,9 @@ sp_proc_stmt_statement:
lex->tok_end otherwise.
*/
if (yychar == YYEMPTY)
i->m_query.length= lip->ptr - sp->m_tmp_query;
i->m_query.length= lip->get_ptr() - sp->m_tmp_query;
else
i->m_query.length= lip->tok_end - sp->m_tmp_query;
i->m_query.length= lip->get_tok_end() - sp->m_tmp_query;
i->m_query.str= strmake_root(thd->mem_root,
sp->m_tmp_query,
i->m_query.length);
@ -4498,7 +4499,7 @@ key_def:
{
LEX *lex=Lex;
const char *key_name= $4 ? $4 : $1;
Key *key= new foreign_key(key_name, lex->col_list,
Key *key= new Foreign_key(key_name, lex->col_list,
$8,
lex->ref_list,
lex->fk_delete_opt,
@ -4925,8 +4926,8 @@ opt_ref_list:
| '(' ref_list ')' opt_on_delete {};
ref_list:
ref_list ',' ident { Lex->ref_list.push_back(new key_part_spec($3.str)); }
| ident { Lex->ref_list.push_back(new key_part_spec($1.str)); };
ref_list ',' ident { Lex->ref_list.push_back(new Key_part_spec($3.str)); }
| ident { Lex->ref_list.push_back(new Key_part_spec($1.str)); };
opt_on_delete:
@ -4940,16 +4941,16 @@ opt_on_delete_list:
opt_on_delete_item:
ON DELETE_SYM delete_option { Lex->fk_delete_opt= $3; }
| ON UPDATE_SYM delete_option { Lex->fk_update_opt= $3; }
| MATCH FULL { Lex->fk_match_option= foreign_key::FK_MATCH_FULL; }
| MATCH PARTIAL { Lex->fk_match_option= foreign_key::FK_MATCH_PARTIAL; }
| MATCH SIMPLE_SYM { Lex->fk_match_option= foreign_key::FK_MATCH_SIMPLE; };
| MATCH FULL { Lex->fk_match_option= Foreign_key::FK_MATCH_FULL; }
| MATCH PARTIAL { Lex->fk_match_option= Foreign_key::FK_MATCH_PARTIAL; }
| MATCH SIMPLE_SYM { Lex->fk_match_option= Foreign_key::FK_MATCH_SIMPLE; };
delete_option:
RESTRICT { $$= (int) foreign_key::FK_OPTION_RESTRICT; }
| CASCADE { $$= (int) foreign_key::FK_OPTION_CASCADE; }
| SET NULL_SYM { $$= (int) foreign_key::FK_OPTION_SET_NULL; }
| NO_SYM ACTION { $$= (int) foreign_key::FK_OPTION_NO_ACTION; }
| SET DEFAULT { $$= (int) foreign_key::FK_OPTION_DEFAULT; };
RESTRICT { $$= (int) Foreign_key::FK_OPTION_RESTRICT; }
| CASCADE { $$= (int) Foreign_key::FK_OPTION_CASCADE; }
| SET NULL_SYM { $$= (int) Foreign_key::FK_OPTION_SET_NULL; }
| NO_SYM ACTION { $$= (int) Foreign_key::FK_OPTION_NO_ACTION; }
| SET DEFAULT { $$= (int) Foreign_key::FK_OPTION_DEFAULT; };
key_type:
key_or_index { $$= Key::MULTIPLE; }
@ -5061,7 +5062,7 @@ key_list:
| key_part order_dir { Lex->col_list.push_back($1); };
key_part:
ident { $$=new key_part_spec($1.str); }
ident { $$=new Key_part_spec($1.str); }
| ident '(' NUM ')'
{
int key_part_len= atoi($3.str);
@ -5069,7 +5070,7 @@ key_part:
{
my_error(ER_KEY_PART_0, MYF(0), $1.str);
}
$$=new key_part_spec($1.str,(uint) key_part_len);
$$=new Key_part_spec($1.str,(uint) key_part_len);
};
opt_ident:
@ -6225,14 +6226,14 @@ remember_name:
{
THD *thd= YYTHD;
Lex_input_stream *lip= thd->m_lip;
$$= (char*) lip->tok_start;
$$= (char*) lip->get_cpp_tok_start();
};
remember_end:
{
THD *thd= YYTHD;
Lex_input_stream *lip= thd->m_lip;
$$=(char*) lip->tok_end;
$$= (char*) lip->get_cpp_tok_end();
};
select_item2:
@ -7999,16 +8000,14 @@ procedure_list2:
| procedure_item;
procedure_item:
remember_name expr
remember_name expr remember_end
{
THD *thd= YYTHD;
Lex_input_stream *lip= thd->m_lip;
if (add_proc_to_list(thd, $2))
MYSQL_YYABORT;
if (!$2->name)
$2->set_name($1,(uint) ((char*) lip->tok_end - $1),
thd->charset());
$2->set_name($1, (uint) ($3 - $1), thd->charset());
}
;
@ -9108,7 +9107,7 @@ load: LOAD DATA_SYM
my_error(ER_SP_BADSTATEMENT, MYF(0), "LOAD DATA");
MYSQL_YYABORT;
}
lex->fname_start= lip->ptr;
lex->fname_start= lip->get_ptr();
}
load_data
{}
@ -9145,7 +9144,7 @@ load_data:
THD *thd= YYTHD;
LEX *lex= thd->lex;
Lex_input_stream *lip= thd->m_lip;
lex->fname_end= lip->ptr;
lex->fname_end= lip->get_ptr();
}
TABLE_SYM table_ident
{
@ -9334,7 +9333,7 @@ param_marker:
my_error(ER_VIEW_SELECT_VARIABLE, MYF(0));
MYSQL_YYABORT;
}
item= new Item_param((uint) (lip->tok_start - thd->query));
item= new Item_param((uint) (lip->get_tok_start() - thd->query));
if (!($$= item) || lex->param_list.push_back(item))
{
my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
@ -9467,7 +9466,7 @@ simple_ident:
Item_splocal *splocal;
splocal= new Item_splocal($1, spv->offset, spv->type,
lip->tok_start_prev -
lip->get_tok_start_prev() -
lex->sphead->m_tmp_query);
#ifndef DBUG_OFF
if (splocal)
@ -10143,7 +10142,7 @@ option_type_value:
lex->option_type=OPT_SESSION;
lex->var_list.empty();
lex->one_shot_set= 0;
lex->sphead->m_tmp_query= lip->tok_start;
lex->sphead->m_tmp_query= lip->get_tok_start();
}
}
ext_option_value
@ -10176,9 +10175,9 @@ option_type_value:
lip->tok_end otherwise.
*/
if (yychar == YYEMPTY)
qbuff.length= lip->ptr - sp->m_tmp_query;
qbuff.length= lip->get_ptr() - sp->m_tmp_query;
else
qbuff.length= lip->tok_end - sp->m_tmp_query;
qbuff.length= lip->get_tok_end() - sp->m_tmp_query;
if (!(qbuff.str= (char*) alloc_root(thd->mem_root,
qbuff.length + 5)))
@ -11360,7 +11359,7 @@ view_tail:
if (!lex->select_lex.add_table_to_list(thd, $3, NULL, TL_OPTION_UPDATING))
MYSQL_YYABORT;
}
view_list_opt AS view_select view_check_option
view_list_opt AS view_select
{}
;
@ -11385,40 +11384,32 @@ view_list:
view_select:
{
THD *thd= YYTHD;
LEX *lex= Lex;
Lex_input_stream *lip= thd->m_lip;
lex->parsing_options.allows_variable= FALSE;
lex->parsing_options.allows_select_into= FALSE;
lex->parsing_options.allows_select_procedure= FALSE;
lex->parsing_options.allows_derived= FALSE;
}
view_select_aux
lex->create_view_select_start= lip->get_cpp_ptr();
}
view_select_aux view_check_option
{
THD *thd= YYTHD;
LEX *lex= Lex;
Lex_input_stream *lip= thd->m_lip;
lex->parsing_options.allows_variable= TRUE;
lex->parsing_options.allows_select_into= TRUE;
lex->parsing_options.allows_select_procedure= TRUE;
lex->parsing_options.allows_derived= TRUE;
lex->create_view_select_end= lip->get_cpp_ptr();
}
;
view_select_aux:
SELECT_SYM remember_name select_init2
{
THD *thd= YYTHD;
LEX *lex= thd->lex;
const char *stmt_beg= (lex->sphead ?
lex->sphead->m_tmp_query : thd->query);
lex->create_view_select_start= $2 - stmt_beg;
}
| '(' remember_name select_paren ')' union_opt
{
THD *thd= YYTHD;
LEX *lex= thd->lex;
const char *stmt_beg= (lex->sphead ?
lex->sphead->m_tmp_query : thd->query);
lex->create_view_select_start= $2 - stmt_beg;
}
;
SELECT_SYM select_init2
| '(' select_paren ')' union_opt
;
view_check_option:
/* empty */
@ -11438,9 +11429,31 @@ view_check_option:
**************************************************************************/
trigger_tail:
TRIGGER_SYM remember_name sp_name trg_action_time trg_event
ON remember_name table_ident FOR_SYM remember_name EACH_SYM ROW_SYM
{
TRIGGER_SYM
remember_name
sp_name
trg_action_time
trg_event
ON
remember_name /* $7 */
{ /* $8 */
THD *thd= YYTHD;
LEX *lex= thd->lex;
Lex_input_stream *lip= thd->m_lip;
lex->raw_trg_on_table_name_begin= lip->get_tok_start();
}
table_ident /* $9 */
FOR_SYM
remember_name /* $11 */
{ /* $12 */
THD *thd= YYTHD;
LEX *lex= thd->lex;
Lex_input_stream *lip= thd->m_lip;
lex->raw_trg_on_table_name_end= lip->get_tok_start();
}
EACH_SYM
ROW_SYM
{ /* $15 */
THD *thd= YYTHD;
LEX *lex= thd->lex;
Lex_input_stream *lip= thd->m_lip;
@ -11459,7 +11472,7 @@ trigger_tail:
sp->init_sp_name(thd, $3);
lex->stmt_definition_begin= $2;
lex->ident.str= $7;
lex->ident.length= $10 - $7;
lex->ident.length= $11 - $7;
sp->m_type= TYPE_ENUM_TRIGGER;
lex->sphead= sp;
@ -11474,12 +11487,10 @@ trigger_tail:
bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics));
lex->sphead->m_chistics= &lex->sp_chistics;
lex->sphead->m_body_begin= lip->ptr;
while (my_isspace(system_charset_info, lex->sphead->m_body_begin[0]))
++lex->sphead->m_body_begin;
lex->sphead->set_body_begin_ptr(lip, lip->get_cpp_ptr());
}
sp_proc_stmt
{
sp_proc_stmt /* $16 */
{ /* $17 */
LEX *lex= Lex;
sp_head *sp= lex->sphead;
@ -11487,7 +11498,7 @@ trigger_tail:
sp->init_strings(YYTHD, lex);
/* Restore flag if it was cleared above */
YYTHD->client_capabilities |= $<ulong_num>13;
YYTHD->client_capabilities |= $<ulong_num>15;
sp->restore_thd_mem_root(YYTHD);
if (sp->is_not_allowed_in_function("trigger"))
@ -11498,7 +11509,7 @@ trigger_tail:
sp_proc_stmt alternatives are not saving/restoring LEX, so
lex->query_tables can be wiped out.
*/
if (!lex->select_lex.add_table_to_list(YYTHD, $8,
if (!lex->select_lex.add_table_to_list(YYTHD, $9,
(LEX_STRING*) 0,
TL_OPTION_UPDATING,
TL_IGNORE))
@ -11556,8 +11567,11 @@ sp_tail:
THD *thd= YYTHD;
LEX *lex= thd->lex;
Lex_input_stream *lip= thd->m_lip;
const char* tmp_param_begin;
lex->sphead->m_param_begin= lip->tok_start+1;
tmp_param_begin= lip->get_cpp_tok_start();
tmp_param_begin++;
lex->sphead->m_param_begin= tmp_param_begin;
}
sp_pdparam_list
')'
@ -11566,7 +11580,7 @@ sp_tail:
LEX *lex= thd->lex;
Lex_input_stream *lip= thd->m_lip;
lex->sphead->m_param_end= lip->tok_start;
lex->sphead->m_param_end= lip->get_cpp_tok_start();
bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics));
}
sp_c_chistics
@ -11576,7 +11590,7 @@ sp_tail:
Lex_input_stream *lip= thd->m_lip;
lex->sphead->m_chistics= &lex->sp_chistics;
lex->sphead->m_body_begin= lip->tok_start;
lex->sphead->set_body_begin_ptr(lip, lip->get_cpp_tok_start());
}
sp_proc_stmt
{

View file

@ -29,21 +29,21 @@
#define FCOMP 17 /* Bytes for a packed field */
static uchar * pack_screens(List<create_field> &create_fields,
static uchar * pack_screens(List<Create_field> &create_fields,
uint *info_length, uint *screens, bool small_file);
static uint pack_keys(uchar *keybuff,uint key_count, KEY *key_info,
ulong data_offset);
static bool pack_header(uchar *forminfo,enum legacy_db_type table_type,
List<create_field> &create_fields,
List<Create_field> &create_fields,
uint info_length, uint screens, uint table_options,
ulong data_offset, handler *file);
static uint get_interval_id(uint *int_count,List<create_field> &create_fields,
create_field *last_field);
static bool pack_fields(File file, List<create_field> &create_fields,
static uint get_interval_id(uint *int_count,List<Create_field> &create_fields,
Create_field *last_field);
static bool pack_fields(File file, List<Create_field> &create_fields,
ulong data_offset);
static bool make_empty_rec(THD *thd, int file, enum legacy_db_type table_type,
uint table_options,
List<create_field> &create_fields,
List<Create_field> &create_fields,
uint reclength, ulong data_offset,
handler *handler);
@ -70,7 +70,7 @@ static bool make_empty_rec(THD *thd, int file, enum legacy_db_type table_type,
bool mysql_create_frm(THD *thd, const char *file_name,
const char *db, const char *table,
HA_CREATE_INFO *create_info,
List<create_field> &create_fields,
List<Create_field> &create_fields,
uint keys, KEY *key_info,
handler *db_file)
{
@ -294,8 +294,8 @@ bool mysql_create_frm(THD *thd, const char *file_name,
Restore all UCS2 intervals.
HEX representation of them is not needed anymore.
*/
List_iterator<create_field> it(create_fields);
create_field *field;
List_iterator<Create_field> it(create_fields);
Create_field *field;
while ((field=it++))
{
if (field->save_interval)
@ -341,7 +341,7 @@ err3:
int rea_create_table(THD *thd, const char *path,
const char *db, const char *table_name,
HA_CREATE_INFO *create_info,
List<create_field> &create_fields,
List<Create_field> &create_fields,
uint keys, KEY *key_info, handler *file)
{
DBUG_ENTER("rea_create_table");
@ -371,7 +371,7 @@ err_handler:
/* Pack screens to a screen for save in a form-file */
static uchar *pack_screens(List<create_field> &create_fields,
static uchar *pack_screens(List<Create_field> &create_fields,
uint *info_length, uint *screens,
bool small_file)
{
@ -380,7 +380,7 @@ static uchar *pack_screens(List<create_field> &create_fields,
uint length,cols;
uchar *info,*pos,*start_screen;
uint fields=create_fields.elements;
List_iterator<create_field> it(create_fields);
List_iterator<Create_field> it(create_fields);
DBUG_ENTER("pack_screens");
start_row=4; end_row=22; cols=80; fields_on_screen=end_row+1-start_row;
@ -388,7 +388,7 @@ static uchar *pack_screens(List<create_field> &create_fields,
*screens=(fields-1)/fields_on_screen+1;
length= (*screens) * (SC_INFO_LENGTH+ (cols>> 1)+4);
create_field *field;
Create_field *field;
while ((field=it++))
length+=(uint) strlen(field->field_name)+1+TE_INFO_LENGTH+cols/2;
@ -401,7 +401,7 @@ static uchar *pack_screens(List<create_field> &create_fields,
it.rewind();
for (i=0 ; i < fields ; i++)
{
create_field *cfield=it++;
Create_field *cfield=it++;
if (row++ == end_row)
{
if (i)
@ -521,7 +521,7 @@ static uint pack_keys(uchar *keybuff, uint key_count, KEY *keyinfo,
/* Make formheader */
static bool pack_header(uchar *forminfo, enum legacy_db_type table_type,
List<create_field> &create_fields,
List<Create_field> &create_fields,
uint info_length, uint screens, uint table_options,
ulong data_offset, handler *file)
{
@ -544,8 +544,8 @@ static bool pack_header(uchar *forminfo, enum legacy_db_type table_type,
/* Check fields */
List_iterator<create_field> it(create_fields);
create_field *field;
List_iterator<Create_field> it(create_fields);
Create_field *field;
while ((field=it++))
{
uint tmp_len= system_charset_info->cset->charpos(system_charset_info,
@ -687,11 +687,11 @@ static bool pack_header(uchar *forminfo, enum legacy_db_type table_type,
/* get each unique interval each own id */
static uint get_interval_id(uint *int_count,List<create_field> &create_fields,
create_field *last_field)
static uint get_interval_id(uint *int_count,List<Create_field> &create_fields,
Create_field *last_field)
{
List_iterator<create_field> it(create_fields);
create_field *field;
List_iterator<Create_field> it(create_fields);
Create_field *field;
TYPELIB *interval=last_field->interval;
while ((field=it++) != last_field)
@ -715,18 +715,18 @@ static uint get_interval_id(uint *int_count,List<create_field> &create_fields,
/* Save fields, fieldnames and intervals */
static bool pack_fields(File file, List<create_field> &create_fields,
static bool pack_fields(File file, List<Create_field> &create_fields,
ulong data_offset)
{
reg2 uint i;
uint int_count, comment_length=0;
uchar buff[MAX_FIELD_WIDTH];
create_field *field;
Create_field *field;
DBUG_ENTER("pack_fields");
/* Write field info */
List_iterator<create_field> it(create_fields);
List_iterator<Create_field> it(create_fields);
int_count=0;
while ((field=it++))
@ -856,7 +856,7 @@ static bool pack_fields(File file, List<create_field> &create_fields,
static bool make_empty_rec(THD *thd, File file,enum legacy_db_type table_type,
uint table_options,
List<create_field> &create_fields,
List<Create_field> &create_fields,
uint reclength,
ulong data_offset,
handler *handler)
@ -867,7 +867,7 @@ static bool make_empty_rec(THD *thd, File file,enum legacy_db_type table_type,
uchar *buff,*null_pos;
TABLE table;
TABLE_SHARE share;
create_field *field;
Create_field *field;
enum_check_fields old_count_cuted_fields= thd->count_cuted_fields;
DBUG_ENTER("make_empty_rec");
@ -893,7 +893,7 @@ static bool make_empty_rec(THD *thd, File file,enum legacy_db_type table_type,
}
null_pos= buff;
List_iterator<create_field> it(create_fields);
List_iterator<Create_field> it(create_fields);
thd->count_cuted_fields= CHECK_FIELD_WARN; // To find wrong default values
while ((field=it++))
{

View file

@ -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))

View file

@ -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 }
};