From 4d891eb91cd0556e05a2022c4120d776ca772f69 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 2 Dec 2005 13:07:02 +0100 Subject: [PATCH 01/28] WL1034 update for latest 5.1 sources BUILD/SETUP.sh: for now no optimizations in debug build - get rid of "value optimized out" --- BUILD/SETUP.sh | 2 +- include/my_sys.h | 1 + libmysqld/Makefile.am | 4 +- mysys/array.c | 25 +++ sql/Makefile.am | 4 +- sql/lex.h | 8 + sql/mysqld.cc | 12 +- sql/set_var.cc | 4 + sql/share/errmsg.txt | 16 ++ sql/sp.cc | 88 +++++---- sql/sp.h | 12 +- sql/sql_acl.cc | 4 +- sql/sql_acl.h | 3 +- sql/sql_lex.cc | 4 +- sql/sql_lex.h | 7 + sql/sql_parse.cc | 128 ++++++++++++++ sql/sql_show.cc | 5 +- sql/sql_yacc.yy | 401 ++++++++++++++++++++++++++++++++++++++++-- sql/table.cc | 3 +- sql/tztime.cc | 12 ++ sql/tztime.h | 1 + 21 files changed, 676 insertions(+), 68 deletions(-) diff --git a/BUILD/SETUP.sh b/BUILD/SETUP.sh index eece41d72e6..7881459d086 100755 --- a/BUILD/SETUP.sh +++ b/BUILD/SETUP.sh @@ -79,7 +79,7 @@ fast_cflags="-O3 -fno-omit-frame-pointer" reckless_cflags="-O3 -fomit-frame-pointer " debug_cflags="-DUNIV_MUST_NOT_INLINE -DEXTRA_DEBUG -DFORCE_INIT_OF_VARS -DSAFEMALLOC -DPEDANTIC_SAFEMALLOC -DSAFE_MUTEX" -debug_extra_cflags="-O1 -Wuninitialized" +debug_extra_cflags="-O0" base_cxxflags="-felide-constructors -fno-exceptions -fno-rtti" amd64_cxxflags="" # If dropping '--with-big-tables', add here "-DBIG_TABLES" diff --git a/include/my_sys.h b/include/my_sys.h index 44fe383bf4f..a814b7f66f6 100644 --- a/include/my_sys.h +++ b/include/my_sys.h @@ -751,6 +751,7 @@ extern void get_dynamic(DYNAMIC_ARRAY *array,gptr element,uint array_index); extern void delete_dynamic(DYNAMIC_ARRAY *array); extern void delete_dynamic_element(DYNAMIC_ARRAY *array, uint array_index); extern void freeze_size(DYNAMIC_ARRAY *array); +extern int get_index_dynamic(DYNAMIC_ARRAY *array, gptr element); #define dynamic_array_ptr(array,array_index) ((array)->buffer+(array_index)*(array)->size_of_element) #define dynamic_element(array,array_index,type) ((type)((array)->buffer) +(array_index)) #define push_dynamic(A,B) insert_dynamic(A,B) diff --git a/libmysqld/Makefile.am b/libmysqld/Makefile.am index da0418eaf9c..4eae790c9db 100644 --- a/libmysqld/Makefile.am +++ b/libmysqld/Makefile.am @@ -62,8 +62,8 @@ sqlsources = derror.cc field.cc field_conv.cc strfunc.cc filesort.cc \ unireg.cc uniques.cc stacktrace.c sql_union.cc hash_filo.cc \ spatial.cc gstream.cc sql_help.cc tztime.cc sql_cursor.cc \ sp_head.cc sp_pcontext.cc sp.cc sp_cache.cc sp_rcontext.cc \ - parse_file.cc sql_view.cc sql_trigger.cc my_decimal.cc \ - rpl_filter.cc sql_partition.cc handlerton.cc sql_plugin.cc + parse_file.cc sql_view.cc sql_trigger.cc my_decimal.cc event.cc \ + rpl_filter.cc sql_partition.cc handlerton.cc sql_plugin.cc libmysqld_int_a_SOURCES= $(libmysqld_sources) $(libmysqlsources) $(sqlsources) EXTRA_libmysqld_a_SOURCES = ha_innodb.cc ha_berkeley.cc ha_archive.cc \ diff --git a/mysys/array.c b/mysys/array.c index 6d00585f24d..a50d8b78178 100644 --- a/mysys/array.c +++ b/mysys/array.c @@ -278,3 +278,28 @@ void freeze_size(DYNAMIC_ARRAY *array) array->max_element=elements; } } + + +/* + Get the index of a dynamic element + + SYNOPSIS + get_index_dynamic() + array Array + element Whose element index + +*/ + +int get_index_dynamic(DYNAMIC_ARRAY *array, gptr element) +{ + uint ret; + if (array->buffer > element) + return -1; + + ret= (element - array->buffer) / array->size_of_element; + if (ret > array->elements) + return -1; + + return ret; + +} diff --git a/sql/Makefile.am b/sql/Makefile.am index 78d5e262fde..0182e58ba6c 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -61,7 +61,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 \ + sql_array.h sql_cursor.h event.h \ sql_plugin.h authors.h mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \ item.cc item_sum.cc item_buff.cc item_func.cc \ @@ -94,7 +94,7 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \ gstream.cc spatial.cc sql_help.cc sql_cursor.cc \ tztime.cc my_time.c my_decimal.cc\ sp_head.cc sp_pcontext.cc sp_rcontext.cc sp.cc \ - sp_cache.cc parse_file.cc sql_trigger.cc \ + sp_cache.cc parse_file.cc sql_trigger.cc event.cc \ sql_plugin.cc\ handlerton.cc EXTRA_mysqld_SOURCES = ha_innodb.cc ha_berkeley.cc ha_archive.cc \ diff --git a/sql/lex.h b/sql/lex.h index e3cbebf4629..41cbae0adea 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -74,6 +74,7 @@ static SYMBOL symbols[] = { { "ASC", SYM(ASC)}, { "ASCII", SYM(ASCII_SYM)}, { "ASENSITIVE", SYM(ASENSITIVE_SYM)}, + { "AT", SYM(AT_SYM)}, { "AUTHORS", SYM(AUTHORS_SYM)}, { "AUTO_INCREMENT", SYM(AUTO_INC)}, { "AVG", SYM(AVG_SYM)}, @@ -121,6 +122,7 @@ static SYMBOL symbols[] = { { "COMMIT", SYM(COMMIT_SYM)}, { "COMMITTED", SYM(COMMITTED_SYM)}, { "COMPACT", SYM(COMPACT_SYM)}, + { "COMPLETION", SYM(COMPLETION_SYM)}, { "COMPRESSED", SYM(COMPRESSED_SYM)}, { "CONCURRENT", SYM(CONCURRENT)}, { "CONDITION", SYM(CONDITION_SYM)}, @@ -180,13 +182,16 @@ static SYMBOL symbols[] = { { "ENABLE", SYM(ENABLE_SYM)}, { "ENCLOSED", SYM(ENCLOSED)}, { "END", SYM(END)}, + { "ENDS", SYM(ENDS_SYM)}, { "ENGINE", SYM(ENGINE_SYM)}, { "ENGINES", SYM(ENGINES_SYM)}, { "ENUM", SYM(ENUM)}, { "ERRORS", SYM(ERRORS)}, { "ESCAPE", SYM(ESCAPE_SYM)}, { "ESCAPED", SYM(ESCAPED)}, + { "EVENT", SYM(EVENT_SYM)}, { "EVENTS", SYM(EVENTS_SYM)}, + { "EVERY", SYM(EVERY_SYM)}, { "EXECUTE", SYM(EXECUTE_SYM)}, { "EXISTS", SYM(EXISTS)}, { "EXIT", SYM(EXIT_SYM)}, @@ -384,6 +389,7 @@ static SYMBOL symbols[] = { { "POLYGON", SYM(POLYGON)}, { "PRECISION", SYM(PRECISION)}, { "PREPARE", SYM(PREPARE_SYM)}, + { "PRESERVE", SYM(PRESERVE_SYM)}, { "PREV", SYM(PREV_SYM)}, { "PRIMARY", SYM(PRIMARY_SYM)}, { "PRIVILEGES", SYM(PRIVILEGES)}, @@ -436,6 +442,7 @@ static SYMBOL symbols[] = { { "ROW_FORMAT", SYM(ROW_FORMAT_SYM)}, { "RTREE", SYM(RTREE_SYM)}, { "SAVEPOINT", SYM(SAVEPOINT_SYM)}, + { "SCHEDULE", SYM(SCHEDULE_SYM)}, { "SCHEMA", SYM(DATABASE)}, { "SCHEMAS", SYM(DATABASES)}, { "SECOND", SYM(SECOND_SYM)}, @@ -484,6 +491,7 @@ static SYMBOL symbols[] = { { "SSL", SYM(SSL_SYM)}, { "START", SYM(START_SYM)}, { "STARTING", SYM(STARTING)}, + { "STARTS", SYM(STARTS_SYM)}, { "STATUS", SYM(STATUS_SYM)}, { "STOP", SYM(STOP_SYM)}, { "STORAGE", SYM(STORAGE_SYM)}, diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 68485600e9e..65ef17b7066 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -24,6 +24,7 @@ #include "stacktrace.h" #include "mysqld_suffix.h" #include "mysys_err.h" +#include "event.h" #include "ha_myisam.h" @@ -3502,6 +3503,8 @@ we force server id to 2, but this MySQL server will not act as a slave."); } } + init_events(); + create_shutdown_thread(); create_maintenance_thread(); @@ -4526,7 +4529,7 @@ enum options_mysqld OPT_MAX_BINLOG_DUMP_EVENTS, OPT_SPORADIC_BINLOG_DUMP_FAIL, OPT_SAFE_USER_CREATE, OPT_SQL_MODE, OPT_HAVE_NAMED_PIPE, - OPT_DO_PSTACK, OPT_REPORT_HOST, + OPT_DO_PSTACK, OPT_EVENT_EXECUTOR, OPT_REPORT_HOST, OPT_REPORT_USER, OPT_REPORT_PASSWORD, OPT_REPORT_PORT, OPT_SHOW_SLAVE_AUTH_INFO, OPT_SLAVE_LOAD_TMPDIR, OPT_NO_MIX_TYPE, @@ -4804,6 +4807,9 @@ Disable with --skip-bdb (will save memory).", (gptr*) &global_system_variables.engine_condition_pushdown, (gptr*) &global_system_variables.engine_condition_pushdown, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"event-executor", OPT_EVENT_EXECUTOR, "Print a symbolic stack trace on failure.", + (gptr*) &opt_event_executor, (gptr*) &opt_event_executor, 0, GET_BOOL, NO_ARG, + 1/*default*/, 0/*min-value*/, 1/*max-value*/, 0, 0, 0}, {"exit-info", 'T', "Used for debugging; Use at your own risk!", 0, 0, 0, GET_LONG, OPT_ARG, 0, 0, 0, 0, 0, 0}, {"external-locking", OPT_USE_LOCKING, "Use system (external) locking. With this option enabled you can run myisamchk to test (not repair) tables while the MySQL server is running.", @@ -6028,6 +6034,7 @@ struct show_var_st status_vars[]= { {"Bytes_sent", (char*) offsetof(STATUS_VAR, bytes_sent), SHOW_LONG_STATUS}, {"Com_admin_commands", (char*) offsetof(STATUS_VAR, com_other), SHOW_LONG_STATUS}, {"Com_alter_db", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_ALTER_DB]), SHOW_LONG_STATUS}, + {"Com_alter_event", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_ALTER_EVENT]), SHOW_LONG_STATUS}, {"Com_alter_table", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_ALTER_TABLE]), SHOW_LONG_STATUS}, {"Com_analyze", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_ANALYZE]), SHOW_LONG_STATUS}, {"Com_backup_table", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_BACKUP_TABLE]), SHOW_LONG_STATUS}, @@ -6038,6 +6045,7 @@ struct show_var_st status_vars[]= { {"Com_checksum", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CHECKSUM]), SHOW_LONG_STATUS}, {"Com_commit", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_COMMIT]), SHOW_LONG_STATUS}, {"Com_create_db", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_DB]), SHOW_LONG_STATUS}, + {"Com_create_event", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_EVENT]), SHOW_LONG_STATUS}, {"Com_create_function", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_FUNCTION]), SHOW_LONG_STATUS}, {"Com_create_index", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_INDEX]), SHOW_LONG_STATUS}, {"Com_create_table", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_TABLE]), SHOW_LONG_STATUS}, @@ -6046,6 +6054,7 @@ struct show_var_st status_vars[]= { {"Com_delete_multi", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DELETE_MULTI]), SHOW_LONG_STATUS}, {"Com_do", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DO]), SHOW_LONG_STATUS}, {"Com_drop_db", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DROP_DB]), SHOW_LONG_STATUS}, + {"Com_drop_event", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DROP_EVENT]), SHOW_LONG_STATUS}, {"Com_drop_function", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DROP_FUNCTION]), SHOW_LONG_STATUS}, {"Com_drop_index", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DROP_INDEX]), SHOW_LONG_STATUS}, {"Com_drop_table", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DROP_TABLE]), SHOW_LONG_STATUS}, @@ -6087,6 +6096,7 @@ struct show_var_st status_vars[]= { {"Com_show_collations", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_COLLATIONS]), SHOW_LONG_STATUS}, {"Com_show_column_types", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_COLUMN_TYPES]), SHOW_LONG_STATUS}, {"Com_show_create_db", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_CREATE_DB]), SHOW_LONG_STATUS}, + {"Com_show_create_event", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_CREATE_EVENT]), SHOW_LONG_STATUS}, {"Com_show_create_table", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_CREATE]), SHOW_LONG_STATUS}, {"Com_show_databases", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_DATABASES]), SHOW_LONG_STATUS}, {"Com_show_engine_logs", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_ENGINE_LOGS]), SHOW_LONG_STATUS}, diff --git a/sql/set_var.cc b/sql/set_var.cc index b505f8cdc2a..b74914e72a6 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -102,6 +102,7 @@ extern ulong ndb_cache_check_time; +extern my_bool event_executor_running_global_var; static HASH system_variable_hash; const char *bool_type_names[]= { "OFF", "ON", NullS }; @@ -206,6 +207,8 @@ sys_var_long_ptr sys_delayed_insert_timeout("delayed_insert_timeout", &delayed_insert_timeout); sys_var_long_ptr sys_delayed_queue_size("delayed_queue_size", &delayed_queue_size); +sys_var_bool_ptr sys_event_executor("event_executor", + &event_executor_running_global_var); sys_var_long_ptr sys_expire_logs_days("expire_logs_days", &expire_logs_days); sys_var_bool_ptr sys_flush("flush", &myisam_flush); @@ -664,6 +667,7 @@ struct show_var_st init_vars[]= { {sys_div_precincrement.name,(char*) &sys_div_precincrement,SHOW_SYS}, {sys_engine_condition_pushdown.name, (char*) &sys_engine_condition_pushdown, SHOW_SYS}, + {sys_event_executor.name, (char*) &sys_event_executor, SHOW_SYS}, {sys_expire_logs_days.name, (char*) &sys_expire_logs_days, SHOW_SYS}, {sys_flush.name, (char*) &sys_flush, SHOW_SYS}, {sys_flush_time.name, (char*) &sys_flush_time, SHOW_SYS}, diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index f556ad9db65..dfe1f2d71ad 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -5721,3 +5721,19 @@ ER_DROP_PARTITION_WHEN_FK_DEFINED swe "Kan inte ta bort en partition när en främmande nyckel är definierad på tabellen" ER_PLUGIN_IS_NOT_LOADED eng "Plugin '%-.64s' is not loaded" +ER_EVENT_ALREADY_EXISTS + eng "Event %s already exists" +ER_EVENT_STORE_FAILED + eng "Failed to create event %s" +ER_EVENT_DOES_NOT_EXIST + eng "Event %s does not exist" +ER_EVENT_CANT_ALTER + eng "Failed to alter event %s" +ER_EVENT_DROP_FAILED + eng "Failed to DROP %s %s" +ER_EVENT_INTERVAL_NOT_POSITIVE + eng "INTERVAL must be positive" +ER_EVENT_ENDS_BEFORE_STARTS + eng "ENDS must be after STARTS" +ER_EVENT_EXEC_TIME_IN_THE_PAST + eng "Activation (AT) time is in the past" diff --git a/sql/sp.cc b/sql/sp.cc index e9deb9b73c4..cda6454b891 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -20,6 +20,9 @@ #include "sp_cache.h" #include "sql_trigger.h" +#define SP_OPEN_TABLE_FOR_UPDATE() \ + open_proc_type_table_for_update(thd, "proc", &mysql_proc_table_exists) + static bool create_string(THD *thd, String *buf, int sp_type, @@ -68,7 +71,7 @@ bool mysql_proc_table_exists= 1; /* - Close mysql.proc, opened with open_proc_table_for_read(). + Close mysql.proc, opened with open_proc_type_table_for_read(). SYNOPSIS close_proc_table() @@ -86,14 +89,16 @@ void close_proc_table(THD *thd, Open_tables_state *backup) /* - Open the mysql.proc table for read. + Open table which has key structure like of mysql.proc for read. SYNOPSIS - open_proc_table_for_read() - thd Thread context - backup Pointer to Open_tables_state instance where information about - currently open tables will be saved, and from which will be - restored when we will end work with mysql.proc. + open_proc_type_table_for_read() + thd Thread context + backup Pointer to Open_tables_state instance where information about + currently open tables will be saved, and from which will be + restored when we will end work with mysql.proc. + tname Table name having primary key structure like mysql.proc + table_exists Ptr to boolean to set whether the system table exists or not NOTES Thanks to restrictions which we put on opening and locking of @@ -104,10 +109,11 @@ void close_proc_table(THD *thd, Open_tables_state *backup) RETURN 0 Error - # Pointer to TABLE object of mysql.proc + # Pointer to TABLE object of tname */ -TABLE *open_proc_table_for_read(THD *thd, Open_tables_state *backup) +TABLE *open_proc_type_table_for_read(THD *thd, Open_tables_state *backup, + const char *tname, bool *table_exists) { TABLE_LIST tables; TABLE *table; @@ -115,22 +121,22 @@ TABLE *open_proc_table_for_read(THD *thd, Open_tables_state *backup) DBUG_ENTER("open_proc_table"); /* - Speed up things if mysql.proc doesn't exists. mysql_proc_table_exists + Speed up things if the table doesn't exists. *table_exists is set when we create or read stored procedure or on flush privileges. */ - if (!mysql_proc_table_exists) + if (!*table_exists) DBUG_RETURN(0); thd->reset_n_backup_open_tables_state(backup); bzero((char*) &tables, sizeof(tables)); tables.db= (char*) "mysql"; - tables.table_name= tables.alias= (char*)"proc"; + tables.table_name= tables.alias= (char*) tname; if (!(table= open_table(thd, &tables, thd->mem_root, ¬_used, MYSQL_LOCK_IGNORE_FLUSH))) { thd->restore_backup_open_tables_state(backup); - mysql_proc_table_exists= 0; + *table_exists= 0; DBUG_RETURN(0); } @@ -152,11 +158,13 @@ TABLE *open_proc_table_for_read(THD *thd, Open_tables_state *backup) /* - Open the mysql.proc table for update. + Open table with primary key struct like mysql.proc for update. SYNOPSIS - open_proc_table_for_update() - thd Thread context + open_proc_type_table_for_update() + thd Thread context + tname Table name with primary key structure like mysql.proc + table_exists Ptr to boolean to set whether the system table exists or not NOTES Table opened with this call should closed using close_thread_tables(). @@ -166,7 +174,8 @@ TABLE *open_proc_table_for_read(THD *thd, Open_tables_state *backup) # Pointer to TABLE object of mysql.proc */ -static TABLE *open_proc_table_for_update(THD *thd) +TABLE *open_proc_type_table_for_update(THD *thd, const char *tname, + bool *table_exists) { TABLE_LIST tables; TABLE *table; @@ -174,7 +183,7 @@ static TABLE *open_proc_table_for_update(THD *thd) bzero((char*) &tables, sizeof(tables)); tables.db= (char*) "mysql"; - tables.table_name= tables.alias= (char*)"proc"; + tables.table_name= tables.alias= (char*) tname; tables.lock_type= TL_WRITE; table= open_ltable(thd, &tables, TL_WRITE); @@ -186,7 +195,7 @@ static TABLE *open_proc_table_for_update(THD *thd) transient. */ if (!(thd->locked_tables || thd->prelocked_mode) || table) - mysql_proc_table_exists= test(table); + *table_exists= test(table); DBUG_RETURN(table); } @@ -196,10 +205,11 @@ static TABLE *open_proc_table_for_update(THD *thd) Find row in open mysql.proc table representing stored routine. SYNOPSIS - db_find_routine_aux() + sp_db_find_routine_aux() thd Thread context type Type of routine to find (function or procedure) - name Name of routine + dbname Name of routine's database + rname Name of the routine inside the db table TABLE object for open mysql.proc table. RETURN VALUE @@ -207,13 +217,14 @@ static TABLE *open_proc_table_for_update(THD *thd) SP_KEY_NOT_FOUND- No routine with given name */ -static int -db_find_routine_aux(THD *thd, int type, sp_name *name, TABLE *table) +int +sp_db_find_routine_aux(THD *thd, int type, const LEX_STRING dbname, + const LEX_STRING rname, TABLE *table) { byte key[MAX_KEY_LENGTH]; // db, name, optional key length type - DBUG_ENTER("db_find_routine_aux"); + DBUG_ENTER("sp_db_find_routine_aux"); DBUG_PRINT("enter", ("type: %d name: %.*s", - type, name->m_name.length, name->m_name.str)); + type, rname.length, rname.str)); /* Create key to find row. We have to use field->store() to be able to @@ -222,11 +233,10 @@ db_find_routine_aux(THD *thd, int type, sp_name *name, TABLE *table) 'db', 'name' and 'type' and the first key is the primary key over the same fields. */ - if (name->m_name.length > table->field[1]->field_length) + if (rname.length > table->field[1]->field_length) DBUG_RETURN(SP_KEY_NOT_FOUND); - table->field[0]->store(name->m_db.str, name->m_db.length, &my_charset_bin); - table->field[1]->store(name->m_name.str, name->m_name.length, - &my_charset_bin); + table->field[0]->store(dbname.str, dbname.length, &my_charset_bin); + table->field[1]->store(rname.str, rname.length, &my_charset_bin); table->field[2]->store((longlong) type, TRUE); key_copy(key, table->record[0], table->key_info, table->key_info->key_length); @@ -283,10 +293,12 @@ db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp) type, name->m_name.length, name->m_name.str)); *sphp= 0; // In case of errors - if (!(table= open_proc_table_for_read(thd, &open_tables_state_backup))) + if (!(table= open_proc_type_table_for_read(thd,&open_tables_state_backup, + "proc", &mysql_proc_table_exists))) DBUG_RETURN(SP_OPEN_TABLE_FAILED); - if ((ret= db_find_routine_aux(thd, type, name, table)) != SP_OK) + if ((ret= sp_db_find_routine_aux(thd, type, name->m_db, name->m_name, + table)) != SP_OK) goto done; if (table->s->fields != MYSQL_PROC_FIELD_COUNT) @@ -493,7 +505,7 @@ db_create_routine(THD *thd, int type, sp_head *sp) goto done; } - if (!(table= open_proc_table_for_update(thd))) + if (!(table= SP_OPEN_TABLE_FOR_UPDATE())) ret= SP_OPEN_TABLE_FAILED; else { @@ -614,9 +626,10 @@ db_drop_routine(THD *thd, int type, sp_name *name) DBUG_PRINT("enter", ("type: %d name: %.*s", type, name->m_name.length, name->m_name.str)); - if (!(table= open_proc_table_for_update(thd))) + if (!(table= SP_OPEN_TABLE_FOR_UPDATE())) DBUG_RETURN(SP_OPEN_TABLE_FAILED); - if ((ret= db_find_routine_aux(thd, type, name, table)) == SP_OK) + if ((ret= sp_db_find_routine_aux(thd, type, name->m_db, name->m_name, + table)) == SP_OK) { if (table->file->delete_row(table->record[0])) ret= SP_DELETE_ROW_FAILED; @@ -636,9 +649,10 @@ db_update_routine(THD *thd, int type, sp_name *name, st_sp_chistics *chistics) DBUG_PRINT("enter", ("type: %d name: %.*s", type, name->m_name.length, name->m_name.str)); - if (!(table= open_proc_table_for_update(thd))) + if (!(table= SP_OPEN_TABLE_FOR_UPDATE())) DBUG_RETURN(SP_OPEN_TABLE_FAILED); - if ((ret= db_find_routine_aux(thd, type, name, table)) == SP_OK) + if ((ret= sp_db_find_routine_aux(thd, type, name->m_db, name->m_name, + table)) == SP_OK) { store_record(table,record[1]); table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; @@ -860,7 +874,7 @@ sp_drop_db_routines(THD *thd, char *db) keylen= sizeof(key); ret= SP_OPEN_TABLE_FAILED; - if (!(table= open_proc_table_for_update(thd))) + if (!(table= SP_OPEN_TABLE_FOR_UPDATE())) goto err; ret= SP_OK; diff --git a/sql/sp.h b/sql/sp.h index 7f314b8903e..157d7dbbea9 100644 --- a/sql/sp.h +++ b/sql/sp.h @@ -31,6 +31,8 @@ #define SP_BAD_IDENTIFIER -9 #define SP_BODY_TOO_LONG -10 +extern bool mysql_proc_table_exists; + /* Drop all routines in database 'db' */ int sp_drop_db_routines(THD *thd, char *db); @@ -97,9 +99,17 @@ extern "C" byte* sp_sroutine_key(const byte *ptr, uint *plen, my_bool first); Routines which allow open/lock and close mysql.proc table even when we already have some tables open and locked. */ -TABLE *open_proc_table_for_read(THD *thd, Open_tables_state *backup); +TABLE *open_proc_type_table_for_read(THD *thd, Open_tables_state *backup, + const char *tname, bool *table_exists); +TABLE *open_proc_type_table_for_update(THD *thd, const char *tname, + bool *table_exists); + void close_proc_table(THD *thd, Open_tables_state *backup); +int +sp_db_find_routine_aux(THD *thd, int type, const LEX_STRING dbname, + const LEX_STRING rname, TABLE *table); + // // Utilities... // diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 59f1e91e2e5..427193cb4a8 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -3971,13 +3971,13 @@ static const char *command_array[]= "ALTER", "SHOW DATABASES", "SUPER", "CREATE TEMPORARY TABLES", "LOCK TABLES", "EXECUTE", "REPLICATION SLAVE", "REPLICATION CLIENT", "CREATE VIEW", "SHOW VIEW", "CREATE ROUTINE", "ALTER ROUTINE", - "CREATE USER" + "CREATE USER", "EVENT" }; static uint command_lengths[]= { 6, 6, 6, 6, 6, 4, 6, 8, 7, 4, 5, 10, 5, 5, 14, 5, 23, 11, 7, 17, 18, 11, 9, - 14, 13, 11 + 14, 13, 11, 5 }; diff --git a/sql/sql_acl.h b/sql/sql_acl.h index 0a9c6ba785f..c1e0e94dd06 100644 --- a/sql/sql_acl.h +++ b/sql/sql_acl.h @@ -42,6 +42,7 @@ #define CREATE_PROC_ACL (1L << 23) #define ALTER_PROC_ACL (1L << 24) #define CREATE_USER_ACL (1L << 25) +#define EVENT_ACL (1L << 26) /* don't forget to update 1. static struct show_privileges_st sys_privileges[] @@ -78,7 +79,7 @@ REFERENCES_ACL | INDEX_ACL | ALTER_ACL | SHOW_DB_ACL | SUPER_ACL | \ CREATE_TMP_ACL | LOCK_TABLES_ACL | REPL_SLAVE_ACL | REPL_CLIENT_ACL | \ EXECUTE_ACL | CREATE_VIEW_ACL | SHOW_VIEW_ACL | CREATE_PROC_ACL | \ - ALTER_PROC_ACL | CREATE_USER_ACL) + ALTER_PROC_ACL | CREATE_USER_ACL | EVENT_ACL) #define DEFAULT_CREATE_PROC_ACLS \ (ALTER_PROC_ACL | EXECUTE_ACL) diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 4d32e26f1b7..f2f065510da 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -177,7 +177,9 @@ void lex_start(THD *thd, const uchar *buf, uint length) lex->spcont= NULL; lex->proc_list.first= 0; lex->query_tables_own_last= 0; - lex->escape_used= FALSE; + lex->escape_used= lex->et_compile_phase= FALSE; + + lex->et= NULL; if (lex->sroutines.records) my_hash_reset(&lex->sroutines); diff --git a/sql/sql_lex.h b/sql/sql_lex.h index d7ad28a95f5..fdcd785f920 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -26,6 +26,7 @@ class sp_name; class sp_instr; class sp_pcontext; class partition_info; +class event_timed; /* The following hack is needed because mysql_yacc.cc does not define @@ -94,6 +95,8 @@ enum enum_sql_command { SQLCOM_SHOW_PROC_CODE, SQLCOM_SHOW_FUNC_CODE, SQLCOM_INSTALL_PLUGIN, SQLCOM_UNINSTALL_PLUGIN, SQLCOM_SHOW_AUTHORS, + SQLCOM_CREATE_EVENT, SQLCOM_ALTER_EVENT, SQLCOM_DROP_EVENT, + SQLCOM_SHOW_CREATE_EVENT, /* This should be the last !!! */ SQLCOM_END @@ -890,6 +893,10 @@ typedef struct st_lex uint sroutines_list_own_elements; st_sp_chistics sp_chistics; + + event_timed *et; + bool et_compile_phase; + bool only_view; /* used for SHOW CREATE TABLE/VIEW */ /* field_list was created for view and should be removed before PS/SP diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 57d7059dd8f..d316906f1f6 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -25,6 +25,7 @@ #include "sp_head.h" #include "sp.h" #include "sp_cache.h" +#include "event.h" #ifdef HAVE_OPENSSL /* @@ -642,6 +643,9 @@ void init_update_queries(void) uc_update_queries[SQLCOM_DROP_INDEX]=1; uc_update_queries[SQLCOM_CREATE_VIEW]=1; uc_update_queries[SQLCOM_DROP_VIEW]=1; + uc_update_queries[SQLCOM_CREATE_EVENT]=1; + uc_update_queries[SQLCOM_ALTER_EVENT]=1; + uc_update_queries[SQLCOM_DROP_EVENT]=1; } bool is_update_query(enum enum_sql_command command) @@ -3669,6 +3673,130 @@ end_with_restore_list: res=mysqld_show_create_db(thd,lex->name,&lex->create_info); break; } + case SQLCOM_CREATE_EVENT: + { + if (check_global_access(thd, EVENT_ACL)) + break; + + DBUG_ASSERT(lex->et); + if (! lex->et->m_db.str) + { + my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0)); + delete lex->et; + lex->et= 0; + goto error; + } + + int result; + uint create_options= lex->create_info.options; + res= (result= evex_create_event(thd, lex->et, create_options)); + switch (result) { + case EVEX_OK: + send_ok(thd, 1); + break; + case EVEX_WRITE_ROW_FAILED: + my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), lex->et->m_name.str); + break; + case EVEX_NO_DB_ERROR: + my_error(ER_BAD_DB_ERROR, MYF(0), lex->et->m_db.str); + break; + default: + //includes EVEX_PARSE_ERROR + my_error(ER_EVENT_STORE_FAILED, MYF(0), lex->et->m_name.str); + break; + } + /* lex->unit.cleanup() is called outside, no need to call it here */ + delete lex->et; + lex->et= 0; + + delete lex->sphead; + lex->sphead= 0; + + break; + } + case SQLCOM_ALTER_EVENT: + { + if (check_global_access(thd, EVENT_ACL)) + break; + + DBUG_ASSERT(lex->et); + if (! lex->et->m_db.str) + { + my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0)); + delete lex->et; + lex->et= 0; + goto error; + } + + int result; + res= (result= evex_update_event(thd, lex->spname, lex->et)); + switch (result) { + case EVEX_OK: + send_ok(thd, 1); + break; + case EVEX_KEY_NOT_FOUND: + my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), lex->et->m_qname.str); + break; + default: + my_error(ER_EVENT_CANT_ALTER, MYF(0), lex->et->m_qname.str); + break; + } + delete lex->et; + lex->et= 0; + + if (lex->sphead) + { + delete lex->sphead; + lex->sphead= 0; + } + + break; + } + case SQLCOM_DROP_EVENT: + { + if (check_global_access(thd, EVENT_ACL)) + break; + + DBUG_ASSERT(lex->et); + if (! lex->et->m_db.str) + { + my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0)); + delete lex->et; + lex->et= 0; + goto error; + } + + int result; + res= (result= evex_drop_event(thd, lex->et, lex->drop_if_exists)); + switch (result) { + case EVEX_OK: + send_ok(thd, 1); + break; + case EVEX_KEY_NOT_FOUND: + my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), lex->et->m_qname.str); + break; + default: + my_error(ER_EVENT_DROP_FAILED, MYF(0), lex->et->m_qname.str); + break; + } + delete lex->et; + lex->et= 0; + + break; + } + case SQLCOM_SHOW_CREATE_EVENT: + { + if (check_global_access(thd, EVENT_ACL)) + break; + + if (lex->spname->m_name.length > NAME_LEN) + { + my_error(ER_TOO_LONG_IDENT, MYF(0), lex->spname->m_name.str); + goto error; + } + send_ok(thd, 1); + break; + } case SQLCOM_CREATE_FUNCTION: // UDF function { if (check_access(thd,INSERT_ACL,"mysql",0,1,0,0)) diff --git a/sql/sql_show.cc b/sql/sql_show.cc index e33fc2e1c56..447201de57c 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -147,6 +147,7 @@ static struct show_privileges_st sys_privileges[]= {"Create user", "Server Admin", "To create new users"}, {"Delete", "Tables", "To delete existing rows"}, {"Drop", "Databases,Tables", "To drop databases, tables, and views"}, + {"Event","Server Admin","Creation, alteration, deletion and execution of events."}, {"Execute", "Functions,Procedures", "To execute stored routines"}, {"File", "File access on server", "To read and write files on the server"}, {"Grant option", "Databases,Tables,Functions,Procedures", "To give to other users those privileges you possess"}, @@ -2930,7 +2931,9 @@ int fill_schema_proc(THD *thd, TABLE_LIST *tables, COND *cond) proc_tables.table_name_length= 4; proc_tables.lock_type= TL_READ; full_access= !check_table_access(thd, SELECT_ACL, &proc_tables, 1); - if (!(proc_table= open_proc_table_for_read(thd, &open_tables_state_backup))) + if (!(proc_table= open_proc_type_table_for_read(thd, &open_tables_state_backup, + "proc", + &mysql_proc_table_exists))) { DBUG_RETURN(1); } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index c79ad97be4e..74ea99b7d98 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -38,6 +38,7 @@ #include "sp_pcontext.h" #include "sp_rcontext.h" #include "sp.h" +#include "event.h" #include #include @@ -136,6 +137,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token ASC %token ASCII_SYM %token ASENSITIVE_SYM +%token AT_SYM %token ATAN %token AUTHORS_SYM %token AUTO_INC @@ -186,6 +188,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token COMMITTED_SYM %token COMMIT_SYM %token COMPACT_SYM +%token COMPLETION_SYM %token COMPRESSED_SYM %token CONCAT %token CONCAT_WS @@ -254,6 +257,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token ENCODE_SYM %token ENCRYPT %token END +%token ENDS_SYM %token ENGINES_SYM %token ENGINE_SYM %token ENUM @@ -262,7 +266,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token ERRORS %token ESCAPED %token ESCAPE_SYM +%token EVENT_SYM %token EVENTS_SYM +%token EVERY_SYM %token EXECUTE_SYM %token EXISTS %token EXIT_SYM @@ -488,6 +494,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token POSITION_SYM %token PRECISION %token PREPARE_SYM +%token PRESERVE_SYM %token PREV_SYM %token PRIMARY_SYM %token PRIVILEGES @@ -544,6 +551,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token ROW_SYM %token RTREE_SYM %token SAVEPOINT_SYM +%token SCHEDULE_SYM %token SECOND_MICROSECOND_SYM %token SECOND_SYM %token SECURITY_SYM @@ -583,6 +591,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token SSL_SYM %token STARTING %token START_SYM +%token STARTS_SYM %token STATUS_SYM %token STD_SYM %token STDDEV_SAMP_SYM @@ -676,6 +685,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token YEAR_SYM %token ZEROFILL + %left JOIN_SYM INNER_SYM STRAIGHT_JOIN CROSS LEFT RIGHT /* A dummy token to force the priority of table_ref production in a join. */ %left TABLE_REF_PRIORITY @@ -857,6 +867,12 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); END_OF_INPUT %type call sp_proc_stmts sp_proc_stmts1 sp_proc_stmt +%type sp_proc_stmt_statement sp_proc_stmt_return +%type sp_proc_stmt_if sp_proc_stmt_case_simple sp_proc_stmt_case +%type sp_labeled_control sp_proc_stmt_unlabeled sp_proc_stmt_leave +%type sp_proc_stmt_iterate sp_proc_stmt_label sp_proc_stmt_goto +%type sp_proc_stmt_open sp_proc_stmt_fetch sp_proc_stmt_close + %type sp_decl_idents sp_opt_inout sp_handler_type sp_hcond_list %type sp_cond sp_hcond %type sp_decls sp_decl @@ -1295,7 +1311,213 @@ create: { Lex->sql_command = SQLCOM_CREATE_USER; } - ; + | CREATE EVENT_SYM opt_if_not_exists sp_name + { + LEX *lex=Lex; + event_timed *et; + + if (lex->et) + { + // ToDo Andrey : Change the error message + my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "EVENT"); + YYABORT; + } + + lex->create_info.options=$3; + + et= new event_timed();// implicitly calls event_timed::init() + lex->et = et; + + if (!lex->et_compile_phase) + et->init_name(YYTHD, $4); + + /* + We have to turn of CLIENT_MULTI_QUERIES while parsing a + stored procedure, otherwise yylex will chop it into pieces + at each ';'. + */ + et->m_old_cmq= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES; + YYTHD->client_capabilities &= ~CLIENT_MULTI_QUERIES; + + lex->sphead= 0; + } + ON SCHEDULE_SYM ev_schedule_time + ev_on_completion + ev_status + ev_comment + DO_SYM ev_sql_stmt + { + LEX *lex=Lex; + + lex->sql_command= SQLCOM_CREATE_EVENT; + } + ; + +ev_schedule_time: EVERY_SYM expr interval + { + LEX *lex=Lex; + if (!lex->et_compile_phase) + { + switch (lex->et->init_interval(YYTHD , $2, $3)) { + case EVEX_PARSE_ERROR: + yyerror(ER(ER_SYNTAX_ERROR)); + YYABORT; + break; + case EVEX_BAD_PARAMS: + my_error(ER_EVENT_INTERVAL_NOT_POSITIVE, MYF(0)); + YYABORT; + break; + } + } + } + ev_starts + ev_ends + | AT_SYM expr + { + LEX *lex=Lex; + if (!lex->et_compile_phase) + { + switch (lex->et->init_execute_at(YYTHD, $2)) { + case EVEX_PARSE_ERROR: + yyerror(ER(ER_SYNTAX_ERROR)); + YYABORT; + break; + case EVEX_BAD_PARAMS: + my_error(ER_EVENT_EXEC_TIME_IN_THE_PAST, MYF(0)); + YYABORT; + break; + } + } + } + ; + +ev_status: /* empty */ + | ENABLE_SYM + { + LEX *lex=Lex; + if (!lex->et_compile_phase) + lex->et->set_event_status(1); + } + | DISABLE_SYM + { + LEX *lex=Lex; + + if (!lex->et_compile_phase) + lex->et->set_event_status(0); + } + ; +ev_starts: /* empty */ + | STARTS_SYM expr + { + LEX *lex= Lex; + if (!lex->et_compile_phase) + lex->et->init_starts(YYTHD, $2); + } + ; +ev_ends: /* empty */ + | ENDS_SYM expr + { + LEX *lex= Lex; + if (!lex->et_compile_phase) + { + switch (lex->et->init_ends(YYTHD, $2)) { + case EVEX_PARSE_ERROR: + yyerror(ER(ER_SYNTAX_ERROR)); + YYABORT; + break; + case EVEX_BAD_PARAMS: + my_error(ER_EVENT_ENDS_BEFORE_STARTS, MYF(0)); + YYABORT; + break; + } + } + } + ; +ev_on_completion: /* empty */ + | ON COMPLETION_SYM PRESERVE_SYM + { + LEX *lex=Lex; + if (!lex->et_compile_phase) + lex->et->set_on_completion_drop(false); + } + | ON COMPLETION_SYM NOT_SYM PRESERVE_SYM + { + LEX *lex=Lex; + if (!lex->et_compile_phase) + lex->et->set_on_completion_drop(true); + } + ; +ev_comment: /* empty */ + | COMMENT_SYM TEXT_STRING_sys + { + LEX *lex= Lex; + if (!lex->et_compile_phase) + { + lex->comment= $2; + lex->et->init_comment(YYTHD, &$2); + } + } + ; + +ev_sql_stmt: + { + LEX *lex= Lex; + sp_head *sp; + + if (!(sp= new sp_head())) + YYABORT; + + sp->reset_thd_mem_root(YYTHD); + sp->init(lex); + + sp->m_type= TYPE_ENUM_PROCEDURE; + lex->sphead= sp; + + bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); + lex->sphead->m_chistics= &lex->sp_chistics; + + lex->sphead->m_body_begin= lex->ptr; + if (!lex->et_compile_phase) + lex->et->m_body_begin= lex->ptr; + } + ev_sql_stmt_inner + { + LEX *lex=Lex; + sp_head *sp= lex->sphead; + // return back to the original memory root ASAP + sp->init_strings(YYTHD, lex, NULL); + sp->restore_thd_mem_root(YYTHD); + + lex->sp_chistics.suid= SP_IS_SUID;//always the definer! + + // Restore flag if it was cleared above + if (lex->et->m_old_cmq) + YYTHD->client_capabilities |= CLIENT_MULTI_QUERIES; + + if (!lex->et_compile_phase) + { + lex->et->init_body(YYTHD); + lex->et->init_definer(YYTHD); + } + } + ; + +ev_sql_stmt_inner: + sp_proc_stmt_statement + | sp_proc_stmt_return + | sp_proc_stmt_if + | sp_proc_stmt_case_simple + | sp_proc_stmt_case + | sp_labeled_control {} + | sp_proc_stmt_unlabeled + | sp_proc_stmt_leave + | sp_proc_stmt_iterate + | sp_proc_stmt_label + | sp_proc_stmt_goto + | sp_proc_stmt_open + | sp_proc_stmt_fetch + | sp_proc_stmt_close + ; clear_privileges: /* Nothing */ @@ -1905,6 +2127,28 @@ sp_opt_default: ; sp_proc_stmt: + sp_proc_stmt_statement + | sp_proc_stmt_return + | sp_proc_stmt_if + | sp_proc_stmt_case_simple + | sp_proc_stmt_case + | sp_labeled_control + {} + | sp_proc_stmt_unlabeled + | sp_proc_stmt_leave + | sp_proc_stmt_iterate + | sp_proc_stmt_label + | sp_proc_stmt_goto + | sp_proc_stmt_open + | sp_proc_stmt_fetch + | sp_proc_stmt_close + ; + +sp_proc_stmt_if: + IF sp_if END IF {} + ; + +sp_proc_stmt_statement: { LEX *lex= Lex; @@ -1947,7 +2191,10 @@ sp_proc_stmt: } sp->restore_lex(YYTHD); } - | RETURN_SYM + ; + +sp_proc_stmt_return: + RETURN_SYM { Lex->sphead->reset_lex(YYTHD); } expr { @@ -1970,13 +2217,18 @@ sp_proc_stmt: } sp->restore_lex(YYTHD); } - | IF sp_if END IF {} - | CASE_SYM WHEN_SYM + ; + +sp_proc_stmt_case_simple: + CASE_SYM WHEN_SYM { Lex->sphead->m_flags&= ~sp_head::IN_SIMPLE_CASE; } sp_case END CASE_SYM {} - | CASE_SYM + ; + +sp_proc_stmt_case: + CASE_SYM { Lex->sphead->reset_lex(YYTHD); } expr WHEN_SYM { @@ -2000,9 +2252,10 @@ sp_proc_stmt: { Lex->spcont->pop_pvar(); } - | sp_labeled_control - {} - | { /* Unlabeled controls get a secret label. */ + ; + +sp_proc_stmt_unlabeled: + { /* Unlabeled controls get a secret label. */ LEX *lex= Lex; lex->spcont->push_label((char *)"", lex->sphead->instructions()); @@ -2013,7 +2266,10 @@ sp_proc_stmt: lex->sphead->backpatch(lex->spcont->pop_label()); } - | LEAVE_SYM label_ident + ; + +sp_proc_stmt_leave: + LEAVE_SYM label_ident { LEX *lex= Lex; sp_head *sp = lex->sphead; @@ -2043,7 +2299,10 @@ sp_proc_stmt: sp->add_instr(i); } } - | ITERATE_SYM label_ident + ; + +sp_proc_stmt_iterate: + ITERATE_SYM label_ident { LEX *lex= Lex; sp_head *sp= lex->sphead; @@ -2071,7 +2330,10 @@ sp_proc_stmt: sp->add_instr(i); } } - | LABEL_SYM IDENT + ; + +sp_proc_stmt_label: + LABEL_SYM IDENT { #ifdef SP_GOTO LEX *lex= Lex; @@ -2096,7 +2358,10 @@ sp_proc_stmt: YYABORT; #endif } - | GOTO_SYM IDENT + ; + +sp_proc_stmt_goto: + GOTO_SYM IDENT { #ifdef SP_GOTO LEX *lex= Lex; @@ -2156,7 +2421,10 @@ sp_proc_stmt: YYABORT; #endif } - | OPEN_SYM ident + ; + +sp_proc_stmt_open: + OPEN_SYM ident { LEX *lex= Lex; sp_head *sp= lex->sphead; @@ -2171,7 +2439,10 @@ sp_proc_stmt: i= new sp_instr_copen(sp->instructions(), lex->spcont, offset); sp->add_instr(i); } - | FETCH_SYM sp_opt_fetch_noise ident INTO + ; + +sp_proc_stmt_fetch: + FETCH_SYM sp_opt_fetch_noise ident INTO { LEX *lex= Lex; sp_head *sp= lex->sphead; @@ -2188,7 +2459,10 @@ sp_proc_stmt: } sp_fetch_list { } - | CLOSE_SYM ident + ; + +sp_proc_stmt_close: + CLOSE_SYM ident { LEX *lex= Lex; sp_head *sp= lex->sphead; @@ -3913,8 +4187,64 @@ alter: } view_list_opt AS view_select view_check_option {} - ; + | ALTER EVENT_SYM sp_name + { + LEX *lex=Lex; + event_timed *et; + if (lex->et) + { + // ToDo Andrey : Change the error message + my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "EVENT"); + YYABORT; + } + lex->spname= 0;//defensive + + et= new event_timed();// implicitly calls event_timed::init() + lex->et = et; + et->init_name(YYTHD, $3); + + /* + We have to turn of CLIENT_MULTI_QUERIES while parsing a + stored procedure, otherwise yylex will chop it into pieces + at each ';'. + */ + et->m_old_cmq= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES; + YYTHD->client_capabilities &= ~CLIENT_MULTI_QUERIES; + + /* + defensive. in sql_parse.cc it is checked whether is not null + and then deleted + */ + lex->sphead= 0; + } + ev_on_schedule + ev_rename_to + ev_on_completion + ev_status + ev_comment + ev_opt_sql_stmt + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_ALTER_EVENT; + } + ; + +ev_on_schedule: /* empty */ + | ON SCHEDULE_SYM ev_schedule_time; + +ev_opt_sql_stmt: /* empty*/ + | DO_SYM ev_sql_stmt; + +ev_rename_to: /* empty */ + | RENAME TO_SYM sp_name + { + LEX *lex=Lex; + lex->spname= $3; //use lex's spname to hold the new name + //the original name is in the event_timed object + } + ; + ident_or_empty: /* empty */ { $$= 0; } | ident { $$= $1.str; }; @@ -6620,7 +6950,26 @@ drop: lex->sql_command= SQLCOM_DROP_TRIGGER; lex->spname= $3; } - ; + | DROP EVENT_SYM if_exists sp_name + { + LEX *lex=Lex; + event_timed *et; + + if (lex->et) + { + // ToDo Andrey : Change the error message + my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "EVENT"); + YYABORT; + } + + et= new event_timed; + lex->et = et; + et->init_name(YYTHD, $4); + + lex->sql_command = SQLCOM_DROP_EVENT; + lex->drop_if_exists= $3; + } + ; table_list: table_name @@ -7272,7 +7621,14 @@ show_param: Lex->spname= $3; #endif } - ; + | CREATE EVENT_SYM sp_name + { + LEX *lex= Lex; + + lex->sql_command = SQLCOM_SHOW_CREATE_EVENT; + lex->spname= $3; + }; + ; show_engine_param: STATUS_SYM @@ -8159,6 +8515,7 @@ keyword_sp: | AGGREGATE_SYM {} | ALGORITHM_SYM {} | ANY_SYM {} + | AT_SYM {} | AUTO_INC {} | AVG_ROW_LENGTH {} | AVG_SYM {} @@ -8179,6 +8536,7 @@ keyword_sp: | COLUMNS {} | COMMITTED_SYM {} | COMPACT_SYM {} + | COMPLETION_SYM {} | COMPRESSED_SYM {} | CONCURRENT {} | CONSISTENT_SYM {} @@ -8195,12 +8553,15 @@ keyword_sp: | DUMPFILE {} | DUPLICATE_SYM {} | DYNAMIC_SYM {} + | ENDS_SYM {} | ENUM {} | ENGINE_SYM {} | ENGINES_SYM {} | ERRORS {} | ESCAPE_SYM {} + | EVENT_SYM {} | EVENTS_SYM {} + | EVERY_SYM {} | EXPANSION_SYM {} | EXTENDED_SYM {} | FAST_SYM {} @@ -8293,6 +8654,7 @@ keyword_sp: | PHASE_SYM {} | POINT_SYM {} | POLYGON {} + | PRESERVE_SYM {} | PREV_SYM {} | PRIVILEGES {} | PROCESS {} @@ -8322,6 +8684,7 @@ keyword_sp: | ROW_FORMAT_SYM {} | ROW_SYM {} | RTREE_SYM {} + | SCHEDULE_SYM {} | SECOND_SYM {} | SERIAL_SYM {} | SERIALIZABLE_SYM {} @@ -8335,6 +8698,7 @@ keyword_sp: | SQL_BUFFER_RESULT {} | SQL_NO_CACHE_SYM {} | SQL_THREAD {} + | STARTS_SYM {} | STATUS_SYM {} | STORAGE_SYM {} | STRING_SYM {} @@ -9061,6 +9425,7 @@ object_privilege: | CREATE ROUTINE_SYM { Lex->grant |= CREATE_PROC_ACL; } | ALTER ROUTINE_SYM { Lex->grant |= ALTER_PROC_ACL; } | CREATE USER { Lex->grant |= CREATE_USER_ACL; } + | EVENT_SYM { Lex->grant |= EVENT_ACL;} ; diff --git a/sql/table.cc b/sql/table.cc index aeccab78211..d5a432306f7 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -282,7 +282,8 @@ int open_table_def(THD *thd, TABLE_SHARE *share, uint db_flags) */ if (share->db.length == 5 && !my_strcasecmp(system_charset_info, share->db.str, "mysql") && - !my_strcasecmp(system_charset_info, share->table_name.str, "proc")) + (!my_strcasecmp(system_charset_info, share->table_name.str, "proc") || + !my_strcasecmp(system_charset_info, share->table_name.str, "event"))) share->system_table= 1; error_given= 1; } diff --git a/sql/tztime.cc b/sql/tztime.cc index eba2f8f4a7b..50c3cf1fe4f 100644 --- a/sql/tztime.cc +++ b/sql/tztime.cc @@ -807,6 +807,18 @@ sec_since_epoch(int year, int mon, int mday, int hour, int min ,int sec) } + /* + Works like sec_since_epoch but expects TIME structure as parameter. +*/ + +my_time_t +sec_since_epoch_TIME(TIME *t) +{ + return sec_since_epoch(t->year, t->month, t->day, + t->hour, t->minute, t->second); +} + + /* Converts local time in broken down TIME representation to my_time_t representation. diff --git a/sql/tztime.h b/sql/tztime.h index a168fe4fb73..312451d84e0 100644 --- a/sql/tztime.h +++ b/sql/tztime.h @@ -64,6 +64,7 @@ extern Time_zone * my_tz_find(const String *name, TABLE_LIST *tz_tables); extern Time_zone * my_tz_find_with_opening_tz_tables(THD *thd, const String *name); extern my_bool my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap); extern void my_tz_free(); +my_time_t sec_since_epoch_TIME(TIME *t); extern TABLE_LIST fake_time_zone_tables_list; From 582299d700bbf61786caa66a44c50088d8658d96 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 2 Dec 2005 13:22:15 +0100 Subject: [PATCH 02/28] add these, forgotten in the previous commit --- libmysqld/event.h | 210 +++++ sql/event.cc | 2310 +++++++++++++++++++++++++++++++++++++++++++++ sql/event.h | 210 +++++ 3 files changed, 2730 insertions(+) create mode 100644 libmysqld/event.h create mode 100644 sql/event.cc create mode 100644 sql/event.h diff --git a/libmysqld/event.h b/libmysqld/event.h new file mode 100644 index 00000000000..5ba96e401ce --- /dev/null +++ b/libmysqld/event.h @@ -0,0 +1,210 @@ +/* -*- C++ -*- */ +#ifndef _EVENT_H_ +#define _EVENT_H_ +#include "sp_head.h" + + +extern ulong opt_event_executor; + +#define EVEX_OK 0 +#define EVEX_KEY_NOT_FOUND -1 +#define EVEX_OPEN_TABLE_FAILED -2 +#define EVEX_WRITE_ROW_FAILED -3 +#define EVEX_DELETE_ROW_FAILED -4 +#define EVEX_GET_FIELD_FAILED -5 +#define EVEX_PARSE_ERROR -6 +#define EVEX_INTERNAL_ERROR -7 +#define EVEX_NO_DB_ERROR -8 +#define EVEX_GENERAL_ERROR -9 +#define EVEX_BAD_PARAMS -10 +#define EVEX_NOT_RUNNING -11 + +#define EVENT_EXEC_NO_MORE (1L << 0) +#define EVENT_NOT_USED (1L << 1) + + +enum enum_event_on_completion +{ + MYSQL_EVENT_ON_COMPLETION_DROP = 1, + MYSQL_EVENT_ON_COMPLETION_PRESERVE +}; + +enum enum_event_status +{ + MYSQL_EVENT_ENABLED = 1, + MYSQL_EVENT_DISABLED +}; + + +class event_timed +{ + event_timed(const event_timed &); /* Prevent use of these */ + void operator=(event_timed &); + +public: + LEX_STRING m_db; + LEX_STRING m_name; + LEX_STRING m_qname; // db.name + LEX_STRING m_body; + + LEX_STRING m_definer_user; + LEX_STRING m_definer_host; + LEX_STRING m_definer;// combination of user and host + + LEX_STRING m_comment; + TIME m_starts; + TIME m_ends; + TIME m_execute_at; + longlong m_expr; + interval_type m_interval; + longlong m_created; + longlong m_modified; + TIME m_last_executed; + enum enum_event_on_completion m_on_completion; + enum enum_event_status m_status; + sp_head *m_sphead; + + + + uint m_old_cmq; // Old CLIENT_MULTI_QUERIES value + const uchar *m_body_begin; + + bool m_dropped; + bool m_free_sphead_on_delete; + uint m_flags;//all kind of purposes + bool m_last_executed_changed; + bool m_status_changed; + + event_timed():m_expr(0), m_created(0), m_modified(0), + m_on_completion(MYSQL_EVENT_ON_COMPLETION_DROP), + m_status(MYSQL_EVENT_ENABLED), m_sphead(0), m_dropped(false), + m_free_sphead_on_delete(true), m_flags(0), + m_last_executed_changed(false), m_status_changed(false) + { init(); } + + ~event_timed() + { + if (m_free_sphead_on_delete) + free_sp(); + } + + void + init(); + + int + init_definer(THD *thd); + + int + init_execute_at(THD *thd, Item *expr); + + int + init_interval(THD *thd, Item *expr, interval_type interval); + + void + init_name(THD *thd, sp_name *name); + + int + init_starts(THD *thd, Item *starts); + + int + init_ends(THD *thd, Item *ends); + + void + event_timed::init_body(THD *thd); + + void + init_comment(THD *thd, LEX_STRING *comment); + + void + set_on_completion_drop(bool drop); + + void + set_event_status(bool enabled); + + int + load_from_row(MEM_ROOT *mem_root, TABLE *table); + + bool + compute_next_execution_time(); + + void + mark_last_executed(); + + bool + drop(THD *thd); + + bool + update_fields(THD *thd); + + char * + get_show_create_event(THD *thd, uint *length); + + int + execute(THD *thd, MEM_ROOT *mem_root); + + int + compile(THD *thd, MEM_ROOT *mem_root); + + void free_sp() + { + if (m_sphead) + { + delete m_sphead; + m_sphead= 0; + } + } +}; + + +int +evex_create_event(THD *thd, event_timed *et, uint create_options); + +int +evex_update_event(THD *thd, sp_name *name, event_timed *et); + +int +evex_drop_event(THD *thd, event_timed *et, bool drop_if_exists); + + +int +init_events(); + +void +shutdown_events(); + +/* +typedef struct st_event_item { + my_time_t execute_at; + sp_head *proc; + char *definer_user; + char *definer_host; +} EVENT_ITEM; +*/ + + +/* +CREATE TABLE `event` ( + `db` varchar(64) character set latin1 collate latin1_bin NOT NULL default '', + `name` varchar(64) NOT NULL default '', + `body` blob NOT NULL, + `definer` varchar(77) character set latin1 collate latin1_bin NOT NULL default '', + `execute_at` datetime default NULL, + `transient_expression` int(11) default NULL, + `interval_type` 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') DEFAULT NULL, + `created` timestamp NOT NULL default '0000-00-00 00:00:00', + `modified` timestamp NOT NULL default '0000-00-00 00:00:00', + `last_executed` datetime default NULL, + `starts` datetime default NULL, + `ends` datetime default NULL, + `status` enum('ENABLED','DISABLED') NOT NULL default 'ENABLED', + `on_completion` enum('DROP','PRESERVE') NOT NULL default 'DROP', + `comment` varchar(64) character set latin1 collate latin1_bin NOT NULL default '', + PRIMARY KEY (`db`,`name`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +*/ + +#endif /* _EVENT_H_ */ diff --git a/sql/event.cc b/sql/event.cc new file mode 100644 index 00000000000..007391f76a0 --- /dev/null +++ b/sql/event.cc @@ -0,0 +1,2310 @@ +#include "mysql_priv.h" +//#include "sql_acl.h" +#include "event.h" +#include "sp.h" + +/* + TODO list : + 1. Move event_timed class to event_timed.cc as well as the part of the header + 2. Do something aboute the events replication. SQL statements issued while + executing an event should not be logged into the binary log. + 3. Add a lock and use it for guarding access to events_array dynamic array. + 4. Add checks everywhere where new instance of THD is created. NULL can be + returned and this will crash the server. The server will crash probably + later but should not be in this code! Add a global variable, and a lock + to guard it, that will specify an error in a worker thread so preventing + new threads from being spawned. + 5. Move executor related code to event_executor.cc and .h + 6. Maybe move all allocations during parsing to evex_mem_root thus saving + double parsing in evex_create_event! + 7. If the server is killed (stopping) try to kill executing events.. + 8. What happens if one renames an event in the DB while it is in memory? + Or even deleting it? + 9. created & modified in the table should be UTC? + 10. Add a lock to event_timed to serialize execution of an event - do not + allow parallel executions. Hmm, however how last_executed is marked + then? The call to event_timed::mark_last_executed() must be moved to + event_timed::execute()? + 11. Consider using conditional variable when doing shutdown instead of + waiting some time (tries < 5). + 12. Fix event_timed::get_show_create_event. + 13. Add logging to file. + 14. Add function documentation whenever needed. +*/ + +enum +{ + EVEX_FIELD_DB = 0, + EVEX_FIELD_NAME, + EVEX_FIELD_BODY, + EVEX_FIELD_DEFINER, + EVEX_FIELD_EXECUTE_AT, + EVEX_FIELD_INTERVAL_EXPR, + EVEX_FIELD_TRANSIENT_INTERVAL, + EVEX_FIELD_CREATED, + EVEX_FIELD_MODIFIED, + EVEX_FIELD_LAST_EXECUTED, + EVEX_FIELD_STARTS, + EVEX_FIELD_ENDS, + EVEX_FIELD_STATUS, + EVEX_FIELD_ON_COMPLETION, + EVEX_FIELD_COMMENT, + EVEX_FIELD_COUNT /* a cool trick to count the number of fields :) */ +}; + + +#define EVEX_OPEN_TABLE_FOR_UPDATE() \ + open_proc_type_table_for_update(thd, "event", &mysql_event_table_exists) + +static bool mysql_event_table_exists= 1; +static DYNAMIC_ARRAY events_array; +static DYNAMIC_ARRAY evex_executing_queue; +static MEM_ROOT evex_mem_root; +static uint workers_count; +static bool evex_is_running= false; +static pthread_mutex_t LOCK_event_arrays, + LOCK_workers_count, + LOCK_evex_running; + +extern int yyparse(void *thd); + +ulong opt_event_executor; +my_bool event_executor_running_global_var= false; + +extern ulong thread_created; +//extern volatile uint thread_running; +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +//////////////// Static functions follow /////////////////////////// +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// + + + +/* NOTE Andrey: Document better + Compares two TIME structures. + + a > b -> 1 + a = b -> 0 + a < b -> -1 +*/ + +static inline int +my_time_compare(TIME *a, TIME *b) +{ +/* + Or maybe it is faster to use TIME_to_ulonglong_datetime + for "a" and "b" +*/ + + DBUG_ENTER("my_time_compare"); + if (a->year > b->year) + DBUG_RETURN(1); + + if (a->year < b->year) + DBUG_RETURN(-1); + + if (a->month > b->month) + DBUG_RETURN(1); + + if (a->month < b->month) + DBUG_RETURN(-1); + + if (a->day > b->day) + DBUG_RETURN(1); + + if (a->day < b->day) + DBUG_RETURN(-1); + + if (a->hour > b->hour) + DBUG_RETURN(1); + + if (a->hour < b->hour) + DBUG_RETURN(-1); + + if (a->minute > b->minute) + DBUG_RETURN(1); + + if (a->minute < b->minute) + DBUG_RETURN(-1); + + if (a->second > b->second) + DBUG_RETURN(1); + + if (a->second < b->second) + DBUG_RETURN(-1); + + /*!! second_part is not compared !*/ + + DBUG_RETURN(0); +} + + +static int +event_timed_compare(event_timed **a, event_timed **b) +{ + return my_time_compare(&(*a)->m_execute_at, &(*b)->m_execute_at); +/* + if (a->sort > b->sort) + return -1; + if (a->sort < b->sort) + return 1; + 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 + + 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) +{ + DBUG_ENTER("evex_fill_row"); + int ret=0; + + if (table->s->fields != EVEX_FIELD_COUNT) + goto get_field_failed; + + DBUG_PRINT("info", ("m_db.len=%d",et->m_db.length)); + DBUG_PRINT("info", ("m_name.len=%d",et->m_name.length)); + + table->field[EVEX_FIELD_DB]-> + store(et->m_db.str, et->m_db.length, system_charset_info); + table->field[EVEX_FIELD_NAME]-> + store(et->m_name.str, et->m_name.length, system_charset_info); + + table->field[EVEX_FIELD_ON_COMPLETION]->set_notnull(); + table->field[EVEX_FIELD_ON_COMPLETION]->store((longlong)et->m_on_completion); + + table->field[EVEX_FIELD_STATUS]->set_notnull(); + table->field[EVEX_FIELD_STATUS]->store((longlong)et->m_status); + et->m_status_changed= false; + + // ToDo: Andrey. How to use users current charset? + if (et->m_body.str) + table->field[EVEX_FIELD_BODY]-> + store(et->m_body.str, et->m_body.length, system_charset_info); + + if (et->m_starts.year) + { + table->field[EVEX_FIELD_STARTS]->set_notnull();// set NULL flag to OFF + table->field[EVEX_FIELD_STARTS]->store_time(&et->m_starts,MYSQL_TIMESTAMP_DATETIME); + } + + if (et->m_ends.year) + { + table->field[EVEX_FIELD_ENDS]->set_notnull(); + table->field[EVEX_FIELD_ENDS]->store_time(&et->m_ends, MYSQL_TIMESTAMP_DATETIME); + } + + if (et->m_expr) + { + // m_expr was fixed in init_interval +// et->m_expr->save_in_field(table->field[EVEX_FIELD_INTERVAL_EXPR], (my_bool)TRUE); + + table->field[EVEX_FIELD_INTERVAL_EXPR]->set_notnull(); + table->field[EVEX_FIELD_INTERVAL_EXPR]->store((longlong)et->m_expr); + + table->field[EVEX_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[EVEX_FIELD_TRANSIENT_INTERVAL]->store((longlong)et->m_interval + 1); + } + else if (et->m_execute_at.year) + { + // fix_fields already called in init_execute_at + table->field[EVEX_FIELD_EXECUTE_AT]->set_notnull(); + table->field[EVEX_FIELD_EXECUTE_AT]->store_time(&et->m_execute_at, MYSQL_TIMESTAMP_DATETIME); + + //this will make it NULL because we don't call set_notnull + table->field[EVEX_FIELD_TRANSIENT_INTERVAL]->store((longlong) 0); + } + else + { + // it is normal to be here when the action is update + // this is an error if the action is create. something is borked + } + + ((Field_timestamp *)table->field[EVEX_FIELD_MODIFIED])->set_time(); + + if ((et->m_comment).length) + table->field[EVEX_FIELD_COMMENT]-> + store((et->m_comment).str, (et->m_comment).length, system_charset_info); + + DBUG_RETURN(0); +parse_error: + DBUG_RETURN(EVEX_PARSE_ERROR); +general_error: + DBUG_RETURN(EVEX_GENERAL_ERROR); +get_field_failed: + DBUG_RETURN(EVEX_GET_FIELD_FAILED); +} + + +/* + Creates an event in mysql.event + + SYNOPSIS + db_create_event() + thd THD + et event_timed object containing information for the event + + DESCRIPTION + Creates an event. Relies on evex_fill_row which is shared with + db_update_event. The name of the event is inside "et". +*/ + +static int +db_create_event(THD *thd, event_timed *et) +{ + int ret; + TABLE *table; + TABLE_LIST tables; + char definer[HOSTNAME_LENGTH+USERNAME_LENGTH+2]; + char olddb[128]; + bool dbchanged; + DBUG_ENTER("db_create_event"); + DBUG_PRINT("enter", ("name: %.*s", et->m_name.length, et->m_name.str)); + + dbchanged= false; + if ((ret= sp_use_new_db(thd, et->m_db.str, olddb, sizeof(olddb), + 0, &dbchanged))) + { + DBUG_PRINT("info", ("cannot use_new_db. code=%d", ret)); + DBUG_RETURN(EVEX_NO_DB_ERROR); + } + + bzero(&tables, sizeof(tables)); + tables.db= (char*)"mysql"; + tables.table_name= tables.alias= (char*)"event"; + + if (!(table= EVEX_OPEN_TABLE_FOR_UPDATE())) + { + if (dbchanged) + (void)mysql_change_db(thd, olddb, 1); + DBUG_RETURN(SP_OPEN_TABLE_FAILED); + } + + restore_record(table, s->default_values); // Get default values for fields + strxmov(definer, et->m_definer_user.str, "@", et->m_definer_host.str, NullS); + + if (table->s->fields != EVEX_FIELD_COUNT) + { + ret= EVEX_GET_FIELD_FAILED; + goto done; + } +/* TODO : Uncomment these and add handling in sql_parse.cc or here + + if (sp->m_name.length > table->field[MYSQL_PROC_FIELD_NAME]->field_length) + { + ret= SP_BAD_IDENTIFIER; + goto done; + } + if (sp->m_body.length > table->field[MYSQL_PROC_FIELD_BODY]->field_length) + { + ret= SP_BODY_TOO_LONG; + goto done; + } +*/ + if (!(et->m_expr) && !(et->m_execute_at.year)) + { + DBUG_PRINT("error", ("neither m_expr nor m_execute_as is set!")); + ret= EVEX_WRITE_ROW_FAILED; + goto done; + } + ret= table->field[EVEX_FIELD_DEFINER]-> + store(definer, (uint)strlen(definer), system_charset_info); + if (ret) + { + ret= EVEX_PARSE_ERROR; + goto done; + } + + ((Field_timestamp *)table->field[EVEX_FIELD_CREATED])->set_time(); + if ((ret= evex_fill_row(thd, table, et))) + goto done; + + ret= EVEX_OK; + if (table->file->write_row(table->record[0])) + ret= EVEX_WRITE_ROW_FAILED; + else if (mysql_bin_log.is_open()) + { + thd->clear_error(); + /* Such a statement can always go directly to binlog, no trans cache */ + Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE); + mysql_bin_log.write(&qinfo); + } + +done: + close_thread_tables(thd); + if (dbchanged) + (void)mysql_change_db(thd, olddb, 1); + DBUG_RETURN(ret); +} + + +/* + Used to execute ALTER EVENT + + SYNOPSIS + db_update_event() + thd THD + sp_name the name of the event to alter + et event's data + + 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, sp_name *name, event_timed *et) +{ + TABLE *table; + int ret; + DBUG_ENTER("db_update_event"); + DBUG_PRINT("enter", ("name: %.*s", et->m_name.length, et->m_name.str)); + if (name) + DBUG_PRINT("enter", ("rename to: %.*s", name->m_name.length, name->m_name.str)); + + // Todo: Handle in sql_prepare.cc SP_OPEN_TABLE_FAILED + if (!(table= EVEX_OPEN_TABLE_FOR_UPDATE())) + DBUG_RETURN(SP_OPEN_TABLE_FAILED); + + ret= sp_db_find_routine_aux(thd, 0/*notype*/, et->m_db, et->m_name, table); + if (ret == EVEX_OK) + { + store_record(table,record[1]); + table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; // Don't update create on row update. + ret= evex_fill_row(thd, table, et); + if (ret) + goto done; + + if (name) + { + table->field[EVEX_FIELD_DB]-> + store(name->m_db.str, name->m_db.length, system_charset_info); + table->field[EVEX_FIELD_NAME]-> + store(name->m_name.str, name->m_name.length, system_charset_info); + } + + if ((table->file->update_row(table->record[1],table->record[0]))) + ret= EVEX_WRITE_ROW_FAILED; + } +done: + close_thread_tables(thd); + DBUG_RETURN(ret); +} + +/* + Use sp_name for look up, return in **ett if found +*/ +static int +db_find_event(THD *thd, sp_name *name, event_timed **ett) +{ + TABLE *table; + int ret; + const char *definer; + char *ptr; + event_timed *et; + Open_tables_state open_tables_state_backup; + DBUG_ENTER("db_find_event"); + DBUG_PRINT("enter", ("name: %*s", + name->m_name.length, name->m_name.str)); + + + if (!(table= open_proc_type_table_for_read(thd, &open_tables_state_backup, + "event", &mysql_event_table_exists))) + DBUG_RETURN(SP_OPEN_TABLE_FAILED); + + if ((ret= sp_db_find_routine_aux(thd, 0/*notype*/, name->m_db, name->m_name, + table)) != SP_OK) + goto done; + + et= new event_timed; + + /* + The table should not be closed beforehand. + ::load_from_row only loads and does not compile + */ + if ((ret= et->load_from_row(&evex_mem_root, table))) + goto done; + +done: + if (ret && et) + { + delete et; + et= 0; + } + close_thread_tables(thd); + thd->restore_backup_open_tables_state(&open_tables_state_backup); + *ett= et; + DBUG_RETURN(ret); +} + + +static int +evex_load_and_compile_event(THD * thd, sp_name *spn, bool use_lock) +{ + int ret= 0; + MEM_ROOT *tmp_mem_root; + event_timed *ett, *ett_copy; + + DBUG_ENTER("db_load_and_compile_event"); + DBUG_PRINT("enter", ("name: %*s", spn->m_name.length, spn->m_name.str)); + + tmp_mem_root= thd->mem_root; + thd->mem_root= &evex_mem_root; + + if (db_find_event(thd, spn, &ett)) + { + ret= EVEX_GENERAL_ERROR; + goto done; + } + + /* + allocate on evex_mem_root. call without evex_mem_root and + and m_sphead will not be cleared! + */ + if ((ret= ett->compile(thd, &evex_mem_root))) + { + thd->mem_root= tmp_mem_root; + goto done; + } + + ett->compute_next_execution_time(); + if (use_lock) + VOID(pthread_mutex_lock(&LOCK_event_arrays)); + + VOID(push_dynamic(&events_array,(gptr) ett)); + ett_copy= dynamic_element(&events_array, events_array.elements - 1, + event_timed*); + VOID(push_dynamic(&evex_executing_queue, (gptr) &ett_copy)); + + /* + There is a copy in the array which we don't need. m_sphead won't be + destroyed. + */ + ett->m_free_sphead_on_delete= false; + delete ett; + + /* + We find where the first element resides in the arraay. And then do a + qsort of events_array.elements (the current number of elements). + We know that the elements are stored in a contiguous block w/o holes. + */ + qsort((gptr) dynamic_element(&evex_executing_queue, 0, event_timed**), + evex_executing_queue.elements, + sizeof(event_timed **), + (qsort_cmp) event_timed_compare); + + if (use_lock) + VOID(pthread_mutex_unlock(&LOCK_event_arrays)); + +done: + if (thd->mem_root != tmp_mem_root) + thd->mem_root= tmp_mem_root; + + if (spn) + delete spn; + DBUG_RETURN(ret); +} + + +static int +evex_remove_from_cache(LEX_STRING *db, LEX_STRING *name, bool use_lock) +{ + uint i; + int ret= 0; + bool need_second_pass= true; + + DBUG_ENTER("evex_remove_from_cache"); + /* + It is possible that 2 (or 1) pass(es) won't find the event in memory. + The reason is that DISABLED events are not cached. + */ + + if (use_lock) + VOID(pthread_mutex_lock(&LOCK_event_arrays)); + + for (i= 0; i < evex_executing_queue.elements; ++i) + { + event_timed **p_et= dynamic_element(&evex_executing_queue, i, event_timed**); + event_timed *ett= *p_et; + DBUG_PRINT("info", ("[%s.%s]==[%s.%s]?",db->str,name->str, + ett->m_db.str, ett->m_name.str)); + if (name->length == ett->m_name.length && + db->length == ett->m_db.length && + 0 == strncmp(db->str, ett->m_db.str, db->length) && + 0 == strncmp(name->str, ett->m_name.str, name->length) + ) + { + int idx; + //we are lucky the event is in the executing queue, no need of second pass + need_second_pass= false; + idx= get_index_dynamic(&events_array, (gptr) ett); + if (idx != -1) + { + //destruct first and then remove. the destructor will delete sp_head + ett->free_sp(); + delete_dynamic_element(&events_array, idx); + delete_dynamic_element(&evex_executing_queue, i); + } + else + { + //this should never happen + DBUG_PRINT("error", ("Sth weird with get_index_dynamic. %d." + "i=%d idx=%d evex_ex_queue.buf=%p evex_ex_queue.elements=%d ett=%p\n" + "events_array=%p events_array.elements=%d events_array.buf=%p\n" + "p_et=%p ett=%p", + __LINE__, i, idx, &evex_executing_queue.buffer, + evex_executing_queue.elements, ett, &events_array, + events_array.elements, events_array.buffer, p_et, ett)); + ret= EVEX_GENERAL_ERROR; + goto done; + } + //ok we have cleaned + } + } + + /* + ToDo Andrey : Think about whether second pass is needed. All events + that are in memory are enabled. If an event is being + disabled (by a SQL stmt) it will be uncached. Hmm... + However is this true for events that has been + disabled because of another reason like - no need + to be executed because ENDS is in the past? + For instance, second_pass is needed when an event + was created as DISABLED but then altered as ENABLED. + */ + if (need_second_pass) + //we haven't found the event in the executing queue. This is nice! :) + //Look for it in the events_array. + for (i= 0; i < events_array.elements; ++i) + { + event_timed *ett= dynamic_element(&events_array, i, event_timed*); + + if (name->length == ett->m_name.length && + db->length == ett->m_db.length && + 0 == strncmp(db->str, ett->m_db.str, db->length) && + 0 == strncmp(name->str, ett->m_name.str, name->length) + ) + delete_dynamic_element(&events_array, i); + } + +done: + if (use_lock) + VOID(pthread_mutex_unlock(&LOCK_event_arrays)); + + DBUG_RETURN(ret); +} + + +/* + Exported functions follow +*/ + +/* + The function exported to the world for creating of events. + + SYNOPSIS + evex_create_event() + thd THD + et event's data + create_options Options specified when in the query. We are + interested whether there is IF NOT EXISTS + + NOTES + - in case there is an event with the same name (db) and + IF NOT EXISTS is specified, an warning is put into the W stack. + TODO + - Add code for in-memory structures - caching & uncaching. +*/ + +int +evex_create_event(THD *thd, event_timed *et, uint create_options) +{ + int ret = 0; + sp_name *spn= 0; + + DBUG_ENTER("evex_create_event"); + DBUG_PRINT("enter", ("name: %*s options:%d", et->m_name.length, + et->m_name.str, create_options)); + + VOID(pthread_mutex_lock(&LOCK_evex_running)); + if (!evex_is_running) + // TODO: put an warning to the user here. + // Is it needed? (Andrey, 051129) + {} + VOID(pthread_mutex_unlock(&LOCK_evex_running)); + + + if ((ret = db_create_event(thd, et)) == EVEX_WRITE_ROW_FAILED && + (create_options & HA_LEX_CREATE_IF_NOT_EXISTS)) + { + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_DB_CREATE_EXISTS, ER(ER_DB_CREATE_EXISTS), + "EVENT", thd->lex->et->m_name.str); + ret= 0; + goto done; + } + /* + A warning is thrown only when create_options is set to + HA_LEX_CREATE_IF_NOT_EXISTS. In this case if EVEX_WRITE_ROW_FAILED, + which means that we have duplicated key -> warning. In all + other cases -> error. + */ + if (ret) + goto done; + + VOID(pthread_mutex_lock(&LOCK_evex_running)); + if (!evex_is_running) + { + VOID(pthread_mutex_unlock(&LOCK_evex_running)); + goto done; + } + VOID(pthread_mutex_unlock(&LOCK_evex_running)); + + //cache only if the event is ENABLED + if (et->m_status == MYSQL_EVENT_ENABLED) + { + spn= new sp_name(et->m_db, et->m_name); + if ((ret= evex_load_and_compile_event(thd, spn, true))) + goto done; + } + +done: + if (spn) + delete spn; + DBUG_RETURN(ret); +} + + +/* + The function exported to the world for alteration of events. + + SYNOPSIS + evex_update_event() + thd THD + name the real name of the event. + et event's data + + 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. + TODO + - Add code for in-memory structures - caching & uncaching. +*/ + +int +evex_update_event(THD *thd, sp_name *name, event_timed *et) +{ + int ret, i; + bool need_second_pass= true; + sp_name *spn= 0; + + DBUG_ENTER("evex_update_event"); + DBUG_PRINT("enter", ("name: %*s", et->m_name.length, et->m_name.str)); + + VOID(pthread_mutex_lock(&LOCK_evex_running)); + if (!evex_is_running) + // put an warning to the user here + {} + VOID(pthread_mutex_unlock(&LOCK_evex_running)); + + if ((ret= db_update_event(thd, name, et))) + goto done_no_evex; + + VOID(pthread_mutex_lock(&LOCK_evex_running)); + if (!evex_is_running) + // not running - therefore no memory structures + goto done_no_evex; + VOID(pthread_mutex_unlock(&LOCK_evex_running)); + + /* + It is possible that 2 (or 1) pass(es) won't find the event in memory. + The reason is that DISABLED events are not cached. + */ + VOID(pthread_mutex_lock(&LOCK_event_arrays)); + if (name) + { + evex_remove_from_cache(&name->m_db, &name->m_name, false); + if (et->m_status == MYSQL_EVENT_ENABLED && + (ret= evex_load_and_compile_event(thd, name, false)) + ) + goto done; + } + else + { + evex_remove_from_cache(&et->m_db, &et->m_name, false); + spn= new sp_name(et->m_db, et->m_name); + if (et->m_status == MYSQL_EVENT_ENABLED && + (ret= evex_load_and_compile_event(thd, spn, false)) + ) + { + delete spn; + goto done; + } + } + +done: + VOID(pthread_mutex_unlock(&LOCK_event_arrays)); + +done_no_evex: + DBUG_RETURN(ret); +} + + +/* + Checks for existance of a specified event + + SYNOPSIS + evex_event_exists() + thd THD + et event's name + + NOTES + Currently unused +*/ + +bool +evex_event_exists(THD *thd, event_timed *et) +{ + TABLE *table; + int ret; + bool opened= FALSE; + Open_tables_state open_tables_state_backup; + DBUG_ENTER("evex_event_exists"); + + if (!(table= open_proc_type_table_for_read(thd, &open_tables_state_backup, + "event", &mysql_event_table_exists))) + DBUG_RETURN(SP_OPEN_TABLE_FAILED); + + ret= sp_db_find_routine_aux(thd, 0/*notype*/, et->m_db, et->m_name, table); + + close_thread_tables(thd); + thd->restore_backup_open_tables_state(&open_tables_state_backup); + thd->clear_error(); + + DBUG_RETURN(ret == SP_OK); +} + + +/* + 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 + + TODO + Update in-memory structures +*/ + +int +evex_drop_event(THD *thd, event_timed *et, bool drop_if_exists) +{ + TABLE *table; + int ret; + bool opened; + DBUG_ENTER("evex_drop_event"); + + VOID(pthread_mutex_lock(&LOCK_evex_running)); + if (!evex_is_running) + // put an warning to the user here + {} + VOID(pthread_mutex_unlock(&LOCK_evex_running)); + + if (!(table= EVEX_OPEN_TABLE_FOR_UPDATE())) + DBUG_RETURN(SP_OPEN_TABLE_FAILED); + + ret= sp_db_find_routine_aux(thd, 0/*notype*/, et->m_db, et->m_name, table); + + if (ret == EVEX_OK) + { + if (table->file->delete_row(table->record[0])) + { + ret= EVEX_DELETE_ROW_FAILED; + goto done; + } + } + else if (ret == SP_KEY_NOT_FOUND && drop_if_exists) + { + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST), + "EVENT", thd->lex->et->m_name.str); + ret= 0; + goto done; + } else + goto done; + + VOID(pthread_mutex_lock(&LOCK_evex_running)); + if (evex_is_running) + ret= evex_remove_from_cache(&et->m_db, &et->m_name, true); + VOID(pthread_mutex_unlock(&LOCK_evex_running)); + +done: + /* + "opened" is switched to TRUE when we open mysql.event for checking. + In this case we have to close the table after finishing working with it. + */ + close_thread_tables(thd); + + DBUG_RETURN(ret); +} + + +/* + !!! This one is executor related so maybe moving it to + event_executor.cc is a good idea or ? +*/ +static int +evex_load_events_from_db(THD *thd) +{ + TABLE *table; + READ_RECORD read_record_info; + MYSQL_LOCK *lock; + Open_tables_state open_tables_state_backup; + int ret= -1; + + DBUG_ENTER("evex_load_events_from_db"); + + if (!(table= open_proc_type_table_for_read(thd, &open_tables_state_backup, + "event", &mysql_event_table_exists))) + DBUG_RETURN(SP_OPEN_TABLE_FAILED); + + VOID(pthread_mutex_lock(&LOCK_event_arrays)); + + init_read_record(&read_record_info, thd, table ,NULL,1,0); + while (!(read_record_info.read_record(&read_record_info))) + { + event_timed *et, *et_copy; + if (!(et= new event_timed())) + { + DBUG_PRINT("evex_load_events_from_db", ("Out of memory")); + ret= -1; + goto end; + } + DBUG_PRINT("evex_load_events_from_db", ("Loading event from row.")); + + if (et->load_from_row(&evex_mem_root, table)) + //error loading! + continue; + + DBUG_PRINT("evex_load_events_from_db", + ("Event %s loaded from row. Time to compile", et->m_name.str)); + + if (et->compile(thd, &evex_mem_root)) + //problem during compile + continue; + // let's find when to be executed + et->compute_next_execution_time(); + + DBUG_PRINT("evex_load_events_from_db", + ("Adding %s to the executor list.", et->m_name.str)); + VOID(push_dynamic(&events_array,(gptr) et)); + // we always add at the end so the number of elements - 1 is the place + // in the buffer + et_copy= dynamic_element(&events_array, events_array.elements - 1, + event_timed*); + VOID(push_dynamic(&evex_executing_queue,(gptr) &et_copy)); + et->m_free_sphead_on_delete= false; + DBUG_PRINT("info", ("")); + delete et; + } + end_read_record(&read_record_info); + + qsort((gptr) dynamic_element(&evex_executing_queue, 0, event_timed**), + evex_executing_queue.elements, + sizeof(event_timed **), + (qsort_cmp) event_timed_compare + ); + VOID(pthread_mutex_unlock(&LOCK_event_arrays)); + + thd->version--; // Force close to free memory + ret= 0; + +end: + close_thread_tables(thd); + thd->restore_backup_open_tables_state(&open_tables_state_backup); + + DBUG_PRINT("evex_load_events_from_db", + ("Events loaded from DB. Status code %d", ret)); + DBUG_RETURN(ret); +} + +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +//////////////// EVENT_TIMED class ///////////////////////////////// +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// + + +/* + Init all member variables + + SYNOPSIS + event_timed::init() +*/ + +void +event_timed::init() +{ + DBUG_ENTER("event_timed::init"); + + m_qname.str= m_db.str= m_name.str= m_body.str= m_comment.str= 0; + m_qname.length= m_db.length= m_name.length= m_body.length= m_comment.length= 0; + + set_zero_time(&m_starts, MYSQL_TIMESTAMP_DATETIME); + set_zero_time(&m_ends, MYSQL_TIMESTAMP_DATETIME); + set_zero_time(&m_execute_at, MYSQL_TIMESTAMP_DATETIME); + set_zero_time(&m_last_executed, MYSQL_TIMESTAMP_DATETIME); + + m_definer_user.str= m_definer_host.str= 0; + m_definer_user.length= m_definer_host.length= 0; + + DBUG_VOID_RETURN; +} + + +/* + Set a name of the event + + SYNOPSIS + event_timed::init_name() + thd THD + name the name extracted in the parser +*/ + +void +event_timed::init_name(THD *thd, sp_name *name) +{ + DBUG_ENTER("event_timed::init_name"); + uint n; /* Counter for nul trimming */ + /* During parsing, we must use thd->mem_root */ + MEM_ROOT *root= thd->mem_root; + + /* We have to copy strings to get them into the right memroot */ + if (name) + { + m_db.length= name->m_db.length; + if (name->m_db.length == 0) + m_db.str= NULL; + else + m_db.str= strmake_root(root, name->m_db.str, name->m_db.length); + m_name.length= name->m_name.length; + m_name.str= strmake_root(root, name->m_name.str, name->m_name.length); + + if (name->m_qname.length == 0) + name->init_qname(thd); + m_qname.length= name->m_qname.length; + m_qname.str= strmake_root(root, name->m_qname.str, m_qname.length); + } + else if (thd->db) + { + m_db.length= thd->db_length; + m_db.str= strmake_root(root, thd->db, m_db.length); + } + + DBUG_PRINT("m_db", ("len=%d db=%s",m_db.length, m_db.str)); + DBUG_PRINT("m_name", ("len=%d name=%s",m_name.length, m_name.str)); + + DBUG_VOID_RETURN; +} + + +/* + Set body of the event - what should be executed. + + SYNOPSIS + event_timed::init_body() + thd THD + + NOTE + The body is extracted by copying all data between the + start of the body set by another method and the current pointer in Lex. +*/ + +void +event_timed::init_body(THD *thd) +{ + DBUG_ENTER("event_timed::init_body"); + MEM_ROOT *root= thd->mem_root; + + m_body.length= thd->lex->ptr - m_body_begin; + // Trim nuls at the end + while (m_body.length && m_body_begin[m_body.length-1] == '\0') + m_body.length--; + + m_body.str= strmake_root(root, (char *)m_body_begin, m_body.length); + + DBUG_VOID_RETURN; +} + + +/* + Set time for execution for one time events. + + SYNOPSIS + event_timed::init_execute_at() + expr when (datetime) + + RETURNS + 0 - OK + EVEX_PARSE_ERROR - fix_fields failed + EVEX_BAD_PARAMS - datetime is in the past +*/ + +int +event_timed::init_execute_at(THD *thd, Item *expr) +{ + my_bool not_used; + TIME ltime; + my_time_t my_time_tmp; + + TIME time_tmp; + DBUG_ENTER("event_timed::init_execute_at"); + + if (expr->fix_fields(thd, &expr)) + DBUG_RETURN(EVEX_PARSE_ERROR); + + if (expr->val_int() == MYSQL_TIMESTAMP_ERROR) + DBUG_RETURN(EVEX_BAD_PARAMS); + + // let's check whether time is in the past + thd->variables.time_zone->gmt_sec_to_TIME(&time_tmp, + (my_time_t) thd->query_start()); + + if (expr->val_int() < TIME_to_ulonglong_datetime(&time_tmp)) + DBUG_RETURN(EVEX_BAD_PARAMS); + + if ((not_used= expr->get_date(<ime, TIME_NO_ZERO_DATE))) + DBUG_RETURN(EVEX_BAD_PARAMS); + + /* + This may result in a 1970-01-01 date if ltime is > 2037-xx-xx + CONVERT_TZ has similar problem + */ + my_tz_UTC->gmt_sec_to_TIME(<ime, TIME_to_timestamp(thd,<ime, ¬_used)); + + + m_execute_at= ltime; + DBUG_RETURN(0); +} + + +/* + Set time for execution for transient events. + + SYNOPSIS + event_timed::init_interval() + expr how much? + interval what is the interval + + RETURNS + 0 - OK + EVEX_PARSE_ERROR - fix_fields failed + EVEX_BAD_PARAMS - Interval is not positive +*/ + +int +event_timed::init_interval(THD *thd, Item *expr, interval_type interval) +{ + longlong tmp; + DBUG_ENTER("event_timed::init_interval"); + + if (expr->fix_fields(thd, &expr)) + DBUG_RETURN(EVEX_PARSE_ERROR); + + if ((tmp= expr->val_int()) <= 0) + DBUG_RETURN(EVEX_BAD_PARAMS); + + m_expr= tmp; + m_interval= interval; + DBUG_RETURN(0); +} + + +/* + Set activation time. + + SYNOPSIS + event_timed::init_starts() + expr how much? + interval what is the interval + + NOTES + Note that activation time is not execution time. + EVERY 5 MINUTE STARTS "2004-12-12 10:00:00" means that + the event will be executed every 5 minutes but this will + start at the date shown above. Expressions are possible : + DATE_ADD(NOW(), INTERVAL 1 DAY) -- start tommorow at + same time. + + RETURNS + 0 - OK + EVEX_PARSE_ERROR - fix_fields failed +*/ + +int +event_timed::init_starts(THD *thd, Item *starts) +{ + my_bool not_used; + TIME ltime; + my_time_t my_time_tmp; + + DBUG_ENTER("event_timed::init_starts"); + + if (starts->fix_fields(thd, &starts)) + DBUG_RETURN(EVEX_PARSE_ERROR); + + if (starts->val_int() == MYSQL_TIMESTAMP_ERROR) + DBUG_RETURN(EVEX_BAD_PARAMS); + + if ((not_used= starts->get_date(<ime, TIME_NO_ZERO_DATE))) + DBUG_RETURN(EVEX_BAD_PARAMS); + + /* + This may result in a 1970-01-01 date if ltime is > 2037-xx-xx + CONVERT_TZ has similar problem + */ + my_tz_UTC->gmt_sec_to_TIME(<ime, TIME_to_timestamp(thd,<ime, ¬_used)); + + m_starts= ltime; + DBUG_RETURN(0); +} + + +/* + Set deactivation time. + + SYNOPSIS + event_timed::init_ends() + thd THD + ends when? + + NOTES + Note that activation time is not execution time. + EVERY 5 MINUTE ENDS "2004-12-12 10:00:00" means that + the event will be executed every 5 minutes but this will + end at the date shown above. Expressions are possible : + DATE_ADD(NOW(), INTERVAL 1 DAY) -- end tommorow at + same time. + + RETURNS + 0 - OK + EVEX_PARSE_ERROR - fix_fields failed + EVEX_BAD_PARAMS - ENDS before STARTS +*/ + +int +event_timed::init_ends(THD *thd, Item *ends) +{ + TIME ltime; + my_time_t my_time_tmp; + my_bool not_used; + + DBUG_ENTER("event_timed::init_ends"); + + if (ends->fix_fields(thd, &ends)) + DBUG_RETURN(EVEX_PARSE_ERROR); + + // the field was already fixed in init_ends + if ((not_used= ends->get_date(<ime, TIME_NO_ZERO_DATE))) + DBUG_RETURN(EVEX_BAD_PARAMS); + + /* + This may result in a 1970-01-01 date if ltime is > 2037-xx-xx + CONVERT_TZ has similar problem + */ + my_tz_UTC->gmt_sec_to_TIME(<ime, TIME_to_timestamp(thd, <ime, ¬_used)); + + if (m_starts.year && my_time_compare(&m_starts, <ime) != -1) + DBUG_RETURN(EVEX_BAD_PARAMS); + + m_ends= ltime; + DBUG_RETURN(0); +} + + +/* + Set behaviour when ENDS has been set and passed by. + + SYNOPSIS + event_timed::init_interval() + drop if set then drop the event otherwise preserve it. +*/ + +void +event_timed::set_on_completion_drop(bool drop) +{ + DBUG_ENTER("event_timed::set_on_completion"); + if (drop) + m_on_completion= MYSQL_EVENT_ON_COMPLETION_DROP; + else + m_on_completion= MYSQL_EVENT_ON_COMPLETION_PRESERVE; + + DBUG_VOID_RETURN; +} + + +/* + Sets event's status. DISABLED - not executable even if + everything else is ok (STARTS, ENDS, INTERVAL and so on). + + SYNOPSIS + event_timed::set_event_status() + enabled set whether enabled or not. +*/ + +void +event_timed::set_event_status(bool enabled) +{ + DBUG_ENTER("event_timed::set_on_completion"); + + m_status_changed= true; + if (enabled) + m_status= MYSQL_EVENT_ENABLED; + else + m_status= MYSQL_EVENT_DISABLED; + + DBUG_VOID_RETURN; +} + + +/* + Sets comment. + + SYNOPSIS + event_timed::init_comment() + thd THD - used for memory allocation + comment the string. +*/ + +void +event_timed::init_comment(THD *thd, LEX_STRING *comment) +{ + DBUG_ENTER("event_timed::init_comment"); + + MEM_ROOT *root= thd->mem_root; + m_comment.length= comment->length; + m_comment.str= strmake_root(root, comment->str, comment->length); + DBUG_PRINT("m_comment", ("len=%d",m_comment.length)); + + DBUG_VOID_RETURN; +} + + +/* + Inits definer (m_definer_user and m_definer_host) during + parsing. + + SYNOPSIS + event_timed::init_definer() +*/ + +int +event_timed::init_definer(THD *thd) +{ + DBUG_ENTER("event_timed::init_definer"); + + m_definer_user.str= strdup_root(thd->mem_root, thd->security_ctx->priv_user); + m_definer_user.length= strlen(thd->security_ctx->priv_user); + + m_definer_host.str= strdup_root(thd->mem_root, thd->security_ctx->priv_host); + m_definer_host.length= strlen(thd->security_ctx->priv_host); + + DBUG_RETURN(0); +} + + +/* + Loads an event from a row from mysql.event + + SYNOPSIS + event_timed::load_from_row() +*/ + +int +event_timed::load_from_row(MEM_ROOT *mem_root, TABLE *table) +{ + longlong created; + longlong modified; + char *ptr; + event_timed *et; + uint len; + bool res1, res2; + + DBUG_ENTER("event_timed::load_from_row"); + + if (!table) + goto error; + + et= this; + + if (table->s->fields != EVEX_FIELD_COUNT) + goto error; + + if ((et->m_db.str= get_field(mem_root, + table->field[EVEX_FIELD_DB])) == NULL) + goto error; + + et->m_db.length= strlen(et->m_db.str); + + if ((et->m_name.str= get_field(mem_root, + table->field[EVEX_FIELD_NAME])) == NULL) + goto error; + + et->m_name.length= strlen(et->m_name.str); + + if ((et->m_body.str= get_field(mem_root, + table->field[EVEX_FIELD_BODY])) == NULL) + goto error; + + et->m_body.length= strlen(et->m_body.str); + + if ((et->m_definer.str= get_field(mem_root, + table->field[EVEX_FIELD_DEFINER])) == NullS) + goto error; + et->m_definer.length= strlen(et->m_definer.str); + + ptr= strchr(et->m_definer.str, '@'); + + if (! ptr) + ptr= et->m_definer.str; // Weird, isn't it? + + len= ptr - et->m_definer.str; + + et->m_definer_user.str= strmake_root(mem_root, et->m_definer.str, len); + et->m_definer_user.length= len; + len= et->m_definer.length - len - 1; //1 is because of @ + et->m_definer_host.str= strmake_root(mem_root, ptr + 1, len);//1: because of @ + et->m_definer_host.length= len; + + + res1= table->field[EVEX_FIELD_STARTS]-> + get_date(&et->m_starts, TIME_NO_ZERO_DATE); + + res2= table->field[EVEX_FIELD_ENDS]-> + get_date(&et->m_ends, TIME_NO_ZERO_DATE); + + et->m_expr= table->field[EVEX_FIELD_INTERVAL_EXPR]->val_int(); + + /* + If res1 and res2 are true then both fields are empty. + Hence if EVEX_FIELD_EXECUTE_AT is empty there is an error. + */ + if (res1 && res2 && !et->m_expr && table->field[EVEX_FIELD_EXECUTE_AT]-> + get_date(&et->m_execute_at, TIME_NO_ZERO_DATE)) + goto error; + + /* + In DB the values start from 1 but enum interval_type starts + from 0 + */ + et->m_interval= (interval_type) + ((ulonglong) table->field[EVEX_FIELD_TRANSIENT_INTERVAL]->val_int() - 1); + + et->m_modified= table->field[EVEX_FIELD_CREATED]->val_int(); + et->m_created= table->field[EVEX_FIELD_MODIFIED]->val_int(); + + /* + ToDo Andrey : Ask PeterG & Serg what to do in this case. + Whether on load last_executed_at should be loaded + or it must be 0ed. If last_executed_at is loaded + then an event can be scheduled for execution + instantly. Let's say an event has to be executed + every 15 mins. The server has been stopped for + more than this time and then started. If L_E_AT + is loaded from DB, execution at L_E_AT+15min + will be scheduled. However this time is in the past. + Hence immediate execution. Due to patch of + ::mark_last_executed() m_last_executed gets time_now + and not m_execute_at. If not like this a big + queue can be scheduled for times which are still in + the past (2, 3 and more executions which will be + consequent). + */ + set_zero_time(&m_last_executed, MYSQL_TIMESTAMP_DATETIME); +#ifdef ANDREY_0 + table->field[EVEX_FIELD_LAST_EXECUTED]-> + get_date(&et->m_last_executed, TIME_NO_ZERO_DATE); +#endif + m_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[EVEX_FIELD_STATUS])) == NullS) + goto error; + + DBUG_PRINT("load_from_row", ("Event [%s] is [%s]", et->m_name.str, ptr)); + et->m_status= (ptr[0]=='E'? MYSQL_EVENT_ENABLED: + MYSQL_EVENT_DISABLED); + + // ToDo : Andrey . Find a way not to allocate ptr on event_mem_root + if ((ptr= get_field(mem_root, + table->field[EVEX_FIELD_ON_COMPLETION])) == NullS) + goto error; + + et->m_on_completion= (ptr[0]=='D'? MYSQL_EVENT_ON_COMPLETION_DROP: + MYSQL_EVENT_ON_COMPLETION_PRESERVE); + + et->m_comment.str= get_field(mem_root, table->field[EVEX_FIELD_COMMENT]); + if (et->m_comment.str != NullS) + et->m_comment.length= strlen(et->m_comment.str); + else + et->m_comment.length= 0; + + DBUG_RETURN(0); +error: + DBUG_RETURN(EVEX_GET_FIELD_FAILED); +} + + +bool +event_timed::compute_next_execution_time() +{ + TIME time_now; + my_time_t now; + int tmp; + + DBUG_ENTER("event_timed::compute_next_execution_time"); + + if (m_status == MYSQL_EVENT_DISABLED) + { + DBUG_PRINT("compute_next_execution_time", + ("Event %s is DISABLED", m_name.str)); + goto ret; + } + //if one-time no need to do computation + if (!m_expr) + { + //let's check whether it was executed + if (m_last_executed.year) + { + DBUG_PRINT("compute_next_execution_time", + ("One-time event %s was already executed", m_name.str)); + if (m_on_completion == MYSQL_EVENT_ON_COMPLETION_DROP) + { + DBUG_PRINT("compute_next_execution_time", + ("One-time event will be dropped.")); + m_dropped= true; + } + m_status= MYSQL_EVENT_DISABLED; + m_status_changed= true; + } + goto ret; + } + time(&now); + my_tz_UTC->gmt_sec_to_TIME(&time_now, now); +/* + sql_print_information("[%s.%s]", m_db.str, m_name.str); + sql_print_information("time_now : [%d-%d-%d %d:%d:%d ]", time_now.year, time_now.month, time_now.day, time_now.hour, time_now.minute, time_now.second); + sql_print_information("m_starts : [%d-%d-%d %d:%d:%d ]", m_starts.year, m_starts.month, m_starts.day, m_starts.hour, m_starts.minute, m_starts.second); + sql_print_information("m_ends : [%d-%d-%d %d:%d:%d ]", m_ends.year, m_ends.month, m_ends.day, m_ends.hour, m_ends.minute, m_ends.second); + sql_print_information("m_last_ex: [%d-%d-%d %d:%d:%d ]", m_last_executed.year, m_last_executed.month, m_last_executed.day, m_last_executed.hour, m_last_executed.minute, m_last_executed.second); +*/ + //if time_now is after m_ends don't execute anymore + if (m_ends.year && (tmp= my_time_compare(&m_ends, &time_now)) == -1) + { + // time_now is after m_ends. don't execute anymore + set_zero_time(&m_execute_at, MYSQL_TIMESTAMP_DATETIME); + if (m_on_completion == MYSQL_EVENT_ON_COMPLETION_DROP) + m_dropped= true; + m_status= MYSQL_EVENT_DISABLED; + m_status_changed= true; + + goto ret; + } + + /* + Here time_now is before or equals m_ends if the latter is set. + Let's check whether time_now is before m_starts. + If so schedule for m_starts + */ + if (m_starts.year && (tmp= my_time_compare(&time_now, &m_starts)) < 1) + { + if (tmp == 0 && my_time_compare(&m_starts, &m_last_executed) == 0) + { + /* + time_now = m_starts = m_last_executed + do nothing or we will schedule for second time execution at m_starts. + */ + } + else + { + //m_starts is in the future + //time_now before m_starts. Scheduling for m_starts + m_execute_at= m_starts; + goto ret; + } + } + + if (m_starts.year && m_ends.year) + { + /* + Both m_starts and m_ends are set and time_now is between them (incl.) + If m_last_executed is set then increase with m_expr. The new TIME is + after m_ends set m_execute_at to 0. And check for m_on_completion + If not set then schedule for now. + */ + if (!m_last_executed.year) + m_execute_at= time_now; + else + { + my_time_t last, ll_ends; + + // There was previous execution + last= sec_since_epoch_TIME(&m_last_executed) + m_expr; + ll_ends= sec_since_epoch_TIME(&m_ends); + //now convert back to TIME + //ToDo Andrey: maybe check for error here? + if (ll_ends < last) + { + // Next execution after ends. No more executions + set_zero_time(&m_execute_at, MYSQL_TIMESTAMP_DATETIME); + if (m_on_completion == MYSQL_EVENT_ON_COMPLETION_DROP) + m_dropped= true; + } + else + my_tz_UTC->gmt_sec_to_TIME(&m_execute_at, last); + } + goto ret; + } + else if (!m_starts.year && !m_ends.year) + { + // both m_starts and m_ends are not set, se we schedule for the next + // based on m_last_executed + if (!m_last_executed.year) + //m_last_executed not set. Schedule the event for now + m_execute_at= time_now; + else + //ToDo Andrey: maybe check for error here? + my_tz_UTC->gmt_sec_to_TIME(&m_execute_at, + sec_since_epoch_TIME(&m_last_executed) + m_expr); + goto ret; + } + else + { + //either m_starts or m_ends is set + if (m_starts.year) + { + /* + - m_starts is set. + - m_starts is not in the future according to check made before + Hence schedule for m_starts + m_expr in case m_last_executed + is not set, otherwise to m_last_executed + m_expr + */ + my_time_t last; + + //convert either m_last_executed or m_starts to seconds + if (m_last_executed.year) + last= sec_since_epoch_TIME(&m_last_executed) + m_expr; + else + last= sec_since_epoch_TIME(&m_starts); + + //now convert back to TIME + //ToDo Andrey: maybe check for error here? + my_tz_UTC->gmt_sec_to_TIME(&m_execute_at, last); + } + else + { + /* + - m_ends is set + - m_ends is after time_now or is equal + Hence check for m_last_execute and increment with m_expr. + If m_last_executed is not set then schedule for now + */ + my_time_t last, ll_ends; + + if (!m_last_executed.year) + m_execute_at= time_now; + else + { + last= sec_since_epoch_TIME(&m_last_executed); + ll_ends= sec_since_epoch_TIME(&m_ends); + last+= m_expr; + //now convert back to TIME + //ToDo Andrey: maybe check for error here? + if (ll_ends < last) + { + set_zero_time(&m_execute_at, MYSQL_TIMESTAMP_DATETIME); + if (m_on_completion == MYSQL_EVENT_ON_COMPLETION_DROP) + m_dropped= true; + } + else + my_tz_UTC->gmt_sec_to_TIME(&m_execute_at, last); + } + } + goto ret; + } +ret: + + DBUG_RETURN(false); +} + + +void +event_timed::mark_last_executed() +{ + TIME time_now; + my_time_t now; + + time(&now); + my_tz_UTC->gmt_sec_to_TIME(&time_now, now); + + m_last_executed= time_now; // was m_execute_at +#ifdef ANDREY_0 + m_last_executed= m_execute_at; +#endif + m_last_executed_changed= true; +} + + +bool +event_timed::drop(THD *thd) +{ + DBUG_ENTER("event_timed::drop"); + + if (evex_drop_event(thd, this, false)) + DBUG_RETURN(true); + + DBUG_RETURN(false); +} + + +bool +event_timed::update_fields(THD *thd) +{ + TABLE *table; + int ret= 0; + bool opened; + + DBUG_ENTER("event_timed::update_time_fields"); + + DBUG_PRINT("enter", ("name: %*s", m_name.length, m_name.str)); + + //no need to update if nothing has changed + if (!(m_status_changed || m_last_executed_changed)) + goto done; + + if (!(table= EVEX_OPEN_TABLE_FOR_UPDATE())) + DBUG_RETURN(SP_OPEN_TABLE_FAILED); + + if ((ret= sp_db_find_routine_aux(thd, 0/*notype*/, m_db, m_name, table))) + goto done; + + store_record(table,record[1]); + table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; // Don't update create on row update. + + if (m_last_executed_changed) + { + table->field[EVEX_FIELD_LAST_EXECUTED]->set_notnull(); + table->field[EVEX_FIELD_LAST_EXECUTED]->store_time(&m_last_executed, + MYSQL_TIMESTAMP_DATETIME); + m_last_executed_changed= false; + } + if (m_status_changed) + { + table->field[EVEX_FIELD_STATUS]->set_notnull(); + table->field[EVEX_FIELD_STATUS]->store((longlong)m_status); + m_status_changed= false; + } + + if ((table->file->update_row(table->record[1],table->record[0]))) + ret= EVEX_WRITE_ROW_FAILED; + +done: + close_thread_tables(thd); + + DBUG_RETURN(ret); +} + + +char * +event_timed::get_show_create_event(THD *thd, uint *length) +{ + char *dst, *ret; + uint len, tmp_len; + + len = strlen("CREATE EVENT ") + m_db.length + strlen(".") + m_name.length + + strlen(" ON SCHEDULE ") + strlen("EVERY 5 MINUTE ") +/* + + strlen("ON COMPLETION ") + + (m_on_completion==MYSQL_EVENT_ON_COMPLETION_DROP? + strlen("NOT PRESERVE "):strlen("PRESERVE ")) + + (m_status==MYSQL_EVENT_ENABLED? + strlen("ENABLE "):strlen("DISABLE ")) + + strlen("COMMENT \"") + m_comment.length + strlen("\" ") +*/ + + strlen("DO ") + + + m_body.length + strlen(";"); + + ret= dst= (char*) alloc_root(thd->mem_root, len); + memcpy(dst, "CREATE EVENT ", tmp_len= strlen("CREATE EVENT ")); + dst+= tmp_len; + memcpy(dst, m_db.str, tmp_len=m_db.length); + dst+= tmp_len; + memcpy(dst, ".", tmp_len= strlen(".")); + dst+= tmp_len; + memcpy(dst, m_name.str, tmp_len= m_name.length); + dst+= tmp_len; + memcpy(dst, " ON SCHEDULE ", tmp_len= strlen(" ON SCHEDULE ")); + dst+= tmp_len; + memcpy(dst, "EVERY 5 MINUTE ", tmp_len= strlen("EVERY 5 MINUTE ")); + dst+= tmp_len; +/* + memcpy(dst, "ON COMPLETION ", tmp_len =strlen("ON COMPLETION ")); + dst+= tmp_len; + memcpy(dst, (m_on_completion==MYSQL_EVENT_ON_COMPLETION_DROP? + "NOT PRESERVE ":"PRESERVE "), + tmp_len =(m_on_completion==MYSQL_EVENT_ON_COMPLETION_DROP? 13:9)); + dst+= tmp_len; + + memcpy(dst, (m_status==MYSQL_EVENT_ENABLED? + "ENABLE ":"DISABLE "), + tmp_len= (m_status==MYSQL_EVENT_ENABLED? 8:9)); + dst+=tmp_len; + + memcpy(dst, "COMMENT \"", tmp_len= strlen("COMMENT \"")); + dst+= tmp_len; + memcpy(dst, m_comment.str, tmp_len= m_comment.length); + dst+= tmp_len; + memcpy(dst, "\" ", tmp_len=2); + dst+= tmp_len; +*/ + memcpy(dst, "DO ", tmp_len=3); + dst+= tmp_len; + + memcpy(dst, m_body.str, tmp_len= m_body.length); + dst+= tmp_len; + memcpy(dst, ";", 1); + ++dst; + *dst= '\0'; + + *length= len; + + return ret; +} + + +int +event_timed::execute(THD *thd, MEM_ROOT *mem_root= NULL) +{ + List empty_item_list; + int ret= 0; + + DBUG_ENTER("event_timed::execute"); + + // TODO Andrey : make this as member variable and delete in destructor + empty_item_list.empty(); + + if (!m_sphead && (ret= compile(thd, mem_root))) + goto done; + + ret= m_sphead->execute_procedure(thd, &empty_item_list); + +done: + // Don't cache m_sphead if allocated on another mem_root + if (mem_root && m_sphead) + { + delete m_sphead; + m_sphead= 0; + } + + DBUG_RETURN(ret); +} + + +int +event_timed::compile(THD *thd, MEM_ROOT *mem_root= NULL) +{ + MEM_ROOT *tmp_mem_root= 0; + LEX *old_lex= thd->lex, lex; + char *old_db; + event_timed *ett; + sp_name *spn; + char *old_query; + uint old_query_len; + st_sp_chistics *p; + + DBUG_ENTER("event_timed::compile"); + // change the memory root for the execution time + if (mem_root) + { + tmp_mem_root= thd->mem_root; + thd->mem_root= mem_root; + } + old_query_len= thd->query_length; + old_query= thd->query; + old_db= thd->db; + thd->db= m_db.str; + thd->query= get_show_create_event(thd, &thd->query_length); + DBUG_PRINT("event_timed::compile", ("query:%s",thd->query)); + + thd->lex= &lex; + lex_start(thd, (uchar*)thd->query, thd->query_length); + lex.et_compile_phase= TRUE; + if (yyparse((void *)thd) || thd->is_fatal_error) + { + // Free lex associated resources + // QQ: Do we really need all this stuff here ? + if (lex.sphead) + { + if (&lex != thd->lex) + thd->lex->sphead->restore_lex(thd); + delete lex.sphead; + lex.sphead= 0; + } + // QQ: anything else ? + lex_end(&lex); + thd->lex= old_lex; + DBUG_RETURN(-1); + } + + m_sphead= lex.sphead; + m_sphead->m_db= m_db; + //copy also chistics since they will vanish otherwise we get 0x0 pointer + // Todo : Handle sql_mode !! + m_sphead->set_definer(m_definer.str, m_definer.length); + m_sphead->set_info(0, 0, &lex.sp_chistics, 0/*sql_mode*/); + m_sphead->optimize(); + lex_end(&lex); + thd->lex= old_lex; + thd->query= old_query; + thd->query_length= old_query_len; + thd->db= old_db; + /* + Change the memory root for the execution time. + */ + if (mem_root) + thd->mem_root= tmp_mem_root; + + DBUG_RETURN(0); +} + + +/******************************** EXECUTOR ************************************/ + + +//extern "C" pthread_handler_decl(event_executor_main, arg); +//extern "C" pthread_handler_decl(event_executor_worker, arg); + +/* + TODO Andrey: Check for command line option whether to start + the main thread or not. +*/ + +pthread_handler_t event_executor_worker(void *arg); +pthread_handler_t event_executor_main(void *arg); + +int +init_events() +{ + pthread_t th; + + DBUG_ENTER("init_events"); + + DBUG_PRINT("info",("Starting events main thread")); + + pthread_mutex_init(&LOCK_event_arrays, MY_MUTEX_INIT_FAST); + pthread_mutex_init(&LOCK_workers_count, MY_MUTEX_INIT_FAST); + pthread_mutex_init(&LOCK_evex_running, MY_MUTEX_INIT_FAST); + + VOID(pthread_mutex_lock(&LOCK_evex_running)); + evex_is_running= false; + event_executor_running_global_var= false; + VOID(pthread_mutex_unlock(&LOCK_evex_running)); + + //TODO Andrey: Change the error code returned! + if (pthread_create(&th, NULL, event_executor_main, (void*)NULL)) + DBUG_RETURN(ER_SLAVE_THREAD); + + DBUG_RETURN(0); +} + + +void +shutdown_events() +{ + VOID(pthread_mutex_lock(&LOCK_evex_running)); + VOID(pthread_mutex_unlock(&LOCK_evex_running)); + pthread_mutex_destroy(&LOCK_event_arrays); + pthread_mutex_destroy(&LOCK_workers_count); + pthread_mutex_destroy(&LOCK_evex_running); +} + + +static int +init_event_thread(THD* thd) +{ + DBUG_ENTER("init_event_thread"); + thd->client_capabilities= 0; + thd->security_ctx->skip_grants(); + my_net_init(&thd->net, 0); + thd->net.read_timeout = slave_net_timeout; + thd->slave_thread= 0; + thd->options= OPTION_AUTO_IS_NULL; + thd->client_capabilities= CLIENT_LOCAL_FILES; + thd->real_id=pthread_self(); + VOID(pthread_mutex_lock(&LOCK_thread_count)); + thd->thread_id= thread_id++; + VOID(pthread_mutex_unlock(&LOCK_thread_count)); + + if (init_thr_lock() || thd->store_globals()) + { + thd->cleanup(); + delete thd; + DBUG_RETURN(-1); + } + +#if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__) + sigset_t set; + VOID(sigemptyset(&set)); // Get mask in use + VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals)); +#endif + + thd->proc_info= "Initialized"; + thd->version= refresh_version; + thd->set_time(); + DBUG_RETURN(0); +} + + +pthread_handler_t event_executor_main(void *arg) +{ + THD *thd; /* needs to be first for thread_stack */ + ulonglong iter_num= 0; + uint i=0, j=0; + + DBUG_ENTER("event_executor_main"); + DBUG_PRINT("event_executor_main", ("EVEX thread started")); + + VOID(pthread_mutex_lock(&LOCK_evex_running)); + evex_is_running= true; + event_executor_running_global_var= opt_event_executor; + VOID(pthread_mutex_unlock(&LOCK_evex_running)); + + // init memory root + init_alloc_root(&evex_mem_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC); + + // needs to call my_thread_init(), otherwise we get a coredump in DBUG_ stuff + my_thread_init(); + + //TODO Andrey: Check for NULL + if (!(thd = new THD)) // note that contructor of THD uses DBUG_ ! + { + sql_print_error("Cannot create THD for event_executor_main"); + goto err_no_thd; + } + thd->thread_stack = (char*)&thd; // remember where our stack is + + pthread_detach_this_thread(); + + if (init_event_thread(thd)) + goto err; + + thd->init_for_queries(); + + VOID(pthread_mutex_lock(&LOCK_thread_count)); + threads.append(thd); + thread_count++; + thread_running++; + VOID(pthread_mutex_unlock(&LOCK_thread_count)); + + DBUG_PRINT("EVEX main thread", ("Initing events_array")); + + VOID(pthread_mutex_lock(&LOCK_event_arrays)); + /* + my_malloc is used as underlying allocator which does not use a mem_root + thus data should be freed at later stage. + */ + VOID(my_init_dynamic_array(&events_array, sizeof(event_timed), 50, 100)); + VOID(my_init_dynamic_array(&evex_executing_queue, sizeof(event_timed *), 50, 100)); + VOID(pthread_mutex_unlock(&LOCK_event_arrays)); + + if (evex_load_events_from_db(thd)) + goto err; + + THD_CHECK_SENTRY(thd); + /* Read queries from the IO/THREAD until this thread is killed */ + while (!thd->killed) + { + TIME time_now; + my_time_t now; + my_ulonglong cnt; + + DBUG_PRINT("info", ("EVEX External Loop %d", ++cnt)); +// sql_print_information("[EVEX] External Loop!"); + my_sleep(500000);// sleep 0.5s + if (!event_executor_running_global_var) + continue; + time(&now); + my_tz_UTC->gmt_sec_to_TIME(&time_now, now); + + + VOID(pthread_mutex_lock(&LOCK_event_arrays)); + for (i= 0; (i < evex_executing_queue.elements) && !thd->killed; ++i) + { + event_timed **p_et=dynamic_element(&evex_executing_queue,i,event_timed**); + event_timed *et= *p_et; +// sql_print_information("[EVEX] External Loop 2!"); + + if (!event_executor_running_global_var) + break;// soon we will do only continue (see the code a bit above) + + thd->proc_info = "Iterating"; + THD_CHECK_SENTRY(thd); + /* + if this is the first event which is after time_now then no + more need to iterate over more elements since the array is sorted. + */ + if (et->m_execute_at.year && + my_time_compare(&time_now, &et->m_execute_at) == -1) + break; + + if (et->m_status == MYSQL_EVENT_ENABLED) + { + pthread_t th; + + DBUG_PRINT("info", (" Spawning a thread %d", ++iter_num)); +// sql_print_information(" Spawning a thread %d", ++iter_num); + + if (pthread_create(&th, NULL, event_executor_worker, (void*)et)) + { + sql_print_error("Problem while trying to create a thread"); + VOID(pthread_mutex_unlock(&LOCK_event_arrays)); + goto err; // for now finish execution of the Executor + } + + et->mark_last_executed(); + et->compute_next_execution_time(); + et->update_fields(thd); + if ((et->m_execute_at.year && !et->m_expr) + || TIME_to_ulonglong_datetime(&et->m_execute_at) == 0L) + et->m_flags |= EVENT_EXEC_NO_MORE; + } + } + /* + Let's remove elements which won't be executed any more + The number is "i" and it is <= up to evex_executing_queue.elements + */ + j= 0; + while (j < i && j < evex_executing_queue.elements) + { + event_timed **p_et= dynamic_element(&evex_executing_queue, j, event_timed**); + event_timed *et= *p_et; + if (et->m_flags & EVENT_EXEC_NO_MORE || et->m_status == MYSQL_EVENT_DISABLED) + { + delete_dynamic_element(&evex_executing_queue, j); + DBUG_PRINT("", ("DELETING FROM EXECUTION QUEUE [%s.%s]",et->m_db.str, et->m_name.str)); + // nulling the position, will delete later + if (et->m_dropped) + { + // we have to drop the event + int idx; + et->drop(thd); + idx= get_index_dynamic(&events_array, (gptr) et); + if (idx != -1) + delete_dynamic_element(&events_array, idx); + else + sql_print_error("Something weird happened with events. %d", __LINE__); + } + continue; + } + ++j; + } + if (evex_executing_queue.elements) + //ToDo Andrey : put a lock here + qsort((gptr) dynamic_element(&evex_executing_queue, 0, event_timed**), + evex_executing_queue.elements, + sizeof(event_timed **), + (qsort_cmp) event_timed_compare + ); + + VOID(pthread_mutex_unlock(&LOCK_event_arrays)); + }// while (!thd->killed) + +err: + VOID(pthread_mutex_lock(&LOCK_evex_running)); + evex_is_running= false; + VOID(pthread_mutex_unlock(&LOCK_evex_running)); + + sql_print_information("Event executor stopping"); + // LEX_STRINGs reside in the memory root and will be destroyed with it. + // Hence no need of delete but only freeing of SP + for (i=0; i < events_array.elements; ++i) + { + event_timed *et= dynamic_element(&events_array, i, event_timed*); + et->free_sp(); + } + // TODO Andrey: USE lock here! + delete_dynamic(&evex_executing_queue); + delete_dynamic(&events_array); + + thd->proc_info = "Clearing"; + DBUG_ASSERT(thd->net.buff != 0); + net_end(&thd->net); // destructor will not free it, because we are weird + THD_CHECK_SENTRY(thd); + pthread_mutex_lock(&LOCK_thread_count); + thread_count--; + thread_running--; + THD_CHECK_SENTRY(thd); + delete thd; + pthread_mutex_unlock(&LOCK_thread_count); + + /* + sleeping some time may help not crash the server. sleeping + is done to wait for spawned threads to finish. + + TODO: A better will be with a conditional variable + */ + { + uint tries= 0; + while (tries++ < 5) + { + VOID(pthread_mutex_lock(&LOCK_workers_count)); + if (!workers_count) + { + VOID(pthread_mutex_unlock(&LOCK_workers_count)); + break; + } + VOID(pthread_mutex_unlock(&LOCK_workers_count)); + DBUG_PRINT("info", ("Sleep %d", tries)); + my_sleep(1000000 * tries);// 1s + } + DBUG_PRINT("info", ("Maybe now it is ok to kill the thread and evex MRoot")); + } + +err_no_thd: + VOID(pthread_mutex_lock(&LOCK_evex_running)); + evex_is_running= false; + VOID(pthread_mutex_unlock(&LOCK_evex_running)); + + free_root(&evex_mem_root, MYF(0)); + sql_print_information("Event executor stopped"); + + shutdown_events(); + + my_thread_end(); + pthread_exit(0); + DBUG_RETURN(0); // Can't return anything here +} + + +pthread_handler_t event_executor_worker(void *event_void) +{ + THD *thd; /* needs to be first for thread_stack */ + List empty_item_list; + event_timed *event = (event_timed *) event_void; + MEM_ROOT mem_root; + ulong save_options; + + + DBUG_ENTER("event_executor_worker"); + VOID(pthread_mutex_lock(&LOCK_workers_count)); + ++workers_count; + VOID(pthread_mutex_unlock(&LOCK_workers_count)); + + init_alloc_root(&mem_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC); + + //we pass this empty list as parameter to the SP_HEAD of the event + empty_item_list.empty(); + + my_thread_init(); + + //TODO Andrey: Check for NULL + if (!(thd = new THD)) // note that contructor of THD uses DBUG_ ! + { + sql_print_error("Cannot create a THD structure in worker thread"); + goto err_no_thd; + } + thd->thread_stack = (char*)&thd; // remember where our stack is + thd->mem_root= &mem_root; +// pthread_detach_this_thread(); + pthread_detach(pthread_self()); + if (init_event_thread(thd)) + goto err; + + thd->init_for_queries(); + save_options= thd->options; + thd->options&= ~OPTION_BIN_LOG; + + VOID(pthread_mutex_lock(&LOCK_thread_count)); + threads.append(thd); + thread_count++; + thread_running++; + VOID(pthread_mutex_unlock(&LOCK_thread_count)); + + //thd->security_ctx->priv_host is char[MAX_HOSTNAME] + + strxnmov(thd->security_ctx->priv_host, sizeof(thd->security_ctx->priv_host), + event->m_definer_host.str, NullS); + + thd->security_ctx->priv_user= event->m_definer_user.str; + + thd->db= event->m_db.str; + if (!check_global_access(thd, EVENT_ACL)) + { + char exec_time[200]; + int ret; + my_TIME_to_str(&event->m_execute_at, exec_time); + DBUG_PRINT("info", (" EVEX EXECUTING event for event %s.%s [EXPR:%d][EXECUTE_AT:%s]", event->m_db.str, event->m_name.str,(int) event->m_expr, exec_time)); +// sql_print_information(" EVEX EXECUTING event for event %s.%s [EXPR:%d][EXECUTE_AT:%s]", event->m_db.str, event->m_name.str,(int) event->m_expr, exec_time); + ret= event->execute(thd); +// sql_print_information(" EVEX EXECUTED event for event %s.%s [EXPR:%d][EXECUTE_AT:%s]. RetCode=%d", event->m_db.str, event->m_name.str,(int) event->m_expr, exec_time, ret); + DBUG_PRINT("info", (" EVEX EXECUTED event for event %s.%s [EXPR:%d][EXECUTE_AT:%s]", event->m_db.str, event->m_name.str,(int) event->m_expr, exec_time)); + } + thd->db= 0; + //reenable (is it needed?) + thd->options= save_options; +err: + VOID(pthread_mutex_lock(&LOCK_thread_count)); + thread_count--; + thread_running--; + /* + Some extra safety, which should not been needed (normally, event deletion + should already have done these assignments (each event which sets these + variables is supposed to set them to 0 before terminating)). + */ + //thd->query= thd->db= thd->catalog= 0; + //thd->query_length= thd->db_length= 0; + VOID(pthread_mutex_unlock(&LOCK_thread_count)); + + //thd->temporary_tables = 0; // remove tempation from destructor to close them + thd->proc_info = "Clearing"; + DBUG_ASSERT(thd->net.buff != 0); + net_end(&thd->net); // destructor will not free it, because we are weird + THD_CHECK_SENTRY(thd); + + VOID(pthread_mutex_lock(&LOCK_thread_count)); + THD_CHECK_SENTRY(thd); + delete thd; + VOID(pthread_mutex_unlock(&LOCK_thread_count)); + +err_no_thd: + + free_root(&mem_root, MYF(0)); +// sql_print_information(" Worker thread exiting"); + + VOID(pthread_mutex_lock(&LOCK_workers_count)); + --workers_count; + VOID(pthread_mutex_unlock(&LOCK_workers_count)); + my_thread_end(); + pthread_exit(0); + DBUG_RETURN(0); // Can't return anything here +} + diff --git a/sql/event.h b/sql/event.h new file mode 100644 index 00000000000..5ba96e401ce --- /dev/null +++ b/sql/event.h @@ -0,0 +1,210 @@ +/* -*- C++ -*- */ +#ifndef _EVENT_H_ +#define _EVENT_H_ +#include "sp_head.h" + + +extern ulong opt_event_executor; + +#define EVEX_OK 0 +#define EVEX_KEY_NOT_FOUND -1 +#define EVEX_OPEN_TABLE_FAILED -2 +#define EVEX_WRITE_ROW_FAILED -3 +#define EVEX_DELETE_ROW_FAILED -4 +#define EVEX_GET_FIELD_FAILED -5 +#define EVEX_PARSE_ERROR -6 +#define EVEX_INTERNAL_ERROR -7 +#define EVEX_NO_DB_ERROR -8 +#define EVEX_GENERAL_ERROR -9 +#define EVEX_BAD_PARAMS -10 +#define EVEX_NOT_RUNNING -11 + +#define EVENT_EXEC_NO_MORE (1L << 0) +#define EVENT_NOT_USED (1L << 1) + + +enum enum_event_on_completion +{ + MYSQL_EVENT_ON_COMPLETION_DROP = 1, + MYSQL_EVENT_ON_COMPLETION_PRESERVE +}; + +enum enum_event_status +{ + MYSQL_EVENT_ENABLED = 1, + MYSQL_EVENT_DISABLED +}; + + +class event_timed +{ + event_timed(const event_timed &); /* Prevent use of these */ + void operator=(event_timed &); + +public: + LEX_STRING m_db; + LEX_STRING m_name; + LEX_STRING m_qname; // db.name + LEX_STRING m_body; + + LEX_STRING m_definer_user; + LEX_STRING m_definer_host; + LEX_STRING m_definer;// combination of user and host + + LEX_STRING m_comment; + TIME m_starts; + TIME m_ends; + TIME m_execute_at; + longlong m_expr; + interval_type m_interval; + longlong m_created; + longlong m_modified; + TIME m_last_executed; + enum enum_event_on_completion m_on_completion; + enum enum_event_status m_status; + sp_head *m_sphead; + + + + uint m_old_cmq; // Old CLIENT_MULTI_QUERIES value + const uchar *m_body_begin; + + bool m_dropped; + bool m_free_sphead_on_delete; + uint m_flags;//all kind of purposes + bool m_last_executed_changed; + bool m_status_changed; + + event_timed():m_expr(0), m_created(0), m_modified(0), + m_on_completion(MYSQL_EVENT_ON_COMPLETION_DROP), + m_status(MYSQL_EVENT_ENABLED), m_sphead(0), m_dropped(false), + m_free_sphead_on_delete(true), m_flags(0), + m_last_executed_changed(false), m_status_changed(false) + { init(); } + + ~event_timed() + { + if (m_free_sphead_on_delete) + free_sp(); + } + + void + init(); + + int + init_definer(THD *thd); + + int + init_execute_at(THD *thd, Item *expr); + + int + init_interval(THD *thd, Item *expr, interval_type interval); + + void + init_name(THD *thd, sp_name *name); + + int + init_starts(THD *thd, Item *starts); + + int + init_ends(THD *thd, Item *ends); + + void + event_timed::init_body(THD *thd); + + void + init_comment(THD *thd, LEX_STRING *comment); + + void + set_on_completion_drop(bool drop); + + void + set_event_status(bool enabled); + + int + load_from_row(MEM_ROOT *mem_root, TABLE *table); + + bool + compute_next_execution_time(); + + void + mark_last_executed(); + + bool + drop(THD *thd); + + bool + update_fields(THD *thd); + + char * + get_show_create_event(THD *thd, uint *length); + + int + execute(THD *thd, MEM_ROOT *mem_root); + + int + compile(THD *thd, MEM_ROOT *mem_root); + + void free_sp() + { + if (m_sphead) + { + delete m_sphead; + m_sphead= 0; + } + } +}; + + +int +evex_create_event(THD *thd, event_timed *et, uint create_options); + +int +evex_update_event(THD *thd, sp_name *name, event_timed *et); + +int +evex_drop_event(THD *thd, event_timed *et, bool drop_if_exists); + + +int +init_events(); + +void +shutdown_events(); + +/* +typedef struct st_event_item { + my_time_t execute_at; + sp_head *proc; + char *definer_user; + char *definer_host; +} EVENT_ITEM; +*/ + + +/* +CREATE TABLE `event` ( + `db` varchar(64) character set latin1 collate latin1_bin NOT NULL default '', + `name` varchar(64) NOT NULL default '', + `body` blob NOT NULL, + `definer` varchar(77) character set latin1 collate latin1_bin NOT NULL default '', + `execute_at` datetime default NULL, + `transient_expression` int(11) default NULL, + `interval_type` 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') DEFAULT NULL, + `created` timestamp NOT NULL default '0000-00-00 00:00:00', + `modified` timestamp NOT NULL default '0000-00-00 00:00:00', + `last_executed` datetime default NULL, + `starts` datetime default NULL, + `ends` datetime default NULL, + `status` enum('ENABLED','DISABLED') NOT NULL default 'ENABLED', + `on_completion` enum('DROP','PRESERVE') NOT NULL default 'DROP', + `comment` varchar(64) character set latin1 collate latin1_bin NOT NULL default '', + PRIMARY KEY (`db`,`name`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +*/ + +#endif /* _EVENT_H_ */ From e7f18c97b8528b283a06c76e7be319c3d8b5cc69 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 2 Dec 2005 13:28:14 +0100 Subject: [PATCH 03/28] add test case with \n at EOF --- mysql-test/t/events.test | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 mysql-test/t/events.test diff --git a/mysql-test/t/events.test b/mysql-test/t/events.test new file mode 100644 index 00000000000..9285b38dc6b --- /dev/null +++ b/mysql-test/t/events.test @@ -0,0 +1,19 @@ +create database events_test; +use events_test; +drop event if exists event1; +create event event1 on schedule every 15 minute starts now() ends date_add(now(), interval 5 hour) DO begin end; +alter event event1 rename to event2; +alter event event2 disable; + +drop event event2; +create event event2 on schedule every 2 second starts now() ends date_add(now(), interval 5 hour) comment "some" DO begin end; +drop event event2; + +create table t_event3 (a int, b float); +drop event if exists event3; +create event event3 on schedule every 50 + 10 minute starts date_add("20010101", interval 5 minute) ends date_add("20151010", interval 5 day) comment "portokala_comment" DO insert into t_event3 values (unix_timestamp(), rand()); +set max_allowed_packet=128000000; +select sha1(space(9999999)); +select count(*) from t_event3; +drop event event3; +drop database events_test; From 7ff79771057678b8c3474963773abe3a591fb812 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 5 Dec 2005 11:45:04 +0100 Subject: [PATCH 04/28] WL#1034 updated - split into several files - forbid parallel execution (before analyse is done how to make it possible) because the same sp_head instance cannot be executed in parallel - added GPL headers - changed EVENT_ACL to be per DB variable - fixed minor problems mysql-test/lib/init_db.sql: WL#1034 updated - add Event_priv to mysql.user (update test) - add updated mysql.event table struct scripts/mysql_fix_privilege_tables.sql: WL#1034 updated add updated mysql.event structure sql/Makefile.am: split event.cc into - event.cc (create/alter/drop) - event_executor.cc (main and worker threads) - event_timed.cc (class event_timed) - event_priv.h (some definitions used internally by the module) sql/event.cc: - added GPL header - split into few files - fixed some issues after code review - now using SP routines for opening/traversing/closing tables (will be reverted) sql/event.h: - add GPL header - remove two methods - inline them in sql_yacc.yy - don't use absolute values for EVEX_ defines but the SP_ equivalents (have to move 100% to SP_ defines and as later step to not transfer messages upwards in the stack but report them at the place they occur) - updated reference table definition - move default mem_root param from event.cc to the header sql/mysqld.cc: WL#1034 rename --event-executor to --event-scheduler executor sounds a bit scary :) sql/set_var.cc: rename internal variable name from event_executor to event_scheduler (ppl won't be scarried anymore :) sql/share/errmsg.txt: omit one %s - not needed sql/sp_head.cc: disable this DBUG_PRINT for a bit. m_next_cached_sp is 0x0 and I get crash here... sql/sp_head.h: remove m_old_cmq, a temporal variable is used in sql_yacc.yy (Serg's idea) to keep the previous state. $$ = .... YYTHD->client_capabilites != $4; (the same is done also for class event_timed) sql/sql_acl.cc: handle pre-5.1.4 table and give the user EVENT_ACL if he had CREATE_ACL sql/sql_acl.h: fix the bitmask sql/sql_parse.cc: - move from EVENT_ACL being global to EVENT_ACL being per DB like CREATE_PROC_ACL - lex->m_qname dropped, because not needed, fix code therefore - add comment that SHOW CREATE EVENT has to be implemented sql/sql_yacc.yy: - use temporal variable of Bison to store a short lived value - fix indentation - inline 2 class event_timed methods in the parser sql/tztime.h: be more expressive - it's already extern --- mysql-test/lib/init_db.sql | 33 +- scripts/mysql_fix_privilege_tables.sql | 38 + sql/Makefile.am | 5 +- sql/event.cc | 1629 +----------------------- sql/event.h | 115 +- sql/event_executor.cc | 533 ++++++++ sql/event_priv.h | 57 + sql/event_timed.cc | 944 ++++++++++++++ sql/mysqld.cc | 2 +- sql/set_var.cc | 2 +- sql/share/errmsg.txt | 2 +- sql/sp_head.cc | 8 +- sql/sp_head.h | 1 - sql/sql_acl.cc | 8 + sql/sql_acl.h | 2 +- sql/sql_parse.cc | 31 +- sql/sql_yacc.yy | 105 +- sql/tztime.h | 2 +- 18 files changed, 1833 insertions(+), 1684 deletions(-) create mode 100644 sql/event_executor.cc create mode 100644 sql/event_priv.h create mode 100644 sql/event_timed.cc diff --git a/mysql-test/lib/init_db.sql b/mysql-test/lib/init_db.sql index 85faf7869d4..4ba7184d551 100644 --- a/mysql-test/lib/init_db.sql +++ b/mysql-test/lib/init_db.sql @@ -89,6 +89,7 @@ CREATE TABLE user ( Create_routine_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Alter_routine_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_user_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, + Event_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, ssl_type enum('','ANY','X509', 'SPECIFIED') COLLATE utf8_general_ci DEFAULT '' NOT NULL, ssl_cipher BLOB NOT NULL, x509_issuer BLOB NOT NULL, @@ -103,9 +104,9 @@ CHARACTER SET utf8 COLLATE utf8_bin comment='Users and global privileges'; -INSERT INTO user VALUES ('localhost' ,'root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0); -INSERT INTO user VALUES ('@HOSTNAME@%' ,'root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0); -REPLACE INTO user VALUES ('127.0.0.1' ,'root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0); +INSERT INTO user VALUES ('localhost' ,'root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0); +INSERT INTO user VALUES ('@HOSTNAME@%' ,'root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0); +REPLACE INTO user VALUES ('127.0.0.1' ,'root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0); INSERT INTO user (host,user) VALUES ('localhost',''); INSERT INTO user (host,user) VALUES ('@HOSTNAME@%',''); @@ -566,3 +567,29 @@ CREATE TABLE proc ( comment char(64) collate utf8_bin DEFAULT '' NOT NULL, PRIMARY KEY (db,name,type) ) character set utf8 comment='Stored Procedures'; + + +CREATE TABLE event ( + 'db' VARCHAR(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '', + 'name' VARCHAR(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '', + 'body' longblob NOT NULL, + 'definer' VARCHAR(77) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '', + 'execute_at' DATETIME default NULL, + 'transient_expression' int(11) default NULL, + 'interval_type' 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') default NULL, + 'created' TIMESTAMP NOT NULL default '0000-00-00 00:00:00', + 'modified' TIMESTAMP NOT NULL default '0000-00-00 00:00:00', + 'last_executed' DATETIME default NULL, + 'starts' DATETIME default NULL, + 'ends' DATETIME default NULL, + 'status' ENUM('ENABLED','DISABLED') NOT NULL default 'ENABLED', + 'on_completion' ENUM('DROP','PRESERVE') NOT NULL default 'DROP', + 'comment' varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '', + PRIMARY KEY ('db','name') +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT 'Events'; diff --git a/scripts/mysql_fix_privilege_tables.sql b/scripts/mysql_fix_privilege_tables.sql index f8f10438505..2949049afe8 100644 --- a/scripts/mysql_fix_privilege_tables.sql +++ b/scripts/mysql_fix_privilege_tables.sql @@ -526,3 +526,41 @@ ALTER TABLE proc MODIFY db char(77) collate utf8_bin DEFAULT '' NOT NULL, MODIFY comment char(64) collate utf8_bin DEFAULT '' NOT NULL; + +# +# EVENT table +# + + +CREATE TABLE event ( + 'db' VARCHAR(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '', + 'name' VARCHAR(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '', + 'body' longblob NOT NULL, + 'definer' VARCHAR(77) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '', + 'execute_at' DATETIME default NULL, + 'transient_expression' int(11) default NULL, + 'interval_type' 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') default NULL, + 'created' TIMESTAMP NOT NULL default '0000-00-00 00:00:00', + 'modified' TIMESTAMP NOT NULL default '0000-00-00 00:00:00', + 'last_executed' DATETIME default NULL, + 'starts' DATETIME default NULL, + 'ends' DATETIME default NULL, + 'status' ENUM('ENABLED','DISABLED') NOT NULL default 'ENABLED', + 'on_completion' ENUM('DROP','PRESERVE') NOT NULL default 'DROP', + 'comment' varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '', + PRIMARY KEY ('db','name') +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT 'Events'; + + +# +# EVENT privilege +# + +ALTER TABLE mysql.user add Event_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL AFTER Create_user_priv; +ALTER TABLE mysql.db add Event_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL; diff --git a/sql/Makefile.am b/sql/Makefile.am index 0182e58ba6c..9b7ace993df 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -61,7 +61,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 event.h \ + sql_array.h sql_cursor.h event.h event_priv.h \ sql_plugin.h authors.h mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \ item.cc item_sum.cc item_buff.cc item_func.cc \ @@ -94,7 +94,8 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \ gstream.cc spatial.cc sql_help.cc sql_cursor.cc \ tztime.cc my_time.c my_decimal.cc\ sp_head.cc sp_pcontext.cc sp_rcontext.cc sp.cc \ - sp_cache.cc parse_file.cc sql_trigger.cc event.cc \ + sp_cache.cc parse_file.cc sql_trigger.cc \ + event_executor.cc event.cc event_timed.cc \ sql_plugin.cc\ handlerton.cc EXTRA_mysqld_SOURCES = ha_innodb.cc ha_berkeley.cc ha_archive.cc \ diff --git a/sql/event.cc b/sql/event.cc index 007391f76a0..82dbc2ebd76 100644 --- a/sql/event.cc +++ b/sql/event.cc @@ -1,77 +1,80 @@ +/* Copyright (C) 2000-2003 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 */ + #include "mysql_priv.h" -//#include "sql_acl.h" #include "event.h" +#include "event_priv.h" #include "sp.h" /* TODO list : - 1. Move event_timed class to event_timed.cc as well as the part of the header - 2. Do something aboute the events replication. SQL statements issued while - executing an event should not be logged into the binary log. - 3. Add a lock and use it for guarding access to events_array dynamic array. - 4. Add checks everywhere where new instance of THD is created. NULL can be + - Remove m_ prefixes of member variables. + + - Use timestamps instead of datetime. + + - Don't use SP's functionality for opening and closing of tables + + - CREATE EVENT should not go into binary log! Does it now? The SQL statements + issued by the EVENT are replicated. + + - Add a lock and use it for guarding access to events_array dynamic array. + + - Add checks everywhere where new instance of THD is created. NULL can be returned and this will crash the server. The server will crash probably later but should not be in this code! Add a global variable, and a lock to guard it, that will specify an error in a worker thread so preventing new threads from being spawned. - 5. Move executor related code to event_executor.cc and .h - 6. Maybe move all allocations during parsing to evex_mem_root thus saving + + - Maybe move all allocations during parsing to evex_mem_root thus saving double parsing in evex_create_event! - 7. If the server is killed (stopping) try to kill executing events.. - 8. What happens if one renames an event in the DB while it is in memory? + + - If the server is killed (stopping) try to kill executing events.. + + - What happens if one renames an event in the DB while it is in memory? Or even deleting it? - 9. created & modified in the table should be UTC? - 10. Add a lock to event_timed to serialize execution of an event - do not + + - created & modified in the table should be UTC? + + - Add a lock to event_timed to serialize execution of an event - do not allow parallel executions. Hmm, however how last_executed is marked then? The call to event_timed::mark_last_executed() must be moved to event_timed::execute()? - 11. Consider using conditional variable when doing shutdown instead of + + - Consider using conditional variable when doing shutdown instead of waiting some time (tries < 5). - 12. Fix event_timed::get_show_create_event. - 13. Add logging to file. - 14. Add function documentation whenever needed. -*/ + - Make event_timed::get_show_create_event() work + - Add function documentation whenever needed. + - Add logging to file -enum -{ - EVEX_FIELD_DB = 0, - EVEX_FIELD_NAME, - EVEX_FIELD_BODY, - EVEX_FIELD_DEFINER, - EVEX_FIELD_EXECUTE_AT, - EVEX_FIELD_INTERVAL_EXPR, - EVEX_FIELD_TRANSIENT_INTERVAL, - EVEX_FIELD_CREATED, - EVEX_FIELD_MODIFIED, - EVEX_FIELD_LAST_EXECUTED, - EVEX_FIELD_STARTS, - EVEX_FIELD_ENDS, - EVEX_FIELD_STATUS, - EVEX_FIELD_ON_COMPLETION, - EVEX_FIELD_COMMENT, - EVEX_FIELD_COUNT /* a cool trick to count the number of fields :) */ -}; +Warning: + - For now parallel execution is not possible because the same sp_head cannot be + executed few times!!! There is still no lock attached to particular event. + + */ -#define EVEX_OPEN_TABLE_FOR_UPDATE() \ - open_proc_type_table_for_update(thd, "event", &mysql_event_table_exists) -static bool mysql_event_table_exists= 1; -static DYNAMIC_ARRAY events_array; -static DYNAMIC_ARRAY evex_executing_queue; -static MEM_ROOT evex_mem_root; -static uint workers_count; -static bool evex_is_running= false; -static pthread_mutex_t LOCK_event_arrays, - LOCK_workers_count, - LOCK_evex_running; -extern int yyparse(void *thd); +bool mysql_event_table_exists= 1; +DYNAMIC_ARRAY events_array; +DYNAMIC_ARRAY evex_executing_queue; +MEM_ROOT evex_mem_root; + -ulong opt_event_executor; -my_bool event_executor_running_global_var= false; -extern ulong thread_created; //extern volatile uint thread_running; //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// @@ -89,7 +92,7 @@ extern ulong thread_created; a < b -> -1 */ -static inline int +inline int my_time_compare(TIME *a, TIME *b) { /* @@ -140,17 +143,10 @@ my_time_compare(TIME *a, TIME *b) } -static int +inline int event_timed_compare(event_timed **a, event_timed **b) { return my_time_compare(&(*a)->m_execute_at, &(*b)->m_execute_at); -/* - if (a->sort > b->sort) - return -1; - if (a->sort < b->sort) - return 1; - return 0; -*/ } @@ -210,15 +206,14 @@ evex_fill_row(THD *thd, TABLE *table, event_timed *et) if (et->m_expr) { - // m_expr was fixed in init_interval -// et->m_expr->save_in_field(table->field[EVEX_FIELD_INTERVAL_EXPR], (my_bool)TRUE); - table->field[EVEX_FIELD_INTERVAL_EXPR]->set_notnull(); table->field[EVEX_FIELD_INTERVAL_EXPR]->store((longlong)et->m_expr); table->field[EVEX_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! + /* + In the enum (C) intervals start from 0 but in mysql enum valid values start + from 1. Thus +1 offset is needed! + */ table->field[EVEX_FIELD_TRANSIENT_INTERVAL]->store((longlong)et->m_interval + 1); } else if (et->m_execute_at.year) @@ -243,10 +238,6 @@ evex_fill_row(THD *thd, TABLE *table, event_timed *et) store((et->m_comment).str, (et->m_comment).length, system_charset_info); DBUG_RETURN(0); -parse_error: - DBUG_RETURN(EVEX_PARSE_ERROR); -general_error: - DBUG_RETURN(EVEX_GENERAL_ERROR); get_field_failed: DBUG_RETURN(EVEX_GET_FIELD_FAILED); } @@ -299,11 +290,6 @@ db_create_event(THD *thd, event_timed *et) restore_record(table, s->default_values); // Get default values for fields strxmov(definer, et->m_definer_user.str, "@", et->m_definer_host.str, NullS); - if (table->s->fields != EVEX_FIELD_COUNT) - { - ret= EVEX_GET_FIELD_FAILED; - goto done; - } /* TODO : Uncomment these and add handling in sql_parse.cc or here if (sp->m_name.length > table->field[MYSQL_PROC_FIELD_NAME]->field_length) @@ -477,6 +463,7 @@ evex_load_and_compile_event(THD * thd, sp_name *spn, bool use_lock) allocate on evex_mem_root. call without evex_mem_root and and m_sphead will not be cleared! */ + if ((ret= ett->compile(thd, &evex_mem_root))) { thd->mem_root= tmp_mem_root; @@ -554,27 +541,23 @@ evex_remove_from_cache(LEX_STRING *db, LEX_STRING *name, bool use_lock) //we are lucky the event is in the executing queue, no need of second pass need_second_pass= false; idx= get_index_dynamic(&events_array, (gptr) ett); - if (idx != -1) - { - //destruct first and then remove. the destructor will delete sp_head - ett->free_sp(); - delete_dynamic_element(&events_array, idx); - delete_dynamic_element(&evex_executing_queue, i); - } - else + if (idx == -1) { //this should never happen - DBUG_PRINT("error", ("Sth weird with get_index_dynamic. %d." + DBUG_PRINT("error", (" get_index_dynamic problem. %d." "i=%d idx=%d evex_ex_queue.buf=%p evex_ex_queue.elements=%d ett=%p\n" "events_array=%p events_array.elements=%d events_array.buf=%p\n" "p_et=%p ett=%p", __LINE__, i, idx, &evex_executing_queue.buffer, evex_executing_queue.elements, ett, &events_array, events_array.elements, events_array.buffer, p_et, ett)); - ret= EVEX_GENERAL_ERROR; - goto done; + DBUG_ASSERT(0); } - //ok we have cleaned + //destruct first and then remove. the destructor will delete sp_head + ett->free_sp(); + delete_dynamic_element(&events_array, idx); + delete_dynamic_element(&evex_executing_queue, i); + // ok, we have cleaned } } @@ -767,41 +750,6 @@ done_no_evex: } -/* - Checks for existance of a specified event - - SYNOPSIS - evex_event_exists() - thd THD - et event's name - - NOTES - Currently unused -*/ - -bool -evex_event_exists(THD *thd, event_timed *et) -{ - TABLE *table; - int ret; - bool opened= FALSE; - Open_tables_state open_tables_state_backup; - DBUG_ENTER("evex_event_exists"); - - if (!(table= open_proc_type_table_for_read(thd, &open_tables_state_backup, - "event", &mysql_event_table_exists))) - DBUG_RETURN(SP_OPEN_TABLE_FAILED); - - ret= sp_db_find_routine_aux(thd, 0/*notype*/, et->m_db, et->m_name, table); - - close_thread_tables(thd); - thd->restore_backup_open_tables_state(&open_tables_state_backup); - thd->clear_error(); - - DBUG_RETURN(ret == SP_OK); -} - - /* Drops an event @@ -868,1443 +816,4 @@ done: } -/* - !!! This one is executor related so maybe moving it to - event_executor.cc is a good idea or ? -*/ -static int -evex_load_events_from_db(THD *thd) -{ - TABLE *table; - READ_RECORD read_record_info; - MYSQL_LOCK *lock; - Open_tables_state open_tables_state_backup; - int ret= -1; - - DBUG_ENTER("evex_load_events_from_db"); - - if (!(table= open_proc_type_table_for_read(thd, &open_tables_state_backup, - "event", &mysql_event_table_exists))) - DBUG_RETURN(SP_OPEN_TABLE_FAILED); - - VOID(pthread_mutex_lock(&LOCK_event_arrays)); - - init_read_record(&read_record_info, thd, table ,NULL,1,0); - while (!(read_record_info.read_record(&read_record_info))) - { - event_timed *et, *et_copy; - if (!(et= new event_timed())) - { - DBUG_PRINT("evex_load_events_from_db", ("Out of memory")); - ret= -1; - goto end; - } - DBUG_PRINT("evex_load_events_from_db", ("Loading event from row.")); - - if (et->load_from_row(&evex_mem_root, table)) - //error loading! - continue; - - DBUG_PRINT("evex_load_events_from_db", - ("Event %s loaded from row. Time to compile", et->m_name.str)); - - if (et->compile(thd, &evex_mem_root)) - //problem during compile - continue; - // let's find when to be executed - et->compute_next_execution_time(); - - DBUG_PRINT("evex_load_events_from_db", - ("Adding %s to the executor list.", et->m_name.str)); - VOID(push_dynamic(&events_array,(gptr) et)); - // we always add at the end so the number of elements - 1 is the place - // in the buffer - et_copy= dynamic_element(&events_array, events_array.elements - 1, - event_timed*); - VOID(push_dynamic(&evex_executing_queue,(gptr) &et_copy)); - et->m_free_sphead_on_delete= false; - DBUG_PRINT("info", ("")); - delete et; - } - end_read_record(&read_record_info); - - qsort((gptr) dynamic_element(&evex_executing_queue, 0, event_timed**), - evex_executing_queue.elements, - sizeof(event_timed **), - (qsort_cmp) event_timed_compare - ); - VOID(pthread_mutex_unlock(&LOCK_event_arrays)); - - thd->version--; // Force close to free memory - ret= 0; - -end: - close_thread_tables(thd); - thd->restore_backup_open_tables_state(&open_tables_state_backup); - - DBUG_PRINT("evex_load_events_from_db", - ("Events loaded from DB. Status code %d", ret)); - DBUG_RETURN(ret); -} - -//////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////// -//////////////// EVENT_TIMED class ///////////////////////////////// -//////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////// - - -/* - Init all member variables - - SYNOPSIS - event_timed::init() -*/ - -void -event_timed::init() -{ - DBUG_ENTER("event_timed::init"); - - m_qname.str= m_db.str= m_name.str= m_body.str= m_comment.str= 0; - m_qname.length= m_db.length= m_name.length= m_body.length= m_comment.length= 0; - - set_zero_time(&m_starts, MYSQL_TIMESTAMP_DATETIME); - set_zero_time(&m_ends, MYSQL_TIMESTAMP_DATETIME); - set_zero_time(&m_execute_at, MYSQL_TIMESTAMP_DATETIME); - set_zero_time(&m_last_executed, MYSQL_TIMESTAMP_DATETIME); - - m_definer_user.str= m_definer_host.str= 0; - m_definer_user.length= m_definer_host.length= 0; - - DBUG_VOID_RETURN; -} - - -/* - Set a name of the event - - SYNOPSIS - event_timed::init_name() - thd THD - name the name extracted in the parser -*/ - -void -event_timed::init_name(THD *thd, sp_name *name) -{ - DBUG_ENTER("event_timed::init_name"); - uint n; /* Counter for nul trimming */ - /* During parsing, we must use thd->mem_root */ - MEM_ROOT *root= thd->mem_root; - - /* We have to copy strings to get them into the right memroot */ - if (name) - { - m_db.length= name->m_db.length; - if (name->m_db.length == 0) - m_db.str= NULL; - else - m_db.str= strmake_root(root, name->m_db.str, name->m_db.length); - m_name.length= name->m_name.length; - m_name.str= strmake_root(root, name->m_name.str, name->m_name.length); - - if (name->m_qname.length == 0) - name->init_qname(thd); - m_qname.length= name->m_qname.length; - m_qname.str= strmake_root(root, name->m_qname.str, m_qname.length); - } - else if (thd->db) - { - m_db.length= thd->db_length; - m_db.str= strmake_root(root, thd->db, m_db.length); - } - - DBUG_PRINT("m_db", ("len=%d db=%s",m_db.length, m_db.str)); - DBUG_PRINT("m_name", ("len=%d name=%s",m_name.length, m_name.str)); - - DBUG_VOID_RETURN; -} - - -/* - Set body of the event - what should be executed. - - SYNOPSIS - event_timed::init_body() - thd THD - - NOTE - The body is extracted by copying all data between the - start of the body set by another method and the current pointer in Lex. -*/ - -void -event_timed::init_body(THD *thd) -{ - DBUG_ENTER("event_timed::init_body"); - MEM_ROOT *root= thd->mem_root; - - m_body.length= thd->lex->ptr - m_body_begin; - // Trim nuls at the end - while (m_body.length && m_body_begin[m_body.length-1] == '\0') - m_body.length--; - - m_body.str= strmake_root(root, (char *)m_body_begin, m_body.length); - - DBUG_VOID_RETURN; -} - - -/* - Set time for execution for one time events. - - SYNOPSIS - event_timed::init_execute_at() - expr when (datetime) - - RETURNS - 0 - OK - EVEX_PARSE_ERROR - fix_fields failed - EVEX_BAD_PARAMS - datetime is in the past -*/ - -int -event_timed::init_execute_at(THD *thd, Item *expr) -{ - my_bool not_used; - TIME ltime; - my_time_t my_time_tmp; - - TIME time_tmp; - DBUG_ENTER("event_timed::init_execute_at"); - - if (expr->fix_fields(thd, &expr)) - DBUG_RETURN(EVEX_PARSE_ERROR); - - if (expr->val_int() == MYSQL_TIMESTAMP_ERROR) - DBUG_RETURN(EVEX_BAD_PARAMS); - - // let's check whether time is in the past - thd->variables.time_zone->gmt_sec_to_TIME(&time_tmp, - (my_time_t) thd->query_start()); - - if (expr->val_int() < TIME_to_ulonglong_datetime(&time_tmp)) - DBUG_RETURN(EVEX_BAD_PARAMS); - - if ((not_used= expr->get_date(<ime, TIME_NO_ZERO_DATE))) - DBUG_RETURN(EVEX_BAD_PARAMS); - - /* - This may result in a 1970-01-01 date if ltime is > 2037-xx-xx - CONVERT_TZ has similar problem - */ - my_tz_UTC->gmt_sec_to_TIME(<ime, TIME_to_timestamp(thd,<ime, ¬_used)); - - - m_execute_at= ltime; - DBUG_RETURN(0); -} - - -/* - Set time for execution for transient events. - - SYNOPSIS - event_timed::init_interval() - expr how much? - interval what is the interval - - RETURNS - 0 - OK - EVEX_PARSE_ERROR - fix_fields failed - EVEX_BAD_PARAMS - Interval is not positive -*/ - -int -event_timed::init_interval(THD *thd, Item *expr, interval_type interval) -{ - longlong tmp; - DBUG_ENTER("event_timed::init_interval"); - - if (expr->fix_fields(thd, &expr)) - DBUG_RETURN(EVEX_PARSE_ERROR); - - if ((tmp= expr->val_int()) <= 0) - DBUG_RETURN(EVEX_BAD_PARAMS); - - m_expr= tmp; - m_interval= interval; - DBUG_RETURN(0); -} - - -/* - Set activation time. - - SYNOPSIS - event_timed::init_starts() - expr how much? - interval what is the interval - - NOTES - Note that activation time is not execution time. - EVERY 5 MINUTE STARTS "2004-12-12 10:00:00" means that - the event will be executed every 5 minutes but this will - start at the date shown above. Expressions are possible : - DATE_ADD(NOW(), INTERVAL 1 DAY) -- start tommorow at - same time. - - RETURNS - 0 - OK - EVEX_PARSE_ERROR - fix_fields failed -*/ - -int -event_timed::init_starts(THD *thd, Item *starts) -{ - my_bool not_used; - TIME ltime; - my_time_t my_time_tmp; - - DBUG_ENTER("event_timed::init_starts"); - - if (starts->fix_fields(thd, &starts)) - DBUG_RETURN(EVEX_PARSE_ERROR); - - if (starts->val_int() == MYSQL_TIMESTAMP_ERROR) - DBUG_RETURN(EVEX_BAD_PARAMS); - - if ((not_used= starts->get_date(<ime, TIME_NO_ZERO_DATE))) - DBUG_RETURN(EVEX_BAD_PARAMS); - - /* - This may result in a 1970-01-01 date if ltime is > 2037-xx-xx - CONVERT_TZ has similar problem - */ - my_tz_UTC->gmt_sec_to_TIME(<ime, TIME_to_timestamp(thd,<ime, ¬_used)); - - m_starts= ltime; - DBUG_RETURN(0); -} - - -/* - Set deactivation time. - - SYNOPSIS - event_timed::init_ends() - thd THD - ends when? - - NOTES - Note that activation time is not execution time. - EVERY 5 MINUTE ENDS "2004-12-12 10:00:00" means that - the event will be executed every 5 minutes but this will - end at the date shown above. Expressions are possible : - DATE_ADD(NOW(), INTERVAL 1 DAY) -- end tommorow at - same time. - - RETURNS - 0 - OK - EVEX_PARSE_ERROR - fix_fields failed - EVEX_BAD_PARAMS - ENDS before STARTS -*/ - -int -event_timed::init_ends(THD *thd, Item *ends) -{ - TIME ltime; - my_time_t my_time_tmp; - my_bool not_used; - - DBUG_ENTER("event_timed::init_ends"); - - if (ends->fix_fields(thd, &ends)) - DBUG_RETURN(EVEX_PARSE_ERROR); - - // the field was already fixed in init_ends - if ((not_used= ends->get_date(<ime, TIME_NO_ZERO_DATE))) - DBUG_RETURN(EVEX_BAD_PARAMS); - - /* - This may result in a 1970-01-01 date if ltime is > 2037-xx-xx - CONVERT_TZ has similar problem - */ - my_tz_UTC->gmt_sec_to_TIME(<ime, TIME_to_timestamp(thd, <ime, ¬_used)); - - if (m_starts.year && my_time_compare(&m_starts, <ime) != -1) - DBUG_RETURN(EVEX_BAD_PARAMS); - - m_ends= ltime; - DBUG_RETURN(0); -} - - -/* - Set behaviour when ENDS has been set and passed by. - - SYNOPSIS - event_timed::init_interval() - drop if set then drop the event otherwise preserve it. -*/ - -void -event_timed::set_on_completion_drop(bool drop) -{ - DBUG_ENTER("event_timed::set_on_completion"); - if (drop) - m_on_completion= MYSQL_EVENT_ON_COMPLETION_DROP; - else - m_on_completion= MYSQL_EVENT_ON_COMPLETION_PRESERVE; - - DBUG_VOID_RETURN; -} - - -/* - Sets event's status. DISABLED - not executable even if - everything else is ok (STARTS, ENDS, INTERVAL and so on). - - SYNOPSIS - event_timed::set_event_status() - enabled set whether enabled or not. -*/ - -void -event_timed::set_event_status(bool enabled) -{ - DBUG_ENTER("event_timed::set_on_completion"); - - m_status_changed= true; - if (enabled) - m_status= MYSQL_EVENT_ENABLED; - else - m_status= MYSQL_EVENT_DISABLED; - - DBUG_VOID_RETURN; -} - - -/* - Sets comment. - - SYNOPSIS - event_timed::init_comment() - thd THD - used for memory allocation - comment the string. -*/ - -void -event_timed::init_comment(THD *thd, LEX_STRING *comment) -{ - DBUG_ENTER("event_timed::init_comment"); - - MEM_ROOT *root= thd->mem_root; - m_comment.length= comment->length; - m_comment.str= strmake_root(root, comment->str, comment->length); - DBUG_PRINT("m_comment", ("len=%d",m_comment.length)); - - DBUG_VOID_RETURN; -} - - -/* - Inits definer (m_definer_user and m_definer_host) during - parsing. - - SYNOPSIS - event_timed::init_definer() -*/ - -int -event_timed::init_definer(THD *thd) -{ - DBUG_ENTER("event_timed::init_definer"); - - m_definer_user.str= strdup_root(thd->mem_root, thd->security_ctx->priv_user); - m_definer_user.length= strlen(thd->security_ctx->priv_user); - - m_definer_host.str= strdup_root(thd->mem_root, thd->security_ctx->priv_host); - m_definer_host.length= strlen(thd->security_ctx->priv_host); - - DBUG_RETURN(0); -} - - -/* - Loads an event from a row from mysql.event - - SYNOPSIS - event_timed::load_from_row() -*/ - -int -event_timed::load_from_row(MEM_ROOT *mem_root, TABLE *table) -{ - longlong created; - longlong modified; - char *ptr; - event_timed *et; - uint len; - bool res1, res2; - - DBUG_ENTER("event_timed::load_from_row"); - - if (!table) - goto error; - - et= this; - - if (table->s->fields != EVEX_FIELD_COUNT) - goto error; - - if ((et->m_db.str= get_field(mem_root, - table->field[EVEX_FIELD_DB])) == NULL) - goto error; - - et->m_db.length= strlen(et->m_db.str); - - if ((et->m_name.str= get_field(mem_root, - table->field[EVEX_FIELD_NAME])) == NULL) - goto error; - - et->m_name.length= strlen(et->m_name.str); - - if ((et->m_body.str= get_field(mem_root, - table->field[EVEX_FIELD_BODY])) == NULL) - goto error; - - et->m_body.length= strlen(et->m_body.str); - - if ((et->m_definer.str= get_field(mem_root, - table->field[EVEX_FIELD_DEFINER])) == NullS) - goto error; - et->m_definer.length= strlen(et->m_definer.str); - - ptr= strchr(et->m_definer.str, '@'); - - if (! ptr) - ptr= et->m_definer.str; // Weird, isn't it? - - len= ptr - et->m_definer.str; - - et->m_definer_user.str= strmake_root(mem_root, et->m_definer.str, len); - et->m_definer_user.length= len; - len= et->m_definer.length - len - 1; //1 is because of @ - et->m_definer_host.str= strmake_root(mem_root, ptr + 1, len);//1: because of @ - et->m_definer_host.length= len; - - - res1= table->field[EVEX_FIELD_STARTS]-> - get_date(&et->m_starts, TIME_NO_ZERO_DATE); - - res2= table->field[EVEX_FIELD_ENDS]-> - get_date(&et->m_ends, TIME_NO_ZERO_DATE); - - et->m_expr= table->field[EVEX_FIELD_INTERVAL_EXPR]->val_int(); - - /* - If res1 and res2 are true then both fields are empty. - Hence if EVEX_FIELD_EXECUTE_AT is empty there is an error. - */ - if (res1 && res2 && !et->m_expr && table->field[EVEX_FIELD_EXECUTE_AT]-> - get_date(&et->m_execute_at, TIME_NO_ZERO_DATE)) - goto error; - - /* - In DB the values start from 1 but enum interval_type starts - from 0 - */ - et->m_interval= (interval_type) - ((ulonglong) table->field[EVEX_FIELD_TRANSIENT_INTERVAL]->val_int() - 1); - - et->m_modified= table->field[EVEX_FIELD_CREATED]->val_int(); - et->m_created= table->field[EVEX_FIELD_MODIFIED]->val_int(); - - /* - ToDo Andrey : Ask PeterG & Serg what to do in this case. - Whether on load last_executed_at should be loaded - or it must be 0ed. If last_executed_at is loaded - then an event can be scheduled for execution - instantly. Let's say an event has to be executed - every 15 mins. The server has been stopped for - more than this time and then started. If L_E_AT - is loaded from DB, execution at L_E_AT+15min - will be scheduled. However this time is in the past. - Hence immediate execution. Due to patch of - ::mark_last_executed() m_last_executed gets time_now - and not m_execute_at. If not like this a big - queue can be scheduled for times which are still in - the past (2, 3 and more executions which will be - consequent). - */ - set_zero_time(&m_last_executed, MYSQL_TIMESTAMP_DATETIME); -#ifdef ANDREY_0 - table->field[EVEX_FIELD_LAST_EXECUTED]-> - get_date(&et->m_last_executed, TIME_NO_ZERO_DATE); -#endif - m_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[EVEX_FIELD_STATUS])) == NullS) - goto error; - - DBUG_PRINT("load_from_row", ("Event [%s] is [%s]", et->m_name.str, ptr)); - et->m_status= (ptr[0]=='E'? MYSQL_EVENT_ENABLED: - MYSQL_EVENT_DISABLED); - - // ToDo : Andrey . Find a way not to allocate ptr on event_mem_root - if ((ptr= get_field(mem_root, - table->field[EVEX_FIELD_ON_COMPLETION])) == NullS) - goto error; - - et->m_on_completion= (ptr[0]=='D'? MYSQL_EVENT_ON_COMPLETION_DROP: - MYSQL_EVENT_ON_COMPLETION_PRESERVE); - - et->m_comment.str= get_field(mem_root, table->field[EVEX_FIELD_COMMENT]); - if (et->m_comment.str != NullS) - et->m_comment.length= strlen(et->m_comment.str); - else - et->m_comment.length= 0; - - DBUG_RETURN(0); -error: - DBUG_RETURN(EVEX_GET_FIELD_FAILED); -} - - -bool -event_timed::compute_next_execution_time() -{ - TIME time_now; - my_time_t now; - int tmp; - - DBUG_ENTER("event_timed::compute_next_execution_time"); - - if (m_status == MYSQL_EVENT_DISABLED) - { - DBUG_PRINT("compute_next_execution_time", - ("Event %s is DISABLED", m_name.str)); - goto ret; - } - //if one-time no need to do computation - if (!m_expr) - { - //let's check whether it was executed - if (m_last_executed.year) - { - DBUG_PRINT("compute_next_execution_time", - ("One-time event %s was already executed", m_name.str)); - if (m_on_completion == MYSQL_EVENT_ON_COMPLETION_DROP) - { - DBUG_PRINT("compute_next_execution_time", - ("One-time event will be dropped.")); - m_dropped= true; - } - m_status= MYSQL_EVENT_DISABLED; - m_status_changed= true; - } - goto ret; - } - time(&now); - my_tz_UTC->gmt_sec_to_TIME(&time_now, now); -/* - sql_print_information("[%s.%s]", m_db.str, m_name.str); - sql_print_information("time_now : [%d-%d-%d %d:%d:%d ]", time_now.year, time_now.month, time_now.day, time_now.hour, time_now.minute, time_now.second); - sql_print_information("m_starts : [%d-%d-%d %d:%d:%d ]", m_starts.year, m_starts.month, m_starts.day, m_starts.hour, m_starts.minute, m_starts.second); - sql_print_information("m_ends : [%d-%d-%d %d:%d:%d ]", m_ends.year, m_ends.month, m_ends.day, m_ends.hour, m_ends.minute, m_ends.second); - sql_print_information("m_last_ex: [%d-%d-%d %d:%d:%d ]", m_last_executed.year, m_last_executed.month, m_last_executed.day, m_last_executed.hour, m_last_executed.minute, m_last_executed.second); -*/ - //if time_now is after m_ends don't execute anymore - if (m_ends.year && (tmp= my_time_compare(&m_ends, &time_now)) == -1) - { - // time_now is after m_ends. don't execute anymore - set_zero_time(&m_execute_at, MYSQL_TIMESTAMP_DATETIME); - if (m_on_completion == MYSQL_EVENT_ON_COMPLETION_DROP) - m_dropped= true; - m_status= MYSQL_EVENT_DISABLED; - m_status_changed= true; - - goto ret; - } - - /* - Here time_now is before or equals m_ends if the latter is set. - Let's check whether time_now is before m_starts. - If so schedule for m_starts - */ - if (m_starts.year && (tmp= my_time_compare(&time_now, &m_starts)) < 1) - { - if (tmp == 0 && my_time_compare(&m_starts, &m_last_executed) == 0) - { - /* - time_now = m_starts = m_last_executed - do nothing or we will schedule for second time execution at m_starts. - */ - } - else - { - //m_starts is in the future - //time_now before m_starts. Scheduling for m_starts - m_execute_at= m_starts; - goto ret; - } - } - - if (m_starts.year && m_ends.year) - { - /* - Both m_starts and m_ends are set and time_now is between them (incl.) - If m_last_executed is set then increase with m_expr. The new TIME is - after m_ends set m_execute_at to 0. And check for m_on_completion - If not set then schedule for now. - */ - if (!m_last_executed.year) - m_execute_at= time_now; - else - { - my_time_t last, ll_ends; - - // There was previous execution - last= sec_since_epoch_TIME(&m_last_executed) + m_expr; - ll_ends= sec_since_epoch_TIME(&m_ends); - //now convert back to TIME - //ToDo Andrey: maybe check for error here? - if (ll_ends < last) - { - // Next execution after ends. No more executions - set_zero_time(&m_execute_at, MYSQL_TIMESTAMP_DATETIME); - if (m_on_completion == MYSQL_EVENT_ON_COMPLETION_DROP) - m_dropped= true; - } - else - my_tz_UTC->gmt_sec_to_TIME(&m_execute_at, last); - } - goto ret; - } - else if (!m_starts.year && !m_ends.year) - { - // both m_starts and m_ends are not set, se we schedule for the next - // based on m_last_executed - if (!m_last_executed.year) - //m_last_executed not set. Schedule the event for now - m_execute_at= time_now; - else - //ToDo Andrey: maybe check for error here? - my_tz_UTC->gmt_sec_to_TIME(&m_execute_at, - sec_since_epoch_TIME(&m_last_executed) + m_expr); - goto ret; - } - else - { - //either m_starts or m_ends is set - if (m_starts.year) - { - /* - - m_starts is set. - - m_starts is not in the future according to check made before - Hence schedule for m_starts + m_expr in case m_last_executed - is not set, otherwise to m_last_executed + m_expr - */ - my_time_t last; - - //convert either m_last_executed or m_starts to seconds - if (m_last_executed.year) - last= sec_since_epoch_TIME(&m_last_executed) + m_expr; - else - last= sec_since_epoch_TIME(&m_starts); - - //now convert back to TIME - //ToDo Andrey: maybe check for error here? - my_tz_UTC->gmt_sec_to_TIME(&m_execute_at, last); - } - else - { - /* - - m_ends is set - - m_ends is after time_now or is equal - Hence check for m_last_execute and increment with m_expr. - If m_last_executed is not set then schedule for now - */ - my_time_t last, ll_ends; - - if (!m_last_executed.year) - m_execute_at= time_now; - else - { - last= sec_since_epoch_TIME(&m_last_executed); - ll_ends= sec_since_epoch_TIME(&m_ends); - last+= m_expr; - //now convert back to TIME - //ToDo Andrey: maybe check for error here? - if (ll_ends < last) - { - set_zero_time(&m_execute_at, MYSQL_TIMESTAMP_DATETIME); - if (m_on_completion == MYSQL_EVENT_ON_COMPLETION_DROP) - m_dropped= true; - } - else - my_tz_UTC->gmt_sec_to_TIME(&m_execute_at, last); - } - } - goto ret; - } -ret: - - DBUG_RETURN(false); -} - - -void -event_timed::mark_last_executed() -{ - TIME time_now; - my_time_t now; - - time(&now); - my_tz_UTC->gmt_sec_to_TIME(&time_now, now); - - m_last_executed= time_now; // was m_execute_at -#ifdef ANDREY_0 - m_last_executed= m_execute_at; -#endif - m_last_executed_changed= true; -} - - -bool -event_timed::drop(THD *thd) -{ - DBUG_ENTER("event_timed::drop"); - - if (evex_drop_event(thd, this, false)) - DBUG_RETURN(true); - - DBUG_RETURN(false); -} - - -bool -event_timed::update_fields(THD *thd) -{ - TABLE *table; - int ret= 0; - bool opened; - - DBUG_ENTER("event_timed::update_time_fields"); - - DBUG_PRINT("enter", ("name: %*s", m_name.length, m_name.str)); - - //no need to update if nothing has changed - if (!(m_status_changed || m_last_executed_changed)) - goto done; - - if (!(table= EVEX_OPEN_TABLE_FOR_UPDATE())) - DBUG_RETURN(SP_OPEN_TABLE_FAILED); - - if ((ret= sp_db_find_routine_aux(thd, 0/*notype*/, m_db, m_name, table))) - goto done; - - store_record(table,record[1]); - table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; // Don't update create on row update. - - if (m_last_executed_changed) - { - table->field[EVEX_FIELD_LAST_EXECUTED]->set_notnull(); - table->field[EVEX_FIELD_LAST_EXECUTED]->store_time(&m_last_executed, - MYSQL_TIMESTAMP_DATETIME); - m_last_executed_changed= false; - } - if (m_status_changed) - { - table->field[EVEX_FIELD_STATUS]->set_notnull(); - table->field[EVEX_FIELD_STATUS]->store((longlong)m_status); - m_status_changed= false; - } - - if ((table->file->update_row(table->record[1],table->record[0]))) - ret= EVEX_WRITE_ROW_FAILED; - -done: - close_thread_tables(thd); - - DBUG_RETURN(ret); -} - - -char * -event_timed::get_show_create_event(THD *thd, uint *length) -{ - char *dst, *ret; - uint len, tmp_len; - - len = strlen("CREATE EVENT ") + m_db.length + strlen(".") + m_name.length + - strlen(" ON SCHEDULE ") + strlen("EVERY 5 MINUTE ") -/* - + strlen("ON COMPLETION ") - + (m_on_completion==MYSQL_EVENT_ON_COMPLETION_DROP? - strlen("NOT PRESERVE "):strlen("PRESERVE ")) - + (m_status==MYSQL_EVENT_ENABLED? - strlen("ENABLE "):strlen("DISABLE ")) - + strlen("COMMENT \"") + m_comment.length + strlen("\" ") -*/ - + strlen("DO ") + - + m_body.length + strlen(";"); - - ret= dst= (char*) alloc_root(thd->mem_root, len); - memcpy(dst, "CREATE EVENT ", tmp_len= strlen("CREATE EVENT ")); - dst+= tmp_len; - memcpy(dst, m_db.str, tmp_len=m_db.length); - dst+= tmp_len; - memcpy(dst, ".", tmp_len= strlen(".")); - dst+= tmp_len; - memcpy(dst, m_name.str, tmp_len= m_name.length); - dst+= tmp_len; - memcpy(dst, " ON SCHEDULE ", tmp_len= strlen(" ON SCHEDULE ")); - dst+= tmp_len; - memcpy(dst, "EVERY 5 MINUTE ", tmp_len= strlen("EVERY 5 MINUTE ")); - dst+= tmp_len; -/* - memcpy(dst, "ON COMPLETION ", tmp_len =strlen("ON COMPLETION ")); - dst+= tmp_len; - memcpy(dst, (m_on_completion==MYSQL_EVENT_ON_COMPLETION_DROP? - "NOT PRESERVE ":"PRESERVE "), - tmp_len =(m_on_completion==MYSQL_EVENT_ON_COMPLETION_DROP? 13:9)); - dst+= tmp_len; - - memcpy(dst, (m_status==MYSQL_EVENT_ENABLED? - "ENABLE ":"DISABLE "), - tmp_len= (m_status==MYSQL_EVENT_ENABLED? 8:9)); - dst+=tmp_len; - - memcpy(dst, "COMMENT \"", tmp_len= strlen("COMMENT \"")); - dst+= tmp_len; - memcpy(dst, m_comment.str, tmp_len= m_comment.length); - dst+= tmp_len; - memcpy(dst, "\" ", tmp_len=2); - dst+= tmp_len; -*/ - memcpy(dst, "DO ", tmp_len=3); - dst+= tmp_len; - - memcpy(dst, m_body.str, tmp_len= m_body.length); - dst+= tmp_len; - memcpy(dst, ";", 1); - ++dst; - *dst= '\0'; - - *length= len; - - return ret; -} - - -int -event_timed::execute(THD *thd, MEM_ROOT *mem_root= NULL) -{ - List empty_item_list; - int ret= 0; - - DBUG_ENTER("event_timed::execute"); - - // TODO Andrey : make this as member variable and delete in destructor - empty_item_list.empty(); - - if (!m_sphead && (ret= compile(thd, mem_root))) - goto done; - - ret= m_sphead->execute_procedure(thd, &empty_item_list); - -done: - // Don't cache m_sphead if allocated on another mem_root - if (mem_root && m_sphead) - { - delete m_sphead; - m_sphead= 0; - } - - DBUG_RETURN(ret); -} - - -int -event_timed::compile(THD *thd, MEM_ROOT *mem_root= NULL) -{ - MEM_ROOT *tmp_mem_root= 0; - LEX *old_lex= thd->lex, lex; - char *old_db; - event_timed *ett; - sp_name *spn; - char *old_query; - uint old_query_len; - st_sp_chistics *p; - - DBUG_ENTER("event_timed::compile"); - // change the memory root for the execution time - if (mem_root) - { - tmp_mem_root= thd->mem_root; - thd->mem_root= mem_root; - } - old_query_len= thd->query_length; - old_query= thd->query; - old_db= thd->db; - thd->db= m_db.str; - thd->query= get_show_create_event(thd, &thd->query_length); - DBUG_PRINT("event_timed::compile", ("query:%s",thd->query)); - - thd->lex= &lex; - lex_start(thd, (uchar*)thd->query, thd->query_length); - lex.et_compile_phase= TRUE; - if (yyparse((void *)thd) || thd->is_fatal_error) - { - // Free lex associated resources - // QQ: Do we really need all this stuff here ? - if (lex.sphead) - { - if (&lex != thd->lex) - thd->lex->sphead->restore_lex(thd); - delete lex.sphead; - lex.sphead= 0; - } - // QQ: anything else ? - lex_end(&lex); - thd->lex= old_lex; - DBUG_RETURN(-1); - } - - m_sphead= lex.sphead; - m_sphead->m_db= m_db; - //copy also chistics since they will vanish otherwise we get 0x0 pointer - // Todo : Handle sql_mode !! - m_sphead->set_definer(m_definer.str, m_definer.length); - m_sphead->set_info(0, 0, &lex.sp_chistics, 0/*sql_mode*/); - m_sphead->optimize(); - lex_end(&lex); - thd->lex= old_lex; - thd->query= old_query; - thd->query_length= old_query_len; - thd->db= old_db; - /* - Change the memory root for the execution time. - */ - if (mem_root) - thd->mem_root= tmp_mem_root; - - DBUG_RETURN(0); -} - - -/******************************** EXECUTOR ************************************/ - - -//extern "C" pthread_handler_decl(event_executor_main, arg); -//extern "C" pthread_handler_decl(event_executor_worker, arg); - -/* - TODO Andrey: Check for command line option whether to start - the main thread or not. -*/ - -pthread_handler_t event_executor_worker(void *arg); -pthread_handler_t event_executor_main(void *arg); - -int -init_events() -{ - pthread_t th; - - DBUG_ENTER("init_events"); - - DBUG_PRINT("info",("Starting events main thread")); - - pthread_mutex_init(&LOCK_event_arrays, MY_MUTEX_INIT_FAST); - pthread_mutex_init(&LOCK_workers_count, MY_MUTEX_INIT_FAST); - pthread_mutex_init(&LOCK_evex_running, MY_MUTEX_INIT_FAST); - - VOID(pthread_mutex_lock(&LOCK_evex_running)); - evex_is_running= false; - event_executor_running_global_var= false; - VOID(pthread_mutex_unlock(&LOCK_evex_running)); - - //TODO Andrey: Change the error code returned! - if (pthread_create(&th, NULL, event_executor_main, (void*)NULL)) - DBUG_RETURN(ER_SLAVE_THREAD); - - DBUG_RETURN(0); -} - - -void -shutdown_events() -{ - VOID(pthread_mutex_lock(&LOCK_evex_running)); - VOID(pthread_mutex_unlock(&LOCK_evex_running)); - pthread_mutex_destroy(&LOCK_event_arrays); - pthread_mutex_destroy(&LOCK_workers_count); - pthread_mutex_destroy(&LOCK_evex_running); -} - - -static int -init_event_thread(THD* thd) -{ - DBUG_ENTER("init_event_thread"); - thd->client_capabilities= 0; - thd->security_ctx->skip_grants(); - my_net_init(&thd->net, 0); - thd->net.read_timeout = slave_net_timeout; - thd->slave_thread= 0; - thd->options= OPTION_AUTO_IS_NULL; - thd->client_capabilities= CLIENT_LOCAL_FILES; - thd->real_id=pthread_self(); - VOID(pthread_mutex_lock(&LOCK_thread_count)); - thd->thread_id= thread_id++; - VOID(pthread_mutex_unlock(&LOCK_thread_count)); - - if (init_thr_lock() || thd->store_globals()) - { - thd->cleanup(); - delete thd; - DBUG_RETURN(-1); - } - -#if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__) - sigset_t set; - VOID(sigemptyset(&set)); // Get mask in use - VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals)); -#endif - - thd->proc_info= "Initialized"; - thd->version= refresh_version; - thd->set_time(); - DBUG_RETURN(0); -} - - -pthread_handler_t event_executor_main(void *arg) -{ - THD *thd; /* needs to be first for thread_stack */ - ulonglong iter_num= 0; - uint i=0, j=0; - - DBUG_ENTER("event_executor_main"); - DBUG_PRINT("event_executor_main", ("EVEX thread started")); - - VOID(pthread_mutex_lock(&LOCK_evex_running)); - evex_is_running= true; - event_executor_running_global_var= opt_event_executor; - VOID(pthread_mutex_unlock(&LOCK_evex_running)); - - // init memory root - init_alloc_root(&evex_mem_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC); - - // needs to call my_thread_init(), otherwise we get a coredump in DBUG_ stuff - my_thread_init(); - - //TODO Andrey: Check for NULL - if (!(thd = new THD)) // note that contructor of THD uses DBUG_ ! - { - sql_print_error("Cannot create THD for event_executor_main"); - goto err_no_thd; - } - thd->thread_stack = (char*)&thd; // remember where our stack is - - pthread_detach_this_thread(); - - if (init_event_thread(thd)) - goto err; - - thd->init_for_queries(); - - VOID(pthread_mutex_lock(&LOCK_thread_count)); - threads.append(thd); - thread_count++; - thread_running++; - VOID(pthread_mutex_unlock(&LOCK_thread_count)); - - DBUG_PRINT("EVEX main thread", ("Initing events_array")); - - VOID(pthread_mutex_lock(&LOCK_event_arrays)); - /* - my_malloc is used as underlying allocator which does not use a mem_root - thus data should be freed at later stage. - */ - VOID(my_init_dynamic_array(&events_array, sizeof(event_timed), 50, 100)); - VOID(my_init_dynamic_array(&evex_executing_queue, sizeof(event_timed *), 50, 100)); - VOID(pthread_mutex_unlock(&LOCK_event_arrays)); - - if (evex_load_events_from_db(thd)) - goto err; - - THD_CHECK_SENTRY(thd); - /* Read queries from the IO/THREAD until this thread is killed */ - while (!thd->killed) - { - TIME time_now; - my_time_t now; - my_ulonglong cnt; - - DBUG_PRINT("info", ("EVEX External Loop %d", ++cnt)); -// sql_print_information("[EVEX] External Loop!"); - my_sleep(500000);// sleep 0.5s - if (!event_executor_running_global_var) - continue; - time(&now); - my_tz_UTC->gmt_sec_to_TIME(&time_now, now); - - - VOID(pthread_mutex_lock(&LOCK_event_arrays)); - for (i= 0; (i < evex_executing_queue.elements) && !thd->killed; ++i) - { - event_timed **p_et=dynamic_element(&evex_executing_queue,i,event_timed**); - event_timed *et= *p_et; -// sql_print_information("[EVEX] External Loop 2!"); - - if (!event_executor_running_global_var) - break;// soon we will do only continue (see the code a bit above) - - thd->proc_info = "Iterating"; - THD_CHECK_SENTRY(thd); - /* - if this is the first event which is after time_now then no - more need to iterate over more elements since the array is sorted. - */ - if (et->m_execute_at.year && - my_time_compare(&time_now, &et->m_execute_at) == -1) - break; - - if (et->m_status == MYSQL_EVENT_ENABLED) - { - pthread_t th; - - DBUG_PRINT("info", (" Spawning a thread %d", ++iter_num)); -// sql_print_information(" Spawning a thread %d", ++iter_num); - - if (pthread_create(&th, NULL, event_executor_worker, (void*)et)) - { - sql_print_error("Problem while trying to create a thread"); - VOID(pthread_mutex_unlock(&LOCK_event_arrays)); - goto err; // for now finish execution of the Executor - } - - et->mark_last_executed(); - et->compute_next_execution_time(); - et->update_fields(thd); - if ((et->m_execute_at.year && !et->m_expr) - || TIME_to_ulonglong_datetime(&et->m_execute_at) == 0L) - et->m_flags |= EVENT_EXEC_NO_MORE; - } - } - /* - Let's remove elements which won't be executed any more - The number is "i" and it is <= up to evex_executing_queue.elements - */ - j= 0; - while (j < i && j < evex_executing_queue.elements) - { - event_timed **p_et= dynamic_element(&evex_executing_queue, j, event_timed**); - event_timed *et= *p_et; - if (et->m_flags & EVENT_EXEC_NO_MORE || et->m_status == MYSQL_EVENT_DISABLED) - { - delete_dynamic_element(&evex_executing_queue, j); - DBUG_PRINT("", ("DELETING FROM EXECUTION QUEUE [%s.%s]",et->m_db.str, et->m_name.str)); - // nulling the position, will delete later - if (et->m_dropped) - { - // we have to drop the event - int idx; - et->drop(thd); - idx= get_index_dynamic(&events_array, (gptr) et); - if (idx != -1) - delete_dynamic_element(&events_array, idx); - else - sql_print_error("Something weird happened with events. %d", __LINE__); - } - continue; - } - ++j; - } - if (evex_executing_queue.elements) - //ToDo Andrey : put a lock here - qsort((gptr) dynamic_element(&evex_executing_queue, 0, event_timed**), - evex_executing_queue.elements, - sizeof(event_timed **), - (qsort_cmp) event_timed_compare - ); - - VOID(pthread_mutex_unlock(&LOCK_event_arrays)); - }// while (!thd->killed) - -err: - VOID(pthread_mutex_lock(&LOCK_evex_running)); - evex_is_running= false; - VOID(pthread_mutex_unlock(&LOCK_evex_running)); - - sql_print_information("Event executor stopping"); - // LEX_STRINGs reside in the memory root and will be destroyed with it. - // Hence no need of delete but only freeing of SP - for (i=0; i < events_array.elements; ++i) - { - event_timed *et= dynamic_element(&events_array, i, event_timed*); - et->free_sp(); - } - // TODO Andrey: USE lock here! - delete_dynamic(&evex_executing_queue); - delete_dynamic(&events_array); - - thd->proc_info = "Clearing"; - DBUG_ASSERT(thd->net.buff != 0); - net_end(&thd->net); // destructor will not free it, because we are weird - THD_CHECK_SENTRY(thd); - pthread_mutex_lock(&LOCK_thread_count); - thread_count--; - thread_running--; - THD_CHECK_SENTRY(thd); - delete thd; - pthread_mutex_unlock(&LOCK_thread_count); - - /* - sleeping some time may help not crash the server. sleeping - is done to wait for spawned threads to finish. - - TODO: A better will be with a conditional variable - */ - { - uint tries= 0; - while (tries++ < 5) - { - VOID(pthread_mutex_lock(&LOCK_workers_count)); - if (!workers_count) - { - VOID(pthread_mutex_unlock(&LOCK_workers_count)); - break; - } - VOID(pthread_mutex_unlock(&LOCK_workers_count)); - DBUG_PRINT("info", ("Sleep %d", tries)); - my_sleep(1000000 * tries);// 1s - } - DBUG_PRINT("info", ("Maybe now it is ok to kill the thread and evex MRoot")); - } - -err_no_thd: - VOID(pthread_mutex_lock(&LOCK_evex_running)); - evex_is_running= false; - VOID(pthread_mutex_unlock(&LOCK_evex_running)); - - free_root(&evex_mem_root, MYF(0)); - sql_print_information("Event executor stopped"); - - shutdown_events(); - - my_thread_end(); - pthread_exit(0); - DBUG_RETURN(0); // Can't return anything here -} - - -pthread_handler_t event_executor_worker(void *event_void) -{ - THD *thd; /* needs to be first for thread_stack */ - List empty_item_list; - event_timed *event = (event_timed *) event_void; - MEM_ROOT mem_root; - ulong save_options; - - - DBUG_ENTER("event_executor_worker"); - VOID(pthread_mutex_lock(&LOCK_workers_count)); - ++workers_count; - VOID(pthread_mutex_unlock(&LOCK_workers_count)); - - init_alloc_root(&mem_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC); - - //we pass this empty list as parameter to the SP_HEAD of the event - empty_item_list.empty(); - - my_thread_init(); - - //TODO Andrey: Check for NULL - if (!(thd = new THD)) // note that contructor of THD uses DBUG_ ! - { - sql_print_error("Cannot create a THD structure in worker thread"); - goto err_no_thd; - } - thd->thread_stack = (char*)&thd; // remember where our stack is - thd->mem_root= &mem_root; -// pthread_detach_this_thread(); - pthread_detach(pthread_self()); - if (init_event_thread(thd)) - goto err; - - thd->init_for_queries(); - save_options= thd->options; - thd->options&= ~OPTION_BIN_LOG; - - VOID(pthread_mutex_lock(&LOCK_thread_count)); - threads.append(thd); - thread_count++; - thread_running++; - VOID(pthread_mutex_unlock(&LOCK_thread_count)); - - //thd->security_ctx->priv_host is char[MAX_HOSTNAME] - - strxnmov(thd->security_ctx->priv_host, sizeof(thd->security_ctx->priv_host), - event->m_definer_host.str, NullS); - - thd->security_ctx->priv_user= event->m_definer_user.str; - - thd->db= event->m_db.str; - if (!check_global_access(thd, EVENT_ACL)) - { - char exec_time[200]; - int ret; - my_TIME_to_str(&event->m_execute_at, exec_time); - DBUG_PRINT("info", (" EVEX EXECUTING event for event %s.%s [EXPR:%d][EXECUTE_AT:%s]", event->m_db.str, event->m_name.str,(int) event->m_expr, exec_time)); -// sql_print_information(" EVEX EXECUTING event for event %s.%s [EXPR:%d][EXECUTE_AT:%s]", event->m_db.str, event->m_name.str,(int) event->m_expr, exec_time); - ret= event->execute(thd); -// sql_print_information(" EVEX EXECUTED event for event %s.%s [EXPR:%d][EXECUTE_AT:%s]. RetCode=%d", event->m_db.str, event->m_name.str,(int) event->m_expr, exec_time, ret); - DBUG_PRINT("info", (" EVEX EXECUTED event for event %s.%s [EXPR:%d][EXECUTE_AT:%s]", event->m_db.str, event->m_name.str,(int) event->m_expr, exec_time)); - } - thd->db= 0; - //reenable (is it needed?) - thd->options= save_options; -err: - VOID(pthread_mutex_lock(&LOCK_thread_count)); - thread_count--; - thread_running--; - /* - Some extra safety, which should not been needed (normally, event deletion - should already have done these assignments (each event which sets these - variables is supposed to set them to 0 before terminating)). - */ - //thd->query= thd->db= thd->catalog= 0; - //thd->query_length= thd->db_length= 0; - VOID(pthread_mutex_unlock(&LOCK_thread_count)); - - //thd->temporary_tables = 0; // remove tempation from destructor to close them - thd->proc_info = "Clearing"; - DBUG_ASSERT(thd->net.buff != 0); - net_end(&thd->net); // destructor will not free it, because we are weird - THD_CHECK_SENTRY(thd); - - VOID(pthread_mutex_lock(&LOCK_thread_count)); - THD_CHECK_SENTRY(thd); - delete thd; - VOID(pthread_mutex_unlock(&LOCK_thread_count)); - -err_no_thd: - - free_root(&mem_root, MYF(0)); -// sql_print_information(" Worker thread exiting"); - - VOID(pthread_mutex_lock(&LOCK_workers_count)); - --workers_count; - VOID(pthread_mutex_unlock(&LOCK_workers_count)); - my_thread_end(); - pthread_exit(0); - DBUG_RETURN(0); // Can't return anything here -} diff --git a/sql/event.h b/sql/event.h index 5ba96e401ce..cab5d9bf257 100644 --- a/sql/event.h +++ b/sql/event.h @@ -1,27 +1,58 @@ -/* -*- C++ -*- */ +/* Copyright (C) 2000-2003 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 */ + #ifndef _EVENT_H_ #define _EVENT_H_ #include "sp_head.h" +#include "sp.h" extern ulong opt_event_executor; -#define EVEX_OK 0 -#define EVEX_KEY_NOT_FOUND -1 -#define EVEX_OPEN_TABLE_FAILED -2 -#define EVEX_WRITE_ROW_FAILED -3 -#define EVEX_DELETE_ROW_FAILED -4 -#define EVEX_GET_FIELD_FAILED -5 -#define EVEX_PARSE_ERROR -6 -#define EVEX_INTERNAL_ERROR -7 -#define EVEX_NO_DB_ERROR -8 -#define EVEX_GENERAL_ERROR -9 -#define EVEX_BAD_PARAMS -10 -#define EVEX_NOT_RUNNING -11 +#define EVEX_OK SP_OK +#define EVEX_KEY_NOT_FOUND SP_KEY_NOT_FOUND +#define EVEX_OPEN_TABLE_FAILED SP_OPEN_TABLE_FAILED +#define EVEX_WRITE_ROW_FAILED SP_WRITE_ROW_FAILED +#define EVEX_DELETE_ROW_FAILED SP_DELETE_ROW_FAILED +#define EVEX_GET_FIELD_FAILED SP_GET_FIELD_FAILED +#define EVEX_PARSE_ERROR SP_PARSE_ERROR +#define EVEX_INTERNAL_ERROR SP_INTERNAL_ERROR +#define EVEX_NO_DB_ERROR SP_NO_DB_ERROR +#define EVEX_GENERAL_ERROR -20 +#define EVEX_BAD_IDENTIFIER SP_BAD_IDENTIFIER +#define EVEX_BODY_TOO_LONG SP_BODY_TOO_LONG +#define EVEX_BAD_PARAMS -21 +#define EVEX_NOT_RUNNING -22 -#define EVENT_EXEC_NO_MORE (1L << 0) -#define EVENT_NOT_USED (1L << 1) +#define EVENT_EXEC_NO_MORE (1L << 0) +#define EVENT_NOT_USED (1L << 1) +#define SP_OK 0 +#define SP_KEY_NOT_FOUND -1 +#define SP_OPEN_TABLE_FAILED -2 +#define SP_WRITE_ROW_FAILED -3 +#define SP_DELETE_ROW_FAILED -4 +#define SP_GET_FIELD_FAILED -5 +#define SP_PARSE_ERROR -6 +#define SP_INTERNAL_ERROR -7 +#define SP_NO_DB_ERROR -8 +#define SP_BAD_IDENTIFIER -9 +#define SP_BODY_TOO_LONG -10 + +extern ulong opt_event_executor; enum enum_event_on_completion { @@ -40,11 +71,12 @@ class event_timed { event_timed(const event_timed &); /* Prevent use of these */ void operator=(event_timed &); + my_bool running; + pthread_mutex_t LOCK_running; public: LEX_STRING m_db; LEX_STRING m_name; - LEX_STRING m_qname; // db.name LEX_STRING m_body; LEX_STRING m_definer_user; @@ -64,9 +96,6 @@ public: enum enum_event_status m_status; sp_head *m_sphead; - - - uint m_old_cmq; // Old CLIENT_MULTI_QUERIES value const uchar *m_body_begin; bool m_dropped; @@ -75,15 +104,19 @@ public: bool m_last_executed_changed; bool m_status_changed; - event_timed():m_expr(0), m_created(0), m_modified(0), + event_timed():running(0), m_expr(0), m_created(0), m_modified(0), m_on_completion(MYSQL_EVENT_ON_COMPLETION_DROP), m_status(MYSQL_EVENT_ENABLED), m_sphead(0), m_dropped(false), m_free_sphead_on_delete(true), m_flags(0), m_last_executed_changed(false), m_status_changed(false) - { init(); } + { + pthread_mutex_init(&LOCK_running, MY_MUTEX_INIT_FAST); + init(); + } ~event_timed() { + pthread_mutex_destroy(&LOCK_running); if (m_free_sphead_on_delete) free_sp(); } @@ -115,12 +148,6 @@ public: void init_comment(THD *thd, LEX_STRING *comment); - void - set_on_completion_drop(bool drop); - - void - set_event_status(bool enabled); - int load_from_row(MEM_ROOT *mem_root, TABLE *table); @@ -140,10 +167,10 @@ public: get_show_create_event(THD *thd, uint *length); int - execute(THD *thd, MEM_ROOT *mem_root); + execute(THD *thd, MEM_ROOT *mem_root= NULL); int - compile(THD *thd, MEM_ROOT *mem_root); + compile(THD *thd, MEM_ROOT *mem_root= NULL); void free_sp() { @@ -172,29 +199,21 @@ init_events(); void shutdown_events(); -/* -typedef struct st_event_item { - my_time_t execute_at; - sp_head *proc; - char *definer_user; - char *definer_host; -} EVENT_ITEM; -*/ + +// auxiliary +int +event_timed_compare(event_timed **a, event_timed **b); /* CREATE TABLE `event` ( - `db` varchar(64) character set latin1 collate latin1_bin NOT NULL default '', - `name` varchar(64) NOT NULL default '', - `body` blob NOT NULL, - `definer` varchar(77) character set latin1 collate latin1_bin NOT NULL default '', + `db` varchar(64) character set utf8 collate utf8_bin NOT NULL default '', + `name` varchar(64) character set utf8 collate utf8_bin NOT NULL default '', + `body` longblob NOT NULL, + `definer` varchar(77) character set utf8 collate utf8_bin NOT NULL default '', `execute_at` datetime default NULL, `transient_expression` int(11) default NULL, - `interval_type` 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') DEFAULT NULL, + `interval_type` 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') default NULL, `created` timestamp NOT NULL default '0000-00-00 00:00:00', `modified` timestamp NOT NULL default '0000-00-00 00:00:00', `last_executed` datetime default NULL, @@ -202,9 +221,9 @@ CREATE TABLE `event` ( `ends` datetime default NULL, `status` enum('ENABLED','DISABLED') NOT NULL default 'ENABLED', `on_completion` enum('DROP','PRESERVE') NOT NULL default 'DROP', - `comment` varchar(64) character set latin1 collate latin1_bin NOT NULL default '', + `comment` varchar(64) character set utf8 collate utf8_bin NOT NULL default '', PRIMARY KEY (`db`,`name`) -) ENGINE=MyISAM DEFAULT CHARSET=latin1 +) ENGINE=MyISAM DEFAULT CHARSET=utf8 */ #endif /* _EVENT_H_ */ diff --git a/sql/event_executor.cc b/sql/event_executor.cc new file mode 100644 index 00000000000..b3029d66f07 --- /dev/null +++ b/sql/event_executor.cc @@ -0,0 +1,533 @@ +/* Copyright (C) 2000-2003 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 */ + +#include "mysql_priv.h" +#include "event.h" +#include "event_priv.h" +#include "sp.h" + +#define DBUG_FAULTY_THR2 + +static uint workers_count; + + +pthread_mutex_t LOCK_event_arrays, + LOCK_workers_count, + LOCK_evex_running; + + +bool evex_is_running= false; + +ulong opt_event_executor; +my_bool event_executor_running_global_var= false; + +extern ulong thread_created; + + +static int +evex_load_events_from_db(THD *thd); + + + +/* + TODO Andrey: Check for command line option whether to start + the main thread or not. +*/ + +pthread_handler_t event_executor_worker(void *arg); +pthread_handler_t event_executor_main(void *arg); + +int +init_events() +{ + pthread_t th; + + DBUG_ENTER("init_events"); + + DBUG_PRINT("info",("Starting events main thread")); + + pthread_mutex_init(&LOCK_event_arrays, MY_MUTEX_INIT_FAST); + pthread_mutex_init(&LOCK_workers_count, MY_MUTEX_INIT_FAST); + pthread_mutex_init(&LOCK_evex_running, MY_MUTEX_INIT_FAST); + + VOID(pthread_mutex_lock(&LOCK_evex_running)); + evex_is_running= false; + event_executor_running_global_var= false; + VOID(pthread_mutex_unlock(&LOCK_evex_running)); + +#ifndef DBUG_FAULTY_THR + //TODO Andrey: Change the error code returned! + if (pthread_create(&th, NULL, event_executor_main, (void*)NULL)) + DBUG_RETURN(ER_SLAVE_THREAD); +#else + event_executor_main(NULL); +#endif + + DBUG_RETURN(0); +} + + +void +shutdown_events() +{ + DBUG_ENTER("shutdown_events"); + + VOID(pthread_mutex_lock(&LOCK_evex_running)); + VOID(pthread_mutex_unlock(&LOCK_evex_running)); + pthread_mutex_destroy(&LOCK_event_arrays); + pthread_mutex_destroy(&LOCK_workers_count); + pthread_mutex_destroy(&LOCK_evex_running); + + DBUG_VOID_RETURN; +} + + +static int +init_event_thread(THD* thd) +{ + DBUG_ENTER("init_event_thread"); + thd->client_capabilities= 0; + thd->security_ctx->skip_grants(); + my_net_init(&thd->net, 0); + thd->net.read_timeout = slave_net_timeout; + thd->slave_thread= 0; + thd->options= OPTION_AUTO_IS_NULL; + thd->client_capabilities= CLIENT_LOCAL_FILES; + thd->real_id=pthread_self(); + VOID(pthread_mutex_lock(&LOCK_thread_count)); + thd->thread_id= thread_id++; + VOID(pthread_mutex_unlock(&LOCK_thread_count)); + + if (init_thr_lock() || thd->store_globals()) + { + thd->cleanup(); + delete thd; + DBUG_RETURN(-1); + } + +#if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__) + sigset_t set; + VOID(sigemptyset(&set)); // Get mask in use + VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals)); +#endif + + thd->proc_info= "Initialized"; + thd->version= refresh_version; + thd->set_time(); + DBUG_RETURN(0); +} + + +pthread_handler_t event_executor_main(void *arg) +{ + THD *thd; /* needs to be first for thread_stack */ + ulonglong iter_num= 0; + uint i=0, j=0; + + DBUG_ENTER("event_executor_main"); + DBUG_PRINT("event_executor_main", ("EVEX thread started")); + + VOID(pthread_mutex_lock(&LOCK_evex_running)); + evex_is_running= true; + event_executor_running_global_var= opt_event_executor; + VOID(pthread_mutex_unlock(&LOCK_evex_running)); + + // init memory root + init_alloc_root(&evex_mem_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC); + + // needs to call my_thread_init(), otherwise we get a coredump in DBUG_ stuff + my_thread_init(); + + //TODO Andrey: Check for NULL + if (!(thd = new THD)) // note that contructor of THD uses DBUG_ ! + { + sql_print_error("Cannot create THD for event_executor_main"); + goto err_no_thd; + } + thd->thread_stack = (char*)&thd; // remember where our stack is + + pthread_detach_this_thread(); + + if (init_event_thread(thd)) + goto err; + + // make this thread invisible it has no vio -> show processlist won't see + thd->system_thread= 0; + + VOID(pthread_mutex_lock(&LOCK_thread_count)); + threads.append(thd); + thread_count++; + thread_running++; + VOID(pthread_mutex_unlock(&LOCK_thread_count)); + + DBUG_PRINT("EVEX main thread", ("Initing events_array")); + + VOID(pthread_mutex_lock(&LOCK_event_arrays)); + /* + my_malloc is used as underlying allocator which does not use a mem_root + thus data should be freed at later stage. + */ + VOID(my_init_dynamic_array(&events_array, sizeof(event_timed), 50, 100)); + VOID(my_init_dynamic_array(&evex_executing_queue, sizeof(event_timed *), 50, 100)); + VOID(pthread_mutex_unlock(&LOCK_event_arrays)); + + if (evex_load_events_from_db(thd)) + goto err; + + THD_CHECK_SENTRY(thd); + /* Read queries from the IO/THREAD until this thread is killed */ + while (!thd->killed) + { + TIME time_now; + my_time_t now; + my_ulonglong cnt; + + DBUG_PRINT("info", ("EVEX External Loop %d", ++cnt)); +// sql_print_information("[EVEX] External Loop!"); + thd->proc_info = "Sleeping"; + my_sleep(1000000);// sleep 1s + if (!event_executor_running_global_var) + continue; + time(&now); + my_tz_UTC->gmt_sec_to_TIME(&time_now, now); + + + VOID(pthread_mutex_lock(&LOCK_event_arrays)); + for (i= 0; (i < evex_executing_queue.elements) && !thd->killed; ++i) + { + event_timed **p_et=dynamic_element(&evex_executing_queue,i,event_timed**); + event_timed *et= *p_et; +// sql_print_information("[EVEX] External Loop 2!"); + + if (!event_executor_running_global_var) + break;// soon we will do only continue (see the code a bit above) + + thd->proc_info = "Iterating"; + THD_CHECK_SENTRY(thd); + /* + if this is the first event which is after time_now then no + more need to iterate over more elements since the array is sorted. + */ + if (et->m_execute_at.year && + my_time_compare(&time_now, &et->m_execute_at) == -1) + break; + + if (et->m_status == MYSQL_EVENT_ENABLED && + !check_access(thd, EVENT_ACL, et->m_db.str, 0, 0, 0, + is_schema_db(et->m_db.str))) + { + pthread_t th; + + DBUG_PRINT("info", (" Spawning a thread %d", ++iter_num)); + thd->proc_info = "Starting new thread"; + sql_print_information(" Spawning a thread %d", ++iter_num); +#ifndef DBUG_FAULTY_THR + if (pthread_create(&th, NULL, event_executor_worker, (void*)et)) + { + sql_print_error("Problem while trying to create a thread"); + VOID(pthread_mutex_unlock(&LOCK_event_arrays)); + goto err; // for now finish execution of the Executor + } +#else + event_executor_worker((void *) et); +#endif + et->mark_last_executed(); + thd->proc_info = "Computing next time"; + et->compute_next_execution_time(); + et->update_fields(thd); + if ((et->m_execute_at.year && !et->m_expr) + || TIME_to_ulonglong_datetime(&et->m_execute_at) == 0L) + et->m_flags |= EVENT_EXEC_NO_MORE; + } + } + /* + Let's remove elements which won't be executed any more + The number is "i" and it is <= up to evex_executing_queue.elements + */ + j= 0; + while (j < i && j < evex_executing_queue.elements) + { + event_timed **p_et= dynamic_element(&evex_executing_queue, j, event_timed**); + event_timed *et= *p_et; + if (et->m_flags & EVENT_EXEC_NO_MORE || et->m_status == MYSQL_EVENT_DISABLED) + { + delete_dynamic_element(&evex_executing_queue, j); + DBUG_PRINT("", ("DELETING FROM EXECUTION QUEUE [%s.%s]",et->m_db.str, et->m_name.str)); + // nulling the position, will delete later + if (et->m_dropped) + { + // we have to drop the event + int idx; + et->drop(thd); + idx= get_index_dynamic(&events_array, (gptr) et); + DBUG_ASSERT(idx != -1); + delete_dynamic_element(&events_array, idx); + } + continue; + } + ++j; + } + if (evex_executing_queue.elements) + //ToDo Andrey : put a lock here + qsort((gptr) dynamic_element(&evex_executing_queue, 0, event_timed**), + evex_executing_queue.elements, + sizeof(event_timed **), + (qsort_cmp) event_timed_compare + ); + + VOID(pthread_mutex_unlock(&LOCK_event_arrays)); + }// while (!thd->killed) + +err: + VOID(pthread_mutex_lock(&LOCK_evex_running)); + evex_is_running= false; + VOID(pthread_mutex_unlock(&LOCK_evex_running)); + + sql_print_information("Event executor stopping"); + // LEX_STRINGs reside in the memory root and will be destroyed with it. + // Hence no need of delete but only freeing of SP + for (i=0; i < events_array.elements; ++i) + { + event_timed *et= dynamic_element(&events_array, i, event_timed*); + et->free_sp(); + } + // TODO Andrey: USE lock here! + delete_dynamic(&evex_executing_queue); + delete_dynamic(&events_array); + + thd->proc_info = "Clearing"; + DBUG_ASSERT(thd->net.buff != 0); + net_end(&thd->net); // destructor will not free it, because we are weird + THD_CHECK_SENTRY(thd); + pthread_mutex_lock(&LOCK_thread_count); + thread_count--; + thread_running--; + THD_CHECK_SENTRY(thd); + delete thd; + pthread_mutex_unlock(&LOCK_thread_count); + + /* + sleeping some time may help not crash the server. sleeping + is done to wait for spawned threads to finish. + + TODO: A better will be with a conditional variable + */ + { + uint tries= 0; + while (tries++ < 5) + { + VOID(pthread_mutex_lock(&LOCK_workers_count)); + if (!workers_count) + { + VOID(pthread_mutex_unlock(&LOCK_workers_count)); + break; + } + VOID(pthread_mutex_unlock(&LOCK_workers_count)); + DBUG_PRINT("info", ("Sleep %d", tries)); + my_sleep(1000000 * tries);// 1s + } + DBUG_PRINT("info", ("Maybe now it is ok to kill the thread and evex MRoot")); + } + +err_no_thd: + VOID(pthread_mutex_lock(&LOCK_evex_running)); + evex_is_running= false; + VOID(pthread_mutex_unlock(&LOCK_evex_running)); + + free_root(&evex_mem_root, MYF(0)); + sql_print_information("Event executor stopped"); + + shutdown_events(); + + my_thread_end(); + pthread_exit(0); + DBUG_RETURN(0); // Can't return anything here +} + + +pthread_handler_t event_executor_worker(void *event_void) +{ + THD *thd; /* needs to be first for thread_stack */ + List empty_item_list; + event_timed *event = (event_timed *) event_void; + MEM_ROOT mem_root; + + DBUG_ENTER("event_executor_worker"); + VOID(pthread_mutex_lock(&LOCK_workers_count)); + ++workers_count; + VOID(pthread_mutex_unlock(&LOCK_workers_count)); + + init_alloc_root(&mem_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC); + + //we pass this empty list as parameter to the SP_HEAD of the event + empty_item_list.empty(); + + my_thread_init(); + + if (!(thd = new THD)) // note that contructor of THD uses DBUG_ ! + { + sql_print_error("Cannot create a THD structure in worker thread"); + goto err_no_thd; + } + thd->thread_stack = (char*)&thd; // remember where our stack is + thd->mem_root= &mem_root; + pthread_detach(pthread_self()); + if (init_event_thread(thd)) + goto err; + + thd->init_for_queries(); + + // make this thread visible it has no vio -> show processlist needs this flag + thd->system_thread= 0; + + VOID(pthread_mutex_lock(&LOCK_thread_count)); + threads.append(thd); + thread_count++; + thread_running++; + VOID(pthread_mutex_unlock(&LOCK_thread_count)); + + // thd->security_ctx->priv_host is char[MAX_HOSTNAME] + + strxnmov(thd->security_ctx->priv_host, sizeof(thd->security_ctx->priv_host), + event->m_definer_host.str, NullS); + + thd->security_ctx->priv_user= event->m_definer_user.str; + + thd->db= event->m_db.str; + { + char exec_time[200]; + int ret; + my_TIME_to_str(&event->m_execute_at, exec_time); + DBUG_PRINT("info", (" EVEX EXECUTING event for event %s.%s [EXPR:%d][EXECUTE_AT:%s]", event->m_db.str, event->m_name.str,(int) event->m_expr, exec_time)); + sql_print_information(" EVEX EXECUTING event for event %s.%s [EXPR:%d][EXECUTE_AT:%s]", event->m_db.str, event->m_name.str,(int) event->m_expr, exec_time); + ret= event->execute(thd, &mem_root); + sql_print_information(" EVEX EXECUTED event for event %s.%s [EXPR:%d][EXECUTE_AT:%s]. RetCode=%d", event->m_db.str, event->m_name.str,(int) event->m_expr, exec_time, ret); + DBUG_PRINT("info", (" EVEX EXECUTED event for event %s.%s [EXPR:%d][EXECUTE_AT:%s]", event->m_db.str, event->m_name.str,(int) event->m_expr, exec_time)); + } + thd->db= 0; + +err: + VOID(pthread_mutex_lock(&LOCK_thread_count)); + thread_count--; + thread_running--; + /* + Some extra safety, which should not been needed (normally, event deletion + should already have done these assignments (each event which sets these + variables is supposed to set them to 0 before terminating)). + */ + VOID(pthread_mutex_unlock(&LOCK_thread_count)); + + thd->proc_info = "Clearing"; + DBUG_ASSERT(thd->net.buff != 0); + net_end(&thd->net); // destructor will not free it, because we are weird + THD_CHECK_SENTRY(thd); + + VOID(pthread_mutex_lock(&LOCK_thread_count)); + THD_CHECK_SENTRY(thd); + delete thd; + VOID(pthread_mutex_unlock(&LOCK_thread_count)); + +err_no_thd: + + free_root(&mem_root, MYF(0)); +// sql_print_information(" Worker thread exiting"); + + VOID(pthread_mutex_lock(&LOCK_workers_count)); + --workers_count; + VOID(pthread_mutex_unlock(&LOCK_workers_count)); + +#ifndef DBUG_FAULTY_THR + my_thread_end(); + pthread_exit(0); +#endif + DBUG_RETURN(0); // Can't return anything here +} + + +static int +evex_load_events_from_db(THD *thd) +{ + TABLE *table; + READ_RECORD read_record_info; + MYSQL_LOCK *lock; + Open_tables_state open_tables_state_backup; + int ret= -1; + + DBUG_ENTER("evex_load_events_from_db"); + + if (!(table= open_proc_type_table_for_read(thd, &open_tables_state_backup, + "event", &mysql_event_table_exists))) + DBUG_RETURN(SP_OPEN_TABLE_FAILED); + + VOID(pthread_mutex_lock(&LOCK_event_arrays)); + + init_read_record(&read_record_info, thd, table ,NULL,1,0); + while (!(read_record_info.read_record(&read_record_info))) + { + event_timed *et, *et_copy; + if (!(et= new event_timed())) + { + DBUG_PRINT("evex_load_events_from_db", ("Out of memory")); + ret= -1; + goto end; + } + DBUG_PRINT("evex_load_events_from_db", ("Loading event from row.")); + + if (et->load_from_row(&evex_mem_root, table)) + //error loading! + continue; + + DBUG_PRINT("evex_load_events_from_db", + ("Event %s loaded from row. Time to compile", et->m_name.str)); + + if (et->compile(thd, &evex_mem_root)) + //problem during compile + continue; + // let's find when to be executed + et->compute_next_execution_time(); + + DBUG_PRINT("evex_load_events_from_db", + ("Adding %s to the executor list.", et->m_name.str)); + VOID(push_dynamic(&events_array,(gptr) et)); + // we always add at the end so the number of elements - 1 is the place + // in the buffer + et_copy= dynamic_element(&events_array, events_array.elements - 1, + event_timed*); + VOID(push_dynamic(&evex_executing_queue,(gptr) &et_copy)); + et->m_free_sphead_on_delete= false; + DBUG_PRINT("info", ("")); + delete et; + } + end_read_record(&read_record_info); + + qsort((gptr) dynamic_element(&evex_executing_queue, 0, event_timed**), + evex_executing_queue.elements, + sizeof(event_timed **), + (qsort_cmp) event_timed_compare + ); + VOID(pthread_mutex_unlock(&LOCK_event_arrays)); + + thd->version--; // Force close to free memory + ret= 0; + +end: + close_thread_tables(thd); + thd->restore_backup_open_tables_state(&open_tables_state_backup); + + DBUG_PRINT("evex_load_events_from_db", + ("Events loaded from DB. Status code %d", ret)); + DBUG_RETURN(ret); +} diff --git a/sql/event_priv.h b/sql/event_priv.h new file mode 100644 index 00000000000..61fd4517bd5 --- /dev/null +++ b/sql/event_priv.h @@ -0,0 +1,57 @@ +/* Copyright (C) 2000-2003 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 */ + +#ifndef _EVENT_PRIV_H_ +#define _EVENT_PRIV_H_ + +#define EVEX_OPEN_TABLE_FOR_UPDATE() \ + open_proc_type_table_for_update(thd, "event", &mysql_event_table_exists) + + +enum +{ + EVEX_FIELD_DB = 0, + EVEX_FIELD_NAME, + EVEX_FIELD_BODY, + EVEX_FIELD_DEFINER, + EVEX_FIELD_EXECUTE_AT, + EVEX_FIELD_INTERVAL_EXPR, + EVEX_FIELD_TRANSIENT_INTERVAL, + EVEX_FIELD_CREATED, + EVEX_FIELD_MODIFIED, + EVEX_FIELD_LAST_EXECUTED, + EVEX_FIELD_STARTS, + EVEX_FIELD_ENDS, + EVEX_FIELD_STATUS, + EVEX_FIELD_ON_COMPLETION, + EVEX_FIELD_COMMENT, + EVEX_FIELD_COUNT /* a cool trick to count the number of fields :) */ +}; + +extern bool evex_is_running; +extern bool mysql_event_table_exists; +extern DYNAMIC_ARRAY events_array; +extern DYNAMIC_ARRAY evex_executing_queue; +extern MEM_ROOT evex_mem_root; +extern pthread_mutex_t LOCK_event_arrays, + LOCK_workers_count, + LOCK_evex_running; + + +int +my_time_compare(TIME *a, TIME *b); + +#endif /* _EVENT_PRIV_H_ */ diff --git a/sql/event_timed.cc b/sql/event_timed.cc new file mode 100644 index 00000000000..579568cee7d --- /dev/null +++ b/sql/event_timed.cc @@ -0,0 +1,944 @@ +/* Copyright (C) 2000-2003 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 */ + +#include "mysql_priv.h" +#include "event.h" +#include "event_priv.h" +#include "sp.h" + + + +extern int yyparse(void *thd); + +/* + Init all member variables + + SYNOPSIS + event_timed::init() +*/ + +void +event_timed::init() +{ + DBUG_ENTER("event_timed::init"); + + m_db.str= m_name.str= m_body.str= m_comment.str= 0; + m_db.length= m_name.length= m_body.length= m_comment.length= 0; + + set_zero_time(&m_starts, MYSQL_TIMESTAMP_DATETIME); + set_zero_time(&m_ends, MYSQL_TIMESTAMP_DATETIME); + set_zero_time(&m_execute_at, MYSQL_TIMESTAMP_DATETIME); + set_zero_time(&m_last_executed, MYSQL_TIMESTAMP_DATETIME); + + m_definer_user.str= m_definer_host.str= 0; + m_definer_user.length= m_definer_host.length= 0; + + DBUG_VOID_RETURN; +} + + +/* + Set a name of the event + + SYNOPSIS + event_timed::init_name() + thd THD + name the name extracted in the parser +*/ + +void +event_timed::init_name(THD *thd, sp_name *name) +{ + DBUG_ENTER("event_timed::init_name"); + uint n; /* Counter for nul trimming */ + /* During parsing, we must use thd->mem_root */ + MEM_ROOT *root= thd->mem_root; + + /* We have to copy strings to get them into the right memroot */ + if (name) + { + m_db.length= name->m_db.length; + if (name->m_db.length == 0) + m_db.str= NULL; + else + m_db.str= strmake_root(root, name->m_db.str, name->m_db.length); + m_name.length= name->m_name.length; + m_name.str= strmake_root(root, name->m_name.str, name->m_name.length); + + if (name->m_qname.length == 0) + name->init_qname(thd); + } + else if (thd->db) + { + m_db.length= thd->db_length; + m_db.str= strmake_root(root, thd->db, m_db.length); + } + + DBUG_PRINT("m_db", ("len=%d db=%s",m_db.length, m_db.str)); + DBUG_PRINT("m_name", ("len=%d name=%s",m_name.length, m_name.str)); + + DBUG_VOID_RETURN; +} + + +/* + Set body of the event - what should be executed. + + SYNOPSIS + event_timed::init_body() + thd THD + + NOTE + The body is extracted by copying all data between the + start of the body set by another method and the current pointer in Lex. +*/ + +void +event_timed::init_body(THD *thd) +{ + DBUG_ENTER("event_timed::init_body"); + MEM_ROOT *root= thd->mem_root; + + m_body.length= thd->lex->ptr - m_body_begin; + // Trim nuls at the end + while (m_body.length && m_body_begin[m_body.length-1] == '\0') + m_body.length--; + + m_body.str= strmake_root(root, (char *)m_body_begin, m_body.length); + + DBUG_VOID_RETURN; +} + + +/* + Set time for execution for one time events. + + SYNOPSIS + event_timed::init_execute_at() + expr when (datetime) + + RETURNS + 0 - OK + EVEX_PARSE_ERROR - fix_fields failed + EVEX_BAD_PARAMS - datetime is in the past +*/ + +int +event_timed::init_execute_at(THD *thd, Item *expr) +{ + my_bool not_used; + TIME ltime; + my_time_t my_time_tmp; + + TIME time_tmp; + DBUG_ENTER("event_timed::init_execute_at"); + + if (expr->fix_fields(thd, &expr)) + DBUG_RETURN(EVEX_PARSE_ERROR); + + if (expr->val_int() == MYSQL_TIMESTAMP_ERROR) + DBUG_RETURN(EVEX_BAD_PARAMS); + + // let's check whether time is in the past + thd->variables.time_zone->gmt_sec_to_TIME(&time_tmp, + (my_time_t) thd->query_start()); + + if (expr->val_int() < TIME_to_ulonglong_datetime(&time_tmp)) + DBUG_RETURN(EVEX_BAD_PARAMS); + + if ((not_used= expr->get_date(<ime, TIME_NO_ZERO_DATE))) + DBUG_RETURN(EVEX_BAD_PARAMS); + + /* + This may result in a 1970-01-01 date if ltime is > 2037-xx-xx + CONVERT_TZ has similar problem + */ + my_tz_UTC->gmt_sec_to_TIME(<ime, TIME_to_timestamp(thd,<ime, ¬_used)); + + + m_execute_at= ltime; + DBUG_RETURN(0); +} + + +/* + Set time for execution for transient events. + + SYNOPSIS + event_timed::init_interval() + expr how much? + interval what is the interval + + RETURNS + 0 - OK + EVEX_PARSE_ERROR - fix_fields failed + EVEX_BAD_PARAMS - Interval is not positive +*/ + +int +event_timed::init_interval(THD *thd, Item *expr, interval_type interval) +{ + longlong tmp; + DBUG_ENTER("event_timed::init_interval"); + + if (expr->fix_fields(thd, &expr)) + DBUG_RETURN(EVEX_PARSE_ERROR); + + if ((tmp= expr->val_int()) <= 0) + DBUG_RETURN(EVEX_BAD_PARAMS); + + m_expr= tmp; + m_interval= interval; + DBUG_RETURN(0); +} + + +/* + Set activation time. + + SYNOPSIS + event_timed::init_starts() + expr how much? + interval what is the interval + + NOTES + Note that activation time is not execution time. + EVERY 5 MINUTE STARTS "2004-12-12 10:00:00" means that + the event will be executed every 5 minutes but this will + start at the date shown above. Expressions are possible : + DATE_ADD(NOW(), INTERVAL 1 DAY) -- start tommorow at + same time. + + RETURNS + 0 - OK + EVEX_PARSE_ERROR - fix_fields failed +*/ + +int +event_timed::init_starts(THD *thd, Item *starts) +{ + my_bool not_used; + TIME ltime; + my_time_t my_time_tmp; + + DBUG_ENTER("event_timed::init_starts"); + + if (starts->fix_fields(thd, &starts)) + DBUG_RETURN(EVEX_PARSE_ERROR); + + if (starts->val_int() == MYSQL_TIMESTAMP_ERROR) + DBUG_RETURN(EVEX_BAD_PARAMS); + + if ((not_used= starts->get_date(<ime, TIME_NO_ZERO_DATE))) + DBUG_RETURN(EVEX_BAD_PARAMS); + + /* + This may result in a 1970-01-01 date if ltime is > 2037-xx-xx + CONVERT_TZ has similar problem + */ + my_tz_UTC->gmt_sec_to_TIME(<ime, TIME_to_timestamp(thd,<ime, ¬_used)); + + m_starts= ltime; + DBUG_RETURN(0); +} + + +/* + Set deactivation time. + + SYNOPSIS + event_timed::init_ends() + thd THD + ends when? + + NOTES + Note that activation time is not execution time. + EVERY 5 MINUTE ENDS "2004-12-12 10:00:00" means that + the event will be executed every 5 minutes but this will + end at the date shown above. Expressions are possible : + DATE_ADD(NOW(), INTERVAL 1 DAY) -- end tommorow at + same time. + + RETURNS + 0 - OK + EVEX_PARSE_ERROR - fix_fields failed + EVEX_BAD_PARAMS - ENDS before STARTS +*/ + +int +event_timed::init_ends(THD *thd, Item *ends) +{ + TIME ltime; + my_time_t my_time_tmp; + my_bool not_used; + + DBUG_ENTER("event_timed::init_ends"); + + if (ends->fix_fields(thd, &ends)) + DBUG_RETURN(EVEX_PARSE_ERROR); + + // the field was already fixed in init_ends + if ((not_used= ends->get_date(<ime, TIME_NO_ZERO_DATE))) + DBUG_RETURN(EVEX_BAD_PARAMS); + + /* + This may result in a 1970-01-01 date if ltime is > 2037-xx-xx + CONVERT_TZ has similar problem + */ + my_tz_UTC->gmt_sec_to_TIME(<ime, TIME_to_timestamp(thd, <ime, ¬_used)); + + if (m_starts.year && my_time_compare(&m_starts, <ime) != -1) + DBUG_RETURN(EVEX_BAD_PARAMS); + + m_ends= ltime; + DBUG_RETURN(0); +} + + +/* + Sets comment. + + SYNOPSIS + event_timed::init_comment() + thd THD - used for memory allocation + comment the string. +*/ + +void +event_timed::init_comment(THD *thd, LEX_STRING *comment) +{ + DBUG_ENTER("event_timed::init_comment"); + + MEM_ROOT *root= thd->mem_root; + m_comment.length= comment->length; + m_comment.str= strmake_root(root, comment->str, comment->length); + DBUG_PRINT("m_comment", ("len=%d",m_comment.length)); + + DBUG_VOID_RETURN; +} + + +/* + Inits definer (m_definer_user and m_definer_host) during + parsing. + + SYNOPSIS + event_timed::init_definer() +*/ + +int +event_timed::init_definer(THD *thd) +{ + DBUG_ENTER("event_timed::init_definer"); + + m_definer_user.str= strdup_root(thd->mem_root, thd->security_ctx->priv_user); + m_definer_user.length= strlen(thd->security_ctx->priv_user); + + m_definer_host.str= strdup_root(thd->mem_root, thd->security_ctx->priv_host); + m_definer_host.length= strlen(thd->security_ctx->priv_host); + + DBUG_RETURN(0); +} + + +/* + Loads an event from a row from mysql.event + + SYNOPSIS + event_timed::load_from_row() +*/ + +int +event_timed::load_from_row(MEM_ROOT *mem_root, TABLE *table) +{ + longlong created; + longlong modified; + char *ptr; + event_timed *et; + uint len; + bool res1, res2; + + DBUG_ENTER("event_timed::load_from_row"); + + if (!table) + goto error; + + et= this; + + if (table->s->fields != EVEX_FIELD_COUNT) + goto error; + + if ((et->m_db.str= get_field(mem_root, + table->field[EVEX_FIELD_DB])) == NULL) + goto error; + + et->m_db.length= strlen(et->m_db.str); + + if ((et->m_name.str= get_field(mem_root, + table->field[EVEX_FIELD_NAME])) == NULL) + goto error; + + et->m_name.length= strlen(et->m_name.str); + + if ((et->m_body.str= get_field(mem_root, + table->field[EVEX_FIELD_BODY])) == NULL) + goto error; + + et->m_body.length= strlen(et->m_body.str); + + if ((et->m_definer.str= get_field(mem_root, + table->field[EVEX_FIELD_DEFINER])) == NullS) + goto error; + et->m_definer.length= strlen(et->m_definer.str); + + ptr= strchr(et->m_definer.str, '@'); + + if (! ptr) + ptr= et->m_definer.str; // Weird, isn't it? + + len= ptr - et->m_definer.str; + + et->m_definer_user.str= strmake_root(mem_root, et->m_definer.str, len); + et->m_definer_user.length= len; + len= et->m_definer.length - len - 1; //1 is because of @ + et->m_definer_host.str= strmake_root(mem_root, ptr + 1, len);//1: because of @ + et->m_definer_host.length= len; + + + res1= table->field[EVEX_FIELD_STARTS]-> + get_date(&et->m_starts, TIME_NO_ZERO_DATE); + + res2= table->field[EVEX_FIELD_ENDS]-> + get_date(&et->m_ends, TIME_NO_ZERO_DATE); + + et->m_expr= table->field[EVEX_FIELD_INTERVAL_EXPR]->val_int(); + + /* + If res1 and res2 are true then both fields are empty. + Hence if EVEX_FIELD_EXECUTE_AT is empty there is an error. + */ + if (res1 && res2 && !et->m_expr && table->field[EVEX_FIELD_EXECUTE_AT]-> + get_date(&et->m_execute_at, TIME_NO_ZERO_DATE)) + goto error; + + /* + In DB the values start from 1 but enum interval_type starts + from 0 + */ + et->m_interval= (interval_type) + ((ulonglong) table->field[EVEX_FIELD_TRANSIENT_INTERVAL]->val_int() - 1); + + et->m_modified= table->field[EVEX_FIELD_CREATED]->val_int(); + et->m_created= table->field[EVEX_FIELD_MODIFIED]->val_int(); + + /* + ToDo Andrey : Ask PeterG & Serg what to do in this case. + Whether on load last_executed_at should be loaded + or it must be 0ed. If last_executed_at is loaded + then an event can be scheduled for execution + instantly. Let's say an event has to be executed + every 15 mins. The server has been stopped for + more than this time and then started. If L_E_AT + is loaded from DB, execution at L_E_AT+15min + will be scheduled. However this time is in the past. + Hence immediate execution. Due to patch of + ::mark_last_executed() m_last_executed gets time_now + and not m_execute_at. If not like this a big + queue can be scheduled for times which are still in + the past (2, 3 and more executions which will be + consequent). + */ + set_zero_time(&m_last_executed, MYSQL_TIMESTAMP_DATETIME); +#ifdef ANDREY_0 + table->field[EVEX_FIELD_LAST_EXECUTED]-> + get_date(&et->m_last_executed, TIME_NO_ZERO_DATE); +#endif + m_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[EVEX_FIELD_STATUS])) == NullS) + goto error; + + DBUG_PRINT("load_from_row", ("Event [%s] is [%s]", et->m_name.str, ptr)); + et->m_status= (ptr[0]=='E'? MYSQL_EVENT_ENABLED: + MYSQL_EVENT_DISABLED); + + // ToDo : Andrey . Find a way not to allocate ptr on event_mem_root + if ((ptr= get_field(mem_root, + table->field[EVEX_FIELD_ON_COMPLETION])) == NullS) + goto error; + + et->m_on_completion= (ptr[0]=='D'? MYSQL_EVENT_ON_COMPLETION_DROP: + MYSQL_EVENT_ON_COMPLETION_PRESERVE); + + et->m_comment.str= get_field(mem_root, table->field[EVEX_FIELD_COMMENT]); + if (et->m_comment.str != NullS) + et->m_comment.length= strlen(et->m_comment.str); + else + et->m_comment.length= 0; + + DBUG_RETURN(0); +error: + DBUG_RETURN(EVEX_GET_FIELD_FAILED); +} + + +bool +event_timed::compute_next_execution_time() +{ + TIME time_now; + my_time_t now; + int tmp; + + DBUG_ENTER("event_timed::compute_next_execution_time"); + + if (m_status == MYSQL_EVENT_DISABLED) + { + DBUG_PRINT("compute_next_execution_time", + ("Event %s is DISABLED", m_name.str)); + goto ret; + } + //if one-time no need to do computation + if (!m_expr) + { + //let's check whether it was executed + if (m_last_executed.year) + { + DBUG_PRINT("compute_next_execution_time", + ("One-time event %s was already executed", m_name.str)); + if (m_on_completion == MYSQL_EVENT_ON_COMPLETION_DROP) + { + DBUG_PRINT("compute_next_execution_time", + ("One-time event will be dropped.")); + m_dropped= true; + } + m_status= MYSQL_EVENT_DISABLED; + m_status_changed= true; + } + goto ret; + } + time(&now); + my_tz_UTC->gmt_sec_to_TIME(&time_now, now); +/* + sql_print_information("[%s.%s]", m_db.str, m_name.str); + sql_print_information("time_now : [%d-%d-%d %d:%d:%d ]", time_now.year, time_now.month, time_now.day, time_now.hour, time_now.minute, time_now.second); + sql_print_information("m_starts : [%d-%d-%d %d:%d:%d ]", m_starts.year, m_starts.month, m_starts.day, m_starts.hour, m_starts.minute, m_starts.second); + sql_print_information("m_ends : [%d-%d-%d %d:%d:%d ]", m_ends.year, m_ends.month, m_ends.day, m_ends.hour, m_ends.minute, m_ends.second); + sql_print_information("m_last_ex: [%d-%d-%d %d:%d:%d ]", m_last_executed.year, m_last_executed.month, m_last_executed.day, m_last_executed.hour, m_last_executed.minute, m_last_executed.second); +*/ + //if time_now is after m_ends don't execute anymore + if (m_ends.year && (tmp= my_time_compare(&m_ends, &time_now)) == -1) + { + // time_now is after m_ends. don't execute anymore + set_zero_time(&m_execute_at, MYSQL_TIMESTAMP_DATETIME); + if (m_on_completion == MYSQL_EVENT_ON_COMPLETION_DROP) + m_dropped= true; + m_status= MYSQL_EVENT_DISABLED; + m_status_changed= true; + + goto ret; + } + + /* + Here time_now is before or equals m_ends if the latter is set. + Let's check whether time_now is before m_starts. + If so schedule for m_starts + */ + if (m_starts.year && (tmp= my_time_compare(&time_now, &m_starts)) < 1) + { + if (tmp == 0 && my_time_compare(&m_starts, &m_last_executed) == 0) + { + /* + time_now = m_starts = m_last_executed + do nothing or we will schedule for second time execution at m_starts. + */ + } + else + { + //m_starts is in the future + //time_now before m_starts. Scheduling for m_starts + m_execute_at= m_starts; + goto ret; + } + } + + if (m_starts.year && m_ends.year) + { + /* + Both m_starts and m_ends are set and time_now is between them (incl.) + If m_last_executed is set then increase with m_expr. The new TIME is + after m_ends set m_execute_at to 0. And check for m_on_completion + If not set then schedule for now. + */ + if (!m_last_executed.year) + m_execute_at= time_now; + else + { + my_time_t last, ll_ends; + + // There was previous execution + last= sec_since_epoch_TIME(&m_last_executed) + m_expr; + ll_ends= sec_since_epoch_TIME(&m_ends); + //now convert back to TIME + //ToDo Andrey: maybe check for error here? + if (ll_ends < last) + { + // Next execution after ends. No more executions + set_zero_time(&m_execute_at, MYSQL_TIMESTAMP_DATETIME); + if (m_on_completion == MYSQL_EVENT_ON_COMPLETION_DROP) + m_dropped= true; + } + else + my_tz_UTC->gmt_sec_to_TIME(&m_execute_at, last); + } + goto ret; + } + else if (!m_starts.year && !m_ends.year) + { + // both m_starts and m_ends are not set, se we schedule for the next + // based on m_last_executed + if (!m_last_executed.year) + //m_last_executed not set. Schedule the event for now + m_execute_at= time_now; + else + //ToDo Andrey: maybe check for error here? + my_tz_UTC->gmt_sec_to_TIME(&m_execute_at, + sec_since_epoch_TIME(&m_last_executed) + m_expr); + goto ret; + } + else + { + //either m_starts or m_ends is set + if (m_starts.year) + { + /* + - m_starts is set. + - m_starts is not in the future according to check made before + Hence schedule for m_starts + m_expr in case m_last_executed + is not set, otherwise to m_last_executed + m_expr + */ + my_time_t last; + + //convert either m_last_executed or m_starts to seconds + if (m_last_executed.year) + last= sec_since_epoch_TIME(&m_last_executed) + m_expr; + else + last= sec_since_epoch_TIME(&m_starts); + + //now convert back to TIME + //ToDo Andrey: maybe check for error here? + my_tz_UTC->gmt_sec_to_TIME(&m_execute_at, last); + } + else + { + /* + - m_ends is set + - m_ends is after time_now or is equal + Hence check for m_last_execute and increment with m_expr. + If m_last_executed is not set then schedule for now + */ + my_time_t last, ll_ends; + + if (!m_last_executed.year) + m_execute_at= time_now; + else + { + last= sec_since_epoch_TIME(&m_last_executed); + ll_ends= sec_since_epoch_TIME(&m_ends); + last+= m_expr; + //now convert back to TIME + //ToDo Andrey: maybe check for error here? + if (ll_ends < last) + { + set_zero_time(&m_execute_at, MYSQL_TIMESTAMP_DATETIME); + if (m_on_completion == MYSQL_EVENT_ON_COMPLETION_DROP) + m_dropped= true; + } + else + my_tz_UTC->gmt_sec_to_TIME(&m_execute_at, last); + } + } + goto ret; + } +ret: + + DBUG_RETURN(false); +} + + +void +event_timed::mark_last_executed() +{ + TIME time_now; + my_time_t now; + + time(&now); + my_tz_UTC->gmt_sec_to_TIME(&time_now, now); + + m_last_executed= time_now; // was m_execute_at +#ifdef ANDREY_0 + m_last_executed= m_execute_at; +#endif + m_last_executed_changed= true; +} + + +bool +event_timed::drop(THD *thd) +{ + + return (bool) evex_drop_event(thd, this, false); +} + + +bool +event_timed::update_fields(THD *thd) +{ + TABLE *table; + int ret= 0; + bool opened; + + DBUG_ENTER("event_timed::update_time_fields"); + + DBUG_PRINT("enter", ("name: %*s", m_name.length, m_name.str)); + + //no need to update if nothing has changed + if (!(m_status_changed || m_last_executed_changed)) + goto done; + + if (!(table= EVEX_OPEN_TABLE_FOR_UPDATE())) + DBUG_RETURN(SP_OPEN_TABLE_FAILED); + + if ((ret= sp_db_find_routine_aux(thd, 0/*notype*/, m_db, m_name, table))) + goto done; + + store_record(table,record[1]); + table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; // Don't update create on row update. + + if (m_last_executed_changed) + { + table->field[EVEX_FIELD_LAST_EXECUTED]->set_notnull(); + table->field[EVEX_FIELD_LAST_EXECUTED]->store_time(&m_last_executed, + MYSQL_TIMESTAMP_DATETIME); + m_last_executed_changed= false; + } + if (m_status_changed) + { + table->field[EVEX_FIELD_STATUS]->set_notnull(); + table->field[EVEX_FIELD_STATUS]->store((longlong)m_status); + m_status_changed= false; + } + + if ((table->file->update_row(table->record[1],table->record[0]))) + ret= EVEX_WRITE_ROW_FAILED; + +done: + close_thread_tables(thd); + + DBUG_RETURN(ret); +} + + +char * +event_timed::get_show_create_event(THD *thd, uint *length) +{ + char *dst, *ret; + uint len, tmp_len; + + len = strlen("CREATE EVENT ") + m_db.length + strlen(".") + m_name.length + + strlen(" ON SCHEDULE ") + strlen("EVERY 5 MINUTE ") +/* + + strlen("ON COMPLETION ") + + (m_on_completion==MYSQL_EVENT_ON_COMPLETION_DROP? + strlen("NOT PRESERVE "):strlen("PRESERVE ")) + + (m_status==MYSQL_EVENT_ENABLED? + strlen("ENABLE "):strlen("DISABLE ")) + + strlen("COMMENT \"") + m_comment.length + strlen("\" ") +*/ + + strlen("DO ") + + + m_body.length + strlen(";"); + + ret= dst= (char*) alloc_root(thd->mem_root, len); + memcpy(dst, "CREATE EVENT ", tmp_len= strlen("CREATE EVENT ")); + dst+= tmp_len; + memcpy(dst, m_db.str, tmp_len=m_db.length); + dst+= tmp_len; + memcpy(dst, ".", tmp_len= strlen(".")); + dst+= tmp_len; + memcpy(dst, m_name.str, tmp_len= m_name.length); + dst+= tmp_len; + memcpy(dst, " ON SCHEDULE ", tmp_len= strlen(" ON SCHEDULE ")); + dst+= tmp_len; + memcpy(dst, "EVERY 5 MINUTE ", tmp_len= strlen("EVERY 5 MINUTE ")); + dst+= tmp_len; +/* + memcpy(dst, "ON COMPLETION ", tmp_len =strlen("ON COMPLETION ")); + dst+= tmp_len; + memcpy(dst, (m_on_completion==MYSQL_EVENT_ON_COMPLETION_DROP? + "NOT PRESERVE ":"PRESERVE "), + tmp_len =(m_on_completion==MYSQL_EVENT_ON_COMPLETION_DROP? 13:9)); + dst+= tmp_len; + + memcpy(dst, (m_status==MYSQL_EVENT_ENABLED? + "ENABLE ":"DISABLE "), + tmp_len= (m_status==MYSQL_EVENT_ENABLED? 8:9)); + dst+=tmp_len; + + memcpy(dst, "COMMENT \"", tmp_len= strlen("COMMENT \"")); + dst+= tmp_len; + memcpy(dst, m_comment.str, tmp_len= m_comment.length); + dst+= tmp_len; + memcpy(dst, "\" ", tmp_len=2); + dst+= tmp_len; +*/ + memcpy(dst, "DO ", tmp_len=3); + dst+= tmp_len; + + memcpy(dst, m_body.str, tmp_len= m_body.length); + dst+= tmp_len; + memcpy(dst, ";", 1); + ++dst; + *dst= '\0'; + + *length= len; + + return ret; +} + + +/* + Executes the event (the underlying sp_head object); + + SYNOPSIS + evex_fill_row() + thd THD + mem_root If != NULL use it to compile the event on it + + Returns + 0 - success + -100 - event in execution (parallel execution is impossible) + others - retcodes of sp_head::execute_procedure() + +*/ + +int +event_timed::execute(THD *thd, MEM_ROOT *mem_root) +{ + List empty_item_list; + int ret= 0; + + DBUG_ENTER("event_timed::execute"); + + VOID(pthread_mutex_lock(&LOCK_running)); + if (running) + { + VOID(pthread_mutex_unlock(&LOCK_running)); + DBUG_RETURN(-100); + } + running= true; + VOID(pthread_mutex_unlock(&LOCK_running)); + + // TODO Andrey : make this as member variable and delete in destructor + empty_item_list.empty(); + + if (!m_sphead && (ret= compile(thd, mem_root))) + goto done; + + ret= m_sphead->execute_procedure(thd, &empty_item_list); + + VOID(pthread_mutex_lock(&LOCK_running)); + running= false; + VOID(pthread_mutex_unlock(&LOCK_running)); + +done: + // Don't cache m_sphead if allocated on another mem_root + if (mem_root && m_sphead) + { + delete m_sphead; + m_sphead= 0; + } + + DBUG_RETURN(ret); +} + + +int +event_timed::compile(THD *thd, MEM_ROOT *mem_root) +{ + MEM_ROOT *tmp_mem_root= 0; + LEX *old_lex= thd->lex, lex; + char *old_db; + event_timed *ett; + sp_name *spn; + char *old_query; + uint old_query_len; + st_sp_chistics *p; + + DBUG_ENTER("event_timed::compile"); + // change the memory root for the execution time + if (mem_root) + { + tmp_mem_root= thd->mem_root; + thd->mem_root= mem_root; + } + old_query_len= thd->query_length; + old_query= thd->query; + old_db= thd->db; + thd->db= m_db.str; + thd->query= get_show_create_event(thd, &thd->query_length); + DBUG_PRINT("event_timed::compile", ("query:%s",thd->query)); + + thd->lex= &lex; + lex_start(thd, (uchar*)thd->query, thd->query_length); + lex.et_compile_phase= TRUE; + if (yyparse((void *)thd) || thd->is_fatal_error) + { + // Free lex associated resources + // QQ: Do we really need all this stuff here ? + if (lex.sphead) + { + if (&lex != thd->lex) + thd->lex->sphead->restore_lex(thd); + delete lex.sphead; + lex.sphead= 0; + } + // QQ: anything else ? + lex_end(&lex); + thd->lex= old_lex; + DBUG_RETURN(-1); + } + + m_sphead= lex.sphead; + m_sphead->m_db= m_db; + //copy also chistics since they will vanish otherwise we get 0x0 pointer + // Todo : Handle sql_mode !! + m_sphead->set_definer(m_definer.str, m_definer.length); + m_sphead->set_info(0, 0, &lex.sp_chistics, 0/*sql_mode*/); + m_sphead->optimize(); + lex_end(&lex); + thd->lex= old_lex; + thd->query= old_query; + thd->query_length= old_query_len; + thd->db= old_db; + /* + Change the memory root for the execution time. + */ + if (mem_root) + thd->mem_root= tmp_mem_root; + + DBUG_RETURN(0); +} + diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 65ef17b7066..8688b72c48f 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -4807,7 +4807,7 @@ Disable with --skip-bdb (will save memory).", (gptr*) &global_system_variables.engine_condition_pushdown, (gptr*) &global_system_variables.engine_condition_pushdown, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"event-executor", OPT_EVENT_EXECUTOR, "Print a symbolic stack trace on failure.", + {"event-scheduler", OPT_EVENT_EXECUTOR, "Enable/disable the event scheduler.", (gptr*) &opt_event_executor, (gptr*) &opt_event_executor, 0, GET_BOOL, NO_ARG, 1/*default*/, 0/*min-value*/, 1/*max-value*/, 0, 0, 0}, {"exit-info", 'T', "Used for debugging; Use at your own risk!", 0, 0, 0, diff --git a/sql/set_var.cc b/sql/set_var.cc index b74914e72a6..727ec1f8d98 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -207,7 +207,7 @@ sys_var_long_ptr sys_delayed_insert_timeout("delayed_insert_timeout", &delayed_insert_timeout); sys_var_long_ptr sys_delayed_queue_size("delayed_queue_size", &delayed_queue_size); -sys_var_bool_ptr sys_event_executor("event_executor", +sys_var_bool_ptr sys_event_executor("event_scheduler", &event_executor_running_global_var); sys_var_long_ptr sys_expire_logs_days("expire_logs_days", &expire_logs_days); diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index dfe1f2d71ad..0c9990b58ed 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -5730,7 +5730,7 @@ ER_EVENT_DOES_NOT_EXIST ER_EVENT_CANT_ALTER eng "Failed to alter event %s" ER_EVENT_DROP_FAILED - eng "Failed to DROP %s %s" + eng "Failed to drop %s" ER_EVENT_INTERVAL_NOT_POSITIVE eng "INTERVAL must be positive" ER_EVENT_ENDS_BEFORE_STARTS diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 0800089209c..a142b559bee 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -958,10 +958,10 @@ int sp_head::execute(THD *thd) DBUG_ASSERT(!(m_flags & IS_INVOKED)); m_flags|= IS_INVOKED; m_first_instance->m_first_free_instance= m_next_cached_sp; - DBUG_PRINT("info", ("first free for 0x%lx ++: 0x%lx->0x%lx, level: %lu, flags %x", - (ulong)m_first_instance, this, m_next_cached_sp, - m_next_cached_sp->m_recursion_level, - m_next_cached_sp->m_flags)); +// DBUG_PRINT("info", ("first free for 0x%lx ++: 0x%lx->0x%lx, level: %lu, flags %x", +// (ulong)m_first_instance, this, m_next_cached_sp, +// m_next_cached_sp->m_recursion_level, +// m_next_cached_sp->m_flags)); /* Check that if there are not any instances after this one then pointer to the last instance points on this instance or if there are diff --git a/sql/sp_head.h b/sql/sp_head.h index 90ce53db9db..b928330e82d 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -130,7 +130,6 @@ public: uint m_returns_len; // For FUNCTIONs only uint m_returns_pack; // For FUNCTIONs only const uchar *m_tmp_query; // Temporary pointer to sub query string - uint m_old_cmq; // Old CLIENT_MULTI_QUERIES value st_sp_chistics *m_chistics; ulong m_sql_mode; // For SHOW CREATE and execution LEX_STRING m_qname; // db.name diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 427193cb4a8..8613bd6b503 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -352,6 +352,14 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) if (table->s->fields <= 36 && (user.access & GRANT_ACL)) user.access|= CREATE_USER_ACL; + + /* + if it is pre 5.1.4 privilege table then map CREATE privilege on + CREATE|ALTER|DROP|EXECUTE EVENT + */ + if (table->s->fields <= 37 && (user.access & CREATE_ACL)) + user.access|= EVENT_ACL; + user.sort= get_sort(2,user.host.hostname,user.user); user.hostname_length= (user.host.hostname ? (uint) strlen(user.host.hostname) : 0); diff --git a/sql/sql_acl.h b/sql/sql_acl.h index c1e0e94dd06..44e42b961a2 100644 --- a/sql/sql_acl.h +++ b/sql/sql_acl.h @@ -57,7 +57,7 @@ (UPDATE_ACL | SELECT_ACL | INSERT_ACL | DELETE_ACL | CREATE_ACL | DROP_ACL | \ GRANT_ACL | REFERENCES_ACL | INDEX_ACL | ALTER_ACL | CREATE_TMP_ACL | \ LOCK_TABLES_ACL | EXECUTE_ACL | CREATE_VIEW_ACL | SHOW_VIEW_ACL | \ - CREATE_PROC_ACL | ALTER_PROC_ACL) + CREATE_PROC_ACL | ALTER_PROC_ACL | EVENT_ACL) #define TABLE_ACLS \ (SELECT_ACL | INSERT_ACL | UPDATE_ACL | DELETE_ACL | CREATE_ACL | DROP_ACL | \ diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index d316906f1f6..b44a5c41020 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3675,9 +3675,6 @@ end_with_restore_list: } case SQLCOM_CREATE_EVENT: { - if (check_global_access(thd, EVENT_ACL)) - break; - DBUG_ASSERT(lex->et); if (! lex->et->m_db.str) { @@ -3686,7 +3683,9 @@ end_with_restore_list: lex->et= 0; goto error; } - + if (check_access(thd, EVENT_ACL, lex->et->m_db.str, 0, 0, 0, + is_schema_db(lex->et->m_db.str))) + break; int result; uint create_options= lex->create_info.options; res= (result= evex_create_event(thd, lex->et, create_options)); @@ -3716,9 +3715,6 @@ end_with_restore_list: } case SQLCOM_ALTER_EVENT: { - if (check_global_access(thd, EVENT_ACL)) - break; - DBUG_ASSERT(lex->et); if (! lex->et->m_db.str) { @@ -3727,6 +3723,9 @@ end_with_restore_list: lex->et= 0; goto error; } + if (check_access(thd, EVENT_ACL, lex->et->m_db.str, 0, 0, 0, + is_schema_db(lex->et->m_db.str))) + break; int result; res= (result= evex_update_event(thd, lex->spname, lex->et)); @@ -3735,10 +3734,10 @@ end_with_restore_list: send_ok(thd, 1); break; case EVEX_KEY_NOT_FOUND: - my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), lex->et->m_qname.str); + my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), lex->et->m_name.str); break; default: - my_error(ER_EVENT_CANT_ALTER, MYF(0), lex->et->m_qname.str); + my_error(ER_EVENT_CANT_ALTER, MYF(0), lex->et->m_name.str); break; } delete lex->et; @@ -3754,9 +3753,6 @@ end_with_restore_list: } case SQLCOM_DROP_EVENT: { - if (check_global_access(thd, EVENT_ACL)) - break; - DBUG_ASSERT(lex->et); if (! lex->et->m_db.str) { @@ -3765,6 +3761,9 @@ end_with_restore_list: lex->et= 0; goto error; } + if (check_access(thd, EVENT_ACL, lex->et->m_db.str, 0, 0, 0, + is_schema_db(lex->et->m_db.str))) + break; int result; res= (result= evex_drop_event(thd, lex->et, lex->drop_if_exists)); @@ -3773,10 +3772,10 @@ end_with_restore_list: send_ok(thd, 1); break; case EVEX_KEY_NOT_FOUND: - my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), lex->et->m_qname.str); + my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), lex->et->m_name.str); break; default: - my_error(ER_EVENT_DROP_FAILED, MYF(0), lex->et->m_qname.str); + my_error(ER_EVENT_DROP_FAILED, MYF(0), lex->et->m_name.str); break; } delete lex->et; @@ -3786,7 +3785,8 @@ end_with_restore_list: } case SQLCOM_SHOW_CREATE_EVENT: { - if (check_global_access(thd, EVENT_ACL)) + if (check_access(thd, EVENT_ACL, lex->spname->m_db.str, 0, 0, 0, + is_schema_db(lex->spname->m_db.str))) break; if (lex->spname->m_name.length > NAME_LEN) @@ -3794,6 +3794,7 @@ end_with_restore_list: my_error(ER_TOO_LONG_IDENT, MYF(0), lex->spname->m_name.str); goto error; } + /* TODO : Implement it */ send_ok(thd, 1); break; } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 74ea99b7d98..b16436dd74a 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1261,7 +1261,7 @@ create: * stored procedure, otherwise yylex will chop it into pieces * at each ';'. */ - sp->m_old_cmq= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES; + $$= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES; YYTHD->client_capabilities &= (~CLIENT_MULTI_QUERIES); } '(' @@ -1294,9 +1294,9 @@ create: YYABORT; sp->init_strings(YYTHD, lex, $3); lex->sql_command= SQLCOM_CREATE_PROCEDURE; + /* Restore flag if it was cleared above */ - if (sp->m_old_cmq) - YYTHD->client_capabilities |= CLIENT_MULTI_QUERIES; + YYTHD->client_capabilities |= $3; sp->restore_thd_mem_root(YYTHD); } | CREATE @@ -1314,32 +1314,35 @@ create: | CREATE EVENT_SYM opt_if_not_exists sp_name { LEX *lex=Lex; - event_timed *et; if (lex->et) { + /* + Recursive events are not possible because recursive SPs + are not also possible. lex->sp_head is not stacked. + */ // ToDo Andrey : Change the error message my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "EVENT"); YYABORT; } - lex->create_info.options=$3; + lex->create_info.options= $3; - et= new event_timed();// implicitly calls event_timed::init() - lex->et = et; + if (!(lex->et= new event_timed())) // implicitly calls event_timed::init() + YYABORT; - if (!lex->et_compile_phase) - et->init_name(YYTHD, $4); - /* We have to turn of CLIENT_MULTI_QUERIES while parsing a stored procedure, otherwise yylex will chop it into pieces at each ';'. */ - et->m_old_cmq= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES; - YYTHD->client_capabilities &= ~CLIENT_MULTI_QUERIES; - - lex->sphead= 0; + $$= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES; + YYTHD->client_capabilities &= (~CLIENT_MULTI_QUERIES); + + if (!lex->et_compile_phase) + lex->et->init_name(YYTHD, $4); + + lex->sphead= 0;//defensive programming } ON SCHEDULE_SYM ev_schedule_time ev_on_completion @@ -1347,9 +1350,14 @@ create: ev_comment DO_SYM ev_sql_stmt { - LEX *lex=Lex; + /* + sql_command is set here because some rules in ev_sql_stmt + can overwrite it + */ + // Restore flag if it was cleared above + YYTHD->client_capabilities |= $5; - lex->sql_command= SQLCOM_CREATE_EVENT; + Lex->sql_command= SQLCOM_CREATE_EVENT; } ; @@ -1396,14 +1404,14 @@ ev_status: /* empty */ { LEX *lex=Lex; if (!lex->et_compile_phase) - lex->et->set_event_status(1); + lex->et->m_status= MYSQL_EVENT_ENABLED; } | DISABLE_SYM { LEX *lex=Lex; if (!lex->et_compile_phase) - lex->et->set_event_status(0); + lex->et->m_status= MYSQL_EVENT_DISABLED; } ; ev_starts: /* empty */ @@ -1438,13 +1446,13 @@ ev_on_completion: /* empty */ { LEX *lex=Lex; if (!lex->et_compile_phase) - lex->et->set_on_completion_drop(false); + lex->et->m_on_completion= MYSQL_EVENT_ON_COMPLETION_PRESERVE; } | ON COMPLETION_SYM NOT_SYM PRESERVE_SYM { LEX *lex=Lex; if (!lex->et_compile_phase) - lex->et->set_on_completion_drop(true); + lex->et->m_on_completion= MYSQL_EVENT_ON_COMPLETION_DROP; } ; ev_comment: /* empty */ @@ -1489,10 +1497,6 @@ ev_sql_stmt: sp->restore_thd_mem_root(YYTHD); lex->sp_chistics.suid= SP_IS_SUID;//always the definer! - - // Restore flag if it was cleared above - if (lex->et->m_old_cmq) - YYTHD->client_capabilities |= CLIENT_MULTI_QUERIES; if (!lex->et_compile_phase) { @@ -1577,7 +1581,7 @@ create_function_tail: * stored procedure, otherwise yylex will chop it into pieces * at each ';'. */ - sp->m_old_cmq= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES; + $$= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES; YYTHD->client_capabilities &= ~CLIENT_MULTI_QUERIES; lex->sphead->m_param_begin= lex->tok_start+1; } @@ -1656,9 +1660,9 @@ create_function_tail: YYABORT; lex->sql_command= SQLCOM_CREATE_SPFUNCTION; sp->init_strings(YYTHD, lex, lex->spname); + /* Restore flag if it was cleared above */ - if (sp->m_old_cmq) - YYTHD->client_capabilities |= CLIENT_MULTI_QUERIES; + YYTHD->client_capabilities |= $2; sp->restore_thd_mem_root(YYTHD); } ; @@ -4194,11 +4198,15 @@ alter: if (lex->et) { + /* + Recursive events are not possible because recursive SPs + are not also possible. lex->sp_head is not stacked. + */ // ToDo Andrey : Change the error message my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "EVENT"); YYABORT; } - lex->spname= 0;//defensive + lex->spname= 0;//defensive programming et= new event_timed();// implicitly calls event_timed::init() lex->et = et; @@ -4209,7 +4217,7 @@ alter: stored procedure, otherwise yylex will chop it into pieces at each ';'. */ - et->m_old_cmq= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES; + $$= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES; YYTHD->client_capabilities &= ~CLIENT_MULTI_QUERIES; /* @@ -4225,8 +4233,12 @@ alter: ev_comment ev_opt_sql_stmt { - LEX *lex=Lex; - lex->sql_command= SQLCOM_ALTER_EVENT; + /* + sql_command is set here because some rules in ev_sql_stmt + can overwrite it + */ + YYTHD->client_capabilities |= $3; + Lex->sql_command= SQLCOM_ALTER_EVENT; } ; @@ -6953,18 +6965,21 @@ drop: | DROP EVENT_SYM if_exists sp_name { LEX *lex=Lex; - event_timed *et; if (lex->et) { // ToDo Andrey : Change the error message + /* + Recursive events are not possible because recursive SPs + are not also possible. lex->sp_head is not stacked. + */ my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "EVENT"); YYABORT; } - et= new event_timed; - lex->et = et; - et->init_name(YYTHD, $4); + if (!(lex->et= new event_timed())) + YYABORT; + lex->et->init_name(YYTHD, $4); lex->sql_command = SQLCOM_DROP_EVENT; lex->drop_if_exists= $3; @@ -7623,10 +7638,8 @@ show_param: } | CREATE EVENT_SYM sp_name { - LEX *lex= Lex; - - lex->sql_command = SQLCOM_SHOW_CREATE_EVENT; - lex->spname= $3; + Lex->sql_command = SQLCOM_SHOW_CREATE_EVENT; + Lex->spname= $3; }; ; @@ -8515,7 +8528,7 @@ keyword_sp: | AGGREGATE_SYM {} | ALGORITHM_SYM {} | ANY_SYM {} - | AT_SYM {} + | AT_SYM {} | AUTO_INC {} | AVG_ROW_LENGTH {} | AVG_SYM {} @@ -8561,8 +8574,8 @@ keyword_sp: | ESCAPE_SYM {} | EVENT_SYM {} | EVENTS_SYM {} - | EVERY_SYM {} - | EXPANSION_SYM {} + | EVERY_SYM {} + | EXPANSION_SYM {} | EXTENDED_SYM {} | FAST_SYM {} | FOUND_SYM {} @@ -8684,7 +8697,7 @@ keyword_sp: | ROW_FORMAT_SYM {} | ROW_SYM {} | RTREE_SYM {} - | SCHEDULE_SYM {} + | SCHEDULE_SYM {} | SECOND_SYM {} | SERIAL_SYM {} | SERIALIZABLE_SYM {} @@ -10076,7 +10089,7 @@ trigger_tail: stored procedure, otherwise yylex will chop it into pieces at each ';'. */ - sp->m_old_cmq= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES; + $$= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES; YYTHD->client_capabilities &= ~CLIENT_MULTI_QUERIES; bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); @@ -10091,8 +10104,8 @@ trigger_tail: lex->sql_command= SQLCOM_CREATE_TRIGGER; sp->init_strings(YYTHD, lex, $3); /* Restore flag if it was cleared above */ - if (sp->m_old_cmq) - YYTHD->client_capabilities |= CLIENT_MULTI_QUERIES; + + YYTHD->client_capabilities |= $11; sp->restore_thd_mem_root(YYTHD); if (sp->is_not_allowed_in_function("trigger")) diff --git a/sql/tztime.h b/sql/tztime.h index 312451d84e0..2d574eadf18 100644 --- a/sql/tztime.h +++ b/sql/tztime.h @@ -64,7 +64,7 @@ extern Time_zone * my_tz_find(const String *name, TABLE_LIST *tz_tables); extern Time_zone * my_tz_find_with_opening_tz_tables(THD *thd, const String *name); extern my_bool my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap); extern void my_tz_free(); -my_time_t sec_since_epoch_TIME(TIME *t); +extern my_time_t sec_since_epoch_TIME(TIME *t); extern TABLE_LIST fake_time_zone_tables_list; From 70856a0d54cb770c216800cdd4db5f60ad327a11 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 6 Dec 2005 16:15:29 +0100 Subject: [PATCH 05/28] WL#1034 updated sources sql/event.cc: update put some error calls to the places they occur sql/event.h: - change the default (does not work in STRICT mode) sql/event_executor.cc: move mutex initialization to evex_init_mutexes so init_events() can be reused when the main thread does not work and set global event_scheduler=1; (this will start the thread) The main thread is now visible with show processlist and can be killed. sql/event_priv.h: don't use anymore SP for opening table sql/event_timed.cc: don't use anymore SP routines for opening mysql.event sql/mysqld.cc: shutdown_events() should be maximal at the end of the server because it destroys mutexes of EVEX. The call should not be in the main thread. sql/set_var.cc: make sys_var_event_executor subclass sys_var_bool_ptr to overload ::update() method - needed to start a killed (non-running) evex main thread sql/set_var.h: declare class sys_var_event_executor sql/share/errmsg.txt: 2 new messages --- sql/event.cc | 305 +++++++++++++++++++++++++++--------------- sql/event.h | 4 +- sql/event_executor.cc | 47 ++++++- sql/event_priv.h | 7 +- sql/event_timed.cc | 2 +- sql/mysqld.cc | 1 + sql/set_var.cc | 3 +- sql/set_var.h | 11 ++ sql/share/errmsg.txt | 4 + 9 files changed, 259 insertions(+), 125 deletions(-) diff --git a/sql/event.cc b/sql/event.cc index 82dbc2ebd76..756ebbf8c9a 100644 --- a/sql/event.cc +++ b/sql/event.cc @@ -21,6 +21,8 @@ /* TODO list : + - The default value of created/modified should not be 0000-00-00 because of + STRICT mode restricions. - Remove m_ prefixes of member variables. - Use timestamps instead of datetime. @@ -150,6 +152,93 @@ event_timed_compare(event_timed **a, event_timed **b) } + +/* + Open mysql.event table for read + + SYNOPSIS + evex_open_event_table_for_read() + thd Thread context + lock_type How to lock the table + RETURN + 0 Error + # Pointer to TABLE object +*/ + +TABLE *evex_open_event_table(THD *thd, enum thr_lock_type lock_type) +{ + TABLE_LIST tables; + bool not_used; + DBUG_ENTER("open_proc_table"); + + /* + Speed up things if the table doesn't exists. *table_exists + is set when we create or read stored procedure or on flush privileges. + */ + if (!mysql_event_table_exists) + DBUG_RETURN(0); + + 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)) + { + mysql_event_table_exists= 0; + DBUG_RETURN(0); + } + + DBUG_RETURN(tables.table); +} + + +/* + Find row in open mysql.event table representing event + + SYNOPSIS + evex_db_find_routine_aux() + 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 + SP_OK - Routine found + SP_KEY_NOT_FOUND- No routine with given name +*/ + +static int +evex_db_find_routine_aux(THD *thd, const LEX_STRING dbname, + const LEX_STRING rname, TABLE *table) +{ + byte key[MAX_KEY_LENGTH]; // db, name, optional key length type + DBUG_ENTER("evex_db_find_routine_aux"); + DBUG_PRINT("enter", ("name: %.*s", rname.length, rname.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 (rname.length > table->field[1]->field_length) + DBUG_RETURN(SP_KEY_NOT_FOUND); + table->field[0]->store(dbname.str, dbname.length, &my_charset_bin); + table->field[1]->store(rname.str, rname.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_RETURN(SP_KEY_NOT_FOUND); + + DBUG_RETURN(0); +} + + + /* Puts some data common to CREATE and ALTER EVENT into a row. @@ -164,13 +253,13 @@ event_timed_compare(event_timed **a, event_timed **b) */ static int -evex_fill_row(THD *thd, TABLE *table, event_timed *et) +evex_fill_row(THD *thd, TABLE *table, event_timed *et, my_bool is_update) { DBUG_ENTER("evex_fill_row"); int ret=0; if (table->s->fields != EVEX_FIELD_COUNT) - goto get_field_failed; + DBUG_RETURN(EVEX_GET_FIELD_FAILED); DBUG_PRINT("info", ("m_db.len=%d",et->m_db.length)); DBUG_PRINT("info", ("m_name.len=%d",et->m_name.length)); @@ -195,13 +284,13 @@ evex_fill_row(THD *thd, TABLE *table, event_timed *et) if (et->m_starts.year) { table->field[EVEX_FIELD_STARTS]->set_notnull();// set NULL flag to OFF - table->field[EVEX_FIELD_STARTS]->store_time(&et->m_starts,MYSQL_TIMESTAMP_DATETIME); + table->field[EVEX_FIELD_STARTS]->store_time(&et->m_starts,MYSQL_TIMESTAMP_DATETIME); } if (et->m_ends.year) { table->field[EVEX_FIELD_ENDS]->set_notnull(); - table->field[EVEX_FIELD_ENDS]->store_time(&et->m_ends, MYSQL_TIMESTAMP_DATETIME); + table->field[EVEX_FIELD_ENDS]->store_time(&et->m_ends, MYSQL_TIMESTAMP_DATETIME); } if (et->m_expr) @@ -220,13 +309,15 @@ evex_fill_row(THD *thd, TABLE *table, event_timed *et) { // fix_fields already called in init_execute_at table->field[EVEX_FIELD_EXECUTE_AT]->set_notnull(); - table->field[EVEX_FIELD_EXECUTE_AT]->store_time(&et->m_execute_at, MYSQL_TIMESTAMP_DATETIME); + table->field[EVEX_FIELD_EXECUTE_AT]->store_time(&et->m_execute_at, + MYSQL_TIMESTAMP_DATETIME); //this will make it NULL because we don't call set_notnull table->field[EVEX_FIELD_TRANSIENT_INTERVAL]->store((longlong) 0); } 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 } @@ -238,8 +329,6 @@ evex_fill_row(THD *thd, TABLE *table, event_timed *et) store((et->m_comment).str, (et->m_comment).length, system_charset_info); DBUG_RETURN(0); -get_field_failed: - DBUG_RETURN(EVEX_GET_FIELD_FAILED); } @@ -259,34 +348,38 @@ get_field_failed: static int db_create_event(THD *thd, event_timed *et) { - int ret; + int ret= EVEX_OK; TABLE *table; - TABLE_LIST tables; char definer[HOSTNAME_LENGTH+USERNAME_LENGTH+2]; char olddb[128]; - bool dbchanged; + bool dbchanged= false; DBUG_ENTER("db_create_event"); DBUG_PRINT("enter", ("name: %.*s", et->m_name.length, et->m_name.str)); - dbchanged= false; + + DBUG_PRINT("info", ("open mysql.event for update")); + if (!(table= evex_open_event_table(thd, TL_WRITE))) + { + my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0)); + goto done; + } + + DBUG_PRINT("info", ("check existance of an event with the same name")); + if (!evex_db_find_routine_aux(thd, et->m_db, et->m_name, table)) + { + my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), et->m_name.str); + goto done; + } + + DBUG_PRINT("info", ("non-existant, go forward")); if ((ret= sp_use_new_db(thd, et->m_db.str, olddb, sizeof(olddb), 0, &dbchanged))) { DBUG_PRINT("info", ("cannot use_new_db. code=%d", ret)); - DBUG_RETURN(EVEX_NO_DB_ERROR); + my_error(ER_BAD_DB_ERROR, MYF(0)); + DBUG_RETURN(0); } - - bzero(&tables, sizeof(tables)); - tables.db= (char*)"mysql"; - tables.table_name= tables.alias= (char*)"event"; - - if (!(table= EVEX_OPEN_TABLE_FOR_UPDATE())) - { - if (dbchanged) - (void)mysql_change_db(thd, olddb, 1); - DBUG_RETURN(SP_OPEN_TABLE_FAILED); - } - + restore_record(table, s->default_values); // Get default values for fields strxmov(definer, et->m_definer_user.str, "@", et->m_definer_host.str, NullS); @@ -305,25 +398,25 @@ db_create_event(THD *thd, event_timed *et) */ if (!(et->m_expr) && !(et->m_execute_at.year)) { - DBUG_PRINT("error", ("neither m_expr nor m_execute_as is set!")); - ret= EVEX_WRITE_ROW_FAILED; + DBUG_PRINT("error", ("neither m_expr nor m_execute_as are set!")); + my_error(ER_EVENT_NEITHER_M_EXPR_NOR_M_AT, MYF(0)); goto done; } - ret= table->field[EVEX_FIELD_DEFINER]-> - store(definer, (uint)strlen(definer), system_charset_info); - if (ret) + + if (table->field[EVEX_FIELD_DEFINER]-> + store(definer, (uint)strlen(definer), system_charset_info)) { - ret= EVEX_PARSE_ERROR; + my_error(ER_EVENT_STORE_FAILED, MYF(0), et->m_name.str); goto done; } - + ((Field_timestamp *)table->field[EVEX_FIELD_CREATED])->set_time(); - if ((ret= evex_fill_row(thd, table, et))) + if ((ret= evex_fill_row(thd, table, et, false))) goto done; ret= EVEX_OK; if (table->file->write_row(table->record[0])) - ret= EVEX_WRITE_ROW_FAILED; + my_error(ER_EVENT_STORE_FAILED, MYF(0), et->m_name.str); else if (mysql_bin_log.is_open()) { thd->clear_error(); @@ -333,9 +426,10 @@ db_create_event(THD *thd, event_timed *et) } done: - close_thread_tables(thd); + // No need to close the table, it will be closed in sql_parse::do_command + if (dbchanged) - (void)mysql_change_db(thd, olddb, 1); + (void) mysql_change_db(thd, olddb, 1); DBUG_RETURN(ret); } @@ -365,34 +459,43 @@ db_update_event(THD *thd, sp_name *name, event_timed *et) DBUG_PRINT("enter", ("rename to: %.*s", name->m_name.length, name->m_name.str)); // Todo: Handle in sql_prepare.cc SP_OPEN_TABLE_FAILED - if (!(table= EVEX_OPEN_TABLE_FOR_UPDATE())) - DBUG_RETURN(SP_OPEN_TABLE_FAILED); - - ret= sp_db_find_routine_aux(thd, 0/*notype*/, et->m_db, et->m_name, table); - if (ret == EVEX_OK) + if (!(table= evex_open_event_table(thd, TL_WRITE))) { - store_record(table,record[1]); - table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; // Don't update create on row update. - ret= evex_fill_row(thd, table, et); - if (ret) - goto done; - - if (name) - { - table->field[EVEX_FIELD_DB]-> - store(name->m_db.str, name->m_db.length, system_charset_info); - table->field[EVEX_FIELD_NAME]-> - store(name->m_name.str, name->m_name.length, system_charset_info); - } + my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0)); + goto done; + } - if ((table->file->update_row(table->record[1],table->record[0]))) - ret= EVEX_WRITE_ROW_FAILED; + if (evex_db_find_routine_aux(thd, et->m_db, et->m_name, table) == SP_KEY_NOT_FOUND) + { + my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), et->m_name.str); + goto done; + } + + store_record(table,record[1]); + + table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; // Don't update create on row update. + if ((ret= evex_fill_row(thd, table, et, true))) + goto done; + + if (name) + { + table->field[EVEX_FIELD_DB]-> + store(name->m_db.str, name->m_db.length, system_charset_info); + table->field[EVEX_FIELD_NAME]-> + store(name->m_name.str, name->m_name.length, system_charset_info); + } + + if ((ret= table->file->update_row(table->record[1],table->record[0]))) + { + my_error(ER_EVENT_STORE_FAILED, MYF(0), et->m_name.str); + goto done; } done: close_thread_tables(thd); DBUG_RETURN(ret); } + /* Use sp_name for look up, return in **ett if found */ @@ -404,18 +507,18 @@ db_find_event(THD *thd, sp_name *name, event_timed **ett) const char *definer; char *ptr; event_timed *et; - Open_tables_state open_tables_state_backup; DBUG_ENTER("db_find_event"); DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str)); - if (!(table= open_proc_type_table_for_read(thd, &open_tables_state_backup, - "event", &mysql_event_table_exists))) - DBUG_RETURN(SP_OPEN_TABLE_FAILED); + if (!(table= evex_open_event_table(thd, TL_READ))) + { + my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0)); + goto done; + } - if ((ret= sp_db_find_routine_aux(thd, 0/*notype*/, name->m_db, name->m_name, - table)) != SP_OK) + if ((ret= evex_db_find_routine_aux(thd, name->m_db, name->m_name, table))) goto done; et= new event_timed; @@ -434,7 +537,6 @@ done: et= 0; } close_thread_tables(thd); - thd->restore_backup_open_tables_state(&open_tables_state_backup); *ett= et; DBUG_RETURN(ret); } @@ -503,8 +605,6 @@ done: if (thd->mem_root != tmp_mem_root) thd->mem_root= tmp_mem_root; - if (spn) - delete spn; DBUG_RETURN(ret); } @@ -619,19 +719,19 @@ int evex_create_event(THD *thd, event_timed *et, uint create_options) { int ret = 0; - sp_name *spn= 0; DBUG_ENTER("evex_create_event"); DBUG_PRINT("enter", ("name: %*s options:%d", et->m_name.length, et->m_name.str, create_options)); +/* VOID(pthread_mutex_lock(&LOCK_evex_running)); if (!evex_is_running) // TODO: put an warning to the user here. // Is it needed? (Andrey, 051129) {} VOID(pthread_mutex_unlock(&LOCK_evex_running)); - +*/ if ((ret = db_create_event(thd, et)) == EVEX_WRITE_ROW_FAILED && (create_options & HA_LEX_CREATE_IF_NOT_EXISTS)) @@ -652,24 +752,16 @@ evex_create_event(THD *thd, event_timed *et, uint create_options) goto done; VOID(pthread_mutex_lock(&LOCK_evex_running)); - if (!evex_is_running) + if (evex_is_running && et->m_status == MYSQL_EVENT_ENABLED) { - VOID(pthread_mutex_unlock(&LOCK_evex_running)); - goto done; - } - VOID(pthread_mutex_unlock(&LOCK_evex_running)); - - //cache only if the event is ENABLED - if (et->m_status == MYSQL_EVENT_ENABLED) - { - spn= new sp_name(et->m_db, et->m_name); - if ((ret= evex_load_and_compile_event(thd, spn, true))) - goto done; + sp_name spn(et->m_db, et->m_name); + ret= evex_load_and_compile_event(thd, &spn, true); } + VOID(pthread_mutex_unlock(&LOCK_evex_running)); done: - if (spn) - delete spn; + // No need to close the table, it will be closed in sql_parse::do_command + DBUG_RETURN(ret); } @@ -685,7 +777,7 @@ done: NOTES et contains data about dbname and event name. - name is the new name of the event. if not null this means + name is the new name of the event, if not null this means that RENAME TO was specified in the query. TODO - Add code for in-memory structures - caching & uncaching. @@ -701,19 +793,24 @@ evex_update_event(THD *thd, sp_name *name, event_timed *et) DBUG_ENTER("evex_update_event"); DBUG_PRINT("enter", ("name: %*s", et->m_name.length, et->m_name.str)); +/* VOID(pthread_mutex_lock(&LOCK_evex_running)); if (!evex_is_running) // put an warning to the user here {} VOID(pthread_mutex_unlock(&LOCK_evex_running)); - +*/ + if ((ret= db_update_event(thd, name, et))) goto done_no_evex; VOID(pthread_mutex_lock(&LOCK_evex_running)); if (!evex_is_running) - // not running - therefore no memory structures + { + // not running - therefore no memory structures + VOID(pthread_mutex_unlock(&LOCK_evex_running)); goto done_no_evex; + } VOID(pthread_mutex_unlock(&LOCK_evex_running)); /* @@ -721,31 +818,23 @@ evex_update_event(THD *thd, sp_name *name, event_timed *et) The reason is that DISABLED events are not cached. */ VOID(pthread_mutex_lock(&LOCK_event_arrays)); - if (name) - { - evex_remove_from_cache(&name->m_db, &name->m_name, false); - if (et->m_status == MYSQL_EVENT_ENABLED && - (ret= evex_load_and_compile_event(thd, name, false)) - ) - goto done; - } - else - { - evex_remove_from_cache(&et->m_db, &et->m_name, false); - spn= new sp_name(et->m_db, et->m_name); - if (et->m_status == MYSQL_EVENT_ENABLED && - (ret= evex_load_and_compile_event(thd, spn, false)) - ) + evex_remove_from_cache(&et->m_db, &et->m_name, false); + if (et->m_status == MYSQL_EVENT_ENABLED) + if (name) + ret= evex_load_and_compile_event(thd, name, false); + else { + spn= new sp_name(et->m_db, et->m_name); + ret= evex_load_and_compile_event(thd, spn, false); delete spn; - goto done; - } - } + } done: VOID(pthread_mutex_unlock(&LOCK_event_arrays)); done_no_evex: + // No need to close the table, it will be closed in sql_parse::do_command + DBUG_RETURN(ret); } @@ -771,13 +860,15 @@ evex_drop_event(THD *thd, event_timed *et, bool drop_if_exists) bool opened; DBUG_ENTER("evex_drop_event"); +/* VOID(pthread_mutex_lock(&LOCK_evex_running)); if (!evex_is_running) // put an warning to the user here {} VOID(pthread_mutex_unlock(&LOCK_evex_running)); - - if (!(table= EVEX_OPEN_TABLE_FOR_UPDATE())) +*/ +//// + if (!(table= evex_open_event_table(thd, TL_WRITE))) DBUG_RETURN(SP_OPEN_TABLE_FAILED); ret= sp_db_find_routine_aux(thd, 0/*notype*/, et->m_db, et->m_name, table); @@ -806,14 +897,8 @@ evex_drop_event(THD *thd, event_timed *et, bool drop_if_exists) VOID(pthread_mutex_unlock(&LOCK_evex_running)); done: - /* - "opened" is switched to TRUE when we open mysql.event for checking. - In this case we have to close the table after finishing working with it. - */ - close_thread_tables(thd); + // No need to close the table, it will be closed in sql_parse::do_command DBUG_RETURN(ret); } - - diff --git a/sql/event.h b/sql/event.h index cab5d9bf257..6c7edbcf3fc 100644 --- a/sql/event.h +++ b/sql/event.h @@ -214,8 +214,8 @@ CREATE TABLE `event` ( `execute_at` datetime default NULL, `transient_expression` int(11) default NULL, `interval_type` 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') default NULL, - `created` timestamp NOT NULL default '0000-00-00 00:00:00', - `modified` timestamp NOT NULL default '0000-00-00 00:00:00', + `created` timestamp NOT NULL, + `modified` timestamp NOT NULL, `last_executed` datetime default NULL, `starts` datetime default NULL, `ends` datetime default NULL, diff --git a/sql/event_executor.cc b/sql/event_executor.cc index b3029d66f07..58504a7e559 100644 --- a/sql/event_executor.cc +++ b/sql/event_executor.cc @@ -36,6 +36,7 @@ my_bool event_executor_running_global_var= false; extern ulong thread_created; +static my_bool evex_mutexes_initted= false; static int evex_load_events_from_db(THD *thd); @@ -50,6 +51,19 @@ evex_load_events_from_db(THD *thd); pthread_handler_t event_executor_worker(void *arg); pthread_handler_t event_executor_main(void *arg); +static +void evex_init_mutexes() +{ + if (evex_mutexes_initted) + { + evex_mutexes_initted= true; + return; + } + pthread_mutex_init(&LOCK_event_arrays, MY_MUTEX_INIT_FAST); + pthread_mutex_init(&LOCK_workers_count, MY_MUTEX_INIT_FAST); + pthread_mutex_init(&LOCK_evex_running, MY_MUTEX_INIT_FAST); +} + int init_events() { @@ -59,15 +73,15 @@ init_events() DBUG_PRINT("info",("Starting events main thread")); - pthread_mutex_init(&LOCK_event_arrays, MY_MUTEX_INIT_FAST); - pthread_mutex_init(&LOCK_workers_count, MY_MUTEX_INIT_FAST); - pthread_mutex_init(&LOCK_evex_running, MY_MUTEX_INIT_FAST); - + evex_init_mutexes(); + VOID(pthread_mutex_lock(&LOCK_evex_running)); evex_is_running= false; event_executor_running_global_var= false; VOID(pthread_mutex_unlock(&LOCK_evex_running)); + DBUG_RETURN(0); +/* #ifndef DBUG_FAULTY_THR //TODO Andrey: Change the error code returned! if (pthread_create(&th, NULL, event_executor_main, (void*)NULL)) @@ -77,6 +91,7 @@ init_events() #endif DBUG_RETURN(0); +*/ } @@ -94,6 +109,7 @@ shutdown_events() DBUG_VOID_RETURN; } +#ifdef ANDREY_0 static int init_event_thread(THD* thd) @@ -165,7 +181,7 @@ pthread_handler_t event_executor_main(void *arg) goto err; // make this thread invisible it has no vio -> show processlist won't see - thd->system_thread= 0; + thd->system_thread= 1; VOID(pthread_mutex_lock(&LOCK_thread_count)); threads.append(thd); @@ -350,7 +366,7 @@ err_no_thd: free_root(&evex_mem_root, MYF(0)); sql_print_information("Event executor stopped"); - shutdown_events(); +// shutdown_events(); my_thread_end(); pthread_exit(0); @@ -391,7 +407,7 @@ pthread_handler_t event_executor_worker(void *event_void) thd->init_for_queries(); // make this thread visible it has no vio -> show processlist needs this flag - thd->system_thread= 0; + thd->system_thread= 1; VOID(pthread_mutex_lock(&LOCK_thread_count)); threads.append(thd); @@ -531,3 +547,20 @@ end: ("Events loaded from DB. Status code %d", ret)); DBUG_RETURN(ret); } + +#endif + +bool sys_var_event_executor::update(THD *thd, set_var *var) +{ +#ifdef ANDREY_0 + // here start the thread if not running. + VOID(pthread_mutex_lock(&LOCK_evex_running)); + if ((my_bool) var->save_result.ulong_value && !evex_is_running) { + VOID(pthread_mutex_unlock(&LOCK_evex_running)); + init_events(); + } else + VOID(pthread_mutex_unlock(&LOCK_evex_running)); +#endif + return sys_var_bool_ptr::update(thd, var); +} + diff --git a/sql/event_priv.h b/sql/event_priv.h index 61fd4517bd5..75d39c3c81e 100644 --- a/sql/event_priv.h +++ b/sql/event_priv.h @@ -17,9 +17,6 @@ #ifndef _EVENT_PRIV_H_ #define _EVENT_PRIV_H_ -#define EVEX_OPEN_TABLE_FOR_UPDATE() \ - open_proc_type_table_for_update(thd, "event", &mysql_event_table_exists) - enum { @@ -53,5 +50,7 @@ extern pthread_mutex_t LOCK_event_arrays, int my_time_compare(TIME *a, TIME *b); - + + +TABLE *evex_open_event_table(THD *thd, enum thr_lock_type lock_type); #endif /* _EVENT_PRIV_H_ */ diff --git a/sql/event_timed.cc b/sql/event_timed.cc index 579568cee7d..b8b4876252a 100644 --- a/sql/event_timed.cc +++ b/sql/event_timed.cc @@ -719,7 +719,7 @@ event_timed::update_fields(THD *thd) if (!(m_status_changed || m_last_executed_changed)) goto done; - if (!(table= EVEX_OPEN_TABLE_FOR_UPDATE())) + if (!(table= evex_open_event_table(thd, TL_WRITE))) DBUG_RETURN(SP_OPEN_TABLE_FAILED); if ((ret= sp_db_find_routine_aux(thd, 0/*notype*/, m_db, m_name, table))) diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 8688b72c48f..4377e1a23d4 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -3568,6 +3568,7 @@ we force server id to 2, but this MySQL server will not act as a slave."); clean_up(1); wait_for_signal_thread_to_end(); clean_up_mutexes(); + shutdown_events(); my_end(opt_endinfo ? MY_CHECK_ERROR | MY_GIVE_INFO : 0); exit(0); diff --git a/sql/set_var.cc b/sql/set_var.cc index 727ec1f8d98..4414ab61d15 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -207,7 +207,7 @@ sys_var_long_ptr sys_delayed_insert_timeout("delayed_insert_timeout", &delayed_insert_timeout); sys_var_long_ptr sys_delayed_queue_size("delayed_queue_size", &delayed_queue_size); -sys_var_bool_ptr sys_event_executor("event_scheduler", +sys_var_event_executor sys_event_executor("event_scheduler", &event_executor_running_global_var); sys_var_long_ptr sys_expire_logs_days("expire_logs_days", &expire_logs_days); @@ -3364,6 +3364,7 @@ bool sys_var_trust_routine_creators::update(THD *thd, set_var *var) return sys_var_bool_ptr::update(thd, var); } + /**************************************************************************** Used templates ****************************************************************************/ diff --git a/sql/set_var.h b/sql/set_var.h index 14059f7e9b7..7d2a7999ddc 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -782,6 +782,17 @@ public: bool update(THD *thd, set_var *var); }; + +class sys_var_event_executor :public sys_var_bool_ptr +{ + /* We need a derived class only to have a warn_deprecated() */ +public: + sys_var_event_executor(const char *name_arg, my_bool *value_arg) : + sys_var_bool_ptr(name_arg, value_arg) {}; + bool update(THD *thd, set_var *var); +}; + + /**************************************************************************** Classes for parsing of the SET command ****************************************************************************/ diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index 0c9990b58ed..5804125ed68 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -5737,3 +5737,7 @@ ER_EVENT_ENDS_BEFORE_STARTS eng "ENDS must be after STARTS" ER_EVENT_EXEC_TIME_IN_THE_PAST eng "Activation (AT) time is in the past" +ER_EVENT_OPEN_TABLE_FAILED + eng "Failed to open mysql.event" +ER_EVENT_NEITHER_M_EXPR_NOR_M_AT + eng "No datetime expression provided" From 7838d468ab2983e6f120bc15f159b9c1a97d5b51 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 6 Dec 2005 16:46:29 +0100 Subject: [PATCH 06/28] WL#1034 make more independent of SP sql/event.cc: use own routine - export it sql/event_priv.h: export these two sql/event_timed.cc: use own routine, don't rely on SP sql/sp.cc: revert changes to SP sql/sp.h: revert changes to SP sql/sql_show.cc: rever changes to SP --- sql/event.cc | 4 +-- sql/event_priv.h | 8 +++-- sql/event_timed.cc | 2 +- sql/sp.cc | 88 +++++++++++++++++++--------------------------- sql/sp.h | 12 +------ sql/sql_show.cc | 4 +-- 6 files changed, 48 insertions(+), 70 deletions(-) diff --git a/sql/event.cc b/sql/event.cc index 756ebbf8c9a..f647f199556 100644 --- a/sql/event.cc +++ b/sql/event.cc @@ -208,7 +208,7 @@ TABLE *evex_open_event_table(THD *thd, enum thr_lock_type lock_type) SP_KEY_NOT_FOUND- No routine with given name */ -static int +int evex_db_find_routine_aux(THD *thd, const LEX_STRING dbname, const LEX_STRING rname, TABLE *table) { @@ -871,7 +871,7 @@ evex_drop_event(THD *thd, event_timed *et, bool drop_if_exists) if (!(table= evex_open_event_table(thd, TL_WRITE))) DBUG_RETURN(SP_OPEN_TABLE_FAILED); - ret= sp_db_find_routine_aux(thd, 0/*notype*/, et->m_db, et->m_name, table); + ret= evex_db_find_routine_aux(thd, et->m_db, et->m_name, table); if (ret == EVEX_OK) { diff --git a/sql/event_priv.h b/sql/event_priv.h index 75d39c3c81e..73618d1b44a 100644 --- a/sql/event_priv.h +++ b/sql/event_priv.h @@ -51,6 +51,10 @@ extern pthread_mutex_t LOCK_event_arrays, int my_time_compare(TIME *a, TIME *b); - -TABLE *evex_open_event_table(THD *thd, enum thr_lock_type lock_type); +int +evex_db_find_routine_aux(THD *thd, const LEX_STRING dbname, + const LEX_STRING rname, TABLE *table); + +TABLE * +evex_open_event_table(THD *thd, enum thr_lock_type lock_type); #endif /* _EVENT_PRIV_H_ */ diff --git a/sql/event_timed.cc b/sql/event_timed.cc index b8b4876252a..72cc9d7fe32 100644 --- a/sql/event_timed.cc +++ b/sql/event_timed.cc @@ -722,7 +722,7 @@ event_timed::update_fields(THD *thd) if (!(table= evex_open_event_table(thd, TL_WRITE))) DBUG_RETURN(SP_OPEN_TABLE_FAILED); - if ((ret= sp_db_find_routine_aux(thd, 0/*notype*/, m_db, m_name, table))) + if ((ret= evex_db_find_routine_aux(thd, m_db, m_name, table))) goto done; store_record(table,record[1]); diff --git a/sql/sp.cc b/sql/sp.cc index cda6454b891..e9deb9b73c4 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -20,9 +20,6 @@ #include "sp_cache.h" #include "sql_trigger.h" -#define SP_OPEN_TABLE_FOR_UPDATE() \ - open_proc_type_table_for_update(thd, "proc", &mysql_proc_table_exists) - static bool create_string(THD *thd, String *buf, int sp_type, @@ -71,7 +68,7 @@ bool mysql_proc_table_exists= 1; /* - Close mysql.proc, opened with open_proc_type_table_for_read(). + Close mysql.proc, opened with open_proc_table_for_read(). SYNOPSIS close_proc_table() @@ -89,16 +86,14 @@ void close_proc_table(THD *thd, Open_tables_state *backup) /* - Open table which has key structure like of mysql.proc for read. + Open the mysql.proc table for read. SYNOPSIS - open_proc_type_table_for_read() - thd Thread context - backup Pointer to Open_tables_state instance where information about - currently open tables will be saved, and from which will be - restored when we will end work with mysql.proc. - tname Table name having primary key structure like mysql.proc - table_exists Ptr to boolean to set whether the system table exists or not + open_proc_table_for_read() + thd Thread context + backup Pointer to Open_tables_state instance where information about + currently open tables will be saved, and from which will be + restored when we will end work with mysql.proc. NOTES Thanks to restrictions which we put on opening and locking of @@ -109,11 +104,10 @@ void close_proc_table(THD *thd, Open_tables_state *backup) RETURN 0 Error - # Pointer to TABLE object of tname + # Pointer to TABLE object of mysql.proc */ -TABLE *open_proc_type_table_for_read(THD *thd, Open_tables_state *backup, - const char *tname, bool *table_exists) +TABLE *open_proc_table_for_read(THD *thd, Open_tables_state *backup) { TABLE_LIST tables; TABLE *table; @@ -121,22 +115,22 @@ TABLE *open_proc_type_table_for_read(THD *thd, Open_tables_state *backup, DBUG_ENTER("open_proc_table"); /* - Speed up things if the table doesn't exists. *table_exists + Speed up things if mysql.proc doesn't exists. mysql_proc_table_exists is set when we create or read stored procedure or on flush privileges. */ - if (!*table_exists) + if (!mysql_proc_table_exists) DBUG_RETURN(0); thd->reset_n_backup_open_tables_state(backup); bzero((char*) &tables, sizeof(tables)); tables.db= (char*) "mysql"; - tables.table_name= tables.alias= (char*) tname; + tables.table_name= tables.alias= (char*)"proc"; if (!(table= open_table(thd, &tables, thd->mem_root, ¬_used, MYSQL_LOCK_IGNORE_FLUSH))) { thd->restore_backup_open_tables_state(backup); - *table_exists= 0; + mysql_proc_table_exists= 0; DBUG_RETURN(0); } @@ -158,13 +152,11 @@ TABLE *open_proc_type_table_for_read(THD *thd, Open_tables_state *backup, /* - Open table with primary key struct like mysql.proc for update. + Open the mysql.proc table for update. SYNOPSIS - open_proc_type_table_for_update() - thd Thread context - tname Table name with primary key structure like mysql.proc - table_exists Ptr to boolean to set whether the system table exists or not + open_proc_table_for_update() + thd Thread context NOTES Table opened with this call should closed using close_thread_tables(). @@ -174,8 +166,7 @@ TABLE *open_proc_type_table_for_read(THD *thd, Open_tables_state *backup, # Pointer to TABLE object of mysql.proc */ -TABLE *open_proc_type_table_for_update(THD *thd, const char *tname, - bool *table_exists) +static TABLE *open_proc_table_for_update(THD *thd) { TABLE_LIST tables; TABLE *table; @@ -183,7 +174,7 @@ TABLE *open_proc_type_table_for_update(THD *thd, const char *tname, bzero((char*) &tables, sizeof(tables)); tables.db= (char*) "mysql"; - tables.table_name= tables.alias= (char*) tname; + tables.table_name= tables.alias= (char*)"proc"; tables.lock_type= TL_WRITE; table= open_ltable(thd, &tables, TL_WRITE); @@ -195,7 +186,7 @@ TABLE *open_proc_type_table_for_update(THD *thd, const char *tname, transient. */ if (!(thd->locked_tables || thd->prelocked_mode) || table) - *table_exists= test(table); + mysql_proc_table_exists= test(table); DBUG_RETURN(table); } @@ -205,11 +196,10 @@ TABLE *open_proc_type_table_for_update(THD *thd, const char *tname, Find row in open mysql.proc table representing stored routine. SYNOPSIS - sp_db_find_routine_aux() + db_find_routine_aux() thd Thread context type Type of routine to find (function or procedure) - dbname Name of routine's database - rname Name of the routine inside the db + name Name of routine table TABLE object for open mysql.proc table. RETURN VALUE @@ -217,14 +207,13 @@ TABLE *open_proc_type_table_for_update(THD *thd, const char *tname, SP_KEY_NOT_FOUND- No routine with given name */ -int -sp_db_find_routine_aux(THD *thd, int type, const LEX_STRING dbname, - const LEX_STRING rname, TABLE *table) +static int +db_find_routine_aux(THD *thd, int type, sp_name *name, TABLE *table) { byte key[MAX_KEY_LENGTH]; // db, name, optional key length type - DBUG_ENTER("sp_db_find_routine_aux"); + DBUG_ENTER("db_find_routine_aux"); DBUG_PRINT("enter", ("type: %d name: %.*s", - type, rname.length, rname.str)); + type, name->m_name.length, name->m_name.str)); /* Create key to find row. We have to use field->store() to be able to @@ -233,10 +222,11 @@ sp_db_find_routine_aux(THD *thd, int type, const LEX_STRING dbname, 'db', 'name' and 'type' and the first key is the primary key over the same fields. */ - if (rname.length > table->field[1]->field_length) + if (name->m_name.length > table->field[1]->field_length) DBUG_RETURN(SP_KEY_NOT_FOUND); - table->field[0]->store(dbname.str, dbname.length, &my_charset_bin); - table->field[1]->store(rname.str, rname.length, &my_charset_bin); + table->field[0]->store(name->m_db.str, name->m_db.length, &my_charset_bin); + table->field[1]->store(name->m_name.str, name->m_name.length, + &my_charset_bin); table->field[2]->store((longlong) type, TRUE); key_copy(key, table->record[0], table->key_info, table->key_info->key_length); @@ -293,12 +283,10 @@ db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp) type, name->m_name.length, name->m_name.str)); *sphp= 0; // In case of errors - if (!(table= open_proc_type_table_for_read(thd,&open_tables_state_backup, - "proc", &mysql_proc_table_exists))) + if (!(table= open_proc_table_for_read(thd, &open_tables_state_backup))) DBUG_RETURN(SP_OPEN_TABLE_FAILED); - if ((ret= sp_db_find_routine_aux(thd, type, name->m_db, name->m_name, - table)) != SP_OK) + if ((ret= db_find_routine_aux(thd, type, name, table)) != SP_OK) goto done; if (table->s->fields != MYSQL_PROC_FIELD_COUNT) @@ -505,7 +493,7 @@ db_create_routine(THD *thd, int type, sp_head *sp) goto done; } - if (!(table= SP_OPEN_TABLE_FOR_UPDATE())) + if (!(table= open_proc_table_for_update(thd))) ret= SP_OPEN_TABLE_FAILED; else { @@ -626,10 +614,9 @@ db_drop_routine(THD *thd, int type, sp_name *name) DBUG_PRINT("enter", ("type: %d name: %.*s", type, name->m_name.length, name->m_name.str)); - if (!(table= SP_OPEN_TABLE_FOR_UPDATE())) + if (!(table= open_proc_table_for_update(thd))) DBUG_RETURN(SP_OPEN_TABLE_FAILED); - if ((ret= sp_db_find_routine_aux(thd, type, name->m_db, name->m_name, - table)) == SP_OK) + if ((ret= db_find_routine_aux(thd, type, name, table)) == SP_OK) { if (table->file->delete_row(table->record[0])) ret= SP_DELETE_ROW_FAILED; @@ -649,10 +636,9 @@ db_update_routine(THD *thd, int type, sp_name *name, st_sp_chistics *chistics) DBUG_PRINT("enter", ("type: %d name: %.*s", type, name->m_name.length, name->m_name.str)); - if (!(table= SP_OPEN_TABLE_FOR_UPDATE())) + if (!(table= open_proc_table_for_update(thd))) DBUG_RETURN(SP_OPEN_TABLE_FAILED); - if ((ret= sp_db_find_routine_aux(thd, type, name->m_db, name->m_name, - table)) == SP_OK) + if ((ret= db_find_routine_aux(thd, type, name, table)) == SP_OK) { store_record(table,record[1]); table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; @@ -874,7 +860,7 @@ sp_drop_db_routines(THD *thd, char *db) keylen= sizeof(key); ret= SP_OPEN_TABLE_FAILED; - if (!(table= SP_OPEN_TABLE_FOR_UPDATE())) + if (!(table= open_proc_table_for_update(thd))) goto err; ret= SP_OK; diff --git a/sql/sp.h b/sql/sp.h index 157d7dbbea9..7f314b8903e 100644 --- a/sql/sp.h +++ b/sql/sp.h @@ -31,8 +31,6 @@ #define SP_BAD_IDENTIFIER -9 #define SP_BODY_TOO_LONG -10 -extern bool mysql_proc_table_exists; - /* Drop all routines in database 'db' */ int sp_drop_db_routines(THD *thd, char *db); @@ -99,17 +97,9 @@ extern "C" byte* sp_sroutine_key(const byte *ptr, uint *plen, my_bool first); Routines which allow open/lock and close mysql.proc table even when we already have some tables open and locked. */ -TABLE *open_proc_type_table_for_read(THD *thd, Open_tables_state *backup, - const char *tname, bool *table_exists); -TABLE *open_proc_type_table_for_update(THD *thd, const char *tname, - bool *table_exists); - +TABLE *open_proc_table_for_read(THD *thd, Open_tables_state *backup); void close_proc_table(THD *thd, Open_tables_state *backup); -int -sp_db_find_routine_aux(THD *thd, int type, const LEX_STRING dbname, - const LEX_STRING rname, TABLE *table); - // // Utilities... // diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 447201de57c..33e96f7799e 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -2931,9 +2931,7 @@ int fill_schema_proc(THD *thd, TABLE_LIST *tables, COND *cond) proc_tables.table_name_length= 4; proc_tables.lock_type= TL_READ; full_access= !check_table_access(thd, SELECT_ACL, &proc_tables, 1); - if (!(proc_table= open_proc_type_table_for_read(thd, &open_tables_state_backup, - "proc", - &mysql_proc_table_exists))) + if (!(proc_table= open_proc_table_for_read(thd, &open_tables_state_backup))) { DBUG_RETURN(1); } From a581a4a1c590aaf333e349ed4f6413e200eadd69 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 6 Dec 2005 17:12:01 +0100 Subject: [PATCH 07/28] rever sql/sp_head.cc: revert --- sql/sp_head.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sql/sp_head.cc b/sql/sp_head.cc index a142b559bee..0800089209c 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -958,10 +958,10 @@ int sp_head::execute(THD *thd) DBUG_ASSERT(!(m_flags & IS_INVOKED)); m_flags|= IS_INVOKED; m_first_instance->m_first_free_instance= m_next_cached_sp; -// DBUG_PRINT("info", ("first free for 0x%lx ++: 0x%lx->0x%lx, level: %lu, flags %x", -// (ulong)m_first_instance, this, m_next_cached_sp, -// m_next_cached_sp->m_recursion_level, -// m_next_cached_sp->m_flags)); + DBUG_PRINT("info", ("first free for 0x%lx ++: 0x%lx->0x%lx, level: %lu, flags %x", + (ulong)m_first_instance, this, m_next_cached_sp, + m_next_cached_sp->m_recursion_level, + m_next_cached_sp->m_flags)); /* Check that if there are not any instances after this one then pointer to the last instance points on this instance or if there are From bc3708a5b9fd72a1fb5e066b3084fec809138558 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 7 Dec 2005 19:26:44 +0100 Subject: [PATCH 08/28] WL#1034 ongoing updates after review sql/event.cc: -my_error() as close as possible to the place where the error occurs. -a thought how to replicate events -use close_thread_tables() in some cases and for others rely on this call being done in sql_parse.cc::do_command() sql/event.h: remove redundant defines sql/event_executor.cc: - reenable the compilation again - don't backup the open_tables_state, it's not needed sql/event_timed.cc: - inline a bit - comment added sql/mysqld.cc: - start mysqld with --event-scheduler=0 by default sql/share/errmsg.txt: 3 new messages sql/sql_parse.cc: remove now obsolete error checking - the errors are reported as closer as possible to the place where they are detected sql/sql_yacc.yy: add WARNING message. fix a bug that was corrupting thd->client_capabilites -> select count(*) from mysql.event was reporting : "Unknown table test.event"!!! Using temporal variable is nice but IMO quite error-prone. --- sql/event.cc | 161 +++++++++++++++++++++++++----------------- sql/event.h | 11 --- sql/event_executor.cc | 14 +--- sql/event_timed.cc | 11 +-- sql/mysqld.cc | 2 +- sql/share/errmsg.txt | 6 ++ sql/sql_parse.cc | 15 +--- sql/sql_yacc.yy | 30 ++++++-- 8 files changed, 139 insertions(+), 111 deletions(-) diff --git a/sql/event.cc b/sql/event.cc index f647f199556..36180adb90f 100644 --- a/sql/event.cc +++ b/sql/event.cc @@ -31,6 +31,18 @@ - CREATE EVENT should not go into binary log! Does it now? The SQL statements issued by the EVENT are replicated. + I have an idea how to solve the problem at failover. So the status field + will be ENUM('DISABLED', 'ENABLED', 'SLAVESIDE_DISABLED'). + In this case when CREATE EVENT is replicated it should go into the binary + as SLAVESIDE_DISABLED if it is ENABLED, when it's created as DISABLEd it + should be replicated as disabled. If an event is ALTERed as DISABLED the + query should go untouched into the binary log, when ALTERed as enable then + it should go as SLAVESIDE_DISABLED. This is regarding the SQL interface. + TT routines however modify mysql.event internally and this does not go the log + so in this case queries has to be injected into the log...somehow... or + maybe a solution is RBR for this case, because the event may go only from + ENABLED to DISABLED status change and this is safe for replicating. As well + an event may be deleted which is also safe for RBR. - Add a lock and use it for guarding access to events_array dynamic array. @@ -225,13 +237,13 @@ evex_db_find_routine_aux(THD *thd, const LEX_STRING dbname, */ if (rname.length > table->field[1]->field_length) DBUG_RETURN(SP_KEY_NOT_FOUND); + table->field[0]->store(dbname.str, dbname.length, &my_charset_bin); table->field[1]->store(rname.str, rname.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)) + if (table->file->index_read_idx(table->record[0], 0, key, + table->key_info->key_length,HA_READ_KEY_EXACT)) DBUG_RETURN(SP_KEY_NOT_FOUND); DBUG_RETURN(0); @@ -256,11 +268,13 @@ static int evex_fill_row(THD *thd, TABLE *table, event_timed *et, my_bool is_update) { DBUG_ENTER("evex_fill_row"); - int ret=0; 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", ("m_db.len=%d",et->m_db.length)); DBUG_PRINT("info", ("m_name.len=%d",et->m_name.length)); @@ -361,27 +375,24 @@ db_create_event(THD *thd, event_timed *et) if (!(table= evex_open_event_table(thd, TL_WRITE))) { my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0)); - goto done; + goto err; } DBUG_PRINT("info", ("check existance of an event with the same name")); if (!evex_db_find_routine_aux(thd, et->m_db, et->m_name, table)) { my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), et->m_name.str); - goto done; + goto err; } DBUG_PRINT("info", ("non-existant, go forward")); - if ((ret= sp_use_new_db(thd, et->m_db.str, olddb, sizeof(olddb), - 0, &dbchanged))) + if ((ret= sp_use_new_db(thd, et->m_db.str,olddb, sizeof(olddb),0, &dbchanged))) { - DBUG_PRINT("info", ("cannot use_new_db. code=%d", ret)); my_error(ER_BAD_DB_ERROR, MYF(0)); - DBUG_RETURN(0); + goto err; } restore_record(table, s->default_values); // Get default values for fields - strxmov(definer, et->m_definer_user.str, "@", et->m_definer_host.str, NullS); /* TODO : Uncomment these and add handling in sql_parse.cc or here @@ -400,23 +411,29 @@ db_create_event(THD *thd, event_timed *et) { DBUG_PRINT("error", ("neither m_expr nor m_execute_as are set!")); my_error(ER_EVENT_NEITHER_M_EXPR_NOR_M_AT, MYF(0)); - goto done; + + goto err; } + strxmov(definer, et->m_definer_user.str, "@", et->m_definer_host.str, NullS); if (table->field[EVEX_FIELD_DEFINER]-> - store(definer, (uint)strlen(definer), system_charset_info)) + store(definer, et->m_definer_user.length + 1 + et->m_definer_host.length, + system_charset_info)) { my_error(ER_EVENT_STORE_FAILED, MYF(0), et->m_name.str); - goto done; + goto err; } ((Field_timestamp *)table->field[EVEX_FIELD_CREATED])->set_time(); if ((ret= evex_fill_row(thd, table, et, false))) - goto done; + goto err; ret= EVEX_OK; if (table->file->write_row(table->record[0])) + { my_error(ER_EVENT_STORE_FAILED, MYF(0), et->m_name.str); + goto err; + } else if (mysql_bin_log.is_open()) { thd->clear_error(); @@ -425,12 +442,15 @@ db_create_event(THD *thd, event_timed *et) mysql_bin_log.write(&qinfo); } -done: // No need to close the table, it will be closed in sql_parse::do_command - if (dbchanged) (void) mysql_change_db(thd, olddb, 1); - DBUG_RETURN(ret); + DBUG_RETURN(EVEX_OK); + +err: + if (dbchanged) + (void) mysql_change_db(thd, olddb, 1); + DBUG_RETURN(EVEX_GENERAL_ERROR); } @@ -462,20 +482,23 @@ db_update_event(THD *thd, sp_name *name, event_timed *et) if (!(table= evex_open_event_table(thd, TL_WRITE))) { my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0)); - goto done; + goto err; } if (evex_db_find_routine_aux(thd, et->m_db, et->m_name, table) == SP_KEY_NOT_FOUND) { my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), et->m_name.str); - goto done; + goto err; } store_record(table,record[1]); - table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; // Don't update create on row update. + // Don't update create on row update. + table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; + + // evex_fill_row() calls my_error() in case of error so no need to handle it here if ((ret= evex_fill_row(thd, table, et, true))) - goto done; + goto err; if (name) { @@ -485,14 +508,20 @@ db_update_event(THD *thd, sp_name *name, event_timed *et) store(name->m_name.str, name->m_name.length, system_charset_info); } - if ((ret= table->file->update_row(table->record[1],table->record[0]))) + if ((ret= table->file->update_row(table->record[1], table->record[0]))) { my_error(ER_EVENT_STORE_FAILED, MYF(0), et->m_name.str); - goto done; + goto err; } -done: + + // close mysql.event or we crash later when loading the event from disk close_thread_tables(thd); - DBUG_RETURN(ret); + DBUG_RETURN(0); + +err: + if (table) + close_thread_tables(thd); + DBUG_RETURN(EVEX_GENERAL_ERROR); } @@ -500,7 +529,7 @@ done: Use sp_name for look up, return in **ett if found */ static int -db_find_event(THD *thd, sp_name *name, event_timed **ett) +db_find_event(THD *thd, sp_name *name, event_timed **ett, TABLE *tbl) { TABLE *table; int ret; @@ -508,27 +537,35 @@ db_find_event(THD *thd, sp_name *name, event_timed **ett) char *ptr; event_timed *et; DBUG_ENTER("db_find_event"); - DBUG_PRINT("enter", ("name: %*s", - name->m_name.length, name->m_name.str)); + DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str)); - - if (!(table= evex_open_event_table(thd, TL_READ))) + if (tbl) + table= tbl; + else if (!(table= evex_open_event_table(thd, TL_READ))) { my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0)); + ret= EVEX_GENERAL_ERROR; goto done; } if ((ret= evex_db_find_routine_aux(thd, name->m_db, name->m_name, table))) - goto done; - + { + my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), et->m_name.str); + goto done; + } et= new event_timed; /* - The table should not be closed beforehand. - ::load_from_row only loads and does not compile + 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(&evex_mem_root, table))) + { + my_error(ER_EVENT_CANNOT_LOAD_FROM_TABLE, MYF(0)); goto done; + } done: if (ret && et) @@ -536,7 +573,9 @@ done: delete et; et= 0; } - close_thread_tables(thd); + // don't close the table if we haven't opened it ourselves + if (!tbl) + close_thread_tables(thd); *ett= et; DBUG_RETURN(ret); } @@ -555,22 +594,16 @@ evex_load_and_compile_event(THD * thd, sp_name *spn, bool use_lock) tmp_mem_root= thd->mem_root; thd->mem_root= &evex_mem_root; - if (db_find_event(thd, spn, &ett)) - { - ret= EVEX_GENERAL_ERROR; + // no need to use my_error() here because db_find_event() has done it + if ((ret= db_find_event(thd, spn, &ett, NULL))) goto done; - } /* - allocate on evex_mem_root. call without evex_mem_root and - and m_sphead will not be cleared! + allocate on evex_mem_root. if you call without evex_mem_root + then m_sphead will not be cleared! */ - if ((ret= ett->compile(thd, &evex_mem_root))) - { - thd->mem_root= tmp_mem_root; goto done; - } ett->compute_next_execution_time(); if (use_lock) @@ -589,7 +622,7 @@ evex_load_and_compile_event(THD * thd, sp_name *spn, bool use_lock) delete ett; /* - We find where the first element resides in the arraay. And then do a + We find where the first element resides in the array. And then do a qsort of events_array.elements (the current number of elements). We know that the elements are stored in a contiguous block w/o holes. */ @@ -613,7 +646,6 @@ static int evex_remove_from_cache(LEX_STRING *db, LEX_STRING *name, bool use_lock) { uint i; - int ret= 0; bool need_second_pass= true; DBUG_ENTER("evex_remove_from_cache"); @@ -690,7 +722,7 @@ done: if (use_lock) VOID(pthread_mutex_unlock(&LOCK_event_arrays)); - DBUG_RETURN(ret); + DBUG_RETURN(0); } @@ -711,8 +743,6 @@ done: NOTES - in case there is an event with the same name (db) and IF NOT EXISTS is specified, an warning is put into the W stack. - TODO - - Add code for in-memory structures - caching & uncaching. */ int @@ -779,8 +809,6 @@ done: 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. - TODO - - Add code for in-memory structures - caching & uncaching. */ int @@ -800,16 +828,19 @@ evex_update_event(THD *thd, sp_name *name, event_timed *et) {} VOID(pthread_mutex_unlock(&LOCK_evex_running)); */ - + /* + db_update_event() opens & closes the table to prevent + crash later in the code when loading and compiling the new definition + */ if ((ret= db_update_event(thd, name, et))) - goto done_no_evex; + goto done; VOID(pthread_mutex_lock(&LOCK_evex_running)); if (!evex_is_running) { // not running - therefore no memory structures VOID(pthread_mutex_unlock(&LOCK_evex_running)); - goto done_no_evex; + goto done; } VOID(pthread_mutex_unlock(&LOCK_evex_running)); @@ -828,12 +859,9 @@ evex_update_event(THD *thd, sp_name *name, event_timed *et) ret= evex_load_and_compile_event(thd, spn, false); delete spn; } - -done: VOID(pthread_mutex_unlock(&LOCK_event_arrays)); -done_no_evex: - // No need to close the table, it will be closed in sql_parse::do_command +done: DBUG_RETURN(ret); } @@ -848,8 +876,6 @@ done_no_evex: et event's name drop_if_exists if set and the event not existing => warning onto the stack - TODO - Update in-memory structures */ int @@ -875,9 +901,9 @@ evex_drop_event(THD *thd, event_timed *et, bool drop_if_exists) if (ret == EVEX_OK) { - if (table->file->delete_row(table->record[0])) + if (ret= table->file->delete_row(table->record[0])) { - ret= EVEX_DELETE_ROW_FAILED; + my_error(ER_EVENT_CANNOT_DELETE, MYF(0)); goto done; } } @@ -897,7 +923,10 @@ evex_drop_event(THD *thd, event_timed *et, bool drop_if_exists) VOID(pthread_mutex_unlock(&LOCK_evex_running)); done: - // No need to close the table, it will be closed in sql_parse::do_command + /* + No need to close the table, it will be closed in sql_parse::do_command() + and evex_remove_from_cache does not try to open a table + */ DBUG_RETURN(ret); } diff --git a/sql/event.h b/sql/event.h index 6c7edbcf3fc..eae38472498 100644 --- a/sql/event.h +++ b/sql/event.h @@ -40,17 +40,6 @@ extern ulong opt_event_executor; #define EVENT_EXEC_NO_MORE (1L << 0) #define EVENT_NOT_USED (1L << 1) -#define SP_OK 0 -#define SP_KEY_NOT_FOUND -1 -#define SP_OPEN_TABLE_FAILED -2 -#define SP_WRITE_ROW_FAILED -3 -#define SP_DELETE_ROW_FAILED -4 -#define SP_GET_FIELD_FAILED -5 -#define SP_PARSE_ERROR -6 -#define SP_INTERNAL_ERROR -7 -#define SP_NO_DB_ERROR -8 -#define SP_BAD_IDENTIFIER -9 -#define SP_BODY_TOO_LONG -10 extern ulong opt_event_executor; diff --git a/sql/event_executor.cc b/sql/event_executor.cc index 58504a7e559..763f440df64 100644 --- a/sql/event_executor.cc +++ b/sql/event_executor.cc @@ -80,8 +80,6 @@ init_events() event_executor_running_global_var= false; VOID(pthread_mutex_unlock(&LOCK_evex_running)); - DBUG_RETURN(0); -/* #ifndef DBUG_FAULTY_THR //TODO Andrey: Change the error code returned! if (pthread_create(&th, NULL, event_executor_main, (void*)NULL)) @@ -91,7 +89,6 @@ init_events() #endif DBUG_RETURN(0); -*/ } @@ -109,7 +106,6 @@ shutdown_events() DBUG_VOID_RETURN; } -#ifdef ANDREY_0 static int init_event_thread(THD* thd) @@ -479,13 +475,11 @@ evex_load_events_from_db(THD *thd) TABLE *table; READ_RECORD read_record_info; MYSQL_LOCK *lock; - Open_tables_state open_tables_state_backup; int ret= -1; DBUG_ENTER("evex_load_events_from_db"); - if (!(table= open_proc_type_table_for_read(thd, &open_tables_state_backup, - "event", &mysql_event_table_exists))) + if (!(table= evex_open_event_table(thd, TL_READ))) DBUG_RETURN(SP_OPEN_TABLE_FAILED); VOID(pthread_mutex_lock(&LOCK_event_arrays)); @@ -541,18 +535,16 @@ evex_load_events_from_db(THD *thd) end: close_thread_tables(thd); - thd->restore_backup_open_tables_state(&open_tables_state_backup); DBUG_PRINT("evex_load_events_from_db", ("Events loaded from DB. Status code %d", ret)); DBUG_RETURN(ret); } -#endif + bool sys_var_event_executor::update(THD *thd, set_var *var) { -#ifdef ANDREY_0 // here start the thread if not running. VOID(pthread_mutex_lock(&LOCK_evex_running)); if ((my_bool) var->save_result.ulong_value && !evex_is_running) { @@ -560,7 +552,7 @@ bool sys_var_event_executor::update(THD *thd, set_var *var) init_events(); } else VOID(pthread_mutex_unlock(&LOCK_evex_running)); -#endif + return sys_var_bool_ptr::update(thd, var); } diff --git a/sql/event_timed.cc b/sql/event_timed.cc index 72cc9d7fe32..45314dc9dbc 100644 --- a/sql/event_timed.cc +++ b/sql/event_timed.cc @@ -322,10 +322,8 @@ event_timed::init_comment(THD *thd, LEX_STRING *comment) { DBUG_ENTER("event_timed::init_comment"); - MEM_ROOT *root= thd->mem_root; - m_comment.length= comment->length; - m_comment.str= strmake_root(root, comment->str, comment->length); - DBUG_PRINT("m_comment", ("len=%d",m_comment.length)); + m_comment.str= strmake_root(thd->mem_root, comment->str, + m_comment.length= comment->length); DBUG_VOID_RETURN; } @@ -359,6 +357,11 @@ event_timed::init_definer(THD *thd) SYNOPSIS event_timed::load_from_row() + + REMARKS + This method is silent on errors and should behave like that. Callers + should handle throwing of error messages. The reason is that the class + should not know about how to deal with communication. */ int diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 4377e1a23d4..dcb49e90b7a 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -4810,7 +4810,7 @@ Disable with --skip-bdb (will save memory).", 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"event-scheduler", OPT_EVENT_EXECUTOR, "Enable/disable the event scheduler.", (gptr*) &opt_event_executor, (gptr*) &opt_event_executor, 0, GET_BOOL, NO_ARG, - 1/*default*/, 0/*min-value*/, 1/*max-value*/, 0, 0, 0}, + 0/*default*/, 0/*min-value*/, 1/*max-value*/, 0, 0, 0}, {"exit-info", 'T', "Used for debugging; Use at your own risk!", 0, 0, 0, GET_LONG, OPT_ARG, 0, 0, 0, 0, 0, 0}, {"external-locking", OPT_USE_LOCKING, "Use system (external) locking. With this option enabled you can run myisamchk to test (not repair) tables while the MySQL server is running.", diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index 5804125ed68..0b8023dc4a5 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -5741,3 +5741,9 @@ ER_EVENT_OPEN_TABLE_FAILED eng "Failed to open mysql.event" ER_EVENT_NEITHER_M_EXPR_NOR_M_AT eng "No datetime expression provided" +ER_EVENT_COL_COUNT_DOESNT_MATCH + eng "Column count of %s.%s is wrong. Table probably corrupted" +ER_EVENT_CANNOT_LOAD_FROM_TABLE + eng "Cannot load from mysql.event. Table probably corrupted" +ER_EVENT_CANNOT_DELETE + eng "Failed to delete the event from mysql.event" diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index b44a5c41020..57ac2d3bb55 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3689,21 +3689,8 @@ end_with_restore_list: int result; uint create_options= lex->create_info.options; res= (result= evex_create_event(thd, lex->et, create_options)); - switch (result) { - case EVEX_OK: + if (result == EVEX_OK) send_ok(thd, 1); - break; - case EVEX_WRITE_ROW_FAILED: - my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), lex->et->m_name.str); - break; - case EVEX_NO_DB_ERROR: - my_error(ER_BAD_DB_ERROR, MYF(0), lex->et->m_db.str); - break; - default: - //includes EVEX_PARSE_ERROR - my_error(ER_EVENT_STORE_FAILED, MYF(0), lex->et->m_name.str); - break; - } /* lex->unit.cleanup() is called outside, no need to call it here */ delete lex->et; lex->et= 0; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index b16436dd74a..082eee98e57 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1312,6 +1312,10 @@ create: Lex->sql_command = SQLCOM_CREATE_USER; } | CREATE EVENT_SYM opt_if_not_exists sp_name + /* + BE CAREFUL when you add a new rule to update the block where + YYTHD->client_capabilities is set back to original value + */ { LEX *lex=Lex; @@ -1350,13 +1354,20 @@ create: ev_comment DO_SYM ev_sql_stmt { + /* + Restore flag if it was cleared above + $1 - CREATE + $2 - EVENT_SYM + $3 - opt_if_not_exists + $4 - sp_name + $5 - the block above + */ + YYTHD->client_capabilities |= $5; + /* sql_command is set here because some rules in ev_sql_stmt can overwrite it */ - // Restore flag if it was cleared above - YYTHD->client_capabilities |= $5; - Lex->sql_command= SQLCOM_CREATE_EVENT; } ; @@ -4192,6 +4203,10 @@ alter: view_list_opt AS view_select view_check_option {} | ALTER EVENT_SYM sp_name + /* + BE CAREFUL when you add a new rule to update the block where + YYTHD->client_capabilities is set back to original value + */ { LEX *lex=Lex; event_timed *et; @@ -4233,11 +4248,18 @@ alter: ev_comment ev_opt_sql_stmt { + /* + $1 - ALTER + $2 - EVENT_SYM + $3 - sp_name + $4 - the block above + */ + YYTHD->client_capabilities |= $4; + /* sql_command is set here because some rules in ev_sql_stmt can overwrite it */ - YYTHD->client_capabilities |= $3; Lex->sql_command= SQLCOM_ALTER_EVENT; } ; From a4bcbd51d901d455c58a934284e00359edbb80c8 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 7 Dec 2005 22:29:00 +0100 Subject: [PATCH 09/28] WL #1034 updates after review sql/event.cc: - fix TODO (remove things already done) - check the length of the event's name and body during creation and report an error if longer than what can be fit into mysql.event (nothing like non-strict mode here) - report to sql_parse.cc and error when open table failed, otherwise send_ok() was being called and the error have become an warning. - update function documentation a bit - evex_db_find_routine_aux returns 0 and not EVEX_OK sql/event_executor.cc: - CS changes to definitions of the main and worker thread routines - reorder code a bit to prevent crashes because of reading of already freed data -> first wait all events to finish their work, namely all worker threads to finish, and then destroy in-memory structures - more error checking and error reporting at the place of failure. sql/event_priv.h: code simplifying macro sql/event_timed.cc: CS cosmetics --- sql/event.cc | 151 +++++++++++++++++--------------------- sql/event_executor.cc | 166 +++++++++++++++++++++--------------------- sql/event_priv.h | 3 + sql/event_timed.cc | 20 +++-- 4 files changed, 168 insertions(+), 172 deletions(-) diff --git a/sql/event.cc b/sql/event.cc index 36180adb90f..56e93ac7c33 100644 --- a/sql/event.cc +++ b/sql/event.cc @@ -27,8 +27,6 @@ - Use timestamps instead of datetime. - - Don't use SP's functionality for opening and closing of tables - - CREATE EVENT should not go into binary log! Does it now? The SQL statements issued by the EVENT are replicated. I have an idea how to solve the problem at failover. So the status field @@ -44,31 +42,16 @@ ENABLED to DISABLED status change and this is safe for replicating. As well an event may be deleted which is also safe for RBR. - - Add a lock and use it for guarding access to events_array dynamic array. - - - Add checks everywhere where new instance of THD is created. NULL can be - returned and this will crash the server. The server will crash probably - later but should not be in this code! Add a global variable, and a lock - to guard it, that will specify an error in a worker thread so preventing - new threads from being spawned. - - Maybe move all allocations during parsing to evex_mem_root thus saving double parsing in evex_create_event! - If the server is killed (stopping) try to kill executing events.. - What happens if one renames an event in the DB while it is in memory? - Or even deleting it? - - - created & modified in the table should be UTC? - - - Add a lock to event_timed to serialize execution of an event - do not - allow parallel executions. Hmm, however how last_executed is marked - then? The call to event_timed::mark_last_executed() must be moved to - event_timed::execute()? - + Or even deleting it? + - Consider using conditional variable when doing shutdown instead of - waiting some time (tries < 5). + waiting till all worker threads end. - Make event_timed::get_show_create_event() work - Add function documentation whenever needed. - Add logging to file @@ -77,7 +60,7 @@ Warning: - For now parallel execution is not possible because the same sp_head cannot be executed few times!!! There is still no lock attached to particular event. - */ +*/ @@ -216,17 +199,17 @@ TABLE *evex_open_event_table(THD *thd, enum thr_lock_type lock_type) table TABLE object for open mysql.event table. RETURN VALUE - SP_OK - Routine found + 0 - Routine found SP_KEY_NOT_FOUND- No routine with given name */ int evex_db_find_routine_aux(THD *thd, const LEX_STRING dbname, - const LEX_STRING rname, TABLE *table) + const LEX_STRING ev_name, TABLE *table) { byte key[MAX_KEY_LENGTH]; // db, name, optional key length type DBUG_ENTER("evex_db_find_routine_aux"); - DBUG_PRINT("enter", ("name: %.*s", rname.length, rname.str)); + DBUG_PRINT("enter", ("name: %.*s", ev_name.length, ev_name.str)); /* Create key to find row. We have to use field->store() to be able to @@ -235,16 +218,16 @@ evex_db_find_routine_aux(THD *thd, const LEX_STRING dbname, 'db' and 'name' and the first key is the primary key over the same fields. */ - if (rname.length > table->field[1]->field_length) - DBUG_RETURN(SP_KEY_NOT_FOUND); + if (ev_name.length > table->field[1]->field_length) + DBUG_RETURN(EVEX_KEY_NOT_FOUND); table->field[0]->store(dbname.str, dbname.length, &my_charset_bin); - table->field[1]->store(rname.str, rname.length, &my_charset_bin); + table->field[1]->store(ev_name.str, ev_name.length, &my_charset_bin); key_copy(key, table->record[0], table->key_info, table->key_info->key_length); if (table->file->index_read_idx(table->record[0], 0, key, - table->key_info->key_length,HA_READ_KEY_EXACT)) - DBUG_RETURN(SP_KEY_NOT_FOUND); + table->key_info->key_length,HA_READ_KEY_EXACT)) + DBUG_RETURN(EVEX_KEY_NOT_FOUND); DBUG_RETURN(0); } @@ -394,19 +377,18 @@ db_create_event(THD *thd, event_timed *et) restore_record(table, s->default_values); // Get default values for fields -/* TODO : Uncomment these and add handling in sql_parse.cc or here - if (sp->m_name.length > table->field[MYSQL_PROC_FIELD_NAME]->field_length) + if (et->m_name.length > table->field[EVEX_FIELD_NAME]->field_length) { - ret= SP_BAD_IDENTIFIER; - goto done; + my_error(ER_TOO_LONG_IDENT, MYF(0), et->m_name.str); + goto err; } - if (sp->m_body.length > table->field[MYSQL_PROC_FIELD_BODY]->field_length) + if (et->m_body.length > table->field[EVEX_FIELD_BODY]->field_length) { - ret= SP_BODY_TOO_LONG; - goto done; + my_error(ER_TOO_LONG_BODY, MYF(0), et->m_name.str); + goto err; } -*/ + if (!(et->m_expr) && !(et->m_execute_at.year)) { DBUG_PRINT("error", ("neither m_expr nor m_execute_as are set!")); @@ -434,7 +416,8 @@ db_create_event(THD *thd, event_timed *et) my_error(ER_EVENT_STORE_FAILED, MYF(0), et->m_name.str); goto err; } - else if (mysql_bin_log.is_open()) + + if (mysql_bin_log.is_open()) { thd->clear_error(); /* Such a statement can always go directly to binlog, no trans cache */ @@ -472,20 +455,20 @@ static int db_update_event(THD *thd, sp_name *name, event_timed *et) { TABLE *table; - int ret; + int ret= EVEX_OPEN_TABLE_FAILED; DBUG_ENTER("db_update_event"); DBUG_PRINT("enter", ("name: %.*s", et->m_name.length, et->m_name.str)); if (name) DBUG_PRINT("enter", ("rename to: %.*s", name->m_name.length, name->m_name.str)); - // Todo: Handle in sql_prepare.cc SP_OPEN_TABLE_FAILED if (!(table= evex_open_event_table(thd, TL_WRITE))) { my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0)); goto err; } - if (evex_db_find_routine_aux(thd, et->m_db, et->m_name, table) == SP_KEY_NOT_FOUND) + if (EVEX_KEY_NOT_FOUND == evex_db_find_routine_aux(thd, et->m_db, et->m_name, + table)) { my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), et->m_name.str); goto err; @@ -526,8 +509,21 @@ err: /* - Use sp_name for look up, return in **ett if found + Looks for a named event in mysql.event and in case of success returns + an object will data loaded from the table. + + SYNOPSIS + db_find_event() + thd THD + name the name of the event to find + ett event's data if event is found + tbl TABLE object to use when not NULL + + NOTES + 1) Use sp_name for look up, return in **ett if found + 2) tbl is not closed at exit */ + static int db_find_event(THD *thd, sp_name *name, event_timed **ett, TABLE *tbl) { @@ -581,6 +577,22 @@ done: } +/* + Looks for a named event in mysql.event and then loads it from + the table, compiles it and insert it into the cache. + + SYNOPSIS + evex_load_and_compile_event() + thd THD + spn the name of the event to alter + use_lock whether to obtain a lock on LOCK_event_arrays or not + + RETURN VALUE + 0 - OK + < 0 - error (in this case underlying functions call my_error()). + +*/ + static int evex_load_and_compile_event(THD * thd, sp_name *spn, bool use_lock) { @@ -727,7 +739,7 @@ done: /* - Exported functions follow + -= Exported functions follow =- */ /* @@ -754,15 +766,6 @@ evex_create_event(THD *thd, event_timed *et, uint create_options) DBUG_PRINT("enter", ("name: %*s options:%d", et->m_name.length, et->m_name.str, create_options)); -/* - VOID(pthread_mutex_lock(&LOCK_evex_running)); - if (!evex_is_running) - // TODO: put an warning to the user here. - // Is it needed? (Andrey, 051129) - {} - VOID(pthread_mutex_unlock(&LOCK_evex_running)); -*/ - if ((ret = db_create_event(thd, et)) == EVEX_WRITE_ROW_FAILED && (create_options & HA_LEX_CREATE_IF_NOT_EXISTS)) { @@ -821,13 +824,6 @@ evex_update_event(THD *thd, sp_name *name, event_timed *et) DBUG_ENTER("evex_update_event"); DBUG_PRINT("enter", ("name: %*s", et->m_name.length, et->m_name.str)); -/* - VOID(pthread_mutex_lock(&LOCK_evex_running)); - if (!evex_is_running) - // put an warning to the user here - {} - VOID(pthread_mutex_unlock(&LOCK_evex_running)); -*/ /* db_update_event() opens & closes the table to prevent crash later in the code when loading and compiling the new definition @@ -837,17 +833,8 @@ evex_update_event(THD *thd, sp_name *name, event_timed *et) VOID(pthread_mutex_lock(&LOCK_evex_running)); if (!evex_is_running) - { - // not running - therefore no memory structures - VOID(pthread_mutex_unlock(&LOCK_evex_running)); - goto done; - } - VOID(pthread_mutex_unlock(&LOCK_evex_running)); + UNLOCK_MUTEX_AND_BAIL_OUT(LOCK_evex_running, done); - /* - It is possible that 2 (or 1) pass(es) won't find the event in memory. - The reason is that DISABLED events are not cached. - */ VOID(pthread_mutex_lock(&LOCK_event_arrays)); evex_remove_from_cache(&et->m_db, &et->m_name, false); if (et->m_status == MYSQL_EVENT_ENABLED) @@ -860,9 +847,14 @@ evex_update_event(THD *thd, sp_name *name, event_timed *et) delete spn; } VOID(pthread_mutex_unlock(&LOCK_event_arrays)); + VOID(pthread_mutex_unlock(&LOCK_evex_running)); + + /* + It is possible that 2 (or 1) pass(es) won't find the event in memory. + The reason is that DISABLED events are not cached. + */ done: - DBUG_RETURN(ret); } @@ -882,24 +874,17 @@ int evex_drop_event(THD *thd, event_timed *et, bool drop_if_exists) { TABLE *table; - int ret; + int ret= EVEX_OPEN_TABLE_FAILED; bool opened; DBUG_ENTER("evex_drop_event"); -/* - VOID(pthread_mutex_lock(&LOCK_evex_running)); - if (!evex_is_running) - // put an warning to the user here - {} - VOID(pthread_mutex_unlock(&LOCK_evex_running)); -*/ -//// if (!(table= evex_open_event_table(thd, TL_WRITE))) - DBUG_RETURN(SP_OPEN_TABLE_FAILED); + { + my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0)); + goto done; + } - ret= evex_db_find_routine_aux(thd, et->m_db, et->m_name, table); - - if (ret == EVEX_OK) + if (!(ret= evex_db_find_routine_aux(thd, et->m_db, et->m_name, table))) { if (ret= table->file->delete_row(table->record[0])) { diff --git a/sql/event_executor.cc b/sql/event_executor.cc index 763f440df64..90c08d045db 100644 --- a/sql/event_executor.cc +++ b/sql/event_executor.cc @@ -21,7 +21,7 @@ #define DBUG_FAULTY_THR2 -static uint workers_count; +extern ulong thread_created; pthread_mutex_t LOCK_event_arrays, @@ -33,10 +33,8 @@ bool evex_is_running= false; ulong opt_event_executor; my_bool event_executor_running_global_var= false; - -extern ulong thread_created; - static my_bool evex_mutexes_initted= false; +static uint workers_count; static int evex_load_events_from_db(THD *thd); @@ -48,8 +46,11 @@ evex_load_events_from_db(THD *thd); the main thread or not. */ -pthread_handler_t event_executor_worker(void *arg); -pthread_handler_t event_executor_main(void *arg); +pthread_handler_t +event_executor_worker(void *arg); + +pthread_handler_t +event_executor_main(void *arg); static void evex_init_mutexes() @@ -142,8 +143,8 @@ init_event_thread(THD* thd) DBUG_RETURN(0); } - -pthread_handler_t event_executor_main(void *arg) +pthread_handler_t +event_executor_main(void *arg) { THD *thd; /* needs to be first for thread_stack */ ulonglong iter_num= 0; @@ -152,13 +153,10 @@ pthread_handler_t event_executor_main(void *arg) DBUG_ENTER("event_executor_main"); DBUG_PRINT("event_executor_main", ("EVEX thread started")); - VOID(pthread_mutex_lock(&LOCK_evex_running)); - evex_is_running= true; - event_executor_running_global_var= opt_event_executor; - VOID(pthread_mutex_unlock(&LOCK_evex_running)); // init memory root init_alloc_root(&evex_mem_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC); + // needs to call my_thread_init(), otherwise we get a coredump in DBUG_ stuff my_thread_init(); @@ -196,6 +194,15 @@ pthread_handler_t event_executor_main(void *arg) VOID(my_init_dynamic_array(&evex_executing_queue, sizeof(event_timed *), 50, 100)); VOID(pthread_mutex_unlock(&LOCK_event_arrays)); + /* + eventually manifest that we are running, not to crashe because of + usage of non-initialized memory structures. + */ + VOID(pthread_mutex_lock(&LOCK_evex_running)); + evex_is_running= true; + event_executor_running_global_var= opt_event_executor; + VOID(pthread_mutex_unlock(&LOCK_evex_running)); + if (evex_load_events_from_db(thd)) goto err; @@ -208,7 +215,6 @@ pthread_handler_t event_executor_main(void *arg) my_ulonglong cnt; DBUG_PRINT("info", ("EVEX External Loop %d", ++cnt)); -// sql_print_information("[EVEX] External Loop!"); thd->proc_info = "Sleeping"; my_sleep(1000000);// sleep 1s if (!event_executor_running_global_var) @@ -216,16 +222,13 @@ pthread_handler_t event_executor_main(void *arg) time(&now); my_tz_UTC->gmt_sec_to_TIME(&time_now, now); - VOID(pthread_mutex_lock(&LOCK_event_arrays)); for (i= 0; (i < evex_executing_queue.elements) && !thd->killed; ++i) { - event_timed **p_et=dynamic_element(&evex_executing_queue,i,event_timed**); - event_timed *et= *p_et; -// sql_print_information("[EVEX] External Loop 2!"); - + event_timed *et= *dynamic_element(&evex_executing_queue,i,event_timed**); +// printf("%llu\n", TIME_to_ulonglong_datetime(&et->m_execute_at)); if (!event_executor_running_global_var) - break;// soon we will do only continue (see the code a bit above) + break; thd->proc_info = "Iterating"; THD_CHECK_SENTRY(thd); @@ -233,7 +236,7 @@ pthread_handler_t event_executor_main(void *arg) if this is the first event which is after time_now then no more need to iterate over more elements since the array is sorted. */ - if (et->m_execute_at.year && + if (et->m_execute_at.year > 1969 && my_time_compare(&time_now, &et->m_execute_at) == -1) break; @@ -250,8 +253,7 @@ pthread_handler_t event_executor_main(void *arg) if (pthread_create(&th, NULL, event_executor_worker, (void*)et)) { sql_print_error("Problem while trying to create a thread"); - VOID(pthread_mutex_unlock(&LOCK_event_arrays)); - goto err; // for now finish execution of the Executor + UNLOCK_MUTEX_AND_BAIL_OUT(LOCK_event_arrays, err); } #else event_executor_worker((void *) et); @@ -272,12 +274,12 @@ pthread_handler_t event_executor_main(void *arg) j= 0; while (j < i && j < evex_executing_queue.elements) { - event_timed **p_et= dynamic_element(&evex_executing_queue, j, event_timed**); - event_timed *et= *p_et; + event_timed *et= *dynamic_element(&evex_executing_queue, j, event_timed**); if (et->m_flags & EVENT_EXEC_NO_MORE || et->m_status == MYSQL_EVENT_DISABLED) { delete_dynamic_element(&evex_executing_queue, j); - DBUG_PRINT("", ("DELETING FROM EXECUTION QUEUE [%s.%s]",et->m_db.str, et->m_name.str)); + DBUG_PRINT("EVEX main thread", ("DELETING FROM EXECUTION QUEUE [%s.%s]", + et->m_db.str, et->m_name.str)); // nulling the position, will delete later if (et->m_dropped) { @@ -301,29 +303,44 @@ pthread_handler_t event_executor_main(void *arg) ); VOID(pthread_mutex_unlock(&LOCK_event_arrays)); - }// while (!thd->killed) + } err: + // First manifest that this thread does not work and then destroy VOID(pthread_mutex_lock(&LOCK_evex_running)); evex_is_running= false; VOID(pthread_mutex_unlock(&LOCK_evex_running)); sql_print_information("Event executor stopping"); - // LEX_STRINGs reside in the memory root and will be destroyed with it. - // Hence no need of delete but only freeing of SP - for (i=0; i < events_array.elements; ++i) - { - event_timed *et= dynamic_element(&events_array, i, event_timed*); - et->free_sp(); - } - // TODO Andrey: USE lock here! + + /* + TODO: A better will be with a conditional variable + */ + /* + Read workers_count without lock, no need for locking. + In the worst case we have to wait 1sec more. + */ + while (workers_count) + my_sleep(1000000);// 1s + + /* + LEX_STRINGs reside in the memory root and will be destroyed with it. + Hence no need of delete but only freeing of SP + */ + for (i= 0; i < events_array.elements; ++i) + dynamic_element(&events_array, i, event_timed*)->free_sp(); + + VOID(pthread_mutex_lock(&LOCK_event_arrays)); + // No need to use lock here if EVEX is not running but anyway delete_dynamic(&evex_executing_queue); delete_dynamic(&events_array); + VOID(pthread_mutex_unlock(&LOCK_evex_running)); thd->proc_info = "Clearing"; DBUG_ASSERT(thd->net.buff != 0); net_end(&thd->net); // destructor will not free it, because we are weird THD_CHECK_SENTRY(thd); + pthread_mutex_lock(&LOCK_thread_count); thread_count--; thread_running--; @@ -331,28 +348,6 @@ err: delete thd; pthread_mutex_unlock(&LOCK_thread_count); - /* - sleeping some time may help not crash the server. sleeping - is done to wait for spawned threads to finish. - - TODO: A better will be with a conditional variable - */ - { - uint tries= 0; - while (tries++ < 5) - { - VOID(pthread_mutex_lock(&LOCK_workers_count)); - if (!workers_count) - { - VOID(pthread_mutex_unlock(&LOCK_workers_count)); - break; - } - VOID(pthread_mutex_unlock(&LOCK_workers_count)); - DBUG_PRINT("info", ("Sleep %d", tries)); - my_sleep(1000000 * tries);// 1s - } - DBUG_PRINT("info", ("Maybe now it is ok to kill the thread and evex MRoot")); - } err_no_thd: VOID(pthread_mutex_lock(&LOCK_evex_running)); @@ -362,30 +357,25 @@ err_no_thd: free_root(&evex_mem_root, MYF(0)); sql_print_information("Event executor stopped"); -// shutdown_events(); - my_thread_end(); pthread_exit(0); - DBUG_RETURN(0); // Can't return anything here + DBUG_RETURN(0);// Can't return anything here } -pthread_handler_t event_executor_worker(void *event_void) +pthread_handler_t +event_executor_worker(void *event_void) { THD *thd; /* needs to be first for thread_stack */ - List empty_item_list; event_timed *event = (event_timed *) event_void; - MEM_ROOT mem_root; + MEM_ROOT worker_mem_root; DBUG_ENTER("event_executor_worker"); VOID(pthread_mutex_lock(&LOCK_workers_count)); ++workers_count; VOID(pthread_mutex_unlock(&LOCK_workers_count)); - init_alloc_root(&mem_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC); - - //we pass this empty list as parameter to the SP_HEAD of the event - empty_item_list.empty(); + init_alloc_root(&worker_mem_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC); my_thread_init(); @@ -395,8 +385,10 @@ pthread_handler_t event_executor_worker(void *event_void) goto err_no_thd; } thd->thread_stack = (char*)&thd; // remember where our stack is - thd->mem_root= &mem_root; + thd->mem_root= &worker_mem_root; + pthread_detach(pthread_self()); + if (init_event_thread(thd)) goto err; @@ -425,7 +417,7 @@ pthread_handler_t event_executor_worker(void *event_void) my_TIME_to_str(&event->m_execute_at, exec_time); DBUG_PRINT("info", (" EVEX EXECUTING event for event %s.%s [EXPR:%d][EXECUTE_AT:%s]", event->m_db.str, event->m_name.str,(int) event->m_expr, exec_time)); sql_print_information(" EVEX EXECUTING event for event %s.%s [EXPR:%d][EXECUTE_AT:%s]", event->m_db.str, event->m_name.str,(int) event->m_expr, exec_time); - ret= event->execute(thd, &mem_root); + ret= event->execute(thd, &worker_mem_root); sql_print_information(" EVEX EXECUTED event for event %s.%s [EXPR:%d][EXECUTE_AT:%s]. RetCode=%d", event->m_db.str, event->m_name.str,(int) event->m_expr, exec_time, ret); DBUG_PRINT("info", (" EVEX EXECUTED event for event %s.%s [EXPR:%d][EXECUTE_AT:%s]", event->m_db.str, event->m_name.str,(int) event->m_expr, exec_time)); } @@ -454,8 +446,7 @@ err: err_no_thd: - free_root(&mem_root, MYF(0)); -// sql_print_information(" Worker thread exiting"); + free_root(&worker_mem_root, MYF(0)); VOID(pthread_mutex_lock(&LOCK_workers_count)); --workers_count; @@ -496,29 +487,38 @@ evex_load_events_from_db(THD *thd) } DBUG_PRINT("evex_load_events_from_db", ("Loading event from row.")); - if (et->load_from_row(&evex_mem_root, table)) - //error loading! - continue; + if ((ret= et->load_from_row(&evex_mem_root, table))) + { + sql_print_error("Error while loading from mysql.event. " + "Table probably corrupted"); + goto end; + } DBUG_PRINT("evex_load_events_from_db", ("Event %s loaded from row. Time to compile", et->m_name.str)); - if (et->compile(thd, &evex_mem_root)) - //problem during compile - continue; + if ((ret= et->compile(thd, &evex_mem_root))) + { + sql_print_error("Error while compiling %s.%s. Aborting load.", + et->m_db.str, et->m_name.str); + goto end; + } // let's find when to be executed et->compute_next_execution_time(); DBUG_PRINT("evex_load_events_from_db", ("Adding %s to the executor list.", et->m_name.str)); VOID(push_dynamic(&events_array,(gptr) et)); - // we always add at the end so the number of elements - 1 is the place - // in the buffer + /* + We always add at the end so the number of elements - 1 is the place + in the buffer. + DYNAMIC_ARRAY copies the object bit by bit so we have a hollow copy + in event_array. We don't need the original therefore we delete it. + */ et_copy= dynamic_element(&events_array, events_array.elements - 1, - event_timed*); + event_timed*); VOID(push_dynamic(&evex_executing_queue,(gptr) &et_copy)); et->m_free_sphead_on_delete= false; - DBUG_PRINT("info", ("")); delete et; } end_read_record(&read_record_info); @@ -536,8 +536,7 @@ evex_load_events_from_db(THD *thd) end: close_thread_tables(thd); - DBUG_PRINT("evex_load_events_from_db", - ("Events loaded from DB. Status code %d", ret)); + DBUG_PRINT("info", ("Finishing with status code %d", ret)); DBUG_RETURN(ret); } @@ -547,7 +546,8 @@ bool sys_var_event_executor::update(THD *thd, set_var *var) { // here start the thread if not running. VOID(pthread_mutex_lock(&LOCK_evex_running)); - if ((my_bool) var->save_result.ulong_value && !evex_is_running) { + if ((my_bool) var->save_result.ulong_value && !evex_is_running) + { VOID(pthread_mutex_unlock(&LOCK_evex_running)); init_events(); } else diff --git a/sql/event_priv.h b/sql/event_priv.h index 73618d1b44a..f377a012e19 100644 --- a/sql/event_priv.h +++ b/sql/event_priv.h @@ -18,6 +18,9 @@ #define _EVENT_PRIV_H_ +#define UNLOCK_MUTEX_AND_BAIL_OUT(__mutex, __label) \ + { VOID(pthread_mutex_unlock(&__mutex)); goto __label; } + enum { EVEX_FIELD_DB = 0, diff --git a/sql/event_timed.cc b/sql/event_timed.cc index 45314dc9dbc..b986e650a13 100644 --- a/sql/event_timed.cc +++ b/sql/event_timed.cc @@ -290,7 +290,7 @@ event_timed::init_ends(THD *thd, Item *ends) if (ends->fix_fields(thd, &ends)) DBUG_RETURN(EVEX_PARSE_ERROR); - // the field was already fixed in init_ends + // the field was already fixed in init_ends if ((not_used= ends->get_date(<ime, TIME_NO_ZERO_DATE))) DBUG_RETURN(EVEX_BAD_PARAMS); @@ -537,10 +537,19 @@ event_timed::compute_next_execution_time() my_tz_UTC->gmt_sec_to_TIME(&time_now, now); /* sql_print_information("[%s.%s]", m_db.str, m_name.str); - sql_print_information("time_now : [%d-%d-%d %d:%d:%d ]", time_now.year, time_now.month, time_now.day, time_now.hour, time_now.minute, time_now.second); - sql_print_information("m_starts : [%d-%d-%d %d:%d:%d ]", m_starts.year, m_starts.month, m_starts.day, m_starts.hour, m_starts.minute, m_starts.second); - sql_print_information("m_ends : [%d-%d-%d %d:%d:%d ]", m_ends.year, m_ends.month, m_ends.day, m_ends.hour, m_ends.minute, m_ends.second); - sql_print_information("m_last_ex: [%d-%d-%d %d:%d:%d ]", m_last_executed.year, m_last_executed.month, m_last_executed.day, m_last_executed.hour, m_last_executed.minute, m_last_executed.second); + sql_print_information("time_now : [%d-%d-%d %d:%d:%d ]", + time_now.year, time_now.month, time_now.day, + time_now.hour, time_now.minute, time_now.second); + sql_print_information("m_starts : [%d-%d-%d %d:%d:%d ]", m_starts.year, + m_starts.month, m_starts.day, m_starts.hour, + m_starts.minute, m_starts.second); + sql_print_information("m_ends : [%d-%d-%d %d:%d:%d ]", m_ends.year, + m_ends.month, m_ends.day, m_ends.hour, + m_ends.minute, m_ends.second); + sql_print_information("m_last_ex: [%d-%d-%d %d:%d:%d ]", m_last_executed.year, + m_last_executed.month, m_last_executed.day, + m_last_executed.hour, m_last_executed.minute, + m_last_executed.second); */ //if time_now is after m_ends don't execute anymore if (m_ends.year && (tmp= my_time_compare(&m_ends, &time_now)) == -1) @@ -702,7 +711,6 @@ event_timed::mark_last_executed() bool event_timed::drop(THD *thd) { - return (bool) evex_drop_event(thd, this, false); } From faa8995e7a4b9504a7f02ef69761f5064f388127 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 8 Dec 2005 00:17:05 +0100 Subject: [PATCH 10/28] WL #1034 updates after review (strip m_ as prefix from member variables' names) sql/event.cc: - change copyright years - remove m_ prefix from member variables (I liked m_) - reorder parameter in evex_update_event() sql/event.h: - change copyright years - remove m_ prefix from member variables - declare some member variables as private (were public) - delete 0 is valid in C++ therefore don't embrace with if() sql/event_executor.cc: - executor => scheduler in messages - fix a bug introduced in last commit -> typo in a mutex name - remove m_ prefix from member variables sql/event_priv.h: - change copyright years sql/event_timed.cc: - change copyright years - strip m_ as prefix from member variables' names sql/sql_parse.cc: - strip m_ as prefix from member variables' names sql/sql_yacc.yy: - strip m_ as prefix from member variables names --- sql/event.cc | 153 +++++++-------- sql/event.h | 81 ++++---- sql/event_executor.cc | 58 +++--- sql/event_priv.h | 2 +- sql/event_timed.cc | 419 +++++++++++++++++++++--------------------- sql/sql_parse.cc | 48 +++-- sql/sql_yacc.yy | 10 +- 7 files changed, 388 insertions(+), 383 deletions(-) diff --git a/sql/event.cc b/sql/event.cc index 56e93ac7c33..5e01dc1c72a 100644 --- a/sql/event.cc +++ b/sql/event.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2000-2003 MySQL AB +/* Copyright (C) 2004-2005 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 @@ -143,7 +143,7 @@ my_time_compare(TIME *a, TIME *b) inline int event_timed_compare(event_timed **a, event_timed **b) { - return my_time_compare(&(*a)->m_execute_at, &(*b)->m_execute_at); + return my_time_compare(&(*a)->execute_at, &(*b)->execute_at); } @@ -258,55 +258,55 @@ evex_fill_row(THD *thd, TABLE *table, event_timed *et, my_bool is_update) DBUG_RETURN(EVEX_GET_FIELD_FAILED); } - DBUG_PRINT("info", ("m_db.len=%d",et->m_db.length)); - DBUG_PRINT("info", ("m_name.len=%d",et->m_name.length)); + DBUG_PRINT("info", ("dbname.len=%d",et->dbname.length)); + DBUG_PRINT("info", ("name.len=%d",et->name.length)); table->field[EVEX_FIELD_DB]-> - store(et->m_db.str, et->m_db.length, system_charset_info); + store(et->dbname.str, et->dbname.length, system_charset_info); table->field[EVEX_FIELD_NAME]-> - store(et->m_name.str, et->m_name.length, system_charset_info); + store(et->name.str, et->name.length, system_charset_info); table->field[EVEX_FIELD_ON_COMPLETION]->set_notnull(); - table->field[EVEX_FIELD_ON_COMPLETION]->store((longlong)et->m_on_completion); + table->field[EVEX_FIELD_ON_COMPLETION]->store((longlong)et->on_completion); table->field[EVEX_FIELD_STATUS]->set_notnull(); - table->field[EVEX_FIELD_STATUS]->store((longlong)et->m_status); - et->m_status_changed= false; + table->field[EVEX_FIELD_STATUS]->store((longlong)et->status); +// et->status_changed= false; // ToDo: Andrey. How to use users current charset? - if (et->m_body.str) + if (et->body.str) table->field[EVEX_FIELD_BODY]-> - store(et->m_body.str, et->m_body.length, system_charset_info); + store(et->body.str, et->body.length, system_charset_info); - if (et->m_starts.year) + if (et->starts.year) { table->field[EVEX_FIELD_STARTS]->set_notnull();// set NULL flag to OFF - table->field[EVEX_FIELD_STARTS]->store_time(&et->m_starts,MYSQL_TIMESTAMP_DATETIME); + table->field[EVEX_FIELD_STARTS]->store_time(&et->starts, MYSQL_TIMESTAMP_DATETIME); } - if (et->m_ends.year) + if (et->ends.year) { table->field[EVEX_FIELD_ENDS]->set_notnull(); - table->field[EVEX_FIELD_ENDS]->store_time(&et->m_ends, MYSQL_TIMESTAMP_DATETIME); + table->field[EVEX_FIELD_ENDS]->store_time(&et->ends, MYSQL_TIMESTAMP_DATETIME); } - if (et->m_expr) + if (et->expression) { table->field[EVEX_FIELD_INTERVAL_EXPR]->set_notnull(); - table->field[EVEX_FIELD_INTERVAL_EXPR]->store((longlong)et->m_expr); + table->field[EVEX_FIELD_INTERVAL_EXPR]->store((longlong)et->expression); table->field[EVEX_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[EVEX_FIELD_TRANSIENT_INTERVAL]->store((longlong)et->m_interval + 1); + table->field[EVEX_FIELD_TRANSIENT_INTERVAL]->store((longlong)et->interval + 1); } - else if (et->m_execute_at.year) + else if (et->execute_at.year) { // fix_fields already called in init_execute_at table->field[EVEX_FIELD_EXECUTE_AT]->set_notnull(); - table->field[EVEX_FIELD_EXECUTE_AT]->store_time(&et->m_execute_at, + table->field[EVEX_FIELD_EXECUTE_AT]->store_time(&et->execute_at, MYSQL_TIMESTAMP_DATETIME); //this will make it NULL because we don't call set_notnull @@ -321,9 +321,9 @@ evex_fill_row(THD *thd, TABLE *table, event_timed *et, my_bool is_update) ((Field_timestamp *)table->field[EVEX_FIELD_MODIFIED])->set_time(); - if ((et->m_comment).length) + if (et->comment.length) table->field[EVEX_FIELD_COMMENT]-> - store((et->m_comment).str, (et->m_comment).length, system_charset_info); + store(et->comment.str, et->comment.length, system_charset_info); DBUG_RETURN(0); } @@ -351,7 +351,7 @@ db_create_event(THD *thd, event_timed *et) char olddb[128]; bool dbchanged= false; DBUG_ENTER("db_create_event"); - DBUG_PRINT("enter", ("name: %.*s", et->m_name.length, et->m_name.str)); + DBUG_PRINT("enter", ("name: %.*s", et->name.length, et->name.str)); DBUG_PRINT("info", ("open mysql.event for update")); @@ -362,14 +362,14 @@ db_create_event(THD *thd, event_timed *et) } DBUG_PRINT("info", ("check existance of an event with the same name")); - if (!evex_db_find_routine_aux(thd, et->m_db, et->m_name, table)) + if (!evex_db_find_routine_aux(thd, et->dbname, et->name, table)) { - my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), et->m_name.str); + 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->m_db.str,olddb, sizeof(olddb),0, &dbchanged))) + 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; @@ -378,31 +378,31 @@ db_create_event(THD *thd, event_timed *et) restore_record(table, s->default_values); // Get default values for fields - if (et->m_name.length > table->field[EVEX_FIELD_NAME]->field_length) + if (et->name.length > table->field[EVEX_FIELD_NAME]->field_length) { - my_error(ER_TOO_LONG_IDENT, MYF(0), et->m_name.str); + my_error(ER_TOO_LONG_IDENT, MYF(0), et->name.str); goto err; } - if (et->m_body.length > table->field[EVEX_FIELD_BODY]->field_length) + if (et->body.length > table->field[EVEX_FIELD_BODY]->field_length) { - my_error(ER_TOO_LONG_BODY, MYF(0), et->m_name.str); + my_error(ER_TOO_LONG_BODY, MYF(0), et->name.str); goto err; } - if (!(et->m_expr) && !(et->m_execute_at.year)) + if (!(et->expression) && !(et->execute_at.year)) { - DBUG_PRINT("error", ("neither m_expr nor m_execute_as are set!")); + DBUG_PRINT("error", ("neither expression nor execute_at are set!")); my_error(ER_EVENT_NEITHER_M_EXPR_NOR_M_AT, MYF(0)); goto err; } - strxmov(definer, et->m_definer_user.str, "@", et->m_definer_host.str, NullS); + strxmov(definer, et->definer_user.str, "@", et->definer_host.str, NullS); if (table->field[EVEX_FIELD_DEFINER]-> - store(definer, et->m_definer_user.length + 1 + et->m_definer_host.length, + store(definer, et->definer_user.length + 1 + et->definer_host.length, system_charset_info)) { - my_error(ER_EVENT_STORE_FAILED, MYF(0), et->m_name.str); + my_error(ER_EVENT_STORE_FAILED, MYF(0), et->name.str); goto err; } @@ -413,7 +413,7 @@ db_create_event(THD *thd, event_timed *et) ret= EVEX_OK; if (table->file->write_row(table->record[0])) { - my_error(ER_EVENT_STORE_FAILED, MYF(0), et->m_name.str); + my_error(ER_EVENT_STORE_FAILED, MYF(0), et->name.str); goto err; } @@ -452,14 +452,15 @@ err: */ static int -db_update_event(THD *thd, sp_name *name, event_timed *et) +db_update_event(THD *thd, sp_name *new_name, event_timed *et) { TABLE *table; int ret= EVEX_OPEN_TABLE_FAILED; DBUG_ENTER("db_update_event"); - DBUG_PRINT("enter", ("name: %.*s", et->m_name.length, et->m_name.str)); - if (name) - DBUG_PRINT("enter", ("rename to: %.*s", name->m_name.length, name->m_name.str)); + DBUG_PRINT("enter", ("name: %.*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)); if (!(table= evex_open_event_table(thd, TL_WRITE))) { @@ -467,10 +468,10 @@ db_update_event(THD *thd, sp_name *name, event_timed *et) goto err; } - if (EVEX_KEY_NOT_FOUND == evex_db_find_routine_aux(thd, et->m_db, et->m_name, + if (EVEX_KEY_NOT_FOUND == evex_db_find_routine_aux(thd, et->dbname, et->name, table)) { - my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), et->m_name.str); + my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), et->name.str); goto err; } @@ -483,17 +484,17 @@ db_update_event(THD *thd, sp_name *name, event_timed *et) if ((ret= evex_fill_row(thd, table, et, true))) goto err; - if (name) + if (new_name) { table->field[EVEX_FIELD_DB]-> - store(name->m_db.str, name->m_db.length, system_charset_info); + store(new_name->m_db.str, new_name->m_db.length, system_charset_info); table->field[EVEX_FIELD_NAME]-> - store(name->m_name.str, name->m_name.length, system_charset_info); + store(new_name->m_name.str, new_name->m_name.length, system_charset_info); } if ((ret= table->file->update_row(table->record[1], table->record[0]))) { - my_error(ER_EVENT_STORE_FAILED, MYF(0), et->m_name.str); + my_error(ER_EVENT_STORE_FAILED, MYF(0), et->name.str); goto err; } @@ -546,7 +547,7 @@ db_find_event(THD *thd, sp_name *name, event_timed **ett, TABLE *tbl) if ((ret= evex_db_find_routine_aux(thd, name->m_db, name->m_name, table))) { - my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), et->m_name.str); + my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name->m_name.str); goto done; } et= new event_timed; @@ -612,7 +613,7 @@ evex_load_and_compile_event(THD * thd, sp_name *spn, bool use_lock) /* allocate on evex_mem_root. if you call without evex_mem_root - then m_sphead will not be cleared! + then sphead will not be cleared! */ if ((ret= ett->compile(thd, &evex_mem_root))) goto done; @@ -627,10 +628,10 @@ evex_load_and_compile_event(THD * thd, sp_name *spn, bool use_lock) VOID(push_dynamic(&evex_executing_queue, (gptr) &ett_copy)); /* - There is a copy in the array which we don't need. m_sphead won't be + There is a copy in the array which we don't need. sphead won't be destroyed. */ - ett->m_free_sphead_on_delete= false; + ett->free_sphead_on_delete= false; delete ett; /* @@ -674,11 +675,11 @@ evex_remove_from_cache(LEX_STRING *db, LEX_STRING *name, bool use_lock) event_timed **p_et= dynamic_element(&evex_executing_queue, i, event_timed**); event_timed *ett= *p_et; DBUG_PRINT("info", ("[%s.%s]==[%s.%s]?",db->str,name->str, - ett->m_db.str, ett->m_name.str)); - if (name->length == ett->m_name.length && - db->length == ett->m_db.length && - 0 == strncmp(db->str, ett->m_db.str, db->length) && - 0 == strncmp(name->str, ett->m_name.str, name->length) + ett->dbname.str, ett->name.str)); + if (name->length == ett->name.length && + db->length == ett->dbname.length && + 0 == strncmp(db->str, ett->dbname.str, db->length) && + 0 == strncmp(name->str, ett->name.str, name->length) ) { int idx; @@ -722,10 +723,10 @@ evex_remove_from_cache(LEX_STRING *db, LEX_STRING *name, bool use_lock) { event_timed *ett= dynamic_element(&events_array, i, event_timed*); - if (name->length == ett->m_name.length && - db->length == ett->m_db.length && - 0 == strncmp(db->str, ett->m_db.str, db->length) && - 0 == strncmp(name->str, ett->m_name.str, name->length) + if (name->length == ett->name.length && + db->length == ett->dbname.length && + 0 == strncmp(db->str, ett->dbname.str, db->length) && + 0 == strncmp(name->str, ett->name.str, name->length) ) delete_dynamic_element(&events_array, i); } @@ -763,15 +764,15 @@ evex_create_event(THD *thd, event_timed *et, uint create_options) int ret = 0; DBUG_ENTER("evex_create_event"); - DBUG_PRINT("enter", ("name: %*s options:%d", et->m_name.length, - et->m_name.str, create_options)); + DBUG_PRINT("enter", ("name: %*s options:%d", et->name.length, + et->name.str, create_options)); if ((ret = db_create_event(thd, et)) == EVEX_WRITE_ROW_FAILED && (create_options & HA_LEX_CREATE_IF_NOT_EXISTS)) { push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_DB_CREATE_EXISTS, ER(ER_DB_CREATE_EXISTS), - "EVENT", thd->lex->et->m_name.str); + "EVENT", et->name.str); ret= 0; goto done; } @@ -785,9 +786,9 @@ evex_create_event(THD *thd, event_timed *et, uint create_options) goto done; VOID(pthread_mutex_lock(&LOCK_evex_running)); - if (evex_is_running && et->m_status == MYSQL_EVENT_ENABLED) + if (evex_is_running && et->status == MYSQL_EVENT_ENABLED) { - sp_name spn(et->m_db, et->m_name); + sp_name spn(et->dbname, et->name); ret= evex_load_and_compile_event(thd, &spn, true); } VOID(pthread_mutex_unlock(&LOCK_evex_running)); @@ -804,9 +805,9 @@ done: SYNOPSIS evex_update_event() - thd THD - name the real name of the event. - et event's data + thd THD + et event's data + new_name set in case of RENAME TO. NOTES et contains data about dbname and event name. @@ -815,14 +816,14 @@ done: */ int -evex_update_event(THD *thd, sp_name *name, event_timed *et) +evex_update_event(THD *thd, event_timed *et, sp_name *name) { int ret, i; bool need_second_pass= true; sp_name *spn= 0; DBUG_ENTER("evex_update_event"); - DBUG_PRINT("enter", ("name: %*s", et->m_name.length, et->m_name.str)); + DBUG_PRINT("enter", ("name: %*s", et->name.length, et->name.str)); /* db_update_event() opens & closes the table to prevent @@ -836,13 +837,13 @@ evex_update_event(THD *thd, sp_name *name, event_timed *et) UNLOCK_MUTEX_AND_BAIL_OUT(LOCK_evex_running, done); VOID(pthread_mutex_lock(&LOCK_event_arrays)); - evex_remove_from_cache(&et->m_db, &et->m_name, false); - if (et->m_status == MYSQL_EVENT_ENABLED) + evex_remove_from_cache(&et->dbname, &et->name, false); + if (et->status == MYSQL_EVENT_ENABLED) if (name) ret= evex_load_and_compile_event(thd, name, false); else { - spn= new sp_name(et->m_db, et->m_name); + spn= new sp_name(et->dbname, et->name); ret= evex_load_and_compile_event(thd, spn, false); delete spn; } @@ -884,9 +885,9 @@ evex_drop_event(THD *thd, event_timed *et, bool drop_if_exists) goto done; } - if (!(ret= evex_db_find_routine_aux(thd, et->m_db, et->m_name, table))) + if (!(ret= evex_db_find_routine_aux(thd, et->dbname, et->name, table))) { - if (ret= table->file->delete_row(table->record[0])) + if ((ret= table->file->delete_row(table->record[0]))) { my_error(ER_EVENT_CANNOT_DELETE, MYF(0)); goto done; @@ -896,7 +897,7 @@ evex_drop_event(THD *thd, event_timed *et, bool drop_if_exists) { push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST), - "EVENT", thd->lex->et->m_name.str); + "EVENT", et->name.str); ret= 0; goto done; } else @@ -904,7 +905,7 @@ evex_drop_event(THD *thd, event_timed *et, bool drop_if_exists) VOID(pthread_mutex_lock(&LOCK_evex_running)); if (evex_is_running) - ret= evex_remove_from_cache(&et->m_db, &et->m_name, true); + ret= evex_remove_from_cache(&et->dbname, &et->name, true); VOID(pthread_mutex_unlock(&LOCK_evex_running)); done: diff --git a/sql/event.h b/sql/event.h index eae38472498..fbb39ff53a2 100644 --- a/sql/event.h +++ b/sql/event.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2000-2003 MySQL AB +/* Copyright (C) 2004-2005 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 @@ -63,41 +63,45 @@ class event_timed my_bool running; pthread_mutex_t LOCK_running; + bool status_changed; + bool last_executed_changed; + TIME last_executed; + public: - LEX_STRING m_db; - LEX_STRING m_name; - LEX_STRING m_body; + LEX_STRING dbname; + LEX_STRING name; + LEX_STRING body; - LEX_STRING m_definer_user; - LEX_STRING m_definer_host; - LEX_STRING m_definer;// combination of user and host + LEX_STRING definer_user; + LEX_STRING definer_host; + LEX_STRING definer;// combination of user and host - LEX_STRING m_comment; - TIME m_starts; - TIME m_ends; - TIME m_execute_at; - longlong m_expr; - interval_type m_interval; - longlong m_created; - longlong m_modified; - TIME m_last_executed; - enum enum_event_on_completion m_on_completion; - enum enum_event_status m_status; - sp_head *m_sphead; + LEX_STRING comment; + TIME starts; + TIME ends; + TIME execute_at; - const uchar *m_body_begin; + longlong expression; + interval_type interval; + + longlong created; + longlong modified; + enum enum_event_on_completion on_completion; + enum enum_event_status status; + sp_head *sphead; + + const uchar *body_begin; - bool m_dropped; - bool m_free_sphead_on_delete; - uint m_flags;//all kind of purposes - bool m_last_executed_changed; - bool m_status_changed; + bool dropped; + bool free_sphead_on_delete; + uint flags;//all kind of purposes - event_timed():running(0), m_expr(0), m_created(0), m_modified(0), - m_on_completion(MYSQL_EVENT_ON_COMPLETION_DROP), - m_status(MYSQL_EVENT_ENABLED), m_sphead(0), m_dropped(false), - m_free_sphead_on_delete(true), m_flags(0), - m_last_executed_changed(false), m_status_changed(false) + event_timed():running(0), status_changed(false), last_executed_changed(false), + expression(0), created(0), modified(0), + on_completion(MYSQL_EVENT_ON_COMPLETION_DROP), + status(MYSQL_EVENT_ENABLED), sphead(0), dropped(false), + free_sphead_on_delete(true), flags(0) + { pthread_mutex_init(&LOCK_running, MY_MUTEX_INIT_FAST); init(); @@ -106,7 +110,7 @@ public: ~event_timed() { pthread_mutex_destroy(&LOCK_running); - if (m_free_sphead_on_delete) + if (free_sphead_on_delete) free_sp(); } @@ -120,10 +124,10 @@ public: init_execute_at(THD *thd, Item *expr); int - init_interval(THD *thd, Item *expr, interval_type interval); + init_interval(THD *thd, Item *expr, interval_type new_interval); void - init_name(THD *thd, sp_name *name); + init_name(THD *thd, sp_name *spn); int init_starts(THD *thd, Item *starts); @@ -135,7 +139,7 @@ public: event_timed::init_body(THD *thd); void - init_comment(THD *thd, LEX_STRING *comment); + init_comment(THD *thd, LEX_STRING *set_comment); int load_from_row(MEM_ROOT *mem_root, TABLE *table); @@ -163,11 +167,8 @@ public: void free_sp() { - if (m_sphead) - { - delete m_sphead; - m_sphead= 0; - } + delete sphead; + sphead= 0; } }; @@ -176,7 +177,7 @@ int evex_create_event(THD *thd, event_timed *et, uint create_options); int -evex_update_event(THD *thd, sp_name *name, event_timed *et); +evex_update_event(THD *thd, event_timed *et, sp_name *new_name); int evex_drop_event(THD *thd, event_timed *et, bool drop_if_exists); diff --git a/sql/event_executor.cc b/sql/event_executor.cc index 90c08d045db..f3db374af54 100644 --- a/sql/event_executor.cc +++ b/sql/event_executor.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2000-2003 MySQL AB +/* Copyright (C) 2004-2005 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 @@ -226,7 +226,7 @@ event_executor_main(void *arg) for (i= 0; (i < evex_executing_queue.elements) && !thd->killed; ++i) { event_timed *et= *dynamic_element(&evex_executing_queue,i,event_timed**); -// printf("%llu\n", TIME_to_ulonglong_datetime(&et->m_execute_at)); +// printf("%llu\n", TIME_to_ulonglong_datetime(&et->execute_at)); if (!event_executor_running_global_var) break; @@ -236,13 +236,13 @@ event_executor_main(void *arg) if this is the first event which is after time_now then no more need to iterate over more elements since the array is sorted. */ - if (et->m_execute_at.year > 1969 && - my_time_compare(&time_now, &et->m_execute_at) == -1) + if (et->execute_at.year > 1969 && + my_time_compare(&time_now, &et->execute_at) == -1) break; - if (et->m_status == MYSQL_EVENT_ENABLED && - !check_access(thd, EVENT_ACL, et->m_db.str, 0, 0, 0, - is_schema_db(et->m_db.str))) + if (et->status == MYSQL_EVENT_ENABLED && + !check_access(thd, EVENT_ACL, et->dbname.str, 0, 0, 0, + is_schema_db(et->dbname.str))) { pthread_t th; @@ -262,9 +262,9 @@ event_executor_main(void *arg) thd->proc_info = "Computing next time"; et->compute_next_execution_time(); et->update_fields(thd); - if ((et->m_execute_at.year && !et->m_expr) - || TIME_to_ulonglong_datetime(&et->m_execute_at) == 0L) - et->m_flags |= EVENT_EXEC_NO_MORE; + if ((et->execute_at.year && !et->expression) + || TIME_to_ulonglong_datetime(&et->execute_at) == 0L) + et->flags |= EVENT_EXEC_NO_MORE; } } /* @@ -275,13 +275,13 @@ event_executor_main(void *arg) while (j < i && j < evex_executing_queue.elements) { event_timed *et= *dynamic_element(&evex_executing_queue, j, event_timed**); - if (et->m_flags & EVENT_EXEC_NO_MORE || et->m_status == MYSQL_EVENT_DISABLED) + if ((et->flags & EVENT_EXEC_NO_MORE) || et->status == MYSQL_EVENT_DISABLED) { delete_dynamic_element(&evex_executing_queue, j); DBUG_PRINT("EVEX main thread", ("DELETING FROM EXECUTION QUEUE [%s.%s]", - et->m_db.str, et->m_name.str)); + et->dbname.str, et->name.str)); // nulling the position, will delete later - if (et->m_dropped) + if (et->dropped) { // we have to drop the event int idx; @@ -311,7 +311,7 @@ err: evex_is_running= false; VOID(pthread_mutex_unlock(&LOCK_evex_running)); - sql_print_information("Event executor stopping"); + sql_print_information("Event scheduler stopping"); /* TODO: A better will be with a conditional variable @@ -334,7 +334,7 @@ err: // No need to use lock here if EVEX is not running but anyway delete_dynamic(&evex_executing_queue); delete_dynamic(&events_array); - VOID(pthread_mutex_unlock(&LOCK_evex_running)); + VOID(pthread_mutex_unlock(&LOCK_event_arrays)); thd->proc_info = "Clearing"; DBUG_ASSERT(thd->net.buff != 0); @@ -355,7 +355,7 @@ err_no_thd: VOID(pthread_mutex_unlock(&LOCK_evex_running)); free_root(&evex_mem_root, MYF(0)); - sql_print_information("Event executor stopped"); + sql_print_information("Event scheduler stopped"); my_thread_end(); pthread_exit(0); @@ -381,7 +381,7 @@ event_executor_worker(void *event_void) if (!(thd = new THD)) // note that contructor of THD uses DBUG_ ! { - sql_print_error("Cannot create a THD structure in worker thread"); + sql_print_error("Cannot create a THD structure in a scheduler worker thread"); goto err_no_thd; } thd->thread_stack = (char*)&thd; // remember where our stack is @@ -406,20 +406,20 @@ event_executor_worker(void *event_void) // thd->security_ctx->priv_host is char[MAX_HOSTNAME] strxnmov(thd->security_ctx->priv_host, sizeof(thd->security_ctx->priv_host), - event->m_definer_host.str, NullS); + event->definer_host.str, NullS); - thd->security_ctx->priv_user= event->m_definer_user.str; + thd->security_ctx->priv_user= event->definer_user.str; - thd->db= event->m_db.str; + thd->db= event->dbname.str; { char exec_time[200]; int ret; - my_TIME_to_str(&event->m_execute_at, exec_time); - DBUG_PRINT("info", (" EVEX EXECUTING event for event %s.%s [EXPR:%d][EXECUTE_AT:%s]", event->m_db.str, event->m_name.str,(int) event->m_expr, exec_time)); - sql_print_information(" EVEX EXECUTING event for event %s.%s [EXPR:%d][EXECUTE_AT:%s]", event->m_db.str, event->m_name.str,(int) event->m_expr, exec_time); + my_TIME_to_str(&event->execute_at, exec_time); + DBUG_PRINT("info", (" EVEX EXECUTING event for event %s.%s [EXPR:%d][EXECUTE_AT:%s]", event->dbname.str, event->name.str,(int) event->expression, exec_time)); + sql_print_information(" EVEX EXECUTING event for event %s.%s [EXPR:%d][EXECUTE_AT:%s]", event->dbname.str, event->name.str,(int) event->expression, exec_time); ret= event->execute(thd, &worker_mem_root); - sql_print_information(" EVEX EXECUTED event for event %s.%s [EXPR:%d][EXECUTE_AT:%s]. RetCode=%d", event->m_db.str, event->m_name.str,(int) event->m_expr, exec_time, ret); - DBUG_PRINT("info", (" EVEX EXECUTED event for event %s.%s [EXPR:%d][EXECUTE_AT:%s]", event->m_db.str, event->m_name.str,(int) event->m_expr, exec_time)); + sql_print_information(" EVEX EXECUTED event for event %s.%s [EXPR:%d][EXECUTE_AT:%s]. RetCode=%d", event->dbname.str, event->name.str,(int) event->expression, exec_time, ret); + DBUG_PRINT("info", (" EVEX EXECUTED event for event %s.%s [EXPR:%d][EXECUTE_AT:%s]", event->dbname.str, event->name.str,(int) event->expression, exec_time)); } thd->db= 0; @@ -495,19 +495,19 @@ evex_load_events_from_db(THD *thd) } DBUG_PRINT("evex_load_events_from_db", - ("Event %s loaded from row. Time to compile", et->m_name.str)); + ("Event %s loaded from row. Time to compile", et->name.str)); if ((ret= et->compile(thd, &evex_mem_root))) { sql_print_error("Error while compiling %s.%s. Aborting load.", - et->m_db.str, et->m_name.str); + et->dbname.str, et->name.str); goto end; } // let's find when to be executed et->compute_next_execution_time(); DBUG_PRINT("evex_load_events_from_db", - ("Adding %s to the executor list.", et->m_name.str)); + ("Adding %s to the executor list.", et->name.str)); VOID(push_dynamic(&events_array,(gptr) et)); /* We always add at the end so the number of elements - 1 is the place @@ -518,7 +518,7 @@ evex_load_events_from_db(THD *thd) et_copy= dynamic_element(&events_array, events_array.elements - 1, event_timed*); VOID(push_dynamic(&evex_executing_queue,(gptr) &et_copy)); - et->m_free_sphead_on_delete= false; + et->free_sphead_on_delete= false; delete et; } end_read_record(&read_record_info); diff --git a/sql/event_priv.h b/sql/event_priv.h index f377a012e19..ceaa91ae199 100644 --- a/sql/event_priv.h +++ b/sql/event_priv.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2000-2003 MySQL AB +/* Copyright (C) 2004-2005 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 diff --git a/sql/event_timed.cc b/sql/event_timed.cc index b986e650a13..8d494a921f0 100644 --- a/sql/event_timed.cc +++ b/sql/event_timed.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2000-2003 MySQL AB +/* Copyright (C) 2004-2005 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 @@ -35,16 +35,16 @@ event_timed::init() { DBUG_ENTER("event_timed::init"); - m_db.str= m_name.str= m_body.str= m_comment.str= 0; - m_db.length= m_name.length= m_body.length= m_comment.length= 0; + dbname.str= name.str= body.str= comment.str= 0; + dbname.length= name.length= body.length= comment.length= 0; - set_zero_time(&m_starts, MYSQL_TIMESTAMP_DATETIME); - set_zero_time(&m_ends, MYSQL_TIMESTAMP_DATETIME); - set_zero_time(&m_execute_at, MYSQL_TIMESTAMP_DATETIME); - set_zero_time(&m_last_executed, MYSQL_TIMESTAMP_DATETIME); + set_zero_time(&starts, MYSQL_TIMESTAMP_DATETIME); + set_zero_time(&ends, MYSQL_TIMESTAMP_DATETIME); + set_zero_time(&execute_at, MYSQL_TIMESTAMP_DATETIME); + set_zero_time(&last_executed, MYSQL_TIMESTAMP_DATETIME); - m_definer_user.str= m_definer_host.str= 0; - m_definer_user.length= m_definer_host.length= 0; + definer_user.str= definer_host.str= 0; + definer_user.length= definer_host.length= 0; DBUG_VOID_RETURN; } @@ -56,11 +56,11 @@ event_timed::init() SYNOPSIS event_timed::init_name() thd THD - name the name extracted in the parser + spn the name extracted in the parser */ void -event_timed::init_name(THD *thd, sp_name *name) +event_timed::init_name(THD *thd, sp_name *spn) { DBUG_ENTER("event_timed::init_name"); uint n; /* Counter for nul trimming */ @@ -68,27 +68,27 @@ event_timed::init_name(THD *thd, sp_name *name) MEM_ROOT *root= thd->mem_root; /* We have to copy strings to get them into the right memroot */ - if (name) + if (spn) { - m_db.length= name->m_db.length; - if (name->m_db.length == 0) - m_db.str= NULL; + dbname.length= spn->m_db.length; + if (spn->m_db.length == 0) + dbname.str= NULL; else - m_db.str= strmake_root(root, name->m_db.str, name->m_db.length); - m_name.length= name->m_name.length; - m_name.str= strmake_root(root, name->m_name.str, name->m_name.length); + dbname.str= strmake_root(root, spn->m_db.str, spn->m_db.length); + name.length= spn->m_name.length; + name.str= strmake_root(root, spn->m_name.str, spn->m_name.length); - if (name->m_qname.length == 0) - name->init_qname(thd); + if (spn->m_qname.length == 0) + spn->init_qname(thd); } else if (thd->db) { - m_db.length= thd->db_length; - m_db.str= strmake_root(root, thd->db, m_db.length); + dbname.length= thd->db_length; + dbname.str= strmake_root(root, thd->db, dbname.length); } - DBUG_PRINT("m_db", ("len=%d db=%s",m_db.length, m_db.str)); - DBUG_PRINT("m_name", ("len=%d name=%s",m_name.length, m_name.str)); + DBUG_PRINT("dbname", ("len=%d db=%s",dbname.length, dbname.str)); + DBUG_PRINT("name", ("len=%d name=%s",name.length, name.str)); DBUG_VOID_RETURN; } @@ -112,12 +112,12 @@ event_timed::init_body(THD *thd) DBUG_ENTER("event_timed::init_body"); MEM_ROOT *root= thd->mem_root; - m_body.length= thd->lex->ptr - m_body_begin; + body.length= thd->lex->ptr - body_begin; // Trim nuls at the end - while (m_body.length && m_body_begin[m_body.length-1] == '\0') - m_body.length--; + while (body.length && body_begin[body.length-1] == '\0') + body.length--; - m_body.str= strmake_root(root, (char *)m_body_begin, m_body.length); + body.str= strmake_root(root, (char *)body_begin, body.length); DBUG_VOID_RETURN; } @@ -169,7 +169,7 @@ event_timed::init_execute_at(THD *thd, Item *expr) my_tz_UTC->gmt_sec_to_TIME(<ime, TIME_to_timestamp(thd,<ime, ¬_used)); - m_execute_at= ltime; + execute_at= ltime; DBUG_RETURN(0); } @@ -180,7 +180,7 @@ event_timed::init_execute_at(THD *thd, Item *expr) SYNOPSIS event_timed::init_interval() expr how much? - interval what is the interval + new_interval what is the interval RETURNS 0 - OK @@ -189,7 +189,7 @@ event_timed::init_execute_at(THD *thd, Item *expr) */ int -event_timed::init_interval(THD *thd, Item *expr, interval_type interval) +event_timed::init_interval(THD *thd, Item *expr, interval_type new_interval) { longlong tmp; DBUG_ENTER("event_timed::init_interval"); @@ -200,8 +200,8 @@ event_timed::init_interval(THD *thd, Item *expr, interval_type interval) if ((tmp= expr->val_int()) <= 0) DBUG_RETURN(EVEX_BAD_PARAMS); - m_expr= tmp; - m_interval= interval; + expression= tmp; + interval= new_interval; DBUG_RETURN(0); } @@ -228,7 +228,7 @@ event_timed::init_interval(THD *thd, Item *expr, interval_type interval) */ int -event_timed::init_starts(THD *thd, Item *starts) +event_timed::init_starts(THD *thd, Item *new_starts) { my_bool not_used; TIME ltime; @@ -236,22 +236,22 @@ event_timed::init_starts(THD *thd, Item *starts) DBUG_ENTER("event_timed::init_starts"); - if (starts->fix_fields(thd, &starts)) + if (new_starts->fix_fields(thd, &new_starts)) DBUG_RETURN(EVEX_PARSE_ERROR); - if (starts->val_int() == MYSQL_TIMESTAMP_ERROR) + if (new_starts->val_int() == MYSQL_TIMESTAMP_ERROR) DBUG_RETURN(EVEX_BAD_PARAMS); - if ((not_used= starts->get_date(<ime, TIME_NO_ZERO_DATE))) + if ((not_used= new_starts->get_date(<ime, TIME_NO_ZERO_DATE))) DBUG_RETURN(EVEX_BAD_PARAMS); /* This may result in a 1970-01-01 date if ltime is > 2037-xx-xx CONVERT_TZ has similar problem */ - my_tz_UTC->gmt_sec_to_TIME(<ime, TIME_to_timestamp(thd,<ime, ¬_used)); + my_tz_UTC->gmt_sec_to_TIME(<ime, TIME_to_timestamp(thd, <ime, ¬_used)); - m_starts= ltime; + starts= ltime; DBUG_RETURN(0); } @@ -262,7 +262,7 @@ event_timed::init_starts(THD *thd, Item *starts) SYNOPSIS event_timed::init_ends() thd THD - ends when? + new_ends when? NOTES Note that activation time is not execution time. @@ -279,7 +279,7 @@ event_timed::init_starts(THD *thd, Item *starts) */ int -event_timed::init_ends(THD *thd, Item *ends) +event_timed::init_ends(THD *thd, Item *new_ends) { TIME ltime; my_time_t my_time_tmp; @@ -287,11 +287,11 @@ event_timed::init_ends(THD *thd, Item *ends) DBUG_ENTER("event_timed::init_ends"); - if (ends->fix_fields(thd, &ends)) + if (new_ends->fix_fields(thd, &new_ends)) DBUG_RETURN(EVEX_PARSE_ERROR); // the field was already fixed in init_ends - if ((not_used= ends->get_date(<ime, TIME_NO_ZERO_DATE))) + if ((not_used= new_ends->get_date(<ime, TIME_NO_ZERO_DATE))) DBUG_RETURN(EVEX_BAD_PARAMS); /* @@ -300,10 +300,10 @@ event_timed::init_ends(THD *thd, Item *ends) */ my_tz_UTC->gmt_sec_to_TIME(<ime, TIME_to_timestamp(thd, <ime, ¬_used)); - if (m_starts.year && my_time_compare(&m_starts, <ime) != -1) + if (starts.year && my_time_compare(&starts, <ime) != -1) DBUG_RETURN(EVEX_BAD_PARAMS); - m_ends= ltime; + ends= ltime; DBUG_RETURN(0); } @@ -318,19 +318,19 @@ event_timed::init_ends(THD *thd, Item *ends) */ void -event_timed::init_comment(THD *thd, LEX_STRING *comment) +event_timed::init_comment(THD *thd, LEX_STRING *set_comment) { DBUG_ENTER("event_timed::init_comment"); - m_comment.str= strmake_root(thd->mem_root, comment->str, - m_comment.length= comment->length); + comment.str= strmake_root(thd->mem_root, set_comment->str, + comment.length= set_comment->length); DBUG_VOID_RETURN; } /* - Inits definer (m_definer_user and m_definer_host) during + Inits definer (definer_user and definer_host) during parsing. SYNOPSIS @@ -342,11 +342,11 @@ event_timed::init_definer(THD *thd) { DBUG_ENTER("event_timed::init_definer"); - m_definer_user.str= strdup_root(thd->mem_root, thd->security_ctx->priv_user); - m_definer_user.length= strlen(thd->security_ctx->priv_user); + definer_user.str= strdup_root(thd->mem_root, thd->security_ctx->priv_user); + definer_user.length= strlen(thd->security_ctx->priv_user); - m_definer_host.str= strdup_root(thd->mem_root, thd->security_ctx->priv_host); - m_definer_host.length= strlen(thd->security_ctx->priv_host); + definer_host.str= strdup_root(thd->mem_root, thd->security_ctx->priv_host); + definer_host.length= strlen(thd->security_ctx->priv_host); DBUG_RETURN(0); } @@ -384,68 +384,68 @@ event_timed::load_from_row(MEM_ROOT *mem_root, TABLE *table) if (table->s->fields != EVEX_FIELD_COUNT) goto error; - if ((et->m_db.str= get_field(mem_root, + if ((et->dbname.str= get_field(mem_root, table->field[EVEX_FIELD_DB])) == NULL) goto error; - et->m_db.length= strlen(et->m_db.str); + et->dbname.length= strlen(et->dbname.str); - if ((et->m_name.str= get_field(mem_root, + if ((et->name.str= get_field(mem_root, table->field[EVEX_FIELD_NAME])) == NULL) goto error; - et->m_name.length= strlen(et->m_name.str); + et->name.length= strlen(et->name.str); - if ((et->m_body.str= get_field(mem_root, + if ((et->body.str= get_field(mem_root, table->field[EVEX_FIELD_BODY])) == NULL) goto error; - et->m_body.length= strlen(et->m_body.str); + et->body.length= strlen(et->body.str); - if ((et->m_definer.str= get_field(mem_root, + if ((et->definer.str= get_field(mem_root, table->field[EVEX_FIELD_DEFINER])) == NullS) goto error; - et->m_definer.length= strlen(et->m_definer.str); + et->definer.length= strlen(et->definer.str); - ptr= strchr(et->m_definer.str, '@'); + ptr= strchr(et->definer.str, '@'); if (! ptr) - ptr= et->m_definer.str; // Weird, isn't it? + ptr= et->definer.str; - len= ptr - et->m_definer.str; + len= ptr - et->definer.str; - et->m_definer_user.str= strmake_root(mem_root, et->m_definer.str, len); - et->m_definer_user.length= len; - len= et->m_definer.length - len - 1; //1 is because of @ - et->m_definer_host.str= strmake_root(mem_root, ptr + 1, len);//1: because of @ - et->m_definer_host.length= len; + et->definer_user.str= strmake_root(mem_root, et->definer.str, len); + et->definer_user.length= len; + len= et->definer.length - len - 1; //1 is because of @ + et->definer_host.str= strmake_root(mem_root, ptr + 1, len);//1: because of @ + et->definer_host.length= len; res1= table->field[EVEX_FIELD_STARTS]-> - get_date(&et->m_starts, TIME_NO_ZERO_DATE); + get_date(&et->starts, TIME_NO_ZERO_DATE); res2= table->field[EVEX_FIELD_ENDS]-> - get_date(&et->m_ends, TIME_NO_ZERO_DATE); + get_date(&et->ends, TIME_NO_ZERO_DATE); - et->m_expr= table->field[EVEX_FIELD_INTERVAL_EXPR]->val_int(); + et->expression= table->field[EVEX_FIELD_INTERVAL_EXPR]->val_int(); /* If res1 and res2 are true then both fields are empty. Hence if EVEX_FIELD_EXECUTE_AT is empty there is an error. */ - if (res1 && res2 && !et->m_expr && table->field[EVEX_FIELD_EXECUTE_AT]-> - get_date(&et->m_execute_at, TIME_NO_ZERO_DATE)) + if (res1 && res2 && !et->expression && table->field[EVEX_FIELD_EXECUTE_AT]-> + get_date(&et->execute_at, TIME_NO_ZERO_DATE)) goto error; /* In DB the values start from 1 but enum interval_type starts from 0 */ - et->m_interval= (interval_type) + et->interval= (interval_type) ((ulonglong) table->field[EVEX_FIELD_TRANSIENT_INTERVAL]->val_int() - 1); - et->m_modified= table->field[EVEX_FIELD_CREATED]->val_int(); - et->m_created= table->field[EVEX_FIELD_MODIFIED]->val_int(); + et->modified= table->field[EVEX_FIELD_CREATED]->val_int(); + et->created= table->field[EVEX_FIELD_MODIFIED]->val_int(); /* ToDo Andrey : Ask PeterG & Serg what to do in this case. @@ -458,25 +458,25 @@ event_timed::load_from_row(MEM_ROOT *mem_root, TABLE *table) is loaded from DB, execution at L_E_AT+15min will be scheduled. However this time is in the past. Hence immediate execution. Due to patch of - ::mark_last_executed() m_last_executed gets time_now - and not m_execute_at. If not like this a big + ::mark_last_executed() last_executed gets time_now + and not execute_at. If not like this a big queue can be scheduled for times which are still in the past (2, 3 and more executions which will be consequent). */ - set_zero_time(&m_last_executed, MYSQL_TIMESTAMP_DATETIME); + set_zero_time(&last_executed, MYSQL_TIMESTAMP_DATETIME); #ifdef ANDREY_0 table->field[EVEX_FIELD_LAST_EXECUTED]-> - get_date(&et->m_last_executed, TIME_NO_ZERO_DATE); + get_date(&et->last_executed, TIME_NO_ZERO_DATE); #endif - m_last_executed_changed= false; + 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[EVEX_FIELD_STATUS])) == NullS) goto error; - DBUG_PRINT("load_from_row", ("Event [%s] is [%s]", et->m_name.str, ptr)); - et->m_status= (ptr[0]=='E'? MYSQL_EVENT_ENABLED: + DBUG_PRINT("load_from_row", ("Event [%s] is [%s]", et->name.str, ptr)); + et->status= (ptr[0]=='E'? MYSQL_EVENT_ENABLED: MYSQL_EVENT_DISABLED); // ToDo : Andrey . Find a way not to allocate ptr on event_mem_root @@ -484,14 +484,14 @@ event_timed::load_from_row(MEM_ROOT *mem_root, TABLE *table) table->field[EVEX_FIELD_ON_COMPLETION])) == NullS) goto error; - et->m_on_completion= (ptr[0]=='D'? MYSQL_EVENT_ON_COMPLETION_DROP: + et->on_completion= (ptr[0]=='D'? MYSQL_EVENT_ON_COMPLETION_DROP: MYSQL_EVENT_ON_COMPLETION_PRESERVE); - et->m_comment.str= get_field(mem_root, table->field[EVEX_FIELD_COMMENT]); - if (et->m_comment.str != NullS) - et->m_comment.length= strlen(et->m_comment.str); + et->comment.str= get_field(mem_root, table->field[EVEX_FIELD_COMMENT]); + if (et->comment.str != NullS) + et->comment.length= strlen(et->comment.str); else - et->m_comment.length= 0; + et->comment.length= 0; DBUG_RETURN(0); error: @@ -499,6 +499,11 @@ error: } +/* + Note: In the comments this->ends is referenced as m_ends + +*/ + bool event_timed::compute_next_execution_time() { @@ -508,179 +513,181 @@ event_timed::compute_next_execution_time() DBUG_ENTER("event_timed::compute_next_execution_time"); - if (m_status == MYSQL_EVENT_DISABLED) + if (status == MYSQL_EVENT_DISABLED) { DBUG_PRINT("compute_next_execution_time", - ("Event %s is DISABLED", m_name.str)); + ("Event %s is DISABLED", name.str)); goto ret; } //if one-time no need to do computation - if (!m_expr) + if (!expression) { //let's check whether it was executed - if (m_last_executed.year) + if (last_executed.year) { DBUG_PRINT("compute_next_execution_time", - ("One-time event %s was already executed", m_name.str)); - if (m_on_completion == MYSQL_EVENT_ON_COMPLETION_DROP) + ("One-time event %s was already executed", name.str)); + if (on_completion == MYSQL_EVENT_ON_COMPLETION_DROP) { DBUG_PRINT("compute_next_execution_time", ("One-time event will be dropped.")); - m_dropped= true; + dropped= true; } - m_status= MYSQL_EVENT_DISABLED; - m_status_changed= true; + status= MYSQL_EVENT_DISABLED; + status_changed= true; } goto ret; } time(&now); my_tz_UTC->gmt_sec_to_TIME(&time_now, now); /* - sql_print_information("[%s.%s]", m_db.str, m_name.str); + sql_print_information("[%s.%s]", dbname.str, name.str); sql_print_information("time_now : [%d-%d-%d %d:%d:%d ]", time_now.year, time_now.month, time_now.day, time_now.hour, time_now.minute, time_now.second); - sql_print_information("m_starts : [%d-%d-%d %d:%d:%d ]", m_starts.year, - m_starts.month, m_starts.day, m_starts.hour, - m_starts.minute, m_starts.second); - sql_print_information("m_ends : [%d-%d-%d %d:%d:%d ]", m_ends.year, - m_ends.month, m_ends.day, m_ends.hour, - m_ends.minute, m_ends.second); - sql_print_information("m_last_ex: [%d-%d-%d %d:%d:%d ]", m_last_executed.year, - m_last_executed.month, m_last_executed.day, - m_last_executed.hour, m_last_executed.minute, - m_last_executed.second); + sql_print_information("starts : [%d-%d-%d %d:%d:%d ]", starts.year, + starts.month, starts.day, starts.hour, + starts.minute, starts.second); + sql_print_information("ends : [%d-%d-%d %d:%d:%d ]", ends.year, + ends.month, ends.day, ends.hour, + ends.minute, ends.second); + sql_print_information("m_last_ex: [%d-%d-%d %d:%d:%d ]", last_executed.year, + last_executed.month, last_executed.day, + last_executed.hour, last_executed.minute, + last_executed.second); */ - //if time_now is after m_ends don't execute anymore - if (m_ends.year && (tmp= my_time_compare(&m_ends, &time_now)) == -1) + //if time_now is after ends don't execute anymore + if (ends.year && (tmp= my_time_compare(&ends, &time_now)) == -1) { - // time_now is after m_ends. don't execute anymore - set_zero_time(&m_execute_at, MYSQL_TIMESTAMP_DATETIME); - if (m_on_completion == MYSQL_EVENT_ON_COMPLETION_DROP) - m_dropped= true; - m_status= MYSQL_EVENT_DISABLED; - m_status_changed= true; + // time_now is after ends. don't execute anymore + set_zero_time(&execute_at, MYSQL_TIMESTAMP_DATETIME); + if (on_completion == MYSQL_EVENT_ON_COMPLETION_DROP) + dropped= true; + status= MYSQL_EVENT_DISABLED; + status_changed= true; goto ret; } /* - Here time_now is before or equals m_ends if the latter is set. - Let's check whether time_now is before m_starts. - If so schedule for m_starts + Here time_now is before or equals ends if the latter is set. + Let's check whether time_now is before starts. + If so schedule for starts */ - if (m_starts.year && (tmp= my_time_compare(&time_now, &m_starts)) < 1) + if (starts.year && (tmp= my_time_compare(&time_now, &starts)) < 1) { - if (tmp == 0 && my_time_compare(&m_starts, &m_last_executed) == 0) + if (tmp == 0 && my_time_compare(&starts, &last_executed) == 0) { /* - time_now = m_starts = m_last_executed - do nothing or we will schedule for second time execution at m_starts. + time_now = starts = last_executed + do nothing or we will schedule for second time execution at starts. */ } else { - //m_starts is in the future - //time_now before m_starts. Scheduling for m_starts - m_execute_at= m_starts; + /* + starts is in the future + time_now before starts. Scheduling for starts + */ + execute_at= starts; goto ret; } } - if (m_starts.year && m_ends.year) + if (starts.year && ends.year) { /* - Both m_starts and m_ends are set and time_now is between them (incl.) - If m_last_executed is set then increase with m_expr. The new TIME is - after m_ends set m_execute_at to 0. And check for m_on_completion + Both starts and m_ends are set and time_now is between them (incl.) + If last_executed is set then increase with m_expression. The new TIME is + after m_ends set execute_at to 0. And check for on_completion If not set then schedule for now. */ - if (!m_last_executed.year) - m_execute_at= time_now; + if (!last_executed.year) + execute_at= time_now; else { my_time_t last, ll_ends; // There was previous execution - last= sec_since_epoch_TIME(&m_last_executed) + m_expr; - ll_ends= sec_since_epoch_TIME(&m_ends); + last= sec_since_epoch_TIME(&last_executed) + expression; + ll_ends= sec_since_epoch_TIME(&ends); //now convert back to TIME //ToDo Andrey: maybe check for error here? if (ll_ends < last) { // Next execution after ends. No more executions - set_zero_time(&m_execute_at, MYSQL_TIMESTAMP_DATETIME); - if (m_on_completion == MYSQL_EVENT_ON_COMPLETION_DROP) - m_dropped= true; + set_zero_time(&execute_at, MYSQL_TIMESTAMP_DATETIME); + if (on_completion == MYSQL_EVENT_ON_COMPLETION_DROP) + dropped= true; } else - my_tz_UTC->gmt_sec_to_TIME(&m_execute_at, last); + my_tz_UTC->gmt_sec_to_TIME(&execute_at, last); } goto ret; } - else if (!m_starts.year && !m_ends.year) + else if (!starts.year && !ends.year) { - // both m_starts and m_ends are not set, se we schedule for the next - // based on m_last_executed - if (!m_last_executed.year) - //m_last_executed not set. Schedule the event for now - m_execute_at= time_now; + // both starts and m_ends are not set, se we schedule for the next + // based on last_executed + if (!last_executed.year) + //last_executed not set. Schedule the event for now + execute_at= time_now; else //ToDo Andrey: maybe check for error here? - my_tz_UTC->gmt_sec_to_TIME(&m_execute_at, - sec_since_epoch_TIME(&m_last_executed) + m_expr); + my_tz_UTC->gmt_sec_to_TIME(&execute_at, + sec_since_epoch_TIME(&last_executed) + expression); goto ret; } else { - //either m_starts or m_ends is set - if (m_starts.year) + //either starts or m_ends is set + if (starts.year) { /* - - m_starts is set. - - m_starts is not in the future according to check made before - Hence schedule for m_starts + m_expr in case m_last_executed - is not set, otherwise to m_last_executed + m_expr + - starts is set. + - starts is not in the future according to check made before + Hence schedule for starts + m_expression in case last_executed + is not set, otherwise to last_executed + m_expression */ my_time_t last; - //convert either m_last_executed or m_starts to seconds - if (m_last_executed.year) - last= sec_since_epoch_TIME(&m_last_executed) + m_expr; + //convert either last_executed or starts to seconds + if (last_executed.year) + last= sec_since_epoch_TIME(&last_executed) + expression; else - last= sec_since_epoch_TIME(&m_starts); + last= sec_since_epoch_TIME(&starts); //now convert back to TIME //ToDo Andrey: maybe check for error here? - my_tz_UTC->gmt_sec_to_TIME(&m_execute_at, last); + my_tz_UTC->gmt_sec_to_TIME(&execute_at, last); } else { /* - m_ends is set - m_ends is after time_now or is equal - Hence check for m_last_execute and increment with m_expr. - If m_last_executed is not set then schedule for now + Hence check for m_last_execute and increment with m_expression. + If last_executed is not set then schedule for now */ my_time_t last, ll_ends; - if (!m_last_executed.year) - m_execute_at= time_now; + if (!last_executed.year) + execute_at= time_now; else { - last= sec_since_epoch_TIME(&m_last_executed); - ll_ends= sec_since_epoch_TIME(&m_ends); - last+= m_expr; + last= sec_since_epoch_TIME(&last_executed); + ll_ends= sec_since_epoch_TIME(&ends); + last+= expression; //now convert back to TIME //ToDo Andrey: maybe check for error here? if (ll_ends < last) { - set_zero_time(&m_execute_at, MYSQL_TIMESTAMP_DATETIME); - if (m_on_completion == MYSQL_EVENT_ON_COMPLETION_DROP) - m_dropped= true; + set_zero_time(&execute_at, MYSQL_TIMESTAMP_DATETIME); + if (on_completion == MYSQL_EVENT_ON_COMPLETION_DROP) + dropped= true; } else - my_tz_UTC->gmt_sec_to_TIME(&m_execute_at, last); + my_tz_UTC->gmt_sec_to_TIME(&execute_at, last); } } goto ret; @@ -700,11 +707,11 @@ event_timed::mark_last_executed() time(&now); my_tz_UTC->gmt_sec_to_TIME(&time_now, now); - m_last_executed= time_now; // was m_execute_at + last_executed= time_now; // was execute_at #ifdef ANDREY_0 - m_last_executed= m_execute_at; + last_executed= execute_at; #endif - m_last_executed_changed= true; + last_executed_changed= true; } @@ -724,33 +731,33 @@ event_timed::update_fields(THD *thd) DBUG_ENTER("event_timed::update_time_fields"); - DBUG_PRINT("enter", ("name: %*s", m_name.length, m_name.str)); + DBUG_PRINT("enter", ("name: %*s", name.length, name.str)); //no need to update if nothing has changed - if (!(m_status_changed || m_last_executed_changed)) + if (!(status_changed || last_executed_changed)) goto done; if (!(table= evex_open_event_table(thd, TL_WRITE))) DBUG_RETURN(SP_OPEN_TABLE_FAILED); - if ((ret= evex_db_find_routine_aux(thd, m_db, m_name, table))) + if ((ret= evex_db_find_routine_aux(thd, dbname, name, table))) goto done; store_record(table,record[1]); table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; // Don't update create on row update. - if (m_last_executed_changed) + if (last_executed_changed) { table->field[EVEX_FIELD_LAST_EXECUTED]->set_notnull(); - table->field[EVEX_FIELD_LAST_EXECUTED]->store_time(&m_last_executed, + table->field[EVEX_FIELD_LAST_EXECUTED]->store_time(&last_executed, MYSQL_TIMESTAMP_DATETIME); - m_last_executed_changed= false; + last_executed_changed= false; } - if (m_status_changed) + if (status_changed) { table->field[EVEX_FIELD_STATUS]->set_notnull(); - table->field[EVEX_FIELD_STATUS]->store((longlong)m_status); - m_status_changed= false; + table->field[EVEX_FIELD_STATUS]->store((longlong)status); + status_changed= false; } if ((table->file->update_row(table->record[1],table->record[0]))) @@ -769,27 +776,27 @@ event_timed::get_show_create_event(THD *thd, uint *length) char *dst, *ret; uint len, tmp_len; - len = strlen("CREATE EVENT ") + m_db.length + strlen(".") + m_name.length + + len = strlen("CREATE EVENT ") + dbname.length + strlen(".") + name.length + strlen(" ON SCHEDULE ") + strlen("EVERY 5 MINUTE ") /* + strlen("ON COMPLETION ") - + (m_on_completion==MYSQL_EVENT_ON_COMPLETION_DROP? + + (on_completion==MYSQL_EVENT_ON_COMPLETION_DROP? strlen("NOT PRESERVE "):strlen("PRESERVE ")) - + (m_status==MYSQL_EVENT_ENABLED? + + (status==MYSQL_EVENT_ENABLED? strlen("ENABLE "):strlen("DISABLE ")) - + strlen("COMMENT \"") + m_comment.length + strlen("\" ") + + strlen("COMMENT \"") + comment.length + strlen("\" ") */ + strlen("DO ") + - + m_body.length + strlen(";"); + + body.length + strlen(";"); ret= dst= (char*) alloc_root(thd->mem_root, len); memcpy(dst, "CREATE EVENT ", tmp_len= strlen("CREATE EVENT ")); dst+= tmp_len; - memcpy(dst, m_db.str, tmp_len=m_db.length); + memcpy(dst, dbname.str, tmp_len=dbname.length); dst+= tmp_len; memcpy(dst, ".", tmp_len= strlen(".")); dst+= tmp_len; - memcpy(dst, m_name.str, tmp_len= m_name.length); + memcpy(dst, name.str, tmp_len= name.length); dst+= tmp_len; memcpy(dst, " ON SCHEDULE ", tmp_len= strlen(" ON SCHEDULE ")); dst+= tmp_len; @@ -798,19 +805,19 @@ event_timed::get_show_create_event(THD *thd, uint *length) /* memcpy(dst, "ON COMPLETION ", tmp_len =strlen("ON COMPLETION ")); dst+= tmp_len; - memcpy(dst, (m_on_completion==MYSQL_EVENT_ON_COMPLETION_DROP? + memcpy(dst, (on_completion==MYSQL_EVENT_ON_COMPLETION_DROP? "NOT PRESERVE ":"PRESERVE "), - tmp_len =(m_on_completion==MYSQL_EVENT_ON_COMPLETION_DROP? 13:9)); + tmp_len =(on_completion==MYSQL_EVENT_ON_COMPLETION_DROP? 13:9)); dst+= tmp_len; - memcpy(dst, (m_status==MYSQL_EVENT_ENABLED? + memcpy(dst, (status==MYSQL_EVENT_ENABLED? "ENABLE ":"DISABLE "), - tmp_len= (m_status==MYSQL_EVENT_ENABLED? 8:9)); + tmp_len= (status==MYSQL_EVENT_ENABLED? 8:9)); dst+=tmp_len; memcpy(dst, "COMMENT \"", tmp_len= strlen("COMMENT \"")); dst+= tmp_len; - memcpy(dst, m_comment.str, tmp_len= m_comment.length); + memcpy(dst, comment.str, tmp_len= comment.length); dst+= tmp_len; memcpy(dst, "\" ", tmp_len=2); dst+= tmp_len; @@ -818,7 +825,7 @@ event_timed::get_show_create_event(THD *thd, uint *length) memcpy(dst, "DO ", tmp_len=3); dst+= tmp_len; - memcpy(dst, m_body.str, tmp_len= m_body.length); + memcpy(dst, body.str, tmp_len= body.length); dst+= tmp_len; memcpy(dst, ";", 1); ++dst; @@ -865,21 +872,21 @@ event_timed::execute(THD *thd, MEM_ROOT *mem_root) // TODO Andrey : make this as member variable and delete in destructor empty_item_list.empty(); - if (!m_sphead && (ret= compile(thd, mem_root))) + if (!sphead && (ret= compile(thd, mem_root))) goto done; - ret= m_sphead->execute_procedure(thd, &empty_item_list); + ret= sphead->execute_procedure(thd, &empty_item_list); VOID(pthread_mutex_lock(&LOCK_running)); running= false; VOID(pthread_mutex_unlock(&LOCK_running)); done: - // Don't cache m_sphead if allocated on another mem_root - if (mem_root && m_sphead) + // Don't cache sphead if allocated on another mem_root + if (mem_root && sphead) { - delete m_sphead; - m_sphead= 0; + delete sphead; + sphead= 0; } DBUG_RETURN(ret); @@ -908,7 +915,7 @@ event_timed::compile(THD *thd, MEM_ROOT *mem_root) old_query_len= thd->query_length; old_query= thd->query; old_db= thd->db; - thd->db= m_db.str; + thd->db= dbname.str; thd->query= get_show_create_event(thd, &thd->query_length); DBUG_PRINT("event_timed::compile", ("query:%s",thd->query)); @@ -932,13 +939,13 @@ event_timed::compile(THD *thd, MEM_ROOT *mem_root) DBUG_RETURN(-1); } - m_sphead= lex.sphead; - m_sphead->m_db= m_db; + sphead= lex.sphead; + sphead->m_db= dbname; //copy also chistics since they will vanish otherwise we get 0x0 pointer // Todo : Handle sql_mode !! - m_sphead->set_definer(m_definer.str, m_definer.length); - m_sphead->set_info(0, 0, &lex.sp_chistics, 0/*sql_mode*/); - m_sphead->optimize(); + sphead->set_definer(definer.str, definer.length); + sphead->set_info(0, 0, &lex.sp_chistics, 0/*sql_mode*/); + sphead->optimize(); lex_end(&lex); thd->lex= old_lex; thd->query= old_query; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 57ac2d3bb55..47de9b0dbbf 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3676,26 +3676,25 @@ end_with_restore_list: case SQLCOM_CREATE_EVENT: { DBUG_ASSERT(lex->et); - if (! lex->et->m_db.str) + + if (! lex->et->dbname.str) { my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0)); delete lex->et; lex->et= 0; goto error; } - if (check_access(thd, EVENT_ACL, lex->et->m_db.str, 0, 0, 0, - is_schema_db(lex->et->m_db.str))) + if (check_access(thd, EVENT_ACL, lex->et->dbname.str, 0, 0, 0, + is_schema_db(lex->et->dbname.str))) break; - int result; - uint create_options= lex->create_info.options; - res= (result= evex_create_event(thd, lex->et, create_options)); - if (result == EVEX_OK) + + if (!(res= evex_create_event(thd, lex->et, (uint) lex->create_info.options))) send_ok(thd, 1); + /* lex->unit.cleanup() is called outside, no need to call it here */ delete lex->et; - lex->et= 0; - delete lex->sphead; + lex->et= 0; lex->sphead= 0; break; @@ -3703,53 +3702,50 @@ end_with_restore_list: case SQLCOM_ALTER_EVENT: { DBUG_ASSERT(lex->et); - if (! lex->et->m_db.str) + if (! lex->et->dbname.str) { my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0)); delete lex->et; lex->et= 0; goto error; } - if (check_access(thd, EVENT_ACL, lex->et->m_db.str, 0, 0, 0, - is_schema_db(lex->et->m_db.str))) + if (check_access(thd, EVENT_ACL, lex->et->dbname.str, 0, 0, 0, + is_schema_db(lex->et->dbname.str))) break; int result; - res= (result= evex_update_event(thd, lex->spname, lex->et)); + res= (result= evex_update_event(thd, lex->et, lex->spname)); switch (result) { case EVEX_OK: send_ok(thd, 1); break; case EVEX_KEY_NOT_FOUND: - my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), lex->et->m_name.str); + my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), lex->et->name.str); break; default: - my_error(ER_EVENT_CANT_ALTER, MYF(0), lex->et->m_name.str); + my_error(ER_EVENT_CANT_ALTER, MYF(0), lex->et->name.str); break; } + /* lex->unit.cleanup() is called outside, no need to call it here */ delete lex->et; + delete lex->sphead; lex->et= 0; - - if (lex->sphead) - { - delete lex->sphead; - lex->sphead= 0; - } + lex->sphead= 0; break; } case SQLCOM_DROP_EVENT: { DBUG_ASSERT(lex->et); - if (! lex->et->m_db.str) + if (! lex->et->dbname.str) { my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0)); delete lex->et; lex->et= 0; goto error; } - if (check_access(thd, EVENT_ACL, lex->et->m_db.str, 0, 0, 0, - is_schema_db(lex->et->m_db.str))) + if (check_access(thd, EVENT_ACL, lex->et->dbname.str, 0, 0, 0, + is_schema_db(lex->et->dbname.str))) break; int result; @@ -3759,10 +3755,10 @@ end_with_restore_list: send_ok(thd, 1); break; case EVEX_KEY_NOT_FOUND: - my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), lex->et->m_name.str); + my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), lex->et->name.str); break; default: - my_error(ER_EVENT_DROP_FAILED, MYF(0), lex->et->m_name.str); + my_error(ER_EVENT_DROP_FAILED, MYF(0), lex->et->name.str); break; } delete lex->et; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 082eee98e57..4b3e694b911 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1415,14 +1415,14 @@ ev_status: /* empty */ { LEX *lex=Lex; if (!lex->et_compile_phase) - lex->et->m_status= MYSQL_EVENT_ENABLED; + lex->et->status= MYSQL_EVENT_ENABLED; } | DISABLE_SYM { LEX *lex=Lex; if (!lex->et_compile_phase) - lex->et->m_status= MYSQL_EVENT_DISABLED; + lex->et->status= MYSQL_EVENT_DISABLED; } ; ev_starts: /* empty */ @@ -1457,13 +1457,13 @@ ev_on_completion: /* empty */ { LEX *lex=Lex; if (!lex->et_compile_phase) - lex->et->m_on_completion= MYSQL_EVENT_ON_COMPLETION_PRESERVE; + lex->et->on_completion= MYSQL_EVENT_ON_COMPLETION_PRESERVE; } | ON COMPLETION_SYM NOT_SYM PRESERVE_SYM { LEX *lex=Lex; if (!lex->et_compile_phase) - lex->et->m_on_completion= MYSQL_EVENT_ON_COMPLETION_DROP; + lex->et->on_completion= MYSQL_EVENT_ON_COMPLETION_DROP; } ; ev_comment: /* empty */ @@ -1497,7 +1497,7 @@ ev_sql_stmt: lex->sphead->m_body_begin= lex->ptr; if (!lex->et_compile_phase) - lex->et->m_body_begin= lex->ptr; + lex->et->body_begin= lex->ptr; } ev_sql_stmt_inner { From c1cb8db8cacf5f7e8f97457690a96da441a320a4 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 8 Dec 2005 15:34:11 +0100 Subject: [PATCH 11/28] - last bits of unneeded error checking in sql_parse.cc thrown away - fix a bug introduced with last commit ALTER EVENT a RENAME TO b; failed - misc sql/event.cc: - rename evex_db_find_routine_aux() to evex_db_find_event_aux() (better name) - change parameter order of db_update_event() - last bits to handle errors as close as possible to the place they occur - fix a bug introduced with last commit: first check for overwriting and event and then check whether the original one exists sql/event.h: add a new error code returned by event_timed::compile() in case of error sql/event_priv.h: rename sql/event_timed.cc: - function rename - add a bit of doc sql/share/errmsg.txt: - extend an error message so it's more informative - add a new error message sql/sql_parse.cc: refactor the cases for CREATE/ALTER/DROP event so use as much common code as possible. last bits of error checking unneeded in sql_parse.cc thrwon out. --- sql/event.cc | 106 ++++++++++++++++++++++++----------------- sql/event.h | 1 + sql/event_priv.h | 2 +- sql/event_timed.cc | 12 ++++- sql/share/errmsg.txt | 4 +- sql/sql_parse.cc | 110 +++++++++++-------------------------------- 6 files changed, 104 insertions(+), 131 deletions(-) diff --git a/sql/event.cc b/sql/event.cc index 5e01dc1c72a..c1c56cb6cb4 100644 --- a/sql/event.cc +++ b/sql/event.cc @@ -192,23 +192,23 @@ TABLE *evex_open_event_table(THD *thd, enum thr_lock_type lock_type) Find row in open mysql.event table representing event SYNOPSIS - evex_db_find_routine_aux() + evex_db_find_event_aux() 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 - SP_KEY_NOT_FOUND- No routine with given name + 0 - Routine found + EVEX_KEY_NOT_FOUND - No routine with given name */ int -evex_db_find_routine_aux(THD *thd, const LEX_STRING dbname, +evex_db_find_event_aux(THD *thd, const LEX_STRING dbname, const LEX_STRING ev_name, TABLE *table) { - byte key[MAX_KEY_LENGTH]; // db, name, optional key length type - DBUG_ENTER("evex_db_find_routine_aux"); + byte key[MAX_KEY_LENGTH]; + DBUG_ENTER("evex_db_find_event_aux"); DBUG_PRINT("enter", ("name: %.*s", ev_name.length, ev_name.str)); /* @@ -218,7 +218,8 @@ evex_db_find_routine_aux(THD *thd, const LEX_STRING dbname, 'db' and 'name' and the first key is the primary key over the same fields. */ - if (ev_name.length > table->field[1]->field_length) + if (dbname.length > table->field[EVEX_FIELD_DB]->field_length || + ev_name.length > table->field[EVEX_FIELD_NAME]->field_length) DBUG_RETURN(EVEX_KEY_NOT_FOUND); table->field[0]->store(dbname.str, dbname.length, &my_charset_bin); @@ -337,6 +338,9 @@ evex_fill_row(THD *thd, TABLE *table, event_timed *et, my_bool is_update) thd THD et event_timed object containing information for the event + 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". @@ -362,7 +366,7 @@ db_create_event(THD *thd, event_timed *et) } DBUG_PRINT("info", ("check existance of an event with the same name")); - if (!evex_db_find_routine_aux(thd, et->dbname, et->name, table)) + if (!evex_db_find_event_aux(thd, et->dbname, et->name, table)) { my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), et->name.str); goto err; @@ -377,7 +381,6 @@ db_create_event(THD *thd, event_timed *et) restore_record(table, s->default_values); // Get default values for fields - if (et->name.length > table->field[EVEX_FIELD_NAME]->field_length) { my_error(ER_TOO_LONG_IDENT, MYF(0), et->name.str); @@ -392,28 +395,28 @@ db_create_event(THD *thd, event_timed *et) 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)); - + my_error(ER_EVENT_NEITHER_M_EXPR_NOR_M_AT, MYF(0)); goto err; } strxmov(definer, et->definer_user.str, "@", et->definer_host.str, NullS); - if (table->field[EVEX_FIELD_DEFINER]-> + if ((ret=table->field[EVEX_FIELD_DEFINER]-> store(definer, et->definer_user.length + 1 + et->definer_host.length, - system_charset_info)) + system_charset_info))) { - my_error(ER_EVENT_STORE_FAILED, MYF(0), et->name.str); + my_error(ER_EVENT_STORE_FAILED, MYF(0), et->name.str, ret); goto err; } ((Field_timestamp *)table->field[EVEX_FIELD_CREATED])->set_time(); + + // evex_fill_row() calls my_error() in case of error so no need to handle it here if ((ret= evex_fill_row(thd, table, et, false))) goto err; - ret= EVEX_OK; if (table->file->write_row(table->record[0])) { - my_error(ER_EVENT_STORE_FAILED, MYF(0), et->name.str); + my_error(ER_EVENT_STORE_FAILED, MYF(0), et->name.str, ret); goto err; } @@ -438,7 +441,7 @@ err: /* - Used to execute ALTER EVENT + Used to execute ALTER EVENT. Pendant to evex_update_event(). SYNOPSIS db_update_event() @@ -452,7 +455,7 @@ err: */ static int -db_update_event(THD *thd, sp_name *new_name, event_timed *et) +db_update_event(THD *thd, event_timed *et, sp_name *new_name) { TABLE *table; int ret= EVEX_OPEN_TABLE_FAILED; @@ -467,13 +470,27 @@ db_update_event(THD *thd, sp_name *new_name, event_timed *et) my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0)); goto err; } - - if (EVEX_KEY_NOT_FOUND == evex_db_find_routine_aux(thd, et->dbname, et->name, + + // first look whether we overwrite + if (new_name && !evex_db_find_event_aux(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 whether 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->dbname, et->name, table)) { my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), et->name.str); goto err; } + store_record(table,record[1]); @@ -494,7 +511,7 @@ db_update_event(THD *thd, sp_name *new_name, event_timed *et) if ((ret= table->file->update_row(table->record[1], table->record[0]))) { - my_error(ER_EVENT_STORE_FAILED, MYF(0), et->name.str); + my_error(ER_EVENT_STORE_FAILED, MYF(0), et->name.str, ret); goto err; } @@ -545,7 +562,7 @@ db_find_event(THD *thd, sp_name *name, event_timed **ett, TABLE *tbl) goto done; } - if ((ret= evex_db_find_routine_aux(thd, name->m_db, name->m_name, table))) + if ((ret= evex_db_find_event_aux(thd, name->m_db, name->m_name, table))) { my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name->m_name.str); goto done; @@ -816,20 +833,20 @@ done: */ int -evex_update_event(THD *thd, event_timed *et, sp_name *name) +evex_update_event(THD *thd, event_timed *et, sp_name *new_name) { int ret, i; bool need_second_pass= true; - sp_name *spn= 0; DBUG_ENTER("evex_update_event"); DBUG_PRINT("enter", ("name: %*s", et->name.length, et->name.str)); /* db_update_event() opens & closes the table to prevent - crash later in the code when loading and compiling the new definition + crash later in the code when loading and compiling the new definition. + Also on error conditions my_error() is called so no need to handle here */ - if ((ret= db_update_event(thd, name, et))) + if ((ret= db_update_event(thd, et, new_name))) goto done; VOID(pthread_mutex_lock(&LOCK_evex_running)); @@ -839,22 +856,20 @@ evex_update_event(THD *thd, event_timed *et, sp_name *name) VOID(pthread_mutex_lock(&LOCK_event_arrays)); evex_remove_from_cache(&et->dbname, &et->name, false); if (et->status == MYSQL_EVENT_ENABLED) - if (name) - ret= evex_load_and_compile_event(thd, name, false); + { + if (new_name) + ret= evex_load_and_compile_event(thd, new_name, false); else { - spn= new sp_name(et->dbname, et->name); - ret= evex_load_and_compile_event(thd, spn, false); - delete spn; + sp_name spn(et->dbname, et->name); + ret= evex_load_and_compile_event(thd, &spn, false); } + if (ret == EVEX_COMPILE_ERROR) + my_error(ER_EVENT_COMPILE_ERROR, MYF(0)); + } VOID(pthread_mutex_unlock(&LOCK_event_arrays)); VOID(pthread_mutex_unlock(&LOCK_evex_running)); - /* - It is possible that 2 (or 1) pass(es) won't find the event in memory. - The reason is that DISABLED events are not cached. - */ - done: DBUG_RETURN(ret); } @@ -885,7 +900,7 @@ evex_drop_event(THD *thd, event_timed *et, bool drop_if_exists) goto done; } - if (!(ret= evex_db_find_routine_aux(thd, et->dbname, et->name, table))) + if (!(ret= evex_db_find_event_aux(thd, et->dbname, et->name, table))) { if ((ret= table->file->delete_row(table->record[0]))) { @@ -893,15 +908,18 @@ evex_drop_event(THD *thd, event_timed *et, bool drop_if_exists) goto done; } } - else if (ret == SP_KEY_NOT_FOUND && drop_if_exists) - { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + 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", et->name.str); - ret= 0; - goto done; - } else + "Event", et->name.str); + ret= 0; + } else + my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), et->name.str); goto done; + } VOID(pthread_mutex_lock(&LOCK_evex_running)); if (evex_is_running) diff --git a/sql/event.h b/sql/event.h index fbb39ff53a2..12a965c1ec8 100644 --- a/sql/event.h +++ b/sql/event.h @@ -31,6 +31,7 @@ extern ulong opt_event_executor; #define EVEX_PARSE_ERROR SP_PARSE_ERROR #define EVEX_INTERNAL_ERROR SP_INTERNAL_ERROR #define EVEX_NO_DB_ERROR SP_NO_DB_ERROR +#define EVEX_COMPILE_ERROR -19 #define EVEX_GENERAL_ERROR -20 #define EVEX_BAD_IDENTIFIER SP_BAD_IDENTIFIER #define EVEX_BODY_TOO_LONG SP_BODY_TOO_LONG diff --git a/sql/event_priv.h b/sql/event_priv.h index ceaa91ae199..bb18dee6e6d 100644 --- a/sql/event_priv.h +++ b/sql/event_priv.h @@ -55,7 +55,7 @@ int my_time_compare(TIME *a, TIME *b); int -evex_db_find_routine_aux(THD *thd, const LEX_STRING dbname, +evex_db_find_event_aux(THD *thd, const LEX_STRING dbname, const LEX_STRING rname, TABLE *table); TABLE * diff --git a/sql/event_timed.cc b/sql/event_timed.cc index 8d494a921f0..ff52c99cb50 100644 --- a/sql/event_timed.cc +++ b/sql/event_timed.cc @@ -740,7 +740,7 @@ event_timed::update_fields(THD *thd) if (!(table= evex_open_event_table(thd, TL_WRITE))) DBUG_RETURN(SP_OPEN_TABLE_FAILED); - if ((ret= evex_db_find_routine_aux(thd, dbname, name, table))) + if ((ret= evex_db_find_event_aux(thd, dbname, name, table))) goto done; store_record(table,record[1]); @@ -893,6 +893,14 @@ done: } +/* + Returns + 0 - Success + EVEX_COMPILE_ERROR - Error during compilation + +*/ + + int event_timed::compile(THD *thd, MEM_ROOT *mem_root) { @@ -936,7 +944,7 @@ event_timed::compile(THD *thd, MEM_ROOT *mem_root) // QQ: anything else ? lex_end(&lex); thd->lex= old_lex; - DBUG_RETURN(-1); + DBUG_RETURN(EVEX_COMPILE_ERROR); } sphead= lex.sphead; diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index 0b8023dc4a5..8d25a27b089 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -5724,7 +5724,7 @@ ER_PLUGIN_IS_NOT_LOADED ER_EVENT_ALREADY_EXISTS eng "Event %s already exists" ER_EVENT_STORE_FAILED - eng "Failed to create event %s" + eng "Failed to store event %s. Error code %d from storage engine." ER_EVENT_DOES_NOT_EXIST eng "Event %s does not exist" ER_EVENT_CANT_ALTER @@ -5747,3 +5747,5 @@ ER_EVENT_CANNOT_LOAD_FROM_TABLE eng "Cannot load from mysql.event. Table probably corrupted" ER_EVENT_CANNOT_DELETE eng "Failed to delete the event from mysql.event" +ER_EVENT_COMPILE_ERROR + eng "Error during compilation of event's body" diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 47de9b0dbbf..cd01a0e7181 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3674,96 +3674,40 @@ end_with_restore_list: break; } case SQLCOM_CREATE_EVENT: - { - DBUG_ASSERT(lex->et); - - if (! lex->et->dbname.str) - { - my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0)); - delete lex->et; - lex->et= 0; - goto error; - } - if (check_access(thd, EVENT_ACL, lex->et->dbname.str, 0, 0, 0, - is_schema_db(lex->et->dbname.str))) - break; - - if (!(res= evex_create_event(thd, lex->et, (uint) lex->create_info.options))) - send_ok(thd, 1); - - /* lex->unit.cleanup() is called outside, no need to call it here */ - delete lex->et; - delete lex->sphead; - lex->et= 0; - lex->sphead= 0; - - break; - } case SQLCOM_ALTER_EVENT: - { - DBUG_ASSERT(lex->et); - if (! lex->et->dbname.str) - { - my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0)); - delete lex->et; - lex->et= 0; - goto error; - } - if (check_access(thd, EVENT_ACL, lex->et->dbname.str, 0, 0, 0, - is_schema_db(lex->et->dbname.str))) - break; - - int result; - res= (result= evex_update_event(thd, lex->et, lex->spname)); - switch (result) { - case EVEX_OK: - send_ok(thd, 1); - break; - case EVEX_KEY_NOT_FOUND: - my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), lex->et->name.str); - break; - default: - my_error(ER_EVENT_CANT_ALTER, MYF(0), lex->et->name.str); - break; - } - /* lex->unit.cleanup() is called outside, no need to call it here */ - delete lex->et; - delete lex->sphead; - lex->et= 0; - lex->sphead= 0; - - break; - } case SQLCOM_DROP_EVENT: { DBUG_ASSERT(lex->et); - if (! lex->et->dbname.str) - { - my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0)); - delete lex->et; - lex->et= 0; - goto error; - } - if (check_access(thd, EVENT_ACL, lex->et->dbname.str, 0, 0, 0, - is_schema_db(lex->et->dbname.str))) - break; + do { + if (! lex->et->dbname.str) + { + my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0)); + res= true; + break; + } + if (check_access(thd, EVENT_ACL, lex->et->dbname.str, 0, 0, 0, + is_schema_db(lex->et->dbname.str))) + break; + switch (lex->sql_command) { + case SQLCOM_CREATE_EVENT: + res= evex_create_event(thd, lex->et, (uint) lex->create_info.options); + break; + case SQLCOM_ALTER_EVENT: + res= evex_update_event(thd, lex->et, lex->spname); + break; + case SQLCOM_DROP_EVENT: + evex_drop_event(thd, lex->et, lex->drop_if_exists); + default:; + } + if (!res) + send_ok(thd, 1); - int result; - res= (result= evex_drop_event(thd, lex->et, lex->drop_if_exists)); - switch (result) { - case EVEX_OK: - send_ok(thd, 1); - break; - case EVEX_KEY_NOT_FOUND: - my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), lex->et->name.str); - break; - default: - my_error(ER_EVENT_DROP_FAILED, MYF(0), lex->et->name.str); - break; - } + /* lex->unit.cleanup() is called outside, no need to call it here */ + } while (0); delete lex->et; + delete lex->sphead; lex->et= 0; - + lex->sphead= 0; break; } case SQLCOM_SHOW_CREATE_EVENT: From 1c5573a47c4ef43558770c1c4e536ba39114dcbb Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 8 Dec 2005 20:37:54 +0100 Subject: [PATCH 12/28] WL #1034 update - handle better non-latin1 identifiers sql/event.cc: - introduce a wrapper to compare easily and correctly LEX_STRINGs - remove few unneeded variables - remove need_second_pass by restructuring the code. this brings performance boost because the code bails out from the loop once it hits what it looks for - handle ALTER EVENT name RENAME TO name (throw an error message). sql/event_executor.cc: - comment about DBUG_FAULTY_THR - indent fix sql/event_timed.cc: - fix a problem when event name is not latin1. a cyrillic event name was crashing the parser so the I emulate SET NAMES utf8. The data is already in utf8, being loaded from the disk. sql/share/errmsg.txt: add new error message --- sql/event.cc | 86 ++++++++++++++++++++++++------------------- sql/event_executor.cc | 11 +++++- sql/event_timed.cc | 28 +++++++++++++- sql/share/errmsg.txt | 2 + 4 files changed, 87 insertions(+), 40 deletions(-) diff --git a/sql/event.cc b/sql/event.cc index c1c56cb6cb4..7a9c476a35a 100644 --- a/sql/event.cc +++ b/sql/event.cc @@ -89,6 +89,15 @@ MEM_ROOT evex_mem_root; a < b -> -1 */ +static +int sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs) +{ + return cs->coll->strnncollsp(cs, + (unsigned char *) s.str,s.length, + (unsigned char *) t.str,t.length, 0); +} + + inline int my_time_compare(TIME *a, TIME *b) { @@ -472,11 +481,20 @@ db_update_event(THD *thd, event_timed *et, sp_name *new_name) } // first look whether we overwrite - if (new_name && !evex_db_find_event_aux(thd, new_name->m_db, new_name->m_name, - table)) + if (new_name) { - my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), new_name->m_name.str); - goto err; + if (!sortcmp_lex_string(et->name, new_name->m_name, system_charset_info) && + !sortcmp_lex_string(et->dbname, new_name->m_db, system_charset_info)) + { + my_error(ER_EVENT_SAME_NAME, MYF(0), et->name.str); + goto err; + } + + if (!evex_db_find_event_aux(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 whether there is such an event. don't exchange the blocks @@ -676,7 +694,6 @@ static int evex_remove_from_cache(LEX_STRING *db, LEX_STRING *name, bool use_lock) { uint i; - bool need_second_pass= true; DBUG_ENTER("evex_remove_from_cache"); /* @@ -689,37 +706,31 @@ evex_remove_from_cache(LEX_STRING *db, LEX_STRING *name, bool use_lock) for (i= 0; i < evex_executing_queue.elements; ++i) { - event_timed **p_et= dynamic_element(&evex_executing_queue, i, event_timed**); - event_timed *ett= *p_et; - DBUG_PRINT("info", ("[%s.%s]==[%s.%s]?",db->str,name->str, - ett->dbname.str, ett->name.str)); - if (name->length == ett->name.length && - db->length == ett->dbname.length && - 0 == strncmp(db->str, ett->dbname.str, db->length) && - 0 == strncmp(name->str, ett->name.str, name->length) - ) + event_timed *et= *dynamic_element(&evex_executing_queue, i, event_timed**); + DBUG_PRINT("info", ("[%s.%s]==[%s.%s]?",db->str,name->str, et->dbname.str, + et->name.str)); + if (!sortcmp_lex_string(*name, et->name, system_charset_info) && + !sortcmp_lex_string(*db, et->dbname, system_charset_info)) { - int idx; + int idx= get_index_dynamic(&events_array, (gptr) et); //we are lucky the event is in the executing queue, no need of second pass - need_second_pass= false; - idx= get_index_dynamic(&events_array, (gptr) ett); if (idx == -1) { //this should never happen DBUG_PRINT("error", (" get_index_dynamic problem. %d." - "i=%d idx=%d evex_ex_queue.buf=%p evex_ex_queue.elements=%d ett=%p\n" - "events_array=%p events_array.elements=%d events_array.buf=%p\n" - "p_et=%p ett=%p", - __LINE__, i, idx, &evex_executing_queue.buffer, - evex_executing_queue.elements, ett, &events_array, - events_array.elements, events_array.buffer, p_et, ett)); + "i=%d idx=%d evex_ex_queue.buf=%p evex_ex_queue.elements=%d et=%p\n" + "events_array=%p events_array.elements=%d events_array.buf=%p et=%p\n", + __LINE__, i, idx, &evex_executing_queue.buffer, + evex_executing_queue.elements, et, &events_array, + events_array.elements, events_array.buffer, et)); DBUG_ASSERT(0); } //destruct first and then remove. the destructor will delete sp_head - ett->free_sp(); + et->free_sp(); delete_dynamic_element(&events_array, idx); delete_dynamic_element(&evex_executing_queue, i); // ok, we have cleaned + goto done; } } @@ -733,20 +744,21 @@ evex_remove_from_cache(LEX_STRING *db, LEX_STRING *name, bool use_lock) For instance, second_pass is needed when an event was created as DISABLED but then altered as ENABLED. */ - if (need_second_pass) - //we haven't found the event in the executing queue. This is nice! :) - //Look for it in the events_array. - for (i= 0; i < events_array.elements; ++i) + /* + we haven't found the event in the executing queue. This is nice! :) + Look for it in the events_array. + */ + for (i= 0; i < events_array.elements; ++i) + { + event_timed *et= dynamic_element(&events_array, i, event_timed*); + + if (!sortcmp_lex_string(*name, et->name, system_charset_info) && + !sortcmp_lex_string(*db, et->dbname, system_charset_info)) { - event_timed *ett= dynamic_element(&events_array, i, event_timed*); - - if (name->length == ett->name.length && - db->length == ett->dbname.length && - 0 == strncmp(db->str, ett->dbname.str, db->length) && - 0 == strncmp(name->str, ett->name.str, name->length) - ) - delete_dynamic_element(&events_array, i); - } + delete_dynamic_element(&events_array, i); + break; + } + } done: if (use_lock) diff --git a/sql/event_executor.cc b/sql/event_executor.cc index f3db374af54..1e45d984425 100644 --- a/sql/event_executor.cc +++ b/sql/event_executor.cc @@ -19,6 +19,15 @@ #include "event_priv.h" #include "sp.h" + +/* + Make this define DBUG_FAULTY_THR to be able to put breakpoints inside + code used by the scheduler's thread(s). In this case user connections + are not possible because the scheduler thread code is ran inside the + main thread (no spawning takes place. If you want to debug client + connection then start with --one-thread and make the define + DBUG_FAULTY_THR2 ! +*/ #define DBUG_FAULTY_THR2 extern ulong thread_created; @@ -204,7 +213,7 @@ event_executor_main(void *arg) VOID(pthread_mutex_unlock(&LOCK_evex_running)); if (evex_load_events_from_db(thd)) - goto err; + goto err; THD_CHECK_SENTRY(thd); /* Read queries from the IO/THREAD until this thread is killed */ diff --git a/sql/event_timed.cc b/sql/event_timed.cc index ff52c99cb50..22c6160edab 100644 --- a/sql/event_timed.cc +++ b/sql/event_timed.cc @@ -904,6 +904,7 @@ done: int event_timed::compile(THD *thd, MEM_ROOT *mem_root) { + int ret= 0; MEM_ROOT *tmp_mem_root= 0; LEX *old_lex= thd->lex, lex; char *old_db; @@ -912,6 +913,19 @@ event_timed::compile(THD *thd, MEM_ROOT *mem_root) char *old_query; uint old_query_len; st_sp_chistics *p; + CHARSET_INFO *old_character_set_client, *old_collation_connection, + *old_character_set_results; + + old_character_set_client= thd->variables.character_set_client; + old_character_set_results= thd->variables.character_set_results; + old_collation_connection= thd->variables.collation_connection; + + thd->variables.character_set_client= + thd->variables.character_set_results= + thd->variables.collation_connection= + get_charset_by_csname("utf8", MY_CS_PRIMARY, MYF(MY_WME)); + + thd->update_charset(); DBUG_ENTER("event_timed::compile"); // change the memory root for the execution time @@ -944,7 +958,9 @@ event_timed::compile(THD *thd, MEM_ROOT *mem_root) // QQ: anything else ? lex_end(&lex); thd->lex= old_lex; - DBUG_RETURN(EVEX_COMPILE_ERROR); + + ret= EVEX_COMPILE_ERROR; + goto done; } sphead= lex.sphead; @@ -954,17 +970,25 @@ event_timed::compile(THD *thd, MEM_ROOT *mem_root) sphead->set_definer(definer.str, definer.length); sphead->set_info(0, 0, &lex.sp_chistics, 0/*sql_mode*/); sphead->optimize(); + ret= 0; +done: lex_end(&lex); thd->lex= old_lex; thd->query= old_query; thd->query_length= old_query_len; thd->db= old_db; + + thd->variables.character_set_client= old_character_set_client; + thd->variables.character_set_results= old_character_set_results; + thd->variables.collation_connection= old_collation_connection; + thd->update_charset(); + /* Change the memory root for the execution time. */ if (mem_root) thd->mem_root= tmp_mem_root; - DBUG_RETURN(0); + DBUG_RETURN(ret); } diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index 8d25a27b089..abd4b746aa8 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -5749,3 +5749,5 @@ ER_EVENT_CANNOT_DELETE eng "Failed to delete the event from mysql.event" ER_EVENT_COMPILE_ERROR eng "Error during compilation of event's body" +ER_EVENT_SAME_NAME + eng "Same old and new event name" From 81eadfcac18f3adc29c55d440e62823855f5b070 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 12 Dec 2005 21:19:19 +0100 Subject: [PATCH 13/28] WL#1034 update QUEUE implementation working now. this should be ready more or less for testing once the debug output is being cleaned and some things around DYNAMIC_ARRAY are cleaned - fix handling in case of errors that lead to crashes, now no more crashes in case of table corruption and such. include/queues.h: introduce a safe version of queue_insert that will extend the queue if necessary. the auto_extent is passed to the _ex version of init_queue() mysys/queues.c: add init_queue_ex() implementation add queue_insert_safe() implementation sql/event.cc: - move mysql_priv.h inclusion to event_priv.h - use a priority queue instead of DYNAMIC_ARRAY which is sorted sql/event.h: reorder sql/event_executor.cc: reorder sql/event_priv.h: - reorder a bit - add macroses and functions for queue manipulation which stay on top of QUEUE (partly implemented for DYNAMIC_ARRAY but will be cleared to be only for QUEUE). sql/event_timed.cc: allocate one more byte and zeroterminate, really --- include/queues.h | 6 ++ mysys/queues.c | 67 +++++++++++- sql/event.cc | 127 ++++++++++++++++++++-- sql/event.h | 6 +- sql/event_executor.cc | 242 +++++++++++++++++++++++++----------------- sql/event_priv.h | 80 ++++++++++++-- sql/event_timed.cc | 7 +- 7 files changed, 410 insertions(+), 125 deletions(-) diff --git a/include/queues.h b/include/queues.h index a8b676b763c..8cb053831f2 100644 --- a/include/queues.h +++ b/include/queues.h @@ -35,6 +35,7 @@ typedef struct st_queue { uint offset_to_key; /* compare is done on element+offset */ int max_at_top; /* Set if queue_top gives max */ int (*compare)(void *, byte *,byte *); + uint auto_extent; } QUEUE; #define queue_top(queue) ((queue)->root[1]) @@ -49,14 +50,19 @@ typedef int (*queue_compare)(void *,byte *, byte *); int init_queue(QUEUE *queue,uint max_elements,uint offset_to_key, pbool max_at_top, queue_compare compare, void *first_cmp_arg); +int init_queue_ex(QUEUE *queue,uint max_elements,uint offset_to_key, + pbool max_at_top, queue_compare compare, + void *first_cmp_arg, uint auto_extent); int reinit_queue(QUEUE *queue,uint max_elements,uint offset_to_key, pbool max_at_top, queue_compare compare, void *first_cmp_arg); int resize_queue(QUEUE *queue, uint max_elements); void delete_queue(QUEUE *queue); void queue_insert(QUEUE *queue,byte *element); +int queue_insert_safe(QUEUE *queue, byte *element); byte *queue_remove(QUEUE *queue,uint idx); #define queue_remove_all(queue) { (queue)->elements= 0; } +#define queue_is_full(queue) (queue->elements == queue->max_elements) void _downheap(QUEUE *queue,uint idx); void queue_fix(QUEUE *queue); #define is_queue_inited(queue) ((queue)->root != 0) diff --git a/mysys/queues.c b/mysys/queues.c index 0e4e251f7e7..8e572a0f195 100644 --- a/mysys/queues.c +++ b/mysys/queues.c @@ -19,7 +19,7 @@ Implemention of queues from "Algoritms in C" by Robert Sedgewick. An optimisation of _downheap suggested in Exercise 7.51 in "Data Structures & Algorithms in C++" by Mark Allen Weiss, Second Edition - was implemented by Mikael Ronström 2005. Also the O(N) algorithm + was implemented by Mikael Ronstrom 2005. Also the O(N) algorithm of queue_fix was implemented. */ @@ -67,6 +67,46 @@ int init_queue(QUEUE *queue, uint max_elements, uint offset_to_key, } + +/* + Init queue, uses init_queue internally for init work but also accepts + auto_extent as parameter + + SYNOPSIS + init_queue_ex() + queue Queue to initialise + max_elements Max elements that will be put in queue + offset_to_key Offset to key in element stored in queue + Used when sending pointers to compare function + max_at_top Set to 1 if you want biggest element on top. + compare Compare function for elements, takes 3 arguments. + first_cmp_arg First argument to compare function + auto_extent When the queue is full and there is insert operation + extend the queue. + + NOTES + Will allocate max_element pointers for queue array + + RETURN + 0 ok + 1 Could not allocate memory +*/ + +int init_queue_ex(QUEUE *queue, uint max_elements, uint offset_to_key, + pbool max_at_top, int (*compare) (void *, byte *, byte *), + void *first_cmp_arg, uint auto_extent) +{ + int ret; + DBUG_ENTER("init_queue_ex"); + + if ((ret= init_queue(queue, max_elements, offset_to_key, max_at_top, compare, + first_cmp_arg))) + DBUG_RETURN(ret); + + queue->auto_extent= auto_extent; + DBUG_RETURN(0); +} + /* Reinitialize queue for other usage @@ -192,6 +232,31 @@ void queue_insert(register QUEUE *queue, byte *element) } } +/* + Does safe insert. If no more space left on the queue resize it. + Return codes: + 0 - OK + 1 - Cannot allocate more memory + 2 - auto_extend is 0, the operation would + +*/ + +int queue_insert_safe(register QUEUE *queue, byte *element) +{ + + if (queue->elements == queue->max_elements) + { + if (!queue->auto_extent) + return 2; + else if (resize_queue(queue, queue->max_elements + queue->auto_extent)) + return 1; + } + + queue_insert(queue, element); + return 0; +} + + /* Remove item from queue */ /* Returns pointer to removed element */ diff --git a/sql/event.cc b/sql/event.cc index 7a9c476a35a..87a846cd2dd 100644 --- a/sql/event.cc +++ b/sql/event.cc @@ -14,16 +14,14 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "mysql_priv.h" -#include "event.h" #include "event_priv.h" +#include "event.h" #include "sp.h" /* TODO list : - The default value of created/modified should not be 0000-00-00 because of STRICT mode restricions. - - Remove m_ prefixes of member variables. - Use timestamps instead of datetime. @@ -53,9 +51,17 @@ - Consider using conditional variable when doing shutdown instead of waiting till all worker threads end. - Make event_timed::get_show_create_event() work + - Add function documentation whenever needed. + - Add logging to file + - Move comparison code to class event_timed + + - Overload event_timed::new to put the event directly in the DYNAMIC_ARRAY. + This will skip copy operation as well as will simplify the code which is + now aware of events_array DYNAMIC_ARRAY + Warning: - For now parallel execution is not possible because the same sp_head cannot be executed few times!!! There is still no lock attached to particular event. @@ -67,19 +73,60 @@ Warning: bool mysql_event_table_exists= 1; DYNAMIC_ARRAY events_array; -DYNAMIC_ARRAY evex_executing_queue; +DYNAMIC_ARRAY EXEC_QUEUE_DARR_NAME; +QUEUE EXEC_QUEUE_QUEUE_NAME; MEM_ROOT evex_mem_root; - //extern volatile uint thread_running; //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// //////////////// Static functions follow /////////////////////////// //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// +void +evex_queue_init(EVEX_QUEUE_TYPE *queue) +{ +#ifndef EVEX_USE_QUEUE + VOID(my_init_dynamic_array(queue, sizeof(event_timed *), 50, 100)); +#else + if (init_queue_ex(queue, 100 /*num_el*/, 0 /*offset*/, + 0 /*smallest_on_top*/, event_timed_compare_q, NULL, + 100 /*auto_extent*/)) + sql_print_error("Insufficient memory to initialize executing queue."); +#endif +} +int +evex_queue_insert2(EVEX_QUEUE_TYPE *queue, EVEX_PTOQEL element) +{ +#ifndef EVEX_USE_QUEUE + VOID(push_dynamic(queue, element)); + return 0; +#else + return queue_insert_safe(queue, element); +#endif +} + +void +evex_queue_top_updated(EVEX_QUEUE_TYPE *queue) +{ +#ifdef EVEX_USE_QUEUE + queue_replaced(queue); +#endif +} + +void +evex_queue_sort(EVEX_QUEUE_TYPE *queue) +{ +#ifndef EVEX_USE_QUEUE + qsort((gptr) dynamic_element(queue, 0, event_timed**), + queue->elements, + sizeof(event_timed **), + (qsort_cmp) event_timed_compare); +#endif +} /* NOTE Andrey: Document better Compares two TIME structures. @@ -98,7 +145,7 @@ int sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs) } -inline int +int my_time_compare(TIME *a, TIME *b) { /* @@ -107,6 +154,7 @@ my_time_compare(TIME *a, TIME *b) */ DBUG_ENTER("my_time_compare"); + if (a->year > b->year) DBUG_RETURN(1); @@ -143,19 +191,53 @@ my_time_compare(TIME *a, TIME *b) if (a->second < b->second) DBUG_RETURN(-1); - /*!! second_part is not compared !*/ + + if (a->second_part > b->second_part) + DBUG_RETURN(1); + + if (a->second_part < b->second_part) + DBUG_RETURN(-1); + DBUG_RETURN(0); } +int +evex_time_diff(TIME *a, TIME *b) +{ + my_bool in_gap; + DBUG_ENTER("my_time_diff"); + + return sec_since_epoch_TIME(a) - sec_since_epoch_TIME(b); +} + inline int event_timed_compare(event_timed **a, event_timed **b) { - return my_time_compare(&(*a)->execute_at, &(*b)->execute_at); + my_ulonglong a_t, b_t; + a_t= TIME_to_ulonglong_datetime(&(*a)->execute_at)*100L + + (*a)->execute_at.second_part; + b_t= TIME_to_ulonglong_datetime(&(*b)->execute_at)*100L + + (*b)->execute_at.second_part; + + if (a_t > b_t) + return 1; + else if (a_t < b_t) + return -1; + else + return 0; + +// return my_time_compare(&(*a)->execute_at, &(*b)->execute_at); } +int +event_timed_compare_q(void *vptr, byte* a, byte *b) +{ + return event_timed_compare((event_timed **)&a, (event_timed **)&b); +} + /* Open mysql.event table for read @@ -660,7 +742,10 @@ evex_load_and_compile_event(THD * thd, sp_name *spn, bool use_lock) VOID(push_dynamic(&events_array,(gptr) ett)); ett_copy= dynamic_element(&events_array, events_array.elements - 1, event_timed*); +/** VOID(push_dynamic(&evex_executing_queue, (gptr) &ett_copy)); +**/ + evex_queue_insert(&EVEX_EQ_NAME, (EVEX_PTOQEL) ett_copy); /* There is a copy in the array which we don't need. sphead won't be @@ -674,11 +759,14 @@ evex_load_and_compile_event(THD * thd, sp_name *spn, bool use_lock) qsort of events_array.elements (the current number of elements). We know that the elements are stored in a contiguous block w/o holes. */ +/** qsort((gptr) dynamic_element(&evex_executing_queue, 0, event_timed**), evex_executing_queue.elements, sizeof(event_timed **), (qsort_cmp) event_timed_compare); - +**/ + evex_queue_sort(&EVEX_EQ_NAME); + if (use_lock) VOID(pthread_mutex_unlock(&LOCK_event_arrays)); @@ -703,7 +791,7 @@ evex_remove_from_cache(LEX_STRING *db, LEX_STRING *name, bool use_lock) if (use_lock) VOID(pthread_mutex_lock(&LOCK_event_arrays)); - +/** for (i= 0; i < evex_executing_queue.elements; ++i) { event_timed *et= *dynamic_element(&evex_executing_queue, i, event_timed**); @@ -733,6 +821,25 @@ evex_remove_from_cache(LEX_STRING *db, LEX_STRING *name, bool use_lock) goto done; } } +**/ + for (i= 0; i < evex_queue_num_elements(EVEX_EQ_NAME); ++i) + { + event_timed *et= *evex_queue_element(&EVEX_EQ_NAME, i, event_timed**); + DBUG_PRINT("info", ("[%s.%s]==[%s.%s]?",db->str,name->str, et->dbname.str, + et->name.str)); + if (!sortcmp_lex_string(*name, et->name, system_charset_info) && + !sortcmp_lex_string(*db, et->dbname, system_charset_info)) + { + int idx= get_index_dynamic(&events_array, (gptr) et); + //we are lucky the event is in the executing queue, no need of second pass + //destruct first and then remove. the destructor will delete sp_head + et->free_sp(); + evex_queue_delete_element(&EVEX_EQ_NAME, idx); + evex_queue_delete_element(&EVEX_EQ_NAME, i); + // ok, we have cleaned + goto done; + } + } /* ToDo Andrey : Think about whether second pass is needed. All events diff --git a/sql/event.h b/sql/event.h index 12a965c1ec8..f3b49a99488 100644 --- a/sql/event.h +++ b/sql/event.h @@ -16,11 +16,9 @@ #ifndef _EVENT_H_ #define _EVENT_H_ -#include "sp_head.h" + #include "sp.h" - - -extern ulong opt_event_executor; +#include "sp_head.h" #define EVEX_OK SP_OK #define EVEX_KEY_NOT_FOUND SP_KEY_NOT_FOUND diff --git a/sql/event_executor.cc b/sql/event_executor.cc index 1e45d984425..74ac1b12297 100644 --- a/sql/event_executor.cc +++ b/sql/event_executor.cc @@ -14,9 +14,8 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "mysql_priv.h" -#include "event.h" #include "event_priv.h" +#include "event.h" #include "sp.h" @@ -40,6 +39,7 @@ pthread_mutex_t LOCK_event_arrays, bool evex_is_running= false; +ulonglong evex_main_thread_id= 0; ulong opt_event_executor; my_bool event_executor_running_global_var= false; static my_bool evex_mutexes_initted= false; @@ -74,6 +74,7 @@ void evex_init_mutexes() pthread_mutex_init(&LOCK_evex_running, MY_MUTEX_INIT_FAST); } + int init_events() { @@ -84,7 +85,7 @@ init_events() DBUG_PRINT("info",("Starting events main thread")); evex_init_mutexes(); - + VOID(pthread_mutex_lock(&LOCK_evex_running)); evex_is_running= false; event_executor_running_global_var= false; @@ -109,6 +110,7 @@ shutdown_events() VOID(pthread_mutex_lock(&LOCK_evex_running)); VOID(pthread_mutex_unlock(&LOCK_evex_running)); + pthread_mutex_destroy(&LOCK_event_arrays); pthread_mutex_destroy(&LOCK_workers_count); pthread_mutex_destroy(&LOCK_evex_running); @@ -182,7 +184,7 @@ event_executor_main(void *arg) if (init_event_thread(thd)) goto err; - + // make this thread invisible it has no vio -> show processlist won't see thd->system_thread= 1; @@ -200,7 +202,12 @@ event_executor_main(void *arg) thus data should be freed at later stage. */ VOID(my_init_dynamic_array(&events_array, sizeof(event_timed), 50, 100)); +/** VOID(my_init_dynamic_array(&evex_executing_queue, sizeof(event_timed *), 50, 100)); +**/ + + evex_queue_init(&EVEX_EQ_NAME); + VOID(pthread_mutex_unlock(&LOCK_event_arrays)); /* @@ -217,107 +224,132 @@ event_executor_main(void *arg) THD_CHECK_SENTRY(thd); /* Read queries from the IO/THREAD until this thread is killed */ + evex_main_thread_id= thd->thread_id; + while (!thd->killed) { TIME time_now; my_time_t now; my_ulonglong cnt; + event_timed *et; DBUG_PRINT("info", ("EVEX External Loop %d", ++cnt)); thd->proc_info = "Sleeping"; - my_sleep(1000000);// sleep 1s - if (!event_executor_running_global_var) + if (!evex_queue_num_elements(EVEX_EQ_NAME) || + !event_executor_running_global_var) + { + my_sleep(1000000);// sleep 1s continue; - time(&now); - my_tz_UTC->gmt_sec_to_TIME(&time_now, now); - - VOID(pthread_mutex_lock(&LOCK_event_arrays)); - for (i= 0; (i < evex_executing_queue.elements) && !thd->killed; ++i) - { - event_timed *et= *dynamic_element(&evex_executing_queue,i,event_timed**); -// printf("%llu\n", TIME_to_ulonglong_datetime(&et->execute_at)); - if (!event_executor_running_global_var) - break; - - thd->proc_info = "Iterating"; - THD_CHECK_SENTRY(thd); - /* - if this is the first event which is after time_now then no - more need to iterate over more elements since the array is sorted. - */ - if (et->execute_at.year > 1969 && - my_time_compare(&time_now, &et->execute_at) == -1) - break; - - if (et->status == MYSQL_EVENT_ENABLED && - !check_access(thd, EVENT_ACL, et->dbname.str, 0, 0, 0, - is_schema_db(et->dbname.str))) - { - pthread_t th; - - DBUG_PRINT("info", (" Spawning a thread %d", ++iter_num)); - thd->proc_info = "Starting new thread"; - sql_print_information(" Spawning a thread %d", ++iter_num); -#ifndef DBUG_FAULTY_THR - if (pthread_create(&th, NULL, event_executor_worker, (void*)et)) - { - sql_print_error("Problem while trying to create a thread"); - UNLOCK_MUTEX_AND_BAIL_OUT(LOCK_event_arrays, err); - } -#else - event_executor_worker((void *) et); -#endif - et->mark_last_executed(); - thd->proc_info = "Computing next time"; - et->compute_next_execution_time(); - et->update_fields(thd); - if ((et->execute_at.year && !et->expression) - || TIME_to_ulonglong_datetime(&et->execute_at) == 0L) - et->flags |= EVENT_EXEC_NO_MORE; - } } - /* - Let's remove elements which won't be executed any more - The number is "i" and it is <= up to evex_executing_queue.elements - */ - j= 0; - while (j < i && j < evex_executing_queue.elements) + { - event_timed *et= *dynamic_element(&evex_executing_queue, j, event_timed**); - if ((et->flags & EVENT_EXEC_NO_MORE) || et->status == MYSQL_EVENT_DISABLED) + int t2sleep; + + + /* + now let's see how much time to sleep, we know there is at least 1 + element in the queue. + */ + VOID(pthread_mutex_lock(&LOCK_event_arrays)); + if (!evex_queue_num_elements(EVEX_EQ_NAME)) { - delete_dynamic_element(&evex_executing_queue, j); - DBUG_PRINT("EVEX main thread", ("DELETING FROM EXECUTION QUEUE [%s.%s]", - et->dbname.str, et->name.str)); - // nulling the position, will delete later - if (et->dropped) - { - // we have to drop the event - int idx; - et->drop(thd); - idx= get_index_dynamic(&events_array, (gptr) et); - DBUG_ASSERT(idx != -1); - delete_dynamic_element(&events_array, idx); - } + VOID(pthread_mutex_unlock(&LOCK_event_arrays)); continue; } - ++j; + et= evex_queue_first_element(&EVEX_EQ_NAME, event_timed*); + + time(&now); + my_tz_UTC->gmt_sec_to_TIME(&time_now, now); + t2sleep= evex_time_diff(&et->execute_at, &time_now); + VOID(pthread_mutex_unlock(&LOCK_event_arrays)); + if (t2sleep > 0) + { + sql_print_information("Sleeping for %d seconds.", t2sleep); + printf("\nWHEN=%llu NOW=%llu\n", TIME_to_ulonglong_datetime(&et->execute_at), TIME_to_ulonglong_datetime(&time_now)); + /* + We sleep t2sleep seconds but we check every second whether this thread + has been killed, or there is new candidate + */ + while (t2sleep-- && !thd->killed && + evex_queue_num_elements(EVEX_EQ_NAME) && + (evex_queue_first_element(&EVEX_EQ_NAME, event_timed*) == et)) + my_sleep(1000000); + sql_print_information("Finished sleeping"); + } + if (!event_executor_running_global_var) + continue; + } - if (evex_executing_queue.elements) - //ToDo Andrey : put a lock here - qsort((gptr) dynamic_element(&evex_executing_queue, 0, event_timed**), - evex_executing_queue.elements, - sizeof(event_timed **), - (qsort_cmp) event_timed_compare - ); + + + VOID(pthread_mutex_lock(&LOCK_event_arrays)); + + if (!evex_queue_num_elements(EVEX_EQ_NAME)) + { + VOID(pthread_mutex_unlock(&LOCK_event_arrays)); + continue; + } + et= evex_queue_first_element(&EVEX_EQ_NAME, event_timed*); + + /* + if this is the first event which is after time_now then no + more need to iterate over more elements since the array is sorted. + */ + if (et->execute_at.year > 1969 && + my_time_compare(&time_now, &et->execute_at) == -1) + { + VOID(pthread_mutex_unlock(&LOCK_event_arrays)); + continue; + } + + if (et->status == MYSQL_EVENT_ENABLED) + { + pthread_t th; + + DBUG_PRINT("info", (" Spawning a thread %d", ++iter_num)); + sql_print_information(" Spawning a thread %d", ++iter_num); +#ifndef DBUG_FAULTY_THR + sql_print_information(" Thread is not debuggable!"); + if (pthread_create(&th, NULL, event_executor_worker, (void*)et)) + { + sql_print_error("Problem while trying to create a thread"); + UNLOCK_MUTEX_AND_BAIL_OUT(LOCK_event_arrays, err); + } +#else + event_executor_worker((void *) et); +#endif + printf("[%10s] exec at [%llu]\n", et->name.str,TIME_to_ulonglong_datetime(&et->execute_at)); + et->mark_last_executed(); + et->compute_next_execution_time(); + printf("[%10s] next at [%llu]\n\n\n", et->name.str,TIME_to_ulonglong_datetime(&et->execute_at)); + et->update_fields(thd); + if ((et->execute_at.year && !et->expression) || + TIME_to_ulonglong_datetime(&et->execute_at) == 0L) + et->flags |= EVENT_EXEC_NO_MORE; + } + if ((et->flags & EVENT_EXEC_NO_MORE) || et->status == MYSQL_EVENT_DISABLED) + { + evex_queue_delete_element(&EVEX_EQ_NAME, 1);// 1 is top + if (et->dropped) + { + // we have to drop the event + int idx; + et->drop(thd); + idx= get_index_dynamic(&events_array, (gptr) et); + DBUG_ASSERT(idx != -1); + delete_dynamic_element(&events_array, idx); + } + } else + evex_queue_first_updated(&EVEX_EQ_NAME); VOID(pthread_mutex_unlock(&LOCK_event_arrays)); - } + }// while err: // First manifest that this thread does not work and then destroy VOID(pthread_mutex_lock(&LOCK_evex_running)); - evex_is_running= false; + evex_is_running= false; + evex_main_thread_id= 0; VOID(pthread_mutex_unlock(&LOCK_evex_running)); sql_print_information("Event scheduler stopping"); @@ -341,7 +373,8 @@ err: VOID(pthread_mutex_lock(&LOCK_event_arrays)); // No need to use lock here if EVEX is not running but anyway - delete_dynamic(&evex_executing_queue); + delete_queue(&executing_queue); + evex_queue_destroy(&EVEX_EQ_NAME); delete_dynamic(&events_array); VOID(pthread_mutex_unlock(&LOCK_event_arrays)); @@ -353,8 +386,10 @@ err: pthread_mutex_lock(&LOCK_thread_count); thread_count--; thread_running--; +#ifndef DBUG_FAULTY_THR THD_CHECK_SENTRY(thd); delete thd; +#endif pthread_mutex_unlock(&LOCK_thread_count); @@ -366,8 +401,10 @@ err_no_thd: free_root(&evex_mem_root, MYF(0)); sql_print_information("Event scheduler stopped"); +#ifndef DBUG_FAULTY_THR my_thread_end(); pthread_exit(0); +#endif DBUG_RETURN(0);// Can't return anything here } @@ -386,6 +423,7 @@ event_executor_worker(void *event_void) init_alloc_root(&worker_mem_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC); +#ifndef DBUG_FAULTY_THR my_thread_init(); if (!(thd = new THD)) // note that contructor of THD uses DBUG_ ! @@ -411,6 +449,9 @@ event_executor_worker(void *event_void) thread_count++; thread_running++; VOID(pthread_mutex_unlock(&LOCK_thread_count)); +#else + thd= current_thd; +#endif // thd->security_ctx->priv_host is char[MAX_HOSTNAME] @@ -420,6 +461,8 @@ event_executor_worker(void *event_void) thd->security_ctx->priv_user= event->definer_user.str; thd->db= event->dbname.str; + if (!check_access(thd, EVENT_ACL, event->dbname.str, 0, 0, 0, + is_schema_db(event->dbname.str))) { char exec_time[200]; int ret; @@ -434,6 +477,7 @@ event_executor_worker(void *event_void) err: VOID(pthread_mutex_lock(&LOCK_thread_count)); +#ifndef DBUG_FAULTY_THR thread_count--; thread_running--; /* @@ -451,6 +495,7 @@ err: VOID(pthread_mutex_lock(&LOCK_thread_count)); THD_CHECK_SENTRY(thd); delete thd; +#endif VOID(pthread_mutex_unlock(&LOCK_thread_count)); err_no_thd: @@ -502,6 +547,12 @@ evex_load_events_from_db(THD *thd) "Table probably corrupted"); goto end; } + if (et->status != MYSQL_EVENT_ENABLED) + { + DBUG_PRINT("evex_load_events_from_db",("Event %s is disabled", et->name.str)); + delete et; + continue; + } DBUG_PRINT("evex_load_events_from_db", ("Event %s loaded from row. Time to compile", et->name.str)); @@ -515,8 +566,7 @@ evex_load_events_from_db(THD *thd) // let's find when to be executed et->compute_next_execution_time(); - DBUG_PRINT("evex_load_events_from_db", - ("Adding %s to the executor list.", et->name.str)); + DBUG_PRINT("evex_load_events_from_db", ("Adding to the exec list.")); VOID(push_dynamic(&events_array,(gptr) et)); /* We always add at the end so the number of elements - 1 is the place @@ -526,23 +576,21 @@ evex_load_events_from_db(THD *thd) */ et_copy= dynamic_element(&events_array, events_array.elements - 1, event_timed*); - VOID(push_dynamic(&evex_executing_queue,(gptr) &et_copy)); + + evex_queue_insert(&EVEX_EQ_NAME, (EVEX_PTOQEL) et_copy); + printf("%p %s\n", et_copy, et_copy->name.str); et->free_sphead_on_delete= false; delete et; } - end_read_record(&read_record_info); - qsort((gptr) dynamic_element(&evex_executing_queue, 0, event_timed**), - evex_executing_queue.elements, - sizeof(event_timed **), - (qsort_cmp) event_timed_compare - ); - VOID(pthread_mutex_unlock(&LOCK_event_arrays)); - - thd->version--; // Force close to free memory ret= 0; end: + 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_PRINT("info", ("Finishing with status code %d", ret)); diff --git a/sql/event_priv.h b/sql/event_priv.h index bb18dee6e6d..f6653e473da 100644 --- a/sql/event_priv.h +++ b/sql/event_priv.h @@ -16,8 +16,11 @@ #ifndef _EVENT_PRIV_H_ #define _EVENT_PRIV_H_ +#include "mysql_priv.h" +#define EVEX_USE_QUEUE + #define UNLOCK_MUTEX_AND_BAIL_OUT(__mutex, __label) \ { VOID(pthread_mutex_unlock(&__mutex)); goto __label; } @@ -41,14 +44,6 @@ enum EVEX_FIELD_COUNT /* a cool trick to count the number of fields :) */ }; -extern bool evex_is_running; -extern bool mysql_event_table_exists; -extern DYNAMIC_ARRAY events_array; -extern DYNAMIC_ARRAY evex_executing_queue; -extern MEM_ROOT evex_mem_root; -extern pthread_mutex_t LOCK_event_arrays, - LOCK_workers_count, - LOCK_evex_running; int @@ -59,5 +54,72 @@ evex_db_find_event_aux(THD *thd, const LEX_STRING dbname, const LEX_STRING rname, TABLE *table); TABLE * -evex_open_event_table(THD *thd, enum thr_lock_type lock_type); +evex_open_event_table(THD *thd, enum thr_lock_type lock_type); + +int +event_timed_compare_q(void *vptr, byte* a, byte *b); + +int +evex_time_diff(TIME *a, TIME *b); + + + +#define EXEC_QUEUE_QUEUE_NAME executing_queue +#define EXEC_QUEUE_DARR_NAME evex_executing_queue + +#ifdef EVEX_USE_QUEUE + #define EVEX_QUEUE_TYPE QUEUE + #define EVEX_PTOQEL byte * + #define EVEX_EQ_NAME executing_queue + + #define evex_queue_first_element(queue, __cast) ((__cast)queue_top(queue)) + #define evex_queue_element(queue, idx, __cast) ((__cast)queue_top(queue)) + #define evex_queue_delete_element(queue, idx) queue_remove(queue, idx) + #define evex_queue_destroy(queue) delete_queue(queue) + #define evex_queue_first_updated(queue) queue_replaced(queue) + #define evex_queue_insert(queue, element) queue_insert_safe(queue, element); + +#else + #define EVEX_QUEUE_TYPE DYNAMIC_ARRAY + #define EVEX_PTOQEL gptr + #define EVEX_EQ_NAME evex_executing_queue + + #define evex_queue_element(queue, idx, __cast) dynamic_element(queue,idx, __cast) + #define evex_queue_delete_element(queue, idx) delete_dynamic_element(queue, idx); + #define evex_queue_destroy(queue) delete_dynamic(queue) +/* + push_dynamic() expects ptr to the memory to put in, to make things fast + so when a pointer has to be put inside a ptr-to-ptr is being passed +*/ + #define evex_queue_first_updated(queue) + #define evex_queue_insert(queue, element) VOID(push_dynamic(queue, &element)) + + +#endif + + +void +evex_queue_init(EVEX_QUEUE_TYPE *queue); + +int +evex_queue_insert2(EVEX_QUEUE_TYPE *queue, EVEX_PTOQEL element); + +void +evex_queue_sort(EVEX_QUEUE_TYPE *queue); + +#define evex_queue_num_elements(queue) queue.elements + + +extern bool evex_is_running; +extern bool mysql_event_table_exists; +extern DYNAMIC_ARRAY events_array; +extern DYNAMIC_ARRAY EXEC_QUEUE_DARR_NAME; +extern QUEUE EXEC_QUEUE_QUEUE_NAME; +extern MEM_ROOT evex_mem_root; +extern pthread_mutex_t LOCK_event_arrays, + LOCK_workers_count, + LOCK_evex_running; +extern ulonglong evex_main_thread_id; + + #endif /* _EVENT_PRIV_H_ */ diff --git a/sql/event_timed.cc b/sql/event_timed.cc index 22c6160edab..7c6399e91a1 100644 --- a/sql/event_timed.cc +++ b/sql/event_timed.cc @@ -14,9 +14,8 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "mysql_priv.h" -#include "event.h" #include "event_priv.h" +#include "event.h" #include "sp.h" @@ -789,7 +788,7 @@ event_timed::get_show_create_event(THD *thd, uint *length) + strlen("DO ") + + body.length + strlen(";"); - ret= dst= (char*) alloc_root(thd->mem_root, len); + ret= dst= (char*) alloc_root(thd->mem_root, len + 1); memcpy(dst, "CREATE EVENT ", tmp_len= strlen("CREATE EVENT ")); dst+= tmp_len; memcpy(dst, dbname.str, tmp_len=dbname.length); @@ -832,7 +831,7 @@ event_timed::get_show_create_event(THD *thd, uint *length) *dst= '\0'; *length= len; - + dst[len]= '\0'; return ret; } From 112d408fe7a215be0eab0c3e56a98c1bbe3a2b5e Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 13 Dec 2005 13:21:11 +0100 Subject: [PATCH 14/28] WL#1034 update cleanup before throwin away DYNAMIC_ARRAY for event storage, once reallocated I get dangling pointers so that's not good. solution will be found however ;) sql/event.cc: - remove unneeded variables - remove commented out code - remove non-cs compliant comment - fix a bug when removing from memory cache - better close the tables than relying on sql_parse.cc::do_command() to do that sql/event_executor.cc: - cleanup sql/event_priv.h: -cleanup sql/event_timed.cc: cleanup --- sql/event.cc | 69 +++++++------------------------------------ sql/event_executor.cc | 7 ++--- sql/event_priv.h | 4 +-- sql/event_timed.cc | 42 ++++---------------------- 4 files changed, 19 insertions(+), 103 deletions(-) diff --git a/sql/event.cc b/sql/event.cc index 87a846cd2dd..f8bfde44dde 100644 --- a/sql/event.cc +++ b/sql/event.cc @@ -73,17 +73,11 @@ Warning: bool mysql_event_table_exists= 1; DYNAMIC_ARRAY events_array; -DYNAMIC_ARRAY EXEC_QUEUE_DARR_NAME; -QUEUE EXEC_QUEUE_QUEUE_NAME; +QUEUE EVEX_EQ_NAME; MEM_ROOT evex_mem_root; -//extern volatile uint thread_running; -//////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////// -//////////////// Static functions follow /////////////////////////// -//////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////// + void evex_queue_init(EVEX_QUEUE_TYPE *queue) { @@ -202,6 +196,7 @@ my_time_compare(TIME *a, TIME *b) DBUG_RETURN(0); } + int evex_time_diff(TIME *a, TIME *b) { @@ -519,14 +514,17 @@ db_create_event(THD *thd, event_timed *et) mysql_bin_log.write(&qinfo); } - // No need to close the table, it will be closed in sql_parse::do_command 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); } @@ -742,9 +740,7 @@ evex_load_and_compile_event(THD * thd, sp_name *spn, bool use_lock) VOID(push_dynamic(&events_array,(gptr) ett)); ett_copy= dynamic_element(&events_array, events_array.elements - 1, event_timed*); -/** - VOID(push_dynamic(&evex_executing_queue, (gptr) &ett_copy)); -**/ + evex_queue_insert(&EVEX_EQ_NAME, (EVEX_PTOQEL) ett_copy); /* @@ -754,19 +750,6 @@ evex_load_and_compile_event(THD * thd, sp_name *spn, bool use_lock) ett->free_sphead_on_delete= false; delete ett; - /* - We find where the first element resides in the array. And then do a - qsort of events_array.elements (the current number of elements). - We know that the elements are stored in a contiguous block w/o holes. - */ -/** - qsort((gptr) dynamic_element(&evex_executing_queue, 0, event_timed**), - evex_executing_queue.elements, - sizeof(event_timed **), - (qsort_cmp) event_timed_compare); -**/ - evex_queue_sort(&EVEX_EQ_NAME); - if (use_lock) VOID(pthread_mutex_unlock(&LOCK_event_arrays)); @@ -791,10 +774,10 @@ evex_remove_from_cache(LEX_STRING *db, LEX_STRING *name, bool use_lock) if (use_lock) VOID(pthread_mutex_lock(&LOCK_event_arrays)); -/** - for (i= 0; i < evex_executing_queue.elements; ++i) + + for (i= 0; i < evex_queue_num_elements(EVEX_EQ_NAME); ++i) { - event_timed *et= *dynamic_element(&evex_executing_queue, i, event_timed**); + event_timed *et= evex_queue_element(&EVEX_EQ_NAME, i, event_timed*); DBUG_PRINT("info", ("[%s.%s]==[%s.%s]?",db->str,name->str, et->dbname.str, et->name.str)); if (!sortcmp_lex_string(*name, et->name, system_charset_info) && @@ -802,39 +785,9 @@ evex_remove_from_cache(LEX_STRING *db, LEX_STRING *name, bool use_lock) { int idx= get_index_dynamic(&events_array, (gptr) et); //we are lucky the event is in the executing queue, no need of second pass - if (idx == -1) - { - //this should never happen - DBUG_PRINT("error", (" get_index_dynamic problem. %d." - "i=%d idx=%d evex_ex_queue.buf=%p evex_ex_queue.elements=%d et=%p\n" - "events_array=%p events_array.elements=%d events_array.buf=%p et=%p\n", - __LINE__, i, idx, &evex_executing_queue.buffer, - evex_executing_queue.elements, et, &events_array, - events_array.elements, events_array.buffer, et)); - DBUG_ASSERT(0); - } //destruct first and then remove. the destructor will delete sp_head et->free_sp(); delete_dynamic_element(&events_array, idx); - delete_dynamic_element(&evex_executing_queue, i); - // ok, we have cleaned - goto done; - } - } -**/ - for (i= 0; i < evex_queue_num_elements(EVEX_EQ_NAME); ++i) - { - event_timed *et= *evex_queue_element(&EVEX_EQ_NAME, i, event_timed**); - DBUG_PRINT("info", ("[%s.%s]==[%s.%s]?",db->str,name->str, et->dbname.str, - et->name.str)); - if (!sortcmp_lex_string(*name, et->name, system_charset_info) && - !sortcmp_lex_string(*db, et->dbname, system_charset_info)) - { - int idx= get_index_dynamic(&events_array, (gptr) et); - //we are lucky the event is in the executing queue, no need of second pass - //destruct first and then remove. the destructor will delete sp_head - et->free_sp(); - evex_queue_delete_element(&EVEX_EQ_NAME, idx); evex_queue_delete_element(&EVEX_EQ_NAME, i); // ok, we have cleaned goto done; diff --git a/sql/event_executor.cc b/sql/event_executor.cc index 74ac1b12297..ea5ed6c059b 100644 --- a/sql/event_executor.cc +++ b/sql/event_executor.cc @@ -25,7 +25,7 @@ are not possible because the scheduler thread code is ran inside the main thread (no spawning takes place. If you want to debug client connection then start with --one-thread and make the define - DBUG_FAULTY_THR2 ! + DBUG_FAULTY_THR ! */ #define DBUG_FAULTY_THR2 @@ -202,9 +202,6 @@ event_executor_main(void *arg) thus data should be freed at later stage. */ VOID(my_init_dynamic_array(&events_array, sizeof(event_timed), 50, 100)); -/** - VOID(my_init_dynamic_array(&evex_executing_queue, sizeof(event_timed *), 50, 100)); -**/ evex_queue_init(&EVEX_EQ_NAME); @@ -533,7 +530,7 @@ evex_load_events_from_db(THD *thd) while (!(read_record_info.read_record(&read_record_info))) { event_timed *et, *et_copy; - if (!(et= new event_timed())) + if (!(et= new event_timed)) { DBUG_PRINT("evex_load_events_from_db", ("Out of memory")); ret= -1; diff --git a/sql/event_priv.h b/sql/event_priv.h index f6653e473da..1b0ad078f5a 100644 --- a/sql/event_priv.h +++ b/sql/event_priv.h @@ -113,13 +113,11 @@ evex_queue_sort(EVEX_QUEUE_TYPE *queue); extern bool evex_is_running; extern bool mysql_event_table_exists; extern DYNAMIC_ARRAY events_array; -extern DYNAMIC_ARRAY EXEC_QUEUE_DARR_NAME; -extern QUEUE EXEC_QUEUE_QUEUE_NAME; extern MEM_ROOT evex_mem_root; extern pthread_mutex_t LOCK_event_arrays, LOCK_workers_count, LOCK_evex_running; extern ulonglong evex_main_thread_id; - +extern QUEUE EVEX_EQ_NAME; #endif /* _EVENT_PRIV_H_ */ diff --git a/sql/event_timed.cc b/sql/event_timed.cc index 7c6399e91a1..19f42a44ec6 100644 --- a/sql/event_timed.cc +++ b/sql/event_timed.cc @@ -776,17 +776,7 @@ event_timed::get_show_create_event(THD *thd, uint *length) uint len, tmp_len; len = strlen("CREATE EVENT ") + dbname.length + strlen(".") + name.length + - strlen(" ON SCHEDULE ") + strlen("EVERY 5 MINUTE ") -/* - + strlen("ON COMPLETION ") - + (on_completion==MYSQL_EVENT_ON_COMPLETION_DROP? - strlen("NOT PRESERVE "):strlen("PRESERVE ")) - + (status==MYSQL_EVENT_ENABLED? - strlen("ENABLE "):strlen("DISABLE ")) - + strlen("COMMENT \"") + comment.length + strlen("\" ") -*/ - + strlen("DO ") + - + body.length + strlen(";"); + strlen(" ON SCHEDULE EVERY 5 MINUTE DO ") + body.length + strlen(";"); ret= dst= (char*) alloc_root(thd->mem_root, len + 1); memcpy(dst, "CREATE EVENT ", tmp_len= strlen("CREATE EVENT ")); @@ -797,31 +787,8 @@ event_timed::get_show_create_event(THD *thd, uint *length) dst+= tmp_len; memcpy(dst, name.str, tmp_len= name.length); dst+= tmp_len; - memcpy(dst, " ON SCHEDULE ", tmp_len= strlen(" ON SCHEDULE ")); - dst+= tmp_len; - memcpy(dst, "EVERY 5 MINUTE ", tmp_len= strlen("EVERY 5 MINUTE ")); - dst+= tmp_len; -/* - memcpy(dst, "ON COMPLETION ", tmp_len =strlen("ON COMPLETION ")); - dst+= tmp_len; - memcpy(dst, (on_completion==MYSQL_EVENT_ON_COMPLETION_DROP? - "NOT PRESERVE ":"PRESERVE "), - tmp_len =(on_completion==MYSQL_EVENT_ON_COMPLETION_DROP? 13:9)); - dst+= tmp_len; - - memcpy(dst, (status==MYSQL_EVENT_ENABLED? - "ENABLE ":"DISABLE "), - tmp_len= (status==MYSQL_EVENT_ENABLED? 8:9)); - dst+=tmp_len; - - memcpy(dst, "COMMENT \"", tmp_len= strlen("COMMENT \"")); - dst+= tmp_len; - memcpy(dst, comment.str, tmp_len= comment.length); - dst+= tmp_len; - memcpy(dst, "\" ", tmp_len=2); - dst+= tmp_len; -*/ - memcpy(dst, "DO ", tmp_len=3); + memcpy(dst, " ON SCHEDULE EVERY 5 MINUTE DO ", + tmp_len= strlen(" ON SCHEDULE EVERY 5 MINUTE DO ")); dst+= tmp_len; memcpy(dst, body.str, tmp_len= body.length); @@ -829,9 +796,10 @@ event_timed::get_show_create_event(THD *thd, uint *length) memcpy(dst, ";", 1); ++dst; *dst= '\0'; - + *length= len; dst[len]= '\0'; + sql_print_information("%d %d[%s]", len, dst-ret, ret); return ret; } From f92086d074fc779f7278fbce8ba12c184fc8d2fb Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 13 Dec 2005 19:16:00 +0100 Subject: [PATCH 15/28] WL#1034 update (cleanups, leaks fixed) sql/event.cc: - update comments - remove dead code - event_timed is no more copied after allocation on a DYNAMIC_ARRAY because there is a problem when the array is reallocated - we get dangling pointers from the scheduling queue. anyway it makes little sense to keep them there except that cleaning is quite efficient but iterating over all events and cleaning them one by one is not that bad considering that happens only when the main scheduler thread is killed or during server shutdown. sql/event_executor.cc: - DYNAMIC_ARRAY is no more sql/event_priv.h: - remove unneeded code/defines. the scheduler's queue is of type QUEUE and cannot run anymore on top of DYNAMIC_ARRAY sql/event_timed.cc: - after parsing (in ::compile() ) destruct the event_timed object sql/sql_parse.cc: in case of syntax error clean up lex->et because there could be an object created. as in the code the same is done for SPs. --- sql/event.cc | 95 +++---------------------------------------- sql/event_executor.cc | 70 +++++++++++-------------------- sql/event_priv.h | 43 +++++--------------- sql/event_timed.cc | 1 + sql/sql_parse.cc | 12 ++++++ 5 files changed, 53 insertions(+), 168 deletions(-) diff --git a/sql/event.cc b/sql/event.cc index f8bfde44dde..362af209f9a 100644 --- a/sql/event.cc +++ b/sql/event.cc @@ -23,8 +23,6 @@ - The default value of created/modified should not be 0000-00-00 because of STRICT mode restricions. - - Use timestamps instead of datetime. - - CREATE EVENT should not go into binary log! Does it now? The SQL statements issued by the EVENT are replicated. I have an idea how to solve the problem at failover. So the status field @@ -43,13 +41,14 @@ - Maybe move all allocations during parsing to evex_mem_root thus saving double parsing in evex_create_event! - - If the server is killed (stopping) try to kill executing events.. + - If the server is killed (stopping) try to kill executing events? - What happens if one renames an event in the DB while it is in memory? Or even deleting it? - Consider using conditional variable when doing shutdown instead of - waiting till all worker threads end. + waiting till all worker threads end. + - Make event_timed::get_show_create_event() work - Add function documentation whenever needed. @@ -58,10 +57,6 @@ - Move comparison code to class event_timed - - Overload event_timed::new to put the event directly in the DYNAMIC_ARRAY. - This will skip copy operation as well as will simplify the code which is - now aware of events_array DYNAMIC_ARRAY - Warning: - For now parallel execution is not possible because the same sp_head cannot be executed few times!!! There is still no lock attached to particular event. @@ -72,7 +67,6 @@ Warning: bool mysql_event_table_exists= 1; -DYNAMIC_ARRAY events_array; QUEUE EVEX_EQ_NAME; MEM_ROOT evex_mem_root; @@ -81,55 +75,13 @@ MEM_ROOT evex_mem_root; void evex_queue_init(EVEX_QUEUE_TYPE *queue) { -#ifndef EVEX_USE_QUEUE - VOID(my_init_dynamic_array(queue, sizeof(event_timed *), 50, 100)); -#else if (init_queue_ex(queue, 100 /*num_el*/, 0 /*offset*/, 0 /*smallest_on_top*/, event_timed_compare_q, NULL, 100 /*auto_extent*/)) sql_print_error("Insufficient memory to initialize executing queue."); -#endif } -int -evex_queue_insert2(EVEX_QUEUE_TYPE *queue, EVEX_PTOQEL element) -{ -#ifndef EVEX_USE_QUEUE - VOID(push_dynamic(queue, element)); - return 0; -#else - return queue_insert_safe(queue, element); -#endif -} - -void -evex_queue_top_updated(EVEX_QUEUE_TYPE *queue) -{ -#ifdef EVEX_USE_QUEUE - queue_replaced(queue); -#endif -} - -void -evex_queue_sort(EVEX_QUEUE_TYPE *queue) -{ -#ifndef EVEX_USE_QUEUE - qsort((gptr) dynamic_element(queue, 0, event_timed**), - queue->elements, - sizeof(event_timed **), - (qsort_cmp) event_timed_compare); -#endif -} - -/* NOTE Andrey: Document better - Compares two TIME structures. - - a > b -> 1 - a = b -> 0 - a < b -> -1 -*/ - static int sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs) { @@ -714,7 +666,7 @@ evex_load_and_compile_event(THD * thd, sp_name *spn, bool use_lock) { int ret= 0; MEM_ROOT *tmp_mem_root; - event_timed *ett, *ett_copy; + event_timed *ett; DBUG_ENTER("db_load_and_compile_event"); DBUG_PRINT("enter", ("name: %*s", spn->m_name.length, spn->m_name.str)); @@ -737,18 +689,12 @@ evex_load_and_compile_event(THD * thd, sp_name *spn, bool use_lock) if (use_lock) VOID(pthread_mutex_lock(&LOCK_event_arrays)); - VOID(push_dynamic(&events_array,(gptr) ett)); - ett_copy= dynamic_element(&events_array, events_array.elements - 1, - event_timed*); - - evex_queue_insert(&EVEX_EQ_NAME, (EVEX_PTOQEL) ett_copy); + evex_queue_insert(&EVEX_EQ_NAME, (EVEX_PTOQEL) ett); /* There is a copy in the array which we don't need. sphead won't be destroyed. */ - ett->free_sphead_on_delete= false; - delete ett; if (use_lock) VOID(pthread_mutex_unlock(&LOCK_event_arrays)); @@ -783,43 +729,14 @@ 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)) { - int idx= get_index_dynamic(&events_array, (gptr) et); - //we are lucky the event is in the executing queue, no need of second pass - //destruct first and then remove. the destructor will delete sp_head et->free_sp(); - delete_dynamic_element(&events_array, idx); + delete et; evex_queue_delete_element(&EVEX_EQ_NAME, i); // ok, we have cleaned goto done; } } - /* - ToDo Andrey : Think about whether second pass is needed. All events - that are in memory are enabled. If an event is being - disabled (by a SQL stmt) it will be uncached. Hmm... - However is this true for events that has been - disabled because of another reason like - no need - to be executed because ENDS is in the past? - For instance, second_pass is needed when an event - was created as DISABLED but then altered as ENABLED. - */ - /* - we haven't found the event in the executing queue. This is nice! :) - Look for it in the events_array. - */ - for (i= 0; i < events_array.elements; ++i) - { - event_timed *et= dynamic_element(&events_array, i, event_timed*); - - if (!sortcmp_lex_string(*name, et->name, system_charset_info) && - !sortcmp_lex_string(*db, et->dbname, system_charset_info)) - { - delete_dynamic_element(&events_array, i); - break; - } - } - done: if (use_lock) VOID(pthread_mutex_unlock(&LOCK_event_arrays)); diff --git a/sql/event_executor.cc b/sql/event_executor.cc index ea5ed6c059b..530625c8b03 100644 --- a/sql/event_executor.cc +++ b/sql/event_executor.cc @@ -160,6 +160,7 @@ event_executor_main(void *arg) THD *thd; /* needs to be first for thread_stack */ ulonglong iter_num= 0; uint i=0, j=0; + my_ulonglong cnt= 0; DBUG_ENTER("event_executor_main"); DBUG_PRINT("event_executor_main", ("EVEX thread started")); @@ -194,24 +195,16 @@ event_executor_main(void *arg) thread_running++; VOID(pthread_mutex_unlock(&LOCK_thread_count)); - DBUG_PRINT("EVEX main thread", ("Initing events_array")); - - VOID(pthread_mutex_lock(&LOCK_event_arrays)); - /* - my_malloc is used as underlying allocator which does not use a mem_root - thus data should be freed at later stage. - */ - VOID(my_init_dynamic_array(&events_array, sizeof(event_timed), 50, 100)); - - evex_queue_init(&EVEX_EQ_NAME); - - VOID(pthread_mutex_unlock(&LOCK_event_arrays)); + DBUG_PRINT("EVEX main thread", ("Initing events_queuey")); /* eventually manifest that we are running, not to crashe because of usage of non-initialized memory structures. */ VOID(pthread_mutex_lock(&LOCK_evex_running)); + VOID(pthread_mutex_lock(&LOCK_event_arrays)); + evex_queue_init(&EVEX_EQ_NAME); + VOID(pthread_mutex_unlock(&LOCK_event_arrays)); evex_is_running= true; event_executor_running_global_var= opt_event_executor; VOID(pthread_mutex_unlock(&LOCK_evex_running)); @@ -222,15 +215,16 @@ event_executor_main(void *arg) THD_CHECK_SENTRY(thd); /* Read queries from the IO/THREAD until this thread is killed */ evex_main_thread_id= thd->thread_id; - + sql_print_information("Scheduler thread started"); while (!thd->killed) { TIME time_now; my_time_t now; - my_ulonglong cnt; event_timed *et; - DBUG_PRINT("info", ("EVEX External Loop %d", ++cnt)); + cnt++; + DBUG_PRINT("info", ("EVEX External Loop %d", cnt)); + if (cnt > 1000) continue; thd->proc_info = "Sleeping"; if (!evex_queue_num_elements(EVEX_EQ_NAME) || !event_executor_running_global_var) @@ -326,18 +320,12 @@ event_executor_main(void *arg) } if ((et->flags & EVENT_EXEC_NO_MORE) || et->status == MYSQL_EVENT_DISABLED) { - evex_queue_delete_element(&EVEX_EQ_NAME, 1);// 1 is top if (et->dropped) - { - // we have to drop the event - int idx; et->drop(thd); - idx= get_index_dynamic(&events_array, (gptr) et); - DBUG_ASSERT(idx != -1); - delete_dynamic_element(&events_array, idx); - } + delete et; + evex_queue_delete_element(&EVEX_EQ_NAME, 1);// 1 is top } else - evex_queue_first_updated(&EVEX_EQ_NAME); + evex_queue_first_updated(&EVEX_EQ_NAME); VOID(pthread_mutex_unlock(&LOCK_event_arrays)); }// while @@ -365,15 +353,15 @@ err: LEX_STRINGs reside in the memory root and will be destroyed with it. Hence no need of delete but only freeing of SP */ - for (i= 0; i < events_array.elements; ++i) - dynamic_element(&events_array, i, event_timed*)->free_sp(); - - VOID(pthread_mutex_lock(&LOCK_event_arrays)); - // No need to use lock here if EVEX is not running but anyway - delete_queue(&executing_queue); + // First we free all objects ... + for (i= 0; i < evex_queue_num_elements(EVEX_EQ_NAME); ++i) + { + event_timed *et= evex_queue_element(&EVEX_EQ_NAME, i, event_timed*); + et->free_sp(); + delete et; + } + // ... then we can thras the whole queue at once evex_queue_destroy(&EVEX_EQ_NAME); - delete_dynamic(&events_array); - VOID(pthread_mutex_unlock(&LOCK_event_arrays)); thd->proc_info = "Clearing"; DBUG_ASSERT(thd->net.buff != 0); @@ -529,7 +517,7 @@ evex_load_events_from_db(THD *thd) init_read_record(&read_record_info, thd, table ,NULL,1,0); while (!(read_record_info.read_record(&read_record_info))) { - event_timed *et, *et_copy; + event_timed *et; if (!(et= new event_timed)) { DBUG_PRINT("evex_load_events_from_db", ("Out of memory")); @@ -564,20 +552,10 @@ evex_load_events_from_db(THD *thd) et->compute_next_execution_time(); DBUG_PRINT("evex_load_events_from_db", ("Adding to the exec list.")); - VOID(push_dynamic(&events_array,(gptr) et)); - /* - We always add at the end so the number of elements - 1 is the place - in the buffer. - DYNAMIC_ARRAY copies the object bit by bit so we have a hollow copy - in event_array. We don't need the original therefore we delete it. - */ - et_copy= dynamic_element(&events_array, events_array.elements - 1, - event_timed*); - evex_queue_insert(&EVEX_EQ_NAME, (EVEX_PTOQEL) et_copy); - printf("%p %s\n", et_copy, et_copy->name.str); - et->free_sphead_on_delete= false; - delete et; + evex_queue_insert(&EVEX_EQ_NAME, (EVEX_PTOQEL) et); + DBUG_PRINT("evex_load_events_from_db", ("%p %*s", + et, et->name.length,et->name.str)); } ret= 0; diff --git a/sql/event_priv.h b/sql/event_priv.h index 1b0ad078f5a..786d65ea94d 100644 --- a/sql/event_priv.h +++ b/sql/event_priv.h @@ -67,52 +67,29 @@ evex_time_diff(TIME *a, TIME *b); #define EXEC_QUEUE_QUEUE_NAME executing_queue #define EXEC_QUEUE_DARR_NAME evex_executing_queue -#ifdef EVEX_USE_QUEUE - #define EVEX_QUEUE_TYPE QUEUE - #define EVEX_PTOQEL byte * - #define EVEX_EQ_NAME executing_queue - #define evex_queue_first_element(queue, __cast) ((__cast)queue_top(queue)) - #define evex_queue_element(queue, idx, __cast) ((__cast)queue_top(queue)) - #define evex_queue_delete_element(queue, idx) queue_remove(queue, idx) - #define evex_queue_destroy(queue) delete_queue(queue) - #define evex_queue_first_updated(queue) queue_replaced(queue) - #define evex_queue_insert(queue, element) queue_insert_safe(queue, element); +#define EVEX_QUEUE_TYPE QUEUE +#define EVEX_PTOQEL byte * -#else - #define EVEX_QUEUE_TYPE DYNAMIC_ARRAY - #define EVEX_PTOQEL gptr - #define EVEX_EQ_NAME evex_executing_queue +#define EVEX_EQ_NAME executing_queue +#define evex_queue_first_element(queue, __cast) ((__cast)queue_top(queue)) +#define evex_queue_element(queue, idx, __cast) ((__cast)queue_element(queue, idx)) +#define evex_queue_delete_element(queue, idx) queue_remove(queue, idx) +#define evex_queue_destroy(queue) delete_queue(queue) +#define evex_queue_first_updated(queue) queue_replaced(queue) +#define evex_queue_insert(queue, element) queue_insert_safe(queue, element); - #define evex_queue_element(queue, idx, __cast) dynamic_element(queue,idx, __cast) - #define evex_queue_delete_element(queue, idx) delete_dynamic_element(queue, idx); - #define evex_queue_destroy(queue) delete_dynamic(queue) -/* - push_dynamic() expects ptr to the memory to put in, to make things fast - so when a pointer has to be put inside a ptr-to-ptr is being passed -*/ - #define evex_queue_first_updated(queue) - #define evex_queue_insert(queue, element) VOID(push_dynamic(queue, &element)) - - -#endif void evex_queue_init(EVEX_QUEUE_TYPE *queue); -int -evex_queue_insert2(EVEX_QUEUE_TYPE *queue, EVEX_PTOQEL element); - -void -evex_queue_sort(EVEX_QUEUE_TYPE *queue); - #define evex_queue_num_elements(queue) queue.elements extern bool evex_is_running; extern bool mysql_event_table_exists; -extern DYNAMIC_ARRAY events_array; +//extern DYNAMIC_ARRAY events_array; extern MEM_ROOT evex_mem_root; extern pthread_mutex_t LOCK_event_arrays, LOCK_workers_count, diff --git a/sql/event_timed.cc b/sql/event_timed.cc index 19f42a44ec6..db5e031b09b 100644 --- a/sql/event_timed.cc +++ b/sql/event_timed.cc @@ -939,6 +939,7 @@ event_timed::compile(THD *thd, MEM_ROOT *mem_root) sphead->optimize(); ret= 0; done: + delete lex.et; lex_end(&lex); thd->lex= old_lex; thd->query= old_query; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index cd01a0e7181..15dc3bde0a5 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3685,9 +3685,11 @@ end_with_restore_list: res= true; break; } + if (check_access(thd, EVENT_ACL, lex->et->dbname.str, 0, 0, 0, is_schema_db(lex->et->dbname.str))) break; + switch (lex->sql_command) { case SQLCOM_CREATE_EVENT: res= evex_create_event(thd, lex->et, (uint) lex->create_info.options); @@ -5652,6 +5654,11 @@ void mysql_parse(THD *thd, char *inBuf, uint length) delete thd->lex->sphead; thd->lex->sphead= NULL; } + if (thd->lex->et) + { + delete thd->lex->et; + thd->lex->et= NULL; + } } else { @@ -5687,6 +5694,11 @@ void mysql_parse(THD *thd, char *inBuf, uint length) delete thd->lex->sphead; thd->lex->sphead= NULL; } + if (thd->lex->et) + { + delete thd->lex->et; + thd->lex->et= NULL; + } } thd->proc_info="freeing items"; thd->end_statement(); From d22bb45cce40cb015982c193664df08afecb14c7 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 13 Dec 2005 23:10:29 +0100 Subject: [PATCH 16/28] WL#1034 (update) - fixed silly bug, the main thread restarted but did not execute events, Quite currious why many calls to pthread_mutex_init() do not lead to abort() sql/event.cc: - remove mysql_event_table_exists - fix possible crash when table is 0x0 sql/event_executor.cc: - make event_executor_running_global_var volatile - fix erroneous reinitilization of a mutex, why did it not crash in debug mode? why pthread_mutex_init() does not abort() in case the mutex was not deinitted beforehand? - first initialization of event_executor_running_global_var inside init_mutexes() - remove debug if() sql/event_priv.h: - remove unneeded definitions sql/event_timed.cc: make backup and then restore the open table state of thd --- sql/event.cc | 21 ++------------------- sql/event_executor.cc | 20 +++++++++----------- sql/event_priv.h | 2 -- sql/event_timed.cc | 12 ++++++++++-- 4 files changed, 21 insertions(+), 34 deletions(-) diff --git a/sql/event.cc b/sql/event.cc index 362af209f9a..806780e5097 100644 --- a/sql/event.cc +++ b/sql/event.cc @@ -64,14 +64,10 @@ Warning: */ - - -bool mysql_event_table_exists= 1; QUEUE EVEX_EQ_NAME; MEM_ROOT evex_mem_root; - void evex_queue_init(EVEX_QUEUE_TYPE *queue) { @@ -175,7 +171,6 @@ event_timed_compare(event_timed **a, event_timed **b) else return 0; -// return my_time_compare(&(*a)->execute_at, &(*b)->execute_at); } @@ -204,23 +199,13 @@ TABLE *evex_open_event_table(THD *thd, enum thr_lock_type lock_type) bool not_used; DBUG_ENTER("open_proc_table"); - /* - Speed up things if the table doesn't exists. *table_exists - is set when we create or read stored procedure or on flush privileges. - */ - if (!mysql_event_table_exists) - DBUG_RETURN(0); - 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)) - { - mysql_event_table_exists= 0; DBUG_RETURN(0); - } DBUG_RETURN(tables.table); } @@ -638,7 +623,7 @@ done: et= 0; } // don't close the table if we haven't opened it ourselves - if (!tbl) + if (!tbl && table) close_thread_tables(thd); *ett= et; DBUG_RETURN(ret); @@ -745,9 +730,7 @@ done: } -/* - -= Exported functions follow =- -*/ + /* The function exported to the world for creating of events. diff --git a/sql/event_executor.cc b/sql/event_executor.cc index 530625c8b03..623e45bf118 100644 --- a/sql/event_executor.cc +++ b/sql/event_executor.cc @@ -41,7 +41,7 @@ bool evex_is_running= false; ulonglong evex_main_thread_id= 0; ulong opt_event_executor; -my_bool event_executor_running_global_var= false; +volatile my_bool event_executor_running_global_var; static my_bool evex_mutexes_initted= false; static uint workers_count; @@ -65,13 +65,14 @@ static void evex_init_mutexes() { if (evex_mutexes_initted) - { - evex_mutexes_initted= true; return; - } + + evex_mutexes_initted= true; pthread_mutex_init(&LOCK_event_arrays, MY_MUTEX_INIT_FAST); pthread_mutex_init(&LOCK_workers_count, MY_MUTEX_INIT_FAST); pthread_mutex_init(&LOCK_evex_running, MY_MUTEX_INIT_FAST); + + event_executor_running_global_var= opt_event_executor; } @@ -88,7 +89,6 @@ init_events() VOID(pthread_mutex_lock(&LOCK_evex_running)); evex_is_running= false; - event_executor_running_global_var= false; VOID(pthread_mutex_unlock(&LOCK_evex_running)); #ifndef DBUG_FAULTY_THR @@ -206,7 +206,6 @@ event_executor_main(void *arg) evex_queue_init(&EVEX_EQ_NAME); VOID(pthread_mutex_unlock(&LOCK_event_arrays)); evex_is_running= true; - event_executor_running_global_var= opt_event_executor; VOID(pthread_mutex_unlock(&LOCK_evex_running)); if (evex_load_events_from_db(thd)) @@ -224,7 +223,7 @@ event_executor_main(void *arg) cnt++; DBUG_PRINT("info", ("EVEX External Loop %d", cnt)); - if (cnt > 1000) continue; + thd->proc_info = "Sleeping"; if (!evex_queue_num_elements(EVEX_EQ_NAME) || !event_executor_running_global_var) @@ -573,18 +572,17 @@ end: } - bool sys_var_event_executor::update(THD *thd, set_var *var) { // here start the thread if not running. VOID(pthread_mutex_lock(&LOCK_evex_running)); - if ((my_bool) var->save_result.ulong_value && !evex_is_running) + *value= var->save_result.ulong_value; + if ((my_bool) *value && !evex_is_running) { VOID(pthread_mutex_unlock(&LOCK_evex_running)); init_events(); } else VOID(pthread_mutex_unlock(&LOCK_evex_running)); - - return sys_var_bool_ptr::update(thd, var); + return 0; } diff --git a/sql/event_priv.h b/sql/event_priv.h index 786d65ea94d..cb45a700cb8 100644 --- a/sql/event_priv.h +++ b/sql/event_priv.h @@ -88,8 +88,6 @@ evex_queue_init(EVEX_QUEUE_TYPE *queue); extern bool evex_is_running; -extern bool mysql_event_table_exists; -//extern DYNAMIC_ARRAY events_array; extern MEM_ROOT evex_mem_root; extern pthread_mutex_t LOCK_event_arrays, LOCK_workers_count, diff --git a/sql/event_timed.cc b/sql/event_timed.cc index db5e031b09b..747eab4558c 100644 --- a/sql/event_timed.cc +++ b/sql/event_timed.cc @@ -725,6 +725,7 @@ bool event_timed::update_fields(THD *thd) { TABLE *table; + Open_tables_state backup; int ret= 0; bool opened; @@ -736,8 +737,14 @@ event_timed::update_fields(THD *thd) if (!(status_changed || last_executed_changed)) goto done; + thd->reset_n_backup_open_tables_state(&backup); + if (!(table= evex_open_event_table(thd, TL_WRITE))) - DBUG_RETURN(SP_OPEN_TABLE_FAILED); + { + ret= SP_OPEN_TABLE_FAILED; + goto done; + } + if ((ret= evex_db_find_event_aux(thd, dbname, name, table))) goto done; @@ -764,6 +771,7 @@ event_timed::update_fields(THD *thd) done: close_thread_tables(thd); + thd->restore_backup_open_tables_state(&backup); DBUG_RETURN(ret); } @@ -798,7 +806,7 @@ event_timed::get_show_create_event(THD *thd, uint *length) *dst= '\0'; *length= len; - dst[len]= '\0'; + sql_print_information("%d %d[%s]", len, dst-ret, ret); return ret; } From 48405ec751ed8cbae94cc7478eb3ab19c29b7734 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 15 Dec 2005 14:12:28 +0100 Subject: [PATCH 17/28] WL#1034 update - fix EVENT_ACL problem that GRANT ALL on some_db.* to someone@somewhere did not get to mysql.db - fix crash when the following is executed : CREATE EVENT P() CREATE EVENT E ON SCHEDULER 1 SECOND DO ROLLBACK; (creation works as well as calling P() which creates the event). mysql-test/lib/init_db.sql: - fix init_db.sql so add Event_priv to the database privs, many tests failed because of that ommision - remove the quotes from the column names mysql-test/t/events.test: - fix the small test, don't create own db scripts/mysql_fix_privilege_tables.sql: - fix that sql/event.cc: - be defensive and don't crash if outside has already has opened some table sql/event_executor.cc: - show in SHOW PROCESSLIST - "event_scheduler" as name of the user of the main thread - use "localhost" as the host where event_scheduler comes from - comment out some debug info, fix other debug info sql/event_timed.cc: - enable EVENT creation inside SP. sphead from lex->sphead goes to et->sphead. it's there only if we compile the event. OTOH when doing CREATE PROCEDURE PROC() CREATE EVENT SOME_EV ON SCHEDULE EVERY 1 SECOND DO ROLLBACK; I have only to get the body of the event which is anonymous SP. Before it being "compiled" but then freed without being used because a bit later it is compiled one more time before being put in the events cache. So it was good that the memory structures weren't reused but scrapped out. Now lex->sphead is not needed during event creation but only where the event's body starts and where it ends so to be able at later stage to compile this anonymous SP (the body of the event). sql/sp_head.cc: - copy over a fix to a crash sql/sql_acl.h: - fix privileges. There was _NO_ documentation about that. Another CHUNK had to be created to so EVENT_ACL gets shifted to it's place in the db table. So how this is calculated? EVENT_ACL is 1 << 26. Remember 26, see which poistion in the db table is EVENT_ACL, it's 17, counted from 0. 26 - 17 = 9, then shift it with 9. CHUNKS are created because in some cases some privileges are in chunks and they are shifted at once. There are few chunks of such privileges which has to be shifted to get to exactly the structure of mysql.db table. sql/sql_parse.cc: - ok, we don't care anymore about lex->sphead because our sphead is lex->et->sphead sql/sql_yacc.yy: - bail out if new event_timed returns 0x0 - enable creation of an event inside a SP CREATE PROCEDURE P() CREATE EVENT E ON SCHEDULE EVERY 1 SECOND DO SELECT 1; --- mysql-test/lib/init_db.sql | 37 ++++++++--------- mysql-test/r/events.result | 21 ++++++++++ mysql-test/t/events.test | 6 +-- scripts/mysql_fix_privilege_tables.sql | 5 ++- sql/event.cc | 3 ++ sql/event_executor.cc | 32 ++++++++------- sql/event_timed.cc | 4 +- sql/sp_head.cc | 8 +++- sql/sql_acl.h | 7 +++- sql/sql_parse.cc | 5 ++- sql/sql_yacc.yy | 56 +++++++++++++++----------- 11 files changed, 113 insertions(+), 71 deletions(-) create mode 100644 mysql-test/r/events.result diff --git a/mysql-test/lib/init_db.sql b/mysql-test/lib/init_db.sql index 4ba7184d551..f075a1ebe93 100644 --- a/mysql-test/lib/init_db.sql +++ b/mysql-test/lib/init_db.sql @@ -22,6 +22,7 @@ CREATE TABLE db ( Create_routine_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Alter_routine_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Execute_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, + Event_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, PRIMARY KEY Host (Host,Db,User), KEY User (User) ) engine=MyISAM @@ -29,8 +30,8 @@ CHARACTER SET utf8 COLLATE utf8_bin comment='Database privileges'; -INSERT INTO db VALUES ('%','test','','Y','Y','Y','Y','Y','Y','N','Y','Y','Y','Y','Y','Y','Y','Y','N','N'); -INSERT INTO db VALUES ('%','test\_%','','Y','Y','Y','Y','Y','Y','N','Y','Y','Y','Y','Y','Y','Y','Y','N','N'); +INSERT INTO db VALUES ('%','test','','Y','Y','Y','Y','Y','Y','N','Y','Y','Y','Y','Y','Y','Y','Y','N','N','Y'); +INSERT INTO db VALUES ('%','test\_%','','Y','Y','Y','Y','Y','Y','N','Y','Y','Y','Y','Y','Y','Y','Y','N','N','Y'); CREATE TABLE host ( @@ -570,26 +571,26 @@ CREATE TABLE proc ( CREATE TABLE event ( - 'db' VARCHAR(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '', - 'name' VARCHAR(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '', - 'body' longblob NOT NULL, - 'definer' VARCHAR(77) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '', - 'execute_at' DATETIME default NULL, - 'transient_expression' int(11) default NULL, - 'interval_type' ENUM('YEAR','QUARTER','MONTH','DAY','HOUR','MINUTE','WEEK', + db VARCHAR(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '', + name VARCHAR(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '', + body longblob NOT NULL, + definer VARCHAR(77) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '', + execute_at DATETIME default NULL, + transient_expression int(11) default NULL, + interval_type 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') default NULL, - 'created' TIMESTAMP NOT NULL default '0000-00-00 00:00:00', - 'modified' TIMESTAMP NOT NULL default '0000-00-00 00:00:00', - 'last_executed' DATETIME default NULL, - 'starts' DATETIME default NULL, - 'ends' DATETIME default NULL, - 'status' ENUM('ENABLED','DISABLED') NOT NULL default 'ENABLED', - 'on_completion' ENUM('DROP','PRESERVE') NOT NULL default 'DROP', - 'comment' varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '', - PRIMARY KEY ('db','name') + created TIMESTAMP NOT NULL, + modified TIMESTAMP NOT NULL, + last_executed DATETIME default NULL, + starts DATETIME default NULL, + ends DATETIME default NULL, + status ENUM('ENABLED','DISABLED') NOT NULL default 'ENABLED', + on_completion ENUM('DROP','PRESERVE') NOT NULL default 'DROP', + comment varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '', + PRIMARY KEY (db, name) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT 'Events'; diff --git a/mysql-test/r/events.result b/mysql-test/r/events.result new file mode 100644 index 00000000000..5816f9888a0 --- /dev/null +++ b/mysql-test/r/events.result @@ -0,0 +1,21 @@ +use test; +drop event if exists event1; +Warnings: +Note 1305 Event event1 does not exist +create event event1 on schedule every 15 minute starts now() ends date_add(now(), interval 5 hour) DO begin end; +alter event event1 rename to event2; +alter event event2 disable; +drop event event2; +create event event2 on schedule every 2 second starts now() ends date_add(now(), interval 5 hour) comment "some" DO begin end; +drop event event2; +create table t_event3 (a int, b float); +drop event if exists event3; +Warnings: +Note 1305 Event event3 does not exist +create event event3 on schedule every 50 + 10 minute starts date_add("20010101", interval 5 minute) ends date_add("20151010", interval 5 day) comment "portokala_comment" DO insert into t_event3 values (unix_timestamp(), rand()); +set max_allowed_packet=128000000; +select count(*) from t_event3; +count(*) +0 +drop event event3; +drop table t_event3; diff --git a/mysql-test/t/events.test b/mysql-test/t/events.test index 9285b38dc6b..bba34947ecd 100644 --- a/mysql-test/t/events.test +++ b/mysql-test/t/events.test @@ -1,5 +1,4 @@ -create database events_test; -use events_test; +use test; drop event if exists event1; create event event1 on schedule every 15 minute starts now() ends date_add(now(), interval 5 hour) DO begin end; alter event event1 rename to event2; @@ -13,7 +12,6 @@ create table t_event3 (a int, b float); drop event if exists event3; create event event3 on schedule every 50 + 10 minute starts date_add("20010101", interval 5 minute) ends date_add("20151010", interval 5 day) comment "portokala_comment" DO insert into t_event3 values (unix_timestamp(), rand()); set max_allowed_packet=128000000; -select sha1(space(9999999)); select count(*) from t_event3; drop event event3; -drop database events_test; +drop table t_event3; diff --git a/scripts/mysql_fix_privilege_tables.sql b/scripts/mysql_fix_privilege_tables.sql index 2949049afe8..255f83e473e 100644 --- a/scripts/mysql_fix_privilege_tables.sql +++ b/scripts/mysql_fix_privilege_tables.sql @@ -562,5 +562,6 @@ CREATE TABLE event ( # EVENT privilege # -ALTER TABLE mysql.user add Event_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL AFTER Create_user_priv; -ALTER TABLE mysql.db add Event_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL; +ALTER TABLE mysql.user add Event_priv enum('N','Y') character set utf8 DEFAULT 'N' NOT NULL AFTER Create_user_priv; +ALTER TABLE mysql.db add Event_priv enum('N','Y') character set utf8 DEFAULT 'N' NOT NULL; + diff --git a/sql/event.cc b/sql/event.cc index 806780e5097..5425b676b60 100644 --- a/sql/event.cc +++ b/sql/event.cc @@ -652,6 +652,7 @@ evex_load_and_compile_event(THD * thd, sp_name *spn, bool use_lock) int ret= 0; MEM_ROOT *tmp_mem_root; event_timed *ett; + Open_tables_state backup; DBUG_ENTER("db_load_and_compile_event"); DBUG_PRINT("enter", ("name: %*s", spn->m_name.length, spn->m_name.str)); @@ -659,10 +660,12 @@ evex_load_and_compile_event(THD * thd, sp_name *spn, bool use_lock) tmp_mem_root= thd->mem_root; thd->mem_root= &evex_mem_root; + thd->reset_n_backup_open_tables_state(&backup); // no need to use my_error() here because db_find_event() has done it if ((ret= db_find_event(thd, spn, &ett, NULL))) 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! diff --git a/sql/event_executor.cc b/sql/event_executor.cc index 623e45bf118..9578cd936ea 100644 --- a/sql/event_executor.cc +++ b/sql/event_executor.cc @@ -30,7 +30,7 @@ #define DBUG_FAULTY_THR2 extern ulong thread_created; - +extern const char *my_localhost; pthread_mutex_t LOCK_event_arrays, LOCK_workers_count, @@ -125,6 +125,7 @@ init_event_thread(THD* thd) DBUG_ENTER("init_event_thread"); thd->client_capabilities= 0; thd->security_ctx->skip_grants(); + thd->security_ctx->host= (char*)my_localhost; my_net_init(&thd->net, 0); thd->net.read_timeout = slave_net_timeout; thd->slave_thread= 0; @@ -211,6 +212,7 @@ event_executor_main(void *arg) if (evex_load_events_from_db(thd)) goto err; + thd->security_ctx->user= my_strdup("event_scheduler", MYF(0)); THD_CHECK_SENTRY(thd); /* Read queries from the IO/THREAD until this thread is killed */ evex_main_thread_id= thd->thread_id; @@ -254,8 +256,8 @@ event_executor_main(void *arg) VOID(pthread_mutex_unlock(&LOCK_event_arrays)); if (t2sleep > 0) { - sql_print_information("Sleeping for %d seconds.", t2sleep); - printf("\nWHEN=%llu NOW=%llu\n", TIME_to_ulonglong_datetime(&et->execute_at), TIME_to_ulonglong_datetime(&time_now)); +// sql_print_information("Sleeping for %d seconds.", t2sleep); +// printf("\nWHEN=%llu NOW=%llu\n", TIME_to_ulonglong_datetime(&et->execute_at), TIME_to_ulonglong_datetime(&time_now)); /* We sleep t2sleep seconds but we check every second whether this thread has been killed, or there is new candidate @@ -264,7 +266,7 @@ event_executor_main(void *arg) evex_queue_num_elements(EVEX_EQ_NAME) && (evex_queue_first_element(&EVEX_EQ_NAME, event_timed*) == et)) my_sleep(1000000); - sql_print_information("Finished sleeping"); +// sql_print_information("Finished sleeping"); } if (!event_executor_running_global_var) continue; @@ -297,9 +299,9 @@ event_executor_main(void *arg) pthread_t th; DBUG_PRINT("info", (" Spawning a thread %d", ++iter_num)); - sql_print_information(" Spawning a thread %d", ++iter_num); +// sql_print_information(" Spawning a thread %d", ++iter_num); #ifndef DBUG_FAULTY_THR - sql_print_information(" Thread is not debuggable!"); +// sql_print_information(" Thread is not debuggable!"); if (pthread_create(&th, NULL, event_executor_worker, (void*)et)) { sql_print_error("Problem while trying to create a thread"); @@ -442,20 +444,18 @@ event_executor_worker(void *event_void) strxnmov(thd->security_ctx->priv_host, sizeof(thd->security_ctx->priv_host), event->definer_host.str, NullS); - thd->security_ctx->priv_user= event->definer_user.str; + thd->security_ctx->user= thd->security_ctx->priv_user= my_strdup(event->definer_user.str, MYF(0)); thd->db= event->dbname.str; if (!check_access(thd, EVENT_ACL, event->dbname.str, 0, 0, 0, is_schema_db(event->dbname.str))) { - char exec_time[200]; int ret; - my_TIME_to_str(&event->execute_at, exec_time); - DBUG_PRINT("info", (" EVEX EXECUTING event for event %s.%s [EXPR:%d][EXECUTE_AT:%s]", event->dbname.str, event->name.str,(int) event->expression, exec_time)); - sql_print_information(" EVEX EXECUTING event for event %s.%s [EXPR:%d][EXECUTE_AT:%s]", event->dbname.str, event->name.str,(int) event->expression, exec_time); + DBUG_PRINT("info", (" EVEX EXECUTING event for event %s.%s [EXPR:%d]", event->dbname.str, event->name.str,(int) event->expression)); + sql_print_information(" EVEX EXECUTING event for event %s.%s [EXPR:%d]", event->dbname.str, event->name.str,(int) event->expression); ret= event->execute(thd, &worker_mem_root); - sql_print_information(" EVEX EXECUTED event for event %s.%s [EXPR:%d][EXECUTE_AT:%s]. RetCode=%d", event->dbname.str, event->name.str,(int) event->expression, exec_time, ret); - DBUG_PRINT("info", (" EVEX EXECUTED event for event %s.%s [EXPR:%d][EXECUTE_AT:%s]", event->dbname.str, event->name.str,(int) event->expression, exec_time)); + sql_print_information(" EVEX EXECUTED event for event %s.%s [EXPR:%d]. RetCode=%d", event->dbname.str, event->name.str,(int) event->expression, ret); + DBUG_PRINT("info", (" EVEX EXECUTED event for event %s.%s [EXPR:%d]. RetCode=%d", event->dbname.str, event->name.str,(int) event->expression, ret)); } thd->db= 0; @@ -505,6 +505,7 @@ evex_load_events_from_db(THD *thd) READ_RECORD read_record_info; MYSQL_LOCK *lock; int ret= -1; + uint count= 0; DBUG_ENTER("evex_load_events_from_db"); @@ -555,6 +556,7 @@ evex_load_events_from_db(THD *thd) evex_queue_insert(&EVEX_EQ_NAME, (EVEX_PTOQEL) et); DBUG_PRINT("evex_load_events_from_db", ("%p %*s", et, et->name.length,et->name.str)); + count++; } ret= 0; @@ -566,8 +568,8 @@ end: thd->version--; // Force close to free memory close_thread_tables(thd); - - DBUG_PRINT("info", ("Finishing with status code %d", ret)); + sql_print_information("Scheduler loaded %d events", count); + DBUG_PRINT("info", ("Finishing with status code %d. Loaded %d events", ret, count)); DBUG_RETURN(ret); } diff --git a/sql/event_timed.cc b/sql/event_timed.cc index 747eab4558c..aa22696b27f 100644 --- a/sql/event_timed.cc +++ b/sql/event_timed.cc @@ -807,7 +807,6 @@ event_timed::get_show_create_event(THD *thd, uint *length) *length= len; - sql_print_information("%d %d[%s]", len, dst-ret, ret); return ret; } @@ -938,7 +937,7 @@ event_timed::compile(THD *thd, MEM_ROOT *mem_root) goto done; } - sphead= lex.sphead; + sphead= lex.et->sphead; sphead->m_db= dbname; //copy also chistics since they will vanish otherwise we get 0x0 pointer // Todo : Handle sql_mode !! @@ -947,6 +946,7 @@ event_timed::compile(THD *thd, MEM_ROOT *mem_root) sphead->optimize(); ret= 0; done: + lex.et->free_sphead_on_delete= false; delete lex.et; lex_end(&lex); thd->lex= old_lex; diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 90d1ec38e64..71ba0fce4a9 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -960,8 +960,12 @@ int sp_head::execute(THD *thd) m_first_instance->m_first_free_instance= m_next_cached_sp; DBUG_PRINT("info", ("first free for 0x%lx ++: 0x%lx->0x%lx, level: %lu, flags %x", (ulong)m_first_instance, this, m_next_cached_sp, - m_next_cached_sp->m_recursion_level, - m_next_cached_sp->m_flags)); + (m_next_cached_sp ? + m_next_cached_sp->m_recursion_level : + 0), + (m_next_cached_sp ? + m_next_cached_sp->m_flags : + 0))); /* Check that if there are not any instances after this one then pointer to the last instance points on this instance or if there are diff --git a/sql/sql_acl.h b/sql/sql_acl.h index 44e42b961a2..993fa991eab 100644 --- a/sql/sql_acl.h +++ b/sql/sql_acl.h @@ -97,17 +97,20 @@ #define DB_CHUNK3 (CREATE_VIEW_ACL | SHOW_VIEW_ACL | \ CREATE_PROC_ACL | ALTER_PROC_ACL ) #define DB_CHUNK4 (EXECUTE_ACL) +#define DB_CHUNK5 (EVENT_ACL) #define fix_rights_for_db(A) (((A) & DB_CHUNK0) | \ (((A) << 4) & DB_CHUNK1) | \ (((A) << 6) & DB_CHUNK2) | \ (((A) << 9) & DB_CHUNK3) | \ - (((A) << 2) & DB_CHUNK4)) + (((A) << 2) & DB_CHUNK4))| \ + (((A) << 9) & DB_CHUNK5) #define get_rights_for_db(A) (((A) & DB_CHUNK0) | \ (((A) & DB_CHUNK1) >> 4) | \ (((A) & DB_CHUNK2) >> 6) | \ (((A) & DB_CHUNK3) >> 9) | \ - (((A) & DB_CHUNK4) >> 2)) + (((A) & DB_CHUNK4) >> 2))| \ + (((A) & DB_CHUNK5) >> 9) #define TBL_CHUNK0 DB_CHUNK0 #define TBL_CHUNK1 DB_CHUNK1 #define TBL_CHUNK2 (CREATE_VIEW_ACL | SHOW_VIEW_ACL) diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 3a729ddd9fa..c21d62952bf 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3704,10 +3704,9 @@ end_with_restore_list: /* lex->unit.cleanup() is called outside, no need to call it here */ } while (0); + lex->et->free_sphead_on_delete= true; delete lex->et; - delete lex->sphead; lex->et= 0; - lex->sphead= 0; break; } case SQLCOM_SHOW_CREATE_EVENT: @@ -5658,6 +5657,7 @@ void mysql_parse(THD *thd, char *inBuf, uint length) } if (thd->lex->et) { + thd->lex->et->free_sphead_on_delete= true; delete thd->lex->et; thd->lex->et= NULL; } @@ -5698,6 +5698,7 @@ void mysql_parse(THD *thd, char *inBuf, uint length) } if (thd->lex->et) { + thd->lex->et->free_sphead_on_delete= true; delete thd->lex->et; thd->lex->et= NULL; } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 4b3e694b911..488eee86627 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -109,6 +109,7 @@ inline Item *is_truth_value(Item *A, bool v1, bool v2) struct { int vars, conds, hndlrs, curs; } spblock; sp_name *spname; struct st_lex *lex; + sp_head *sphead; } %{ @@ -1345,8 +1346,6 @@ create: if (!lex->et_compile_phase) lex->et->init_name(YYTHD, $4); - - lex->sphead= 0;//defensive programming } ON SCHEDULE_SYM ev_schedule_time ev_on_completion @@ -1482,33 +1481,46 @@ ev_sql_stmt: { LEX *lex= Lex; sp_head *sp; - - if (!(sp= new sp_head())) - YYABORT; - - sp->reset_thd_mem_root(YYTHD); - sp->init(lex); + + $$= lex->sphead; - sp->m_type= TYPE_ENUM_PROCEDURE; - lex->sphead= sp; + if (!lex->sphead) + { + if (!(sp= new sp_head())) + YYABORT; - bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); - lex->sphead->m_chistics= &lex->sp_chistics; + sp->reset_thd_mem_root(YYTHD); + sp->init(lex); + + sp->m_type= TYPE_ENUM_PROCEDURE; + + lex->sphead= sp; + + bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); + lex->sphead->m_chistics= &lex->sp_chistics; - lex->sphead->m_body_begin= lex->ptr; + lex->sphead->m_body_begin= lex->ptr; + } + if (!lex->et_compile_phase) lex->et->body_begin= lex->ptr; } ev_sql_stmt_inner { LEX *lex=Lex; - sp_head *sp= lex->sphead; - // return back to the original memory root ASAP - sp->init_strings(YYTHD, lex, NULL); - sp->restore_thd_mem_root(YYTHD); + + if (!$1) + { + sp_head *sp= lex->sphead; + // return back to the original memory root ASAP + sp->init_strings(YYTHD, lex, NULL); + sp->restore_thd_mem_root(YYTHD); - lex->sp_chistics.suid= SP_IS_SUID;//always the definer! + lex->sp_chistics.suid= SP_IS_SUID;//always the definer! + lex->et->sphead= lex->sphead; + lex->sphead= NULL; + } if (!lex->et_compile_phase) { lex->et->init_body(YYTHD); @@ -4223,7 +4235,8 @@ alter: } lex->spname= 0;//defensive programming - et= new event_timed();// implicitly calls event_timed::init() + if (!(et= new event_timed()))// implicitly calls event_timed::init() + YYABORT; lex->et = et; et->init_name(YYTHD, $3); @@ -4235,11 +4248,6 @@ alter: $$= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES; YYTHD->client_capabilities &= ~CLIENT_MULTI_QUERIES; - /* - defensive. in sql_parse.cc it is checked whether is not null - and then deleted - */ - lex->sphead= 0; } ev_on_schedule ev_rename_to From 20bcd568438831b6d9bc1e2ab51b705a9d2649d8 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 15 Dec 2005 19:11:08 +0100 Subject: [PATCH 18/28] WL#1034 update make compile-pentium-debug-max work libmysqld/Makefile.am: compile all three into libmysqld --- libmysqld/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libmysqld/Makefile.am b/libmysqld/Makefile.am index 4eae790c9db..21dc56d379f 100644 --- a/libmysqld/Makefile.am +++ b/libmysqld/Makefile.am @@ -62,7 +62,7 @@ sqlsources = derror.cc field.cc field_conv.cc strfunc.cc filesort.cc \ unireg.cc uniques.cc stacktrace.c sql_union.cc hash_filo.cc \ spatial.cc gstream.cc sql_help.cc tztime.cc sql_cursor.cc \ sp_head.cc sp_pcontext.cc sp.cc sp_cache.cc sp_rcontext.cc \ - parse_file.cc sql_view.cc sql_trigger.cc my_decimal.cc event.cc \ + parse_file.cc sql_view.cc sql_trigger.cc my_decimal.cc event_executor.cc event.cc event_timed.cc \ rpl_filter.cc sql_partition.cc handlerton.cc sql_plugin.cc libmysqld_int_a_SOURCES= $(libmysqld_sources) $(libmysqlsources) $(sqlsources) From 2d11a567105500fef4b32b2568ed64f38369c2e2 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 16 Dec 2005 09:49:24 +0100 Subject: [PATCH 19/28] WL#1034 update fix bug with DROP EVENT non_existant; giving back OK + warning sql/share/errmsg.txt: fix error message sql/sql_parse.cc: check the result from the function so in case of non-existing event don't return OK which will make the error already thrown an warning --- sql/share/errmsg.txt | 2 +- sql/sql_parse.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index abd4b746aa8..0f5358d8176 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -5726,7 +5726,7 @@ ER_EVENT_ALREADY_EXISTS ER_EVENT_STORE_FAILED eng "Failed to store event %s. Error code %d from storage engine." ER_EVENT_DOES_NOT_EXIST - eng "Event %s does not exist" + eng "Unknown event '%s'" ER_EVENT_CANT_ALTER eng "Failed to alter event %s" ER_EVENT_DROP_FAILED diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index c21d62952bf..f06fa0bbafe 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3696,7 +3696,7 @@ end_with_restore_list: res= evex_update_event(thd, lex->et, lex->spname); break; case SQLCOM_DROP_EVENT: - evex_drop_event(thd, lex->et, lex->drop_if_exists); + res= evex_drop_event(thd, lex->et, lex->drop_if_exists); default:; } if (!res) From 7634f7d60c8b069d17160049381798858ba6807d Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 16 Dec 2005 12:42:39 +0100 Subject: [PATCH 20/28] WL #1034 update - handle crashes where the table definition has been changed (different number of fields) sql/event.cc: change the way table is opened check whether the number of fields is exact, otherwise report damaged table sql/event_executor.cc: - move around some code - use the new way of table opening sql/event_priv.h: - new declaration sql/event_timed.cc: - now 0 is ok --- sql/event.cc | 29 ++++++++++++++++++++--------- sql/event_executor.cc | 11 +++++++---- sql/event_priv.h | 4 ++-- sql/event_timed.cc | 2 +- 4 files changed, 30 insertions(+), 16 deletions(-) diff --git a/sql/event.cc b/sql/event.cc index 5425b676b60..365ebcb063f 100644 --- a/sql/event.cc +++ b/sql/event.cc @@ -188,12 +188,15 @@ event_timed_compare_q(void *vptr, byte* a, byte *b) evex_open_event_table_for_read() thd Thread context lock_type How to lock the table + table The table pointer RETURN - 0 Error - # Pointer to TABLE object + 1 Cannot lock table + 2 The table is corrupted - different number of fields + 0 OK */ -TABLE *evex_open_event_table(THD *thd, enum thr_lock_type lock_type) +int +evex_open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table) { TABLE_LIST tables; bool not_used; @@ -205,9 +208,17 @@ TABLE *evex_open_event_table(THD *thd, enum thr_lock_type lock_type) tables.lock_type= lock_type; if (simple_open_n_lock_tables(thd, &tables)) - DBUG_RETURN(0); + DBUG_RETURN(1); + + if (tables.table->s->fields != EVEX_FIELD_COUNT) + { + my_error(ER_EVENT_COL_COUNT_DOESNT_MATCH, MYF(0), "mysql", "event"); + close_thread_tables(thd); + DBUG_RETURN(2); + } + *table= tables.table; - DBUG_RETURN(tables.table); + DBUG_RETURN(0); } @@ -382,7 +393,7 @@ db_create_event(THD *thd, event_timed *et) DBUG_PRINT("info", ("open mysql.event for update")); - if (!(table= evex_open_event_table(thd, TL_WRITE))) + if (evex_open_event_table(thd, TL_WRITE, &table)) { my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0)); goto err; @@ -491,7 +502,7 @@ db_update_event(THD *thd, event_timed *et, sp_name *new_name) DBUG_PRINT("enter", ("rename to: %.*s", new_name->m_name.length, new_name->m_name.str)); - if (!(table= evex_open_event_table(thd, TL_WRITE))) + if (evex_open_event_table(thd, TL_WRITE, &table)) { my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0)); goto err; @@ -590,7 +601,7 @@ db_find_event(THD *thd, sp_name *name, event_timed **ett, TABLE *tbl) if (tbl) table= tbl; - else if (!(table= evex_open_event_table(thd, TL_READ))) + else if (evex_open_event_table(thd, TL_READ, &table)) { my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0)); ret= EVEX_GENERAL_ERROR; @@ -869,7 +880,7 @@ evex_drop_event(THD *thd, event_timed *et, bool drop_if_exists) bool opened; DBUG_ENTER("evex_drop_event"); - if (!(table= evex_open_event_table(thd, TL_WRITE))) + if (evex_open_event_table(thd, TL_WRITE, &table)) { my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0)); goto done; diff --git a/sql/event_executor.cc b/sql/event_executor.cc index 9578cd936ea..2f258257b5f 100644 --- a/sql/event_executor.cc +++ b/sql/event_executor.cc @@ -209,13 +209,13 @@ event_executor_main(void *arg) evex_is_running= true; VOID(pthread_mutex_unlock(&LOCK_evex_running)); + thd->security_ctx->user= my_strdup("event_scheduler", MYF(0)); + if (evex_load_events_from_db(thd)) goto err; - thd->security_ctx->user= my_strdup("event_scheduler", MYF(0)); - THD_CHECK_SENTRY(thd); - /* Read queries from the IO/THREAD until this thread is killed */ evex_main_thread_id= thd->thread_id; + sql_print_information("Scheduler thread started"); while (!thd->killed) { @@ -509,8 +509,11 @@ evex_load_events_from_db(THD *thd) DBUG_ENTER("evex_load_events_from_db"); - if (!(table= evex_open_event_table(thd, TL_READ))) + if ((ret= evex_open_event_table(thd, TL_READ, &table))) + { + sql_print_error("Table mysql.event is damaged."); DBUG_RETURN(SP_OPEN_TABLE_FAILED); + } VOID(pthread_mutex_lock(&LOCK_event_arrays)); diff --git a/sql/event_priv.h b/sql/event_priv.h index cb45a700cb8..7eeced892a3 100644 --- a/sql/event_priv.h +++ b/sql/event_priv.h @@ -53,8 +53,8 @@ int evex_db_find_event_aux(THD *thd, const LEX_STRING dbname, const LEX_STRING rname, TABLE *table); -TABLE * -evex_open_event_table(THD *thd, enum thr_lock_type lock_type); +int +evex_open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table); int event_timed_compare_q(void *vptr, byte* a, byte *b); diff --git a/sql/event_timed.cc b/sql/event_timed.cc index aa22696b27f..84a3eadef0e 100644 --- a/sql/event_timed.cc +++ b/sql/event_timed.cc @@ -739,7 +739,7 @@ event_timed::update_fields(THD *thd) thd->reset_n_backup_open_tables_state(&backup); - if (!(table= evex_open_event_table(thd, TL_WRITE))) + if (evex_open_event_table(thd, TL_WRITE, &table)) { ret= SP_OPEN_TABLE_FAILED; goto done; From a820fa4fb11b8a5b60dbc9b27c18ac9aa973909c Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 16 Dec 2005 13:01:46 +0100 Subject: [PATCH 21/28] WL #1034 update - varchar -> char - transient_expression -> interval_value - interval_type -> interval_field mysql-test/lib/init_db.sql: - varchar -> char - transient_expression -> interval_value - interval_type -> interval_field scripts/mysql_fix_privilege_tables.sql: - varchar -> char - transient_expression -> interval_value - interval_type -> interval_field sql/event.h: - varchar -> char - transient_expression -> interval_value - interval_type -> interval_field --- mysql-test/lib/init_db.sql | 12 ++++---- scripts/mysql_fix_privilege_tables.sql | 32 ++++++++++---------- sql/event.h | 42 +++++++++++++++----------- 3 files changed, 46 insertions(+), 40 deletions(-) diff --git a/mysql-test/lib/init_db.sql b/mysql-test/lib/init_db.sql index f075a1ebe93..6235f91f849 100644 --- a/mysql-test/lib/init_db.sql +++ b/mysql-test/lib/init_db.sql @@ -571,13 +571,13 @@ CREATE TABLE proc ( CREATE TABLE event ( - db VARCHAR(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '', - name VARCHAR(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '', + db char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '', + name char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '', body longblob NOT NULL, - definer VARCHAR(77) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '', + definer char(77) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '', execute_at DATETIME default NULL, - transient_expression int(11) default NULL, - interval_type ENUM('YEAR','QUARTER','MONTH','DAY','HOUR','MINUTE','WEEK', + interval_value int(11) default NULL, + interval_field ENUM('YEAR','QUARTER','MONTH','DAY','HOUR','MINUTE','WEEK', 'SECOND','MICROSECOND', 'YEAR_MONTH','DAY_HOUR', 'DAY_MINUTE','DAY_SECOND', 'HOUR_MINUTE','HOUR_SECOND', @@ -592,5 +592,5 @@ CREATE TABLE event ( status ENUM('ENABLED','DISABLED') NOT NULL default 'ENABLED', on_completion ENUM('DROP','PRESERVE') NOT NULL default 'DROP', comment varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '', - PRIMARY KEY (db, name) + PRIMARY KEY (db,name) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT 'Events'; diff --git a/scripts/mysql_fix_privilege_tables.sql b/scripts/mysql_fix_privilege_tables.sql index 255f83e473e..58be425ebf2 100644 --- a/scripts/mysql_fix_privilege_tables.sql +++ b/scripts/mysql_fix_privilege_tables.sql @@ -533,28 +533,28 @@ ALTER TABLE proc MODIFY db CREATE TABLE event ( - 'db' VARCHAR(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '', - 'name' VARCHAR(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '', - 'body' longblob NOT NULL, - 'definer' VARCHAR(77) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '', - 'execute_at' DATETIME default NULL, - 'transient_expression' int(11) default NULL, - 'interval_type' ENUM('YEAR','QUARTER','MONTH','DAY','HOUR','MINUTE','WEEK', + db char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '', + name char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '', + body longblob NOT NULL, + definer char(77) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '', + execute_at DATETIME default NULL, + interval_value int(11) default NULL, + interval_field 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') default NULL, - 'created' TIMESTAMP NOT NULL default '0000-00-00 00:00:00', - 'modified' TIMESTAMP NOT NULL default '0000-00-00 00:00:00', - 'last_executed' DATETIME default NULL, - 'starts' DATETIME default NULL, - 'ends' DATETIME default NULL, - 'status' ENUM('ENABLED','DISABLED') NOT NULL default 'ENABLED', - 'on_completion' ENUM('DROP','PRESERVE') NOT NULL default 'DROP', - 'comment' varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '', - PRIMARY KEY ('db','name') + created TIMESTAMP NOT NULL, + modified TIMESTAMP NOT NULL, + last_executed DATETIME default NULL, + starts DATETIME default NULL, + ends DATETIME default NULL, + status ENUM('ENABLED','DISABLED') NOT NULL default 'ENABLED', + on_completion ENUM('DROP','PRESERVE') NOT NULL default 'DROP', + comment varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '', + PRIMARY KEY (db,name) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT 'Events'; diff --git a/sql/event.h b/sql/event.h index f3b49a99488..8337a7c82d9 100644 --- a/sql/event.h +++ b/sql/event.h @@ -195,24 +195,30 @@ event_timed_compare(event_timed **a, event_timed **b); /* -CREATE TABLE `event` ( - `db` varchar(64) character set utf8 collate utf8_bin NOT NULL default '', - `name` varchar(64) character set utf8 collate utf8_bin NOT NULL default '', - `body` longblob NOT NULL, - `definer` varchar(77) character set utf8 collate utf8_bin NOT NULL default '', - `execute_at` datetime default NULL, - `transient_expression` int(11) default NULL, - `interval_type` 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') default NULL, - `created` timestamp NOT NULL, - `modified` timestamp NOT NULL, - `last_executed` datetime default NULL, - `starts` datetime default NULL, - `ends` datetime default NULL, - `status` enum('ENABLED','DISABLED') NOT NULL default 'ENABLED', - `on_completion` enum('DROP','PRESERVE') NOT NULL default 'DROP', - `comment` varchar(64) character set utf8 collate utf8_bin NOT NULL default '', - PRIMARY KEY (`db`,`name`) -) ENGINE=MyISAM DEFAULT CHARSET=utf8 +CREATE TABLE event ( + db char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '', + name char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '', + body longblob NOT NULL, + definer char(77) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '', + execute_at DATETIME default NULL, + interval_value int(11) default NULL, + interval_field 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') default NULL, + created TIMESTAMP NOT NULL, + modified TIMESTAMP NOT NULL, + last_executed DATETIME default NULL, + starts DATETIME default NULL, + ends DATETIME default NULL, + status ENUM('ENABLED','DISABLED') NOT NULL default 'ENABLED', + on_completion ENUM('DROP','PRESERVE') NOT NULL default 'DROP', + comment varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '', + PRIMARY KEY (db,name) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT 'Events'; */ #endif /* _EVENT_H_ */ From 9b323387b817351caadcdb57b880242081c01abe Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 20 Dec 2005 14:21:02 +0200 Subject: [PATCH 22/28] WL #1034 update - fix one bug found by PeterG, namely bug #51 #there is a major problem currently after removing the specialised DYNAMIC_ARRAY as storage for the events. I have to reintroduce similar storage, this time probably some linked list or maybe some API on top of DYNAMIC_ARRAY. sql/event.cc: close the table sql/event.h: change definition sql/event_executor.cc: don't start the thread in advance sql/event_timed.cc: - don't call evex_drop_event - quote the name during compilation to make create event `the rain in spain goes into the drain` not fail. --- sql/event.cc | 8 ++++---- sql/event.h | 6 +++--- sql/event_executor.cc | 31 +++++++++++++++++++--------- sql/event_timed.cc | 48 ++++++++++++++++++++++++++++++++----------- 4 files changed, 64 insertions(+), 29 deletions(-) diff --git a/sql/event.cc b/sql/event.cc index 365ebcb063f..162c187d091 100644 --- a/sql/event.cc +++ b/sql/event.cc @@ -877,7 +877,6 @@ evex_drop_event(THD *thd, event_timed *et, bool drop_if_exists) { TABLE *table; int ret= EVEX_OPEN_TABLE_FAILED; - bool opened; DBUG_ENTER("evex_drop_event"); if (evex_open_event_table(thd, TL_WRITE, &table)) @@ -912,11 +911,12 @@ evex_drop_event(THD *thd, event_timed *et, bool drop_if_exists) ret= evex_remove_from_cache(&et->dbname, &et->name, true); VOID(pthread_mutex_unlock(&LOCK_evex_running)); -done: +done: /* - No need to close the table, it will be closed in sql_parse::do_command() - and evex_remove_from_cache does not try to open a table + evex_drop_event() is used by event_timed::drop therefore + we have to close our thread tables. */ + close_thread_tables(thd); DBUG_RETURN(ret); } diff --git a/sql/event.h b/sql/event.h index 8337a7c82d9..66a0b6ef7ec 100644 --- a/sql/event.h +++ b/sql/event.h @@ -102,13 +102,13 @@ public: free_sphead_on_delete(true), flags(0) { - pthread_mutex_init(&LOCK_running, MY_MUTEX_INIT_FAST); + pthread_mutex_init(&this->LOCK_running, MY_MUTEX_INIT_FAST); init(); } ~event_timed() { - pthread_mutex_destroy(&LOCK_running); + pthread_mutex_destroy(&this->LOCK_running); if (free_sphead_on_delete) free_sp(); } @@ -149,7 +149,7 @@ public: void mark_last_executed(); - bool + int drop(THD *thd); bool diff --git a/sql/event_executor.cc b/sql/event_executor.cc index 2f258257b5f..3a4a650ee87 100644 --- a/sql/event_executor.cc +++ b/sql/event_executor.cc @@ -90,14 +90,17 @@ init_events() VOID(pthread_mutex_lock(&LOCK_evex_running)); evex_is_running= false; VOID(pthread_mutex_unlock(&LOCK_evex_running)); - + + if (event_executor_running_global_var) + { #ifndef DBUG_FAULTY_THR - //TODO Andrey: Change the error code returned! - if (pthread_create(&th, NULL, event_executor_main, (void*)NULL)) - DBUG_RETURN(ER_SLAVE_THREAD); + //TODO Andrey: Change the error code returned! + if (pthread_create(&th, NULL, event_executor_main, (void*)NULL)) + DBUG_RETURN(ER_SLAVE_THREAD); #else - event_executor_main(NULL); + event_executor_main(NULL); #endif + } DBUG_RETURN(0); } @@ -249,6 +252,17 @@ event_executor_main(void *arg) continue; } et= evex_queue_first_element(&EVEX_EQ_NAME, event_timed*); + if (et->status == MYSQL_EVENT_DISABLED) + { + DBUG_PRINT("evex_load_events_from_db",("Now it is disabled-exec no more")); + if (et->dropped) + et->drop(thd); + delete et; + evex_queue_delete_element(&EVEX_EQ_NAME, 1);// 1 is top + VOID(pthread_mutex_unlock(&LOCK_event_arrays)); + sql_print_information("Event found disabled, dropping."); + continue; + } time(&now); my_tz_UTC->gmt_sec_to_TIME(&time_now, now); @@ -256,8 +270,6 @@ event_executor_main(void *arg) VOID(pthread_mutex_unlock(&LOCK_event_arrays)); if (t2sleep > 0) { -// sql_print_information("Sleeping for %d seconds.", t2sleep); -// printf("\nWHEN=%llu NOW=%llu\n", TIME_to_ulonglong_datetime(&et->execute_at), TIME_to_ulonglong_datetime(&time_now)); /* We sleep t2sleep seconds but we check every second whether this thread has been killed, or there is new candidate @@ -266,11 +278,9 @@ event_executor_main(void *arg) evex_queue_num_elements(EVEX_EQ_NAME) && (evex_queue_first_element(&EVEX_EQ_NAME, event_timed*) == et)) my_sleep(1000000); -// sql_print_information("Finished sleeping"); } if (!event_executor_running_global_var) continue; - } @@ -316,7 +326,7 @@ event_executor_main(void *arg) printf("[%10s] next at [%llu]\n\n\n", et->name.str,TIME_to_ulonglong_datetime(&et->execute_at)); et->update_fields(thd); if ((et->execute_at.year && !et->expression) || - TIME_to_ulonglong_datetime(&et->execute_at) == 0L) + TIME_to_ulonglong_datetime(&et->execute_at) == 0) et->flags |= EVENT_EXEC_NO_MORE; } if ((et->flags & EVENT_EXEC_NO_MORE) || et->status == MYSQL_EVENT_DISABLED) @@ -551,6 +561,7 @@ evex_load_events_from_db(THD *thd) et->dbname.str, et->name.str); goto end; } + // let's find when to be executed et->compute_next_execution_time(); diff --git a/sql/event_timed.cc b/sql/event_timed.cc index 84a3eadef0e..50d171a658d 100644 --- a/sql/event_timed.cc +++ b/sql/event_timed.cc @@ -714,10 +714,34 @@ event_timed::mark_last_executed() } -bool +/* + Returns : + 0 - OK + -1 - Cannot open mysql.event + -2 - Cannot find the event in mysql.event (already deleted?) + + others - return code from SE in case deletion of the event row + failed. +*/ + +int event_timed::drop(THD *thd) { - return (bool) evex_drop_event(thd, this, false); + TABLE *table; + int ret= 0; + DBUG_ENTER("event_timed::drop"); + + if (evex_open_event_table(thd, TL_WRITE, &table)) + DBUG_RETURN(-1); + + if (evex_db_find_event_aux(thd, dbname, name, table)) + DBUG_RETURN(-2); + + if ((ret= table->file->delete_row(table->record[0]))) + DBUG_RETURN(ret); + + close_thread_tables(thd); + DBUG_RETURN(0); } @@ -783,11 +807,11 @@ event_timed::get_show_create_event(THD *thd, uint *length) char *dst, *ret; uint len, tmp_len; - len = strlen("CREATE EVENT ") + dbname.length + strlen(".") + name.length + - strlen(" ON SCHEDULE EVERY 5 MINUTE DO ") + body.length + strlen(";"); + len = strlen("CREATE EVENT `") + dbname.length + strlen(".") + name.length + + strlen("` ON SCHEDULE EVERY 5 MINUTE DO ") + body.length + strlen(";"); ret= dst= (char*) alloc_root(thd->mem_root, len + 1); - memcpy(dst, "CREATE EVENT ", tmp_len= strlen("CREATE EVENT ")); + memcpy(dst, "CREATE EVENT `", tmp_len= strlen("CREATE EVENT `")); dst+= tmp_len; memcpy(dst, dbname.str, tmp_len=dbname.length); dst+= tmp_len; @@ -795,8 +819,8 @@ event_timed::get_show_create_event(THD *thd, uint *length) dst+= tmp_len; memcpy(dst, name.str, tmp_len= name.length); dst+= tmp_len; - memcpy(dst, " ON SCHEDULE EVERY 5 MINUTE DO ", - tmp_len= strlen(" ON SCHEDULE EVERY 5 MINUTE DO ")); + memcpy(dst, "` ON SCHEDULE EVERY 5 MINUTE DO ", + tmp_len= strlen("` ON SCHEDULE EVERY 5 MINUTE DO ")); dst+= tmp_len; memcpy(dst, body.str, tmp_len= body.length); @@ -834,14 +858,14 @@ event_timed::execute(THD *thd, MEM_ROOT *mem_root) DBUG_ENTER("event_timed::execute"); - VOID(pthread_mutex_lock(&LOCK_running)); + VOID(pthread_mutex_lock(&this->LOCK_running)); if (running) { - VOID(pthread_mutex_unlock(&LOCK_running)); + VOID(pthread_mutex_unlock(&this->LOCK_running)); DBUG_RETURN(-100); } running= true; - VOID(pthread_mutex_unlock(&LOCK_running)); + VOID(pthread_mutex_unlock(&this->LOCK_running)); // TODO Andrey : make this as member variable and delete in destructor empty_item_list.empty(); @@ -851,9 +875,9 @@ event_timed::execute(THD *thd, MEM_ROOT *mem_root) ret= sphead->execute_procedure(thd, &empty_item_list); - VOID(pthread_mutex_lock(&LOCK_running)); + VOID(pthread_mutex_lock(&this->LOCK_running)); running= false; - VOID(pthread_mutex_unlock(&LOCK_running)); + VOID(pthread_mutex_unlock(&this->LOCK_running)); done: // Don't cache sphead if allocated on another mem_root From 3a12408a70279203835520599748c5a44800505a Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 28 Dec 2005 12:07:57 +0200 Subject: [PATCH 23/28] WL #1034 (update) - improve the stability of the executor - make create event if not exists work as before sql/event.cc: refactoring: - have only 1 routine for comparing TIME structures fix: - after previous refactoring IF NOT EXISTS of CREATE EVENT did not work anymore. Now it will work. sql/event.h: update definitions sql/event_executor.cc: - don't load DISABLED events - if an event is being disabled because of time restrictions - drop it from the prio queue - move dropping to the worker process sql/event_priv.h: - remove unneeded func sql/share/errmsg.txt: fix error message sql/sql_parse.cc: - support 0 rows affected when CREATE EVENT IF NOT EXISTS --- sql/event.cc | 151 ++++++++++++------------------------------ sql/event.h | 9 ++- sql/event_executor.cc | 51 ++++++++------ sql/event_priv.h | 4 -- sql/share/errmsg.txt | 2 +- sql/sql_parse.cc | 10 +-- 6 files changed, 83 insertions(+), 144 deletions(-) diff --git a/sql/event.cc b/sql/event.cc index 162c187d091..bb15f4182da 100644 --- a/sql/event.cc +++ b/sql/event.cc @@ -71,9 +71,8 @@ MEM_ROOT evex_mem_root; void evex_queue_init(EVEX_QUEUE_TYPE *queue) { - if (init_queue_ex(queue, 100 /*num_el*/, 0 /*offset*/, - 0 /*smallest_on_top*/, event_timed_compare_q, NULL, - 100 /*auto_extent*/)) + if (init_queue_ex(queue, 30 /*num_el*/, 0 /*offset*/, 0 /*smallest_on_top*/, + event_timed_compare_q, NULL, 30 /*auto_extent*/)) sql_print_error("Insufficient memory to initialize executing queue."); } @@ -81,103 +80,37 @@ evex_queue_init(EVEX_QUEUE_TYPE *queue) static int sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs) { - return cs->coll->strnncollsp(cs, - (unsigned char *) s.str,s.length, - (unsigned char *) t.str,t.length, 0); + return cs->coll->strnncollsp(cs, (unsigned char *) s.str,s.length, + (unsigned char *) t.str,t.length, 0); } int my_time_compare(TIME *a, TIME *b) { -/* - Or maybe it is faster to use TIME_to_ulonglong_datetime - for "a" and "b" -*/ - - DBUG_ENTER("my_time_compare"); - - if (a->year > b->year) - DBUG_RETURN(1); - - if (a->year < b->year) - DBUG_RETURN(-1); - - if (a->month > b->month) - DBUG_RETURN(1); - - if (a->month < b->month) - DBUG_RETURN(-1); - - if (a->day > b->day) - DBUG_RETURN(1); - - if (a->day < b->day) - DBUG_RETURN(-1); - - if (a->hour > b->hour) - DBUG_RETURN(1); - - if (a->hour < b->hour) - DBUG_RETURN(-1); - - if (a->minute > b->minute) - DBUG_RETURN(1); - - if (a->minute < b->minute) - DBUG_RETURN(-1); - - if (a->second > b->second) - DBUG_RETURN(1); - - if (a->second < b->second) - DBUG_RETURN(-1); - - - if (a->second_part > b->second_part) - DBUG_RETURN(1); - - if (a->second_part < b->second_part) - DBUG_RETURN(-1); - - - DBUG_RETURN(0); -} - - -int -evex_time_diff(TIME *a, TIME *b) -{ - my_bool in_gap; - DBUG_ENTER("my_time_diff"); - - return sec_since_epoch_TIME(a) - sec_since_epoch_TIME(b); -} - - -inline int -event_timed_compare(event_timed **a, event_timed **b) -{ - my_ulonglong a_t, b_t; - a_t= TIME_to_ulonglong_datetime(&(*a)->execute_at)*100L + - (*a)->execute_at.second_part; - b_t= TIME_to_ulonglong_datetime(&(*b)->execute_at)*100L + - (*b)->execute_at.second_part; + 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; if (a_t > b_t) return 1; else if (a_t < b_t) return -1; - else - return 0; - + + return 0; +} + + +inline int +event_timed_compare(event_timed *a, event_timed *b) +{ + return my_time_compare(&a->execute_at, &b->execute_at); } int event_timed_compare_q(void *vptr, byte* a, byte *b) { - return event_timed_compare((event_timed **)&a, (event_timed **)&b); + return event_timed_compare((event_timed *)a, (event_timed *)b); } @@ -369,8 +302,10 @@ evex_fill_row(THD *thd, TABLE *table, event_timed *et, my_bool is_update) SYNOPSIS db_create_event() - thd THD - et event_timed object containing information for the 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 @@ -381,9 +316,10 @@ evex_fill_row(THD *thd, TABLE *table, event_timed *et, my_bool is_update) */ static int -db_create_event(THD *thd, event_timed *et) +db_create_event(THD *thd, event_timed *et, my_bool create_if_not, + uint *rows_affected) { - int ret= EVEX_OK; + int ret= 0; TABLE *table; char definer[HOSTNAME_LENGTH+USERNAME_LENGTH+2]; char olddb[128]; @@ -391,7 +327,7 @@ db_create_event(THD *thd, event_timed *et) 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 (evex_open_event_table(thd, TL_WRITE, &table)) { @@ -402,8 +338,10 @@ db_create_event(THD *thd, event_timed *et) DBUG_PRINT("info", ("check existance of an event with the same name")); if (!evex_db_find_event_aux(thd, et->dbname, et->name, table)) { - my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), et->name.str); - goto err; + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_EVENT_ALREADY_EXISTS, ER(ER_EVENT_ALREADY_EXISTS), + et->name.str); + goto ok; } DBUG_PRINT("info", ("non-existant, go forward")); @@ -461,7 +399,9 @@ db_create_event(THD *thd, event_timed *et) Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE); mysql_bin_log.write(&qinfo); } - + + *rows_affected= 1; +ok: if (dbchanged) (void) mysql_change_db(thd, olddb, 1); if (table) @@ -755,6 +695,7 @@ done: et event's data create_options Options specified when in the query. We are interested whether there is IF NOT EXISTS + rows_affected How many rows were affected NOTES - in case there is an event with the same name (db) and @@ -762,7 +703,8 @@ done: */ int -evex_create_event(THD *thd, event_timed *et, uint create_options) +evex_create_event(THD *thd, event_timed *et, uint create_options, + uint *rows_affected) { int ret = 0; @@ -770,22 +712,9 @@ evex_create_event(THD *thd, event_timed *et, uint create_options) DBUG_PRINT("enter", ("name: %*s options:%d", et->name.length, et->name.str, create_options)); - if ((ret = db_create_event(thd, et)) == EVEX_WRITE_ROW_FAILED && - (create_options & HA_LEX_CREATE_IF_NOT_EXISTS)) - { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, - ER_DB_CREATE_EXISTS, ER(ER_DB_CREATE_EXISTS), - "EVENT", et->name.str); - ret= 0; - goto done; - } - /* - A warning is thrown only when create_options is set to - HA_LEX_CREATE_IF_NOT_EXISTS. In this case if EVEX_WRITE_ROW_FAILED, - which means that we have duplicated key -> warning. In all - other cases -> error. - */ - if (ret) + if ((ret = db_create_event(thd, et, + create_options & HA_LEX_CREATE_IF_NOT_EXISTS, + rows_affected))) goto done; VOID(pthread_mutex_lock(&LOCK_evex_running)); @@ -819,7 +748,8 @@ done: */ int -evex_update_event(THD *thd, event_timed *et, sp_name *new_name) +evex_update_event(THD *thd, event_timed *et, sp_name *new_name, + uint *rows_affected) { int ret, i; bool need_second_pass= true; @@ -873,7 +803,8 @@ done: */ int -evex_drop_event(THD *thd, event_timed *et, bool drop_if_exists) +evex_drop_event(THD *thd, event_timed *et, bool drop_if_exists, + uint *rows_affected) { TABLE *table; int ret= EVEX_OPEN_TABLE_FAILED; diff --git a/sql/event.h b/sql/event.h index 66a0b6ef7ec..975b9953611 100644 --- a/sql/event.h +++ b/sql/event.h @@ -173,13 +173,16 @@ public: int -evex_create_event(THD *thd, event_timed *et, uint create_options); +evex_create_event(THD *thd, event_timed *et, uint create_options, + uint *rows_affected); int -evex_update_event(THD *thd, event_timed *et, sp_name *new_name); +evex_update_event(THD *thd, event_timed *et, sp_name *new_name, + uint *rows_affected); int -evex_drop_event(THD *thd, event_timed *et, bool drop_if_exists); +evex_drop_event(THD *thd, event_timed *et, bool drop_if_exists, + uint *rows_affected); int diff --git a/sql/event_executor.cc b/sql/event_executor.cc index 3a4a650ee87..c8e7ebcdb5a 100644 --- a/sql/event_executor.cc +++ b/sql/event_executor.cc @@ -61,8 +61,15 @@ event_executor_worker(void *arg); pthread_handler_t event_executor_main(void *arg); -static -void evex_init_mutexes() +static int +evex_time_diff(TIME *a, TIME *b) +{ + return sec_since_epoch_TIME(a) - sec_since_epoch_TIME(b); +} + + +static void +evex_init_mutexes() { if (evex_mutexes_initted) return; @@ -239,8 +246,6 @@ event_executor_main(void *arg) { int t2sleep; - - /* now let's see how much time to sleep, we know there is at least 1 element in the queue. @@ -272,7 +277,7 @@ event_executor_main(void *arg) { /* We sleep t2sleep seconds but we check every second whether this thread - has been killed, or there is new candidate + has been killed, or there is a new candidate */ while (t2sleep-- && !thd->killed && evex_queue_num_elements(EVEX_EQ_NAME) && @@ -308,10 +313,13 @@ event_executor_main(void *arg) { pthread_t th; + printf("[%10s] exec at [%llu]\n", et->name.str,TIME_to_ulonglong_datetime(&et->execute_at)); + et->mark_last_executed(); + et->compute_next_execution_time(); + printf("[%10s] next at [%llu]\n\n\n", et->name.str,TIME_to_ulonglong_datetime(&et->execute_at)); + et->update_fields(thd); DBUG_PRINT("info", (" Spawning a thread %d", ++iter_num)); -// sql_print_information(" Spawning a thread %d", ++iter_num); #ifndef DBUG_FAULTY_THR -// sql_print_information(" Thread is not debuggable!"); if (pthread_create(&th, NULL, event_executor_worker, (void*)et)) { sql_print_error("Problem while trying to create a thread"); @@ -320,24 +328,15 @@ event_executor_main(void *arg) #else event_executor_worker((void *) et); #endif - printf("[%10s] exec at [%llu]\n", et->name.str,TIME_to_ulonglong_datetime(&et->execute_at)); - et->mark_last_executed(); - et->compute_next_execution_time(); - printf("[%10s] next at [%llu]\n\n\n", et->name.str,TIME_to_ulonglong_datetime(&et->execute_at)); - et->update_fields(thd); if ((et->execute_at.year && !et->expression) || TIME_to_ulonglong_datetime(&et->execute_at) == 0) et->flags |= EVENT_EXEC_NO_MORE; - } - if ((et->flags & EVENT_EXEC_NO_MORE) || et->status == MYSQL_EVENT_DISABLED) - { - if (et->dropped) - et->drop(thd); - delete et; - evex_queue_delete_element(&EVEX_EQ_NAME, 1);// 1 is top - } else - evex_queue_first_updated(&EVEX_EQ_NAME); + if ((et->flags & EVENT_EXEC_NO_MORE) || et->status == MYSQL_EVENT_DISABLED) + evex_queue_delete_element(&EVEX_EQ_NAME, 1);// 1 is top + else + evex_queue_first_updated(&EVEX_EQ_NAME); + } VOID(pthread_mutex_unlock(&LOCK_event_arrays)); }// while @@ -454,7 +453,8 @@ event_executor_worker(void *event_void) strxnmov(thd->security_ctx->priv_host, sizeof(thd->security_ctx->priv_host), event->definer_host.str, NullS); - thd->security_ctx->user= thd->security_ctx->priv_user= my_strdup(event->definer_user.str, MYF(0)); + thd->security_ctx->user= thd->security_ctx->priv_user= + my_strdup(event->definer_user.str, MYF(0)); thd->db= event->dbname.str; if (!check_access(thd, EVENT_ACL, event->dbname.str, 0, 0, 0, @@ -467,6 +467,13 @@ event_executor_worker(void *event_void) sql_print_information(" EVEX EXECUTED event for event %s.%s [EXPR:%d]. RetCode=%d", event->dbname.str, event->name.str,(int) event->expression, ret); DBUG_PRINT("info", (" EVEX EXECUTED event for event %s.%s [EXPR:%d]. RetCode=%d", event->dbname.str, event->name.str,(int) event->expression, ret)); } + if ((event->flags & EVENT_EXEC_NO_MORE) || event->status==MYSQL_EVENT_DISABLED) + { + if (event->dropped) + event->drop(thd); + delete event; + } + thd->db= 0; err: diff --git a/sql/event_priv.h b/sql/event_priv.h index 7eeced892a3..3d5ce00a16e 100644 --- a/sql/event_priv.h +++ b/sql/event_priv.h @@ -59,10 +59,6 @@ evex_open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table); int event_timed_compare_q(void *vptr, byte* a, byte *b); -int -evex_time_diff(TIME *a, TIME *b); - - #define EXEC_QUEUE_QUEUE_NAME executing_queue #define EXEC_QUEUE_DARR_NAME evex_executing_queue diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index 0f5358d8176..d40e9924c02 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -5722,7 +5722,7 @@ ER_DROP_PARTITION_WHEN_FK_DEFINED ER_PLUGIN_IS_NOT_LOADED eng "Plugin '%-.64s' is not loaded" ER_EVENT_ALREADY_EXISTS - eng "Event %s already exists" + eng "Event '%-.64s' already exists" ER_EVENT_STORE_FAILED eng "Failed to store event %s. Error code %d from storage engine." ER_EVENT_DOES_NOT_EXIST diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index f06fa0bbafe..99ca3e63fc1 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3675,6 +3675,7 @@ end_with_restore_list: case SQLCOM_ALTER_EVENT: case SQLCOM_DROP_EVENT: { + uint rows_affected= 1; DBUG_ASSERT(lex->et); do { if (! lex->et->dbname.str) @@ -3690,17 +3691,18 @@ end_with_restore_list: switch (lex->sql_command) { case SQLCOM_CREATE_EVENT: - res= evex_create_event(thd, lex->et, (uint) lex->create_info.options); + res= evex_create_event(thd, lex->et, (uint) lex->create_info.options, + &rows_affected); break; case SQLCOM_ALTER_EVENT: - res= evex_update_event(thd, lex->et, lex->spname); + res= evex_update_event(thd, lex->et, lex->spname, &rows_affected); break; case SQLCOM_DROP_EVENT: - res= evex_drop_event(thd, lex->et, lex->drop_if_exists); + res= evex_drop_event(thd, lex->et, lex->drop_if_exists, &rows_affected); default:; } if (!res) - send_ok(thd, 1); + send_ok(thd, rows_affected); /* lex->unit.cleanup() is called outside, no need to call it here */ } while (0); From b3e352725fcdf9d620913b5ad2349fd7349eb3c9 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 28 Dec 2005 13:00:13 +0200 Subject: [PATCH 24/28] WL #1034 update - enable/disable -> enabled/disabled - fixed error message sql/lex.h: change ENABLE to ENABLED and DISABLE to DISABLED in create/alter event sql/share/errmsg.txt: fix error msg sql/sql_yacc.yy: ENABLE -> ENABLED DISABLE -> DISABLED --- sql/lex.h | 2 ++ sql/share/errmsg.txt | 4 ++-- sql/sql_yacc.yy | 8 ++++++-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/sql/lex.h b/sql/lex.h index 41cbae0adea..8be5f0ef99d 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -165,6 +165,7 @@ static SYMBOL symbols[] = { { "DETERMINISTIC", SYM(DETERMINISTIC_SYM)}, { "DIRECTORY", SYM(DIRECTORY_SYM)}, { "DISABLE", SYM(DISABLE_SYM)}, + { "DISABLED", SYM(DISABLED_SYM)}, { "DISCARD", SYM(DISCARD)}, { "DISTINCT", SYM(DISTINCT)}, { "DISTINCTROW", SYM(DISTINCT)}, /* Access likes this */ @@ -180,6 +181,7 @@ static SYMBOL symbols[] = { { "ELSE", SYM(ELSE)}, { "ELSEIF", SYM(ELSEIF_SYM)}, { "ENABLE", SYM(ENABLE_SYM)}, + { "ENABLED", SYM(ENABLED_SYM)}, { "ENCLOSED", SYM(ENCLOSED)}, { "END", SYM(END)}, { "ENDS", SYM(ENDS_SYM)}, diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index d40e9924c02..71550d4eda4 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -5726,9 +5726,9 @@ ER_EVENT_ALREADY_EXISTS ER_EVENT_STORE_FAILED eng "Failed to store event %s. Error code %d from storage engine." ER_EVENT_DOES_NOT_EXIST - eng "Unknown event '%s'" + eng "Unknown event '%-.64s'" ER_EVENT_CANT_ALTER - eng "Failed to alter event %s" + eng "Failed to alter event '%-.64s'" ER_EVENT_DROP_FAILED eng "Failed to drop %s" ER_EVENT_INTERVAL_NOT_POSITIVE diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 488eee86627..f7277f6ca21 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -240,6 +240,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token DETERMINISTIC_SYM %token DIRECTORY_SYM %token DISABLE_SYM +%token DISABLED_SYM %token DISCARD %token DISTINCT %token DIV_SYM @@ -254,6 +255,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token ELSEIF_SYM %token ELT_FUNC %token ENABLE_SYM +%token ENABLED_SYM %token ENCLOSED %token ENCODE_SYM %token ENCRYPT @@ -1410,13 +1412,13 @@ ev_schedule_time: EVERY_SYM expr interval ; ev_status: /* empty */ - | ENABLE_SYM + | ENABLED_SYM { LEX *lex=Lex; if (!lex->et_compile_phase) lex->et->status= MYSQL_EVENT_ENABLED; } - | DISABLE_SYM + | DISABLED_SYM { LEX *lex=Lex; @@ -8592,6 +8594,7 @@ keyword_sp: | DELAY_KEY_WRITE_SYM {} | DES_KEY_FILE {} | DIRECTORY_SYM {} + | DISABLED_SYM {} | DISCARD {} | DUMPFILE {} | DUPLICATE_SYM {} @@ -8611,6 +8614,7 @@ keyword_sp: | FOUND_SYM {} | DISABLE_SYM {} | ENABLE_SYM {} + | ENABLED_SYM {} | FULL {} | FILE_SYM {} | FIRST_SYM {} From 8ba78896b3b0f6e6607fc51909ae125a1fca4787 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 28 Dec 2005 13:43:27 +0200 Subject: [PATCH 25/28] WL#1034 update - fix crash of an event that alters itself - make "alter event xyz;" invalid because at least 1 clause is needed. sql/sql_yacc.yy: - make "alter event xyz;" a syntax error. the user has to provide at least one clause --- sql/sql_yacc.yy | 47 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index f7277f6ca21..6589018060d 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1411,12 +1411,13 @@ ev_schedule_time: EVERY_SYM expr interval } ; -ev_status: /* empty */ +ev_status: /* empty */ {$$= 0;} | ENABLED_SYM { LEX *lex=Lex; if (!lex->et_compile_phase) - lex->et->status= MYSQL_EVENT_ENABLED; + lex->et->status= MYSQL_EVENT_ENABLED; + $$= 1; } | DISABLED_SYM { @@ -1424,6 +1425,7 @@ ev_status: /* empty */ if (!lex->et_compile_phase) lex->et->status= MYSQL_EVENT_DISABLED; + $$= 1; } ; ev_starts: /* empty */ @@ -1453,21 +1455,23 @@ ev_ends: /* empty */ } } ; -ev_on_completion: /* empty */ +ev_on_completion: /* empty */ {$$= 0;} | ON COMPLETION_SYM PRESERVE_SYM { LEX *lex=Lex; if (!lex->et_compile_phase) - lex->et->on_completion= MYSQL_EVENT_ON_COMPLETION_PRESERVE; + lex->et->on_completion= MYSQL_EVENT_ON_COMPLETION_PRESERVE; + $$= 1; } | ON COMPLETION_SYM NOT_SYM PRESERVE_SYM { LEX *lex=Lex; if (!lex->et_compile_phase) - lex->et->on_completion= MYSQL_EVENT_ON_COMPLETION_DROP; + lex->et->on_completion= MYSQL_EVENT_ON_COMPLETION_DROP; + $$= 1; } ; -ev_comment: /* empty */ +ev_comment: /* empty */ {$$= 0;} | COMMENT_SYM TEXT_STRING_sys { LEX *lex= Lex; @@ -1476,6 +1480,7 @@ ev_comment: /* empty */ lex->comment= $2; lex->et->init_comment(YYTHD, &$2); } + $$= 1; } ; @@ -4270,24 +4275,42 @@ alter: sql_command is set here because some rules in ev_sql_stmt can overwrite it */ + printf("5=%d 6=%d 7=%d 8=%d 9=%d 10=%d", $5 , $6 , $7 , + $8 , $9 , $10); + if (!($5 || $6 || $7 || + $8 || $9 || $10)) + { + yyerror(ER(ER_SYNTAX_ERROR)); + YYABORT; + } Lex->sql_command= SQLCOM_ALTER_EVENT; } ; -ev_on_schedule: /* empty */ - | ON SCHEDULE_SYM ev_schedule_time; +ev_on_schedule: /* empty */ { $$= 0;} + | ON SCHEDULE_SYM ev_schedule_time + { + $$= 1; + } + ; -ev_opt_sql_stmt: /* empty*/ - | DO_SYM ev_sql_stmt; - -ev_rename_to: /* empty */ +ev_rename_to: /* empty */ { $$= 0;} | RENAME TO_SYM sp_name { LEX *lex=Lex; lex->spname= $3; //use lex's spname to hold the new name //the original name is in the event_timed object + $$= 1; } ; + +ev_opt_sql_stmt: /* empty*/ { $$= 0;} + | DO_SYM ev_sql_stmt + { + $$= 1; + } + ; + ident_or_empty: /* empty */ { $$= 0; } From f3aea4764374dc069b8d91c15f0982261561894b Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 28 Dec 2005 18:29:32 +0200 Subject: [PATCH 26/28] WL #1034 update - fix problem with too long identifier name sql/event.cc: report an error when identifier too long sql/event_priv.h: name the enum fix truncation problem sql/share/errmsg.txt: new error message sql/sql_yacc.yy: remove debug info and whitespace --- sql/event.cc | 45 ++++++++++++++++++++++++++++++++++---------- sql/event_priv.h | 7 ++++--- sql/share/errmsg.txt | 2 ++ sql/sql_yacc.yy | 4 +--- 4 files changed, 42 insertions(+), 16 deletions(-) diff --git a/sql/event.cc b/sql/event.cc index bb15f4182da..006afbe949b 100644 --- a/sql/event.cc +++ b/sql/event.cc @@ -211,6 +211,11 @@ evex_db_find_event_aux(THD *thd, const LEX_STRING dbname, table the row to fill out et Event's data + Returns + 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. */ @@ -218,6 +223,8 @@ evex_db_find_event_aux(THD *thd, const LEX_STRING dbname, static int evex_fill_row(THD *thd, TABLE *table, event_timed *et, my_bool is_update) { + enum evex_table_field field_num; + DBUG_ENTER("evex_fill_row"); if (table->s->fields != EVEX_FIELD_COUNT) @@ -229,10 +236,13 @@ evex_fill_row(THD *thd, TABLE *table, event_timed *et, my_bool is_update) DBUG_PRINT("info", ("dbname.len=%d",et->dbname.length)); DBUG_PRINT("info", ("name.len=%d",et->name.length)); - table->field[EVEX_FIELD_DB]-> - store(et->dbname.str, et->dbname.length, system_charset_info); - table->field[EVEX_FIELD_NAME]-> - store(et->name.str, et->name.length, system_charset_info); + if (table->field[field_num= EVEX_FIELD_DB]-> + store(et->dbname.str, et->dbname.length, system_charset_info)) + goto trunc_err; + + if (table->field[field_num= EVEX_FIELD_NAME]-> + store(et->name.str, et->name.length, system_charset_info)) + goto trunc_err; table->field[EVEX_FIELD_ON_COMPLETION]->set_notnull(); table->field[EVEX_FIELD_ON_COMPLETION]->store((longlong)et->on_completion); @@ -243,8 +253,9 @@ evex_fill_row(THD *thd, TABLE *table, event_timed *et, my_bool is_update) // ToDo: Andrey. How to use users current charset? if (et->body.str) - table->field[EVEX_FIELD_BODY]-> - store(et->body.str, et->body.length, system_charset_info); + if (table->field[field_num= EVEX_FIELD_BODY]-> + store(et->body.str, et->body.length, system_charset_info)) + goto trunc_err; if (et->starts.year) { @@ -290,10 +301,14 @@ evex_fill_row(THD *thd, TABLE *table, event_timed *et, my_bool is_update) ((Field_timestamp *)table->field[EVEX_FIELD_MODIFIED])->set_time(); if (et->comment.length) - table->field[EVEX_FIELD_COMMENT]-> - store(et->comment.str, et->comment.length, system_charset_info); + if (table->field[field_num= EVEX_FIELD_COMMENT]-> + store(et->comment.str, et->comment.length, system_charset_info)) + goto trunc_err; - DBUG_RETURN(0); + DBUG_RETURN(0); +trunc_err: + my_error(ER_EVENT_DATA_TOO_LONG, MYF(0)); + DBUG_RETURN(EVEX_GENERAL_ERROR); } @@ -353,11 +368,21 @@ db_create_event(THD *thd, event_timed *et, my_bool create_if_not, restore_record(table, s->default_values); // Get default values for fields - if (et->name.length > table->field[EVEX_FIELD_NAME]->field_length) + 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[EVEX_FIELD_BODY]->field_length) { my_error(ER_TOO_LONG_BODY, MYF(0), et->name.str); diff --git a/sql/event_priv.h b/sql/event_priv.h index 3d5ce00a16e..05834383ae5 100644 --- a/sql/event_priv.h +++ b/sql/event_priv.h @@ -24,7 +24,7 @@ #define UNLOCK_MUTEX_AND_BAIL_OUT(__mutex, __label) \ { VOID(pthread_mutex_unlock(&__mutex)); goto __label; } -enum +enum evex_table_field { EVEX_FIELD_DB = 0, EVEX_FIELD_NAME, @@ -42,9 +42,10 @@ enum EVEX_FIELD_ON_COMPLETION, EVEX_FIELD_COMMENT, EVEX_FIELD_COUNT /* a cool trick to count the number of fields :) */ -}; - +} ; +#define EVEX_DB_FIELD_LEN 64 +#define EVEX_NAME_FIELD_LEN 64 int my_time_compare(TIME *a, TIME *b); diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index 71550d4eda4..f5f94c6c956 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -5751,3 +5751,5 @@ ER_EVENT_COMPILE_ERROR eng "Error during compilation of event's body" ER_EVENT_SAME_NAME eng "Same old and new event name" +ER_EVENT_DATA_TOO_LONG + eng "Data for column '%s' too long" diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 6589018060d..1fec9fefb0d 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -4275,10 +4275,8 @@ alter: sql_command is set here because some rules in ev_sql_stmt can overwrite it */ - printf("5=%d 6=%d 7=%d 8=%d 9=%d 10=%d", $5 , $6 , $7 , - $8 , $9 , $10); if (!($5 || $6 || $7 || - $8 || $9 || $10)) + $8 || $9 || $10)) { yyerror(ER(ER_SYNTAX_ERROR)); YYABORT; From f133a92312e6ca3040f58c05f4ea8d13f2f2ff5c Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 10 Jan 2006 11:31:45 +0100 Subject: [PATCH 27/28] WL 1034 update (pre-push cleanups removing debugging code) sql/event.cc: - comment - fix 80 cols - fix a crash when dropping a running event (after it has finished its work because the memory got freed in remove_from_cache but the event was still running) sql/event.h: - add new method sql/event_executor.cc: - remove printf-s - fix 80cols - fix message --- sql/event.cc | 38 ++++++++++++++++++++++++-------------- sql/event.h | 13 +++++++++++++ sql/event_executor.cc | 25 ++++++++++++++++--------- 3 files changed, 53 insertions(+), 23 deletions(-) diff --git a/sql/event.cc b/sql/event.cc index 006afbe949b..26f960e4bae 100644 --- a/sql/event.cc +++ b/sql/event.cc @@ -100,13 +100,17 @@ my_time_compare(TIME *a, TIME *b) } -inline int +int event_timed_compare(event_timed *a, event_timed *b) { return my_time_compare(&a->execute_at, &b->execute_at); } +/* + Callback for the prio queue +*/ + int event_timed_compare_q(void *vptr, byte* a, byte *b) { @@ -244,14 +248,11 @@ evex_fill_row(THD *thd, TABLE *table, event_timed *et, my_bool is_update) store(et->name.str, et->name.length, system_charset_info)) goto trunc_err; - table->field[EVEX_FIELD_ON_COMPLETION]->set_notnull(); + // both ON_COMPLETION and STATUS are NOT NULL thus not calling set_notnull() table->field[EVEX_FIELD_ON_COMPLETION]->store((longlong)et->on_completion); - table->field[EVEX_FIELD_STATUS]->set_notnull(); table->field[EVEX_FIELD_STATUS]->store((longlong)et->status); -// et->status_changed= false; - // ToDo: Andrey. How to use users current charset? if (et->body.str) if (table->field[field_num= EVEX_FIELD_BODY]-> store(et->body.str, et->body.length, system_charset_info)) @@ -260,13 +261,15 @@ evex_fill_row(THD *thd, TABLE *table, event_timed *et, my_bool is_update) if (et->starts.year) { table->field[EVEX_FIELD_STARTS]->set_notnull();// set NULL flag to OFF - table->field[EVEX_FIELD_STARTS]->store_time(&et->starts, MYSQL_TIMESTAMP_DATETIME); + table->field[EVEX_FIELD_STARTS]-> + store_time(&et->starts, MYSQL_TIMESTAMP_DATETIME); } if (et->ends.year) { table->field[EVEX_FIELD_ENDS]->set_notnull(); - table->field[EVEX_FIELD_ENDS]->store_time(&et->ends, MYSQL_TIMESTAMP_DATETIME); + table->field[EVEX_FIELD_ENDS]-> + store_time(&et->ends, MYSQL_TIMESTAMP_DATETIME); } if (et->expression) @@ -276,10 +279,10 @@ evex_fill_row(THD *thd, TABLE *table, event_timed *et, my_bool is_update) table->field[EVEX_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! + In the enum (C) intervals start from 0 but in mysql enum valid values start + from 1. Thus +1 offset is needed! */ - table->field[EVEX_FIELD_TRANSIENT_INTERVAL]->store((longlong)et->interval + 1); + table->field[EVEX_FIELD_TRANSIENT_INTERVAL]->store((longlong)et->interval+1); } else if (et->execute_at.year) { @@ -288,8 +291,7 @@ evex_fill_row(THD *thd, TABLE *table, event_timed *et, my_bool is_update) table->field[EVEX_FIELD_EXECUTE_AT]->store_time(&et->execute_at, MYSQL_TIMESTAMP_DATETIME); - //this will make it NULL because we don't call set_notnull - table->field[EVEX_FIELD_TRANSIENT_INTERVAL]->store((longlong) 0); + table->field[EVEX_FIELD_TRANSIENT_INTERVAL]->set_null(); } else { @@ -693,8 +695,16 @@ 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)) { - et->free_sp(); - delete et; + if (!et->is_running()) + { + et->free_sp(); + delete et; + } + else + { + et->flags|= EVENT_EXEC_NO_MORE; + et->dropped= true; + } evex_queue_delete_element(&EVEX_EQ_NAME, i); // ok, we have cleaned goto done; diff --git a/sql/event.h b/sql/event.h index 975b9953611..2a23534b97f 100644 --- a/sql/event.h +++ b/sql/event.h @@ -113,6 +113,7 @@ public: free_sp(); } + void init(); @@ -164,6 +165,18 @@ public: int compile(THD *thd, MEM_ROOT *mem_root= NULL); + my_bool + is_running() + { + my_bool ret; + + VOID(pthread_mutex_lock(&this->LOCK_running)); + ret= running; + VOID(pthread_mutex_unlock(&this->LOCK_running)); + + return ret; + } + void free_sp() { delete sphead; diff --git a/sql/event_executor.cc b/sql/event_executor.cc index c8e7ebcdb5a..07e53d62a5e 100644 --- a/sql/event_executor.cc +++ b/sql/event_executor.cc @@ -313,10 +313,8 @@ event_executor_main(void *arg) { pthread_t th; - printf("[%10s] exec at [%llu]\n", et->name.str,TIME_to_ulonglong_datetime(&et->execute_at)); et->mark_last_executed(); et->compute_next_execution_time(); - printf("[%10s] next at [%llu]\n\n\n", et->name.str,TIME_to_ulonglong_datetime(&et->execute_at)); et->update_fields(thd); DBUG_PRINT("info", (" Spawning a thread %d", ++iter_num)); #ifndef DBUG_FAULTY_THR @@ -461,11 +459,19 @@ event_executor_worker(void *event_void) is_schema_db(event->dbname.str))) { int ret; - DBUG_PRINT("info", (" EVEX EXECUTING event for event %s.%s [EXPR:%d]", event->dbname.str, event->name.str,(int) event->expression)); - sql_print_information(" EVEX EXECUTING event for event %s.%s [EXPR:%d]", event->dbname.str, event->name.str,(int) event->expression); + DBUG_PRINT("info", (" EVEX EXECUTING event %s.%s [EXPR:%d]", + event->dbname.str, event->name.str,(int) event->expression)); + sql_print_information(" EVEX EXECUTING event %s.%s [EXPR:%d]", + event->dbname.str, event->name.str,(int) event->expression); + ret= event->execute(thd, &worker_mem_root); - sql_print_information(" EVEX EXECUTED event for event %s.%s [EXPR:%d]. RetCode=%d", event->dbname.str, event->name.str,(int) event->expression, ret); - DBUG_PRINT("info", (" EVEX EXECUTED event for event %s.%s [EXPR:%d]. RetCode=%d", event->dbname.str, event->name.str,(int) event->expression, ret)); + + sql_print_information(" EVEX EXECUTED event %s.%s [EXPR:%d]. RetCode=%d", + event->dbname.str, event->name.str, + (int) event->expression, ret); + DBUG_PRINT("info", (" EVEX EXECUTED event %s.%s [EXPR:%d]. RetCode=%d", + event->dbname.str, event->name.str, + (int) event->expression, ret)); } if ((event->flags & EVENT_EXEC_NO_MORE) || event->status==MYSQL_EVENT_DISABLED) { @@ -554,7 +560,7 @@ evex_load_events_from_db(THD *thd) } if (et->status != MYSQL_EVENT_ENABLED) { - DBUG_PRINT("evex_load_events_from_db",("Event %s is disabled", et->name.str)); + DBUG_PRINT("evex_load_events_from_db",("%s is disabled",et->name.str)); delete et; continue; } @@ -589,8 +595,9 @@ end: thd->version--; // Force close to free memory close_thread_tables(thd); - sql_print_information("Scheduler loaded %d events", count); - DBUG_PRINT("info", ("Finishing with status code %d. Loaded %d events", ret, count)); + sql_print_information("Scheduler loaded %d event%s", count, (count == 1)?"":"s"); + DBUG_PRINT("info", ("Status code %d. Loaded %d event(s)", ret, count)); + DBUG_RETURN(ret); } From 1ef97f1f3b3dc53a5945fb4fc7db6a3fea926379 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 10 Jan 2006 19:16:58 +0100 Subject: [PATCH 28/28] WL #1034 (Internal CRON) pre-push updates - fixed test results - fixed bug caught by information_schema.test . Bison temporal variables are very nice but extremely error-prone (Count one more time just to be sure). mysql-test/r/connect.result: fix result for WL#1034 (internal CRON) mysql-test/r/events.result: fix result for WL#1034 (internal CRON) mysql-test/r/grant.result: fix result for WL#1034 (internal CRON) mysql-test/r/information_schema.result: fix result for WL#1034 (internal CRON) mysql-test/r/lowercase_table_grant.result: fix result for WL#1034 (internal CRON) mysql-test/r/ps.result: fix result for WL#1034 (internal CRON) mysql-test/r/system_mysql_db.result: fix result for WL#1034 (internal CRON) mysql-test/t/events.test: fix result for WL#1034 (internal CRON) sql/sql_yacc.yy: - fix bug introduced by me when making usage of temporal bison variables. COUNT 7 times then write! --- mysql-test/r/connect.result | 3 +++ mysql-test/r/events.result | 2 +- mysql-test/r/grant.result | 17 +++++++++-------- mysql-test/r/information_schema.result | 10 ++++++++-- mysql-test/r/lowercase_table_grant.result | 8 ++++---- mysql-test/r/ps.result | 6 +++--- mysql-test/r/system_mysql_db.result | 3 +++ mysql-test/t/events.test | 2 +- sql/sql_yacc.yy | 8 ++++++-- 9 files changed, 38 insertions(+), 21 deletions(-) diff --git a/mysql-test/r/connect.result b/mysql-test/r/connect.result index 01c5f61e5ad..ed017641aa9 100644 --- a/mysql-test/r/connect.result +++ b/mysql-test/r/connect.result @@ -3,6 +3,7 @@ show tables; Tables_in_mysql columns_priv db +event func help_category help_keyword @@ -31,6 +32,7 @@ show tables; Tables_in_mysql columns_priv db +event func help_category help_keyword @@ -67,6 +69,7 @@ show tables; Tables_in_mysql columns_priv db +event func help_category help_keyword diff --git a/mysql-test/r/events.result b/mysql-test/r/events.result index 5816f9888a0..d8aee068c5e 100644 --- a/mysql-test/r/events.result +++ b/mysql-test/r/events.result @@ -4,7 +4,7 @@ Warnings: Note 1305 Event event1 does not exist create event event1 on schedule every 15 minute starts now() ends date_add(now(), interval 5 hour) DO begin end; alter event event1 rename to event2; -alter event event2 disable; +alter event event2 disabled; drop event event2; create event event2 on schedule every 2 second starts now() ends date_add(now(), interval 5 hour) comment "some" DO begin end; drop event event2; diff --git a/mysql-test/r/grant.result b/mysql-test/r/grant.result index 448847bc919..38ff211fe2a 100644 --- a/mysql-test/r/grant.result +++ b/mysql-test/r/grant.result @@ -11,8 +11,8 @@ GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' REQUIRE CIPHER 'EDH-RSA-DES-CBC3 GRANT SELECT ON `mysqltest`.* TO 'mysqltest_1'@'localhost' grant delete on mysqltest.* to mysqltest_1@localhost; select * from mysql.user where user="mysqltest_1"; -Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Create_user_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections max_user_connections -localhost mysqltest_1 N N N N N N N N N N N N N N N N N N N N N N N N N N SPECIFIED EDH-RSA-DES-CBC3-SHA 0 0 0 0 +Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Create_user_priv Event_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections max_user_connections +localhost mysqltest_1 N N N N N N N N N N N N N N N N N N N N N N N N N N N SPECIFIED EDH-RSA-DES-CBC3-SHA 0 0 0 0 show grants for mysqltest_1@localhost; Grants for mysqltest_1@localhost GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' REQUIRE CIPHER 'EDH-RSA-DES-CBC3-SHA' @@ -42,15 +42,15 @@ delete from mysql.user where user='mysqltest_1'; flush privileges; grant usage on *.* to mysqltest_1@localhost with max_queries_per_hour 10; select * from mysql.user where user="mysqltest_1"; -Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Create_user_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections max_user_connections -localhost mysqltest_1 N N N N N N N N N N N N N N N N N N N N N N N N N N 10 0 0 0 +Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Create_user_priv Event_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections max_user_connections +localhost mysqltest_1 N N N N N N N N N N N N N N N N N N N N N N N N N N N 10 0 0 0 show grants for mysqltest_1@localhost; Grants for mysqltest_1@localhost GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' WITH MAX_QUERIES_PER_HOUR 10 grant usage on *.* to mysqltest_1@localhost with max_updates_per_hour 20 max_connections_per_hour 30; select * from mysql.user where user="mysqltest_1"; -Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Create_user_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections max_user_connections -localhost mysqltest_1 N N N N N N N N N N N N N N N N N N N N N N N N N N 10 20 30 0 +Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Create_user_priv Event_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections max_user_connections +localhost mysqltest_1 N N N N N N N N N N N N N N N N N N N N N N N N N N N 10 20 30 0 show grants for mysqltest_1@localhost; Grants for mysqltest_1@localhost GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' WITH MAX_QUERIES_PER_HOUR 10 MAX_UPDATES_PER_HOUR 20 MAX_CONNECTIONS_PER_HOUR 30 @@ -85,7 +85,7 @@ revoke LOCK TABLES, ALTER on mysqltest.* from mysqltest_1@localhost; show grants for mysqltest_1@localhost; Grants for mysqltest_1@localhost GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' -GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, REFERENCES, INDEX, CREATE TEMPORARY TABLES, EXECUTE, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE ON `mysqltest`.* TO 'mysqltest_1'@'localhost' WITH GRANT OPTION +GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, REFERENCES, INDEX, CREATE TEMPORARY TABLES, EXECUTE, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, EVENT ON `mysqltest`.* TO 'mysqltest_1'@'localhost' WITH GRANT OPTION revoke all privileges on mysqltest.* from mysqltest_1@localhost; delete from mysql.user where user='mysqltest_1'; flush privileges; @@ -448,6 +448,7 @@ Create view Tables To create new views Create user Server Admin To create new users Delete Tables To delete existing rows Drop Databases,Tables To drop databases, tables, and views +Event Server Admin Creation, alteration, deletion and execution of events. Execute Functions,Procedures To execute stored routines File File access on server To read and write files on the server Grant option Databases,Tables,Functions,Procedures To give to other users those privileges you possess @@ -593,7 +594,7 @@ delete from tables_priv where host = '' and user = 'mysqltest_1'; flush privileges; set @user123="non-existent"; select * from mysql.db where user=@user123; -Host Db User Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Grant_priv References_priv Index_priv Alter_priv Create_tmp_table_priv Lock_tables_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Execute_priv +Host Db User Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Grant_priv References_priv Index_priv Alter_priv Create_tmp_table_priv Lock_tables_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Execute_priv Event_priv set names koi8r; create database ÂÄ; grant select on ÂÄ.* to root@localhost; diff --git a/mysql-test/r/information_schema.result b/mysql-test/r/information_schema.result index c7c3de9451a..1c927f12f93 100644 --- a/mysql-test/r/information_schema.result +++ b/mysql-test/r/information_schema.result @@ -54,6 +54,7 @@ VIEWS USER_PRIVILEGES columns_priv db +event func help_category help_keyword @@ -394,6 +395,7 @@ GRANTEE TABLE_CATALOG TABLE_SCHEMA PRIVILEGE_TYPE IS_GRANTABLE 'mysqltest_1'@'localhost' NULL test SHOW VIEW YES 'mysqltest_1'@'localhost' NULL test CREATE ROUTINE YES 'mysqltest_1'@'localhost' NULL test ALTER ROUTINE YES +'mysqltest_1'@'localhost' NULL test EVENT YES select * from information_schema.TABLE_PRIVILEGES where grantee like '%mysqltest_1%'; GRANTEE TABLE_CATALOG TABLE_SCHEMA TABLE_NAME PRIVILEGE_TYPE IS_GRANTABLE 'mysqltest_1'@'localhost' NULL test t1 SELECT NO @@ -723,7 +725,7 @@ CREATE TABLE t_crashme ( f1 BIGINT); CREATE VIEW a1 (t_CRASHME) AS SELECT f1 FROM t_crashme GROUP BY f1; CREATE VIEW a2 AS SELECT t_CRASHME FROM a1; count(*) -102 +103 drop view a2, a1; drop table t_crashme; select table_schema,table_name, column_name from @@ -747,6 +749,10 @@ TABLES CREATE_TIME datetime TABLES UPDATE_TIME datetime TABLES CHECK_TIME datetime TRIGGERS CREATED datetime +event execute_at datetime +event last_executed datetime +event starts datetime +event ends datetime SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES A WHERE NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.COLUMNS B @@ -794,7 +800,7 @@ flush privileges; SELECT table_schema, count(*) FROM information_schema.TABLES GROUP BY TABLE_SCHEMA; table_schema count(*) information_schema 16 -mysql 18 +mysql 19 create table t1 (i int, j int); create trigger trg1 before insert on t1 for each row begin diff --git a/mysql-test/r/lowercase_table_grant.result b/mysql-test/r/lowercase_table_grant.result index c3813d57074..df7e8066e72 100644 --- a/mysql-test/r/lowercase_table_grant.result +++ b/mysql-test/r/lowercase_table_grant.result @@ -6,8 +6,8 @@ Grants for mysqltest_1@localhost GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' GRANT ALL PRIVILEGES ON `mysqltest`.* TO 'mysqltest_1'@'localhost' select * from db where user = 'mysqltest_1'; -Host Db User Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Grant_priv References_priv Index_priv Alter_priv Create_tmp_table_priv Lock_tables_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Execute_priv -localhost mysqltest mysqltest_1 Y Y Y Y Y Y N Y Y Y Y Y Y Y Y Y Y +Host Db User Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Grant_priv References_priv Index_priv Alter_priv Create_tmp_table_priv Lock_tables_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Execute_priv Event_priv +localhost mysqltest mysqltest_1 Y Y Y Y Y Y N Y Y Y Y Y Y Y Y Y Y Y update db set db = 'MYSQLtest' where db = 'mysqltest' and user = 'mysqltest_1' and host = 'localhost'; flush privileges; show grants for mysqltest_1@localhost; @@ -15,8 +15,8 @@ Grants for mysqltest_1@localhost GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' GRANT ALL PRIVILEGES ON `mysqltest`.* TO 'mysqltest_1'@'localhost' select * from db where user = 'mysqltest_1'; -Host Db User Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Grant_priv References_priv Index_priv Alter_priv Create_tmp_table_priv Lock_tables_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Execute_priv -localhost MYSQLtest mysqltest_1 Y Y Y Y Y Y N Y Y Y Y Y Y Y Y Y Y +Host Db User Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Grant_priv References_priv Index_priv Alter_priv Create_tmp_table_priv Lock_tables_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Execute_priv Event_priv +localhost MYSQLtest mysqltest_1 Y Y Y Y Y Y N Y Y Y Y Y Y Y Y Y Y Y delete from db where db = 'MYSQLtest' and user = 'mysqltest_1' and host = 'localhost'; flush privileges; drop user mysqltest_1@localhost; diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result index 994c375da83..7928cbd916f 100644 --- a/mysql-test/r/ps.result +++ b/mysql-test/r/ps.result @@ -533,13 +533,13 @@ SET @aux= "SELECT COUNT(*) prepare my_stmt from @aux; execute my_stmt; COUNT(*) -37 +38 execute my_stmt; COUNT(*) -37 +38 execute my_stmt; COUNT(*) -37 +38 deallocate prepare my_stmt; drop procedure if exists p1| drop table if exists t1| diff --git a/mysql-test/r/system_mysql_db.result b/mysql-test/r/system_mysql_db.result index 706165fef8a..32f0dc8b68c 100644 --- a/mysql-test/r/system_mysql_db.result +++ b/mysql-test/r/system_mysql_db.result @@ -3,6 +3,7 @@ show tables; Tables_in_db columns_priv db +event func help_category help_keyword @@ -42,6 +43,7 @@ db CREATE TABLE `db` ( `Create_routine_priv` enum('N','Y') character set utf8 NOT NULL default 'N', `Alter_routine_priv` enum('N','Y') character set utf8 NOT NULL default 'N', `Execute_priv` enum('N','Y') character set utf8 NOT NULL default 'N', + `Event_priv` enum('N','Y') character set utf8 NOT NULL default 'N', PRIMARY KEY (`Host`,`Db`,`User`), KEY `User` (`User`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Database privileges' @@ -101,6 +103,7 @@ user CREATE TABLE `user` ( `Create_routine_priv` enum('N','Y') character set utf8 NOT NULL default 'N', `Alter_routine_priv` enum('N','Y') character set utf8 NOT NULL default 'N', `Create_user_priv` enum('N','Y') character set utf8 NOT NULL default 'N', + `Event_priv` enum('N','Y') character set utf8 NOT NULL default 'N', `ssl_type` enum('','ANY','X509','SPECIFIED') character set utf8 NOT NULL default '', `ssl_cipher` blob NOT NULL, `x509_issuer` blob NOT NULL, diff --git a/mysql-test/t/events.test b/mysql-test/t/events.test index bba34947ecd..44b115a1572 100644 --- a/mysql-test/t/events.test +++ b/mysql-test/t/events.test @@ -2,7 +2,7 @@ use test; drop event if exists event1; create event event1 on schedule every 15 minute starts now() ends date_add(now(), interval 5 hour) DO begin end; alter event event1 rename to event2; -alter event event2 disable; +alter event event2 disabled; drop event event2; create event event2 on schedule every 2 second starts now() ends date_add(now(), interval 5 hour) comment "some" DO begin end; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 1fec9fefb0d..14647c00307 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1298,8 +1298,12 @@ create: sp->init_strings(YYTHD, lex, $3); lex->sql_command= SQLCOM_CREATE_PROCEDURE; - /* Restore flag if it was cleared above */ - YYTHD->client_capabilities |= $3; + /* + Restore flag if it was cleared above + Be careful with counting. the block where we save the value + is $4. + */ + YYTHD->client_capabilities |= $4; sp->restore_thd_mem_root(YYTHD); } | CREATE