Commit 317c6851 authored by unknown's avatar unknown
Browse files

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
parent b9d41f5d
Loading
Loading
Loading
Loading
+49 −1
Original line number Diff line number Diff line
@@ -13,6 +13,54 @@ alter event event3 rename to 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;
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;
set global event_scheduler = 1;
select sleep(2);
@@ -64,7 +112,7 @@ SHOW GRANTS;
Grants for ev_test@localhost
GRANT USAGE ON *.* 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:";
SHOW EVENTS;
ERROR 42000: Access denied for user 'ev_test'@'localhost' to database 'events_test2'
+32 −0
Original line number Diff line number Diff line
@@ -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;
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;
set global event_scheduler = 1;
select sleep(2);
+28 −24
Original line number Diff line number Diff line
@@ -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))
      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)
  {
    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_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)
  {
    // fix_fields already called in init_execute_at
    table->field[EVEX_FIELD_INTERVAL_EXPR]->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]->store_time(&et->execute_at,
                                                    MYSQL_TIMESTAMP_DATETIME);    
    
    table->field[EVEX_FIELD_TRANSIENT_INTERVAL]->set_null();  
  }
  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();

  if (et->comment.length)
    if (table->field[field_num= EVEX_FIELD_COMMENT]->
	store(et->comment.str, et->comment.length, system_charset_info))
  if (et->comment.str)
  {
    if (table->field[field_num= EVEX_FIELD_COMMENT]->store(et->comment.str,
                                                           et->comment.length,
                                                           system_charset_info))
      goto trunc_err;
  }

  DBUG_RETURN(0);
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);
}

@@ -443,13 +445,15 @@ db_create_event(THD *thd, event_timed *et, my_bool create_if_not,
    goto err;
  }

#ifdef USE_THIS_CODE_AS_TEMPLATE_WHEN_EVENT_REPLICATION_IS_AGREED   
  if (mysql_bin_log.is_open())
  {
    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->query, thd->query_length, FALSE, FALSE);
  }
#endif
  
  *rows_affected= 1;
ok:
+3 −0
Original line number Diff line number Diff line
@@ -100,6 +100,9 @@ class event_timed
  TIME starts;
  TIME ends;
  TIME execute_at;
  my_bool starts_null;
  my_bool ends_null;
  my_bool execute_at_null;

  longlong expression;
  interval_type interval;
+9 −8
Original line number Diff line number Diff line
@@ -321,8 +321,7 @@ event_executor_main(void *arg)
    et= evex_queue_first_element(&EVEX_EQ_NAME, event_timed*);
    DBUG_PRINT("evex main thread",("got event from the queue"));
      
    if (et->execute_at.year > 1969 &&
        my_time_compare(&time_now, &et->execute_at) == -1)
    if (!et->execute_at_null && my_time_compare(&time_now,&et->execute_at) == -1)
    {
      DBUG_PRINT("evex main thread",("still not the time for execution"));
      VOID(pthread_mutex_unlock(&LOCK_event_arrays));
@@ -359,8 +358,11 @@ event_executor_main(void *arg)
#else
      event_executor_worker((void *) et);
#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;

      if ((et->flags & EVENT_EXEC_NO_MORE) || et->status == MYSQL_EVENT_DISABLED)
@@ -506,7 +508,6 @@ event_executor_worker(void *event_void)
    if (ret == EVEX_COMPILE_ERROR)
      sql_print_information("    EVEX COMPILE ERROR for event %s.%s",
                             event->dbname.str, event->name.str);
    
    DBUG_PRINT("info", ("    EVEX EXECUTED event %s.%s  [EXPR:%d]. RetCode=%d",
                        event->dbname.str, event->name.str,
                        (int) event->expression, ret));
Loading