Commit 3cf9e6eb authored by unknown's avatar unknown

Bug#35997 Event scheduler seems to let the server crash, if it is embedded.

The event scheduler was not designed to work in embedded mode. This
patch disables and excludes the event scheduler when the server is
compiled for embedded build.


libmysqld/Makefile.am:
  Reduce the amount of event code in an embedded build.
mysql-test/t/events_trans.test:
  Disable test if run in embedded mode.
sql/Makefile.am:
  Introduce definition HAVE_EVENT_SCHEDULER and one new source file.
sql/event_data_objects.cc:
  Refactor Event_parse_data to new file.
sql/event_data_objects.h:
  Refactor Event_parse_data to new file.
  Move global definitions to new file.
sql/event_queue.cc:
  Move all parsed items to Event_parse_data for easier modularization.
sql/events.cc:
  Move all parsed items to Event_parse_data for easier modularization.
sql/mysqld.cc:
  Disable the event schedular subsystem if the server is compiled in
  embedded mode.
sql/set_var.cc:
  Disable the event schedular subsystem if the server is compiled in
  embedded mode.
sql/set_var.h:
  Disable the event schedular subsystem if the server is compiled in
  embedded mode.
sql/sql_db.cc:
  Disable the event schedular subsystem if the server is compiled in
  embedded mode.
sql/sql_parse.cc:
  Disable the event schedular subsystem if the server is compiled in
  embedded mode.
sql/sql_show.cc:
  Disable the event schedular subsystem if the server is compiled in
  embedded mode.
sql/sql_test.cc:
  Disable the event schedular subsystem if the server is compiled in
  embedded mode.
sql/sql_yacc.yy:
  Only include event-code needed for parsing to reduce impact on embedded
  build.
  Move all constants to Event_parse_data class.
mysql-test/r/events_embedded.result:
  Add test case to make sure the 'event_scheduler' can't be activated
  in embedded mode.
mysql-test/r/is_embedded.require:
  Add test case to make sure the 'event_scheduler' can't be activated
  in embedded mode.
mysql-test/t/events_embedded.test:
  Add test case to make sure the 'event_scheduler' can't be activated
  in embedded mode.
sql/event_parse_data.cc:
  New file. Extracted Event_parse data into a new file.
sql/event_parse_data.h:
  New file. Extracted Event_parse data into a new file.
