mirror of
https://github.com/MariaDB/server.git
synced 2025-08-01 03:47:19 +03:00
WL #1034 updates after review
sql/event.cc: - fix TODO (remove things already done) - check the length of the event's name and body during creation and report an error if longer than what can be fit into mysql.event (nothing like non-strict mode here) - report to sql_parse.cc and error when open table failed, otherwise send_ok() was being called and the error have become an warning. - update function documentation a bit - evex_db_find_routine_aux returns 0 and not EVEX_OK sql/event_executor.cc: - CS changes to definitions of the main and worker thread routines - reorder code a bit to prevent crashes because of reading of already freed data -> first wait all events to finish their work, namely all worker threads to finish, and then destroy in-memory structures - more error checking and error reporting at the place of failure. sql/event_priv.h: code simplifying macro sql/event_timed.cc: CS cosmetics
This commit is contained in:
151
sql/event.cc
151
sql/event.cc
@ -27,8 +27,6 @@
|
|||||||
|
|
||||||
- Use timestamps instead of datetime.
|
- Use timestamps instead of datetime.
|
||||||
|
|
||||||
- Don't use SP's functionality for opening and closing of tables
|
|
||||||
|
|
||||||
- CREATE EVENT should not go into binary log! Does it now? The SQL statements
|
- CREATE EVENT should not go into binary log! Does it now? The SQL statements
|
||||||
issued by the EVENT are replicated.
|
issued by the EVENT are replicated.
|
||||||
I have an idea how to solve the problem at failover. So the status field
|
I have an idea how to solve the problem at failover. So the status field
|
||||||
@ -44,31 +42,16 @@
|
|||||||
ENABLED to DISABLED status change and this is safe for replicating. As well
|
ENABLED to DISABLED status change and this is safe for replicating. As well
|
||||||
an event may be deleted which is also safe for RBR.
|
an event may be deleted which is also safe for RBR.
|
||||||
|
|
||||||
- Add a lock and use it for guarding access to events_array dynamic array.
|
|
||||||
|
|
||||||
- Add checks everywhere where new instance of THD is created. NULL can be
|
|
||||||
returned and this will crash the server. The server will crash probably
|
|
||||||
later but should not be in this code! Add a global variable, and a lock
|
|
||||||
to guard it, that will specify an error in a worker thread so preventing
|
|
||||||
new threads from being spawned.
|
|
||||||
|
|
||||||
- Maybe move all allocations during parsing to evex_mem_root thus saving
|
- Maybe move all allocations during parsing to evex_mem_root thus saving
|
||||||
double parsing in evex_create_event!
|
double parsing in evex_create_event!
|
||||||
|
|
||||||
- If the server is killed (stopping) try to kill executing events..
|
- If the server is killed (stopping) try to kill executing events..
|
||||||
|
|
||||||
- What happens if one renames an event in the DB while it is in memory?
|
- What happens if one renames an event in the DB while it is in memory?
|
||||||
Or even deleting it?
|
Or even deleting it?
|
||||||
|
|
||||||
- created & modified in the table should be UTC?
|
|
||||||
|
|
||||||
- Add a lock to event_timed to serialize execution of an event - do not
|
|
||||||
allow parallel executions. Hmm, however how last_executed is marked
|
|
||||||
then? The call to event_timed::mark_last_executed() must be moved to
|
|
||||||
event_timed::execute()?
|
|
||||||
|
|
||||||
- Consider using conditional variable when doing shutdown instead of
|
- Consider using conditional variable when doing shutdown instead of
|
||||||
waiting some time (tries < 5).
|
waiting till all worker threads end.
|
||||||
- Make event_timed::get_show_create_event() work
|
- Make event_timed::get_show_create_event() work
|
||||||
- Add function documentation whenever needed.
|
- Add function documentation whenever needed.
|
||||||
- Add logging to file
|
- Add logging to file
|
||||||
@ -77,7 +60,7 @@ Warning:
|
|||||||
- For now parallel execution is not possible because the same sp_head cannot be
|
- For now parallel execution is not possible because the same sp_head cannot be
|
||||||
executed few times!!! There is still no lock attached to particular event.
|
executed few times!!! There is still no lock attached to particular event.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -216,17 +199,17 @@ TABLE *evex_open_event_table(THD *thd, enum thr_lock_type lock_type)
|
|||||||
table TABLE object for open mysql.event table.
|
table TABLE object for open mysql.event table.
|
||||||
|
|
||||||
RETURN VALUE
|
RETURN VALUE
|
||||||
SP_OK - Routine found
|
0 - Routine found
|
||||||
SP_KEY_NOT_FOUND- No routine with given name
|
SP_KEY_NOT_FOUND- No routine with given name
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int
|
int
|
||||||
evex_db_find_routine_aux(THD *thd, const LEX_STRING dbname,
|
evex_db_find_routine_aux(THD *thd, const LEX_STRING dbname,
|
||||||
const LEX_STRING rname, TABLE *table)
|
const LEX_STRING ev_name, TABLE *table)
|
||||||
{
|
{
|
||||||
byte key[MAX_KEY_LENGTH]; // db, name, optional key length type
|
byte key[MAX_KEY_LENGTH]; // db, name, optional key length type
|
||||||
DBUG_ENTER("evex_db_find_routine_aux");
|
DBUG_ENTER("evex_db_find_routine_aux");
|
||||||
DBUG_PRINT("enter", ("name: %.*s", rname.length, rname.str));
|
DBUG_PRINT("enter", ("name: %.*s", ev_name.length, ev_name.str));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Create key to find row. We have to use field->store() to be able to
|
Create key to find row. We have to use field->store() to be able to
|
||||||
@ -235,16 +218,16 @@ evex_db_find_routine_aux(THD *thd, const LEX_STRING dbname,
|
|||||||
'db' and 'name' and the first key is the primary key over the
|
'db' and 'name' and the first key is the primary key over the
|
||||||
same fields.
|
same fields.
|
||||||
*/
|
*/
|
||||||
if (rname.length > table->field[1]->field_length)
|
if (ev_name.length > table->field[1]->field_length)
|
||||||
DBUG_RETURN(SP_KEY_NOT_FOUND);
|
DBUG_RETURN(EVEX_KEY_NOT_FOUND);
|
||||||
|
|
||||||
table->field[0]->store(dbname.str, dbname.length, &my_charset_bin);
|
table->field[0]->store(dbname.str, dbname.length, &my_charset_bin);
|
||||||
table->field[1]->store(rname.str, rname.length, &my_charset_bin);
|
table->field[1]->store(ev_name.str, ev_name.length, &my_charset_bin);
|
||||||
key_copy(key, table->record[0], table->key_info, table->key_info->key_length);
|
key_copy(key, table->record[0], table->key_info, table->key_info->key_length);
|
||||||
|
|
||||||
if (table->file->index_read_idx(table->record[0], 0, key,
|
if (table->file->index_read_idx(table->record[0], 0, key,
|
||||||
table->key_info->key_length,HA_READ_KEY_EXACT))
|
table->key_info->key_length,HA_READ_KEY_EXACT))
|
||||||
DBUG_RETURN(SP_KEY_NOT_FOUND);
|
DBUG_RETURN(EVEX_KEY_NOT_FOUND);
|
||||||
|
|
||||||
DBUG_RETURN(0);
|
DBUG_RETURN(0);
|
||||||
}
|
}
|
||||||
@ -394,19 +377,18 @@ db_create_event(THD *thd, event_timed *et)
|
|||||||
|
|
||||||
restore_record(table, s->default_values); // Get default values for fields
|
restore_record(table, s->default_values); // Get default values for fields
|
||||||
|
|
||||||
/* TODO : Uncomment these and add handling in sql_parse.cc or here
|
|
||||||
|
|
||||||
if (sp->m_name.length > table->field[MYSQL_PROC_FIELD_NAME]->field_length)
|
if (et->m_name.length > table->field[EVEX_FIELD_NAME]->field_length)
|
||||||
{
|
{
|
||||||
ret= SP_BAD_IDENTIFIER;
|
my_error(ER_TOO_LONG_IDENT, MYF(0), et->m_name.str);
|
||||||
goto done;
|
goto err;
|
||||||
}
|
}
|
||||||
if (sp->m_body.length > table->field[MYSQL_PROC_FIELD_BODY]->field_length)
|
if (et->m_body.length > table->field[EVEX_FIELD_BODY]->field_length)
|
||||||
{
|
{
|
||||||
ret= SP_BODY_TOO_LONG;
|
my_error(ER_TOO_LONG_BODY, MYF(0), et->m_name.str);
|
||||||
goto done;
|
goto err;
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
if (!(et->m_expr) && !(et->m_execute_at.year))
|
if (!(et->m_expr) && !(et->m_execute_at.year))
|
||||||
{
|
{
|
||||||
DBUG_PRINT("error", ("neither m_expr nor m_execute_as are set!"));
|
DBUG_PRINT("error", ("neither m_expr nor m_execute_as are set!"));
|
||||||
@ -434,7 +416,8 @@ db_create_event(THD *thd, event_timed *et)
|
|||||||
my_error(ER_EVENT_STORE_FAILED, MYF(0), et->m_name.str);
|
my_error(ER_EVENT_STORE_FAILED, MYF(0), et->m_name.str);
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
else 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 */
|
||||||
@ -472,20 +455,20 @@ static int
|
|||||||
db_update_event(THD *thd, sp_name *name, event_timed *et)
|
db_update_event(THD *thd, sp_name *name, event_timed *et)
|
||||||
{
|
{
|
||||||
TABLE *table;
|
TABLE *table;
|
||||||
int ret;
|
int ret= EVEX_OPEN_TABLE_FAILED;
|
||||||
DBUG_ENTER("db_update_event");
|
DBUG_ENTER("db_update_event");
|
||||||
DBUG_PRINT("enter", ("name: %.*s", et->m_name.length, et->m_name.str));
|
DBUG_PRINT("enter", ("name: %.*s", et->m_name.length, et->m_name.str));
|
||||||
if (name)
|
if (name)
|
||||||
DBUG_PRINT("enter", ("rename to: %.*s", name->m_name.length, name->m_name.str));
|
DBUG_PRINT("enter", ("rename to: %.*s", name->m_name.length, name->m_name.str));
|
||||||
|
|
||||||
// Todo: Handle in sql_prepare.cc SP_OPEN_TABLE_FAILED
|
|
||||||
if (!(table= evex_open_event_table(thd, TL_WRITE)))
|
if (!(table= evex_open_event_table(thd, TL_WRITE)))
|
||||||
{
|
{
|
||||||
my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
|
my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (evex_db_find_routine_aux(thd, et->m_db, et->m_name, table) == SP_KEY_NOT_FOUND)
|
if (EVEX_KEY_NOT_FOUND == evex_db_find_routine_aux(thd, et->m_db, et->m_name,
|
||||||
|
table))
|
||||||
{
|
{
|
||||||
my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), et->m_name.str);
|
my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), et->m_name.str);
|
||||||
goto err;
|
goto err;
|
||||||
@ -526,8 +509,21 @@ err:
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Use sp_name for look up, return in **ett if found
|
Looks for a named event in mysql.event and in case of success returns
|
||||||
|
an object will data loaded from the table.
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
db_find_event()
|
||||||
|
thd THD
|
||||||
|
name the name of the event to find
|
||||||
|
ett event's data if event is found
|
||||||
|
tbl TABLE object to use when not NULL
|
||||||
|
|
||||||
|
NOTES
|
||||||
|
1) Use sp_name for look up, return in **ett if found
|
||||||
|
2) tbl is not closed at exit
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int
|
static int
|
||||||
db_find_event(THD *thd, sp_name *name, event_timed **ett, TABLE *tbl)
|
db_find_event(THD *thd, sp_name *name, event_timed **ett, TABLE *tbl)
|
||||||
{
|
{
|
||||||
@ -581,6 +577,22 @@ done:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Looks for a named event in mysql.event and then loads it from
|
||||||
|
the table, compiles it and insert it into the cache.
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
evex_load_and_compile_event()
|
||||||
|
thd THD
|
||||||
|
spn the name of the event to alter
|
||||||
|
use_lock whether to obtain a lock on LOCK_event_arrays or not
|
||||||
|
|
||||||
|
RETURN VALUE
|
||||||
|
0 - OK
|
||||||
|
< 0 - error (in this case underlying functions call my_error()).
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
static int
|
static int
|
||||||
evex_load_and_compile_event(THD * thd, sp_name *spn, bool use_lock)
|
evex_load_and_compile_event(THD * thd, sp_name *spn, bool use_lock)
|
||||||
{
|
{
|
||||||
@ -727,7 +739,7 @@ done:
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Exported functions follow
|
-= Exported functions follow =-
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -754,15 +766,6 @@ evex_create_event(THD *thd, event_timed *et, uint create_options)
|
|||||||
DBUG_PRINT("enter", ("name: %*s options:%d", et->m_name.length,
|
DBUG_PRINT("enter", ("name: %*s options:%d", et->m_name.length,
|
||||||
et->m_name.str, create_options));
|
et->m_name.str, create_options));
|
||||||
|
|
||||||
/*
|
|
||||||
VOID(pthread_mutex_lock(&LOCK_evex_running));
|
|
||||||
if (!evex_is_running)
|
|
||||||
// TODO: put an warning to the user here.
|
|
||||||
// Is it needed? (Andrey, 051129)
|
|
||||||
{}
|
|
||||||
VOID(pthread_mutex_unlock(&LOCK_evex_running));
|
|
||||||
*/
|
|
||||||
|
|
||||||
if ((ret = db_create_event(thd, et)) == EVEX_WRITE_ROW_FAILED &&
|
if ((ret = db_create_event(thd, et)) == EVEX_WRITE_ROW_FAILED &&
|
||||||
(create_options & HA_LEX_CREATE_IF_NOT_EXISTS))
|
(create_options & HA_LEX_CREATE_IF_NOT_EXISTS))
|
||||||
{
|
{
|
||||||
@ -821,13 +824,6 @@ evex_update_event(THD *thd, sp_name *name, event_timed *et)
|
|||||||
DBUG_ENTER("evex_update_event");
|
DBUG_ENTER("evex_update_event");
|
||||||
DBUG_PRINT("enter", ("name: %*s", et->m_name.length, et->m_name.str));
|
DBUG_PRINT("enter", ("name: %*s", et->m_name.length, et->m_name.str));
|
||||||
|
|
||||||
/*
|
|
||||||
VOID(pthread_mutex_lock(&LOCK_evex_running));
|
|
||||||
if (!evex_is_running)
|
|
||||||
// put an warning to the user here
|
|
||||||
{}
|
|
||||||
VOID(pthread_mutex_unlock(&LOCK_evex_running));
|
|
||||||
*/
|
|
||||||
/*
|
/*
|
||||||
db_update_event() opens & closes the table to prevent
|
db_update_event() opens & closes the table to prevent
|
||||||
crash later in the code when loading and compiling the new definition
|
crash later in the code when loading and compiling the new definition
|
||||||
@ -837,17 +833,8 @@ evex_update_event(THD *thd, sp_name *name, event_timed *et)
|
|||||||
|
|
||||||
VOID(pthread_mutex_lock(&LOCK_evex_running));
|
VOID(pthread_mutex_lock(&LOCK_evex_running));
|
||||||
if (!evex_is_running)
|
if (!evex_is_running)
|
||||||
{
|
UNLOCK_MUTEX_AND_BAIL_OUT(LOCK_evex_running, done);
|
||||||
// not running - therefore no memory structures
|
|
||||||
VOID(pthread_mutex_unlock(&LOCK_evex_running));
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
VOID(pthread_mutex_unlock(&LOCK_evex_running));
|
|
||||||
|
|
||||||
/*
|
|
||||||
It is possible that 2 (or 1) pass(es) won't find the event in memory.
|
|
||||||
The reason is that DISABLED events are not cached.
|
|
||||||
*/
|
|
||||||
VOID(pthread_mutex_lock(&LOCK_event_arrays));
|
VOID(pthread_mutex_lock(&LOCK_event_arrays));
|
||||||
evex_remove_from_cache(&et->m_db, &et->m_name, false);
|
evex_remove_from_cache(&et->m_db, &et->m_name, false);
|
||||||
if (et->m_status == MYSQL_EVENT_ENABLED)
|
if (et->m_status == MYSQL_EVENT_ENABLED)
|
||||||
@ -860,9 +847,14 @@ evex_update_event(THD *thd, sp_name *name, event_timed *et)
|
|||||||
delete spn;
|
delete spn;
|
||||||
}
|
}
|
||||||
VOID(pthread_mutex_unlock(&LOCK_event_arrays));
|
VOID(pthread_mutex_unlock(&LOCK_event_arrays));
|
||||||
|
VOID(pthread_mutex_unlock(&LOCK_evex_running));
|
||||||
|
|
||||||
|
/*
|
||||||
|
It is possible that 2 (or 1) pass(es) won't find the event in memory.
|
||||||
|
The reason is that DISABLED events are not cached.
|
||||||
|
*/
|
||||||
|
|
||||||
done:
|
done:
|
||||||
|
|
||||||
DBUG_RETURN(ret);
|
DBUG_RETURN(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -882,24 +874,17 @@ int
|
|||||||
evex_drop_event(THD *thd, event_timed *et, bool drop_if_exists)
|
evex_drop_event(THD *thd, event_timed *et, bool drop_if_exists)
|
||||||
{
|
{
|
||||||
TABLE *table;
|
TABLE *table;
|
||||||
int ret;
|
int ret= EVEX_OPEN_TABLE_FAILED;
|
||||||
bool opened;
|
bool opened;
|
||||||
DBUG_ENTER("evex_drop_event");
|
DBUG_ENTER("evex_drop_event");
|
||||||
|
|
||||||
/*
|
|
||||||
VOID(pthread_mutex_lock(&LOCK_evex_running));
|
|
||||||
if (!evex_is_running)
|
|
||||||
// put an warning to the user here
|
|
||||||
{}
|
|
||||||
VOID(pthread_mutex_unlock(&LOCK_evex_running));
|
|
||||||
*/
|
|
||||||
////
|
|
||||||
if (!(table= evex_open_event_table(thd, TL_WRITE)))
|
if (!(table= evex_open_event_table(thd, TL_WRITE)))
|
||||||
DBUG_RETURN(SP_OPEN_TABLE_FAILED);
|
{
|
||||||
|
my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
ret= evex_db_find_routine_aux(thd, et->m_db, et->m_name, table);
|
if (!(ret= evex_db_find_routine_aux(thd, et->m_db, et->m_name, table)))
|
||||||
|
|
||||||
if (ret == EVEX_OK)
|
|
||||||
{
|
{
|
||||||
if (ret= table->file->delete_row(table->record[0]))
|
if (ret= table->file->delete_row(table->record[0]))
|
||||||
{
|
{
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
#define DBUG_FAULTY_THR2
|
#define DBUG_FAULTY_THR2
|
||||||
|
|
||||||
static uint workers_count;
|
extern ulong thread_created;
|
||||||
|
|
||||||
|
|
||||||
pthread_mutex_t LOCK_event_arrays,
|
pthread_mutex_t LOCK_event_arrays,
|
||||||
@ -33,10 +33,8 @@ bool evex_is_running= false;
|
|||||||
|
|
||||||
ulong opt_event_executor;
|
ulong opt_event_executor;
|
||||||
my_bool event_executor_running_global_var= false;
|
my_bool event_executor_running_global_var= false;
|
||||||
|
|
||||||
extern ulong thread_created;
|
|
||||||
|
|
||||||
static my_bool evex_mutexes_initted= false;
|
static my_bool evex_mutexes_initted= false;
|
||||||
|
static uint workers_count;
|
||||||
|
|
||||||
static int
|
static int
|
||||||
evex_load_events_from_db(THD *thd);
|
evex_load_events_from_db(THD *thd);
|
||||||
@ -48,8 +46,11 @@ evex_load_events_from_db(THD *thd);
|
|||||||
the main thread or not.
|
the main thread or not.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pthread_handler_t event_executor_worker(void *arg);
|
pthread_handler_t
|
||||||
pthread_handler_t event_executor_main(void *arg);
|
event_executor_worker(void *arg);
|
||||||
|
|
||||||
|
pthread_handler_t
|
||||||
|
event_executor_main(void *arg);
|
||||||
|
|
||||||
static
|
static
|
||||||
void evex_init_mutexes()
|
void evex_init_mutexes()
|
||||||
@ -142,8 +143,8 @@ init_event_thread(THD* thd)
|
|||||||
DBUG_RETURN(0);
|
DBUG_RETURN(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pthread_handler_t
|
||||||
pthread_handler_t event_executor_main(void *arg)
|
event_executor_main(void *arg)
|
||||||
{
|
{
|
||||||
THD *thd; /* needs to be first for thread_stack */
|
THD *thd; /* needs to be first for thread_stack */
|
||||||
ulonglong iter_num= 0;
|
ulonglong iter_num= 0;
|
||||||
@ -152,13 +153,10 @@ pthread_handler_t event_executor_main(void *arg)
|
|||||||
DBUG_ENTER("event_executor_main");
|
DBUG_ENTER("event_executor_main");
|
||||||
DBUG_PRINT("event_executor_main", ("EVEX thread started"));
|
DBUG_PRINT("event_executor_main", ("EVEX thread started"));
|
||||||
|
|
||||||
VOID(pthread_mutex_lock(&LOCK_evex_running));
|
|
||||||
evex_is_running= true;
|
|
||||||
event_executor_running_global_var= opt_event_executor;
|
|
||||||
VOID(pthread_mutex_unlock(&LOCK_evex_running));
|
|
||||||
|
|
||||||
// init memory root
|
// init memory root
|
||||||
init_alloc_root(&evex_mem_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC);
|
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();
|
my_thread_init();
|
||||||
@ -196,6 +194,15 @@ pthread_handler_t event_executor_main(void *arg)
|
|||||||
VOID(my_init_dynamic_array(&evex_executing_queue, sizeof(event_timed *), 50, 100));
|
VOID(my_init_dynamic_array(&evex_executing_queue, sizeof(event_timed *), 50, 100));
|
||||||
VOID(pthread_mutex_unlock(&LOCK_event_arrays));
|
VOID(pthread_mutex_unlock(&LOCK_event_arrays));
|
||||||
|
|
||||||
|
/*
|
||||||
|
eventually manifest that we are running, not to crashe because of
|
||||||
|
usage of non-initialized memory structures.
|
||||||
|
*/
|
||||||
|
VOID(pthread_mutex_lock(&LOCK_evex_running));
|
||||||
|
evex_is_running= true;
|
||||||
|
event_executor_running_global_var= opt_event_executor;
|
||||||
|
VOID(pthread_mutex_unlock(&LOCK_evex_running));
|
||||||
|
|
||||||
if (evex_load_events_from_db(thd))
|
if (evex_load_events_from_db(thd))
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
@ -208,7 +215,6 @@ pthread_handler_t event_executor_main(void *arg)
|
|||||||
my_ulonglong cnt;
|
my_ulonglong cnt;
|
||||||
|
|
||||||
DBUG_PRINT("info", ("EVEX External Loop %d", ++cnt));
|
DBUG_PRINT("info", ("EVEX External Loop %d", ++cnt));
|
||||||
// sql_print_information("[EVEX] External Loop!");
|
|
||||||
thd->proc_info = "Sleeping";
|
thd->proc_info = "Sleeping";
|
||||||
my_sleep(1000000);// sleep 1s
|
my_sleep(1000000);// sleep 1s
|
||||||
if (!event_executor_running_global_var)
|
if (!event_executor_running_global_var)
|
||||||
@ -216,16 +222,13 @@ pthread_handler_t event_executor_main(void *arg)
|
|||||||
time(&now);
|
time(&now);
|
||||||
my_tz_UTC->gmt_sec_to_TIME(&time_now, now);
|
my_tz_UTC->gmt_sec_to_TIME(&time_now, now);
|
||||||
|
|
||||||
|
|
||||||
VOID(pthread_mutex_lock(&LOCK_event_arrays));
|
VOID(pthread_mutex_lock(&LOCK_event_arrays));
|
||||||
for (i= 0; (i < evex_executing_queue.elements) && !thd->killed; ++i)
|
for (i= 0; (i < evex_executing_queue.elements) && !thd->killed; ++i)
|
||||||
{
|
{
|
||||||
event_timed **p_et=dynamic_element(&evex_executing_queue,i,event_timed**);
|
event_timed *et= *dynamic_element(&evex_executing_queue,i,event_timed**);
|
||||||
event_timed *et= *p_et;
|
// printf("%llu\n", TIME_to_ulonglong_datetime(&et->m_execute_at));
|
||||||
// sql_print_information("[EVEX] External Loop 2!");
|
|
||||||
|
|
||||||
if (!event_executor_running_global_var)
|
if (!event_executor_running_global_var)
|
||||||
break;// soon we will do only continue (see the code a bit above)
|
break;
|
||||||
|
|
||||||
thd->proc_info = "Iterating";
|
thd->proc_info = "Iterating";
|
||||||
THD_CHECK_SENTRY(thd);
|
THD_CHECK_SENTRY(thd);
|
||||||
@ -233,7 +236,7 @@ pthread_handler_t event_executor_main(void *arg)
|
|||||||
if this is the first event which is after time_now then no
|
if this is the first event which is after time_now then no
|
||||||
more need to iterate over more elements since the array is sorted.
|
more need to iterate over more elements since the array is sorted.
|
||||||
*/
|
*/
|
||||||
if (et->m_execute_at.year &&
|
if (et->m_execute_at.year > 1969 &&
|
||||||
my_time_compare(&time_now, &et->m_execute_at) == -1)
|
my_time_compare(&time_now, &et->m_execute_at) == -1)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -250,8 +253,7 @@ pthread_handler_t event_executor_main(void *arg)
|
|||||||
if (pthread_create(&th, NULL, event_executor_worker, (void*)et))
|
if (pthread_create(&th, NULL, event_executor_worker, (void*)et))
|
||||||
{
|
{
|
||||||
sql_print_error("Problem while trying to create a thread");
|
sql_print_error("Problem while trying to create a thread");
|
||||||
VOID(pthread_mutex_unlock(&LOCK_event_arrays));
|
UNLOCK_MUTEX_AND_BAIL_OUT(LOCK_event_arrays, err);
|
||||||
goto err; // for now finish execution of the Executor
|
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
event_executor_worker((void *) et);
|
event_executor_worker((void *) et);
|
||||||
@ -272,12 +274,12 @@ pthread_handler_t event_executor_main(void *arg)
|
|||||||
j= 0;
|
j= 0;
|
||||||
while (j < i && j < evex_executing_queue.elements)
|
while (j < i && j < evex_executing_queue.elements)
|
||||||
{
|
{
|
||||||
event_timed **p_et= dynamic_element(&evex_executing_queue, j, event_timed**);
|
event_timed *et= *dynamic_element(&evex_executing_queue, j, event_timed**);
|
||||||
event_timed *et= *p_et;
|
|
||||||
if (et->m_flags & EVENT_EXEC_NO_MORE || et->m_status == MYSQL_EVENT_DISABLED)
|
if (et->m_flags & EVENT_EXEC_NO_MORE || et->m_status == MYSQL_EVENT_DISABLED)
|
||||||
{
|
{
|
||||||
delete_dynamic_element(&evex_executing_queue, j);
|
delete_dynamic_element(&evex_executing_queue, j);
|
||||||
DBUG_PRINT("", ("DELETING FROM EXECUTION QUEUE [%s.%s]",et->m_db.str, et->m_name.str));
|
DBUG_PRINT("EVEX main thread", ("DELETING FROM EXECUTION QUEUE [%s.%s]",
|
||||||
|
et->m_db.str, et->m_name.str));
|
||||||
// nulling the position, will delete later
|
// nulling the position, will delete later
|
||||||
if (et->m_dropped)
|
if (et->m_dropped)
|
||||||
{
|
{
|
||||||
@ -301,29 +303,44 @@ pthread_handler_t event_executor_main(void *arg)
|
|||||||
);
|
);
|
||||||
|
|
||||||
VOID(pthread_mutex_unlock(&LOCK_event_arrays));
|
VOID(pthread_mutex_unlock(&LOCK_event_arrays));
|
||||||
}// while (!thd->killed)
|
}
|
||||||
|
|
||||||
err:
|
err:
|
||||||
|
// First manifest that this thread does not work and then destroy
|
||||||
VOID(pthread_mutex_lock(&LOCK_evex_running));
|
VOID(pthread_mutex_lock(&LOCK_evex_running));
|
||||||
evex_is_running= false;
|
evex_is_running= false;
|
||||||
VOID(pthread_mutex_unlock(&LOCK_evex_running));
|
VOID(pthread_mutex_unlock(&LOCK_evex_running));
|
||||||
|
|
||||||
sql_print_information("Event executor stopping");
|
sql_print_information("Event executor stopping");
|
||||||
// LEX_STRINGs reside in the memory root and will be destroyed with it.
|
|
||||||
// Hence no need of delete but only freeing of SP
|
/*
|
||||||
for (i=0; i < events_array.elements; ++i)
|
TODO: A better will be with a conditional variable
|
||||||
{
|
*/
|
||||||
event_timed *et= dynamic_element(&events_array, i, event_timed*);
|
/*
|
||||||
et->free_sp();
|
Read workers_count without lock, no need for locking.
|
||||||
}
|
In the worst case we have to wait 1sec more.
|
||||||
// TODO Andrey: USE lock here!
|
*/
|
||||||
|
while (workers_count)
|
||||||
|
my_sleep(1000000);// 1s
|
||||||
|
|
||||||
|
/*
|
||||||
|
LEX_STRINGs reside in the memory root and will be destroyed with it.
|
||||||
|
Hence no need of delete but only freeing of SP
|
||||||
|
*/
|
||||||
|
for (i= 0; i < events_array.elements; ++i)
|
||||||
|
dynamic_element(&events_array, i, event_timed*)->free_sp();
|
||||||
|
|
||||||
|
VOID(pthread_mutex_lock(&LOCK_event_arrays));
|
||||||
|
// No need to use lock here if EVEX is not running but anyway
|
||||||
delete_dynamic(&evex_executing_queue);
|
delete_dynamic(&evex_executing_queue);
|
||||||
delete_dynamic(&events_array);
|
delete_dynamic(&events_array);
|
||||||
|
VOID(pthread_mutex_unlock(&LOCK_evex_running));
|
||||||
|
|
||||||
thd->proc_info = "Clearing";
|
thd->proc_info = "Clearing";
|
||||||
DBUG_ASSERT(thd->net.buff != 0);
|
DBUG_ASSERT(thd->net.buff != 0);
|
||||||
net_end(&thd->net); // destructor will not free it, because we are weird
|
net_end(&thd->net); // destructor will not free it, because we are weird
|
||||||
THD_CHECK_SENTRY(thd);
|
THD_CHECK_SENTRY(thd);
|
||||||
|
|
||||||
pthread_mutex_lock(&LOCK_thread_count);
|
pthread_mutex_lock(&LOCK_thread_count);
|
||||||
thread_count--;
|
thread_count--;
|
||||||
thread_running--;
|
thread_running--;
|
||||||
@ -331,28 +348,6 @@ err:
|
|||||||
delete thd;
|
delete thd;
|
||||||
pthread_mutex_unlock(&LOCK_thread_count);
|
pthread_mutex_unlock(&LOCK_thread_count);
|
||||||
|
|
||||||
/*
|
|
||||||
sleeping some time may help not crash the server. sleeping
|
|
||||||
is done to wait for spawned threads to finish.
|
|
||||||
|
|
||||||
TODO: A better will be with a conditional variable
|
|
||||||
*/
|
|
||||||
{
|
|
||||||
uint tries= 0;
|
|
||||||
while (tries++ < 5)
|
|
||||||
{
|
|
||||||
VOID(pthread_mutex_lock(&LOCK_workers_count));
|
|
||||||
if (!workers_count)
|
|
||||||
{
|
|
||||||
VOID(pthread_mutex_unlock(&LOCK_workers_count));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
VOID(pthread_mutex_unlock(&LOCK_workers_count));
|
|
||||||
DBUG_PRINT("info", ("Sleep %d", tries));
|
|
||||||
my_sleep(1000000 * tries);// 1s
|
|
||||||
}
|
|
||||||
DBUG_PRINT("info", ("Maybe now it is ok to kill the thread and evex MRoot"));
|
|
||||||
}
|
|
||||||
|
|
||||||
err_no_thd:
|
err_no_thd:
|
||||||
VOID(pthread_mutex_lock(&LOCK_evex_running));
|
VOID(pthread_mutex_lock(&LOCK_evex_running));
|
||||||
@ -362,30 +357,25 @@ err_no_thd:
|
|||||||
free_root(&evex_mem_root, MYF(0));
|
free_root(&evex_mem_root, MYF(0));
|
||||||
sql_print_information("Event executor stopped");
|
sql_print_information("Event executor stopped");
|
||||||
|
|
||||||
// shutdown_events();
|
|
||||||
|
|
||||||
my_thread_end();
|
my_thread_end();
|
||||||
pthread_exit(0);
|
pthread_exit(0);
|
||||||
DBUG_RETURN(0); // Can't return anything here
|
DBUG_RETURN(0);// Can't return anything here
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pthread_handler_t event_executor_worker(void *event_void)
|
pthread_handler_t
|
||||||
|
event_executor_worker(void *event_void)
|
||||||
{
|
{
|
||||||
THD *thd; /* needs to be first for thread_stack */
|
THD *thd; /* needs to be first for thread_stack */
|
||||||
List<Item> empty_item_list;
|
|
||||||
event_timed *event = (event_timed *) event_void;
|
event_timed *event = (event_timed *) event_void;
|
||||||
MEM_ROOT mem_root;
|
MEM_ROOT worker_mem_root;
|
||||||
|
|
||||||
DBUG_ENTER("event_executor_worker");
|
DBUG_ENTER("event_executor_worker");
|
||||||
VOID(pthread_mutex_lock(&LOCK_workers_count));
|
VOID(pthread_mutex_lock(&LOCK_workers_count));
|
||||||
++workers_count;
|
++workers_count;
|
||||||
VOID(pthread_mutex_unlock(&LOCK_workers_count));
|
VOID(pthread_mutex_unlock(&LOCK_workers_count));
|
||||||
|
|
||||||
init_alloc_root(&mem_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC);
|
init_alloc_root(&worker_mem_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC);
|
||||||
|
|
||||||
//we pass this empty list as parameter to the SP_HEAD of the event
|
|
||||||
empty_item_list.empty();
|
|
||||||
|
|
||||||
my_thread_init();
|
my_thread_init();
|
||||||
|
|
||||||
@ -395,8 +385,10 @@ pthread_handler_t event_executor_worker(void *event_void)
|
|||||||
goto err_no_thd;
|
goto err_no_thd;
|
||||||
}
|
}
|
||||||
thd->thread_stack = (char*)&thd; // remember where our stack is
|
thd->thread_stack = (char*)&thd; // remember where our stack is
|
||||||
thd->mem_root= &mem_root;
|
thd->mem_root= &worker_mem_root;
|
||||||
|
|
||||||
pthread_detach(pthread_self());
|
pthread_detach(pthread_self());
|
||||||
|
|
||||||
if (init_event_thread(thd))
|
if (init_event_thread(thd))
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
@ -425,7 +417,7 @@ pthread_handler_t event_executor_worker(void *event_void)
|
|||||||
my_TIME_to_str(&event->m_execute_at, exec_time);
|
my_TIME_to_str(&event->m_execute_at, exec_time);
|
||||||
DBUG_PRINT("info", (" EVEX EXECUTING event for event %s.%s [EXPR:%d][EXECUTE_AT:%s]", event->m_db.str, event->m_name.str,(int) event->m_expr, exec_time));
|
DBUG_PRINT("info", (" EVEX EXECUTING event for event %s.%s [EXPR:%d][EXECUTE_AT:%s]", event->m_db.str, event->m_name.str,(int) event->m_expr, exec_time));
|
||||||
sql_print_information(" EVEX EXECUTING event for event %s.%s [EXPR:%d][EXECUTE_AT:%s]", event->m_db.str, event->m_name.str,(int) event->m_expr, exec_time);
|
sql_print_information(" EVEX EXECUTING event for event %s.%s [EXPR:%d][EXECUTE_AT:%s]", event->m_db.str, event->m_name.str,(int) event->m_expr, exec_time);
|
||||||
ret= event->execute(thd, &mem_root);
|
ret= event->execute(thd, &worker_mem_root);
|
||||||
sql_print_information(" EVEX EXECUTED event for event %s.%s [EXPR:%d][EXECUTE_AT:%s]. RetCode=%d", event->m_db.str, event->m_name.str,(int) event->m_expr, exec_time, ret);
|
sql_print_information(" EVEX EXECUTED event for event %s.%s [EXPR:%d][EXECUTE_AT:%s]. RetCode=%d", event->m_db.str, event->m_name.str,(int) event->m_expr, exec_time, ret);
|
||||||
DBUG_PRINT("info", (" EVEX EXECUTED event for event %s.%s [EXPR:%d][EXECUTE_AT:%s]", event->m_db.str, event->m_name.str,(int) event->m_expr, exec_time));
|
DBUG_PRINT("info", (" EVEX EXECUTED event for event %s.%s [EXPR:%d][EXECUTE_AT:%s]", event->m_db.str, event->m_name.str,(int) event->m_expr, exec_time));
|
||||||
}
|
}
|
||||||
@ -454,8 +446,7 @@ err:
|
|||||||
|
|
||||||
err_no_thd:
|
err_no_thd:
|
||||||
|
|
||||||
free_root(&mem_root, MYF(0));
|
free_root(&worker_mem_root, MYF(0));
|
||||||
// sql_print_information(" Worker thread exiting");
|
|
||||||
|
|
||||||
VOID(pthread_mutex_lock(&LOCK_workers_count));
|
VOID(pthread_mutex_lock(&LOCK_workers_count));
|
||||||
--workers_count;
|
--workers_count;
|
||||||
@ -496,29 +487,38 @@ evex_load_events_from_db(THD *thd)
|
|||||||
}
|
}
|
||||||
DBUG_PRINT("evex_load_events_from_db", ("Loading event from row."));
|
DBUG_PRINT("evex_load_events_from_db", ("Loading event from row."));
|
||||||
|
|
||||||
if (et->load_from_row(&evex_mem_root, table))
|
if ((ret= et->load_from_row(&evex_mem_root, table)))
|
||||||
//error loading!
|
{
|
||||||
continue;
|
sql_print_error("Error while loading from mysql.event. "
|
||||||
|
"Table probably corrupted");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
DBUG_PRINT("evex_load_events_from_db",
|
DBUG_PRINT("evex_load_events_from_db",
|
||||||
("Event %s loaded from row. Time to compile", et->m_name.str));
|
("Event %s loaded from row. Time to compile", et->m_name.str));
|
||||||
|
|
||||||
if (et->compile(thd, &evex_mem_root))
|
if ((ret= et->compile(thd, &evex_mem_root)))
|
||||||
//problem during compile
|
{
|
||||||
continue;
|
sql_print_error("Error while compiling %s.%s. Aborting load.",
|
||||||
|
et->m_db.str, et->m_name.str);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
// let's find when to be executed
|
// let's find when to be executed
|
||||||
et->compute_next_execution_time();
|
et->compute_next_execution_time();
|
||||||
|
|
||||||
DBUG_PRINT("evex_load_events_from_db",
|
DBUG_PRINT("evex_load_events_from_db",
|
||||||
("Adding %s to the executor list.", et->m_name.str));
|
("Adding %s to the executor list.", et->m_name.str));
|
||||||
VOID(push_dynamic(&events_array,(gptr) et));
|
VOID(push_dynamic(&events_array,(gptr) et));
|
||||||
// we always add at the end so the number of elements - 1 is the place
|
/*
|
||||||
// in the buffer
|
We always add at the end so the number of elements - 1 is the place
|
||||||
|
in the buffer.
|
||||||
|
DYNAMIC_ARRAY copies the object bit by bit so we have a hollow copy
|
||||||
|
in event_array. We don't need the original therefore we delete it.
|
||||||
|
*/
|
||||||
et_copy= dynamic_element(&events_array, events_array.elements - 1,
|
et_copy= dynamic_element(&events_array, events_array.elements - 1,
|
||||||
event_timed*);
|
event_timed*);
|
||||||
VOID(push_dynamic(&evex_executing_queue,(gptr) &et_copy));
|
VOID(push_dynamic(&evex_executing_queue,(gptr) &et_copy));
|
||||||
et->m_free_sphead_on_delete= false;
|
et->m_free_sphead_on_delete= false;
|
||||||
DBUG_PRINT("info", (""));
|
|
||||||
delete et;
|
delete et;
|
||||||
}
|
}
|
||||||
end_read_record(&read_record_info);
|
end_read_record(&read_record_info);
|
||||||
@ -536,8 +536,7 @@ evex_load_events_from_db(THD *thd)
|
|||||||
end:
|
end:
|
||||||
close_thread_tables(thd);
|
close_thread_tables(thd);
|
||||||
|
|
||||||
DBUG_PRINT("evex_load_events_from_db",
|
DBUG_PRINT("info", ("Finishing with status code %d", ret));
|
||||||
("Events loaded from DB. Status code %d", ret));
|
|
||||||
DBUG_RETURN(ret);
|
DBUG_RETURN(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -547,7 +546,8 @@ 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.
|
||||||
VOID(pthread_mutex_lock(&LOCK_evex_running));
|
VOID(pthread_mutex_lock(&LOCK_evex_running));
|
||||||
if ((my_bool) var->save_result.ulong_value && !evex_is_running) {
|
if ((my_bool) var->save_result.ulong_value && !evex_is_running)
|
||||||
|
{
|
||||||
VOID(pthread_mutex_unlock(&LOCK_evex_running));
|
VOID(pthread_mutex_unlock(&LOCK_evex_running));
|
||||||
init_events();
|
init_events();
|
||||||
} else
|
} else
|
||||||
|
@ -18,6 +18,9 @@
|
|||||||
#define _EVENT_PRIV_H_
|
#define _EVENT_PRIV_H_
|
||||||
|
|
||||||
|
|
||||||
|
#define UNLOCK_MUTEX_AND_BAIL_OUT(__mutex, __label) \
|
||||||
|
{ VOID(pthread_mutex_unlock(&__mutex)); goto __label; }
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
EVEX_FIELD_DB = 0,
|
EVEX_FIELD_DB = 0,
|
||||||
|
@ -290,7 +290,7 @@ event_timed::init_ends(THD *thd, Item *ends)
|
|||||||
if (ends->fix_fields(thd, &ends))
|
if (ends->fix_fields(thd, &ends))
|
||||||
DBUG_RETURN(EVEX_PARSE_ERROR);
|
DBUG_RETURN(EVEX_PARSE_ERROR);
|
||||||
|
|
||||||
// the field was already fixed in init_ends
|
// the field was already fixed in init_ends
|
||||||
if ((not_used= ends->get_date(<ime, TIME_NO_ZERO_DATE)))
|
if ((not_used= ends->get_date(<ime, TIME_NO_ZERO_DATE)))
|
||||||
DBUG_RETURN(EVEX_BAD_PARAMS);
|
DBUG_RETURN(EVEX_BAD_PARAMS);
|
||||||
|
|
||||||
@ -537,10 +537,19 @@ event_timed::compute_next_execution_time()
|
|||||||
my_tz_UTC->gmt_sec_to_TIME(&time_now, now);
|
my_tz_UTC->gmt_sec_to_TIME(&time_now, now);
|
||||||
/*
|
/*
|
||||||
sql_print_information("[%s.%s]", m_db.str, m_name.str);
|
sql_print_information("[%s.%s]", m_db.str, m_name.str);
|
||||||
sql_print_information("time_now : [%d-%d-%d %d:%d:%d ]", time_now.year, time_now.month, time_now.day, time_now.hour, time_now.minute, time_now.second);
|
sql_print_information("time_now : [%d-%d-%d %d:%d:%d ]",
|
||||||
sql_print_information("m_starts : [%d-%d-%d %d:%d:%d ]", m_starts.year, m_starts.month, m_starts.day, m_starts.hour, m_starts.minute, m_starts.second);
|
time_now.year, time_now.month, time_now.day,
|
||||||
sql_print_information("m_ends : [%d-%d-%d %d:%d:%d ]", m_ends.year, m_ends.month, m_ends.day, m_ends.hour, m_ends.minute, m_ends.second);
|
time_now.hour, time_now.minute, time_now.second);
|
||||||
sql_print_information("m_last_ex: [%d-%d-%d %d:%d:%d ]", m_last_executed.year, m_last_executed.month, m_last_executed.day, m_last_executed.hour, m_last_executed.minute, m_last_executed.second);
|
sql_print_information("m_starts : [%d-%d-%d %d:%d:%d ]", m_starts.year,
|
||||||
|
m_starts.month, m_starts.day, m_starts.hour,
|
||||||
|
m_starts.minute, m_starts.second);
|
||||||
|
sql_print_information("m_ends : [%d-%d-%d %d:%d:%d ]", m_ends.year,
|
||||||
|
m_ends.month, m_ends.day, m_ends.hour,
|
||||||
|
m_ends.minute, m_ends.second);
|
||||||
|
sql_print_information("m_last_ex: [%d-%d-%d %d:%d:%d ]", m_last_executed.year,
|
||||||
|
m_last_executed.month, m_last_executed.day,
|
||||||
|
m_last_executed.hour, m_last_executed.minute,
|
||||||
|
m_last_executed.second);
|
||||||
*/
|
*/
|
||||||
//if time_now is after m_ends don't execute anymore
|
//if time_now is after m_ends don't execute anymore
|
||||||
if (m_ends.year && (tmp= my_time_compare(&m_ends, &time_now)) == -1)
|
if (m_ends.year && (tmp= my_time_compare(&m_ends, &time_now)) == -1)
|
||||||
@ -702,7 +711,6 @@ event_timed::mark_last_executed()
|
|||||||
bool
|
bool
|
||||||
event_timed::drop(THD *thd)
|
event_timed::drop(THD *thd)
|
||||||
{
|
{
|
||||||
|
|
||||||
return (bool) evex_drop_event(thd, this, false);
|
return (bool) evex_drop_event(thd, this, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user