diff options
Diffstat (limited to 'sql/event.cc')
-rw-r--r-- | sql/event.cc | 839 |
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); +} |