parent ae7c0dea
...@@ -73,12 +73,10 @@ sqlsources = derror.cc field.cc field_conv.cc strfunc.cc filesort.cc \ ...@@ -73,12 +73,10 @@ sqlsources = derror.cc field.cc field_conv.cc strfunc.cc filesort.cc \
spatial.cc gstream.cc sql_help.cc tztime.cc sql_cursor.cc \ spatial.cc gstream.cc sql_help.cc tztime.cc sql_cursor.cc \
sp_head.cc sp_pcontext.cc sp.cc sp_cache.cc sp_rcontext.cc \ sp_head.cc sp_pcontext.cc sp.cc sp_cache.cc sp_rcontext.cc \
parse_file.cc sql_view.cc sql_trigger.cc my_decimal.cc \ parse_file.cc sql_view.cc sql_trigger.cc my_decimal.cc \
event_scheduler.cc events.cc event_data_objects.cc \
event_queue.cc event_db_repository.cc \
rpl_filter.cc sql_partition.cc sql_builtin.cc sql_plugin.cc \ rpl_filter.cc sql_partition.cc sql_builtin.cc sql_plugin.cc \
sql_tablespace.cc \ sql_tablespace.cc \
rpl_injector.cc my_user.c partition_info.cc \ rpl_injector.cc my_user.c partition_info.cc \
sql_servers.cc sql_servers.cc event_parse_data.cc
libmysqld_int_a_SOURCES= $(libmysqld_sources) libmysqld_int_a_SOURCES= $(libmysqld_sources)
nodist_libmysqld_int_a_SOURCES= $(libmysqlsources) $(sqlsources) nodist_libmysqld_int_a_SOURCES= $(libmysqlsources) $(sqlsources)
......
set global event_scheduler=ON;
ERROR HY000: Unknown system variable 'event_scheduler'
--source include/is_embedded.inc
--error 1193
set global event_scheduler=ON;
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
# Tests that require transactions # Tests that require transactions
# #
-- source include/have_innodb.inc -- source include/have_innodb.inc
-- source include/not_embedded.inc
--disable_warnings --disable_warnings
drop database if exists events_test; drop database if exists events_test;
drop database if exists mysqltest_no_such_database; drop database if exists mysqltest_no_such_database;
......
...@@ -73,7 +73,7 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \ ...@@ -73,7 +73,7 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \
parse_file.h sql_view.h sql_trigger.h \ parse_file.h sql_view.h sql_trigger.h \
sql_array.h sql_cursor.h events.h scheduler.h \ sql_array.h sql_cursor.h events.h scheduler.h \
event_db_repository.h event_queue.h \ event_db_repository.h event_queue.h \
sql_plugin.h authors.h \ sql_plugin.h authors.h event_parse_data.h \
event_data_objects.h event_scheduler.h \ event_data_objects.h event_scheduler.h \
sql_partition.h partition_info.h partition_element.h \ sql_partition.h partition_info.h partition_element.h \
contributors.h sql_servers.h contributors.h sql_servers.h
...@@ -120,7 +120,7 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \ ...@@ -120,7 +120,7 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \
event_queue.cc event_db_repository.cc events.cc \ event_queue.cc event_db_repository.cc events.cc \
sql_plugin.cc sql_binlog.cc \ sql_plugin.cc sql_binlog.cc \
sql_builtin.cc sql_tablespace.cc partition_info.cc \ sql_builtin.cc sql_tablespace.cc partition_info.cc \
sql_servers.cc sql_servers.cc event_parse_data.cc
nodist_mysqld_SOURCES = mini_client_errors.c pack.c client.c my_time.c my_user.c nodist_mysqld_SOURCES = mini_client_errors.c pack.c client.c my_time.c my_user.c
...@@ -140,6 +140,7 @@ DEFS = -DMYSQL_SERVER \ ...@@ -140,6 +140,7 @@ DEFS = -DMYSQL_SERVER \
-DDATADIR="\"$(MYSQLDATAdir)\"" \ -DDATADIR="\"$(MYSQLDATAdir)\"" \
-DSHAREDIR="\"$(MYSQLSHAREdir)\"" \ -DSHAREDIR="\"$(MYSQLSHAREdir)\"" \
-DPLUGINDIR="\"$(pkgplugindir)\"" \ -DPLUGINDIR="\"$(pkgplugindir)\"" \
-DHAVE_EVENT_SCHEDULER \
@DEFS@ @DEFS@
BUILT_MAINT_SRC = sql_yacc.cc sql_yacc.h BUILT_MAINT_SRC = sql_yacc.cc sql_yacc.h
......
...@@ -25,8 +25,6 @@ ...@@ -25,8 +25,6 @@
@{ @{
*/ */
#define EVEX_MAX_INTERVAL_VALUE 1000000000L
/*************************************************************************/ /*************************************************************************/
/** /**
...@@ -187,524 +185,6 @@ Event_queue_element_for_exec::~Event_queue_element_for_exec() ...@@ -187,524 +185,6 @@ Event_queue_element_for_exec::~Event_queue_element_for_exec()
} }
/*
Returns a new instance
SYNOPSIS
Event_parse_data::new_instance()
RETURN VALUE
Address or NULL in case of error
NOTE
Created on THD's mem_root
*/
Event_parse_data *
Event_parse_data::new_instance(THD *thd)
{
return new (thd->mem_root) Event_parse_data;
}
/*
Constructor
SYNOPSIS
Event_parse_data::Event_parse_data()
*/
Event_parse_data::Event_parse_data()
:on_completion(Event_basic::ON_COMPLETION_DROP),
status(Event_basic::ENABLED),
do_not_create(FALSE),
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;
comment.str= NULL;
comment.length= 0;
DBUG_VOID_RETURN;
}
/*
Set a name of the event
SYNOPSIS
Event_parse_data::init_name()
thd THD
spn the name extracted in the parser
*/
void
Event_parse_data::init_name(THD *thd, sp_name *spn)
{
DBUG_ENTER("Event_parse_data::init_name");
/* We have to copy strings to get them into the right memroot */
dbname.length= spn->m_db.length;
dbname.str= thd->strmake(spn->m_db.str, spn->m_db.length);
name.length= spn->m_name.length;
name.str= thd->strmake(spn->m_name.str, spn->m_name.length);
if (spn->m_qname.length == 0)
spn->init_qname(thd);
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
will never be executed. If it has ON COMPLETION NOT PRESERVE
(default), then it would normally be dropped already, so on CREATE
EVENT we give a warning, and do not create anyting. On ALTER EVENT
we give a error, and do not change the event.
If the event has ON COMPLETION PRESERVE, then we see if the event is
created or altered to the ENABLED (default) state. If so, then we
give a warning, and change the state to DISABLED.
Otherwise it is a valid event in ON COMPLETION PRESERVE DISABLE
state.
*/
void
Event_parse_data::check_if_in_the_past(THD *thd, my_time_t ltime_utc)
{
if (ltime_utc >= (my_time_t) thd->query_start())
return;
if (on_completion == Event_basic::ON_COMPLETION_DROP)
{
switch (thd->lex->sql_command) {
case SQLCOM_CREATE_EVENT:
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
ER_EVENT_CANNOT_CREATE_IN_THE_PAST,
ER(ER_EVENT_CANNOT_CREATE_IN_THE_PAST));
break;
case SQLCOM_ALTER_EVENT:
my_error(ER_EVENT_CANNOT_ALTER_IN_THE_PAST, MYF(0));
break;
default:
DBUG_ASSERT(0);
}
do_not_create= TRUE;
}
else if (status == Event_basic::ENABLED)
{
status= Event_basic::DISABLED;
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
ER_EVENT_EXEC_TIME_IN_THE_PAST,
ER(ER_EVENT_EXEC_TIME_IN_THE_PAST));
}
}
/*
Sets time for execution for one-time event.
SYNOPSIS
Event_parse_data::init_execute_at()
thd Thread
RETURN VALUE
0 OK
ER_WRONG_VALUE Wrong value for execute at (reported)
*/
int
Event_parse_data::init_execute_at(THD *thd)
{
my_bool not_used;
MYSQL_TIME ltime;
my_time_t ltime_utc;
DBUG_ENTER("Event_parse_data::init_execute_at");
if (!item_execute_at)
DBUG_RETURN(0);
if (item_execute_at->fix_fields(thd, &item_execute_at))
goto wrong_value;
/* no starts and/or ends in case of execute_at */
DBUG_PRINT("info", ("starts_null && ends_null should be 1 is %d",
(starts_null && ends_null)));
DBUG_ASSERT(starts_null && ends_null);
if ((not_used= item_execute_at->get_date(&ltime, TIME_NO_ZERO_DATE)))
goto wrong_value;
ltime_utc= TIME_to_timestamp(thd,&ltime,&not_used);
if (!ltime_utc)
{
DBUG_PRINT("error", ("Execute AT after year 2037"));
goto wrong_value;
}
check_if_in_the_past(thd, ltime_utc);
execute_at_null= FALSE;
execute_at= ltime_utc;
DBUG_RETURN(0);
wrong_value:
report_bad_value("AT", item_execute_at);
DBUG_RETURN(ER_WRONG_VALUE);
}
/*
Sets time for execution of multi-time event.s
SYNOPSIS
Event_parse_data::init_interval()
thd Thread
RETURN VALUE
0 OK
EVEX_BAD_PARAMS Interval is not positive or MICROSECOND (reported)
ER_WRONG_VALUE Wrong value for interval (reported)
*/
int
Event_parse_data::init_interval(THD *thd)
{
String value;
INTERVAL interval_tmp;
DBUG_ENTER("Event_parse_data::init_interval");
if (!item_expression)
DBUG_RETURN(0);
switch (interval) {
case INTERVAL_MINUTE_MICROSECOND:
case INTERVAL_HOUR_MICROSECOND:
case INTERVAL_DAY_MICROSECOND:
case INTERVAL_SECOND_MICROSECOND:
case INTERVAL_MICROSECOND:
my_error(ER_NOT_SUPPORTED_YET, MYF(0), "MICROSECOND");
DBUG_RETURN(EVEX_BAD_PARAMS);
default:
break;
}
if (item_expression->fix_fields(thd, &item_expression))
goto wrong_value;
value.alloc(MAX_DATETIME_FULL_WIDTH*MY_CHARSET_BIN_MB_MAXLEN);
if (get_interval_value(item_expression, interval, &value, &interval_tmp))
goto wrong_value;
expression= 0;
switch (interval) {
case INTERVAL_YEAR:
expression= interval_tmp.year;
break;
case INTERVAL_QUARTER:
case INTERVAL_MONTH:
expression= interval_tmp.month;
break;
case INTERVAL_WEEK:
case INTERVAL_DAY:
expression= interval_tmp.day;
break;
case INTERVAL_HOUR:
expression= interval_tmp.hour;
break;
case INTERVAL_MINUTE:
expression= interval_tmp.minute;
break;
case INTERVAL_SECOND:
expression= interval_tmp.second;
break;
case INTERVAL_YEAR_MONTH: // Allow YEAR-MONTH YYYYYMM
expression= interval_tmp.year* 12 + interval_tmp.month;
break;
case INTERVAL_DAY_HOUR:
expression= interval_tmp.day* 24 + interval_tmp.hour;
break;
case INTERVAL_DAY_MINUTE:
expression= (interval_tmp.day* 24 + interval_tmp.hour) * 60 +
interval_tmp.minute;
break;
case INTERVAL_HOUR_SECOND: /* day is anyway 0 */
case INTERVAL_DAY_SECOND:
/* DAY_SECOND having problems because of leap seconds? */
expression= ((interval_tmp.day* 24 + interval_tmp.hour) * 60 +
interval_tmp.minute)*60
+ interval_tmp.second;
break;
case INTERVAL_HOUR_MINUTE:
expression= interval_tmp.hour * 60 + interval_tmp.minute;
break;
case INTERVAL_MINUTE_SECOND:
expression= interval_tmp.minute * 60 + interval_tmp.second;
break;
case INTERVAL_LAST:
DBUG_ASSERT(0);
default:
;/* these are the microsec stuff */
}
if (interval_tmp.neg || expression == 0 ||
expression > EVEX_MAX_INTERVAL_VALUE)
{
my_error(ER_EVENT_INTERVAL_NOT_POSITIVE_OR_TOO_BIG, MYF(0));
DBUG_RETURN(EVEX_BAD_PARAMS);
}
DBUG_RETURN(0);
wrong_value:
report_bad_value("INTERVAL", item_expression);
DBUG_RETURN(ER_WRONG_VALUE);
}
/*
Sets STARTS.
SYNOPSIS
Event_parse_data::init_starts()
expr how much?
NOTES
Note that activation time is not execution time.
EVERY 5 MINUTE STARTS "2004-12-12 10:00:00" means that
the event will be executed every 5 minutes but this will
start at the date shown above. Expressions are possible :
DATE_ADD(NOW(), INTERVAL 1 DAY) -- start tommorow at
same time.
RETURN VALUE
0 OK
ER_WRONG_VALUE Starts before now
*/
int
Event_parse_data::init_starts(THD *thd)
{
my_bool not_used;
MYSQL_TIME ltime;
my_time_t ltime_utc;
DBUG_ENTER("Event_parse_data::init_starts");
if (!item_starts)
DBUG_RETURN(0);
if (item_starts->fix_fields(thd, &item_starts))
goto wrong_value;
if ((not_used= item_starts->get_date(&ltime, TIME_NO_ZERO_DATE)))
goto wrong_value;
ltime_utc= TIME_to_timestamp(thd, &ltime, &not_used);
if (!ltime_utc)
goto wrong_value;
DBUG_PRINT("info",("now: %ld starts: %ld",
(long) thd->query_start(), (long) ltime_utc));
starts_null= FALSE;
starts= ltime_utc;
DBUG_RETURN(0);
wrong_value:
report_bad_value("STARTS", item_starts);
DBUG_RETURN(ER_WRONG_VALUE);
}
/*
Sets ENDS (deactivation time).
SYNOPSIS
Event_parse_data::init_ends()
thd THD
NOTES
Note that activation time is not execution time.
EVERY 5 MINUTE ENDS "2004-12-12 10:00:00" means that
the event will be executed every 5 minutes but this will
end at the date shown above. Expressions are possible :
DATE_ADD(NOW(), INTERVAL 1 DAY) -- end tommorow at
same time.
RETURN VALUE
0 OK
EVEX_BAD_PARAMS Error (reported)
*/
int
Event_parse_data::init_ends(THD *thd)
{
my_bool not_used;
MYSQL_TIME ltime;
my_time_t ltime_utc;
DBUG_ENTER("Event_parse_data::init_ends");
if (!item_ends)
DBUG_RETURN(0);
if (item_ends->fix_fields(thd, &item_ends))
goto error_bad_params;
DBUG_PRINT("info", ("convert to TIME"));
if ((not_used= item_ends->get_date(&ltime, TIME_NO_ZERO_DATE)))
goto error_bad_params;
ltime_utc= TIME_to_timestamp(thd, &ltime, &not_used);
if (!ltime_utc)
goto error_bad_params;
/* Check whether ends is after starts */
DBUG_PRINT("info", ("ENDS after STARTS?"));
if (!starts_null && starts >= ltime_utc)
goto error_bad_params;
check_if_in_the_past(thd, ltime_utc);
ends_null= FALSE;
ends= ltime_utc;
DBUG_RETURN(0);
error_bad_params:
my_error(ER_EVENT_ENDS_BEFORE_STARTS, MYF(0));
DBUG_RETURN(EVEX_BAD_PARAMS);
}
/*
Prints an error message about invalid value. Internally used
during input data verification
SYNOPSIS
Event_parse_data::report_bad_value()
item_name The name of the parameter
bad_item The parameter
*/
void
Event_parse_data::report_bad_value(const char *item_name, Item *bad_item)
{
char buff[120];
String str(buff,(uint32) sizeof(buff), system_charset_info);
String *str2= bad_item->fixed? bad_item->val_str(&str):NULL;
my_error(ER_WRONG_VALUE, MYF(0), item_name, str2? str2->c_ptr_safe():"NULL");
}
/*
Checks for validity the data gathered during the parsing phase.
SYNOPSIS
Event_parse_data::check_parse_data()
thd Thread
RETURN VALUE
FALSE OK
TRUE Error (reported)
*/
bool
Event_parse_data::check_parse_data(THD *thd)
{
bool ret;
DBUG_ENTER("Event_parse_data::check_parse_data");
DBUG_PRINT("info", ("execute_at: 0x%lx expr=0x%lx starts=0x%lx ends=0x%lx",
(long) item_execute_at, (long) item_expression,
(long) item_starts, (long) item_ends));
init_name(thd, identifier);
init_definer(thd);
ret= init_execute_at(thd) || init_interval(thd) || init_starts(thd) ||
init_ends(thd);
check_originator_id(thd);
DBUG_RETURN(ret);
}
/*
Inits definer (definer_user and definer_host) during parsing.
SYNOPSIS
Event_parse_data::init_definer()
thd Thread
*/
void
Event_parse_data::init_definer(THD *thd)
{
DBUG_ENTER("Event_parse_data::init_definer");
DBUG_ASSERT(thd->lex->definer);
const char *definer_user= thd->lex->definer->user.str;
const char *definer_host= thd->lex->definer->host.str;
int definer_user_len= thd->lex->definer->user.length;
int definer_host_len= thd->lex->definer->host.length;
DBUG_PRINT("info",("init definer_user thd->mem_root: 0x%lx "
"definer_user: 0x%lx", (long) thd->mem_root,
(long) definer_user));
/* + 1 for @ */
DBUG_PRINT("info",("init definer as whole"));
definer.length= definer_user_len + definer_host_len + 1;
definer.str= (char*) thd->alloc(definer.length + 1);
DBUG_PRINT("info",("copy the user"));
memcpy(definer.str, definer_user, definer_user_len);
definer.str[definer_user_len]= '@';
DBUG_PRINT("info",("copy the host"));
memcpy(definer.str + definer_user_len + 1, definer_host, definer_host_len);
definer.str[definer.length]= '\0';
DBUG_PRINT("info",("definer [%s] initted", definer.str));
DBUG_VOID_RETURN;
}
/**
Set the originator id of the event to the server_id if executing on
the master or set to the server_id of the master if executing on
the slave. If executing on slave, also set status to SLAVESIDE_DISABLED.
SYNOPSIS
Event_parse_data::check_originator_id()
*/
void Event_parse_data::check_originator_id(THD *thd)
{
/* Disable replicated events on slave. */
if ((thd->system_thread == SYSTEM_THREAD_SLAVE_SQL) ||
(thd->system_thread == SYSTEM_THREAD_SLAVE_IO))
{
DBUG_PRINT("info", ("Invoked object status set to SLAVESIDE_DISABLED."));
if ((status == Event_basic::ENABLED) ||
(status == Event_basic::DISABLED))
status = Event_basic::SLAVESIDE_DISABLED;
originator = thd->server_id;
}
else
originator = server_id;
}
/* /*
Constructor Constructor
...@@ -799,8 +279,9 @@ Event_basic::load_time_zone(THD *thd, const LEX_STRING tz_name) ...@@ -799,8 +279,9 @@ Event_basic::load_time_zone(THD *thd, const LEX_STRING tz_name)
Event_queue_element::Event_queue_element(): Event_queue_element::Event_queue_element():
status_changed(FALSE), last_executed_changed(FALSE), status_changed(FALSE), last_executed_changed(FALSE),
on_completion(ON_COMPLETION_DROP), status(ENABLED), on_completion(Event_parse_data::ON_COMPLETION_DROP),
expression(0), dropped(FALSE), execution_count(0) status(Event_parse_data::ENABLED), expression(0), dropped(FALSE),
execution_count(0)
{ {
DBUG_ENTER("Event_queue_element::Event_queue_element"); DBUG_ENTER("Event_queue_element::Event_queue_element");
...@@ -1057,14 +538,14 @@ Event_queue_element::load_from_row(THD *thd, TABLE *table) ...@@ -1057,14 +538,14 @@ Event_queue_element::load_from_row(THD *thd, TABLE *table)
switch (ptr[0]) switch (ptr[0])
{ {
case 'E' : case 'E' :
status = Event_queue_element::ENABLED; status = Event_parse_data::ENABLED;
break; break;
case 'S' : case 'S' :
status = Event_queue_element::SLAVESIDE_DISABLED; status = Event_parse_data::SLAVESIDE_DISABLED;
break; break;
case 'D' : case 'D' :
default: default:
status = Event_queue_element::DISABLED; status = Event_parse_data::DISABLED;
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)
...@@ -1076,8 +557,8 @@ Event_queue_element::load_from_row(THD *thd, TABLE *table) ...@@ -1076,8 +557,8 @@ Event_queue_element::load_from_row(THD *thd, TABLE *table)
table->field[ET_FIELD_ON_COMPLETION])) == NullS) table->field[ET_FIELD_ON_COMPLETION])) == NullS)
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
on_completion= (ptr[0]=='D'? Event_queue_element::ON_COMPLETION_DROP: on_completion= (ptr[0]=='D'? Event_parse_data::ON_COMPLETION_DROP:
Event_queue_element::ON_COMPLETION_PRESERVE); Event_parse_data::ON_COMPLETION_PRESERVE);
DBUG_RETURN(FALSE); DBUG_RETURN(FALSE);
} }
...@@ -1423,7 +904,7 @@ Event_queue_element::compute_next_execution_time() ...@@ -1423,7 +904,7 @@ Event_queue_element::compute_next_execution_time()
(long) starts, (long) ends, (long) last_executed, (long) starts, (long) ends, (long) last_executed,
(long) this)); (long) this));
if (status != Event_queue_element::ENABLED) if (status != Event_parse_data::ENABLED)
{ {
DBUG_PRINT("compute_next_execution_time", DBUG_PRINT("compute_next_execution_time",
("Event %s is DISABLED", name.str)); ("Event %s is DISABLED", name.str));
...@@ -1437,10 +918,10 @@ Event_queue_element::compute_next_execution_time() ...@@ -1437,10 +918,10 @@ Event_queue_element::compute_next_execution_time()
{ {
DBUG_PRINT("info",("One-time event %s.%s of was already executed", DBUG_PRINT("info",("One-time event %s.%s of was already executed",
dbname.str, name.str)); dbname.str, name.str));
dropped= (on_completion == Event_queue_element::ON_COMPLETION_DROP); dropped= (on_completion == Event_parse_data::ON_COMPLETION_DROP);
DBUG_PRINT("info",("One-time event will be dropped: %d.", dropped)); DBUG_PRINT("info",("One-time event will be dropped: %d.", dropped));
status= Event_queue_element::DISABLED; status= Event_parse_data::DISABLED;
status_changed= TRUE; status_changed= TRUE;
} }
goto ret; goto ret;
...@@ -1457,10 +938,10 @@ Event_queue_element::compute_next_execution_time() ...@@ -1457,10 +938,10 @@ Event_queue_element::compute_next_execution_time()
/* time_now is after ends. don't execute anymore */ /* time_now is after ends. don't execute anymore */
execute_at= 0; execute_at= 0;
execute_at_null= TRUE; execute_at_null= TRUE;
if (on_completion == Event_queue_element::ON_COMPLETION_DROP) if (on_completion == Event_parse_data::ON_COMPLETION_DROP)
dropped= TRUE; dropped= TRUE;
DBUG_PRINT("info", ("Dropped: %d", dropped)); DBUG_PRINT("info", ("Dropped: %d", dropped));
status= Event_queue_element::DISABLED; status= Event_parse_data::DISABLED;
status_changed= TRUE; status_changed= TRUE;
goto ret; goto ret;
...@@ -1521,9 +1002,9 @@ Event_queue_element::compute_next_execution_time() ...@@ -1521,9 +1002,9 @@ Event_queue_element::compute_next_execution_time()
/* Next execution after ends. No more executions */ /* Next execution after ends. No more executions */
execute_at= 0; execute_at= 0;
execute_at_null= TRUE; execute_at_null= TRUE;
if (on_completion == Event_queue_element::ON_COMPLETION_DROP) if (on_completion == Event_parse_data::ON_COMPLETION_DROP)
dropped= TRUE; dropped= TRUE;
status= Event_queue_element::DISABLED; status= Event_parse_data::DISABLED;
status_changed= TRUE; status_changed= TRUE;
} }
else else
...@@ -1613,9 +1094,9 @@ Event_queue_element::compute_next_execution_time() ...@@ -1613,9 +1094,9 @@ Event_queue_element::compute_next_execution_time()
DBUG_PRINT("info", ("Next execution after ENDS. Stop executing.")); DBUG_PRINT("info", ("Next execution after ENDS. Stop executing."));
execute_at= 0; execute_at= 0;
execute_at_null= TRUE; execute_at_null= TRUE;
status= Event_queue_element::DISABLED; status= Event_parse_data::DISABLED;
status_changed= TRUE; status_changed= TRUE;
if (on_completion == Event_queue_element::ON_COMPLETION_DROP) if (on_completion == Event_parse_data::ON_COMPLETION_DROP)
dropped= TRUE; dropped= TRUE;
} }
else else
...@@ -1767,14 +1248,14 @@ Event_timed::get_create_event(THD *thd, String *buf) ...@@ -1767,14 +1248,14 @@ Event_timed::get_create_event(THD *thd, String *buf)
STRING_WITH_LEN("ON SCHEDULE AT")); STRING_WITH_LEN("ON SCHEDULE AT"));
} }
if (on_completion == Event_timed::ON_COMPLETION_DROP) if (on_completion == Event_parse_data::ON_COMPLETION_DROP)
buf->append(STRING_WITH_LEN(" ON COMPLETION NOT PRESERVE ")); buf->append(STRING_WITH_LEN(" ON COMPLETION NOT PRESERVE "));
else else
buf->append(STRING_WITH_LEN(" ON COMPLETION PRESERVE ")); buf->append(STRING_WITH_LEN(" ON COMPLETION PRESERVE "));
if (status == Event_timed::ENABLED) if (status == Event_parse_data::ENABLED)
buf->append(STRING_WITH_LEN("ENABLE")); buf->append(STRING_WITH_LEN("ENABLE"));
else if (status == Event_timed::SLAVESIDE_DISABLED) else if (status == Event_parse_data::SLAVESIDE_DISABLED)
buf->append(STRING_WITH_LEN("DISABLE ON SLAVE")); buf->append(STRING_WITH_LEN("DISABLE ON SLAVE"));
else else
buf->append(STRING_WITH_LEN("DISABLE")); buf->append(STRING_WITH_LEN("DISABLE"));
......
...@@ -22,10 +22,7 @@ ...@@ -22,10 +22,7 @@
@file event_data_objects.h @file event_data_objects.h
*/ */
#define EVEX_GET_FIELD_FAILED -2 #include "event_parse_data.h"
#define EVEX_BAD_PARAMS -5
#define EVEX_MICROSECOND_UNSUP -6
class Event_queue_element_for_exec class Event_queue_element_for_exec
{ {
...@@ -54,23 +51,6 @@ class Event_basic ...@@ -54,23 +51,6 @@ class Event_basic
MEM_ROOT mem_root; MEM_ROOT mem_root;
public: public:
/*
ENABLED = feature can function normally (is turned on)
SLAVESIDE_DISABLED = feature is turned off on slave
DISABLED = feature is turned off
*/
enum enum_status
{
ENABLED = 1,
DISABLED,
SLAVESIDE_DISABLED
};
enum enum_on_completion
{
ON_COMPLETION_DROP = 1,
ON_COMPLETION_PRESERVE
};
LEX_STRING dbname; LEX_STRING dbname;
LEX_STRING name; LEX_STRING name;
...@@ -201,83 +181,6 @@ class Event_job_data : public Event_basic ...@@ -201,83 +181,6 @@ class Event_job_data : public Event_basic
}; };
class Event_parse_data : public Sql_alloc
{
public:
int on_completion;
int status;
longlong originator;
/*
do_not_create will be set if STARTS time is in the past and
on_completion == ON_COMPLETION_DROP.
*/
bool do_not_create;
bool body_changed;
LEX_STRING dbname;
LEX_STRING name;
LEX_STRING definer;// combination of user and host
LEX_STRING comment;
Item* item_starts;
Item* item_ends;
Item* item_execute_at;
my_time_t starts;
my_time_t ends;
my_time_t execute_at;
my_bool starts_null;
my_bool ends_null;
my_bool execute_at_null;
sp_name *identifier;
Item* item_expression;
longlong expression;
interval_type interval;
static Event_parse_data *
new_instance(THD *thd);
bool
check_parse_data(THD *thd);
private:
void
init_definer(THD *thd);
void
init_name(THD *thd, sp_name *spn);
int
init_execute_at(THD *thd);
int
init_interval(THD *thd);
int
init_starts(THD *thd);
int
init_ends(THD *thd);
Event_parse_data();
~Event_parse_data();
void
report_bad_value(const char *item_name, Item *bad_item);
void
check_if_in_the_past(THD *thd, my_time_t ltime_utc);
Event_parse_data(const Event_parse_data &); /* Prevent use of these */
void check_originator_id(THD *thd);
void operator=(Event_parse_data &);
};
/* Compares only the schema part of the identifier */ /* Compares only the schema part of the identifier */
bool bool
event_basic_db_equal(LEX_STRING db, Event_basic *et); event_basic_db_equal(LEX_STRING db, Event_basic *et);
......
/* Copyright (C) 2000-2003 MySQL AB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include "mysql_priv.h"
#include "sp_head.h"
#include "event_parse_data.h"
/*
Returns a new instance
SYNOPSIS
Event_parse_data::new_instance()
RETURN VALUE
Address or NULL in case of error
NOTE
Created on THD's mem_root
*/
Event_parse_data *
Event_parse_data::new_instance(THD *thd)
{
return new (thd->mem_root) Event_parse_data;
}
/*
Constructor
SYNOPSIS
Event_parse_data::Event_parse_data()
*/
Event_parse_data::Event_parse_data()
:on_completion(Event_parse_data::ON_COMPLETION_DROP),
status(Event_parse_data::ENABLED),
do_not_create(FALSE),
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;
comment.str= NULL;
comment.length= 0;
DBUG_VOID_RETURN;
}
/*
Set a name of the event
SYNOPSIS
Event_parse_data::init_name()
thd THD
spn the name extracted in the parser
*/
void
Event_parse_data::init_name(THD *thd, sp_name *spn)
{
DBUG_ENTER("Event_parse_data::init_name");
/* We have to copy strings to get them into the right memroot */
dbname.length= spn->m_db.length;
dbname.str= thd->strmake(spn->m_db.str, spn->m_db.length);
name.length= spn->m_name.length;
name.str= thd->strmake(spn->m_name.str, spn->m_name.length);
if (spn->m_qname.length == 0)
spn->init_qname(thd);
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
will never be executed. If it has ON COMPLETION NOT PRESERVE
(default), then it would normally be dropped already, so on CREATE
EVENT we give a warning, and do not create anyting. On ALTER EVENT
we give a error, and do not change the event.
If the event has ON COMPLETION PRESERVE, then we see if the event is
created or altered to the ENABLED (default) state. If so, then we
give a warning, and change the state to DISABLED.
Otherwise it is a valid event in ON COMPLETION PRESERVE DISABLE
state.
*/
void
Event_parse_data::check_if_in_the_past(THD *thd, my_time_t ltime_utc)
{
if (ltime_utc >= (my_time_t) thd->query_start())
return;
if (on_completion == Event_parse_data::ON_COMPLETION_DROP)
{
switch (thd->lex->sql_command) {
case SQLCOM_CREATE_EVENT:
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
ER_EVENT_CANNOT_CREATE_IN_THE_PAST,
ER(ER_EVENT_CANNOT_CREATE_IN_THE_PAST));
break;
case SQLCOM_ALTER_EVENT:
my_error(ER_EVENT_CANNOT_ALTER_IN_THE_PAST, MYF(0));
break;
default:
DBUG_ASSERT(0);
}
do_not_create= TRUE;
}
else if (status == Event_parse_data::ENABLED)
{
status= Event_parse_data::DISABLED;
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
ER_EVENT_EXEC_TIME_IN_THE_PAST,
ER(ER_EVENT_EXEC_TIME_IN_THE_PAST));
}
}
/*
Sets time for execution for one-time event.
SYNOPSIS
Event_parse_data::init_execute_at()
thd Thread
RETURN VALUE
0 OK
ER_WRONG_VALUE Wrong value for execute at (reported)
*/
int
Event_parse_data::init_execute_at(THD *thd)
{
my_bool not_used;
MYSQL_TIME ltime;
my_time_t ltime_utc;
DBUG_ENTER("Event_parse_data::init_execute_at");
if (!item_execute_at)
DBUG_RETURN(0);
if (item_execute_at->fix_fields(thd, &item_execute_at))
goto wrong_value;
/* no starts and/or ends in case of execute_at */
DBUG_PRINT("info", ("starts_null && ends_null should be 1 is %d",
(starts_null && ends_null)));
DBUG_ASSERT(starts_null && ends_null);
if ((not_used= item_execute_at->get_date(&ltime, TIME_NO_ZERO_DATE)))
goto wrong_value;
ltime_utc= TIME_to_timestamp(thd,&ltime,&not_used);
if (!ltime_utc)
{
DBUG_PRINT("error", ("Execute AT after year 2037"));
goto wrong_value;
}
check_if_in_the_past(thd, ltime_utc);
execute_at_null= FALSE;
execute_at= ltime_utc;
DBUG_RETURN(0);
wrong_value:
report_bad_value("AT", item_execute_at);
DBUG_RETURN(ER_WRONG_VALUE);
}
/*
Sets time for execution of multi-time event.s
SYNOPSIS
Event_parse_data::init_interval()
thd Thread
RETURN VALUE
0 OK
EVEX_BAD_PARAMS Interval is not positive or MICROSECOND (reported)
ER_WRONG_VALUE Wrong value for interval (reported)
*/
int
Event_parse_data::init_interval(THD *thd)
{
String value;
INTERVAL interval_tmp;
DBUG_ENTER("Event_parse_data::init_interval");
if (!item_expression)
DBUG_RETURN(0);
switch (interval) {
case INTERVAL_MINUTE_MICROSECOND:
case INTERVAL_HOUR_MICROSECOND:
case INTERVAL_DAY_MICROSECOND:
case INTERVAL_SECOND_MICROSECOND:
case INTERVAL_MICROSECOND:
my_error(ER_NOT_SUPPORTED_YET, MYF(0), "MICROSECOND");
DBUG_RETURN(EVEX_BAD_PARAMS);
default:
break;
}
if (item_expression->fix_fields(thd, &item_expression))
goto wrong_value;
value.alloc(MAX_DATETIME_FULL_WIDTH*MY_CHARSET_BIN_MB_MAXLEN);
if (get_interval_value(item_expression, interval, &value, &interval_tmp))
goto wrong_value;
expression= 0;
switch (interval) {
case INTERVAL_YEAR:
expression= interval_tmp.year;
break;
case INTERVAL_QUARTER:
case INTERVAL_MONTH:
expression= interval_tmp.month;
break;
case INTERVAL_WEEK:
case INTERVAL_DAY:
expression= interval_tmp.day;
break;
case INTERVAL_HOUR:
expression= interval_tmp.hour;
break;
case INTERVAL_MINUTE:
expression= interval_tmp.minute;
break;
case INTERVAL_SECOND:
expression= interval_tmp.second;
break;
case INTERVAL_YEAR_MONTH: // Allow YEAR-MONTH YYYYYMM
expression= interval_tmp.year* 12 + interval_tmp.month;
break;
case INTERVAL_DAY_HOUR:
expression= interval_tmp.day* 24 + interval_tmp.hour;
break;
case INTERVAL_DAY_MINUTE:
expression= (interval_tmp.day* 24 + interval_tmp.hour) * 60 +
interval_tmp.minute;
break;
case INTERVAL_HOUR_SECOND: /* day is anyway 0 */
case INTERVAL_DAY_SECOND:
/* DAY_SECOND having problems because of leap seconds? */
expression= ((interval_tmp.day* 24 + interval_tmp.hour) * 60 +
interval_tmp.minute)*60
+ interval_tmp.second;
break;
case INTERVAL_HOUR_MINUTE:
expression= interval_tmp.hour * 60 + interval_tmp.minute;
break;
case INTERVAL_MINUTE_SECOND:
expression= interval_tmp.minute * 60 + interval_tmp.second;
break;
case INTERVAL_LAST:
DBUG_ASSERT(0);
default:
;/* these are the microsec stuff */
}
if (interval_tmp.neg || expression == 0 ||
expression > EVEX_MAX_INTERVAL_VALUE)
{
my_error(ER_EVENT_INTERVAL_NOT_POSITIVE_OR_TOO_BIG, MYF(0));
DBUG_RETURN(EVEX_BAD_PARAMS);
}
DBUG_RETURN(0);
wrong_value:
report_bad_value("INTERVAL", item_expression);
DBUG_RETURN(ER_WRONG_VALUE);
}
/*
Sets STARTS.
SYNOPSIS
Event_parse_data::init_starts()
expr how much?
NOTES
Note that activation time is not execution time.
EVERY 5 MINUTE STARTS "2004-12-12 10:00:00" means that
the event will be executed every 5 minutes but this will
start at the date shown above. Expressions are possible :
DATE_ADD(NOW(), INTERVAL 1 DAY) -- start tommorow at
same time.
RETURN VALUE
0 OK
ER_WRONG_VALUE Starts before now
*/
int
Event_parse_data::init_starts(THD *thd)
{
my_bool not_used;
MYSQL_TIME ltime;
my_time_t ltime_utc;
DBUG_ENTER("Event_parse_data::init_starts");
if (!item_starts)
DBUG_RETURN(0);
if (item_starts->fix_fields(thd, &item_starts))
goto wrong_value;
if ((not_used= item_starts->get_date(&ltime, TIME_NO_ZERO_DATE)))
goto wrong_value;
ltime_utc= TIME_to_timestamp(thd, &ltime, &not_used);
if (!ltime_utc)
goto wrong_value;
DBUG_PRINT("info",("now: %ld starts: %ld",
(long) thd->query_start(), (long) ltime_utc));
starts_null= FALSE;
starts= ltime_utc;
DBUG_RETURN(0);
wrong_value:
report_bad_value("STARTS", item_starts);
DBUG_RETURN(ER_WRONG_VALUE);
}
/*
Sets ENDS (deactivation time).
SYNOPSIS
Event_parse_data::init_ends()
thd THD
NOTES
Note that activation time is not execution time.
EVERY 5 MINUTE ENDS "2004-12-12 10:00:00" means that
the event will be executed every 5 minutes but this will
end at the date shown above. Expressions are possible :
DATE_ADD(NOW(), INTERVAL 1 DAY) -- end tommorow at
same time.
RETURN VALUE
0 OK
EVEX_BAD_PARAMS Error (reported)
*/
int
Event_parse_data::init_ends(THD *thd)
{
my_bool not_used;
MYSQL_TIME ltime;
my_time_t ltime_utc;
DBUG_ENTER("Event_parse_data::init_ends");
if (!item_ends)
DBUG_RETURN(0);
if (item_ends->fix_fields(thd, &item_ends))
goto error_bad_params;
DBUG_PRINT("info", ("convert to TIME"));
if ((not_used= item_ends->get_date(&ltime, TIME_NO_ZERO_DATE)))
goto error_bad_params;
ltime_utc= TIME_to_timestamp(thd, &ltime, &not_used);
if (!ltime_utc)
goto error_bad_params;
/* Check whether ends is after starts */
DBUG_PRINT("info", ("ENDS after STARTS?"));
if (!starts_null && starts >= ltime_utc)
goto error_bad_params;
check_if_in_the_past(thd, ltime_utc);
ends_null= FALSE;
ends= ltime_utc;
DBUG_RETURN(0);
error_bad_params:
my_error(ER_EVENT_ENDS_BEFORE_STARTS, MYF(0));
DBUG_RETURN(EVEX_BAD_PARAMS);
}
/*
Prints an error message about invalid value. Internally used
during input data verification
SYNOPSIS
Event_parse_data::report_bad_value()
item_name The name of the parameter
bad_item The parameter
*/
void
Event_parse_data::report_bad_value(const char *item_name, Item *bad_item)
{
char buff[120];
String str(buff,(uint32) sizeof(buff), system_charset_info);
String *str2= bad_item->fixed? bad_item->val_str(&str):NULL;
my_error(ER_WRONG_VALUE, MYF(0), item_name, str2? str2->c_ptr_safe():"NULL");
}
/*
Checks for validity the data gathered during the parsing phase.
SYNOPSIS
Event_parse_data::check_parse_data()
thd Thread
RETURN VALUE
FALSE OK
TRUE Error (reported)
*/
bool
Event_parse_data::check_parse_data(THD *thd)
{
bool ret;
DBUG_ENTER("Event_parse_data::check_parse_data");
DBUG_PRINT("info", ("execute_at: 0x%lx expr=0x%lx starts=0x%lx ends=0x%lx",
(long) item_execute_at, (long) item_expression,
(long) item_starts, (long) item_ends));
init_name(thd, identifier);
init_definer(thd);
ret= init_execute_at(thd) || init_interval(thd) || init_starts(thd) ||
init_ends(thd);
check_originator_id(thd);
DBUG_RETURN(ret);
}
/*
Inits definer (definer_user and definer_host) during parsing.
SYNOPSIS
Event_parse_data::init_definer()
thd Thread
*/
void
Event_parse_data::init_definer(THD *thd)
{
DBUG_ENTER("Event_parse_data::init_definer");
DBUG_ASSERT(thd->lex->definer);
const char *definer_user= thd->lex->definer->user.str;
const char *definer_host= thd->lex->definer->host.str;
int definer_user_len= thd->lex->definer->user.length;
int definer_host_len= thd->lex->definer->host.length;
DBUG_PRINT("info",("init definer_user thd->mem_root: 0x%lx "
"definer_user: 0x%lx", (long) thd->mem_root,
(long) definer_user));
/* + 1 for @ */
DBUG_PRINT("info",("init definer as whole"));
definer.length= definer_user_len + definer_host_len + 1;
definer.str= (char*) thd->alloc(definer.length + 1);
DBUG_PRINT("info",("copy the user"));
memcpy(definer.str, definer_user, definer_user_len);
definer.str[definer_user_len]= '@';
DBUG_PRINT("info",("copy the host"));
memcpy(definer.str + definer_user_len + 1, definer_host, definer_host_len);
definer.str[definer.length]= '\0';
DBUG_PRINT("info",("definer [%s] initted", definer.str));
DBUG_VOID_RETURN;
}
/**
Set the originator id of the event to the server_id if executing on
the master or set to the server_id of the master if executing on
the slave. If executing on slave, also set status to SLAVESIDE_DISABLED.
SYNOPSIS
Event_parse_data::check_originator_id()
*/
void Event_parse_data::check_originator_id(THD *thd)
{
/* Disable replicated events on slave. */
if ((thd->system_thread == SYSTEM_THREAD_SLAVE_SQL) ||
(thd->system_thread == SYSTEM_THREAD_SLAVE_IO))
{
DBUG_PRINT("info", ("Invoked object status set to SLAVESIDE_DISABLED."));
if ((status == Event_parse_data::ENABLED) ||
(status == Event_parse_data::DISABLED))
status = Event_parse_data::SLAVESIDE_DISABLED;
originator = thd->server_id;
}
else
originator = server_id;
}
/* Copyright (C) 2000-2003 MySQL AB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#ifndef _EVENT_PARSE_DATA_H_
#define _EVENT_PARSE_DATA_H_
#define EVEX_GET_FIELD_FAILED -2
#define EVEX_BAD_PARAMS -5
#define EVEX_MICROSECOND_UNSUP -6
#define EVEX_MAX_INTERVAL_VALUE 1000000000L
class Event_parse_data : public Sql_alloc
{
public:
/*
ENABLED = feature can function normally (is turned on)
SLAVESIDE_DISABLED = feature is turned off on slave
DISABLED = feature is turned off
*/
enum enum_status
{
ENABLED = 1,
DISABLED,
SLAVESIDE_DISABLED
};
enum enum_on_completion
{
ON_COMPLETION_DROP = 1,
ON_COMPLETION_PRESERVE
};
int on_completion;
int status;
longlong originator;
/*
do_not_create will be set if STARTS time is in the past and
on_completion == ON_COMPLETION_DROP.
*/
bool do_not_create;
bool body_changed;
LEX_STRING dbname;
LEX_STRING name;
LEX_STRING definer;// combination of user and host
LEX_STRING comment;
Item* item_starts;
Item* item_ends;
Item* item_execute_at;
my_time_t starts;
my_time_t ends;
my_time_t execute_at;
my_bool starts_null;
my_bool ends_null;
my_bool execute_at_null;
sp_name *identifier;
Item* item_expression;
longlong expression;
interval_type interval;
static Event_parse_data *
new_instance(THD *thd);
bool
check_parse_data(THD *thd);
private:
void
init_definer(THD *thd);
void
init_name(THD *thd, sp_name *spn);
int
init_execute_at(THD *thd);
int
init_interval(THD *thd);
int
init_starts(THD *thd);
int
init_ends(THD *thd);
Event_parse_data();
~Event_parse_data();
void
report_bad_value(const char *item_name, Item *bad_item);
void
check_if_in_the_past(THD *thd, my_time_t ltime_utc);
Event_parse_data(const Event_parse_data &); /* Prevent use of these */
void check_originator_id(THD *thd);
void operator=(Event_parse_data &);
};
#endif
...@@ -65,10 +65,10 @@ int event_queue_element_compare_q(void *vptr, uchar* a, uchar *b) ...@@ -65,10 +65,10 @@ int event_queue_element_compare_q(void *vptr, uchar* a, uchar *b)
my_time_t lhs = left->execute_at; my_time_t lhs = left->execute_at;
my_time_t rhs = right->execute_at; my_time_t rhs = right->execute_at;
if (left->status == Event_queue_element::DISABLED) if (left->status == Event_parse_data::DISABLED)
return right->status != Event_queue_element::DISABLED; return right->status != Event_parse_data::DISABLED;
if (right->status == Event_queue_element::DISABLED) if (right->status == Event_parse_data::DISABLED)
return 1; return 1;
return (lhs < rhs ? -1 : (lhs > rhs ? 1 : 0)); return (lhs < rhs ? -1 : (lhs > rhs ? 1 : 0));
...@@ -198,7 +198,7 @@ Event_queue::create_event(THD *thd, Event_queue_element *new_element, ...@@ -198,7 +198,7 @@ Event_queue::create_event(THD *thd, Event_queue_element *new_element,
/* Will do nothing if the event is disabled */ /* Will do nothing if the event is disabled */
new_element->compute_next_execution_time(); new_element->compute_next_execution_time();
if (new_element->status != Event_queue_element::ENABLED) if (new_element->status != Event_parse_data::ENABLED)
{ {
delete new_element; delete new_element;
*created= FALSE; *created= FALSE;
...@@ -236,8 +236,8 @@ Event_queue::update_event(THD *thd, LEX_STRING dbname, LEX_STRING name, ...@@ -236,8 +236,8 @@ Event_queue::update_event(THD *thd, LEX_STRING dbname, LEX_STRING name,
DBUG_ENTER("Event_queue::update_event"); DBUG_ENTER("Event_queue::update_event");
DBUG_PRINT("enter", ("thd: 0x%lx et=[%s.%s]", (long) thd, dbname.str, name.str)); DBUG_PRINT("enter", ("thd: 0x%lx et=[%s.%s]", (long) thd, dbname.str, name.str));
if ((new_element->status == Event_queue_element::DISABLED) || if ((new_element->status == Event_parse_data::DISABLED) ||
(new_element->status == Event_queue_element::SLAVESIDE_DISABLED)) (new_element->status == Event_parse_data::SLAVESIDE_DISABLED))
{ {
DBUG_PRINT("info", ("The event is disabled.")); DBUG_PRINT("info", ("The event is disabled."));
/* /*
...@@ -452,7 +452,7 @@ Event_queue::recalculate_activation_times(THD *thd) ...@@ -452,7 +452,7 @@ Event_queue::recalculate_activation_times(THD *thd)
for (i= queue.elements; i > 0; i--) for (i= queue.elements; i > 0; i--)
{ {
Event_queue_element *element = (Event_queue_element*)queue_element(&queue, i - 1); Event_queue_element *element = (Event_queue_element*)queue_element(&queue, i - 1);
if (element->status != Event_queue_element::DISABLED) if (element->status != Event_parse_data::DISABLED)
break; break;
/* /*
This won't cause queue re-order, because we remove This won't cause queue re-order, because we remove
...@@ -615,14 +615,14 @@ Event_queue::get_top_for_execution_if_time(THD *thd, ...@@ -615,14 +615,14 @@ Event_queue::get_top_for_execution_if_time(THD *thd,
DBUG_PRINT("info", ("Ready for execution")); DBUG_PRINT("info", ("Ready for execution"));
top->mark_last_executed(thd); top->mark_last_executed(thd);
if (top->compute_next_execution_time()) if (top->compute_next_execution_time())
top->status= Event_queue_element::DISABLED; top->status= Event_parse_data::DISABLED;
DBUG_PRINT("info", ("event %s status is %d", top->name.str, top->status)); DBUG_PRINT("info", ("event %s status is %d", top->name.str, top->status));
top->execution_count++; top->execution_count++;
(*event_name)->dropped= top->dropped; (*event_name)->dropped= top->dropped;
top->update_timing_fields(thd); top->update_timing_fields(thd);
if (top->status == Event_queue_element::DISABLED) if (top->status == Event_parse_data::DISABLED)
{ {
DBUG_PRINT("info", ("removing from the queue")); DBUG_PRINT("info", ("removing from the queue"));
sql_print_information("Event Scheduler: Last execution of %s.%s. %s", sql_print_information("Event Scheduler: Last execution of %s.%s. %s",
......
...@@ -861,6 +861,7 @@ Events::fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */) ...@@ -861,6 +861,7 @@ Events::fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */)
bool bool
Events::init(my_bool opt_noacl) Events::init(my_bool opt_noacl)
{ {
THD *thd; THD *thd;
bool res= FALSE; bool res= FALSE;
...@@ -954,7 +955,6 @@ Events::init(my_bool opt_noacl) ...@@ -954,7 +955,6 @@ Events::init(my_bool opt_noacl)
DBUG_RETURN(res); DBUG_RETURN(res);
} }
/* /*
Cleans up scheduler's resources. Called at server shutdown. Cleans up scheduler's resources. Called at server shutdown.
...@@ -1170,7 +1170,7 @@ Events::load_events_from_db(THD *thd) ...@@ -1170,7 +1170,7 @@ Events::load_events_from_db(THD *thd)
goto end; goto end;
} }
drop_on_completion= (et->on_completion == drop_on_completion= (et->on_completion ==
Event_queue_element::ON_COMPLETION_DROP); Event_parse_data::ON_COMPLETION_DROP);
if (event_queue->create_event(thd, et, &created)) if (event_queue->create_event(thd, et, &created))
......
...@@ -3498,7 +3498,9 @@ static int init_thread_environment() ...@@ -3498,7 +3498,9 @@ static int init_thread_environment()
(void) pthread_mutex_init(&LOCK_server_started, MY_MUTEX_INIT_FAST); (void) pthread_mutex_init(&LOCK_server_started, MY_MUTEX_INIT_FAST);
(void) pthread_cond_init(&COND_server_started,NULL); (void) pthread_cond_init(&COND_server_started,NULL);
sp_cache_init(); sp_cache_init();
#ifdef HAVE_EVENT_SCHEDULER
Events::init_mutexes(); Events::init_mutexes();
#endif
/* Parameter for threads created for connections */ /* Parameter for threads created for connections */
(void) pthread_attr_init(&connection_attrib); (void) pthread_attr_init(&connection_attrib);
(void) pthread_attr_setdetachstate(&connection_attrib, (void) pthread_attr_setdetachstate(&connection_attrib,
...@@ -7831,8 +7833,12 @@ mysqld_get_one_option(int optid, ...@@ -7831,8 +7833,12 @@ mysqld_get_one_option(int optid,
} }
#endif #endif
case OPT_EVENT_SCHEDULER: case OPT_EVENT_SCHEDULER:
#ifndef HAVE_EVENT_SCHEDULER
sql_perror("Event scheduler is not supported in embedded build.");
#else
if (Events::set_opt_event_scheduler(argument)) if (Events::set_opt_event_scheduler(argument))
exit(1); exit(1);
#endif
break; break;
case (int) OPT_SKIP_NEW: case (int) OPT_SKIP_NEW:
opt_specialflag|= SPECIAL_NO_NEW_FUNC; opt_specialflag|= SPECIAL_NO_NEW_FUNC;
......
...@@ -241,7 +241,10 @@ static sys_var_long_ptr sys_delayed_insert_timeout(&vars, "delayed_insert_timeou ...@@ -241,7 +241,10 @@ static sys_var_long_ptr sys_delayed_insert_timeout(&vars, "delayed_insert_timeou
static sys_var_long_ptr sys_delayed_queue_size(&vars, "delayed_queue_size", static sys_var_long_ptr sys_delayed_queue_size(&vars, "delayed_queue_size",
&delayed_queue_size); &delayed_queue_size);
#ifdef HAVE_EVENT_SCHEDULER
static sys_var_event_scheduler sys_event_scheduler(&vars, "event_scheduler"); static sys_var_event_scheduler sys_event_scheduler(&vars, "event_scheduler");
#endif
static sys_var_long_ptr sys_expire_logs_days(&vars, "expire_logs_days", static sys_var_long_ptr sys_expire_logs_days(&vars, "expire_logs_days",
&expire_logs_days); &expire_logs_days);
static sys_var_bool_ptr sys_flush(&vars, "flush", &myisam_flush); static sys_var_bool_ptr sys_flush(&vars, "flush", &myisam_flush);
...@@ -4026,13 +4029,12 @@ uchar *sys_var_thd_dbug::value_ptr(THD *thd, enum_var_type type, LEX_STRING *b) ...@@ -4026,13 +4029,12 @@ uchar *sys_var_thd_dbug::value_ptr(THD *thd, enum_var_type type, LEX_STRING *b)
return (uchar*) thd->strdup(buf); return (uchar*) thd->strdup(buf);
} }
#ifdef HAVE_EVENT_SCHEDULER
bool sys_var_event_scheduler::check(THD *thd, set_var *var) bool sys_var_event_scheduler::check(THD *thd, set_var *var)
{ {
return check_enum(thd, var, &Events::var_typelib); return check_enum(thd, var, &Events::var_typelib);
} }
/* /*
The update method of the global variable event_scheduler. The update method of the global variable event_scheduler.
If event_scheduler is switched from 0 to 1 then the scheduler main If event_scheduler is switched from 0 to 1 then the scheduler main
...@@ -4071,7 +4073,7 @@ uchar *sys_var_event_scheduler::value_ptr(THD *thd, enum_var_type type, ...@@ -4071,7 +4073,7 @@ uchar *sys_var_event_scheduler::value_ptr(THD *thd, enum_var_type type,
{ {
return (uchar *) Events::get_opt_event_scheduler_str(); return (uchar *) Events::get_opt_event_scheduler_str();
} }
#endif
/**************************************************************************** /****************************************************************************
Used templates Used templates
......
...@@ -1085,7 +1085,7 @@ class sys_var_thd_lc_time_names :public sys_var_thd ...@@ -1085,7 +1085,7 @@ class sys_var_thd_lc_time_names :public sys_var_thd
virtual void set_default(THD *thd, enum_var_type type); virtual void set_default(THD *thd, enum_var_type type);
}; };
#ifdef HAVE_EVENT_SCHEDULER
class sys_var_event_scheduler :public sys_var_long_ptr class sys_var_event_scheduler :public sys_var_long_ptr
{ {
/* We need a derived class only to have a warn_deprecated() */ /* We need a derived class only to have a warn_deprecated() */
...@@ -1101,6 +1101,7 @@ class sys_var_event_scheduler :public sys_var_long_ptr ...@@ -1101,6 +1101,7 @@ class sys_var_event_scheduler :public sys_var_long_ptr
return type != STRING_RESULT && type != INT_RESULT; return type != STRING_RESULT && type != INT_RESULT;
} }
}; };
#endif
extern void fix_binlog_format_after_update(THD *thd, enum_var_type type); extern void fix_binlog_format_after_update(THD *thd, enum_var_type type);
......
...@@ -922,7 +922,9 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) ...@@ -922,7 +922,9 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
ha_drop_database(path); ha_drop_database(path);
query_cache_invalidate1(db); query_cache_invalidate1(db);
(void) sp_drop_db_routines(thd, db); /* @todo Do not ignore errors */ (void) sp_drop_db_routines(thd, db); /* @todo Do not ignore errors */
#ifdef HAVE_EVENT_SCHEDULER
Events::drop_schema_events(thd, db); Events::drop_schema_events(thd, db);
#endif
error = 0; error = 0;
} }
} }
......
...@@ -1998,7 +1998,12 @@ mysql_execute_command(THD *thd) ...@@ -1998,7 +1998,12 @@ mysql_execute_command(THD *thd)
DBUG_ASSERT(thd->transaction.stmt.modified_non_trans_table == FALSE); DBUG_ASSERT(thd->transaction.stmt.modified_non_trans_table == FALSE);
switch (lex->sql_command) { switch (lex->sql_command) {
case SQLCOM_SHOW_EVENTS: case SQLCOM_SHOW_EVENTS:
#ifndef HAVE_EVENT_SCHEDULER
my_error(ER_NOT_SUPPORTED_YET, MYF(0), "embedded server");
break;
#endif
case SQLCOM_SHOW_STATUS_PROC: case SQLCOM_SHOW_STATUS_PROC:
case SQLCOM_SHOW_STATUS_FUNC: case SQLCOM_SHOW_STATUS_FUNC:
res= execute_sqlcom_select(thd, all_tables); res= execute_sqlcom_select(thd, all_tables);
...@@ -3483,6 +3488,7 @@ mysql_execute_command(THD *thd) ...@@ -3483,6 +3488,7 @@ mysql_execute_command(THD *thd)
} }
case SQLCOM_CREATE_EVENT: case SQLCOM_CREATE_EVENT:
case SQLCOM_ALTER_EVENT: case SQLCOM_ALTER_EVENT:
#ifdef HAVE_EVENT_SCHEDULER
do do
{ {
DBUG_ASSERT(lex->event_parse_data); DBUG_ASSERT(lex->event_parse_data);
...@@ -3536,6 +3542,10 @@ mysql_execute_command(THD *thd) ...@@ -3536,6 +3542,10 @@ mysql_execute_command(THD *thd)
lex->drop_if_exists))) lex->drop_if_exists)))
my_ok(thd); my_ok(thd);
break; break;
#else
my_error(ER_NOT_SUPPORTED_YET,MYF(0),"embedded server");
break;
#endif
case SQLCOM_CREATE_FUNCTION: // UDF function case SQLCOM_CREATE_FUNCTION: // UDF function
{ {
if (check_access(thd,INSERT_ACL,"mysql",0,1,0,0)) if (check_access(thd,INSERT_ACL,"mysql",0,1,0,0))
......
...@@ -25,8 +25,10 @@ ...@@ -25,8 +25,10 @@
#include "sql_trigger.h" #include "sql_trigger.h"
#include "authors.h" #include "authors.h"
#include "contributors.h" #include "contributors.h"
#ifdef HAVE_EVENT_SCHEDULER
#include "events.h" #include "events.h"
#include "event_data_objects.h" #include "event_data_objects.h"
#endif
#include <my_dir.h> #include <my_dir.h>
#define STR_OR_NIL(S) ((S) ? (S) : "<nil>") #define STR_OR_NIL(S) ((S) ? (S) : "<nil>")
...@@ -287,7 +289,9 @@ static struct show_privileges_st sys_privileges[]= ...@@ -287,7 +289,9 @@ static struct show_privileges_st sys_privileges[]=
{"Create user", "Server Admin", "To create new users"}, {"Create user", "Server Admin", "To create new users"},
{"Delete", "Tables", "To delete existing rows"}, {"Delete", "Tables", "To delete existing rows"},
{"Drop", "Databases,Tables", "To drop databases, tables, and views"}, {"Drop", "Databases,Tables", "To drop databases, tables, and views"},
#ifdef HAVE_EVENT_SCHEDULER
{"Event","Server Admin","To create, alter, drop and execute events"}, {"Event","Server Admin","To create, alter, drop and execute events"},
#endif
{"Execute", "Functions,Procedures", "To execute stored routines"}, {"Execute", "Functions,Procedures", "To execute stored routines"},
{"File", "File access on server", "To read and write files on the server"}, {"File", "File access on server", "To read and write files on the server"},
{"Grant option", "Databases,Tables,Functions,Procedures", "To give to other users those privileges you possess"}, {"Grant option", "Databases,Tables,Functions,Procedures", "To give to other users those privileges you possess"},
...@@ -4959,7 +4963,7 @@ static interval_type get_real_interval_type(interval_type i_type) ...@@ -4959,7 +4963,7 @@ static interval_type get_real_interval_type(interval_type i_type)
#endif #endif
#ifdef HAVE_EVENT_SCHEDULER
/* /*
Loads an event from mysql.event and copies it's data to a row of Loads an event from mysql.event and copies it's data to a row of
I_S.EVENTS I_S.EVENTS
...@@ -5079,14 +5083,14 @@ copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table) ...@@ -5079,14 +5083,14 @@ copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table)
switch (et.status) switch (et.status)
{ {
case Event_timed::ENABLED: case Event_parse_data::ENABLED:
sch_table->field[ISE_STATUS]->store(STRING_WITH_LEN("ENABLED"), scs); sch_table->field[ISE_STATUS]->store(STRING_WITH_LEN("ENABLED"), scs);
break; break;
case Event_timed::SLAVESIDE_DISABLED: case Event_parse_data::SLAVESIDE_DISABLED:
sch_table->field[ISE_STATUS]->store(STRING_WITH_LEN("SLAVESIDE_DISABLED"), sch_table->field[ISE_STATUS]->store(STRING_WITH_LEN("SLAVESIDE_DISABLED"),
scs); scs);
break; break;
case Event_timed::DISABLED: case Event_parse_data::DISABLED:
sch_table->field[ISE_STATUS]->store(STRING_WITH_LEN("DISABLED"), scs); sch_table->field[ISE_STATUS]->store(STRING_WITH_LEN("DISABLED"), scs);
break; break;
default: default:
...@@ -5095,7 +5099,7 @@ copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table) ...@@ -5095,7 +5099,7 @@ copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table)
sch_table->field[ISE_ORIGINATOR]->store(et.originator, TRUE); sch_table->field[ISE_ORIGINATOR]->store(et.originator, TRUE);
/* on_completion */ /* on_completion */
if (et.on_completion == Event_timed::ON_COMPLETION_DROP) if (et.on_completion == Event_parse_data::ON_COMPLETION_DROP)
sch_table->field[ISE_ON_COMPLETION]-> sch_table->field[ISE_ON_COMPLETION]->
store(STRING_WITH_LEN("NOT PRESERVE"), scs); store(STRING_WITH_LEN("NOT PRESERVE"), scs);
else else
...@@ -5145,7 +5149,7 @@ copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table) ...@@ -5145,7 +5149,7 @@ copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table)
DBUG_RETURN(0); DBUG_RETURN(0);
} }
#endif
int fill_open_tables(THD *thd, TABLE_LIST *tables, COND *cond) int fill_open_tables(THD *thd, TABLE_LIST *tables, COND *cond)
{ {
...@@ -6541,8 +6545,10 @@ ST_SCHEMA_TABLE schema_tables[]= ...@@ -6541,8 +6545,10 @@ ST_SCHEMA_TABLE schema_tables[]=
fill_schema_column_privileges, 0, 0, -1, -1, 0, 0}, fill_schema_column_privileges, 0, 0, -1, -1, 0, 0},
{"ENGINES", engines_fields_info, create_schema_table, {"ENGINES", engines_fields_info, create_schema_table,
fill_schema_engines, make_old_format, 0, -1, -1, 0, 0}, fill_schema_engines, make_old_format, 0, -1, -1, 0, 0},
#ifdef HAVE_EVENT_SCHEDULER
{"EVENTS", events_fields_info, create_schema_table, {"EVENTS", events_fields_info, create_schema_table,
Events::fill_schema_events, make_old_format, 0, -1, -1, 0, 0}, Events::fill_schema_events, make_old_format, 0, -1, -1, 0, 0},
#endif
{"FILES", files_fields_info, create_schema_table, {"FILES", files_fields_info, create_schema_table,
fill_schema_files, 0, 0, -1, -1, 0, 0}, fill_schema_files, 0, 0, -1, -1, 0, 0},
{"GLOBAL_STATUS", variables_fields_info, create_schema_table, {"GLOBAL_STATUS", variables_fields_info, create_schema_table,
......
...@@ -27,7 +27,9 @@ ...@@ -27,7 +27,9 @@
#include <sys/malloc.h> #include <sys/malloc.h>
#endif #endif
#ifdef HAVE_EVENT_SCHEDULER
#include "events.h" #include "events.h"
#endif
static const char *lock_descriptions[] = static const char *lock_descriptions[] =
{ {
...@@ -539,6 +541,8 @@ Estimated memory (with thread stack): %ld\n", ...@@ -539,6 +541,8 @@ Estimated memory (with thread stack): %ld\n",
(long) (thread_count * my_thread_stack_size + info.hblkhd + info.arena)); (long) (thread_count * my_thread_stack_size + info.hblkhd + info.arena));
#endif #endif
#ifdef HAVE_EVENT_SCHEDULER
Events::dump_internal_status(); Events::dump_internal_status();
#endif
puts(""); puts("");
} }
...@@ -42,7 +42,7 @@ ...@@ -42,7 +42,7 @@
#include "sp_pcontext.h" #include "sp_pcontext.h"
#include "sp_rcontext.h" #include "sp_rcontext.h"
#include "sp.h" #include "sp.h"
#include "event_data_objects.h" #include "event_parse_data.h"
#include <myisam.h> #include <myisam.h>
#include <myisammrg.h> #include <myisammrg.h>
...@@ -1783,7 +1783,6 @@ event_tail: ...@@ -1783,7 +1783,6 @@ event_tail:
LEX *lex=Lex; LEX *lex=Lex;
lex->create_info.options= $2; lex->create_info.options= $2;
if (!(lex->event_parse_data= Event_parse_data::new_instance(thd))) if (!(lex->event_parse_data= Event_parse_data::new_instance(thd)))
MYSQL_YYABORT; MYSQL_YYABORT;
lex->event_parse_data->identifier= $3; lex->event_parse_data->identifier= $3;
...@@ -1840,17 +1839,17 @@ opt_ev_status: ...@@ -1840,17 +1839,17 @@ opt_ev_status:
/* empty */ { $$= 0; } /* empty */ { $$= 0; }
| ENABLE_SYM | ENABLE_SYM
{ {
Lex->event_parse_data->status= Event_basic::ENABLED; Lex->event_parse_data->status= Event_parse_data::ENABLED;
$$= 1; $$= 1;
} }
| DISABLE_SYM ON SLAVE | DISABLE_SYM ON SLAVE
{ {
Lex->event_parse_data->status= Event_basic::SLAVESIDE_DISABLED; Lex->event_parse_data->status= Event_parse_data::SLAVESIDE_DISABLED;
$$= 1; $$= 1;
} }
| DISABLE_SYM | DISABLE_SYM
{ {
Lex->event_parse_data->status= Event_basic::DISABLED; Lex->event_parse_data->status= Event_parse_data::DISABLED;
$$= 1; $$= 1;
} }
; ;
...@@ -1883,13 +1882,13 @@ ev_on_completion: ...@@ -1883,13 +1882,13 @@ ev_on_completion:
ON COMPLETION_SYM PRESERVE_SYM ON COMPLETION_SYM PRESERVE_SYM
{ {
Lex->event_parse_data->on_completion= Lex->event_parse_data->on_completion=
Event_basic::ON_COMPLETION_PRESERVE; Event_parse_data::ON_COMPLETION_PRESERVE;
$$= 1; $$= 1;
} }
| ON COMPLETION_SYM NOT_SYM PRESERVE_SYM | ON COMPLETION_SYM NOT_SYM PRESERVE_SYM
{ {
Lex->event_parse_data->on_completion= Lex->event_parse_data->on_completion=
Event_basic::ON_COMPLETION_DROP; Event_parse_data::ON_COMPLETION_DROP;
$$= 1; $$= 1;
} }
; ;
......
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