Commit d098bfff authored by unknown's avatar unknown

Merge malff@bk-internal.mysql.com:/home/bk/mysql-5.1-runtime

into  weblab.(none):/home/marcsql/TREE/mysql-5.1-27857

parents 8b8b2888 83de46bc
...@@ -904,6 +904,14 @@ extern CHARSET_INFO *get_charset(uint cs_number, myf flags); ...@@ -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_name(const char *cs_name, myf flags);
extern CHARSET_INFO *get_charset_by_csname(const char *cs_name, extern CHARSET_INFO *get_charset_by_csname(const char *cs_name,
uint cs_flags, myf my_flags); 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 void free_charsets(void);
extern char *get_charsets_dir(char *buf); extern char *get_charsets_dir(char *buf);
extern my_bool my_charset_same(CHARSET_INFO *cs1, CHARSET_INFO *cs2); extern my_bool my_charset_same(CHARSET_INFO *cs1, CHARSET_INFO *cs2);
......
...@@ -18,3 +18,4 @@ drop database events_test; ...@@ -18,3 +18,4 @@ drop database events_test;
let $wait_condition= let $wait_condition=
select count(*) = 0 from information_schema.processlist select count(*) = 0 from information_schema.processlist
where db='events_test' and command = 'Connect' and user=current_user(); where db='events_test' and command = 'Connect' and user=current_user();
--source include/wait_condition.inc
...@@ -573,6 +573,70 @@ CHARSET_INFO *get_charset_by_csname(const char *cs_name, ...@@ -573,6 +573,70 @@ CHARSET_INFO *get_charset_by_csname(const char *cs_name,
} }
/**
Resolve character set by the character set name (utf8, latin1, ...).
The function tries to resolve character set by the specified name. If
there is character set with the given name, it is assigned to the "cs"
parameter and FALSE is returned. If there is no such character set,
"default_cs" is assigned to the "cs" and TRUE is returned.
@param[out] cs Variable to store character set.
@param[in] cs_name Character set name.
@param[in] default_cs Default character set.
@return FALSE if character set was resolved successfully; TRUE if there
is no character set with given name.
*/
bool resolve_charset(CHARSET_INFO **cs,
const char *cs_name,
CHARSET_INFO *default_cs)
{
*cs= get_charset_by_csname(cs_name, MY_CS_PRIMARY, MYF(0));
if (*cs == NULL)
{
*cs= default_cs;
return TRUE;
}
return FALSE;
}
/**
Resolve collation by the collation name (utf8_general_ci, ...).
The function tries to resolve collation by the specified name. If there
is collation with the given name, it is assigned to the "cl" parameter
and FALSE is returned. If there is no such collation, "default_cl" is
assigned to the "cl" and TRUE is returned.
@param[out] cl Variable to store collation.
@param[in] cl_name Collation name.
@param[in] default_cl Default collation.
@return FALSE if collation was resolved successfully; TRUE if there is no
collation with given name.
*/
bool resolve_collation(CHARSET_INFO **cl,
const char *cl_name,
CHARSET_INFO *default_cl)
{
*cl= get_charset_by_name(cl_name, MYF(0));
if (*cl == NULL)
{
*cl= default_cl;
return TRUE;
}
return FALSE;
}
/* /*
Escape string with backslashes (\) Escape string with backslashes (\)
......
...@@ -94,17 +94,18 @@ Event_parse_data::Event_parse_data() ...@@ -94,17 +94,18 @@ Event_parse_data::Event_parse_data()
:on_completion(Event_basic::ON_COMPLETION_DROP), :on_completion(Event_basic::ON_COMPLETION_DROP),
status(Event_basic::ENABLED), status(Event_basic::ENABLED),
do_not_create(FALSE), do_not_create(FALSE),
item_starts(NULL), item_ends(NULL), item_execute_at(NULL), body_changed(FALSE),
starts_null(TRUE), ends_null(TRUE), execute_at_null(TRUE), item_starts(NULL), item_ends(NULL), item_execute_at(NULL),
item_expression(NULL), expression(0) starts_null(TRUE), ends_null(TRUE), execute_at_null(TRUE),
item_expression(NULL), expression(0)
{ {
DBUG_ENTER("Event_parse_data::Event_parse_data"); DBUG_ENTER("Event_parse_data::Event_parse_data");
/* Actually in the parser STARTS is always set */ /* Actually in the parser STARTS is always set */
starts= ends= execute_at= 0; starts= ends= execute_at= 0;
body.str= comment.str= NULL; comment.str= NULL;
body.length= comment.length= 0; comment.length= 0;
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
...@@ -137,36 +138,6 @@ Event_parse_data::init_name(THD *thd, sp_name *spn) ...@@ -137,36 +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.
See related code in sp_head::init_strings().
*/
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);
body.length= thd->m_lip->get_cpp_ptr() - body_begin;
body.str= thd->strmake(body_begin, body.length);
trim_whitespace(thd->charset(), & body);
DBUG_VOID_RETURN;
}
/* /*
This function is called on CREATE EVENT or ALTER EVENT. When either 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 ENDS or AT is in the past, we are trying to create an event that
...@@ -788,36 +759,32 @@ Event_timed::init() ...@@ -788,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.
SYNOPSIS Callers should handle throwing of error messages. The reason is that the
Event_job_data::load_from_row(THD *thd, TABLE *table) class should not know about how to deal with communication.
RETURN VALUE @return Operation status
0 OK @retval FALSE OK
EVEX_GET_FIELD_FAILED Error @retval TRUE 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.
*/ */
int bool
Event_job_data::load_from_row(THD *thd, TABLE *table) Event_job_data::load_from_row(THD *thd, TABLE *table)
{ {
char *ptr; char *ptr;
uint len; uint len;
LEX_STRING tz_name;
DBUG_ENTER("Event_job_data::load_from_row"); DBUG_ENTER("Event_job_data::load_from_row");
if (!table) if (!table)
goto error; DBUG_RETURN(TRUE);
if (table->s->fields < ET_FIELD_COUNT) if (table->s->fields < ET_FIELD_COUNT)
goto error; DBUG_RETURN(TRUE);
LEX_STRING tz_name;
if (load_string_fields(table->field, if (load_string_fields(table->field,
ET_FIELD_DB, &dbname, ET_FIELD_DB, &dbname,
ET_FIELD_NAME, &name, ET_FIELD_NAME, &name,
...@@ -825,10 +792,10 @@ Event_job_data::load_from_row(THD *thd, TABLE *table) ...@@ -825,10 +792,10 @@ Event_job_data::load_from_row(THD *thd, TABLE *table)
ET_FIELD_DEFINER, &definer, ET_FIELD_DEFINER, &definer,
ET_FIELD_TIME_ZONE, &tz_name, ET_FIELD_TIME_ZONE, &tz_name,
ET_FIELD_COUNT)) ET_FIELD_COUNT))
goto error; DBUG_RETURN(TRUE);
if (load_time_zone(thd, tz_name)) if (load_time_zone(thd, tz_name))
goto error; DBUG_RETURN(TRUE);
ptr= strchr(definer.str, '@'); ptr= strchr(definer.str, '@');
...@@ -845,29 +812,23 @@ Event_job_data::load_from_row(THD *thd, TABLE *table) ...@@ -845,29 +812,23 @@ Event_job_data::load_from_row(THD *thd, TABLE *table)
sql_mode= (ulong) table->field[ET_FIELD_SQL_MODE]->val_int(); sql_mode= (ulong) table->field[ET_FIELD_SQL_MODE]->val_int();
DBUG_RETURN(0); DBUG_RETURN(FALSE);
error:
DBUG_RETURN(EVEX_GET_FIELD_FAILED);
} }
/* /**
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)
RETURN VALUE @details This method is silent on errors and should behave like that.
0 OK Callers should handle throwing of error messages. The reason is that the
EVEX_GET_FIELD_FAILED Error class should not know about how to deal with communication.
NOTES @return Operation status
This method is silent on errors and should behave like that. Callers @retval FALSE OK
should handle throwing of error messages. The reason is that the class @retval TRUE Error
should not know about how to deal with communication.
*/ */
int bool
Event_queue_element::load_from_row(THD *thd, TABLE *table) Event_queue_element::load_from_row(THD *thd, TABLE *table)
{ {
char *ptr; char *ptr;
...@@ -877,10 +838,10 @@ Event_queue_element::load_from_row(THD *thd, TABLE *table) ...@@ -877,10 +838,10 @@ Event_queue_element::load_from_row(THD *thd, TABLE *table)
DBUG_ENTER("Event_queue_element::load_from_row"); DBUG_ENTER("Event_queue_element::load_from_row");
if (!table) if (!table)
goto error; DBUG_RETURN(TRUE);
if (table->s->fields < ET_FIELD_COUNT) if (table->s->fields < ET_FIELD_COUNT)
goto error; DBUG_RETURN(TRUE);
if (load_string_fields(table->field, if (load_string_fields(table->field,
ET_FIELD_DB, &dbname, ET_FIELD_DB, &dbname,
...@@ -888,10 +849,10 @@ Event_queue_element::load_from_row(THD *thd, TABLE *table) ...@@ -888,10 +849,10 @@ Event_queue_element::load_from_row(THD *thd, TABLE *table)
ET_FIELD_DEFINER, &definer, ET_FIELD_DEFINER, &definer,
ET_FIELD_TIME_ZONE, &tz_name, ET_FIELD_TIME_ZONE, &tz_name,
ET_FIELD_COUNT)) ET_FIELD_COUNT))
goto error; DBUG_RETURN(TRUE);
if (load_time_zone(thd, tz_name)) if (load_time_zone(thd, tz_name))
goto error; DBUG_RETURN(TRUE);
starts_null= table->field[ET_FIELD_STARTS]->is_null(); starts_null= table->field[ET_FIELD_STARTS]->is_null();
if (!starts_null) if (!starts_null)
...@@ -921,7 +882,7 @@ Event_queue_element::load_from_row(THD *thd, TABLE *table) ...@@ -921,7 +882,7 @@ Event_queue_element::load_from_row(THD *thd, TABLE *table)
{ {
if (table->field[ET_FIELD_EXECUTE_AT]->get_date(&time, if (table->field[ET_FIELD_EXECUTE_AT]->get_date(&time,
TIME_NO_ZERO_DATE)) TIME_NO_ZERO_DATE))
goto error; DBUG_RETURN(TRUE);
execute_at= sec_since_epoch_TIME(&time); execute_at= sec_since_epoch_TIME(&time);
} }
...@@ -940,13 +901,13 @@ Event_queue_element::load_from_row(THD *thd, TABLE *table) ...@@ -940,13 +901,13 @@ Event_queue_element::load_from_row(THD *thd, TABLE *table)
table->field[ET_FIELD_TRANSIENT_INTERVAL]->val_str(&str); table->field[ET_FIELD_TRANSIENT_INTERVAL]->val_str(&str);
if (!(tmp.length= str.length())) if (!(tmp.length= str.length()))
goto error; DBUG_RETURN(TRUE);
tmp.str= str.c_ptr_safe(); tmp.str= str.c_ptr_safe();
i= find_string_in_array(interval_type_to_name, &tmp, system_charset_info); i= find_string_in_array(interval_type_to_name, &tmp, system_charset_info);
if (i < 0) if (i < 0)
goto error; DBUG_RETURN(TRUE);
interval= (interval_type) i; interval= (interval_type) i;
} }
...@@ -959,7 +920,7 @@ Event_queue_element::load_from_row(THD *thd, TABLE *table) ...@@ -959,7 +920,7 @@ Event_queue_element::load_from_row(THD *thd, TABLE *table)
last_executed_changed= FALSE; last_executed_changed= FALSE;
if ((ptr= get_field(&mem_root, table->field[ET_FIELD_STATUS])) == NullS) 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)); DBUG_PRINT("load_from_row", ("Event [%s] is [%s]", name.str, ptr));
...@@ -978,40 +939,34 @@ Event_queue_element::load_from_row(THD *thd, TABLE *table) ...@@ -978,40 +939,34 @@ Event_queue_element::load_from_row(THD *thd, TABLE *table)
break; break;
} }
if ((ptr= get_field(&mem_root, table->field[ET_FIELD_ORIGINATOR])) == NullS) 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(); originator = table->field[ET_FIELD_ORIGINATOR]->val_int();
/* ToDo : Andrey . Find a way not to allocate ptr on event_mem_root */ /* ToDo : Andrey . Find a way not to allocate ptr on event_mem_root */
if ((ptr= get_field(&mem_root, if ((ptr= get_field(&mem_root,
table->field[ET_FIELD_ON_COMPLETION])) == NullS) table->field[ET_FIELD_ON_COMPLETION])) == NullS)
goto error; DBUG_RETURN(TRUE);
on_completion= (ptr[0]=='D'? Event_queue_element::ON_COMPLETION_DROP: on_completion= (ptr[0]=='D'? Event_queue_element::ON_COMPLETION_DROP:
Event_queue_element::ON_COMPLETION_PRESERVE); Event_queue_element::ON_COMPLETION_PRESERVE);
DBUG_RETURN(0); DBUG_RETURN(FALSE);
error:
DBUG_RETURN(EVEX_GET_FIELD_FAILED);
} }
/* /**
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)
RETURN VALUE @details This method is silent on errors and should behave like that.
0 OK Callers should handle throwing of error messages. The reason is that the
EVEX_GET_FIELD_FAILED Error class should not know about how to deal with communication.
NOTES @return Operation status
This method is silent on errors and should behave like that. Callers @retval FALSE OK
should handle throwing of error messages. The reason is that the class @retval TRUE Error
should not know about how to deal with communication.
*/ */
int bool
Event_timed::load_from_row(THD *thd, TABLE *table) Event_timed::load_from_row(THD *thd, TABLE *table)
{ {
char *ptr; char *ptr;
...@@ -1020,12 +975,12 @@ Event_timed::load_from_row(THD *thd, TABLE *table) ...@@ -1020,12 +975,12 @@ Event_timed::load_from_row(THD *thd, TABLE *table)
DBUG_ENTER("Event_timed::load_from_row"); DBUG_ENTER("Event_timed::load_from_row");
if (Event_queue_element::load_from_row(thd, table)) if (Event_queue_element::load_from_row(thd, table))
goto error; DBUG_RETURN(TRUE);
if (load_string_fields(table->field, if (load_string_fields(table->field,
ET_FIELD_BODY, &body, ET_FIELD_BODY, &body,
ET_FIELD_COUNT)) ET_FIELD_COUNT))
goto error; DBUG_RETURN(TRUE);
ptr= strchr(definer.str, '@'); ptr= strchr(definer.str, '@');
...@@ -1052,9 +1007,7 @@ Event_timed::load_from_row(THD *thd, TABLE *table) ...@@ -1052,9 +1007,7 @@ Event_timed::load_from_row(THD *thd, TABLE *table)
sql_mode= (ulong) table->field[ET_FIELD_SQL_MODE]->val_int(); sql_mode= (ulong) table->field[ET_FIELD_SQL_MODE]->val_int();
DBUG_RETURN(0); DBUG_RETURN(FALSE);
error:
DBUG_RETURN(EVEX_GET_FIELD_FAILED);
} }
...@@ -1875,11 +1828,9 @@ Event_job_data::execute(THD *thd, bool drop) ...@@ -1875,11 +1828,9 @@ Event_job_data::execute(THD *thd, bool drop)
{ {
Lex_input_stream lip(thd, thd->query, thd->query_length); Lex_input_stream lip(thd, thd->query, thd->query_length);
thd->m_lip= &lip;
lex_start(thd); lex_start(thd);
int err= MYSQLparse(thd);
if (err || thd->is_fatal_error) if (parse_sql(thd, &lip))
{ {
sql_print_error("Event Scheduler: " sql_print_error("Event Scheduler: "
"%serror during compilation of %s.%s", "%serror during compilation of %s.%s",
......
...@@ -77,7 +77,7 @@ class Event_basic ...@@ -77,7 +77,7 @@ class Event_basic
Event_basic(); Event_basic();
virtual ~Event_basic(); virtual ~Event_basic();
virtual int virtual bool
load_from_row(THD *thd, TABLE *table) = 0; load_from_row(THD *thd, TABLE *table) = 0;
protected: protected:
...@@ -119,7 +119,7 @@ class Event_queue_element : public Event_basic ...@@ -119,7 +119,7 @@ class Event_queue_element : public Event_basic
Event_queue_element(); Event_queue_element();
virtual ~Event_queue_element(); virtual ~Event_queue_element();
virtual int virtual bool
load_from_row(THD *thd, TABLE *table); load_from_row(THD *thd, TABLE *table);
bool bool
...@@ -157,7 +157,7 @@ class Event_timed : public Event_queue_element ...@@ -157,7 +157,7 @@ class Event_timed : public Event_queue_element
void void
init(); init();
virtual int virtual bool
load_from_row(THD *thd, TABLE *table); load_from_row(THD *thd, TABLE *table);
int int
...@@ -176,7 +176,7 @@ class Event_job_data : public Event_basic ...@@ -176,7 +176,7 @@ class Event_job_data : public Event_basic
Event_job_data(); Event_job_data();
virtual int virtual bool
load_from_row(THD *thd, TABLE *table); load_from_row(THD *thd, TABLE *table);
bool bool
...@@ -205,12 +205,11 @@ class Event_parse_data : public Sql_alloc ...@@ -205,12 +205,11 @@ class Event_parse_data : public Sql_alloc
*/ */
bool do_not_create; bool do_not_create;
const char *body_begin; bool body_changed;
LEX_STRING dbname; LEX_STRING dbname;
LEX_STRING name; LEX_STRING name;
LEX_STRING definer;// combination of user and host LEX_STRING definer;// combination of user and host
LEX_STRING body;
LEX_STRING comment; LEX_STRING comment;
Item* item_starts; Item* item_starts;
...@@ -235,9 +234,6 @@ class Event_parse_data : public Sql_alloc ...@@ -235,9 +234,6 @@ class Event_parse_data : public Sql_alloc
bool bool
check_parse_data(THD *thd); check_parse_data(THD *thd);
void
init_body(THD *thd);
private: private:
void void
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "mysql_priv.h" #include "mysql_priv.h"
#include "event_db_repository.h" #include "event_db_repository.h"
#include "sp_head.h"
#include "event_data_objects.h" #include "event_data_objects.h"
#include "events.h" #include "events.h"
#include "sql_show.h" #include "sql_show.h"
...@@ -141,7 +142,10 @@ const TABLE_FIELD_W_TYPE event_table_fields[ET_FIELD_COUNT] = ...@@ -141,7 +142,10 @@ const TABLE_FIELD_W_TYPE event_table_fields[ET_FIELD_COUNT] =
*/ */
static bool 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) my_bool is_update)
{ {
CHARSET_INFO *scs= system_charset_info; CHARSET_INFO *scs= system_charset_info;
...@@ -152,7 +156,6 @@ mysql_event_fill_row(THD *thd, TABLE *table, Event_parse_data *et, ...@@ -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", ("dbname=[%s]", et->dbname.str));
DBUG_PRINT("info", ("name =[%s]", et->name.str)); DBUG_PRINT("info", ("name =[%s]", et->name.str));
DBUG_PRINT("info", ("body =[%s]", et->body.str));
if (table->s->fields < ET_FIELD_COUNT) if (table->s->fields < ET_FIELD_COUNT)
{ {
...@@ -187,11 +190,18 @@ mysql_event_fill_row(THD *thd, TABLE *table, Event_parse_data *et, ...@@ -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 Change the SQL_MODE only if body was present in an ALTER EVENT and of course
always during CREATE EVENT. 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); 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; goto err_truncate;
}
} }
if (et->expression) if (et->expression)
...@@ -513,10 +523,12 @@ Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data, ...@@ -513,10 +523,12 @@ Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data,
{ {
int ret= 1; int ret= 1;
TABLE *table= NULL; TABLE *table= NULL;
sp_head *sp= thd->lex->sphead;
DBUG_ENTER("Event_db_repository::create_event"); DBUG_ENTER("Event_db_repository::create_event");
DBUG_PRINT("info", ("open mysql.event for update")); DBUG_PRINT("info", ("open mysql.event for update"));
DBUG_ASSERT(sp);
if (open_event_table(thd, TL_WRITE, &table)) if (open_event_table(thd, TL_WRITE, &table))
goto end; goto end;
...@@ -561,7 +573,7 @@ Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data, ...@@ -561,7 +573,7 @@ Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data,
goto end; 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); my_error(ER_TOO_LONG_BODY, MYF(0), parse_data->name.str);
goto end; goto end;
...@@ -573,7 +585,7 @@ Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data, ...@@ -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 mysql_event_fill_row() calls my_error() in case of error so no need to
handle it here 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; goto end;
table->field[ET_FIELD_STATUS]->store((longlong)parse_data->status, TRUE); 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, ...@@ -617,7 +629,9 @@ Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data,
{ {
CHARSET_INFO *scs= system_charset_info; CHARSET_INFO *scs= system_charset_info;
TABLE *table= NULL; TABLE *table= NULL;
sp_head *sp= thd->lex->sphead;
int ret= 1; int ret= 1;
DBUG_ENTER("Event_db_repository::update_event"); DBUG_ENTER("Event_db_repository::update_event");
/* None or both must be set */ /* None or both must be set */
...@@ -661,7 +675,7 @@ Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data, ...@@ -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 mysql_event_fill_row() calls my_error() in case of error so no need to
handle it here 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; goto end;
if (new_dbname) if (new_dbname)
......
...@@ -3376,8 +3376,8 @@ TYPELIB *ha_known_exts(void) ...@@ -3376,8 +3376,8 @@ TYPELIB *ha_known_exts(void)
const char **ext, *old_ext; const char **ext, *old_ext;
known_extensions_id= mysys_usage_id; known_extensions_id= mysys_usage_id;
found_exts.push_back((char*) triggers_file_ext); found_exts.push_back((char*) TRG_EXT);
found_exts.push_back((char*) trigname_file_ext); found_exts.push_back((char*) TRN_EXT);
plugin_foreach(NULL, exts_handlerton, plugin_foreach(NULL, exts_handlerton,
MYSQL_STORAGE_ENGINE_PLUGIN, &found_exts); MYSQL_STORAGE_ENGINE_PLUGIN, &found_exts);
......
...@@ -101,7 +101,7 @@ char* query_table_status(THD *thd,const char *db,const char *table_name); ...@@ -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), \ ER_WARN_DEPRECATED_SYNTAX, ER(ER_WARN_DEPRECATED_SYNTAX), \
(Old), (Ver), (New)); \ (Old), (Ver), (New)); \
else \ 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)); \ "in MySQL %s. Please use %s instead.", (Old), (Ver), (New)); \
} while(0) } while(0)
...@@ -618,6 +618,8 @@ bool check_string_char_length(LEX_STRING *str, const char *err_msg, ...@@ -618,6 +618,8 @@ bool check_string_char_length(LEX_STRING *str, const char *err_msg,
uint max_char_length, CHARSET_INFO *cs, uint max_char_length, CHARSET_INFO *cs,
bool no_error); bool no_error);
bool parse_sql(THD *thd, class Lex_input_stream *lip);
enum enum_mysql_completiontype { enum enum_mysql_completiontype {
ROLLBACK_RELEASE=-2, ROLLBACK=1, ROLLBACK_AND_CHAIN=7, ROLLBACK_RELEASE=-2, ROLLBACK=1, ROLLBACK_AND_CHAIN=7,
COMMIT_RELEASE=-1, COMMIT=0, COMMIT_AND_CHAIN=6 COMMIT_RELEASE=-1, COMMIT=0, COMMIT_AND_CHAIN=6
...@@ -1588,6 +1590,7 @@ bool check_db_dir_existence(const char *db_name); ...@@ -1588,6 +1590,7 @@ bool check_db_dir_existence(const char *db_name);
bool load_db_opt(THD *thd, const char *path, HA_CREATE_INFO *create); bool load_db_opt(THD *thd, const char *path, HA_CREATE_INFO *create);
bool load_db_opt_by_name(THD *thd, const char *db_name, bool load_db_opt_by_name(THD *thd, const char *db_name,
HA_CREATE_INFO *db_create_info); HA_CREATE_INFO *db_create_info);
CHARSET_INFO *get_default_db_collation(THD *thd, const char *db_name);
bool my_dbopt_init(void); bool my_dbopt_init(void);
void my_dbopt_cleanup(void); void my_dbopt_cleanup(void);
extern int creating_database; // How many database locks are made extern int creating_database; // How many database locks are made
...@@ -1608,8 +1611,8 @@ extern const char *first_keyword, *my_localhost, *delayed_user, *binary_keyword; ...@@ -1608,8 +1611,8 @@ extern const char *first_keyword, *my_localhost, *delayed_user, *binary_keyword;
extern const char **errmesg; /* Error messages */ extern const char **errmesg; /* Error messages */
extern const char *myisam_recover_options_str; extern const char *myisam_recover_options_str;
extern const char *in_left_expr_name, *in_additional_cond, *in_having_cond; extern const char *in_left_expr_name, *in_additional_cond, *in_having_cond;
extern const char * const triggers_file_ext; extern const char * const TRG_EXT;
extern const char * const trigname_file_ext; extern const char * const TRN_EXT;
extern Eq_creator eq_creator; extern Eq_creator eq_creator;
extern Ne_creator ne_creator; extern Ne_creator ne_creator;
extern Gt_creator gt_creator; extern Gt_creator gt_creator;
...@@ -1953,7 +1956,6 @@ void free_list(I_List <i_string_pair> *list); ...@@ -1953,7 +1956,6 @@ void free_list(I_List <i_string_pair> *list);
void free_list(I_List <i_string> *list); void free_list(I_List <i_string> *list);
/* sql_yacc.cc */ /* sql_yacc.cc */
extern int MYSQLparse(void *thd);
#ifndef DBUG_OFF #ifndef DBUG_OFF
extern void turn_parser_debug_on(); extern void turn_parser_debug_on();
#endif #endif
...@@ -2145,6 +2147,12 @@ bool schema_table_store_record(THD *thd, TABLE *table); ...@@ -2145,6 +2147,12 @@ bool schema_table_store_record(THD *thd, TABLE *table);
int item_create_init(); int item_create_init();
void item_create_cleanup(); 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_SERVER */
#endif /* MYSQL_CLIENT */ #endif /* MYSQL_CLIENT */
......
...@@ -384,32 +384,32 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, ...@@ -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))) if ((ret= sp_use_new_db(thd, name->m_db, &old_db, 1, &dbchanged)))
goto end; goto end;
thd->spcont= NULL;
{ {
Lex_input_stream lip(thd, defstr.c_ptr(), defstr.length()); Lex_input_stream lip(thd, defstr.c_ptr(), defstr.length());
thd->m_lip= &lip;
lex_start(thd); lex_start(thd);
ret= MYSQLparse(thd);
}
thd->spcont= 0; if (parse_sql(thd, &lip) || newlex.sphead == NULL)
if (ret || thd->is_fatal_error || newlex.sphead == NULL) {
{ sp_head *sp= newlex.sphead;
sp_head *sp= newlex.sphead;
if (dbchanged && (ret= mysql_change_db(thd, &old_db, TRUE))) if (dbchanged && (ret= mysql_change_db(thd, &old_db, TRUE)))
goto end; goto end;
delete sp; delete sp;
ret= SP_PARSE_ERROR; ret= SP_PARSE_ERROR;
} }
else else
{ {
if (dbchanged && (ret= mysql_change_db(thd, &old_db, TRUE))) if (dbchanged && (ret= mysql_change_db(thd, &old_db, TRUE)))
goto end; goto end;
*sphp= newlex.sphead; *sphp= newlex.sphead;
(*sphp)->set_definer(&definer_user_name, &definer_host_name); (*sphp)->set_definer(&definer_user_name, &definer_host_name);
(*sphp)->set_info(created, modified, &chistics, sql_mode); (*sphp)->set_info(created, modified, &chistics, sql_mode);
(*sphp)->optimize(); (*sphp)->optimize();
}
} }
end: end:
lex_end(thd->lex); lex_end(thd->lex);
thd->spcont= old_spcont; thd->spcont= old_spcont;
......
...@@ -1268,30 +1268,31 @@ set_routine_security_ctx(THD *thd, sp_head *sp, bool is_proc, ...@@ -1268,30 +1268,31 @@ set_routine_security_ctx(THD *thd, sp_head *sp, bool is_proc,
#endif // ! NO_EMBEDDED_ACCESS_CHECKS #endif // ! NO_EMBEDDED_ACCESS_CHECKS
/* /**
Execute trigger stored program.
Execute a trigger: Execute a trigger:
- changes security context for triggers - changes security context for triggers;
- switch to new memroot - switch to new memroot;
- call sp_head::execute - call sp_head::execute;
- restore old memroot - restore old memroot;
- restores security context - 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.
SYNOPSIS @return Error status.
sp_head::execute_trigger() @retval FALSE on success.
thd Thread handle @retval TRUE on error.
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
*/ */
bool 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) GRANT_INFO *grant_info)
{ {
sp_rcontext *octx = thd->spcont; sp_rcontext *octx = thd->spcont;
...@@ -1304,6 +1305,46 @@ sp_head::execute_trigger(THD *thd, const char *db, const char *table, ...@@ -1304,6 +1305,46 @@ sp_head::execute_trigger(THD *thd, const char *db, const char *table,
DBUG_ENTER("sp_head::execute_trigger"); DBUG_ENTER("sp_head::execute_trigger");
DBUG_PRINT("info", ("trigger %s", m_name.str)); 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 Prepare arena and memroot for objects which lifetime is whole
duration of trigger call (sp_rcontext, it's tables and items, duration of trigger call (sp_rcontext, it's tables and items,
...@@ -1336,6 +1377,11 @@ sp_head::execute_trigger(THD *thd, const char *db, const char *table, ...@@ -1336,6 +1377,11 @@ sp_head::execute_trigger(THD *thd, const char *db, const char *table,
err_with_cleanup: err_with_cleanup:
thd->restore_active_arena(&call_arena, &backup_arena); 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; delete nctx;
call_arena.free_items(); call_arena.free_items();
free_root(&call_mem_root, MYF(0)); free_root(&call_mem_root, MYF(0));
......
...@@ -216,8 +216,10 @@ class sp_head :private Query_arena ...@@ -216,8 +216,10 @@ class sp_head :private Query_arena
destroy(); destroy();
bool bool
execute_trigger(THD *thd, const char *db, const char *table, execute_trigger(THD *thd,
GRANT_INFO *grant_onfo); const LEX_STRING *db_name,
const LEX_STRING *table_name,
GRANT_INFO *grant_info);
bool bool
execute_function(THD *thd, Item **args, uint argcount, Field *return_fld); execute_function(THD *thd, Item **args, uint argcount, Field *return_fld);
......
...@@ -342,7 +342,8 @@ THD::THD() ...@@ -342,7 +342,8 @@ THD::THD()
in_lock_tables(0), in_lock_tables(0),
bootstrap(0), bootstrap(0),
derived_tables_processing(FALSE), derived_tables_processing(FALSE),
spcont(NULL) spcont(NULL),
m_lip(NULL)
{ {
ulong tmp; ulong tmp;
......
...@@ -538,6 +538,37 @@ bool load_db_opt_by_name(THD *thd, const char *db_name, ...@@ -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 Create a database
...@@ -751,10 +782,8 @@ bool mysql_alter_db(THD *thd, const char *db, HA_CREATE_INFO *create_info) ...@@ -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))) if ((error=write_db_opt(thd, path, create_info)))
goto exit; goto exit;
/* /* Change options if current database is being altered. */
Change options if current database is being altered
TODO: Delete this code
*/
if (thd->db && !strcmp(thd->db,db)) if (thd->db && !strcmp(thd->db,db))
{ {
thd->db_charset= create_info->default_table_charset ? 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) ...@@ -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; Security_context *sctx= thd->security_ctx;
ulong db_access= sctx->db_access; ulong db_access= sctx->db_access;
CHARSET_INFO *db_default_cl;
DBUG_ENTER("mysql_change_db"); DBUG_ENTER("mysql_change_db");
DBUG_PRINT("enter",("name: '%s'", new_db_name->str)); 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) ...@@ -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(). attributes and will be freed in THD::~THD().
*/ */
{ db_default_cl= get_default_db_collation(thd, new_db_file_name.str);
HA_CREATE_INFO db_options;
load_db_opt_by_name(thd, new_db_name->str, &db_options); mysql_change_db_impl(thd, &new_db_file_name, db_access, db_default_cl);
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);
}
DBUG_RETURN(FALSE); DBUG_RETURN(FALSE);
} }
......
...@@ -5343,12 +5343,11 @@ void mysql_parse(THD *thd, const char *inBuf, uint length, ...@@ -5343,12 +5343,11 @@ void mysql_parse(THD *thd, const char *inBuf, uint length,
sp_cache_flush_obsolete(&thd->sp_func_cache); sp_cache_flush_obsolete(&thd->sp_func_cache);
Lex_input_stream lip(thd, inBuf, length); 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; *found_semicolon= lip.found_semicolon;
if (!err && ! thd->is_fatal_error) if (!err)
{ {
#ifndef NO_EMBEDDED_ACCESS_CHECKS #ifndef NO_EMBEDDED_ACCESS_CHECKS
if (mqh_used && thd->user_connect && if (mqh_used && thd->user_connect &&
...@@ -5371,8 +5370,8 @@ void mysql_parse(THD *thd, const char *inBuf, uint length, ...@@ -5371,8 +5370,8 @@ void mysql_parse(THD *thd, const char *inBuf, uint length,
PROCESSLIST. PROCESSLIST.
Note that we don't need LOCK_thread_count to modify query_length. Note that we don't need LOCK_thread_count to modify query_length.
*/ */
if (lip.found_semicolon && if (*found_semicolon &&
(thd->query_length= (ulong)(lip.found_semicolon - thd->query))) (thd->query_length= (ulong)(*found_semicolon - thd->query)))
thd->query_length--; thd->query_length--;
/* Actually execute the query */ /* Actually execute the query */
mysql_execute_command(thd); mysql_execute_command(thd);
...@@ -5426,12 +5425,10 @@ bool mysql_test_parse_for_slave(THD *thd, char *inBuf, uint length) ...@@ -5426,12 +5425,10 @@ bool mysql_test_parse_for_slave(THD *thd, char *inBuf, uint length)
DBUG_ENTER("mysql_test_parse_for_slave"); DBUG_ENTER("mysql_test_parse_for_slave");
Lex_input_stream lip(thd, inBuf, length); Lex_input_stream lip(thd, inBuf, length);
thd->m_lip= &lip;
lex_start(thd); lex_start(thd);
mysql_reset_thd_for_next_command(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)) all_tables_not_ok(thd,(TABLE_LIST*) lex->select_lex.table_list.first))
error= 1; /* Ignore question */ error= 1; /* Ignore question */
thd->end_statement(); thd->end_statement();
...@@ -7123,3 +7120,34 @@ bool check_string_char_length(LEX_STRING *str, const char *err_msg, ...@@ -7123,3 +7120,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); my_error(ER_WRONG_STRING_LENGTH, MYF(0), str->str, err_msg, max_char_length);
return TRUE; return TRUE;
} }
extern int MYSQLparse(void *thd); // from sql_yacc.cc
/**
This is a wrapper of MYSQLparse(). All the code should call parse_sql()
instead of MYSQLparse().
@param thd Thread context.
@param lip Lexer context.
@return Error status.
@retval FALSE on success.
@retval TRUE on parsing error.
*/
bool parse_sql(THD *thd, Lex_input_stream *lip)
{
bool err_status;
DBUG_ASSERT(thd->m_lip == NULL);
thd->m_lip= lip;
err_status= MYSQLparse(thd) != 0 || thd->is_fatal_error;
thd->m_lip= NULL;
return err_status;
}
...@@ -3696,7 +3696,6 @@ bool mysql_unpack_partition(THD *thd, ...@@ -3696,7 +3696,6 @@ bool mysql_unpack_partition(THD *thd,
thd->variables.character_set_client= system_charset_info; thd->variables.character_set_client= system_charset_info;
Lex_input_stream lip(thd, part_buf, part_info_len); Lex_input_stream lip(thd, part_buf, part_info_len);
thd->m_lip= &lip;
lex_start(thd); lex_start(thd);
/* /*
...@@ -3725,7 +3724,7 @@ bool mysql_unpack_partition(THD *thd, ...@@ -3725,7 +3724,7 @@ bool mysql_unpack_partition(THD *thd,
lex.part_info->part_state= part_state; lex.part_info->part_state= part_state;
lex.part_info->part_state_len= part_state_len; lex.part_info->part_state_len= part_state_len;
DBUG_PRINT("info", ("Parse: %s", part_buf)); DBUG_PRINT("info", ("Parse: %s", part_buf));
if (MYSQLparse((void*)thd) || thd->is_fatal_error) if (parse_sql(thd, &lip))
{ {
thd->free_items(); thd->free_items();
goto end; goto end;
......
...@@ -2858,12 +2858,11 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) ...@@ -2858,12 +2858,11 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
Lex_input_stream lip(thd, thd->query, thd->query_length); Lex_input_stream lip(thd, thd->query, thd->query_length);
lip.stmt_prepare_mode= TRUE; lip.stmt_prepare_mode= TRUE;
thd->m_lip= &lip;
lex_start(thd); lex_start(thd);
int err= MYSQLparse((void *)thd);
error= err || thd->is_fatal_error || error= parse_sql(thd, &lip) ||
thd->net.report_error || init_param_array(this); thd->net.report_error ||
init_param_array(this);
/* /*
While doing context analysis of the query (in check_prepared_statement) While doing context analysis of the query (in check_prepared_statement)
......
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
static const LEX_STRING triggers_file_type= static const LEX_STRING triggers_file_type=
{ C_STRING_WITH_LEN("TRIGGERS") }; { C_STRING_WITH_LEN("TRIGGERS") };
const char * const triggers_file_ext= ".TRG"; const char * const TRG_EXT= ".TRG";
/* /*
Table of .TRG file field descriptors. Table of .TRG file field descriptors.
...@@ -79,7 +79,7 @@ struct st_trigname ...@@ -79,7 +79,7 @@ struct st_trigname
static const LEX_STRING trigname_file_type= static const LEX_STRING trigname_file_type=
{ C_STRING_WITH_LEN("TRIGGERNAME") }; { C_STRING_WITH_LEN("TRIGGERNAME") };
const char * const trigname_file_ext= ".TRN"; const char * const TRN_EXT= ".TRN";
static File_option trigname_file_parameters[]= static File_option trigname_file_parameters[]=
{ {
...@@ -132,6 +132,7 @@ class Handle_old_incorrect_trigger_table_hook: public Unknown_key_hook ...@@ -132,6 +132,7 @@ class Handle_old_incorrect_trigger_table_hook: public Unknown_key_hook
LEX_STRING *trigger_table_value; LEX_STRING *trigger_table_value;
}; };
/* /*
Create or drop trigger for table. Create or drop trigger for table.
...@@ -463,14 +464,14 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables, ...@@ -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 sql_create_definition_file() files handles renaming and backup of older
versions 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, tables->db, tables->table_name,
triggers_file_ext, 0); TRG_EXT, 0);
file.str= file_buff; file.str= file_buff;
trigname_file.length= build_table_filename(trigname_buff, FN_REFLEN-1, trigname_file.length= build_table_filename(trigname_buff, FN_REFLEN-1,
tables->db, tables->db,
lex->spname->m_name.str, lex->spname->m_name.str,
trigname_file_ext, 0); TRN_EXT, 0);
trigname_file.str= trigname_buff; trigname_file.str= trigname_buff;
/* Use the filesystem to enforce trigger namespace constraints. */ /* Use the filesystem to enforce trigger namespace constraints. */
...@@ -604,7 +605,7 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables, ...@@ -604,7 +605,7 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
static bool rm_trigger_file(char *path, const char *db, static bool rm_trigger_file(char *path, const char *db,
const char *table_name) 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)); return my_delete(path, MYF(MY_WME));
} }
...@@ -627,8 +628,7 @@ static bool rm_trigger_file(char *path, const char *db, ...@@ -627,8 +628,7 @@ static bool rm_trigger_file(char *path, const char *db,
static bool rm_trigname_file(char *path, const char *db, static bool rm_trigname_file(char *path, const char *db,
const char *trigger_name) const char *trigger_name)
{ {
build_table_filename(path, FN_REFLEN-1, build_table_filename(path, FN_REFLEN - 1, db, trigger_name, TRN_EXT, 0);
db, trigger_name, trigname_file_ext, 0);
return my_delete(path, MYF(MY_WME)); return my_delete(path, MYF(MY_WME));
} }
...@@ -653,8 +653,8 @@ static bool save_trigger_file(Table_triggers_list *triggers, const char *db, ...@@ -653,8 +653,8 @@ static bool save_trigger_file(Table_triggers_list *triggers, const char *db,
char file_buff[FN_REFLEN]; char file_buff[FN_REFLEN];
LEX_STRING file; LEX_STRING file;
file.length= build_table_filename(file_buff, FN_REFLEN-1, db, table_name, file.length= build_table_filename(file_buff, FN_REFLEN - 1, db, table_name,
triggers_file_ext, 0); TRG_EXT, 0);
file.str= file_buff; file.str= file_buff;
return sql_create_definition_file(NULL, &file, &triggers_file_type, return sql_create_definition_file(NULL, &file, &triggers_file_type,
(uchar*)triggers, triggers_file_parameters, (uchar*)triggers, triggers_file_parameters,
...@@ -834,8 +834,8 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, ...@@ -834,8 +834,8 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
DBUG_ENTER("Table_triggers_list::check_n_load"); DBUG_ENTER("Table_triggers_list::check_n_load");
path.length= build_table_filename(path_buff, FN_REFLEN-1, path.length= build_table_filename(path_buff, FN_REFLEN - 1,
db, table_name, triggers_file_ext, 0); db, table_name, TRG_EXT, 0);
path.str= path_buff; path.str= path_buff;
// QQ: should we analyze errno somehow ? // QQ: should we analyze errno somehow ?
...@@ -981,12 +981,10 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, ...@@ -981,12 +981,10 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
thd->variables.sql_mode= (ulong)*trg_sql_mode; thd->variables.sql_mode= (ulong)*trg_sql_mode;
Lex_input_stream lip(thd, trg_create_str->str, trg_create_str->length); Lex_input_stream lip(thd, trg_create_str->str, trg_create_str->length);
thd->m_lip= &lip;
lex_start(thd); lex_start(thd);
thd->spcont= 0; 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 */ /* Currently sphead is always deleted in case of a parse error */
DBUG_ASSERT(lex.sphead == 0); DBUG_ASSERT(lex.sphead == 0);
...@@ -1108,7 +1106,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, ...@@ -1108,7 +1106,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
be merged into .FRM anyway. be merged into .FRM anyway.
*/ */
my_error(ER_WRONG_OBJECT, MYF(0), my_error(ER_WRONG_OBJECT, MYF(0),
table_name, triggers_file_ext+1, "TRIGGER"); table_name, TRG_EXT + 1, "TRIGGER");
DBUG_RETURN(1); DBUG_RETURN(1);
} }
...@@ -1168,83 +1166,66 @@ bool Table_triggers_list::get_trigger_info(THD *thd, trg_event_type event, ...@@ -1168,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 Find trigger's table from trigger identifier and add it to
the statement table list. the statement table list.
SYNOPSIS @param[in] thd Thread context.
mysql_table_for_trigger() @param[in] trg_name Trigger name.
thd - current thread context @param[in] if_exists TRUE if SQL statement contains "IF EXISTS" clause.
trig - identifier for trigger That means a warning instead of error should be
if_exists - treat a not existing trigger as a warning if TRUE thrown if trigger with given name does not exist.
table - pointer to TABLE_LIST object for the table trigger (output) @param[out] table Pointer to TABLE_LIST object for the
table trigger.
RETURN VALUE
0 Success @return Operation status
1 Error @retval FALSE On success.
@retval TRUE Otherwise.
*/ */
int bool add_table_for_trigger(THD *thd,
add_table_for_trigger(THD *thd, sp_name *trig, bool if_exists, sp_name *trg_name,
TABLE_LIST **table) bool if_exists,
TABLE_LIST **table)
{ {
LEX *lex= thd->lex; LEX *lex= thd->lex;
char path_buff[FN_REFLEN]; char trn_path_buff[FN_REFLEN];
LEX_STRING path; LEX_STRING trn_path= { trn_path_buff, 0 };
File_parser *parser; LEX_STRING tbl_name;
struct st_trigname trigname;
Handle_old_incorrect_trigger_table_hook trigger_table_hook(
path_buff, &trigname.trigger_table);
DBUG_ENTER("add_table_for_trigger"); DBUG_ENTER("add_table_for_trigger");
DBUG_ASSERT(table != NULL);
path.length= build_table_filename(path_buff, FN_REFLEN-1, build_trn_path(thd, trg_name, &trn_path);
trig->m_db.str, trig->m_name.str,
trigname_file_ext, 0);
path.str= path_buff;
if (access(path_buff, F_OK)) if (check_trn_exists(&trn_path))
{ {
if (if_exists) if (if_exists)
{ {
push_warning_printf(thd, push_warning_printf(thd,
MYSQL_ERROR::WARN_LEVEL_NOTE, MYSQL_ERROR::WARN_LEVEL_NOTE,
ER_TRG_DOES_NOT_EXIST, ER_TRG_DOES_NOT_EXIST,
ER(ER_TRG_DOES_NOT_EXIST)); ER(ER_TRG_DOES_NOT_EXIST));
*table= NULL; *table= NULL;
DBUG_RETURN(0);
DBUG_RETURN(FALSE);
} }
my_error(ER_TRG_DOES_NOT_EXIST, MYF(0)); 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, if (load_table_name_for_trigger(thd, trg_name, &trn_path, &tbl_name))
trigname_file_parameters, 1, DBUG_RETURN(TRUE);
&trigger_table_hook))
DBUG_RETURN(1);
/* We need to reset statement table list to be PS/SP friendly. */ /* We need to reset statement table list to be PS/SP friendly. */
lex->query_tables= 0; lex->query_tables= 0;
lex->query_tables_last= &lex->query_tables; 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) *table= sp_add_to_query_tables(thd, lex, trg_name->m_db.str,
DBUG_RETURN(1); tbl_name.str, TL_IGNORE);
DBUG_RETURN(0); DBUG_RETURN(*table ? FALSE : TRUE);
} }
...@@ -1428,7 +1409,7 @@ Table_triggers_list::change_table_name_in_trignames(const char *db_name, ...@@ -1428,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, trigname_file.length= build_table_filename(trigname_buff, FN_REFLEN-1,
db_name, trigger->str, db_name, trigger->str,
trigname_file_ext, 0); TRN_EXT, 0);
trigname_file.str= trigname_buff; trigname_file.str= trigname_buff;
trigname.trigger_table= *new_table_name; trigname.trigger_table= *new_table_name;
...@@ -1537,77 +1518,54 @@ bool Table_triggers_list::change_table_name(THD *thd, const char *db, ...@@ -1537,77 +1518,54 @@ bool Table_triggers_list::change_table_name(THD *thd, const char *db,
} }
bool Table_triggers_list::process_triggers(THD *thd, trg_event_type event, /**
trg_action_time_type time_type, Execute trigger for given (event, time) pair.
bool old_row_is_record1)
{
bool err_status= FALSE;
sp_head *sp_trigger= bodies[event][time_type];
if (sp_trigger) The operation executes trigger for the specified event (insert, update,
{ delete) and time (after, before) if it is set.
Sub_statement_state statement_state;
if (old_row_is_record1) @param thd
{ @param event
old_field= record1_field; @param time_type,
new_field= trigger_table->field; @param old_row_is_record1
}
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, @return Error status.
&subject_table_grants[event][time_type], @retval FALSE on success.
trigger_table->s->db.str, @retval TRUE on error.
trigger_table->s->table_name.str); */
/* Check that the definer has TRIGGER privilege on the subject table. */ 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;
Sub_statement_state statement_state;
if (!(subject_table_grants[event][time_type].privilege & TRIGGER_ACL)) if (!bodies[event][time_type])
{ return FALSE;
char priv_desc[128];
get_privilege_desc(priv_desc, sizeof(priv_desc), TRIGGER_ACL);
my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0), priv_desc, if (old_row_is_record1)
thd->security_ctx->priv_user, thd->security_ctx->host_or_ip, {
trigger_table->s->table_name.str); old_field= record1_field;
new_field= trigger_table->field;
}
else
{
new_field= record1_field;
old_field= trigger_table->field;
}
sctx->restore_security_context(thd, save_ctx); thd->reset_sub_statement_state(&statement_state, SUB_STMT_TRIGGER);
return TRUE;
}
#endif // NO_EMBEDDED_ACCESS_CHECKS
thd->reset_sub_statement_state(&statement_state, SUB_STMT_TRIGGER); err_status=
err_status= sp_trigger->execute_trigger bodies[event][time_type]->execute_trigger(
(thd, trigger_table->s->db.str, trigger_table->s->table_name.str, thd,
&subject_table_grants[event][time_type]); &trigger_table->s->db,
thd->restore_sub_statement_state(&statement_state); &trigger_table->s->table_name,
&subject_table_grants[event][time_type]);
#ifndef NO_EMBEDDED_ACCESS_CHECKS thd->restore_sub_statement_state(&statement_state);
sctx->restore_security_context(thd, save_ctx);
#endif // NO_EMBEDDED_ACCESS_CHECKS
}
return err_status; return err_status;
} }
...@@ -1749,3 +1707,95 @@ process_unknown_string(char *&unknown_key, uchar* base, MEM_ROOT *mem_root, ...@@ -1749,3 +1707,95 @@ process_unknown_string(char *&unknown_key, uchar* base, MEM_ROOT *mem_root,
} }
DBUG_RETURN(FALSE); DBUG_RETURN(FALSE);
} }
/**
Contruct path to TRN-file.
@param thd[in] Thread context.
@param trg_name[in] Trigger name.
@param trn_path[out] Variable to store constructed path
*/
void build_trn_path(THD *thd, const sp_name *trg_name, LEX_STRING *trn_path)
{
/* Construct path to the TRN-file. */
trn_path->length= build_table_filename(trn_path->str,
FN_REFLEN - 1,
trg_name->m_db.str,
trg_name->m_name.str,
TRN_EXT,
0);
}
/**
Check if TRN-file exists.
@return
@retval TRUE if TRN-file does not exist.
@retval FALSE if TRN-file exists.
*/
bool check_trn_exists(const LEX_STRING *trn_path)
{
return access(trn_path->str, F_OK) != 0;
}
/**
Retrieve table name for given trigger.
@param thd[in] Thread context.
@param trg_name[in] Trigger name.
@param trn_path[in] Path to the corresponding TRN-file.
@param tbl_name[out] Variable to store retrieved table name.
@return Error status.
@retval FALSE on success.
@retval TRUE if table name could not be retrieved.
*/
bool load_table_name_for_trigger(THD *thd,
const sp_name *trg_name,
const LEX_STRING *trn_path,
LEX_STRING *tbl_name)
{
File_parser *parser;
struct st_trigname trn_data;
Handle_old_incorrect_trigger_table_hook trigger_table_hook(
trn_path->str,
&trn_data.trigger_table);
DBUG_ENTER("load_table_name_for_trigger");
/* Parse the TRN-file. */
if (!(parser= sql_parse_prepare(trn_path, thd->mem_root, TRUE)))
DBUG_RETURN(TRUE);
if (!is_equal(&trigname_file_type, parser->type()))
{
my_error(ER_WRONG_OBJECT, MYF(0),
trg_name->m_name.str,
TRN_EXT + 1,
"TRIGGERNAME");
DBUG_RETURN(TRUE);
}
if (parser->parse((uchar*) &trn_data, thd->mem_root,
trigname_file_parameters, 1,
&trigger_table_hook))
DBUG_RETURN(TRUE);
/* Copy trigger table name. */
*tbl_name= trn_data.trigger_table;
/* That's all. */
DBUG_RETURN(FALSE);
}
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
/* /*
This class holds all information about triggers of table. 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 class Table_triggers_list: public Sql_alloc
...@@ -143,6 +143,17 @@ class Table_triggers_list: public Sql_alloc ...@@ -143,6 +143,17 @@ class Table_triggers_list: public Sql_alloc
extern const LEX_STRING trg_action_time_type_names[]; extern const LEX_STRING trg_action_time_type_names[];
extern const LEX_STRING trg_event_type_names[]; extern const LEX_STRING trg_event_type_names[];
int bool add_table_for_trigger(THD *thd,
add_table_for_trigger(THD *thd, sp_name *trig, bool if_exists, sp_name *trg_name,
TABLE_LIST **table); 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);
...@@ -893,7 +893,7 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, ...@@ -893,7 +893,7 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
LEX *old_lex, *lex; LEX *old_lex, *lex;
Query_arena *arena, backup; Query_arena *arena, backup;
TABLE_LIST *top_view= table->top_table(); TABLE_LIST *top_view= table->top_table();
int res; bool res;
bool result, view_is_mergeable; bool result, view_is_mergeable;
TABLE_LIST *view_main_select_tables; TABLE_LIST *view_main_select_tables;
DBUG_ENTER("mysql_make_view"); DBUG_ENTER("mysql_make_view");
...@@ -1005,7 +1005,6 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, ...@@ -1005,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); Lex_input_stream lip(thd, table->query.str, table->query.length);
thd->m_lip= &lip;
lex_start(thd); lex_start(thd);
view_select= &lex->select_lex; view_select= &lex->select_lex;
view_select->select_number= ++thd->select_number; view_select->select_number= ++thd->select_number;
...@@ -1039,7 +1038,7 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, ...@@ -1039,7 +1038,7 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
MODE_IGNORE_SPACE | MODE_NO_BACKSLASH_ESCAPES); MODE_IGNORE_SPACE | MODE_NO_BACKSLASH_ESCAPES);
CHARSET_INFO *save_cs= thd->variables.character_set_client; CHARSET_INFO *save_cs= thd->variables.character_set_client;
thd->variables.character_set_client= system_charset_info; 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) || if ((old_lex->sql_command == SQLCOM_SHOW_FIELDS) ||
(old_lex->sql_command == SQLCOM_SHOW_CREATE)) (old_lex->sql_command == SQLCOM_SHOW_CREATE))
...@@ -1048,7 +1047,7 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, ...@@ -1048,7 +1047,7 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
thd->variables.character_set_client= save_cs; thd->variables.character_set_client= save_cs;
thd->variables.sql_mode= save_mode; 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= lex->query_tables;
TABLE_LIST *view_tables_tail= 0; TABLE_LIST *view_tables_tail= 0;
......
...@@ -1873,9 +1873,6 @@ ev_sql_stmt: ...@@ -1873,9 +1873,6 @@ ev_sql_stmt:
lex->sphead->m_chistics= &lex->sp_chistics; lex->sphead->m_chistics= &lex->sp_chistics;
lex->sphead->m_body_begin= lip->get_cpp_ptr(); lex->sphead->m_body_begin= lip->get_cpp_ptr();
lex->event_parse_data->body_begin= lip->get_cpp_ptr();
} }
ev_sql_stmt_inner ev_sql_stmt_inner
{ {
...@@ -1888,7 +1885,7 @@ ev_sql_stmt: ...@@ -1888,7 +1885,7 @@ ev_sql_stmt:
lex->sp_chistics.suid= SP_IS_SUID; //always the definer! lex->sp_chistics.suid= SP_IS_SUID; //always the definer!
lex->event_parse_data->init_body(thd); lex->event_parse_data->body_changed= TRUE;
} }
; ;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment