mirror of
https://github.com/MariaDB/server.git
synced 2025-07-30 16:24:05 +03:00
fix for bug#16537 (Events: mysql.event.starts is null)
- now when the event is created and STARTS is omitted then STARTS is implicitly CURRENT_TIMESTAMP - This CS also fixed incorrect presentation of STARTS/ENDS in I_S.EVENTS (incorporated review changes) mysql-test/r/events.result: results of new test cases mysql-test/t/events.test: new test cases for bug #16537 (Events: mysql.event.starts is null) sql/event.cc: - check whether event_timed::starts_null only in case event_timed::expression is set, so for recurring events only - disable binlogging of CREATE EVENT statement. It should not be replicated but the result of the execution. Still the replication is untouched topic. sql/event.h: - add flags whether starts, ends and execute_at are null or not sql/event_executor.cc: - check whether xxx_null instead of !xxxx.year sql/event_timed.cc: - introduce xxx_null and change the usage of xxx.year to !xxx_null sql/sql_show.cc: - don't show 0000-00-00 in I_S.EVENTS when the value is NULL sql/sql_yacc.yy: - if STARTS is omitted default to current_timestamp
This commit is contained in:
@ -13,6 +13,54 @@ alter event event3 rename to event2;
|
|||||||
drop event event2;
|
drop event event2;
|
||||||
create event event2 on schedule every 2 second starts now() ends date_add(now(), interval 5 hour) comment "some" DO begin end;
|
create event event2 on schedule every 2 second starts now() ends date_add(now(), interval 5 hour) comment "some" DO begin end;
|
||||||
drop event event2;
|
drop event event2;
|
||||||
|
CREATE EVENT event_starts_test ON SCHEDULE EVERY 10 SECOND COMMENT "" DO SELECT 1;
|
||||||
|
SHOW EVENTS;
|
||||||
|
Db Name Definer Type Execute at Interval value Interval field Starts Ends Status
|
||||||
|
events_test event_starts_test root@localhost RECURRING NULL 10 INTERVAL_SECOND # # ENABLED
|
||||||
|
SELECT starts IS NULL, ends IS NULL, comment FROM mysql.event WHERE db='events_test' AND name='event_starts_test';
|
||||||
|
starts IS NULL ends IS NULL comment
|
||||||
|
0 1
|
||||||
|
ALTER EVENT event_starts_test ON SCHEDULE AT '2020-02-02 20:00:02';
|
||||||
|
SHOW EVENTS;
|
||||||
|
Db Name Definer Type Execute at Interval value Interval field Starts Ends Status
|
||||||
|
events_test event_starts_test root@localhost ONE TIME 2020-02-02 17:00:02 NULL NULL # # ENABLED
|
||||||
|
SELECT starts IS NULL, ends IS NULL, comment FROM mysql.event WHERE db='events_test' AND name='event_starts_test';
|
||||||
|
starts IS NULL ends IS NULL comment
|
||||||
|
1 1
|
||||||
|
ALTER EVENT event_starts_test COMMENT "non-empty comment";
|
||||||
|
SHOW EVENTS;
|
||||||
|
Db Name Definer Type Execute at Interval value Interval field Starts Ends Status
|
||||||
|
events_test event_starts_test root@localhost ONE TIME 2020-02-02 17:00:02 NULL NULL # # ENABLED
|
||||||
|
SELECT starts IS NULL, ends IS NULL, comment FROM mysql.event WHERE db='events_test' AND name='event_starts_test';
|
||||||
|
starts IS NULL ends IS NULL comment
|
||||||
|
1 1 non-empty comment
|
||||||
|
ALTER EVENT event_starts_test COMMENT "";
|
||||||
|
SHOW EVENTS;
|
||||||
|
Db Name Definer Type Execute at Interval value Interval field Starts Ends Status
|
||||||
|
events_test event_starts_test root@localhost ONE TIME 2020-02-02 17:00:02 NULL NULL # # ENABLED
|
||||||
|
SELECT starts IS NULL, ends IS NULL, comment FROM mysql.event WHERE db='events_test' AND name='event_starts_test';
|
||||||
|
starts IS NULL ends IS NULL comment
|
||||||
|
1 1
|
||||||
|
DROP EVENT event_starts_test;
|
||||||
|
CREATE EVENT event_starts_test ON SCHEDULE EVERY 20 SECOND STARTS '2020-02-02 20:00:02' ENDS '2022-02-02 20:00:02' DO SELECT 2;
|
||||||
|
SHOW EVENTS;
|
||||||
|
Db Name Definer Type Execute at Interval value Interval field Starts Ends Status
|
||||||
|
events_test event_starts_test root@localhost RECURRING NULL 20 INTERVAL_SECOND # # ENABLED
|
||||||
|
SELECT starts IS NULL, ends IS NULL, comment FROM mysql.event WHERE db='events_test' AND name='event_starts_test';
|
||||||
|
starts IS NULL ends IS NULL comment
|
||||||
|
0 0
|
||||||
|
ALTER EVENT event_starts_test COMMENT "non-empty comment";
|
||||||
|
SHOW EVENTS;
|
||||||
|
Db Name Definer Type Execute at Interval value Interval field Starts Ends Status
|
||||||
|
events_test event_starts_test root@localhost RECURRING NULL 20 INTERVAL_SECOND # # ENABLED
|
||||||
|
SELECT starts IS NULL, ends IS NULL, comment FROM mysql.event WHERE db='events_test' AND name='event_starts_test';
|
||||||
|
starts IS NULL ends IS NULL comment
|
||||||
|
0 0 non-empty comment
|
||||||
|
ALTER EVENT event_starts_test COMMENT "";
|
||||||
|
SHOW EVENTS;
|
||||||
|
Db Name Definer Type Execute at Interval value Interval field Starts Ends Status
|
||||||
|
events_test event_starts_test root@localhost RECURRING NULL 20 INTERVAL_SECOND # # ENABLED
|
||||||
|
DROP EVENT event_starts_test;
|
||||||
create event e_43 on schedule every 1 second do set @a = 5;
|
create event e_43 on schedule every 1 second do set @a = 5;
|
||||||
set global event_scheduler = 1;
|
set global event_scheduler = 1;
|
||||||
select sleep(2);
|
select sleep(2);
|
||||||
@ -64,7 +112,7 @@ SHOW GRANTS;
|
|||||||
Grants for ev_test@localhost
|
Grants for ev_test@localhost
|
||||||
GRANT USAGE ON *.* TO 'ev_test'@'localhost'
|
GRANT USAGE ON *.* TO 'ev_test'@'localhost'
|
||||||
GRANT ALL PRIVILEGES ON `events_test`.* TO 'ev_test'@'localhost'
|
GRANT ALL PRIVILEGES ON `events_test`.* TO 'ev_test'@'localhost'
|
||||||
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, REFERENCES, INDEX, ALTER, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE ON `events_test2`.* TO 'ev_test'@'localhost'
|
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, REFERENCES, INDEX, ALTER, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, TRIGGER ON `events_test2`.* TO 'ev_test'@'localhost'
|
||||||
"Here comes an error:";
|
"Here comes an error:";
|
||||||
SHOW EVENTS;
|
SHOW EVENTS;
|
||||||
ERROR 42000: Access denied for user 'ev_test'@'localhost' to database 'events_test2'
|
ERROR 42000: Access denied for user 'ev_test'@'localhost' to database 'events_test2'
|
||||||
|
@ -15,6 +15,38 @@ drop event event2;
|
|||||||
create event event2 on schedule every 2 second starts now() ends date_add(now(), interval 5 hour) comment "some" DO begin end;
|
create event event2 on schedule every 2 second starts now() ends date_add(now(), interval 5 hour) comment "some" DO begin end;
|
||||||
drop event event2;
|
drop event event2;
|
||||||
|
|
||||||
|
# BUG #16537 (Events: mysql.event.starts is null)
|
||||||
|
CREATE EVENT event_starts_test ON SCHEDULE EVERY 10 SECOND COMMENT "" DO SELECT 1;
|
||||||
|
--replace_column 8 # 9 #
|
||||||
|
SHOW EVENTS;
|
||||||
|
SELECT starts IS NULL, ends IS NULL, comment FROM mysql.event WHERE db='events_test' AND name='event_starts_test';
|
||||||
|
ALTER EVENT event_starts_test ON SCHEDULE AT '2020-02-02 20:00:02';
|
||||||
|
--replace_column 8 # 9 #
|
||||||
|
SHOW EVENTS;
|
||||||
|
SELECT starts IS NULL, ends IS NULL, comment FROM mysql.event WHERE db='events_test' AND name='event_starts_test';
|
||||||
|
ALTER EVENT event_starts_test COMMENT "non-empty comment";
|
||||||
|
--replace_column 8 # 9 #
|
||||||
|
SHOW EVENTS;
|
||||||
|
SELECT starts IS NULL, ends IS NULL, comment FROM mysql.event WHERE db='events_test' AND name='event_starts_test';
|
||||||
|
ALTER EVENT event_starts_test COMMENT "";
|
||||||
|
--replace_column 8 # 9 #
|
||||||
|
SHOW EVENTS;
|
||||||
|
SELECT starts IS NULL, ends IS NULL, comment FROM mysql.event WHERE db='events_test' AND name='event_starts_test';
|
||||||
|
DROP EVENT event_starts_test;
|
||||||
|
CREATE EVENT event_starts_test ON SCHEDULE EVERY 20 SECOND STARTS '2020-02-02 20:00:02' ENDS '2022-02-02 20:00:02' DO SELECT 2;
|
||||||
|
--replace_column 8 # 9 #
|
||||||
|
SHOW EVENTS;
|
||||||
|
SELECT starts IS NULL, ends IS NULL, comment FROM mysql.event WHERE db='events_test' AND name='event_starts_test';
|
||||||
|
ALTER EVENT event_starts_test COMMENT "non-empty comment";
|
||||||
|
--replace_column 8 # 9 #
|
||||||
|
SHOW EVENTS;
|
||||||
|
SELECT starts IS NULL, ends IS NULL, comment FROM mysql.event WHERE db='events_test' AND name='event_starts_test';
|
||||||
|
ALTER EVENT event_starts_test COMMENT "";
|
||||||
|
--replace_column 8 # 9 #
|
||||||
|
SHOW EVENTS;
|
||||||
|
DROP EVENT event_starts_test;
|
||||||
|
#
|
||||||
|
#
|
||||||
create event e_43 on schedule every 1 second do set @a = 5;
|
create event e_43 on schedule every 1 second do set @a = 5;
|
||||||
set global event_scheduler = 1;
|
set global event_scheduler = 1;
|
||||||
select sleep(2);
|
select sleep(2);
|
||||||
|
52
sql/event.cc
52
sql/event.cc
@ -273,20 +273,6 @@ evex_fill_row(THD *thd, TABLE *table, event_timed *et, my_bool is_update)
|
|||||||
store(et->body.str, et->body.length, system_charset_info))
|
store(et->body.str, et->body.length, system_charset_info))
|
||||||
goto trunc_err;
|
goto trunc_err;
|
||||||
|
|
||||||
if (et->starts.year)
|
|
||||||
{
|
|
||||||
table->field[EVEX_FIELD_STARTS]->set_notnull();// set NULL flag to OFF
|
|
||||||
table->field[EVEX_FIELD_STARTS]->
|
|
||||||
store_time(&et->starts, MYSQL_TIMESTAMP_DATETIME);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (et->ends.year)
|
|
||||||
{
|
|
||||||
table->field[EVEX_FIELD_ENDS]->set_notnull();
|
|
||||||
table->field[EVEX_FIELD_ENDS]->
|
|
||||||
store_time(&et->ends, MYSQL_TIMESTAMP_DATETIME);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (et->expression)
|
if (et->expression)
|
||||||
{
|
{
|
||||||
table->field[EVEX_FIELD_INTERVAL_EXPR]->set_notnull();
|
table->field[EVEX_FIELD_INTERVAL_EXPR]->set_notnull();
|
||||||
@ -300,18 +286,31 @@ evex_fill_row(THD *thd, TABLE *table, event_timed *et, my_bool is_update)
|
|||||||
table->field[EVEX_FIELD_TRANSIENT_INTERVAL]->store((longlong)et->interval+1);
|
table->field[EVEX_FIELD_TRANSIENT_INTERVAL]->store((longlong)et->interval+1);
|
||||||
|
|
||||||
table->field[EVEX_FIELD_EXECUTE_AT]->set_null();
|
table->field[EVEX_FIELD_EXECUTE_AT]->set_null();
|
||||||
|
|
||||||
|
if (!et->starts_null)
|
||||||
|
{
|
||||||
|
table->field[EVEX_FIELD_STARTS]->set_notnull();// set NULL flag to OFF
|
||||||
|
table->field[EVEX_FIELD_STARTS]->
|
||||||
|
store_time(&et->starts, MYSQL_TIMESTAMP_DATETIME);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!et->ends_null)
|
||||||
|
{
|
||||||
|
table->field[EVEX_FIELD_ENDS]->set_notnull();
|
||||||
|
table->field[EVEX_FIELD_ENDS]->
|
||||||
|
store_time(&et->ends, MYSQL_TIMESTAMP_DATETIME);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (et->execute_at.year)
|
else if (et->execute_at.year)
|
||||||
{
|
{
|
||||||
// fix_fields already called in init_execute_at
|
|
||||||
table->field[EVEX_FIELD_INTERVAL_EXPR]->set_null();
|
table->field[EVEX_FIELD_INTERVAL_EXPR]->set_null();
|
||||||
table->field[EVEX_FIELD_TRANSIENT_INTERVAL]->set_null();
|
table->field[EVEX_FIELD_TRANSIENT_INTERVAL]->set_null();
|
||||||
|
table->field[EVEX_FIELD_STARTS]->set_null();
|
||||||
|
table->field[EVEX_FIELD_ENDS]->set_null();
|
||||||
|
|
||||||
table->field[EVEX_FIELD_EXECUTE_AT]->set_notnull();
|
table->field[EVEX_FIELD_EXECUTE_AT]->set_notnull();
|
||||||
table->field[EVEX_FIELD_EXECUTE_AT]->store_time(&et->execute_at,
|
table->field[EVEX_FIELD_EXECUTE_AT]->store_time(&et->execute_at,
|
||||||
MYSQL_TIMESTAMP_DATETIME);
|
MYSQL_TIMESTAMP_DATETIME);
|
||||||
|
|
||||||
table->field[EVEX_FIELD_TRANSIENT_INTERVAL]->set_null();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -322,14 +321,17 @@ evex_fill_row(THD *thd, TABLE *table, event_timed *et, my_bool is_update)
|
|||||||
|
|
||||||
((Field_timestamp *)table->field[EVEX_FIELD_MODIFIED])->set_time();
|
((Field_timestamp *)table->field[EVEX_FIELD_MODIFIED])->set_time();
|
||||||
|
|
||||||
if (et->comment.length)
|
if (et->comment.str)
|
||||||
if (table->field[field_num= EVEX_FIELD_COMMENT]->
|
{
|
||||||
store(et->comment.str, et->comment.length, system_charset_info))
|
if (table->field[field_num= EVEX_FIELD_COMMENT]->store(et->comment.str,
|
||||||
|
et->comment.length,
|
||||||
|
system_charset_info))
|
||||||
goto trunc_err;
|
goto trunc_err;
|
||||||
|
}
|
||||||
|
|
||||||
DBUG_RETURN(0);
|
DBUG_RETURN(0);
|
||||||
trunc_err:
|
trunc_err:
|
||||||
my_error(ER_EVENT_DATA_TOO_LONG, MYF(0));
|
my_error(ER_EVENT_DATA_TOO_LONG, MYF(0), table->field[field_num]->field_name);
|
||||||
DBUG_RETURN(EVEX_GENERAL_ERROR);
|
DBUG_RETURN(EVEX_GENERAL_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -442,14 +444,16 @@ db_create_event(THD *thd, event_timed *et, my_bool create_if_not,
|
|||||||
my_error(ER_EVENT_STORE_FAILED, MYF(0), et->name.str, ret);
|
my_error(ER_EVENT_STORE_FAILED, MYF(0), et->name.str, ret);
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef USE_THIS_CODE_AS_TEMPLATE_WHEN_EVENT_REPLICATION_IS_AGREED
|
||||||
if (mysql_bin_log.is_open())
|
if (mysql_bin_log.is_open())
|
||||||
{
|
{
|
||||||
thd->clear_error();
|
thd->clear_error();
|
||||||
/* Such a statement can always go directly to binlog, no trans cache */
|
// Such a statement can always go directly to binlog, no trans cache
|
||||||
thd->binlog_query(THD::MYSQL_QUERY_TYPE,
|
thd->binlog_query(THD::MYSQL_QUERY_TYPE,
|
||||||
thd->query, thd->query_length, FALSE, FALSE);
|
thd->query, thd->query_length, FALSE, FALSE);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
*rows_affected= 1;
|
*rows_affected= 1;
|
||||||
ok:
|
ok:
|
||||||
|
@ -100,6 +100,9 @@ public:
|
|||||||
TIME starts;
|
TIME starts;
|
||||||
TIME ends;
|
TIME ends;
|
||||||
TIME execute_at;
|
TIME execute_at;
|
||||||
|
my_bool starts_null;
|
||||||
|
my_bool ends_null;
|
||||||
|
my_bool execute_at_null;
|
||||||
|
|
||||||
longlong expression;
|
longlong expression;
|
||||||
interval_type interval;
|
interval_type interval;
|
||||||
|
@ -203,7 +203,7 @@ event_executor_main(void *arg)
|
|||||||
|
|
||||||
if (init_event_thread(thd))
|
if (init_event_thread(thd))
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
// make this thread invisible it has no vio -> show processlist won't see
|
// make this thread invisible it has no vio -> show processlist won't see
|
||||||
thd->system_thread= 1;
|
thd->system_thread= 1;
|
||||||
|
|
||||||
@ -321,8 +321,7 @@ event_executor_main(void *arg)
|
|||||||
et= evex_queue_first_element(&EVEX_EQ_NAME, event_timed*);
|
et= evex_queue_first_element(&EVEX_EQ_NAME, event_timed*);
|
||||||
DBUG_PRINT("evex main thread",("got event from the queue"));
|
DBUG_PRINT("evex main thread",("got event from the queue"));
|
||||||
|
|
||||||
if (et->execute_at.year > 1969 &&
|
if (!et->execute_at_null && my_time_compare(&time_now,&et->execute_at) == -1)
|
||||||
my_time_compare(&time_now, &et->execute_at) == -1)
|
|
||||||
{
|
{
|
||||||
DBUG_PRINT("evex main thread",("still not the time for execution"));
|
DBUG_PRINT("evex main thread",("still not the time for execution"));
|
||||||
VOID(pthread_mutex_unlock(&LOCK_event_arrays));
|
VOID(pthread_mutex_unlock(&LOCK_event_arrays));
|
||||||
@ -359,8 +358,11 @@ event_executor_main(void *arg)
|
|||||||
#else
|
#else
|
||||||
event_executor_worker((void *) et);
|
event_executor_worker((void *) et);
|
||||||
#endif
|
#endif
|
||||||
if ((et->execute_at.year && !et->expression) ||
|
/*
|
||||||
TIME_to_ulonglong_datetime(&et->execute_at) == 0)
|
1. For one-time event : year is > 0 and expression is 0
|
||||||
|
2. For recurring, expression is != -=> check execute_at_null in this case
|
||||||
|
*/
|
||||||
|
if ((et->execute_at.year && !et->expression) || et->execute_at_null)
|
||||||
et->flags |= EVENT_EXEC_NO_MORE;
|
et->flags |= EVENT_EXEC_NO_MORE;
|
||||||
|
|
||||||
if ((et->flags & EVENT_EXEC_NO_MORE) || et->status == MYSQL_EVENT_DISABLED)
|
if ((et->flags & EVENT_EXEC_NO_MORE) || et->status == MYSQL_EVENT_DISABLED)
|
||||||
@ -481,9 +483,9 @@ event_executor_worker(void *event_void)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// thd->security_ctx->priv_host is char[MAX_HOSTNAME]
|
// thd->security_ctx->priv_host is char[MAX_HOSTNAME]
|
||||||
|
|
||||||
strxnmov(thd->security_ctx->priv_host, sizeof(thd->security_ctx->priv_host),
|
strxnmov(thd->security_ctx->priv_host, sizeof(thd->security_ctx->priv_host),
|
||||||
event->definer_host.str, NullS);
|
event->definer_host.str, NullS);
|
||||||
|
|
||||||
thd->security_ctx->user= thd->security_ctx->priv_user=
|
thd->security_ctx->user= thd->security_ctx->priv_user=
|
||||||
my_strdup(event->definer_user.str, MYF(0));
|
my_strdup(event->definer_user.str, MYF(0));
|
||||||
@ -506,7 +508,6 @@ event_executor_worker(void *event_void)
|
|||||||
if (ret == EVEX_COMPILE_ERROR)
|
if (ret == EVEX_COMPILE_ERROR)
|
||||||
sql_print_information(" EVEX COMPILE ERROR for event %s.%s",
|
sql_print_information(" EVEX COMPILE ERROR for event %s.%s",
|
||||||
event->dbname.str, event->name.str);
|
event->dbname.str, event->name.str);
|
||||||
|
|
||||||
DBUG_PRINT("info", (" EVEX EXECUTED event %s.%s [EXPR:%d]. RetCode=%d",
|
DBUG_PRINT("info", (" EVEX EXECUTED event %s.%s [EXPR:%d]. RetCode=%d",
|
||||||
event->dbname.str, event->name.str,
|
event->dbname.str, event->name.str,
|
||||||
(int) event->expression, ret));
|
(int) event->expression, ret));
|
||||||
|
@ -41,6 +41,7 @@ event_timed::init()
|
|||||||
set_zero_time(&ends, MYSQL_TIMESTAMP_DATETIME);
|
set_zero_time(&ends, MYSQL_TIMESTAMP_DATETIME);
|
||||||
set_zero_time(&execute_at, MYSQL_TIMESTAMP_DATETIME);
|
set_zero_time(&execute_at, MYSQL_TIMESTAMP_DATETIME);
|
||||||
set_zero_time(&last_executed, MYSQL_TIMESTAMP_DATETIME);
|
set_zero_time(&last_executed, MYSQL_TIMESTAMP_DATETIME);
|
||||||
|
starts_null= ends_null= execute_at_null= TRUE;
|
||||||
|
|
||||||
definer_user.str= definer_host.str= 0;
|
definer_user.str= definer_host.str= 0;
|
||||||
definer_user.length= definer_host.length= 0;
|
definer_user.length= definer_host.length= 0;
|
||||||
@ -141,14 +142,18 @@ event_timed::init_execute_at(THD *thd, Item *expr)
|
|||||||
{
|
{
|
||||||
my_bool not_used;
|
my_bool not_used;
|
||||||
TIME ltime;
|
TIME ltime;
|
||||||
my_time_t my_time_tmp;
|
|
||||||
|
|
||||||
TIME time_tmp;
|
TIME time_tmp;
|
||||||
DBUG_ENTER("event_timed::init_execute_at");
|
DBUG_ENTER("event_timed::init_execute_at");
|
||||||
|
|
||||||
if (expr->fix_fields(thd, &expr))
|
if (expr->fix_fields(thd, &expr))
|
||||||
DBUG_RETURN(EVEX_PARSE_ERROR);
|
DBUG_RETURN(EVEX_PARSE_ERROR);
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
|
||||||
// let's check whether time is in the past
|
// let's check whether time is in the past
|
||||||
thd->variables.time_zone->gmt_sec_to_TIME(&time_tmp,
|
thd->variables.time_zone->gmt_sec_to_TIME(&time_tmp,
|
||||||
(my_time_t) thd->query_start());
|
(my_time_t) thd->query_start());
|
||||||
@ -161,14 +166,13 @@ event_timed::init_execute_at(THD *thd, Item *expr)
|
|||||||
TIME_to_ulonglong_datetime(&time_tmp))
|
TIME_to_ulonglong_datetime(&time_tmp))
|
||||||
DBUG_RETURN(EVEX_BAD_PARAMS);
|
DBUG_RETURN(EVEX_BAD_PARAMS);
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
This may result in a 1970-01-01 date if ltime is > 2037-xx-xx
|
This may result in a 1970-01-01 date if ltime is > 2037-xx-xx
|
||||||
CONVERT_TZ has similar problem
|
CONVERT_TZ has similar problem
|
||||||
*/
|
*/
|
||||||
my_tz_UTC->gmt_sec_to_TIME(<ime, TIME_to_timestamp(thd,<ime, ¬_used));
|
my_tz_UTC->gmt_sec_to_TIME(<ime, TIME_to_timestamp(thd,<ime, ¬_used));
|
||||||
|
|
||||||
|
execute_at_null= FALSE;
|
||||||
execute_at= ltime;
|
execute_at= ltime;
|
||||||
DBUG_RETURN(0);
|
DBUG_RETURN(0);
|
||||||
}
|
}
|
||||||
@ -303,7 +307,7 @@ event_timed::init_starts(THD *thd, Item *new_starts)
|
|||||||
if ((not_used= new_starts->get_date(<ime, TIME_NO_ZERO_DATE)))
|
if ((not_used= new_starts->get_date(<ime, TIME_NO_ZERO_DATE)))
|
||||||
DBUG_RETURN(EVEX_BAD_PARAMS);
|
DBUG_RETURN(EVEX_BAD_PARAMS);
|
||||||
|
|
||||||
// let's check whether time is in the past
|
/* let's check whether time is in the past */
|
||||||
thd->variables.time_zone->gmt_sec_to_TIME(&time_tmp,
|
thd->variables.time_zone->gmt_sec_to_TIME(&time_tmp,
|
||||||
(my_time_t) thd->query_start());
|
(my_time_t) thd->query_start());
|
||||||
|
|
||||||
@ -320,6 +324,7 @@ event_timed::init_starts(THD *thd, Item *new_starts)
|
|||||||
my_tz_UTC->gmt_sec_to_TIME(<ime, TIME_to_timestamp(thd, <ime, ¬_used));
|
my_tz_UTC->gmt_sec_to_TIME(<ime, TIME_to_timestamp(thd, <ime, ¬_used));
|
||||||
|
|
||||||
starts= ltime;
|
starts= ltime;
|
||||||
|
starts_null= FALSE;
|
||||||
DBUG_RETURN(0);
|
DBUG_RETURN(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -329,7 +334,7 @@ event_timed::init_starts(THD *thd, Item *new_starts)
|
|||||||
|
|
||||||
SYNOPSIS
|
SYNOPSIS
|
||||||
event_timed::init_ends()
|
event_timed::init_ends()
|
||||||
thd THD
|
thd THD
|
||||||
new_ends when?
|
new_ends when?
|
||||||
|
|
||||||
NOTES
|
NOTES
|
||||||
@ -342,15 +347,14 @@ event_timed::init_starts(THD *thd, Item *new_starts)
|
|||||||
|
|
||||||
RETURNS
|
RETURNS
|
||||||
0 - OK
|
0 - OK
|
||||||
EVEX_PARSE_ERROR - fix_fields failed
|
EVEX_PARSE_ERROR fix_fields failed
|
||||||
EVEX_BAD_PARAMS - ENDS before STARTS
|
EVEX_BAD_PARAMS ENDS before STARTS
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int
|
int
|
||||||
event_timed::init_ends(THD *thd, Item *new_ends)
|
event_timed::init_ends(THD *thd, Item *new_ends)
|
||||||
{
|
{
|
||||||
TIME ltime;
|
TIME ltime, ltime_now;
|
||||||
my_time_t my_time_tmp;
|
|
||||||
my_bool not_used;
|
my_bool not_used;
|
||||||
|
|
||||||
DBUG_ENTER("event_timed::init_ends");
|
DBUG_ENTER("event_timed::init_ends");
|
||||||
@ -358,20 +362,34 @@ event_timed::init_ends(THD *thd, Item *new_ends)
|
|||||||
if (new_ends->fix_fields(thd, &new_ends))
|
if (new_ends->fix_fields(thd, &new_ends))
|
||||||
DBUG_RETURN(EVEX_PARSE_ERROR);
|
DBUG_RETURN(EVEX_PARSE_ERROR);
|
||||||
|
|
||||||
// the field was already fixed in init_ends
|
DBUG_PRINT("info", ("convert to TIME"));
|
||||||
if ((not_used= new_ends->get_date(<ime, TIME_NO_ZERO_DATE)))
|
if ((not_used= new_ends->get_date(<ime, TIME_NO_ZERO_DATE)))
|
||||||
DBUG_RETURN(EVEX_BAD_PARAMS);
|
DBUG_RETURN(EVEX_BAD_PARAMS);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
This may result in a 1970-01-01 date if ltime is > 2037-xx-xx
|
This may result in a 1970-01-01 date if ltime is > 2037-xx-xx ?
|
||||||
CONVERT_TZ has similar problem
|
CONVERT_TZ has similar problem ?
|
||||||
*/
|
*/
|
||||||
|
DBUG_PRINT("info", ("get the UTC time"));
|
||||||
my_tz_UTC->gmt_sec_to_TIME(<ime, TIME_to_timestamp(thd, <ime, ¬_used));
|
my_tz_UTC->gmt_sec_to_TIME(<ime, TIME_to_timestamp(thd, <ime, ¬_used));
|
||||||
|
|
||||||
if (starts.year && my_time_compare(&starts, <ime) != -1)
|
/* Check whether ends is after starts */
|
||||||
|
DBUG_PRINT("info", ("ENDS after STARTS?"));
|
||||||
|
if (!starts_null && my_time_compare(&starts, <ime) != -1)
|
||||||
|
DBUG_RETURN(EVEX_BAD_PARAMS);
|
||||||
|
|
||||||
|
/*
|
||||||
|
The parser forces starts to be provided but one day STARTS could be
|
||||||
|
set before NOW() and in this case the following check should be done.
|
||||||
|
Check whether ENDS is not in the past.
|
||||||
|
*/
|
||||||
|
DBUG_PRINT("info", ("ENDS after NOW?"));
|
||||||
|
my_tz_UTC->gmt_sec_to_TIME(<ime_now, thd->query_start());
|
||||||
|
if (my_time_compare(<ime_now, <ime) == 1)
|
||||||
DBUG_RETURN(EVEX_BAD_PARAMS);
|
DBUG_RETURN(EVEX_BAD_PARAMS);
|
||||||
|
|
||||||
ends= ltime;
|
ends= ltime;
|
||||||
|
ends_null= FALSE;
|
||||||
DBUG_RETURN(0);
|
DBUG_RETURN(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -391,7 +409,7 @@ event_timed::init_comment(THD *thd, LEX_STRING *set_comment)
|
|||||||
DBUG_ENTER("event_timed::init_comment");
|
DBUG_ENTER("event_timed::init_comment");
|
||||||
|
|
||||||
comment.str= strmake_root(thd->mem_root, set_comment->str,
|
comment.str= strmake_root(thd->mem_root, set_comment->str,
|
||||||
comment.length= set_comment->length);
|
comment.length= set_comment->length);
|
||||||
|
|
||||||
DBUG_VOID_RETURN;
|
DBUG_VOID_RETURN;
|
||||||
}
|
}
|
||||||
@ -498,28 +516,37 @@ event_timed::load_from_row(MEM_ROOT *mem_root, TABLE *table)
|
|||||||
et->definer_host.str= strmake_root(mem_root, ptr + 1, len);//1: because of @
|
et->definer_host.str= strmake_root(mem_root, ptr + 1, len);//1: because of @
|
||||||
et->definer_host.length= len;
|
et->definer_host.length= len;
|
||||||
|
|
||||||
res1= table->field[EVEX_FIELD_STARTS]->
|
et->starts_null= table->field[EVEX_FIELD_STARTS]->is_null();
|
||||||
get_date(&et->starts, TIME_NO_ZERO_DATE);
|
res1= table->field[EVEX_FIELD_STARTS]->get_date(&et->starts,TIME_NO_ZERO_DATE);
|
||||||
|
|
||||||
res2= table->field[EVEX_FIELD_ENDS]->
|
et->ends_null= table->field[EVEX_FIELD_ENDS]->is_null();
|
||||||
get_date(&et->ends, TIME_NO_ZERO_DATE);
|
res2= table->field[EVEX_FIELD_ENDS]->get_date(&et->ends, TIME_NO_ZERO_DATE);
|
||||||
|
|
||||||
et->expression= table->field[EVEX_FIELD_INTERVAL_EXPR]->val_int();
|
if (!table->field[EVEX_FIELD_INTERVAL_EXPR]->is_null())
|
||||||
|
et->expression= table->field[EVEX_FIELD_INTERVAL_EXPR]->val_int();
|
||||||
|
else
|
||||||
|
et->expression= 0;
|
||||||
/*
|
/*
|
||||||
If res1 and res2 are true then both fields are empty.
|
If res1 and res2 are true then both fields are empty.
|
||||||
Hence if EVEX_FIELD_EXECUTE_AT is empty there is an error.
|
Hence if EVEX_FIELD_EXECUTE_AT is empty there is an error.
|
||||||
*/
|
*/
|
||||||
if (res1 && res2 && !et->expression && table->field[EVEX_FIELD_EXECUTE_AT]->
|
et->execute_at_null= table->field[EVEX_FIELD_EXECUTE_AT]->is_null();
|
||||||
get_date(&et->execute_at, TIME_NO_ZERO_DATE))
|
DBUG_ASSERT(!(et->starts_null && et->ends_null && !et->expression &&
|
||||||
|
et->execute_at_null));
|
||||||
|
if (!et->expression &&
|
||||||
|
table->field[EVEX_FIELD_EXECUTE_AT]->get_date(&et->execute_at,
|
||||||
|
TIME_NO_ZERO_DATE))
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
In DB the values start from 1 but enum interval_type starts
|
In DB the values start from 1 but enum interval_type starts
|
||||||
from 0
|
from 0
|
||||||
*/
|
*/
|
||||||
et->interval= (interval_type)
|
if (!table->field[EVEX_FIELD_TRANSIENT_INTERVAL]->is_null())
|
||||||
|
et->interval= (interval_type)
|
||||||
((ulonglong) table->field[EVEX_FIELD_TRANSIENT_INTERVAL]->val_int() - 1);
|
((ulonglong) table->field[EVEX_FIELD_TRANSIENT_INTERVAL]->val_int() - 1);
|
||||||
|
else
|
||||||
|
et->interval= (interval_type) 0;
|
||||||
|
|
||||||
et->modified= table->field[EVEX_FIELD_CREATED]->val_int();
|
et->modified= table->field[EVEX_FIELD_CREATED]->val_int();
|
||||||
et->created= table->field[EVEX_FIELD_MODIFIED]->val_int();
|
et->created= table->field[EVEX_FIELD_MODIFIED]->val_int();
|
||||||
@ -676,14 +703,11 @@ event_timed::compute_next_execution_time()
|
|||||||
//let's check whether it was executed
|
//let's check whether it was executed
|
||||||
if (last_executed.year)
|
if (last_executed.year)
|
||||||
{
|
{
|
||||||
DBUG_PRINT("compute_next_execution_time",
|
DBUG_PRINT("info",("One-time event %s.%s of was already executed",
|
||||||
("One-time event %s was already executed", name.str));
|
dbname.str, name.str, definer.str));
|
||||||
if (on_completion == MYSQL_EVENT_ON_COMPLETION_DROP)
|
dropped= (on_completion == MYSQL_EVENT_ON_COMPLETION_DROP);
|
||||||
{
|
DBUG_PRINT("info",("One-time event will be dropped=%d.", dropped));
|
||||||
DBUG_PRINT("compute_next_execution_time",
|
|
||||||
("One-time event will be dropped."));
|
|
||||||
dropped= true;
|
|
||||||
}
|
|
||||||
status= MYSQL_EVENT_DISABLED;
|
status= MYSQL_EVENT_DISABLED;
|
||||||
status_changed= true;
|
status_changed= true;
|
||||||
}
|
}
|
||||||
@ -710,10 +734,11 @@ event_timed::compute_next_execution_time()
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
//if time_now is after ends don't execute anymore
|
//if time_now is after ends don't execute anymore
|
||||||
if (ends.year && (tmp= my_time_compare(&ends, &time_now)) == -1)
|
if (!ends_null && (tmp= my_time_compare(&ends, &time_now)) == -1)
|
||||||
{
|
{
|
||||||
// time_now is after ends. don't execute anymore
|
// time_now is after ends. don't execute anymore
|
||||||
set_zero_time(&execute_at, MYSQL_TIMESTAMP_DATETIME);
|
set_zero_time(&execute_at, MYSQL_TIMESTAMP_DATETIME);
|
||||||
|
execute_at_null= TRUE;
|
||||||
if (on_completion == MYSQL_EVENT_ON_COMPLETION_DROP)
|
if (on_completion == MYSQL_EVENT_ON_COMPLETION_DROP)
|
||||||
dropped= true;
|
dropped= true;
|
||||||
status= MYSQL_EVENT_DISABLED;
|
status= MYSQL_EVENT_DISABLED;
|
||||||
@ -727,7 +752,7 @@ event_timed::compute_next_execution_time()
|
|||||||
Let's check whether time_now is before starts.
|
Let's check whether time_now is before starts.
|
||||||
If so schedule for starts
|
If so schedule for starts
|
||||||
*/
|
*/
|
||||||
if (starts.year && (tmp= my_time_compare(&time_now, &starts)) < 1)
|
if (!starts_null && (tmp= my_time_compare(&time_now, &starts)) < 1)
|
||||||
{
|
{
|
||||||
if (tmp == 0 && my_time_compare(&starts, &last_executed) == 0)
|
if (tmp == 0 && my_time_compare(&starts, &last_executed) == 0)
|
||||||
{
|
{
|
||||||
@ -743,11 +768,12 @@ event_timed::compute_next_execution_time()
|
|||||||
time_now before starts. Scheduling for starts
|
time_now before starts. Scheduling for starts
|
||||||
*/
|
*/
|
||||||
execute_at= starts;
|
execute_at= starts;
|
||||||
|
execute_at_null= FALSE;
|
||||||
goto ret;
|
goto ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (starts.year && ends.year)
|
if (!starts_null && !ends_null)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
Both starts and m_ends are set and time_now is between them (incl.)
|
Both starts and m_ends are set and time_now is between them (incl.)
|
||||||
@ -756,7 +782,10 @@ event_timed::compute_next_execution_time()
|
|||||||
If not set then schedule for now.
|
If not set then schedule for now.
|
||||||
*/
|
*/
|
||||||
if (!last_executed.year)
|
if (!last_executed.year)
|
||||||
|
{
|
||||||
execute_at= time_now;
|
execute_at= time_now;
|
||||||
|
execute_at_null= FALSE;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
TIME next_exec;
|
TIME next_exec;
|
||||||
@ -769,15 +798,19 @@ event_timed::compute_next_execution_time()
|
|||||||
{
|
{
|
||||||
// Next execution after ends. No more executions
|
// Next execution after ends. No more executions
|
||||||
set_zero_time(&execute_at, MYSQL_TIMESTAMP_DATETIME);
|
set_zero_time(&execute_at, MYSQL_TIMESTAMP_DATETIME);
|
||||||
|
execute_at_null= TRUE;
|
||||||
if (on_completion == MYSQL_EVENT_ON_COMPLETION_DROP)
|
if (on_completion == MYSQL_EVENT_ON_COMPLETION_DROP)
|
||||||
dropped= true;
|
dropped= true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
execute_at= next_exec;
|
execute_at= next_exec;
|
||||||
|
execute_at_null= FALSE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
goto ret;
|
goto ret;
|
||||||
}
|
}
|
||||||
else if (!starts.year && !ends.year)
|
else if (starts_null && ends_null)
|
||||||
{
|
{
|
||||||
// both starts and m_ends are not set, se we schedule for the next
|
// both starts and m_ends are not set, se we schedule for the next
|
||||||
// based on last_executed
|
// based on last_executed
|
||||||
@ -789,11 +822,12 @@ event_timed::compute_next_execution_time()
|
|||||||
else
|
else
|
||||||
//last_executed not set. Schedule the event for now
|
//last_executed not set. Schedule the event for now
|
||||||
execute_at= time_now;
|
execute_at= time_now;
|
||||||
|
execute_at_null= FALSE;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//either starts or m_ends is set
|
//either starts or m_ends is set
|
||||||
if (starts.year)
|
if (!starts_null)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
- starts is set.
|
- starts is set.
|
||||||
@ -808,6 +842,7 @@ event_timed::compute_next_execution_time()
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
execute_at= starts;
|
execute_at= starts;
|
||||||
|
execute_at_null= FALSE;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -830,11 +865,15 @@ event_timed::compute_next_execution_time()
|
|||||||
if (my_time_compare(&ends, &next_exec) == -1)
|
if (my_time_compare(&ends, &next_exec) == -1)
|
||||||
{
|
{
|
||||||
set_zero_time(&execute_at, MYSQL_TIMESTAMP_DATETIME);
|
set_zero_time(&execute_at, MYSQL_TIMESTAMP_DATETIME);
|
||||||
|
execute_at_null= TRUE;
|
||||||
if (on_completion == MYSQL_EVENT_ON_COMPLETION_DROP)
|
if (on_completion == MYSQL_EVENT_ON_COMPLETION_DROP)
|
||||||
dropped= true;
|
dropped= true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
execute_at= next_exec;
|
execute_at= next_exec;
|
||||||
|
execute_at_null= FALSE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
goto ret;
|
goto ret;
|
||||||
@ -1025,7 +1064,7 @@ event_timed::execute(THD *thd, MEM_ROOT *mem_root)
|
|||||||
|
|
||||||
if (!sphead && (ret= compile(thd, mem_root)))
|
if (!sphead && (ret= compile(thd, mem_root)))
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
ret= sphead->execute_procedure(thd, &empty_item_list);
|
ret= sphead->execute_procedure(thd, &empty_item_list);
|
||||||
|
|
||||||
VOID(pthread_mutex_lock(&this->LOCK_running));
|
VOID(pthread_mutex_lock(&this->LOCK_running));
|
||||||
|
@ -3883,35 +3883,32 @@ fill_events_copy_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table)
|
|||||||
{
|
{
|
||||||
//type
|
//type
|
||||||
sch_table->field[5]->store(STRING_WITH_LEN("RECURRING"), scs);
|
sch_table->field[5]->store(STRING_WITH_LEN("RECURRING"), scs);
|
||||||
//execute_at
|
|
||||||
sch_table->field[6]->set_null();
|
|
||||||
//interval_value
|
//interval_value
|
||||||
sch_table->field[7]->set_notnull();
|
sch_table->field[7]->set_notnull();
|
||||||
sch_table->field[7]->store((longlong) et.expression);
|
sch_table->field[7]->store((longlong) et.expression);
|
||||||
|
|
||||||
//interval_type
|
//interval_type
|
||||||
LEX_STRING *ival=&interval_type_to_name[get_real_interval_type(et.interval)];
|
LEX_STRING *ival=&interval_type_to_name[get_real_interval_type(et.interval)];
|
||||||
sch_table->field[8]->set_notnull();
|
sch_table->field[8]->set_notnull();
|
||||||
sch_table->field[8]->store(ival->str, ival->length, scs);
|
sch_table->field[8]->store(ival->str, ival->length, scs);
|
||||||
//starts & ends
|
|
||||||
|
//starts & ends
|
||||||
sch_table->field[10]->set_notnull();
|
sch_table->field[10]->set_notnull();
|
||||||
sch_table->field[10]->store_time(&et.starts, MYSQL_TIMESTAMP_DATETIME);
|
sch_table->field[10]->store_time(&et.starts, MYSQL_TIMESTAMP_DATETIME);
|
||||||
sch_table->field[11]->set_notnull();
|
|
||||||
sch_table->field[11]->store_time(&et.ends, MYSQL_TIMESTAMP_DATETIME);
|
if (!et.ends_null)
|
||||||
|
{
|
||||||
|
sch_table->field[11]->set_notnull();
|
||||||
|
sch_table->field[11]->store_time(&et.ends, MYSQL_TIMESTAMP_DATETIME);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//type
|
//type
|
||||||
sch_table->field[5]->store(STRING_WITH_LEN("ONE TIME"), scs);
|
sch_table->field[5]->store(STRING_WITH_LEN("ONE TIME"), scs);
|
||||||
//execute_at
|
|
||||||
sch_table->field[6]->set_notnull();
|
sch_table->field[6]->set_notnull();
|
||||||
sch_table->field[6]->store_time(&et.execute_at, MYSQL_TIMESTAMP_DATETIME);
|
sch_table->field[6]->store_time(&et.execute_at, MYSQL_TIMESTAMP_DATETIME);
|
||||||
//interval
|
|
||||||
sch_table->field[7]->set_null();
|
|
||||||
//interval_type
|
|
||||||
sch_table->field[8]->set_null();
|
|
||||||
//starts & ends
|
|
||||||
sch_table->field[10]->set_null();
|
|
||||||
sch_table->field[11]->set_null();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//status
|
//status
|
||||||
|
@ -1475,6 +1475,9 @@ opt_ev_status: /* empty */ {$<ulong_num>$= 0;}
|
|||||||
;
|
;
|
||||||
|
|
||||||
ev_starts: /* empty */
|
ev_starts: /* empty */
|
||||||
|
{
|
||||||
|
Lex->et->init_starts(YYTHD, new Item_func_now_local());
|
||||||
|
}
|
||||||
| STARTS_SYM expr
|
| STARTS_SYM expr
|
||||||
{
|
{
|
||||||
LEX *lex= Lex;
|
LEX *lex= Lex;
|
||||||
|
Reference in New Issue
Block a user