Loading mysql-test/r/events.result +48 −0 Original line number Diff line number Diff line Loading @@ -37,6 +37,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 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 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 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 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; alter event e_43 do alter event e_43 do set @a = 4; Loading mysql-test/t/events.test +32 −0 Original line number Diff line number Diff line Loading @@ -48,6 +48,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; --sleep 2 Loading sql/event.cc +122 −120 Original line number Diff line number Diff line Loading @@ -120,6 +120,7 @@ static TABLE_FIELD_W_TYPE event_table_fields[EVEX_FIELD_COUNT] = { { {(char *) STRING_WITH_LEN("last_executed")}, {(char *) STRING_WITH_LEN("datetime")}, {NULL, 0} }, { {(char *) STRING_WITH_LEN("starts")}, Loading Loading @@ -343,8 +344,6 @@ event_timed_compare_q(void *vptr, byte* a, byte *b) RETURNS 0 - OK 1 - Error */ int Loading Loading @@ -619,7 +618,7 @@ evex_fill_row(THD *thd, TABLE *table, event_timed *et, my_bool is_update) store(et->name.str, et->name.length, system_charset_info)) goto trunc_err; // both ON_COMPLETION and STATUS are NOT NULL thus not calling set_notnull() /* both ON_COMPLETION and STATUS are NOT NULL thus not calling set_notnull() */ table->field[EVEX_FIELD_ON_COMPLETION]->store((longlong)et->on_completion); table->field[EVEX_FIELD_STATUS]->store((longlong)et->status); Loading @@ -637,20 +636,6 @@ evex_fill_row(THD *thd, TABLE *table, event_timed *et, my_bool is_update) 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(); Loading @@ -664,36 +649,54 @@ 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(); 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 { DBUG_ASSERT(is_update); // it is normal to be here when the action is update // this is an error if the action is create. something is borked /* it is normal to be here when the action is update this is an error if the action is create. something is borked */ } ((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); } Loading Loading @@ -797,7 +800,10 @@ db_create_event(THD *thd, event_timed *et, my_bool create_if_not, ((Field_timestamp *)table->field[EVEX_FIELD_CREATED])->set_time(); // evex_fill_row() calls my_error() in case of error so no need to handle it here /* evex_fill_row() calls my_error() in case of error so no need to handle it here */ if ((ret= evex_fill_row(thd, table, et, false))) goto err; Loading @@ -807,6 +813,7 @@ 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(); Loading @@ -814,6 +821,7 @@ db_create_event(THD *thd, event_timed *et, my_bool create_if_not, thd->binlog_query(THD::MYSQL_QUERY_TYPE, thd->query, thd->query_length, FALSE, FALSE); } #endif *rows_affected= 1; ok: Loading Loading @@ -865,7 +873,7 @@ db_update_event(THD *thd, event_timed *et, sp_name *new_name) goto err; } // first look whether we overwrite /* first look whether we overwrite */ if (new_name) { if (!sortcmp_lex_string(et->name, new_name->m_name, system_charset_info) && Loading Loading @@ -894,13 +902,12 @@ db_update_event(THD *thd, event_timed *et, sp_name *new_name) goto err; } store_record(table,record[1]); // Don't update create on row update. /* Don't update create on row update. */ table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; // evex_fill_row() calls my_error() in case of error so no need to handle it here /* evex_fill_row() calls my_error() in case of error so no need to handle it here */ if ((ret= evex_fill_row(thd, table, et, true))) goto err; Loading @@ -918,7 +925,7 @@ db_update_event(THD *thd, event_timed *et, sp_name *new_name) goto err; } // close mysql.event or we crash later when loading the event from disk /* close mysql.event or we crash later when loading the event from disk */ close_thread_tables(thd); DBUG_RETURN(0); Loading Loading @@ -995,7 +1002,7 @@ db_find_event(THD *thd, sp_name *name, LEX_STRING *definer, event_timed **ett, delete et; et= 0; } // don't close the table if we haven't opened it ourselves /* don't close the table if we haven't opened it ourselves */ if (!tbl && table) close_thread_tables(thd); *ett= et; Loading @@ -1017,7 +1024,6 @@ db_find_event(THD *thd, sp_name *name, LEX_STRING *definer, event_timed **ett, RETURN VALUE 0 - OK < 0 - error (in this case underlying functions call my_error()). */ static int Loading @@ -1036,7 +1042,7 @@ evex_load_and_compile_event(THD * thd, sp_name *spn, LEX_STRING definer, thd->mem_root= &evex_mem_root; thd->reset_n_backup_open_tables_state(&backup); // no need to use my_error() here because db_find_event() has done it /* no need to use my_error() here because db_find_event() has done it */ ret= db_find_event(thd, spn, &definer, &ett, NULL, NULL); thd->restore_backup_open_tables_state(&backup); if (ret) Loading Loading @@ -1088,7 +1094,7 @@ evex_load_and_compile_event(THD * thd, sp_name *spn, LEX_STRING definer, ALTER EVENT. RETURNS 0 - OK (always) 0 OK (always) */ static int Loading Loading @@ -1131,7 +1137,7 @@ evex_remove_from_cache(LEX_STRING *db, LEX_STRING *name, bool use_lock, } DBUG_PRINT("evex_remove_from_cache", ("delete from queue")); evex_queue_delete_element(&EVEX_EQ_NAME, i); // ok, we have cleaned /* ok, we have cleaned */ ret= 0; goto done; } Loading Loading @@ -1185,7 +1191,7 @@ evex_create_event(THD *thd, event_timed *et, uint create_options, VOID(pthread_mutex_unlock(&LOCK_evex_running)); done: // No need to close the table, it will be closed in sql_parse::do_command /* No need to close the table, it will be closed in sql_parse::do_command */ DBUG_RETURN(ret); } Loading Loading @@ -1259,7 +1265,6 @@ evex_update_event(THD *thd, event_timed *et, sp_name *new_name, et event's name drop_if_exists if set and the event not existing => warning onto the stack rows_affected affected number of rows is returned heres */ int db_drop_event(THD *thd, event_timed *et, bool drop_if_exists, Loading Loading @@ -1362,7 +1367,6 @@ evex_drop_event(THD *thd, event_timed *et, bool drop_if_exists, RETURNS 0 - OK 1 - Error during writing to the wire */ int Loading Loading @@ -1413,7 +1417,6 @@ evex_show_create_event(THD *thd, sp_name *spn, LEX_STRING definer) protocol->store((char*) sql_mode_str, sql_mode_len, system_charset_info); protocol->store(show_str.c_ptr(), show_str.length(), system_charset_info); ret= protocol->write(); send_eof(thd); Loading Loading @@ -1449,7 +1452,6 @@ evex_show_create_event(THD *thd, sp_name *spn, LEX_STRING definer) spawned and can_spawn() is the right method. - event_timed::can_spawn() returns false -> being runned ATM just set the flags so it should drop itself. */ int Loading Loading @@ -1521,7 +1523,7 @@ evex_drop_db_events(THD *thd, char *db) } DBUG_PRINT("info",("%d elements in the queue", evex_queue_num_elements(EVEX_EQ_NAME))); evex_queue_delete_element(&EVEX_EQ_NAME, i);// 1 is top evex_queue_delete_element(&EVEX_EQ_NAME, i);// 0 is top DBUG_PRINT("info",("%d elements in the queue", evex_queue_num_elements(EVEX_EQ_NAME))); /* Loading Loading @@ -1598,7 +1600,7 @@ evex_drop_db_events(THD *thd, char *db) VOID(pthread_mutex_unlock(&LOCK_event_arrays)); end_read_record(&read_record_info); thd->version--; // Force close to free memory thd->version--; /* Force close to free memory */ close_thread_tables(thd); Loading sql/event.h +3 −0 Original line number Diff line number Diff line Loading @@ -103,6 +103,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; Loading sql/event_executor.cc +66 −63 Original line number Diff line number Diff line Loading @@ -135,7 +135,7 @@ evex_check_system_tables() bool not_used; Open_tables_state backup; // thd is 0x0 during boot of the server. Later it's !=0x0 /* thd is 0x0 during boot of the server. Later it's !=0x0 */ if (!thd) return; Loading Loading @@ -206,7 +206,7 @@ init_events() if (event_executor_running_global_var) { #ifndef DBUG_FAULTY_THR //TODO Andrey: Change the error code returned! /* TODO Andrey: Change the error code returned! */ if (pthread_create(&th, &connection_attrib, event_executor_main,(void*)NULL)) DBUG_RETURN(ER_SLAVE_THREAD); #else Loading Loading @@ -339,14 +339,14 @@ executor_wait_till_next_event_exec(THD *thd) if (et->dropped) et->drop(thd); delete et; evex_queue_delete_element(&EVEX_EQ_NAME, 1);// 1 is top evex_queue_delete_element(&EVEX_EQ_NAME, 0);// 0 is top, internally 1 VOID(pthread_mutex_unlock(&LOCK_event_arrays)); sql_print_information("Event found disabled, dropping."); DBUG_RETURN(1); } DBUG_PRINT("evex main thread",("computing time to sleep till next exec")); // set the internal clock of thd /* set the internal clock of thd */ thd->end_time(); my_tz_UTC->gmt_sec_to_TIME(&time_now, thd->query_start()); t2sleep= evex_time_diff(&et->execute_at, &time_now); Loading Loading @@ -387,7 +387,7 @@ executor_wait_till_next_event_exec(THD *thd) SYNOPSIS event_executor_main() arg - unused arg unused NOTES 1. The host of the thead is my_localhost Loading @@ -407,11 +407,10 @@ event_executor_main(void *arg) DBUG_PRINT("event_executor_main", ("EVEX thread started")); // init memory root /* init memory root */ init_alloc_root(&evex_mem_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC); // needs to call my_thread_init(), otherwise we get a coredump in DBUG_ stuff /* needs to call my_thread_init(), otherwise we get a coredump in DBUG_ stuff*/ my_thread_init(); if (sizeof(my_time_t) != sizeof(time_t)) Loading @@ -422,8 +421,8 @@ event_executor_main(void *arg) goto err_no_thd; } //TODO Andrey: Check for NULL if (!(thd = new THD)) // note that contructor of THD uses DBUG_ ! /* note that contructor of THD uses DBUG_ ! */ if (!(thd = new THD)) { sql_print_error("SCHEDULER: Cannot create THD for the main thread."); goto err_no_thd; Loading Loading @@ -523,8 +522,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)); Loading Loading @@ -571,8 +569,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) Loading @@ -582,10 +583,10 @@ event_executor_main(void *arg) } DBUG_PRINT("evex main thread",("unlocking")); VOID(pthread_mutex_unlock(&LOCK_event_arrays)); }// while }/* while */ finish: // First manifest that this thread does not work and then destroy /* First manifest that this thread does not work and then destroy */ VOID(pthread_mutex_lock(&LOCK_evex_running)); evex_is_running= false; evex_main_thread_id= 0; Loading Loading @@ -625,7 +626,7 @@ event_executor_main(void *arg) delete et; } VOID(pthread_mutex_unlock(&LOCK_event_arrays)); // ... then we can thrash the whole queue at once /* ... then we can thrash the whole queue at once */ evex_queue_destroy(&EVEX_EQ_NAME); thd->proc_info = "Clearing"; Loading Loading @@ -665,7 +666,7 @@ event_executor_main(void *arg) SYNOPSIS event_executor_worker() arg - the event_timed object to be processed arg The event_timed object to be processed */ pthread_handler_t Loading @@ -682,7 +683,7 @@ event_executor_worker(void *event_void) #ifndef DBUG_FAULTY_THR my_thread_init(); if (!(thd = new THD)) // note that contructor of THD uses DBUG_ ! if (!(thd = new THD)) /* note that contructor of THD uses DBUG_ ! */ { sql_print_error("SCHEDULER: Cannot create a THD structure in an worker."); goto err_no_thd; Loading @@ -697,7 +698,7 @@ event_executor_worker(void *event_void) thd->init_for_queries(); // make this thread visible it has no vio -> show processlist needs this flag /* make this thread visible it has no vio -> show processlist needs this flag */ thd->system_thread= 1; VOID(pthread_mutex_lock(&LOCK_thread_count)); Loading Loading @@ -778,8 +779,8 @@ event_executor_worker(void *event_void) thd - Thread context. Used for memory allocation in some cases. RETURNS 0 - OK !0 - Error 0 OK !0 Error NOTES Reports the error to the console Loading Loading @@ -845,7 +846,7 @@ evex_load_events_from_db(THD *thd) break; } // let's find when to be executed /* let's find when to be executed */ if (et->compute_next_execution_time()) { sql_print_error("SCHEDULER: Error while computing execution time of %s.%s." Loading @@ -867,7 +868,8 @@ evex_load_events_from_db(THD *thd) VOID(pthread_mutex_unlock(&LOCK_event_arrays)); end_read_record(&read_record_info); thd->version--; // Force close to free memory /* Force close to free memory */ thd->version--; close_thread_tables(thd); if (!ret) Loading @@ -889,13 +891,13 @@ evex_load_events_from_db(THD *thd) car - the new value Returns 0 - OK (always) 0 OK (always) */ bool sys_var_event_executor::update(THD *thd, set_var *var) { // here start the thread if not running. /* here start the thread if not running. */ DBUG_ENTER("sys_var_event_executor::update"); VOID(pthread_mutex_lock(&LOCK_evex_running)); *value= var->save_result.ulong_value; Loading Loading @@ -952,7 +954,8 @@ evex_print_warnings(THD *thd, event_timed *et) while ((err= it++)) { String err_msg(msg_buf, sizeof(msg_buf), system_charset_info); err_msg.length(0);// set it to 0 or we start adding at the end /* set it to 0 or we start adding at the end. That's the trick ;) */ err_msg.length(0); if (!prefix.length()) { prefix.append("SCHEDULER: ["); Loading Loading
mysql-test/r/events.result +48 −0 Original line number Diff line number Diff line Loading @@ -37,6 +37,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 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 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 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 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; alter event e_43 do alter event e_43 do set @a = 4; Loading
mysql-test/t/events.test +32 −0 Original line number Diff line number Diff line Loading @@ -48,6 +48,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; --sleep 2 Loading
sql/event.cc +122 −120 Original line number Diff line number Diff line Loading @@ -120,6 +120,7 @@ static TABLE_FIELD_W_TYPE event_table_fields[EVEX_FIELD_COUNT] = { { {(char *) STRING_WITH_LEN("last_executed")}, {(char *) STRING_WITH_LEN("datetime")}, {NULL, 0} }, { {(char *) STRING_WITH_LEN("starts")}, Loading Loading @@ -343,8 +344,6 @@ event_timed_compare_q(void *vptr, byte* a, byte *b) RETURNS 0 - OK 1 - Error */ int Loading Loading @@ -619,7 +618,7 @@ evex_fill_row(THD *thd, TABLE *table, event_timed *et, my_bool is_update) store(et->name.str, et->name.length, system_charset_info)) goto trunc_err; // both ON_COMPLETION and STATUS are NOT NULL thus not calling set_notnull() /* both ON_COMPLETION and STATUS are NOT NULL thus not calling set_notnull() */ table->field[EVEX_FIELD_ON_COMPLETION]->store((longlong)et->on_completion); table->field[EVEX_FIELD_STATUS]->store((longlong)et->status); Loading @@ -637,20 +636,6 @@ evex_fill_row(THD *thd, TABLE *table, event_timed *et, my_bool is_update) 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(); Loading @@ -664,36 +649,54 @@ 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(); 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 { DBUG_ASSERT(is_update); // it is normal to be here when the action is update // this is an error if the action is create. something is borked /* it is normal to be here when the action is update this is an error if the action is create. something is borked */ } ((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); } Loading Loading @@ -797,7 +800,10 @@ db_create_event(THD *thd, event_timed *et, my_bool create_if_not, ((Field_timestamp *)table->field[EVEX_FIELD_CREATED])->set_time(); // evex_fill_row() calls my_error() in case of error so no need to handle it here /* evex_fill_row() calls my_error() in case of error so no need to handle it here */ if ((ret= evex_fill_row(thd, table, et, false))) goto err; Loading @@ -807,6 +813,7 @@ 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(); Loading @@ -814,6 +821,7 @@ db_create_event(THD *thd, event_timed *et, my_bool create_if_not, thd->binlog_query(THD::MYSQL_QUERY_TYPE, thd->query, thd->query_length, FALSE, FALSE); } #endif *rows_affected= 1; ok: Loading Loading @@ -865,7 +873,7 @@ db_update_event(THD *thd, event_timed *et, sp_name *new_name) goto err; } // first look whether we overwrite /* first look whether we overwrite */ if (new_name) { if (!sortcmp_lex_string(et->name, new_name->m_name, system_charset_info) && Loading Loading @@ -894,13 +902,12 @@ db_update_event(THD *thd, event_timed *et, sp_name *new_name) goto err; } store_record(table,record[1]); // Don't update create on row update. /* Don't update create on row update. */ table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; // evex_fill_row() calls my_error() in case of error so no need to handle it here /* evex_fill_row() calls my_error() in case of error so no need to handle it here */ if ((ret= evex_fill_row(thd, table, et, true))) goto err; Loading @@ -918,7 +925,7 @@ db_update_event(THD *thd, event_timed *et, sp_name *new_name) goto err; } // close mysql.event or we crash later when loading the event from disk /* close mysql.event or we crash later when loading the event from disk */ close_thread_tables(thd); DBUG_RETURN(0); Loading Loading @@ -995,7 +1002,7 @@ db_find_event(THD *thd, sp_name *name, LEX_STRING *definer, event_timed **ett, delete et; et= 0; } // don't close the table if we haven't opened it ourselves /* don't close the table if we haven't opened it ourselves */ if (!tbl && table) close_thread_tables(thd); *ett= et; Loading @@ -1017,7 +1024,6 @@ db_find_event(THD *thd, sp_name *name, LEX_STRING *definer, event_timed **ett, RETURN VALUE 0 - OK < 0 - error (in this case underlying functions call my_error()). */ static int Loading @@ -1036,7 +1042,7 @@ evex_load_and_compile_event(THD * thd, sp_name *spn, LEX_STRING definer, thd->mem_root= &evex_mem_root; thd->reset_n_backup_open_tables_state(&backup); // no need to use my_error() here because db_find_event() has done it /* no need to use my_error() here because db_find_event() has done it */ ret= db_find_event(thd, spn, &definer, &ett, NULL, NULL); thd->restore_backup_open_tables_state(&backup); if (ret) Loading Loading @@ -1088,7 +1094,7 @@ evex_load_and_compile_event(THD * thd, sp_name *spn, LEX_STRING definer, ALTER EVENT. RETURNS 0 - OK (always) 0 OK (always) */ static int Loading Loading @@ -1131,7 +1137,7 @@ evex_remove_from_cache(LEX_STRING *db, LEX_STRING *name, bool use_lock, } DBUG_PRINT("evex_remove_from_cache", ("delete from queue")); evex_queue_delete_element(&EVEX_EQ_NAME, i); // ok, we have cleaned /* ok, we have cleaned */ ret= 0; goto done; } Loading Loading @@ -1185,7 +1191,7 @@ evex_create_event(THD *thd, event_timed *et, uint create_options, VOID(pthread_mutex_unlock(&LOCK_evex_running)); done: // No need to close the table, it will be closed in sql_parse::do_command /* No need to close the table, it will be closed in sql_parse::do_command */ DBUG_RETURN(ret); } Loading Loading @@ -1259,7 +1265,6 @@ evex_update_event(THD *thd, event_timed *et, sp_name *new_name, et event's name drop_if_exists if set and the event not existing => warning onto the stack rows_affected affected number of rows is returned heres */ int db_drop_event(THD *thd, event_timed *et, bool drop_if_exists, Loading Loading @@ -1362,7 +1367,6 @@ evex_drop_event(THD *thd, event_timed *et, bool drop_if_exists, RETURNS 0 - OK 1 - Error during writing to the wire */ int Loading Loading @@ -1413,7 +1417,6 @@ evex_show_create_event(THD *thd, sp_name *spn, LEX_STRING definer) protocol->store((char*) sql_mode_str, sql_mode_len, system_charset_info); protocol->store(show_str.c_ptr(), show_str.length(), system_charset_info); ret= protocol->write(); send_eof(thd); Loading Loading @@ -1449,7 +1452,6 @@ evex_show_create_event(THD *thd, sp_name *spn, LEX_STRING definer) spawned and can_spawn() is the right method. - event_timed::can_spawn() returns false -> being runned ATM just set the flags so it should drop itself. */ int Loading Loading @@ -1521,7 +1523,7 @@ evex_drop_db_events(THD *thd, char *db) } DBUG_PRINT("info",("%d elements in the queue", evex_queue_num_elements(EVEX_EQ_NAME))); evex_queue_delete_element(&EVEX_EQ_NAME, i);// 1 is top evex_queue_delete_element(&EVEX_EQ_NAME, i);// 0 is top DBUG_PRINT("info",("%d elements in the queue", evex_queue_num_elements(EVEX_EQ_NAME))); /* Loading Loading @@ -1598,7 +1600,7 @@ evex_drop_db_events(THD *thd, char *db) VOID(pthread_mutex_unlock(&LOCK_event_arrays)); end_read_record(&read_record_info); thd->version--; // Force close to free memory thd->version--; /* Force close to free memory */ close_thread_tables(thd); Loading
sql/event.h +3 −0 Original line number Diff line number Diff line Loading @@ -103,6 +103,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; Loading
sql/event_executor.cc +66 −63 Original line number Diff line number Diff line Loading @@ -135,7 +135,7 @@ evex_check_system_tables() bool not_used; Open_tables_state backup; // thd is 0x0 during boot of the server. Later it's !=0x0 /* thd is 0x0 during boot of the server. Later it's !=0x0 */ if (!thd) return; Loading Loading @@ -206,7 +206,7 @@ init_events() if (event_executor_running_global_var) { #ifndef DBUG_FAULTY_THR //TODO Andrey: Change the error code returned! /* TODO Andrey: Change the error code returned! */ if (pthread_create(&th, &connection_attrib, event_executor_main,(void*)NULL)) DBUG_RETURN(ER_SLAVE_THREAD); #else Loading Loading @@ -339,14 +339,14 @@ executor_wait_till_next_event_exec(THD *thd) if (et->dropped) et->drop(thd); delete et; evex_queue_delete_element(&EVEX_EQ_NAME, 1);// 1 is top evex_queue_delete_element(&EVEX_EQ_NAME, 0);// 0 is top, internally 1 VOID(pthread_mutex_unlock(&LOCK_event_arrays)); sql_print_information("Event found disabled, dropping."); DBUG_RETURN(1); } DBUG_PRINT("evex main thread",("computing time to sleep till next exec")); // set the internal clock of thd /* set the internal clock of thd */ thd->end_time(); my_tz_UTC->gmt_sec_to_TIME(&time_now, thd->query_start()); t2sleep= evex_time_diff(&et->execute_at, &time_now); Loading Loading @@ -387,7 +387,7 @@ executor_wait_till_next_event_exec(THD *thd) SYNOPSIS event_executor_main() arg - unused arg unused NOTES 1. The host of the thead is my_localhost Loading @@ -407,11 +407,10 @@ event_executor_main(void *arg) DBUG_PRINT("event_executor_main", ("EVEX thread started")); // init memory root /* init memory root */ init_alloc_root(&evex_mem_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC); // needs to call my_thread_init(), otherwise we get a coredump in DBUG_ stuff /* needs to call my_thread_init(), otherwise we get a coredump in DBUG_ stuff*/ my_thread_init(); if (sizeof(my_time_t) != sizeof(time_t)) Loading @@ -422,8 +421,8 @@ event_executor_main(void *arg) goto err_no_thd; } //TODO Andrey: Check for NULL if (!(thd = new THD)) // note that contructor of THD uses DBUG_ ! /* note that contructor of THD uses DBUG_ ! */ if (!(thd = new THD)) { sql_print_error("SCHEDULER: Cannot create THD for the main thread."); goto err_no_thd; Loading Loading @@ -523,8 +522,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)); Loading Loading @@ -571,8 +569,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) Loading @@ -582,10 +583,10 @@ event_executor_main(void *arg) } DBUG_PRINT("evex main thread",("unlocking")); VOID(pthread_mutex_unlock(&LOCK_event_arrays)); }// while }/* while */ finish: // First manifest that this thread does not work and then destroy /* First manifest that this thread does not work and then destroy */ VOID(pthread_mutex_lock(&LOCK_evex_running)); evex_is_running= false; evex_main_thread_id= 0; Loading Loading @@ -625,7 +626,7 @@ event_executor_main(void *arg) delete et; } VOID(pthread_mutex_unlock(&LOCK_event_arrays)); // ... then we can thrash the whole queue at once /* ... then we can thrash the whole queue at once */ evex_queue_destroy(&EVEX_EQ_NAME); thd->proc_info = "Clearing"; Loading Loading @@ -665,7 +666,7 @@ event_executor_main(void *arg) SYNOPSIS event_executor_worker() arg - the event_timed object to be processed arg The event_timed object to be processed */ pthread_handler_t Loading @@ -682,7 +683,7 @@ event_executor_worker(void *event_void) #ifndef DBUG_FAULTY_THR my_thread_init(); if (!(thd = new THD)) // note that contructor of THD uses DBUG_ ! if (!(thd = new THD)) /* note that contructor of THD uses DBUG_ ! */ { sql_print_error("SCHEDULER: Cannot create a THD structure in an worker."); goto err_no_thd; Loading @@ -697,7 +698,7 @@ event_executor_worker(void *event_void) thd->init_for_queries(); // make this thread visible it has no vio -> show processlist needs this flag /* make this thread visible it has no vio -> show processlist needs this flag */ thd->system_thread= 1; VOID(pthread_mutex_lock(&LOCK_thread_count)); Loading Loading @@ -778,8 +779,8 @@ event_executor_worker(void *event_void) thd - Thread context. Used for memory allocation in some cases. RETURNS 0 - OK !0 - Error 0 OK !0 Error NOTES Reports the error to the console Loading Loading @@ -845,7 +846,7 @@ evex_load_events_from_db(THD *thd) break; } // let's find when to be executed /* let's find when to be executed */ if (et->compute_next_execution_time()) { sql_print_error("SCHEDULER: Error while computing execution time of %s.%s." Loading @@ -867,7 +868,8 @@ evex_load_events_from_db(THD *thd) VOID(pthread_mutex_unlock(&LOCK_event_arrays)); end_read_record(&read_record_info); thd->version--; // Force close to free memory /* Force close to free memory */ thd->version--; close_thread_tables(thd); if (!ret) Loading @@ -889,13 +891,13 @@ evex_load_events_from_db(THD *thd) car - the new value Returns 0 - OK (always) 0 OK (always) */ bool sys_var_event_executor::update(THD *thd, set_var *var) { // here start the thread if not running. /* here start the thread if not running. */ DBUG_ENTER("sys_var_event_executor::update"); VOID(pthread_mutex_lock(&LOCK_evex_running)); *value= var->save_result.ulong_value; Loading Loading @@ -952,7 +954,8 @@ evex_print_warnings(THD *thd, event_timed *et) while ((err= it++)) { String err_msg(msg_buf, sizeof(msg_buf), system_charset_info); err_msg.length(0);// set it to 0 or we start adding at the end /* set it to 0 or we start adding at the end. That's the trick ;) */ err_msg.length(0); if (!prefix.length()) { prefix.append("SCHEDULER: ["); Loading