diff options
author | unknown <andrey@lmy004.> | 2006-06-28 01:28:03 +0200 |
---|---|---|
committer | unknown <andrey@lmy004.> | 2006-06-28 01:28:03 +0200 |
commit | acefb78bc3fbf28376d8713e1dc9f056dc3cdbf6 (patch) | |
tree | bf91f4ab6e97d25e3da5172541a1f76237dccfe4 | |
parent | cace147c63a03b4dc9bd3e55ae3bda4f5e9db97b (diff) | |
download | mariadb-git-acefb78bc3fbf28376d8713e1dc9f056dc3cdbf6.tar.gz |
WL#3337 (Events new architecture)
5th cut, moved DB related code to Event_db_repository and
updated accordingly the remanining code.
Moved change/restore_security_context() to class THD
Removed events_priv.h
Next step is to reorganize create/update_event() and parsing for them.
But probably some other refactoring could be done in the meanwhile.
The changes so far pass the test suite.
BitKeeper/deleted/.del-events_priv.h~2e8bce2cf35997df:
Delete: sql/events_priv.h
sql/Makefile.am:
events_priv.h is no more
sql/event_data_objects.cc:
reorganize events code
sql/event_data_objects.h:
reorganize events code
sql/event_db_repository.cc:
reorganize events code
sql/event_db_repository.h:
reorganize events code
sql/event_scheduler.cc:
reorganize events code
sql/event_scheduler.h:
reorganize events code
sql/events.cc:
reorganize events code
sql/events.h:
reorganize events code
sql/mysqld.cc:
reorganize events code
sql/set_var.cc:
reorganize events code
sql/sql_class.cc:
add ::change_security_context() and restore_security_context()
sql/sql_class.h:
add ::change_security_context() and restore_security_context()
sql/sql_db.cc:
reorganize Events code
sql/sql_parse.cc:
reorganize Events code
sql/sql_show.cc:
reorganize Events code
-rw-r--r-- | sql/Makefile.am | 2 | ||||
-rw-r--r-- | sql/event_data_objects.cc | 88 | ||||
-rw-r--r-- | sql/event_data_objects.h | 32 | ||||
-rw-r--r-- | sql/event_db_repository.cc | 854 | ||||
-rw-r--r-- | sql/event_db_repository.h | 82 | ||||
-rw-r--r-- | sql/event_scheduler.cc | 91 | ||||
-rw-r--r-- | sql/event_scheduler.h | 37 | ||||
-rw-r--r-- | sql/events.cc | 812 | ||||
-rw-r--r-- | sql/events.h | 95 | ||||
-rw-r--r-- | sql/events_priv.h | 94 | ||||
-rw-r--r-- | sql/mysqld.cc | 2 | ||||
-rw-r--r-- | sql/set_var.cc | 2 | ||||
-rw-r--r-- | sql/sql_class.cc | 57 | ||||
-rw-r--r-- | sql/sql_class.h | 8 | ||||
-rw-r--r-- | sql/sql_db.cc | 2 | ||||
-rw-r--r-- | sql/sql_parse.cc | 20 | ||||
-rw-r--r-- | sql/sql_show.cc | 4 |
17 files changed, 1210 insertions, 1072 deletions
diff --git a/sql/Makefile.am b/sql/Makefile.am index 29645302ecc..49f85b3921d 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -64,7 +64,7 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \ tztime.h my_decimal.h\ sp_head.h sp_pcontext.h sp_rcontext.h sp.h sp_cache.h \ parse_file.h sql_view.h sql_trigger.h \ - sql_array.h sql_cursor.h events.h events_priv.h \ + sql_array.h sql_cursor.h events.h \ sql_plugin.h authors.h sql_partition.h event_data_objects.h \ event_queue.h event_db_repository.h \ partition_info.h partition_element.h event_scheduler.h \ diff --git a/sql/event_data_objects.cc b/sql/event_data_objects.cc index 71739bf0f1c..4b72c7e55a7 100644 --- a/sql/event_data_objects.cc +++ b/sql/event_data_objects.cc @@ -16,12 +16,15 @@ #define MYSQL_LEX 1 #include "mysql_priv.h" -#include "events_priv.h" #include "events.h" #include "event_data_objects.h" +#include "event_db_repository.h" #include "sp_head.h" +#define EVEX_MAX_INTERVAL_VALUE 2147483647L + + Event_parse_data * Event_parse_data::new_instance(THD *thd) { @@ -733,29 +736,29 @@ Event_timed::load_from_row(MEM_ROOT *mem_root, TABLE *table) et= this; - if (table->s->fields != Events::FIELD_COUNT) + if (table->s->fields != ET_FIELD_COUNT) goto error; if ((et->dbname.str= get_field(mem_root, - table->field[Events::FIELD_DB])) == NULL) + table->field[ET_FIELD_DB])) == NULL) goto error; et->dbname.length= strlen(et->dbname.str); if ((et->name.str= get_field(mem_root, - table->field[Events::FIELD_NAME])) == NULL) + table->field[ET_FIELD_NAME])) == NULL) goto error; et->name.length= strlen(et->name.str); if ((et->body.str= get_field(mem_root, - table->field[Events::FIELD_BODY])) == NULL) + table->field[ET_FIELD_BODY])) == NULL) goto error; et->body.length= strlen(et->body.str); if ((et->definer.str= get_field(mem_root, - table->field[Events::FIELD_DEFINER])) == NullS) + table->field[ET_FIELD_DEFINER])) == NullS) goto error; et->definer.length= strlen(et->definer.str); @@ -772,27 +775,27 @@ Event_timed::load_from_row(MEM_ROOT *mem_root, TABLE *table) et->definer_host.str= strmake_root(mem_root, ptr + 1, len);/* 1:because of @*/ et->definer_host.length= len; - et->starts_null= table->field[Events::FIELD_STARTS]->is_null(); - res1= table->field[Events::FIELD_STARTS]-> + et->starts_null= table->field[ET_FIELD_STARTS]->is_null(); + res1= table->field[ET_FIELD_STARTS]-> get_date(&et->starts,TIME_NO_ZERO_DATE); - et->ends_null= table->field[Events::FIELD_ENDS]->is_null(); - res2= table->field[Events::FIELD_ENDS]->get_date(&et->ends, TIME_NO_ZERO_DATE); + et->ends_null= table->field[ET_FIELD_ENDS]->is_null(); + res2= table->field[ET_FIELD_ENDS]->get_date(&et->ends, TIME_NO_ZERO_DATE); - if (!table->field[Events::FIELD_INTERVAL_EXPR]->is_null()) - et->expression= table->field[Events::FIELD_INTERVAL_EXPR]->val_int(); + if (!table->field[ET_FIELD_INTERVAL_EXPR]->is_null()) + et->expression= table->field[ET_FIELD_INTERVAL_EXPR]->val_int(); else et->expression= 0; /* If res1 and res2 are true then both fields are empty. - Hence if Events::FIELD_EXECUTE_AT is empty there is an error. + Hence if ET_FIELD_EXECUTE_AT is empty there is an error. */ et->execute_at_null= - table->field[Events::FIELD_EXECUTE_AT]->is_null(); + table->field[ET_FIELD_EXECUTE_AT]->is_null(); DBUG_ASSERT(!(et->starts_null && et->ends_null && !et->expression && et->execute_at_null)); if (!et->expression && - table->field[Events::FIELD_EXECUTE_AT]-> get_date(&et->execute_at, + table->field[ET_FIELD_EXECUTE_AT]-> get_date(&et->execute_at, TIME_NO_ZERO_DATE)) goto error; @@ -800,22 +803,22 @@ Event_timed::load_from_row(MEM_ROOT *mem_root, TABLE *table) In DB the values start from 1 but enum interval_type starts from 0 */ - if (!table->field[Events::FIELD_TRANSIENT_INTERVAL]->is_null()) + if (!table->field[ET_FIELD_TRANSIENT_INTERVAL]->is_null()) et->interval= (interval_type) ((ulonglong) - table->field[Events::FIELD_TRANSIENT_INTERVAL]->val_int() - 1); + table->field[ET_FIELD_TRANSIENT_INTERVAL]->val_int() - 1); else et->interval= (interval_type) 0; - et->created= table->field[Events::FIELD_CREATED]->val_int(); - et->modified= table->field[Events::FIELD_MODIFIED]->val_int(); + et->created= table->field[ET_FIELD_CREATED]->val_int(); + et->modified= table->field[ET_FIELD_MODIFIED]->val_int(); - table->field[Events::FIELD_LAST_EXECUTED]-> + table->field[ET_FIELD_LAST_EXECUTED]-> get_date(&et->last_executed, TIME_NO_ZERO_DATE); last_executed_changed= false; /* ToDo : Andrey . Find a way not to allocate ptr on event_mem_root */ - if ((ptr= get_field(mem_root, table->field[Events::FIELD_STATUS])) == NullS) + if ((ptr= get_field(mem_root, table->field[ET_FIELD_STATUS])) == NullS) goto error; DBUG_PRINT("load_from_row", ("Event [%s] is [%s]", et->name.str, ptr)); @@ -823,20 +826,20 @@ Event_timed::load_from_row(MEM_ROOT *mem_root, TABLE *table) /* ToDo : Andrey . Find a way not to allocate ptr on event_mem_root */ if ((ptr= get_field(mem_root, - table->field[Events::FIELD_ON_COMPLETION])) == NullS) + table->field[ET_FIELD_ON_COMPLETION])) == NullS) goto error; et->on_completion= (ptr[0]=='D'? Event_timed::ON_COMPLETION_DROP: Event_timed::ON_COMPLETION_PRESERVE); - et->comment.str= get_field(mem_root, table->field[Events::FIELD_COMMENT]); + et->comment.str= get_field(mem_root, table->field[ET_FIELD_COMMENT]); if (et->comment.str != NullS) et->comment.length= strlen(et->comment.str); else et->comment.length= 0; - et->sql_mode= (ulong) table->field[Events::FIELD_SQL_MODE]->val_int(); + et->sql_mode= (ulong) table->field[ET_FIELD_SQL_MODE]->val_int(); DBUG_RETURN(0); error: @@ -1277,6 +1280,7 @@ Event_timed::mark_last_executed(THD *thd) } + /* Drops the event @@ -1299,7 +1303,8 @@ Event_timed::drop(THD *thd) uint tmp= 0; DBUG_ENTER("Event_timed::drop"); - DBUG_RETURN(db_drop_event(thd, dbname, name, false, &tmp)); + DBUG_RETURN(Events::get_instance()-> + db_repository.drop_event(thd, dbname, name, false, &tmp)); } @@ -1336,7 +1341,7 @@ Event_timed::update_fields(THD *thd) thd->reset_n_backup_open_tables_state(&backup); - if (Events::open_event_table(thd, TL_WRITE, &table)) + if (Events::get_instance()->open_event_table(thd, TL_WRITE, &table)) { ret= EVEX_OPEN_TABLE_FAILED; goto done; @@ -1352,15 +1357,15 @@ Event_timed::update_fields(THD *thd) if (last_executed_changed) { - table->field[Events::FIELD_LAST_EXECUTED]->set_notnull(); - table->field[Events::FIELD_LAST_EXECUTED]->store_time(&last_executed, + table->field[ET_FIELD_LAST_EXECUTED]->set_notnull(); + table->field[ET_FIELD_LAST_EXECUTED]->store_time(&last_executed, MYSQL_TIMESTAMP_DATETIME); last_executed_changed= false; } if (status_changed) { - table->field[Events::FIELD_STATUS]->set_notnull(); - table->field[Events::FIELD_STATUS]->store((longlong)status, true); + table->field[ET_FIELD_STATUS]->set_notnull(); + table->field[ET_FIELD_STATUS]->store((longlong)status, true); status_changed= false; } @@ -1630,8 +1635,8 @@ Event_timed::compile(THD *thd, MEM_ROOT *mem_root) thd->query_length= show_create.length(); DBUG_PRINT("info", ("query:%s",thd->query)); - change_security_context(thd, definer_user, definer_host, dbname, - &security_ctx, &save_ctx); + thd->change_security_context(definer_user, definer_host, dbname, + &security_ctx, &save_ctx); thd->lex= &lex; lex_start(thd, (uchar*)thd->query, thd->query_length); lex.et_compile_phase= TRUE; @@ -1669,7 +1674,7 @@ done: lex.et->deinit_mutexes(); lex_end(&lex); - restore_security_context(thd, save_ctx); + thd->restore_security_context(save_ctx); DBUG_PRINT("note", ("return old data on its place. set back NAMES")); thd->lex= old_lex; @@ -1870,23 +1875,6 @@ event_timed_db_equal(Event_timed *et, LEX_STRING *db) } -/* - Checks whether two events have the same definer - - SYNOPSIS - event_timed_definer_equal() - - Returns - TRUE definers are equal - FALSE definers are not equal -*/ - -bool -event_timed_definer_equal(Event_timed *et, LEX_STRING *definer) -{ - return !sortcmp_lex_string(et->definer, *definer, system_charset_info); -} - /* Checks whether two events are equal by identifiers diff --git a/sql/event_data_objects.h b/sql/event_data_objects.h index a9483465a8d..ff547cebd5b 100644 --- a/sql/event_data_objects.h +++ b/sql/event_data_objects.h @@ -40,9 +40,35 @@ #define EVENT_FREE_WHEN_FINISHED (1L << 2) +#define EVENT_EXEC_STARTED 0 +#define EVENT_EXEC_ALREADY_EXEC 1 +#define EVENT_EXEC_CANT_FORK 2 + + class sp_head; class Sql_alloc; +class Event_timed; + +/* Compares only the schema part of the identifier */ +bool +event_timed_db_equal(Event_timed *et, LEX_STRING *db); + + +/* Compares the whole identifier*/ +bool +event_timed_identifier_equal(Event_timed *a, Event_timed *b); + +/* Compares only the schema part of the identifier */ +bool +event_timed_db_equal(sp_name *name, LEX_STRING *db); + + +/* Compares the whole identifier*/ +bool +event_timed_identifier_equal(sp_name *a, Event_timed *b); + + class Event_timed { Event_timed(const Event_timed &); /* Prevent use of these */ @@ -296,4 +322,10 @@ public: }; + +class Event_queue_element : public Event_timed +{ + +}; + #endif /* _EVENT_DATA_OBJECTS_H_ */ diff --git a/sql/event_db_repository.cc b/sql/event_db_repository.cc index 547e1e6887b..249910c5f50 100644 --- a/sql/event_db_repository.cc +++ b/sql/event_db_repository.cc @@ -17,3 +17,857 @@ #include "mysql_priv.h" #include "event_db_repository.h" #include "event_data_objects.h" +#include "sp_head.h" +#include "sp.h" +#include "events.h" + +#define EVEX_DB_FIELD_LEN 64 +#define EVEX_NAME_FIELD_LEN 64 + + +time_t mysql_event_last_create_time= 0L; + +static +TABLE_FIELD_W_TYPE event_table_fields[ET_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")}, + {NULL, 0} + }, + { + {(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")} + } +}; + + +/* + Puts some data common to CREATE and ALTER EVENT into a row. + + SYNOPSIS + evex_fill_row() + thd THD + table the row to fill out + et Event's data + + RETURN VALUE + 0 - OK + EVEX_GENERAL_ERROR - bad data + EVEX_GET_FIELD_FAILED - field count does not match. table corrupted? + + DESCRIPTION + Used both when an event is created and when it is altered. +*/ + +static int +evex_fill_row(THD *thd, TABLE *table, Event_timed *et, my_bool is_update) +{ + CHARSET_INFO *scs= system_charset_info; + enum enum_events_table_field field_num; + + DBUG_ENTER("evex_fill_row"); + + 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= ET_FIELD_DEFINER]-> + store(et->definer.str, et->definer.length, scs)) + goto err_truncate; + + if (table->field[field_num= ET_FIELD_DB]-> + store(et->dbname.str, et->dbname.length, scs)) + goto err_truncate; + + if (table->field[field_num= ET_FIELD_NAME]-> + store(et->name.str, et->name.length, scs)) + goto err_truncate; + + /* both ON_COMPLETION and STATUS are NOT NULL thus not calling set_notnull()*/ + table->field[ET_FIELD_ON_COMPLETION]-> + store((longlong)et->on_completion, true); + + table->field[ET_FIELD_STATUS]->store((longlong)et->status, true); + + /* + Change the SQL_MODE only if body was present in an ALTER EVENT and of course + always during CREATE EVENT. + */ + if (et->body.str) + { + table->field[ET_FIELD_SQL_MODE]-> + store((longlong)thd->variables.sql_mode, true); + + if (table->field[field_num= ET_FIELD_BODY]-> + store(et->body.str, et->body.length, scs)) + goto err_truncate; + } + + if (et->expression) + { + table->field[ET_FIELD_INTERVAL_EXPR]->set_notnull(); + table->field[ET_FIELD_INTERVAL_EXPR]-> + store((longlong)et->expression, true); + + table->field[ET_FIELD_TRANSIENT_INTERVAL]->set_notnull(); + /* + In the enum (C) intervals start from 0 but in mysql enum valid values start + from 1. Thus +1 offset is needed! + */ + table->field[ET_FIELD_TRANSIENT_INTERVAL]-> + store((longlong)et->interval+1, true); + + table->field[ET_FIELD_EXECUTE_AT]->set_null(); + + if (!et->starts_null) + { + table->field[ET_FIELD_STARTS]->set_notnull(); + table->field[ET_FIELD_STARTS]-> + store_time(&et->starts, MYSQL_TIMESTAMP_DATETIME); + } + + if (!et->ends_null) + { + table->field[ET_FIELD_ENDS]->set_notnull(); + table->field[ET_FIELD_ENDS]-> + store_time(&et->ends, MYSQL_TIMESTAMP_DATETIME); + } + } + else if (et->execute_at.year) + { + table->field[ET_FIELD_INTERVAL_EXPR]->set_null(); + table->field[ET_FIELD_TRANSIENT_INTERVAL]->set_null(); + table->field[ET_FIELD_STARTS]->set_null(); + table->field[ET_FIELD_ENDS]->set_null(); + + table->field[ET_FIELD_EXECUTE_AT]->set_notnull(); + table->field[ET_FIELD_EXECUTE_AT]-> + store_time(&et->execute_at, MYSQL_TIMESTAMP_DATETIME); + } + else + { + DBUG_ASSERT(is_update); + /* + it is normal to be here when the action is update + this is an error if the action is create. something is borked + */ + } + + ((Field_timestamp *)table->field[ET_FIELD_MODIFIED])->set_time(); + + if (et->comment.str) + { + if (table->field[field_num= ET_FIELD_COMMENT]-> + store(et->comment.str, et->comment.length, scs)) + goto err_truncate; + } + + DBUG_RETURN(0); +err_truncate: + my_error(ER_EVENT_DATA_TOO_LONG, MYF(0), table->field[field_num]->field_name); + DBUG_RETURN(EVEX_GENERAL_ERROR); +} + + +/* + 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. + + RETURN VALUE + 0 - Routine found + EVEX_KEY_NOT_FOUND - No routine with given name +*/ + +int +evex_db_find_event_by_name(THD *thd, const LEX_STRING dbname, + const LEX_STRING ev_name, + TABLE *table) +{ + return Events::get_instance()->db_repository. + find_event_by_name(thd, dbname, ev_name, table); +} + + +/* + 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 + + RETURN VALUE + 0 ok In this case *ett is set to the event + # error *ett == 0 +*/ + +int +Event_db_repository::find_event(THD *thd, sp_name *name, Event_timed **ett, + TABLE *tbl, MEM_ROOT *root) +{ + TABLE *table; + int ret; + Event_timed *et= NULL; + DBUG_ENTER("db_find_event"); + DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str)); + + if (tbl) + table= tbl; + else if (Events::get_instance()->db_repository.open_event_table(thd, TL_READ, &table)) + { + my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0)); + ret= EVEX_GENERAL_ERROR; + goto done; + } + + if ((ret= evex_db_find_event_by_name(thd, name->m_db, name->m_name, table))) + { + my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name->m_name.str); + goto done; + } + et= new Event_timed; + + /* + 1)The table should not be closed beforehand. ::load_from_row() only loads + and does not compile + + 2)::load_from_row() is silent on error therefore we emit error msg here + */ + if ((ret= et->load_from_row(root, table))) + { + my_error(ER_CANNOT_LOAD_FROM_TABLE, MYF(0)); + goto done; + } + +done: + if (ret) + { + delete et; + et= 0; + } + /* don't close the table if we haven't opened it ourselves */ + if (!tbl && table) + close_thread_tables(thd); + *ett= et; + DBUG_RETURN(ret); +} + + +int +Event_db_repository::init_repository() +{ + init_alloc_root(&repo_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC); + return 0; +} + + +void +Event_db_repository::deinit_repository() +{ + free_root(&repo_root, MYF(0)); +} + + +/* + Open mysql.event table for read + + SYNOPSIS + Events::open_event_table() + thd Thread context + lock_type How to lock the table + table We will store the open table here + + RETURN VALUE + 1 Cannot lock table + 2 The table is corrupted - different number of fields + 0 OK +*/ + +int +Event_db_repository::open_event_table(THD *thd, enum thr_lock_type lock_type, + TABLE **table) +{ + TABLE_LIST tables; + DBUG_ENTER("Event_db_repository::open_event_table"); + + bzero((char*) &tables, sizeof(tables)); + tables.db= (char*) "mysql"; + tables.table_name= tables.alias= (char*) "event"; + tables.lock_type= lock_type; + + if (simple_open_n_lock_tables(thd, &tables)) + DBUG_RETURN(1); + + if (table_check_intact(tables.table, ET_FIELD_COUNT, + event_table_fields, + &mysql_event_last_create_time, + ER_CANNOT_LOAD_FROM_TABLE)) + { + close_thread_tables(thd); + DBUG_RETURN(2); + } + *table= tables.table; + tables.table->use_all_columns(); + DBUG_RETURN(0); +} + + +/* + Creates an event in mysql.event + + SYNOPSIS + Event_db_repository::create_event() + thd THD + et Event_timed object containing information for the event + create_if_not If an warning should be generated in case event exists + rows_affected How many rows were affected + + RETURN VALUE + 0 - OK + EVEX_GENERAL_ERROR - Failure + + DESCRIPTION + Creates an event. Relies on evex_fill_row which is shared with + db_update_event. The name of the event is inside "et". +*/ + +int +Event_db_repository::create_event(THD *thd, Event_timed *et, + my_bool create_if_not, uint *rows_affected) +{ + int ret= 0; + CHARSET_INFO *scs= system_charset_info; + TABLE *table; + char olddb[128]; + bool dbchanged= false; + DBUG_ENTER("Event_db_repository::create_event"); + DBUG_PRINT("enter", ("name: %.*s", et->name.length, et->name.str)); + + *rows_affected= 0; + DBUG_PRINT("info", ("open mysql.event for update")); + if (open_event_table(thd, TL_WRITE, &table)) + { + 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_by_name(thd, et->dbname, et->name, table)) + { + 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")); + if ((ret= sp_use_new_db(thd, et->dbname.str,olddb, sizeof(olddb),0, + &dbchanged))) + { + my_error(ER_BAD_DB_ERROR, MYF(0)); + goto err; + } + + restore_record(table, s->default_values); // Get default values for fields + + if (system_charset_info->cset->numchars(system_charset_info, et->dbname.str, + et->dbname.str + et->dbname.length) + > EVEX_DB_FIELD_LEN) + { + my_error(ER_TOO_LONG_IDENT, MYF(0), et->dbname.str); + goto err; + } + if (system_charset_info->cset->numchars(system_charset_info, et->name.str, + et->name.str + et->name.length) + > EVEX_DB_FIELD_LEN) + { + my_error(ER_TOO_LONG_IDENT, MYF(0), et->name.str); + goto err; + } + + if (et->body.length > table->field[ET_FIELD_BODY]->field_length) + { + my_error(ER_TOO_LONG_BODY, MYF(0), et->name.str); + goto err; + } + + if (!(et->expression) && !(et->execute_at.year)) + { + DBUG_PRINT("error", ("neither expression nor execute_at are set!")); + my_error(ER_EVENT_NEITHER_M_EXPR_NOR_M_AT, MYF(0)); + goto err; + } + + ((Field_timestamp *)table->field[ET_FIELD_CREATED])->set_time(); + + /* + evex_fill_row() calls my_error() in case of error so no need to + handle it here + */ + if ((ret= evex_fill_row(thd, table, et, false))) + goto err; + + if (table->file->ha_write_row(table->record[0])) + { + my_error(ER_EVENT_STORE_FAILED, MYF(0), et->name.str, ret); + goto err; + } + +#ifdef USE_THIS_CODE_AS_TEMPLATE_WHEN_EVENT_REPLICATION_IS_AGREED + if (mysql_bin_log.is_open()) + { + thd->clear_error(); + /* Such a statement can always go directly to binlog, no trans cache */ + thd->binlog_query(THD::MYSQL_QUERY_TYPE, thd->query, thd->query_length, + FALSE, FALSE); + } +#endif + + *rows_affected= 1; +ok: + if (dbchanged) + (void) mysql_change_db(thd, olddb, 1); + if (table) + close_thread_tables(thd); + DBUG_RETURN(EVEX_OK); + +err: + if (dbchanged) + (void) mysql_change_db(thd, olddb, 1); + if (table) + close_thread_tables(thd); + DBUG_RETURN(EVEX_GENERAL_ERROR); +} + + +/* + Used to execute ALTER EVENT. Pendant to Events::update_event(). + + SYNOPSIS + Event_db_repository::update_event() + thd THD + sp_name the name of the event to alter + et event's data + + RETURN VALUE + 0 OK + EVEX_GENERAL_ERROR Error occured (my_error() called) + + NOTES + sp_name is passed since this is the name of the event to + alter in case of RENAME TO. +*/ + +int +Event_db_repository::update_event(THD *thd, Event_timed *et, sp_name *new_name) +{ + CHARSET_INFO *scs= system_charset_info; + TABLE *table; + int ret= EVEX_OPEN_TABLE_FAILED; + DBUG_ENTER("Event_db_repository::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->definer.length, et->definer.str)); + if (new_name) + DBUG_PRINT("enter", ("rename to: %.*s", new_name->m_name.length, + new_name->m_name.str)); + + if (open_event_table(thd, TL_WRITE, &table)) + { + my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0)); + goto err; + } + + /* first look whether we overwrite */ + if (new_name) + { + if (!sortcmp_lex_string(et->name, new_name->m_name, scs) && + !sortcmp_lex_string(et->dbname, new_name->m_db, scs)) + { + my_error(ER_EVENT_SAME_NAME, MYF(0), et->name.str); + goto err; + } + + if (!evex_db_find_event_by_name(thd,new_name->m_db,new_name->m_name,table)) + { + my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), new_name->m_name.str); + goto err; + } + } + /* + ...and then if there is such an event. Don't exchange the blocks + because you will get error 120 from table handler because new_name will + 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 == find_event_by_name(thd, et->dbname, et->name, table)) + { + my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), et->name.str); + goto err; + } + + store_record(table,record[1]); + + /* Don't update create on row update. */ + table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; + + /* + evex_fill_row() calls my_error() in case of error so no need to + handle it here + */ + if ((ret= evex_fill_row(thd, table, et, true))) + goto err; + + if (new_name) + { + table->field[ET_FIELD_DB]-> + store(new_name->m_db.str, new_name->m_db.length, scs); + table->field[ET_FIELD_NAME]-> + store(new_name->m_name.str, new_name->m_name.length, scs); + } + + if ((ret= table->file->ha_update_row(table->record[1], table->record[0]))) + { + my_error(ER_EVENT_STORE_FAILED, MYF(0), et->name.str, ret); + goto err; + } + + /* close mysql.event or we crash later when loading the event from disk */ + close_thread_tables(thd); + DBUG_RETURN(0); + +err: + if (table) + close_thread_tables(thd); + DBUG_RETURN(EVEX_GENERAL_ERROR); +} + + +/* + Drops an event + + SYNOPSIS + Event_db_repository::drop_event() + thd THD + db database name + name 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 + + RETURN VALUE + 0 OK + !0 Error (my_error() called) +*/ + +int +Event_db_repository::drop_event(THD *thd, LEX_STRING db, LEX_STRING name, + bool drop_if_exists, uint *rows_affected) +{ + TABLE *table; + Open_tables_state backup; + int ret; + + DBUG_ENTER("Event_db_repository::drop_event"); + DBUG_PRINT("enter", ("db=%s name=%s", db.str, name.str)); + ret= EVEX_OPEN_TABLE_FAILED; + + thd->reset_n_backup_open_tables_state(&backup); + if (open_event_table(thd, TL_WRITE, &table)) + { + my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0)); + goto done; + } + + if (!(ret= evex_db_find_event_by_name(thd, db, name, table))) + { + if ((ret= table->file->ha_delete_row(table->record[0]))) + my_error(ER_EVENT_CANNOT_DELETE, MYF(0)); + } + else if (ret == EVEX_KEY_NOT_FOUND) + { + if (drop_if_exists) + { + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST), + "Event", name.str); + ret= 0; + } else + my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name.str); + } + +done: + /* + evex_drop_event() is used by Event_timed::drop therefore + we have to close our thread tables. + */ + close_thread_tables(thd); + thd->restore_backup_open_tables_state(&backup); + DBUG_RETURN(ret); +} + + +int +Event_db_repository::find_event_by_name(THD *thd, LEX_STRING db, + LEX_STRING name, TABLE *table) +{ + byte key[MAX_KEY_LENGTH]; + DBUG_ENTER("Event_db_repository::find_event_by_name"); + DBUG_PRINT("enter", ("name: %.*s", name.length, name.str)); + + /* + Create key to find row. We have to use field->store() to be able to + handle VARCHAR and CHAR fields. + Assumption here is that the two first fields in the table are + 'db' and 'name' and the first key is the primary key over the + same fields. + */ + if (db.length > table->field[ET_FIELD_DB]->field_length || + name.length > table->field[ET_FIELD_NAME]->field_length) + + DBUG_RETURN(EVEX_KEY_NOT_FOUND); + + table->field[ET_FIELD_DB]->store(db.str, db.length, &my_charset_bin); + table->field[ET_FIELD_NAME]->store(name.str, 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, + table->key_info->key_length, + HA_READ_KEY_EXACT)) + { + DBUG_PRINT("info", ("Row not found")); + DBUG_RETURN(EVEX_KEY_NOT_FOUND); + } + + DBUG_PRINT("info", ("Row found!")); + DBUG_RETURN(0); +} + + +int +Event_db_repository::drop_schema_events(THD *thd, LEX_STRING schema) +{ + return drop_events_by_field(thd, ET_FIELD_DB, schema); +} + + +int +Event_db_repository::drop_user_events(THD *thd, LEX_STRING definer) +{ + return drop_events_by_field(thd, ET_FIELD_DEFINER, definer); +} + + +/* + Drops all events in the selected database, from mysql.event. + + SYNOPSIS + drop_events_from_table_by_field() + thd Thread + table mysql.event TABLE + field Which field of the row to use for matching + field_value The value that should match + + RETURN VALUE + 0 OK + !0 Error from ha_delete_row +*/ + +int +Event_db_repository::drop_events_by_field(THD *thd, + enum enum_events_table_field field, + LEX_STRING field_value) +{ + int ret= 0; + TABLE *table; + Open_tables_state backup; + READ_RECORD read_record_info; + DBUG_ENTER("drop_events_from_table_by_field"); + DBUG_PRINT("enter", ("field=%d field_value=%s", field, field_value.str)); + + if (open_event_table(thd, TL_WRITE, &table)) + { + my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0)); + DBUG_RETURN(1); + } + + /* 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 (!ret && !(read_record_info.read_record(&read_record_info)) ) + { + char *et_field= get_field(thd->mem_root, table->field[field]); + + LEX_STRING et_field_lex= {et_field, strlen(et_field)}; + DBUG_PRINT("info", ("Current event %s name=%s", et_field, + get_field(thd->mem_root, table->field[ET_FIELD_NAME]))); + + if (!sortcmp_lex_string(et_field_lex, field_value, system_charset_info)) + { + DBUG_PRINT("info", ("Dropping")); + if ((ret= table->file->ha_delete_row(table->record[0]))) + my_error(ER_EVENT_DROP_FAILED, MYF(0), + get_field(thd->mem_root, table->field[ET_FIELD_NAME])); + } + } + end_read_record(&read_record_info); + thd->version--; /* Force close to free memory */ + + DBUG_RETURN(ret); +} + + +/* + Looks for a named event in mysql.event and then loads it from + the table, compiles and inserts it into the cache. + + SYNOPSIS + Event_scheduler::load_named_event() + thd THD + etn The name of the event to load and compile on scheduler's root + etn_new The loaded event + + RETURN VALUE + NULL Error during compile or the event is non-enabled. + otherwise Address +*/ + +int +Event_db_repository::load_named_event(THD *thd, Event_timed *etn, + Event_timed **etn_new) +{ + int ret= 0; + MEM_ROOT *tmp_mem_root; + Event_timed *et_loaded= NULL; + Open_tables_state backup; + + DBUG_ENTER("Event_scheduler::load_and_compile_event"); + DBUG_PRINT("enter",("thd=%p name:%*s",thd, etn->name.length, etn->name.str)); + + thd->reset_n_backup_open_tables_state(&backup); + /* No need to use my_error() here because db_find_event() has done it */ + { + sp_name spn(etn->dbname, etn->name); + ret= find_event(thd, &spn, &et_loaded, NULL, &repo_root); + } + thd->restore_backup_open_tables_state(&backup); + /* In this case no memory was allocated so we don't need to clean */ + if (ret) + DBUG_RETURN(OP_LOAD_ERROR); + + if (et_loaded->status != Event_timed::ENABLED) + { + /* + We don't load non-enabled events. + In db_find_event() `et_new` was allocated on the heap and not on + scheduler_root therefore we delete it here. + */ + delete et_loaded; + DBUG_RETURN(OP_DISABLED_EVENT); + } + + et_loaded->compute_next_execution_time(); + *etn_new= et_loaded; + + DBUG_RETURN(OP_OK); +} diff --git a/sql/event_db_repository.h b/sql/event_db_repository.h index d8a8784089e..c0cfccf215a 100644 --- a/sql/event_db_repository.h +++ b/sql/event_db_repository.h @@ -16,5 +16,87 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +enum enum_events_table_field +{ + ET_FIELD_DB = 0, + ET_FIELD_NAME, + ET_FIELD_BODY, + ET_FIELD_DEFINER, + ET_FIELD_EXECUTE_AT, + ET_FIELD_INTERVAL_EXPR, + ET_FIELD_TRANSIENT_INTERVAL, + ET_FIELD_CREATED, + ET_FIELD_MODIFIED, + ET_FIELD_LAST_EXECUTED, + ET_FIELD_STARTS, + ET_FIELD_ENDS, + ET_FIELD_STATUS, + ET_FIELD_ON_COMPLETION, + ET_FIELD_SQL_MODE, + ET_FIELD_COMMENT, + ET_FIELD_COUNT /* a cool trick to count the number of fields :) */ +}; + + +int +evex_db_find_event_by_name(THD *thd, const LEX_STRING dbname, + const LEX_STRING ev_name, + TABLE *table); + +class Event_queue_element; + +class Event_db_repository +{ +public: + Event_db_repository(){} + ~Event_db_repository(){} + + int + init_repository(); + + void + deinit_repository(); + + int + open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table); + + int + create_event(THD *thd, Event_timed *et, my_bool create_if_not, + uint *rows_affected); + + int + update_event(THD *thd, Event_timed *et, sp_name *new_name); + + int + drop_event(THD *thd, LEX_STRING db, LEX_STRING name, bool drop_if_exists, + uint *rows_affected); + + int + drop_schema_events(THD *thd, LEX_STRING schema); + + int + drop_user_events(THD *thd, LEX_STRING definer); + + int + find_event(THD *thd, sp_name *name, Event_timed **ett, TABLE *tbl, + MEM_ROOT *root); + + int + load_named_event(THD *thd, Event_timed *etn, Event_timed **etn_new); + + int + find_event_by_name(THD *thd, LEX_STRING db, LEX_STRING name, TABLE *table); +private: + + int + drop_events_by_field(THD *thd, enum enum_events_table_field field, + LEX_STRING field_value); + + MEM_ROOT repo_root; + + /* Prevent use of these */ + Event_db_repository(const Event_db_repository &); + void operator=(Event_db_repository &); +}; #endif /* _EVENT_DB_REPOSITORY_H_ */ diff --git a/sql/event_scheduler.cc b/sql/event_scheduler.cc index f83bc5f25a3..60c9cda0a56 100644 --- a/sql/event_scheduler.cc +++ b/sql/event_scheduler.cc @@ -15,10 +15,10 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "mysql_priv.h" -#include "events_priv.h" #include "events.h" #include "event_data_objects.h" #include "event_scheduler.h" +#include "event_db_repository.h" #include "sp_head.h" /* @@ -574,8 +574,8 @@ event_worker_thread(void *arg) to change the context before sending the signal. We are under LOCK_scheduler_data being held by Event_scheduler::run() -> ::execute_top(). */ - change_security_context(thd, event->definer_user, event->definer_host, - event->dbname, &security_ctx, &save_ctx); + thd->change_security_context(event->definer_user, event->definer_host, + event->dbname, &security_ctx, &save_ctx); DBUG_PRINT("info", ("master_access=%d db_access=%d", thd->security_ctx->master_access, thd->security_ctx->db_access)); @@ -687,7 +687,7 @@ Event_scheduler::get_instance() */ bool -Event_scheduler::init() +Event_scheduler::init(Event_db_repository *db_repo) { int i= 0; bool ret= FALSE; @@ -695,6 +695,7 @@ Event_scheduler::init() DBUG_PRINT("enter", ("this=%p", this)); LOCK_SCHEDULER_DATA(); + db_repository= db_repo; for (;i < COND_LAST; i++) if (pthread_cond_init(&cond_vars[i], NULL)) { @@ -783,10 +784,10 @@ Event_scheduler::destroy() OP_LOAD_ERROR Error during loading from disk */ -enum Event_scheduler::enum_error_code +int Event_scheduler::create_event(THD *thd, Event_timed *et, bool check_existence) { - enum enum_error_code res; + int res; Event_timed *et_new; DBUG_ENTER("Event_scheduler::create_event"); DBUG_PRINT("enter", ("thd=%p et=%p lock=%p",thd,et,&LOCK_scheduler_data)); @@ -805,7 +806,7 @@ Event_scheduler::create_event(THD *thd, Event_timed *et, bool check_existence) } /* We need to load the event on scheduler_root */ - if (!(res= load_named_event(thd, et, &et_new))) + if (!(res= db_repository->load_named_event(thd, et, &et_new))) { queue_insert_safe(&queue, (byte *) et_new); DBUG_PRINT("info", ("Sending COND_new_work")); @@ -904,12 +905,12 @@ Event_scheduler::drop_event(THD *thd, sp_name *name) OP_ALREADY_EXISTS Event already in the queue */ -enum Event_scheduler::enum_error_code +int Event_scheduler::update_event(THD *thd, Event_timed *et, LEX_STRING *new_schema, LEX_STRING *new_name) { - enum enum_error_code res; + int res= OP_OK; Event_timed *et_old, *et_new= NULL; LEX_STRING old_schema, old_name; @@ -947,7 +948,7 @@ Event_scheduler::update_event(THD *thd, Event_timed *et, 1. Error occured 2. If the replace is DISABLED, we don't load it into the queue. */ - if (!(res= load_named_event(thd, et, &et_new))) + if (!(res= db_repository->load_named_event(thd, et, &et_new))) { queue_insert_safe(&queue, (byte *) et_new); DBUG_PRINT("info", ("Sending COND_new_work")); @@ -961,7 +962,7 @@ Event_scheduler::update_event(THD *thd, Event_timed *et, et->dbname= old_schema; et->name= old_name; } - + DBUG_PRINT("info", ("res=%d", res)); UNLOCK_SCHEDULER_DATA(); /* Andrey: Is this comment still truthful ??? @@ -1111,11 +1112,11 @@ Event_scheduler::find_event(sp_name *name, bool remove_from_q) */ void -Event_scheduler::drop_matching_events(THD *thd, LEX_STRING *pattern, +Event_scheduler::drop_matching_events(THD *thd, LEX_STRING pattern, bool (*comparator)(Event_timed *,LEX_STRING *)) { DBUG_ENTER("Event_scheduler::drop_matching_events"); - DBUG_PRINT("enter", ("pattern=%*s state=%d", pattern->length, pattern->str, + DBUG_PRINT("enter", ("pattern=%*s state=%d", pattern.length, pattern.str, state)); if (is_running_or_suspended()) { @@ -1124,7 +1125,7 @@ Event_scheduler::drop_matching_events(THD *thd, LEX_STRING *pattern, { Event_timed *et= (Event_timed *) queue_element(&queue, i); DBUG_PRINT("info", ("[%s.%s]?", et->dbname.str, et->name.str)); - if (comparator(et, pattern)) + if (comparator(et, &pattern)) { /* The queue is ordered. If we remove an element, then all elements after @@ -1179,7 +1180,7 @@ Event_scheduler::drop_matching_events(THD *thd, LEX_STRING *pattern, */ int -Event_scheduler::drop_schema_events(THD *thd, LEX_STRING *schema) +Event_scheduler::drop_schema_events(THD *thd, LEX_STRING schema) { int ret; DBUG_ENTER("Event_scheduler::drop_schema_events"); @@ -1187,7 +1188,6 @@ Event_scheduler::drop_schema_events(THD *thd, LEX_STRING *schema) if (is_running_or_suspended()) drop_matching_events(thd, schema, event_timed_db_equal); - ret= db_drop_events_from_table(thd, schema); UNLOCK_SCHEDULER_DATA(); DBUG_RETURN(ret); @@ -1713,7 +1713,7 @@ Event_scheduler::stop_all_running_events(THD *thd) The caller must have acquited LOCK_scheduler_data. */ -enum Event_scheduler::enum_error_code +int Event_scheduler::stop() { THD *thd= current_thd; @@ -1778,7 +1778,7 @@ Event_scheduler::stop() OP_OK OK */ -enum Event_scheduler::enum_error_code +int Event_scheduler::suspend_or_resume( enum Event_scheduler::enum_suspend_or_resume action) { @@ -2116,59 +2116,6 @@ Event_scheduler::events_count() } -/* - Looks for a named event in mysql.event and then loads it from - the table, compiles and inserts it into the cache. - - SYNOPSIS - Event_scheduler::load_named_event() - thd THD - etn The name of the event to load and compile on scheduler's root - etn_new The loaded event - - RETURN VALUE - NULL Error during compile or the event is non-enabled. - otherwise Address -*/ - -enum Event_scheduler::enum_error_code -Event_scheduler::load_named_event(THD *thd, Event_timed *etn, Event_timed **etn_new) -{ - int ret= 0; - MEM_ROOT *tmp_mem_root; - Event_timed *et_loaded= NULL; - Open_tables_state backup; - - DBUG_ENTER("Event_scheduler::load_and_compile_event"); - DBUG_PRINT("enter",("thd=%p name:%*s",thd, etn->name.length, etn->name.str)); - - thd->reset_n_backup_open_tables_state(&backup); - /* No need to use my_error() here because db_find_event() has done it */ - { - sp_name spn(etn->dbname, etn->name); - ret= db_find_event(thd, &spn, &et_loaded, NULL, &scheduler_root); - } - thd->restore_backup_open_tables_state(&backup); - /* In this case no memory was allocated so we don't need to clean */ - if (ret) - DBUG_RETURN(OP_LOAD_ERROR); - - if (et_loaded->status != Event_timed::ENABLED) - { - /* - We don't load non-enabled events. - In db_find_event() `et_new` was allocated on the heap and not on - scheduler_root therefore we delete it here. - */ - delete et_loaded; - DBUG_RETURN(OP_DISABLED_EVENT); - } - - et_loaded->compute_next_execution_time(); - *etn_new= et_loaded; - - DBUG_RETURN(OP_OK); -} /* @@ -2212,7 +2159,7 @@ Event_scheduler::load_events_from_db(THD *thd) DBUG_RETURN(EVEX_GENERAL_ERROR); } - if ((ret= Events::open_event_table(thd, TL_READ, &table))) + if ((ret= Events::get_instance()->open_event_table(thd, TL_READ, &table))) { sql_print_error("SCHEDULER: Table mysql.event is damaged. Can not open."); DBUG_RETURN(EVEX_OPEN_TABLE_FAILED); diff --git a/sql/event_scheduler.h b/sql/event_scheduler.h index 183ef450be2..bd099d10839 100644 --- a/sql/event_scheduler.h +++ b/sql/event_scheduler.h @@ -18,6 +18,7 @@ class sp_name; class Event_timed; +class Event_db_repository; class THD; typedef bool * (*event_timed_identifier_comparator)(Event_timed*, Event_timed*); @@ -31,17 +32,6 @@ events_shutdown(); class Event_scheduler { public: - /* Return codes */ - enum enum_error_code - { - OP_OK= 0, - OP_NOT_RUNNING, - OP_CANT_KILL, - OP_CANT_INIT, - OP_DISABLED_EVENT, - OP_LOAD_ERROR, - OP_ALREADY_EXISTS - }; enum enum_state { @@ -66,10 +56,10 @@ public: /* Methods for queue management follow */ - enum enum_error_code + int create_event(THD *thd, Event_timed *et, bool check_existence); - enum enum_error_code + int update_event(THD *thd, Event_timed *et, LEX_STRING *new_schema, LEX_STRING *new_name); @@ -78,10 +68,10 @@ public: int - drop_schema_events(THD *thd, LEX_STRING *schema); + drop_schema_events(THD *thd, LEX_STRING schema); int - drop_user_events(THD *thd, LEX_STRING *definer, uint *dropped_num) + drop_user_events(THD *thd, LEX_STRING *definer) { DBUG_ASSERT(0); return 0;} uint @@ -92,20 +82,24 @@ public: bool start(); - enum enum_error_code + int stop(); bool start_suspended(); + /* + Need to be public because has to be called from the function + passed to pthread_create. + */ bool run(THD *thd); - enum enum_error_code + int suspend_or_resume(enum enum_suspend_or_resume action); bool - init(); + init(Event_db_repository *db_repo); void destroy(); @@ -156,14 +150,11 @@ private: void stop_all_running_events(THD *thd); - enum enum_error_code - load_named_event(THD *thd, Event_timed *etn, Event_timed **etn_new); - int load_events_from_db(THD *thd); void - drop_matching_events(THD *thd, LEX_STRING *pattern, + drop_matching_events(THD *thd, LEX_STRING pattern, bool (*)(Event_timed *,LEX_STRING *)); bool @@ -230,6 +221,8 @@ private: /* The MEM_ROOT of the object */ MEM_ROOT scheduler_root; + Event_db_repository *db_repository; + /* The sorted queue with the Event_timed objects */ QUEUE queue; diff --git a/sql/events.cc b/sql/events.cc index c9d05994873..52e814043d8 100644 --- a/sql/events.cc +++ b/sql/events.cc @@ -15,11 +15,10 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "mysql_priv.h" -#include "events_priv.h" #include "events.h" #include "event_data_objects.h" #include "event_scheduler.h" -#include "sp.h" +#include "event_db_repository.h" #include "sp_head.h" /* @@ -48,10 +47,6 @@ Warning: */ -MEM_ROOT evex_mem_root; -time_t mysql_event_last_create_time= 0L; - - const char *event_scheduler_state_names[]= { "OFF", "0", "ON", "1", "SUSPEND", "2", NullS }; @@ -63,104 +58,10 @@ TYPELIB Events::opt_typelib= NULL }; +Events Events::singleton; ulong Events::opt_event_scheduler= 2; -static -TABLE_FIELD_W_TYPE event_table_fields[Events::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")}, - {NULL, 0} - }, - { - {(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")} - } -}; - /* Compares 2 LEX strings regarding case. @@ -188,6 +89,14 @@ int sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs) } +Events * +Events::get_instance() +{ + DBUG_ENTER("Events::get_instance"); + DBUG_RETURN(&singleton); +} + + /* Reconstructs interval expression from interval type and expression value that is in form of a value of the smalles entity: @@ -207,9 +116,8 @@ int sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs) */ int -Events::reconstruct_interval_expression(String *buf, - interval_type interval, - longlong expression) +Events::reconstruct_interval_expression(String *buf, interval_type interval, + longlong expression) { ulonglong expr= expression; char tmp_buff[128], *end; @@ -341,545 +249,7 @@ int Events::open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table) { - TABLE_LIST tables; - DBUG_ENTER("open_events_table"); - - bzero((char*) &tables, sizeof(tables)); - tables.db= (char*) "mysql"; - tables.table_name= tables.alias= (char*) "event"; - tables.lock_type= lock_type; - - if (simple_open_n_lock_tables(thd, &tables)) - DBUG_RETURN(1); - - if (table_check_intact(tables.table, Events::FIELD_COUNT, - event_table_fields, - &mysql_event_last_create_time, - ER_CANNOT_LOAD_FROM_TABLE)) - { - close_thread_tables(thd); - DBUG_RETURN(2); - } - *table= tables.table; - tables.table->use_all_columns(); - DBUG_RETURN(0); -} - - -/* - Find row in open mysql.event table representing event - - SYNOPSIS - evex_db_find_event_aux() - thd Thread context - et event_timed object containing dbname & name - 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, 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. - - RETURN VALUE - 0 - Routine found - EVEX_KEY_NOT_FOUND - No routine with given name -*/ - -int -evex_db_find_event_by_name(THD *thd, const LEX_STRING dbname, - const LEX_STRING ev_name, - TABLE *table) -{ - byte key[MAX_KEY_LENGTH]; - DBUG_ENTER("evex_db_find_event_by_name"); - 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 - handle VARCHAR and CHAR fields. - Assumption here is that the two first fields in the table are - 'db' and 'name' and the first key is the primary key over the - same fields. - */ - if (dbname.length > table->field[Events::FIELD_DB]->field_length || - ev_name.length > table->field[Events::FIELD_NAME]->field_length) - - DBUG_RETURN(EVEX_KEY_NOT_FOUND); - - table->field[Events::FIELD_DB]->store(dbname.str, dbname.length, - &my_charset_bin); - table->field[Events::FIELD_NAME]->store(ev_name.str, ev_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, - table->key_info->key_length, - HA_READ_KEY_EXACT)) - { - DBUG_PRINT("info", ("Row not found")); - DBUG_RETURN(EVEX_KEY_NOT_FOUND); - } - - DBUG_PRINT("info", ("Row found!")); - DBUG_RETURN(0); -} - - -/* - Puts some data common to CREATE and ALTER EVENT into a row. - - SYNOPSIS - evex_fill_row() - thd THD - table the row to fill out - et Event's data - - RETURN VALUE - 0 - OK - EVEX_GENERAL_ERROR - bad data - EVEX_GET_FIELD_FAILED - field count does not match. table corrupted? - - DESCRIPTION - Used both when an event is created and when it is altered. -*/ - -static int -evex_fill_row(THD *thd, TABLE *table, Event_timed *et, my_bool is_update) -{ - CHARSET_INFO *scs= system_charset_info; - enum Events::enum_table_field field_num; - - DBUG_ENTER("evex_fill_row"); - - 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= Events::FIELD_DEFINER]-> - store(et->definer.str, et->definer.length, scs)) - goto err_truncate; - - if (table->field[field_num= Events::FIELD_DB]-> - store(et->dbname.str, et->dbname.length, scs)) - goto err_truncate; - - if (table->field[field_num= Events::FIELD_NAME]-> - store(et->name.str, et->name.length, scs)) - goto err_truncate; - - /* both ON_COMPLETION and STATUS are NOT NULL thus not calling set_notnull()*/ - table->field[Events::FIELD_ON_COMPLETION]-> - store((longlong)et->on_completion, true); - - table->field[Events::FIELD_STATUS]->store((longlong)et->status, true); - - /* - Change the SQL_MODE only if body was present in an ALTER EVENT and of course - always during CREATE EVENT. - */ - if (et->body.str) - { - table->field[Events::FIELD_SQL_MODE]-> - store((longlong)thd->variables.sql_mode, true); - - if (table->field[field_num= Events::FIELD_BODY]-> - store(et->body.str, et->body.length, scs)) - goto err_truncate; - } - - if (et->expression) - { - table->field[Events::FIELD_INTERVAL_EXPR]->set_notnull(); - table->field[Events::FIELD_INTERVAL_EXPR]-> - store((longlong)et->expression, true); - - table->field[Events::FIELD_TRANSIENT_INTERVAL]->set_notnull(); - /* - In the enum (C) intervals start from 0 but in mysql enum valid values start - from 1. Thus +1 offset is needed! - */ - table->field[Events::FIELD_TRANSIENT_INTERVAL]-> - store((longlong)et->interval+1, true); - - table->field[Events::FIELD_EXECUTE_AT]->set_null(); - - if (!et->starts_null) - { - table->field[Events::FIELD_STARTS]->set_notnull(); - table->field[Events::FIELD_STARTS]-> - store_time(&et->starts, MYSQL_TIMESTAMP_DATETIME); - } - - if (!et->ends_null) - { - table->field[Events::FIELD_ENDS]->set_notnull(); - table->field[Events::FIELD_ENDS]-> - store_time(&et->ends, MYSQL_TIMESTAMP_DATETIME); - } - } - else if (et->execute_at.year) - { - table->field[Events::FIELD_INTERVAL_EXPR]->set_null(); - table->field[Events::FIELD_TRANSIENT_INTERVAL]->set_null(); - table->field[Events::FIELD_STARTS]->set_null(); - table->field[Events::FIELD_ENDS]->set_null(); - - table->field[Events::FIELD_EXECUTE_AT]->set_notnull(); - table->field[Events::FIELD_EXECUTE_AT]-> - store_time(&et->execute_at, MYSQL_TIMESTAMP_DATETIME); - } - else - { - DBUG_ASSERT(is_update); - /* - it is normal to be here when the action is update - this is an error if the action is create. something is borked - */ - } - - ((Field_timestamp *)table->field[Events::FIELD_MODIFIED])->set_time(); - - if (et->comment.str) - { - if (table->field[field_num= Events::FIELD_COMMENT]-> - store(et->comment.str, et->comment.length, scs)) - goto err_truncate; - } - - DBUG_RETURN(0); -err_truncate: - my_error(ER_EVENT_DATA_TOO_LONG, MYF(0), table->field[field_num]->field_name); - DBUG_RETURN(EVEX_GENERAL_ERROR); -} - - -/* - Creates an event in mysql.event - - SYNOPSIS - db_create_event() - thd THD - et Event_timed object containing information for the event - create_if_not If an warning should be generated in case event exists - rows_affected How many rows were affected - - RETURN VALUE - 0 - OK - EVEX_GENERAL_ERROR - Failure - - DESCRIPTION - Creates an event. Relies on evex_fill_row which is shared with - db_update_event. The name of the event is inside "et". -*/ - -int -db_create_event(THD *thd, Event_timed *et, my_bool create_if_not, - uint *rows_affected) -{ - int ret= 0; - CHARSET_INFO *scs= system_charset_info; - TABLE *table; - char olddb[128]; - bool dbchanged= false; - DBUG_ENTER("db_create_event"); - DBUG_PRINT("enter", ("name: %.*s", et->name.length, et->name.str)); - - *rows_affected= 0; - DBUG_PRINT("info", ("open mysql.event for update")); - if (Events::open_event_table(thd, TL_WRITE, &table)) - { - 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, table)) - { - 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")); - if ((ret= sp_use_new_db(thd, et->dbname.str,olddb, sizeof(olddb),0, - &dbchanged))) - { - my_error(ER_BAD_DB_ERROR, MYF(0)); - goto err; - } - - restore_record(table, s->default_values); // Get default values for fields - - if (system_charset_info->cset->numchars(system_charset_info, et->dbname.str, - et->dbname.str + et->dbname.length) - > EVEX_DB_FIELD_LEN) - { - my_error(ER_TOO_LONG_IDENT, MYF(0), et->dbname.str); - goto err; - } - if (system_charset_info->cset->numchars(system_charset_info, et->name.str, - et->name.str + et->name.length) - > EVEX_DB_FIELD_LEN) - { - my_error(ER_TOO_LONG_IDENT, MYF(0), et->name.str); - goto err; - } - - if (et->body.length > table->field[Events::FIELD_BODY]->field_length) - { - my_error(ER_TOO_LONG_BODY, MYF(0), et->name.str); - goto err; - } - - if (!(et->expression) && !(et->execute_at.year)) - { - DBUG_PRINT("error", ("neither expression nor execute_at are set!")); - my_error(ER_EVENT_NEITHER_M_EXPR_NOR_M_AT, MYF(0)); - goto err; - } - - ((Field_timestamp *)table->field[Events::FIELD_CREATED])->set_time(); - - /* - evex_fill_row() calls my_error() in case of error so no need to - handle it here - */ - if ((ret= evex_fill_row(thd, table, et, false))) - goto err; - - if (table->file->ha_write_row(table->record[0])) - { - my_error(ER_EVENT_STORE_FAILED, MYF(0), et->name.str, ret); - goto err; - } - -#ifdef USE_THIS_CODE_AS_TEMPLATE_WHEN_EVENT_REPLICATION_IS_AGREED - if (mysql_bin_log.is_open()) - { - thd->clear_error(); - /* Such a statement can always go directly to binlog, no trans cache */ - thd->binlog_query(THD::MYSQL_QUERY_TYPE, thd->query, thd->query_length, - FALSE, FALSE); - } -#endif - - *rows_affected= 1; -ok: - if (dbchanged) - (void) mysql_change_db(thd, olddb, 1); - if (table) - close_thread_tables(thd); - DBUG_RETURN(EVEX_OK); - -err: - if (dbchanged) - (void) mysql_change_db(thd, olddb, 1); - if (table) - close_thread_tables(thd); - DBUG_RETURN(EVEX_GENERAL_ERROR); -} - - -/* - Used to execute ALTER EVENT. Pendant to Events::update_event(). - - SYNOPSIS - db_update_event() - thd THD - sp_name the name of the event to alter - et event's data - - RETURN VALUE - 0 OK - EVEX_GENERAL_ERROR Error occured (my_error() called) - - NOTES - sp_name is passed since this is the name of the event to - alter in case of RENAME TO. -*/ - -static int -db_update_event(THD *thd, Event_timed *et, sp_name *new_name) -{ - CHARSET_INFO *scs= system_charset_info; - 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->definer.length, et->definer.str)); - if (new_name) - DBUG_PRINT("enter", ("rename to: %.*s", new_name->m_name.length, - new_name->m_name.str)); - - if (Events::open_event_table(thd, TL_WRITE, &table)) - { - my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0)); - goto err; - } - - /* first look whether we overwrite */ - if (new_name) - { - if (!sortcmp_lex_string(et->name, new_name->m_name, scs) && - !sortcmp_lex_string(et->dbname, new_name->m_db, scs)) - { - my_error(ER_EVENT_SAME_NAME, MYF(0), et->name.str); - goto err; - } - - if (!evex_db_find_event_by_name(thd,new_name->m_db,new_name->m_name,table)) - { - my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), new_name->m_name.str); - goto err; - } - } - /* - ...and then if there is such an event. Don't exchange the blocks - because you will get error 120 from table handler because new_name will - 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, table)) - { - my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), et->name.str); - goto err; - } - - store_record(table,record[1]); - - /* Don't update create on row update. */ - table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; - - /* - evex_fill_row() calls my_error() in case of error so no need to - handle it here - */ - if ((ret= evex_fill_row(thd, table, et, true))) - goto err; - - if (new_name) - { - table->field[Events::FIELD_DB]-> - store(new_name->m_db.str, new_name->m_db.length, scs); - table->field[Events::FIELD_NAME]-> - store(new_name->m_name.str, new_name->m_name.length, scs); - } - - if ((ret= table->file->ha_update_row(table->record[1], table->record[0]))) - { - my_error(ER_EVENT_STORE_FAILED, MYF(0), et->name.str, ret); - goto err; - } - - /* close mysql.event or we crash later when loading the event from disk */ - close_thread_tables(thd); - DBUG_RETURN(0); - -err: - if (table) - close_thread_tables(thd); - DBUG_RETURN(EVEX_GENERAL_ERROR); -} - - -/* - 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 - - RETURN VALUE - 0 ok In this case *ett is set to the event - # error *ett == 0 -*/ - -int -db_find_event(THD *thd, sp_name *name, Event_timed **ett, TABLE *tbl, - MEM_ROOT *root) -{ - TABLE *table; - int ret; - 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 (Events::open_event_table(thd, TL_READ, &table)) - { - my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0)); - ret= EVEX_GENERAL_ERROR; - goto done; - } - - if ((ret= evex_db_find_event_by_name(thd, name->m_db, name->m_name, table))) - { - my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name->m_name.str); - goto done; - } - et= new Event_timed; - - /* - 1)The table should not be closed beforehand. ::load_from_row() only loads - and does not compile - - 2)::load_from_row() is silent on error therefore we emit error msg here - */ - if ((ret= et->load_from_row(root, table))) - { - my_error(ER_CANNOT_LOAD_FROM_TABLE, MYF(0)); - goto done; - } - -done: - if (ret) - { - delete et; - et= 0; - } - /* don't close the table if we haven't opened it ourselves */ - if (!tbl && table) - close_thread_tables(thd); - *ett= et; - DBUG_RETURN(ret); + return db_repository.open_event_table(thd, lock_type, table); } @@ -913,9 +283,10 @@ Events::create_event(THD *thd, Event_timed *et, Event_parse_data *parse_data, DBUG_PRINT("enter", ("name: %*s options:%d", et->name.length, et->name.str, create_options)); - if (!(ret = db_create_event(thd, et, - create_options & HA_LEX_CREATE_IF_NOT_EXISTS, - rows_affected))) + if (!(ret= db_repository. + create_event(thd, et, + create_options & HA_LEX_CREATE_IF_NOT_EXISTS, + rows_affected))) { Event_scheduler *scheduler= Event_scheduler::get_instance(); if (scheduler->initialized() && @@ -960,7 +331,7 @@ Events::update_event(THD *thd, Event_timed *et, Event_parse_data *parse_data, crash later in the code when loading and compiling the new definition. Also on error conditions my_error() is called so no need to handle here */ - if (!(ret= db_update_event(thd, et, new_name))) + if (!(ret= db_repository.update_event(thd, et, new_name))) { Event_scheduler *scheduler= Event_scheduler::get_instance(); if (scheduler->initialized() && @@ -977,67 +348,6 @@ Events::update_event(THD *thd, Event_timed *et, Event_parse_data *parse_data, Drops an event SYNOPSIS - 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 - - RETURN VALUE - 0 OK - !0 Error (my_error() called) -*/ - -int db_drop_event(THD *thd, LEX_STRING db, LEX_STRING name, - bool drop_if_exists, uint *rows_affected) -{ - TABLE *table; - Open_tables_state backup; - int ret; - - DBUG_ENTER("db_drop_event"); - DBUG_PRINT("enter", ("db=%s name=%s", db.str, name.str)); - ret= EVEX_OPEN_TABLE_FAILED; - - thd->reset_n_backup_open_tables_state(&backup); - if (Events::open_event_table(thd, TL_WRITE, &table)) - { - my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0)); - goto done; - } - - if (!(ret= evex_db_find_event_by_name(thd, db, name, table))) - { - if ((ret= table->file->ha_delete_row(table->record[0]))) - my_error(ER_EVENT_CANNOT_DELETE, MYF(0)); - } - else if (ret == EVEX_KEY_NOT_FOUND) - { - if (drop_if_exists) - { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, - ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST), - "Event", name.str); - ret= 0; - } else - my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name.str); - } - -done: - /* - evex_drop_event() is used by Event_timed::drop therefore - 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 Events::drop_event() thd THD name event's name @@ -1056,14 +366,14 @@ Events::drop_event(THD *thd, sp_name *name, bool drop_if_exists, int ret; DBUG_ENTER("Events::drop_event"); - if (!(ret= db_drop_event(thd, name->m_db, name->m_name, drop_if_exists, - rows_affected))) + + if (!(ret= db_repository.drop_event(thd, name->m_db, name->m_name, + drop_if_exists, rows_affected))) { Event_scheduler *scheduler= Event_scheduler::get_instance(); if (scheduler->initialized() && (ret= scheduler->drop_event(thd, name))) my_error(ER_EVENT_MODIFY_QUEUE_ERROR, MYF(0), ret); } - DBUG_RETURN(ret); } @@ -1092,7 +402,7 @@ Events::show_create_event(THD *thd, sp_name *spn) 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, &et, NULL, thd->mem_root); + ret= db_repository.find_event(thd, spn, &et, NULL, thd->mem_root); thd->restore_backup_open_tables_state(&backup); if (!ret) @@ -1164,75 +474,13 @@ Events::drop_schema_events(THD *thd, char *db) DBUG_PRINT("enter", ("dropping events from %s", db)); Event_scheduler *scheduler= Event_scheduler::get_instance(); - if (scheduler->initialized()) - ret= scheduler->drop_schema_events(thd, &db_lex); - else - ret= db_drop_events_from_table(thd, &db_lex); - - DBUG_RETURN(ret); -} - - -/* - Drops all events in the selected database, from mysql.event. - - SYNOPSIS - evex_drop_db_events_from_table() - thd Thread - db Schema name - - RETURN VALUE - 0 OK - !0 Error from ha_delete_row -*/ - -int -db_drop_events_from_table(THD *thd, LEX_STRING *db) -{ - int ret; - TABLE *table; - READ_RECORD read_record_info; - DBUG_ENTER("db_drop_events_from_table"); - DBUG_PRINT("info", ("dropping events from %s", db->str)); - - if ((ret= Events::open_event_table(thd, TL_WRITE, &table))) - { - if (my_errno != ENOENT) - sql_print_error("Table mysql.event is damaged. Got error %d on open", - my_errno); - DBUG_RETURN(ret); - } - /* 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= get_field(thd->mem_root, - table->field[Events::FIELD_DB]); - - LEX_STRING et_db_lex= {et_db, strlen(et_db)}; - DBUG_PRINT("info", ("Current event %s.%s", et_db, - get_field(thd->mem_root, - table->field[Events::FIELD_NAME]))); - - if (!sortcmp_lex_string(et_db_lex, *db, system_charset_info)) - { - DBUG_PRINT("info", ("Dropping")); - if ((ret= table->file->ha_delete_row(table->record[0]))) - my_error(ER_EVENT_DROP_FAILED, MYF(0), - get_field(thd->mem_root, - table->field[Events::FIELD_NAME])); - } - } - end_read_record(&read_record_info); - thd->version--; /* Force close to free memory */ - - close_thread_tables(thd); + ret= scheduler->drop_schema_events(thd, db_lex); + ret= db_repository.drop_schema_events(thd, db_lex); DBUG_RETURN(ret); } - /* Inits the scheduler's structures. @@ -1251,14 +499,17 @@ int Events::init() { int ret= 0; + Event_db_repository *db_repo; DBUG_ENTER("Events::init"); + db_repo= &get_instance()->db_repository; + db_repo->init_repository(); /* it should be an assignment! */ if (opt_event_scheduler) { Event_scheduler *scheduler= Event_scheduler::get_instance(); DBUG_ASSERT(opt_event_scheduler == 1 || opt_event_scheduler == 2); - DBUG_RETURN(scheduler->init() || + DBUG_RETURN(scheduler->init(db_repo) || (opt_event_scheduler == 1? scheduler->start(): scheduler->start_suspended())); } @@ -1270,22 +521,23 @@ Events::init() Cleans up scheduler's resources. Called at server shutdown. SYNOPSIS - Events::shutdown() + Events::deinit() NOTES This function is not synchronized. */ void -Events::shutdown() +Events::deinit() { - DBUG_ENTER("Events::shutdown"); + DBUG_ENTER("Events::deinit"); Event_scheduler *scheduler= Event_scheduler::get_instance(); if (scheduler->initialized()) { scheduler->stop(); scheduler->destroy(); } + get_instance()->db_repository.deinit_repository(); DBUG_VOID_RETURN; } diff --git a/sql/events.h b/sql/events.h index d56a544493d..339f87c3806 100644 --- a/sql/events.h +++ b/sql/events.h @@ -20,74 +20,91 @@ class sp_name; class Event_timed; class Event_parse_data; +#include "event_db_repository.h" + +/* Return codes */ +enum enum_events_error_code +{ + OP_OK= 0, + OP_NOT_RUNNING, + OP_CANT_KILL, + OP_CANT_INIT, + OP_DISABLED_EVENT, + OP_LOAD_ERROR, + OP_ALREADY_EXISTS +}; + +int +sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs); + + class Events { public: + /* + Quite NOT the best practice and will be removed once + Event_timed::drop() and Event_timed is fixed not do drop directly + or other scheme will be found. + */ + friend class Event_timed; + static ulong opt_event_scheduler; static TYPELIB opt_typelib; - enum enum_table_field - { - FIELD_DB = 0, - FIELD_NAME, - FIELD_BODY, - FIELD_DEFINER, - FIELD_EXECUTE_AT, - FIELD_INTERVAL_EXPR, - FIELD_TRANSIENT_INTERVAL, - FIELD_CREATED, - FIELD_MODIFIED, - FIELD_LAST_EXECUTED, - FIELD_STARTS, - FIELD_ENDS, - FIELD_STATUS, - FIELD_ON_COMPLETION, - FIELD_SQL_MODE, - FIELD_COMMENT, - FIELD_COUNT /* a cool trick to count the number of fields :) */ - }; - static int + init(); + + static void + deinit(); + + static void + init_mutexes(); + + static void + destroy_mutexes(); + + static Events* + get_instance(); + + int create_event(THD *thd, Event_timed *et, Event_parse_data *parse_data, uint create_options, uint *rows_affected); - static int + int update_event(THD *thd, Event_timed *et, Event_parse_data *parse_data, sp_name *new_name, uint *rows_affected); - static int + int drop_event(THD *thd, sp_name *name, bool drop_if_exists, uint *rows_affected); - static int + int open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table); - static int + int show_create_event(THD *thd, sp_name *spn); + /* Needed for both SHOW CREATE EVENT and INFORMATION_SCHEMA */ static int reconstruct_interval_expression(String *buf, interval_type interval, longlong expression); - static int + int drop_schema_events(THD *thd, char *db); - static int + int dump_internal_status(THD *thd); - - static int - init(); - - static void - shutdown(); - - static void - init_mutexes(); - - static void - destroy_mutexes(); + Event_db_repository db_repository; private: + /* Singleton DP is used */ + Events(){} + ~Events(){} + + /* Singleton instance */ + static Events singleton; + + /* Prevent use of these */ Events(const Events &); void operator=(Events &); diff --git a/sql/events_priv.h b/sql/events_priv.h deleted file mode 100644 index fe208ace280..00000000000 --- a/sql/events_priv.h +++ /dev/null @@ -1,94 +0,0 @@ -#ifndef _EVENT_PRIV_H_ -#define _EVENT_PRIV_H_ -/* Copyright (C) 2004-2006 MySQL AB - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#define EVENT_EXEC_STARTED 0 -#define EVENT_EXEC_ALREADY_EXEC 1 -#define EVENT_EXEC_CANT_FORK 2 - -#define EVEX_DB_FIELD_LEN 64 -#define EVEX_NAME_FIELD_LEN 64 -#define EVEX_MAX_INTERVAL_VALUE 2147483647L - -class Event_timed; -class sp_name; - -int -evex_db_find_event_by_name(THD *thd, const LEX_STRING dbname, - const LEX_STRING ev_name, - TABLE *table); - -int -db_drop_event(THD *thd, LEX_STRING db, LEX_STRING name, bool drop_if_exists, - uint *rows_affected); -int -db_find_event(THD *thd, sp_name *name, Event_timed **ett, TABLE *tbl, - MEM_ROOT *root); - -int -db_create_event(THD *thd, Event_timed *et, my_bool create_if_not, - uint *rows_affected); - -int -db_drop_events_from_table(THD *thd, LEX_STRING *db); - -int -sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs); - -/* Compares only the name part of the identifier */ -bool -event_timed_name_equal(Event_timed *et, LEX_STRING *name); - -/* Compares only the schema part of the identifier */ -bool -event_timed_db_equal(Event_timed *et, LEX_STRING *db); - -/* - Compares only the definer part of the identifier. Use during DROP USER - to drop user's events. (Still not implemented) -*/ -bool -event_timed_definer_equal(Event_timed *et, LEX_STRING *definer); - -/* Compares the whole identifier*/ -bool -event_timed_identifier_equal(Event_timed *a, Event_timed *b); - - - -/* Compares only the name part of the identifier */ -bool -event_timed_name_equal(sp_name *name, LEX_STRING *event_name); - -/* Compares only the schema part of the identifier */ -bool -event_timed_db_equal(sp_name *name, LEX_STRING *db); - -/* Compares the whole identifier*/ -bool -event_timed_identifier_equal(sp_name *a, Event_timed *b); - - -bool -change_security_context(THD *thd, LEX_STRING user, LEX_STRING host, - LEX_STRING db, Security_context *s_ctx, - Security_context **backup); - -void -restore_security_context(THD *thd, Security_context *backup); - -#endif /* _EVENT_PRIV_H_ */ diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 3f93de00594..a82f66acd19 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -886,7 +886,7 @@ static void close_connections(void) } (void) pthread_mutex_unlock(&LOCK_thread_count); // For unlink from list - Events::shutdown(); + Events::deinit(); end_slave(); if (thread_count) diff --git a/sql/set_var.cc b/sql/set_var.cc index e6b0625f097..98bcf3be6b2 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -3891,7 +3891,7 @@ byte *sys_var_thd_dbug::value_ptr(THD *thd, enum_var_type type, LEX_STRING *b) bool sys_var_event_scheduler::update(THD *thd, set_var *var) { - enum Event_scheduler::enum_error_code res; + int res; Event_scheduler *scheduler= Event_scheduler::get_instance(); /* here start the thread if not running. */ DBUG_ENTER("sys_var_event_scheduler::update"); diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 23603afc038..97fc6feb5f7 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -2048,6 +2048,63 @@ bool Security_context::set_user(char *user_arg) return user == 0; } +/* + Switches the security context + SYNOPSIS + THD::change_security_context() + user The user + host The host of the user + db The schema for which the security_ctx will be loaded + s_ctx Security context to load state into + backup Where to store the old context + + RETURN VALUE + FALSE OK + TRUE Error (generates error too) +*/ + +bool +THD::change_security_context(LEX_STRING user, LEX_STRING host, + LEX_STRING db, Security_context *s_ctx, + Security_context **backup) +{ + DBUG_ENTER("change_security_context"); + DBUG_PRINT("info",("%s@%s@%s", user.str, host.str, db.str)); +#ifndef NO_EMBEDDED_ACCESS_CHECKS + s_ctx->init(); + *backup= 0; + if (acl_getroot_no_password(s_ctx, user.str, host.str, host.str, db.str)) + { + my_error(ER_NO_SUCH_USER, MYF(0), user.str, host.str); + DBUG_RETURN(TRUE); + } + *backup= security_ctx; + security_ctx= s_ctx; +#endif + DBUG_RETURN(FALSE); +} + + +/* + Restores the security context + SYNOPSIS + restore_security_context() + thd Thread + backup Context to switch to +*/ + +void +THD::restore_security_context(Security_context *backup) +{ + DBUG_ENTER("restore_security_context"); +#ifndef NO_EMBEDDED_ACCESS_CHECKS + if (backup) + security_ctx= backup; +#endif + DBUG_VOID_RETURN; +} + + /**************************************************************************** Handling of open and locked tables states. diff --git a/sql/sql_class.h b/sql/sql_class.h index fa4a29ff112..191ecc2ab18 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -868,6 +868,14 @@ public: char *db, *catalog; Security_context main_security_ctx; Security_context *security_ctx; + + bool + change_security_context(LEX_STRING user, LEX_STRING host, + LEX_STRING db, Security_context *s_ctx, + Security_context **backup); + + void + restore_security_context(Security_context *backup); /* remote (peer) port */ uint16 peer_port; diff --git a/sql/sql_db.cc b/sql/sql_db.cc index 8dd62fc8494..83516da38d4 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -904,7 +904,7 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) exit: (void)sp_drop_db_routines(thd, db); /* QQ Ignore errors for now */ - error= Events::drop_schema_events(thd, db); + error= Events::get_instance()->drop_schema_events(thd, db); /* If this database was the client's selected database, we silently change the client's selected database to nothing (to have an empty SELECT DATABASE() diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 4f75339e5a2..0d52bc41a48 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3847,13 +3847,14 @@ end_with_restore_list: switch (lex->sql_command) { case SQLCOM_CREATE_EVENT: - res= Events::create_event(thd, lex->et, lex->event_parse_data, - (uint) lex->create_info.options, - &rows_affected); + res= Events::get_instance()-> + create_event(thd, lex->et, lex->event_parse_data, + (uint) lex->create_info.options, &rows_affected); break; case SQLCOM_ALTER_EVENT: - res= Events::update_event(thd, lex->et, lex->event_parse_data, - lex->spname, &rows_affected); + res= Events::get_instance()-> + update_event(thd, lex->et, lex->event_parse_data, + lex->spname, &rows_affected); break; default:; } @@ -3895,7 +3896,7 @@ end_with_restore_list: } if (lex->sql_command == SQLCOM_SHOW_CREATE_EVENT) - res= Events::show_create_event(thd, lex->spname); + res= Events::get_instance()->show_create_event(thd, lex->spname); else { uint rows_affected= 1; @@ -3904,8 +3905,9 @@ end_with_restore_list: res= -1; break; } - if (!(res= Events::drop_event(thd, lex->spname, lex->drop_if_exists, - &rows_affected))) + if (!(res= Events::get_instance()->drop_event(thd, lex->spname, + lex->drop_if_exists, + &rows_affected))) send_ok(thd, rows_affected); } break; @@ -3913,7 +3915,7 @@ end_with_restore_list: #ifndef DBUG_OFF case SQLCOM_SHOW_SCHEDULER_STATUS: { - res= Events::dump_internal_status(thd); + res= Events::get_instance()->dump_internal_status(thd); break; } #endif diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 33fa9758c6c..77906973a38 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -4324,7 +4324,7 @@ int events_table_index_read_for_db(THD *thd, TABLE *schema_table, DBUG_PRINT("info", ("Using prefix scanning on PK")); event_table->file->ha_index_init(0, 1); - event_table->field[Events::FIELD_DB]-> + event_table->field[ET_FIELD_DB]-> store(thd->lex->select_lex.db, strlen(thd->lex->select_lex.db), scs); key_info= event_table->key_info; key_len= key_info->key_part[0].store_length; @@ -4443,7 +4443,7 @@ int fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */) thd->lex->select_lex.db:"(null)")); thd->reset_n_backup_open_tables_state(&backup); - if (Events::open_event_table(thd, TL_READ, &event_table)) + if (Events::get_instance()->open_event_table(thd, TL_READ, &event_table)) { sql_print_error("Table mysql.event is damaged."); thd->restore_backup_open_tables_state(&backup); |