summaryrefslogtreecommitdiff
path: root/sql/event.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/event.cc')
-rw-r--r--sql/event.cc839
1 files changed, 775 insertions, 64 deletions
diff --git a/sql/event.cc b/sql/event.cc
index 6d62be903bd..d9e71a263b8 100644
--- a/sql/event.cc
+++ b/sql/event.cc
@@ -51,8 +51,6 @@
- Make event_timed::get_show_create_event() work
- - Add function documentation whenever needed.
-
- Add logging to file
- Move comparison code to class event_timed
@@ -66,8 +64,142 @@ Warning:
QUEUE EVEX_EQ_NAME;
MEM_ROOT evex_mem_root;
+time_t mysql_event_last_create_time= 0L;
+
+
+static TABLE_FIELD_W_TYPE event_table_fields[EVEX_FIELD_COUNT] = {
+ {
+ {(char *) STRING_WITH_LEN("db")},
+ {(char *) STRING_WITH_LEN("char(64)")},
+ {(char *) STRING_WITH_LEN("utf8")}
+ },
+ {
+ {(char *) STRING_WITH_LEN("name")},
+ {(char *) STRING_WITH_LEN("char(64)")},
+ {(char *) STRING_WITH_LEN("utf8")}
+ },
+ {
+ {(char *) STRING_WITH_LEN("body")},
+ {(char *) STRING_WITH_LEN("longblob")},
+ {NULL, 0}
+ },
+ {
+ {(char *) STRING_WITH_LEN("definer")},
+ {(char *) STRING_WITH_LEN("char(77)")},
+ {(char *) STRING_WITH_LEN("utf8")}
+ },
+ {
+ {(char *) STRING_WITH_LEN("execute_at")},
+ {(char *) STRING_WITH_LEN("datetime")},
+ {NULL, 0}
+ },
+ {
+ {(char *) STRING_WITH_LEN("interval_value")},
+ {(char *) STRING_WITH_LEN("int(11)")},
+ {NULL, 0}
+ },
+ {
+ {(char *) STRING_WITH_LEN("interval_field")},
+ {(char *) STRING_WITH_LEN("enum('YEAR','QUARTER','MONTH','DAY',"
+ "'HOUR','MINUTE','WEEK','SECOND','MICROSECOND','YEAR_MONTH','DAY_HOUR',"
+ "'DAY_MINUTE','DAY_SECOND','HOUR_MINUTE','HOUR_SECOND','MINUTE_SECOND',"
+ "'DAY_MICROSECOND','HOUR_MICROSECOND','MINUTE_MICROSECOND',"
+ "'SECOND_MICROSECOND')")},
+ {NULL, 0}
+ },
+ {
+ {(char *) STRING_WITH_LEN("created")},
+ {(char *) STRING_WITH_LEN("timestamp")},
+ {NULL, 0}
+ },
+ {
+ {(char *) STRING_WITH_LEN("modified")},
+ {(char *) STRING_WITH_LEN("timestamp")},
+ {NULL, 0}
+ },
+ {
+ {(char *) STRING_WITH_LEN("last_executed")},
+ {(char *) STRING_WITH_LEN("datetime")},
+ },
+ {
+ {(char *) STRING_WITH_LEN("starts")},
+ {(char *) STRING_WITH_LEN("datetime")},
+ {NULL, 0}
+ },
+ {
+ {(char *) STRING_WITH_LEN("ends")},
+ {(char *) STRING_WITH_LEN("datetime")},
+ {NULL, 0}
+ },
+ {
+ {(char *) STRING_WITH_LEN("status")},
+ {(char *) STRING_WITH_LEN("enum('ENABLED','DISABLED')")},
+ {NULL, 0}
+ },
+ {
+ {(char *) STRING_WITH_LEN("on_completion")},
+ {(char *) STRING_WITH_LEN("enum('DROP','PRESERVE')")},
+ {NULL, 0}
+ },
+ {
+ {(char *) STRING_WITH_LEN("sql_mode")},
+ {(char *) STRING_WITH_LEN("set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES',"
+ "'IGNORE_SPACE','NOT_USED','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION',"
+ "'NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB',"
+ "'NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40',"
+ "'ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES',"
+ "'STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES',"
+ "'ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER',"
+ "'HIGH_NOT_PRECEDENCE')")},
+ {NULL, 0}
+ },
+ {
+ {(char *) STRING_WITH_LEN("comment")},
+ {(char *) STRING_WITH_LEN("char(64)")},
+ {(char *) STRING_WITH_LEN("utf8")}
+ }
+};
+
+
+LEX_STRING interval_type_to_name[] = {
+ {(char *) STRING_WITH_LEN("YEAR")},
+ {(char *) STRING_WITH_LEN("QUARTER")},
+ {(char *) STRING_WITH_LEN("MONTH")},
+ {(char *) STRING_WITH_LEN("DAY")},
+ {(char *) STRING_WITH_LEN("HOUR")},
+ {(char *) STRING_WITH_LEN("MINUTE")},
+ {(char *) STRING_WITH_LEN("WEEK")},
+ {(char *) STRING_WITH_LEN("SECOND")},
+ {(char *) STRING_WITH_LEN("MICROSECOND")},
+ {(char *) STRING_WITH_LEN("YEAR_MONTH")},
+ {(char *) STRING_WITH_LEN("DAY_HOUR")},
+ {(char *) STRING_WITH_LEN("DAY_MINUTE")},
+ {(char *) STRING_WITH_LEN("DAY_SECOND")},
+ {(char *) STRING_WITH_LEN("HOUR_MINUTE")},
+ {(char *) STRING_WITH_LEN("HOUR_SECOND")},
+ {(char *) STRING_WITH_LEN("MINUTE_SECOND")},
+ {(char *) STRING_WITH_LEN("DAY_MICROSECOND")},
+ {(char *) STRING_WITH_LEN("HOUR_MICROSECOND")},
+ {(char *) STRING_WITH_LEN("MINUTE_MICROSECOND")},
+ {(char *) STRING_WITH_LEN("SECOND_MICROSECOND")}
+};
+
+/*
+ Inits the scheduler queue - prioritized queue from mysys/queue.c
+
+ Synopsis
+ evex_queue_init()
+
+ queue - pointer the the memory to be initialized as queue. has to be
+ allocated from the caller
+
+ Notes
+ During initialization the queue is sized for 30 events, and when is full
+ will auto extent with 30.
+*/
+
void
evex_queue_init(EVEX_QUEUE_TYPE *queue)
{
@@ -77,7 +209,25 @@ evex_queue_init(EVEX_QUEUE_TYPE *queue)
}
-static
+/*
+ Compares 2 LEX strings regarding case.
+
+ Synopsis
+ my_time_compare()
+
+ s - first LEX_STRING
+ t - second LEX_STRING
+ cs - charset
+
+ RETURNS:
+ -1 - s < t
+ 0 - s == t
+ 1 - s > t
+
+ Notes
+ TIME.second_part is not considered during comparison
+*/
+
int sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs)
{
return cs->coll->strnncollsp(cs, (unsigned char *) s.str,s.length,
@@ -85,11 +235,35 @@ int sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs)
}
+/*
+ Compares 2 TIME structures
+
+ Synopsis
+ my_time_compare()
+
+ a - first TIME
+ b - second time
+
+ RETURNS:
+ -1 - a < b
+ 0 - a == b
+ 1 - a > b
+
+ Notes
+ TIME.second_part is not considered during comparison
+*/
+
int
my_time_compare(TIME *a, TIME *b)
{
+
+#ifdef ENABLE_WHEN_WE_HAVE_MILLISECOND_IN_TIMESTAMPS
my_ulonglong a_t= TIME_to_ulonglong_datetime(a)*100L + a->second_part;
my_ulonglong b_t= TIME_to_ulonglong_datetime(b)*100L + b->second_part;
+#else
+ my_ulonglong a_t= TIME_to_ulonglong_datetime(a);
+ my_ulonglong b_t= TIME_to_ulonglong_datetime(b);
+#endif
if (a_t > b_t)
return 1;
@@ -100,6 +274,24 @@ my_time_compare(TIME *a, TIME *b)
}
+/*
+ Compares the execute_at members of 2 event_timed instances
+
+ Synopsis
+ event_timed_compare()
+
+ a - first event_timed object
+ b - second event_timed object
+
+ RETURNS:
+ -1 - a->execute_at < b->execute_at
+ 0 - a->execute_at == b->execute_at
+ 1 - a->execute_at > b->execute_at
+
+ Notes
+ execute_at.second_part is not considered during comparison
+*/
+
int
event_timed_compare(event_timed *a, event_timed *b)
{
@@ -108,7 +300,24 @@ event_timed_compare(event_timed *a, event_timed *b)
/*
- Callback for the prio queue
+ Compares the execute_at members of 2 event_timed instances.
+ Used as callback for the prioritized queue when shifting
+ elements inside.
+
+ Synopsis
+ event_timed_compare()
+
+ vptr - not used (set it to NULL)
+ a - first event_timed object
+ b - second event_timed object
+
+ RETURNS:
+ -1 - a->execute_at < b->execute_at
+ 0 - a->execute_at == b->execute_at
+ 1 - a->execute_at > b->execute_at
+
+ Notes
+ execute_at.second_part is not considered during comparison
*/
int
@@ -119,6 +328,145 @@ event_timed_compare_q(void *vptr, byte* a, byte *b)
/*
+ Reconstructs interval expression from interval type and expression
+ value that is in form of a value of the smalles entity:
+ For
+ YEAR_MONTH - expression is in months
+ DAY_MINUTE - expression is in minutes
+
+ Synopsis
+ event_reconstruct_interval_expression()
+ buf - preallocated String buffer to add the value to
+ interval - the interval type (for instance YEAR_MONTH)
+ expression - the value in the lowest entity
+
+ RETURNS
+ 0 - OK
+ 1 - Error
+
+
+*/
+
+int
+event_reconstruct_interval_expression(String *buf,
+ interval_type interval,
+ longlong expression)
+{
+ ulonglong expr= expression;
+ char tmp_buff[128], *end;
+ bool close_quote= TRUE;
+ int multipl= 0;
+ char separator=':';
+
+ switch (interval) {
+ case INTERVAL_YEAR_MONTH:
+ multipl= 12;
+ separator= '-';
+ goto common_1_lev_code;
+ case INTERVAL_DAY_HOUR:
+ multipl= 24;
+ separator= ' ';
+ goto common_1_lev_code;
+ case INTERVAL_HOUR_MINUTE:
+ case INTERVAL_MINUTE_SECOND:
+ multipl= 60;
+common_1_lev_code:
+ buf->append('\'');
+ end= longlong10_to_str(expression/multipl, tmp_buff, 10);
+ buf->append(tmp_buff, (uint) (end- tmp_buff));
+ expr= expr - (expr/multipl)*multipl;
+ break;
+ case INTERVAL_DAY_MINUTE:
+ {
+ int tmp_expr= expr;
+
+ tmp_expr/=(24*60);
+ buf->append('\'');
+ end= longlong10_to_str(tmp_expr, tmp_buff, 10);
+ buf->append(tmp_buff, (uint) (end- tmp_buff));// days
+ buf->append(' ');
+
+ tmp_expr= expr - tmp_expr*(24*60);//minutes left
+ end= longlong10_to_str(tmp_expr/60, tmp_buff, 10);
+ buf->append(tmp_buff, (uint) (end- tmp_buff));// hours
+
+ expr= tmp_expr - (tmp_expr/60)*60;
+ /* the code after the switch will finish */
+ }
+ break;
+ case INTERVAL_HOUR_SECOND:
+ {
+ int tmp_expr= expr;
+
+ buf->append('\'');
+ end= longlong10_to_str(tmp_expr/3600, tmp_buff, 10);
+ buf->append(tmp_buff, (uint) (end- tmp_buff));// hours
+ buf->append(':');
+
+ tmp_expr= tmp_expr - (tmp_expr/3600)*3600;
+ end= longlong10_to_str(tmp_expr/60, tmp_buff, 10);
+ buf->append(tmp_buff, (uint) (end- tmp_buff));// minutes
+
+ expr= tmp_expr - (tmp_expr/60)*60;
+ /* the code after the switch will finish */
+ }
+ break;
+ case INTERVAL_DAY_SECOND:
+ {
+ int tmp_expr= expr;
+
+ tmp_expr/=(24*3600);
+ buf->append('\'');
+ end= longlong10_to_str(tmp_expr, tmp_buff, 10);
+ buf->append(tmp_buff, (uint) (end- tmp_buff));// days
+ buf->append(' ');
+
+ tmp_expr= expr - tmp_expr*(24*3600);//seconds left
+ end= longlong10_to_str(tmp_expr/3600, tmp_buff, 10);
+ buf->append(tmp_buff, (uint) (end- tmp_buff));// hours
+ buf->append(':');
+
+ tmp_expr= tmp_expr - (tmp_expr/3600)*3600;
+ end= longlong10_to_str(tmp_expr/60, tmp_buff, 10);
+ buf->append(tmp_buff, (uint) (end- tmp_buff));// minutes
+
+ expr= tmp_expr - (tmp_expr/60)*60;
+ /* the code after the switch will finish */
+ }
+ break;
+ case INTERVAL_DAY_MICROSECOND:
+ case INTERVAL_HOUR_MICROSECOND:
+ case INTERVAL_MINUTE_MICROSECOND:
+ case INTERVAL_SECOND_MICROSECOND:
+ my_error(ER_NOT_SUPPORTED_YET, MYF(0), "MICROSECOND");
+ return 1;
+ break;
+ case INTERVAL_QUARTER:
+ expr/= 3;
+ close_quote= FALSE;
+ break;
+ case INTERVAL_WEEK:
+ expr/= 7;
+ default:
+ close_quote= FALSE;
+ break;
+ }
+ if (close_quote)
+ buf->append(separator);
+ end= longlong10_to_str(expr, tmp_buff, 10);
+ buf->append(tmp_buff, (uint) (end- tmp_buff));
+ if (close_quote)
+ buf->append('\'');
+
+ buf->append(' ');
+ LEX_STRING *ival= &interval_type_to_name[interval];
+ buf->append(ival->str, ival->length);
+
+ return 0;
+}
+
+
+/*
Open mysql.event table for read
SYNOPSIS
@@ -126,6 +474,7 @@ event_timed_compare_q(void *vptr, byte* a, byte *b)
thd Thread context
lock_type How to lock the table
table The table pointer
+
RETURN
1 Cannot lock table
2 The table is corrupted - different number of fields
@@ -147,9 +496,10 @@ evex_open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table)
if (simple_open_n_lock_tables(thd, &tables))
DBUG_RETURN(1);
- if (tables.table->s->fields != EVEX_FIELD_COUNT)
+ if (table_check_intact(tables.table, EVEX_FIELD_COUNT, event_table_fields,
+ &mysql_event_last_create_time,
+ ER_EVENT_CANNOT_LOAD_FROM_TABLE))
{
- my_error(ER_EVENT_COL_COUNT_DOESNT_MATCH, MYF(0), "mysql", "event");
close_thread_tables(thd);
DBUG_RETURN(2);
}
@@ -165,6 +515,28 @@ evex_open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table)
SYNOPSIS
evex_db_find_event_aux()
thd Thread context
+ et evet_timed object containing dbname, name & definer
+ table TABLE object for open mysql.event table.
+
+ RETURN VALUE
+ 0 - Routine found
+ EVEX_KEY_NOT_FOUND - No routine with given name
+*/
+
+inline int
+evex_db_find_event_aux(THD *thd, event_timed *et, TABLE *table)
+{
+ return evex_db_find_event_by_name(thd, et->dbname, et->name,
+ et->definer, table);
+}
+
+
+/*
+ Find row in open mysql.event table representing event
+
+ SYNOPSIS
+ evex_db_find_event_by_name()
+ thd Thread context
dbname Name of event's database
rname Name of the event inside the db
table TABLE object for open mysql.event table.
@@ -175,11 +547,13 @@ evex_open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table)
*/
int
-evex_db_find_event_aux(THD *thd, const LEX_STRING dbname,
- const LEX_STRING ev_name, TABLE *table)
+evex_db_find_event_by_name(THD *thd, const LEX_STRING dbname,
+ const LEX_STRING ev_name,
+ const LEX_STRING user_name,
+ TABLE *table)
{
byte key[MAX_KEY_LENGTH];
- DBUG_ENTER("evex_db_find_event_aux");
+ DBUG_ENTER("evex_db_find_event_by_name");
DBUG_PRINT("enter", ("name: %.*s", ev_name.length, ev_name.str));
/*
@@ -190,11 +564,17 @@ evex_db_find_event_aux(THD *thd, const LEX_STRING dbname,
same fields.
*/
if (dbname.length > table->field[EVEX_FIELD_DB]->field_length ||
- ev_name.length > table->field[EVEX_FIELD_NAME]->field_length)
+ ev_name.length > table->field[EVEX_FIELD_NAME]->field_length ||
+ user_name.length > table->field[EVEX_FIELD_DEFINER]->field_length)
+
DBUG_RETURN(EVEX_KEY_NOT_FOUND);
- table->field[0]->store(dbname.str, dbname.length, &my_charset_bin);
- table->field[1]->store(ev_name.str, ev_name.length, &my_charset_bin);
+ table->field[EVEX_FIELD_DB]->store(dbname.str, dbname.length, &my_charset_bin);
+ table->field[EVEX_FIELD_NAME]->store(ev_name.str, ev_name.length,
+ &my_charset_bin);
+ table->field[EVEX_FIELD_DEFINER]->store(user_name.str, user_name.length,
+ &my_charset_bin);
+
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,
@@ -205,7 +585,6 @@ evex_db_find_event_aux(THD *thd, const LEX_STRING dbname,
}
-
/*
Puts some data common to CREATE and ALTER EVENT into a row.
@@ -231,14 +610,9 @@ evex_fill_row(THD *thd, TABLE *table, event_timed *et, my_bool is_update)
DBUG_ENTER("evex_fill_row");
- if (table->s->fields != EVEX_FIELD_COUNT)
- {
- my_error(ER_EVENT_COL_COUNT_DOESNT_MATCH, MYF(0), "mysql", "event");
- DBUG_RETURN(EVEX_GET_FIELD_FAILED);
- }
-
- DBUG_PRINT("info", ("dbname.len=%d",et->dbname.length));
- DBUG_PRINT("info", ("name.len=%d",et->name.length));
+ DBUG_PRINT("info", ("dbname=[%s]", et->dbname.str));
+ DBUG_PRINT("info", ("name =[%s]", et->name.str));
+ DBUG_PRINT("info", ("body =[%s]", et->body.str));
if (table->field[field_num= EVEX_FIELD_DB]->
store(et->dbname.str, et->dbname.length, system_charset_info))
@@ -283,10 +657,15 @@ evex_fill_row(THD *thd, TABLE *table, event_timed *et, my_bool is_update)
from 1. Thus +1 offset is needed!
*/
table->field[EVEX_FIELD_TRANSIENT_INTERVAL]->store((longlong)et->interval+1);
+
+ table->field[EVEX_FIELD_EXECUTE_AT]->set_null();
}
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_EXECUTE_AT]->set_notnull();
table->field[EVEX_FIELD_EXECUTE_AT]->store_time(&et->execute_at,
MYSQL_TIMESTAMP_DATETIME);
@@ -351,14 +730,19 @@ db_create_event(THD *thd, event_timed *et, my_bool create_if_not,
my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
goto err;
}
-
+
DBUG_PRINT("info", ("check existance of an event with the same name"));
- if (!evex_db_find_event_aux(thd, et->dbname, et->name, table))
+ if (!evex_db_find_event_aux(thd, et, table))
{
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
- ER_EVENT_ALREADY_EXISTS, ER(ER_EVENT_ALREADY_EXISTS),
- et->name.str);
- goto ok;
+ if (create_if_not)
+ {
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ ER_EVENT_ALREADY_EXISTS, ER(ER_EVENT_ALREADY_EXISTS),
+ et->name.str);
+ goto ok;
+ }
+ my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), et->name.str);
+ goto err;
}
DBUG_PRINT("info", ("non-existant, go forward"));
@@ -398,10 +782,9 @@ db_create_event(THD *thd, event_timed *et, my_bool create_if_not,
goto err;
}
- strxmov(definer, et->definer_user.str, "@", et->definer_host.str, NullS);
- if ((ret=table->field[EVEX_FIELD_DEFINER]->
- store(definer, et->definer_user.length + 1 + et->definer_host.length,
- system_charset_info)))
+ if ((ret=table->field[EVEX_FIELD_DEFINER]->store(et->definer.str,
+ et->definer.length,
+ system_charset_info)))
{
my_error(ER_EVENT_STORE_FAILED, MYF(0), et->name.str, ret);
goto err;
@@ -464,7 +847,9 @@ db_update_event(THD *thd, event_timed *et, sp_name *new_name)
TABLE *table;
int ret= EVEX_OPEN_TABLE_FAILED;
DBUG_ENTER("db_update_event");
+ DBUG_PRINT("enter", ("dbname: %.*s", et->dbname.length, et->dbname.str));
DBUG_PRINT("enter", ("name: %.*s", et->name.length, et->name.str));
+ DBUG_PRINT("enter", ("user: %.*s", et->name.length, et->name.str));
if (new_name)
DBUG_PRINT("enter", ("rename to: %.*s", new_name->m_name.length,
new_name->m_name.str));
@@ -485,7 +870,8 @@ db_update_event(THD *thd, event_timed *et, sp_name *new_name)
goto err;
}
- if (!evex_db_find_event_aux(thd, new_name->m_db, new_name->m_name, table))
+ if (!evex_db_find_event_by_name(thd, new_name->m_db, new_name->m_name,
+ et->definer, table))
{
my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), new_name->m_name.str);
goto err;
@@ -497,8 +883,7 @@ db_update_event(THD *thd, event_timed *et, sp_name *new_name)
overwrite the key and SE will tell us that it cannot find the already found
row (copied into record[1] later
*/
- if (EVEX_KEY_NOT_FOUND == evex_db_find_event_aux(thd, et->dbname, et->name,
- table))
+ if (EVEX_KEY_NOT_FOUND == evex_db_find_event_aux(thd, et, table))
{
my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), et->name.str);
goto err;
@@ -547,6 +932,7 @@ err:
db_find_event()
thd THD
name the name of the event to find
+ definer who owns the event
ett event's data if event is found
tbl TABLE object to use when not NULL
@@ -556,16 +942,19 @@ err:
*/
static int
-db_find_event(THD *thd, sp_name *name, event_timed **ett, TABLE *tbl)
+db_find_event(THD *thd, sp_name *name, LEX_STRING *definer, event_timed **ett,
+ TABLE *tbl, MEM_ROOT *root)
{
TABLE *table;
int ret;
- const char *definer;
char *ptr;
- event_timed *et;
+ event_timed *et=NULL;
DBUG_ENTER("db_find_event");
DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str));
+ if (!root)
+ root= &evex_mem_root;
+
if (tbl)
table= tbl;
else if (evex_open_event_table(thd, TL_READ, &table))
@@ -575,7 +964,8 @@ db_find_event(THD *thd, sp_name *name, event_timed **ett, TABLE *tbl)
goto done;
}
- if ((ret= evex_db_find_event_aux(thd, name->m_db, name->m_name, table)))
+ if ((ret= evex_db_find_event_by_name(thd, name->m_db, name->m_name, *definer,
+ table)))
{
my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name->m_name.str);
goto done;
@@ -588,7 +978,7 @@ db_find_event(THD *thd, sp_name *name, event_timed **ett, TABLE *tbl)
2)::load_from_row() is silent on error therefore we emit error msg here
*/
- if ((ret= et->load_from_row(&evex_mem_root, table)))
+ if ((ret= et->load_from_row(root, table)))
{
my_error(ER_EVENT_CANNOT_LOAD_FROM_TABLE, MYF(0));
goto done;
@@ -616,6 +1006,7 @@ done:
evex_load_and_compile_event()
thd THD
spn the name of the event to alter
+ definer who is the owner
use_lock whether to obtain a lock on LOCK_event_arrays or not
RETURN VALUE
@@ -625,7 +1016,8 @@ done:
*/
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, LEX_STRING definer,
+ bool use_lock)
{
int ret= 0;
MEM_ROOT *tmp_mem_root;
@@ -640,10 +1032,11 @@ evex_load_and_compile_event(THD * thd, sp_name *spn, bool use_lock)
thd->reset_n_backup_open_tables_state(&backup);
// no need to use my_error() here because db_find_event() has done it
- if ((ret= db_find_event(thd, spn, &ett, NULL)))
+ ret= db_find_event(thd, spn, &definer, &ett, NULL, NULL);
+ thd->restore_backup_open_tables_state(&backup);
+ if (ret)
goto done;
- thd->restore_backup_open_tables_state(&backup);
/*
allocate on evex_mem_root. if you call without evex_mem_root
then sphead will not be cleared!
@@ -673,9 +1066,31 @@ done:
}
+/*
+ Removes from queue in memory the event which is identified by the tupple
+ (db, name).
+
+ SYNOPSIS
+ evex_remove_from_cache()
+
+ db - db name
+ name - event name
+ use_lock - whether to lock the mutex LOCK_event_arrays or not in case it
+ has been already locked outside
+ is_drop - if an event is currently being executed then we can also delete
+ the event_timed instance, so we alarm the event that it should
+ drop itself if this parameter is set to TRUE. It's false on
+ ALTER EVENT.
+
+ RETURNS
+ 0 - OK (always)
+*/
+
static int
-evex_remove_from_cache(LEX_STRING *db, LEX_STRING *name, bool use_lock)
+evex_remove_from_cache(LEX_STRING *db, LEX_STRING *name, bool use_lock,
+ bool is_drop)
{
+ //ToDo : Add definer to the tuple (db, name) to become triple
uint i;
DBUG_ENTER("evex_remove_from_cache");
@@ -695,16 +1110,20 @@ evex_remove_from_cache(LEX_STRING *db, LEX_STRING *name, bool use_lock)
if (!sortcmp_lex_string(*name, et->name, system_charset_info) &&
!sortcmp_lex_string(*db, et->dbname, system_charset_info))
{
- if (!et->is_running())
+ if (et->can_spawn_now())
{
+ DBUG_PRINT("evex_remove_from_cache", ("not running - free and delete"));
et->free_sp();
delete et;
}
else
{
+ DBUG_PRINT("evex_remove_from_cache",
+ ("running.defer mem free. is_drop=%d", is_drop));
et->flags|= EVENT_EXEC_NO_MORE;
- et->dropped= true;
+ et->dropped= is_drop;
}
+ DBUG_PRINT("evex_remove_from_cache", ("delete from queue"));
evex_queue_delete_element(&EVEX_EQ_NAME, i);
// ok, we have cleaned
goto done;
@@ -719,8 +1138,6 @@ done:
}
-
-
/*
The function exported to the world for creating of events.
@@ -756,7 +1173,7 @@ evex_create_event(THD *thd, event_timed *et, uint create_options,
if (evex_is_running && et->status == MYSQL_EVENT_ENABLED)
{
sp_name spn(et->dbname, et->name);
- ret= evex_load_and_compile_event(thd, &spn, true);
+ ret= evex_load_and_compile_event(thd, &spn, et->definer, true);
}
VOID(pthread_mutex_unlock(&LOCK_evex_running));
@@ -778,8 +1195,8 @@ done:
NOTES
et contains data about dbname and event name.
- name is the new name of the event, if not null this means
- that RENAME TO was specified in the query.
+ new_name is the new name of the event, if not null (this means
+ that RENAME TO was specified in the query)
*/
int
@@ -805,15 +1222,15 @@ evex_update_event(THD *thd, event_timed *et, sp_name *new_name,
UNLOCK_MUTEX_AND_BAIL_OUT(LOCK_evex_running, done);
VOID(pthread_mutex_lock(&LOCK_event_arrays));
- evex_remove_from_cache(&et->dbname, &et->name, false);
+ evex_remove_from_cache(&et->dbname, &et->name, false, false);
if (et->status == MYSQL_EVENT_ENABLED)
{
if (new_name)
- ret= evex_load_and_compile_event(thd, new_name, false);
+ ret= evex_load_and_compile_event(thd, new_name, et->definer, false);
else
{
sp_name spn(et->dbname, et->name);
- ret= evex_load_and_compile_event(thd, &spn, false);
+ ret= evex_load_and_compile_event(thd, &spn, et->definer, false);
}
if (ret == EVEX_COMPILE_ERROR)
my_error(ER_EVENT_COMPILE_ERROR, MYF(0));
@@ -830,28 +1247,32 @@ done:
Drops an event
SYNOPSIS
- evex_drop_event()
+ db_drop_event()
thd THD
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
-evex_drop_event(THD *thd, event_timed *et, bool drop_if_exists,
- uint *rows_affected)
+int db_drop_event(THD *thd, event_timed *et, bool drop_if_exists,
+ uint *rows_affected)
{
TABLE *table;
- int ret= EVEX_OPEN_TABLE_FAILED;
- DBUG_ENTER("evex_drop_event");
+ Open_tables_state backup;
+ int ret;
+
+ DBUG_ENTER("db_drop_event");
+ ret= EVEX_OPEN_TABLE_FAILED;
+ thd->reset_n_backup_open_tables_state(&backup);
if (evex_open_event_table(thd, TL_WRITE, &table))
{
my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
goto done;
}
- if (!(ret= evex_db_find_event_aux(thd, et->dbname, et->name, table)))
+ if (!(ret= evex_db_find_event_aux(thd, et, table)))
{
if ((ret= table->file->ha_delete_row(table->record[0])))
{
@@ -872,10 +1293,6 @@ evex_drop_event(THD *thd, event_timed *et, bool drop_if_exists,
goto done;
}
- VOID(pthread_mutex_lock(&LOCK_evex_running));
- if (evex_is_running)
- ret= evex_remove_from_cache(&et->dbname, &et->name, true);
- VOID(pthread_mutex_unlock(&LOCK_evex_running));
done:
/*
@@ -883,7 +1300,301 @@ done:
we have to close our thread tables.
*/
close_thread_tables(thd);
+ thd->restore_backup_open_tables_state(&backup);
+ DBUG_RETURN(ret);
+}
+
+
+/*
+ Drops an event
+
+ SYNOPSIS
+ evex_drop_event()
+ thd THD
+ 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
+evex_drop_event(THD *thd, event_timed *et, bool drop_if_exists,
+ uint *rows_affected)
+{
+ TABLE *table;
+ int ret= 0;
+
+ DBUG_ENTER("evex_drop_event");
+
+
+ VOID(pthread_mutex_lock(&LOCK_evex_running));
+ if (evex_is_running)
+ ret= evex_remove_from_cache(&et->dbname, &et->name, true, true);
+ VOID(pthread_mutex_unlock(&LOCK_evex_running));
+
+ if (ret == 1)
+ ret= 0;
+ else if (ret == 0)
+ ret= db_drop_event(thd, et, drop_if_exists, rows_affected);
+ else
+ my_error(ER_UNKNOWN_ERROR, MYF(0));
DBUG_RETURN(ret);
}
+
+/*
+ SHOW CREATE EVENT
+
+ SYNOPSIS
+ evex_show_create_event()
+ thd THD
+ spn the name of the event (db, name)
+ definer the definer of the event
+
+ RETURNS
+ 0 - OK
+ 1 - Error during writing to the wire
+
+*/
+
+int
+evex_show_create_event(THD *thd, sp_name *spn, LEX_STRING definer)
+{
+ int ret;
+ event_timed *et= NULL;
+ Open_tables_state backup;
+
+ DBUG_ENTER("evex_update_event");
+ DBUG_PRINT("enter", ("name: %*s", spn->m_name.length, spn->m_name.str));
+
+ thd->reset_n_backup_open_tables_state(&backup);
+ ret= db_find_event(thd, spn, &definer, &et, NULL, thd->mem_root);
+ thd->restore_backup_open_tables_state(&backup);
+
+ if (!ret && et)
+ {
+ Protocol *protocol= thd->protocol;
+ char show_str_buf[768];
+ String show_str(show_str_buf, sizeof(show_str_buf), system_charset_info);
+ List<Item> field_list;
+ const char *sql_mode_str;
+ ulong sql_mode_len=0;
+
+ show_str.length(0);
+ show_str.set_charset(system_charset_info);
+
+ if (et->get_create_event(thd, &show_str))
+ DBUG_RETURN(1);
+
+ field_list.push_back(new Item_empty_string("Event", NAME_LEN));
+
+ sql_mode_str=
+ sys_var_thd_sql_mode::symbolic_mode_representation(thd, et->sql_mode,
+ &sql_mode_len);
+
+ field_list.push_back(new Item_empty_string("sql_mode", sql_mode_len));
+
+ field_list.push_back(new Item_empty_string("Create Event",
+ show_str.length()));
+ if (protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS |
+ Protocol::SEND_EOF))
+ DBUG_RETURN(1);
+
+ protocol->prepare_for_resend();
+ protocol->store(et->name.str, et->name.length, system_charset_info);
+
+ 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);
+ }
+
+ DBUG_RETURN(ret);
+}
+
+
+/*
+ evex_drop_db_events - Drops all events in the selected database
+
+ thd - Thread
+ db - ASCIIZ the name of the database
+
+ Returns:
+ 0 - OK
+ 1 - Failed to delete a specific row
+ 2 - Got NULL while reading db name from a row
+
+ Note:
+ The algo is the following
+ 1. Go through the in-memory cache, if the scheduler is working
+ and for every event whose dbname matches the database we drop
+ check whether is currently in execution:
+ - event_timed::can_spawn() returns true -> the event is not
+ being executed in a child thread. The reason not to use
+ event_timed::is_running() is that the latter shows only if
+ it is being executed, which is 99% of the time in the thread
+ but there are some initiliazations before and after the
+ anonymous SP is being called. So if we delete in this moment
+ -=> *boom*, so we have to check whether the thread has been
+ 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
+evex_drop_db_events(THD *thd, char *db)
+{
+ TABLE *table;
+ READ_RECORD read_record_info;
+ MYSQL_LOCK *lock;
+ int ret= 0;
+ uint i;
+ LEX_STRING db_lex= {db, strlen(db)};
+
+ DBUG_ENTER("evex_drop_db_events");
+ DBUG_PRINT("info",("dropping events from %s", db));
+
+
+ VOID(pthread_mutex_lock(&LOCK_event_arrays));
+
+ if ((ret= evex_open_event_table(thd, TL_WRITE, &table)))
+ {
+ sql_print_error("Table mysql.event is damaged.");
+ VOID(pthread_mutex_unlock(&LOCK_event_arrays));
+ DBUG_RETURN(SP_OPEN_TABLE_FAILED);
+ }
+
+ DBUG_PRINT("info",("%d elements in the queue",
+ evex_queue_num_elements(EVEX_EQ_NAME)));
+ VOID(pthread_mutex_lock(&LOCK_evex_running));
+ if (!evex_is_running)
+ goto skip_memory;
+
+ for (i= 0; i < evex_queue_num_elements(EVEX_EQ_NAME); ++i)
+ {
+ event_timed *et= evex_queue_element(&EVEX_EQ_NAME, i, event_timed*);
+ if (sortcmp_lex_string(et->dbname, db_lex, system_charset_info))
+ continue;
+
+ if (et->can_spawn_now_n_lock(thd))
+ {
+ DBUG_PRINT("info",("event %s not running - direct delete", et->name.str));
+ if (!(ret= evex_db_find_event_aux(thd, et, table)))
+ {
+ DBUG_PRINT("info",("event %s found on disk", et->name.str));
+ if ((ret= table->file->ha_delete_row(table->record[0])))
+ {
+ sql_print_error("Error while deleting a row - dropping "
+ "a database. Skipping the rest.");
+ my_error(ER_EVENT_DROP_FAILED, MYF(0), et->name.str);
+ goto end;
+ }
+ DBUG_PRINT("info",("deleted event [%s] num [%d]. Time to free mem",
+ et->name.str, i));
+ }
+ else if (ret == EVEX_KEY_NOT_FOUND)
+ {
+ sql_print_error("Expected to find event %s.%s of %s on disk-not there.",
+ et->dbname.str, et->name.str, et->definer.str);
+ }
+ et->free_sp();
+ delete et;
+ et= 0;
+ /* no need to call et->spawn_unlock because we already cleaned et */
+ }
+ else
+ {
+ DBUG_PRINT("info",("event %s is running. setting exec_no_more and dropped",
+ et->name.str));
+ et->flags|= EVENT_EXEC_NO_MORE;
+ et->dropped= TRUE;
+ }
+ 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
+ DBUG_PRINT("info",("%d elements in the queue",
+ evex_queue_num_elements(EVEX_EQ_NAME)));
+ /*
+ decrease so we start at the same position, there will be
+ less elements in the queue, it will still be ordered so on
+ next iteration it will be again i the current element or if
+ no more we finish.
+ */
+ --i;
+ }
+
+skip_memory:
+ /*
+ The reasoning behind having two loops is the following:
+ If there was only one loop, the table-scan, then for every element which
+ matches, the queue in memory has to be searched to remove the element.
+ While if we go first over the queue and remove what's in there we have only
+ one pass over it and after finishing it, moving to table-scan for the disabled
+ events. This needs quite less time and means quite less locking on
+ LOCK_event_arrays.
+ */
+ DBUG_PRINT("info",("Mem-cache checked, now going to db for disabled events"));
+ /* only enabled events are in memory, so we go now and delete the rest */
+ init_read_record(&read_record_info, thd, table ,NULL,1,0);
+ while (!(read_record_info.read_record(&read_record_info)) && !ret)
+ {
+ char *et_db;
+
+ if ((et_db= get_field(thd->mem_root, table->field[EVEX_FIELD_DB])) == NULL)
+ {
+ ret= 2;
+ break;
+ }
+
+ LEX_STRING et_db_lex= {et_db, strlen(et_db)};
+ if (!sortcmp_lex_string(et_db_lex, db_lex, system_charset_info))
+ {
+ event_timed ett;
+ char *ptr;
+
+ if ((ptr= get_field(thd->mem_root, table->field[EVEX_FIELD_STATUS]))
+ == NullS)
+ {
+ sql_print_error("Error while loading from mysql.event. "
+ "Table probably corrupted");
+ goto end;
+ }
+ /*
+ When not running nothing is in memory so we have to clean
+ everything.
+ We don't delete EVENT_ENABLED events when the scheduler is running
+ because maybe this is an event which we asked to drop itself when
+ it is finished and it hasn't finished yet, so we don't touch it.
+ It will drop itself. The not running ENABLED events has been already
+ deleted from ha_delete_row() above in the loop over the QUEUE
+ (in case the executor is running).
+ 'D' stands for DISABLED, 'E' for ENABLED - it's an enum
+ */
+ if ((evex_is_running && ptr[0] == 'D') || !evex_is_running)
+ {
+ DBUG_PRINT("info", ("Dropping %s.%s", et_db, ett.name.str));
+ if ((ret= table->file->ha_delete_row(table->record[0])))
+ {
+ my_error(ER_EVENT_DROP_FAILED, MYF(0), ett.name.str);
+ goto end;
+ }
+ }
+ }
+ }
+ DBUG_PRINT("info",("Disk checked for disabled events. Finishing."));
+
+end:
+ VOID(pthread_mutex_unlock(&LOCK_evex_running));
+ VOID(pthread_mutex_unlock(&LOCK_event_arrays));
+ end_read_record(&read_record_info);
+
+ thd->version--; // Force close to free memory
+
+ close_thread_tables(thd);
+
+ DBUG_RETURN(ret);
+}