diff options
author | unknown <kostja@vajra.(none)> | 2007-04-05 15:49:46 +0400 |
---|---|---|
committer | unknown <kostja@vajra.(none)> | 2007-04-05 15:49:46 +0400 |
commit | a36054f4201568fac1717b451040ffa7925900a2 (patch) | |
tree | a91b4f984fd6317085b8bd7e94cd91134184aab6 /sql | |
parent | a0c4e184f80de8db3b9d1340715502454ee09ef6 (diff) | |
parent | fa1d637e896d71932adcd1451c1564f724590189 (diff) | |
download | mariadb-git-a36054f4201568fac1717b451040ffa7925900a2.tar.gz |
Merge bk-internal.mysql.com:/home/bk/mysql-5.1-runtime
into vajra.(none):/opt/local/work/mysql-5.1-c1
mysql-test/r/events_bugs.result:
Auto merged
mysql-test/r/events_scheduling.result:
Auto merged
mysql-test/t/events.test:
Auto merged
mysql-test/t/events_scheduling.test:
Auto merged
sql/event_data_objects.h:
Auto merged
sql/event_db_repository.h:
Auto merged
sql/mysqld.cc:
Auto merged
sql/set_var.cc:
Auto merged
sql/set_var.h:
Auto merged
sql/sql_parse.cc:
Auto merged
sql/sql_show.cc:
Auto merged
sql/table.h:
Auto merged
sql/share/errmsg.txt:
Auto merged
mysql-test/r/events.result:
e
Use local.
mysql-test/r/events_restart_phase1.result:
Use local
mysql-test/r/events_time_zone.result:
SCCS merged
mysql-test/t/events_restart_phase1.test:
Use local
sql/event_data_objects.cc:
Use local
sql/event_db_repository.cc:
Manual merge.
sql/event_queue.cc:
Manual merge.
sql/events.cc:
Manual merge.
Diffstat (limited to 'sql')
-rw-r--r-- | sql/event_data_objects.cc | 160 | ||||
-rw-r--r-- | sql/event_data_objects.h | 3 | ||||
-rw-r--r-- | sql/event_db_repository.cc | 654 | ||||
-rw-r--r-- | sql/event_db_repository.h | 28 | ||||
-rw-r--r-- | sql/event_queue.cc | 79 | ||||
-rw-r--r-- | sql/event_queue.h | 41 | ||||
-rw-r--r-- | sql/event_scheduler.cc | 99 | ||||
-rw-r--r-- | sql/event_scheduler.h | 25 | ||||
-rw-r--r-- | sql/events.cc | 956 | ||||
-rw-r--r-- | sql/events.h | 106 | ||||
-rw-r--r-- | sql/mysqld.cc | 46 | ||||
-rw-r--r-- | sql/set_var.cc | 65 | ||||
-rw-r--r-- | sql/set_var.h | 6 | ||||
-rw-r--r-- | sql/share/errmsg.txt | 4 | ||||
-rw-r--r-- | sql/sp_head.cc | 28 | ||||
-rw-r--r-- | sql/sql_db.cc | 2 | ||||
-rw-r--r-- | sql/sql_parse.cc | 53 | ||||
-rw-r--r-- | sql/sql_show.cc | 25 | ||||
-rw-r--r-- | sql/sql_test.cc | 2 | ||||
-rw-r--r-- | sql/table.cc | 245 | ||||
-rw-r--r-- | sql/table.h | 18 |
21 files changed, 1306 insertions, 1339 deletions
diff --git a/sql/event_data_objects.cc b/sql/event_data_objects.cc index e0b5ec23418..95bfba548de 100644 --- a/sql/event_data_objects.cc +++ b/sql/event_data_objects.cc @@ -20,8 +20,6 @@ #include "event_db_repository.h" #include "sp_head.h" -/* That's a provisional solution */ -extern Event_db_repository events_event_db_repository; #define EVEX_MAX_INTERVAL_VALUE 1000000000L @@ -101,8 +99,7 @@ Event_parse_data::new_instance(THD *thd) */ Event_parse_data::Event_parse_data() - :on_completion(Event_basic::ON_COMPLETION_DROP), - status(Event_basic::ENABLED), do_not_create(FALSE), + :on_completion(ON_COMPLETION_DROP), status(ENABLED), do_not_create(FALSE), item_starts(NULL), item_ends(NULL), item_execute_at(NULL), starts_null(TRUE), ends_null(TRUE), execute_at_null(TRUE), item_expression(NULL), expression(0) @@ -216,7 +213,7 @@ Event_parse_data::init_body(THD *thd) ++body_begin; --body.length; } - body.str= thd->strmake((char *)body_begin, body.length); + body.str= thd->strmake(body_begin, body.length); DBUG_VOID_RETURN; } @@ -244,7 +241,7 @@ Event_parse_data::check_if_in_the_past(THD *thd, my_time_t ltime_utc) if (ltime_utc >= (my_time_t) thd->query_start()) return; - if (on_completion == Event_basic::ON_COMPLETION_DROP) + if (on_completion == ON_COMPLETION_DROP) { switch (thd->lex->sql_command) { case SQLCOM_CREATE_EVENT: @@ -261,9 +258,9 @@ Event_parse_data::check_if_in_the_past(THD *thd, my_time_t ltime_utc) do_not_create= TRUE; } - else if (status == Event_basic::ENABLED) + else if (status == ENABLED) { - status= Event_basic::DISABLED; + status= DISABLED; push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_EVENT_EXEC_TIME_IN_THE_PAST, ER(ER_EVENT_EXEC_TIME_IN_THE_PAST)); @@ -589,9 +586,9 @@ Event_parse_data::check_parse_data(THD *thd) init_name(thd, identifier); init_definer(thd); + ret= init_execute_at(thd) || init_interval(thd) || init_starts(thd) || init_ends(thd); - check_originator_id(thd); DBUG_RETURN(ret); } @@ -639,31 +636,6 @@ Event_parse_data::init_definer(THD *thd) /* - Set the originator id of the event to the server_id if executing on - the master or set to the server_id of the master if executing on - the slave. If executing on slave, also set status to SLAVESIDE_DISABLED. - - SYNOPSIS - Event_parse_data::check_originator_id() -*/ -void Event_parse_data::check_originator_id(THD *thd) -{ - /* Disable replicated events on slave. */ - if ((thd->system_thread == SYSTEM_THREAD_SLAVE_SQL) || - (thd->system_thread == SYSTEM_THREAD_SLAVE_IO)) - { - DBUG_PRINT("info", ("Invoked object status set to SLAVESIDE_DISABLED.")); - if ((status == Event_basic::ENABLED) || - (status == Event_basic::DISABLED)) - status = Event_basic::SLAVESIDE_DISABLED; - originator = thd->server_id; - } - else - originator = server_id; -} - - -/* Constructor SYNOPSIS @@ -720,7 +692,7 @@ Event_basic::load_string_fields(Field **fields, ...) va_start(args, fields); field_name= (enum enum_events_table_field) va_arg(args, int); - while (field_name != ET_FIELD_COUNT) + while (field_name < ET_FIELD_COUNT) { field_value= va_arg(args, LEX_STRING *); if ((field_value->str= get_field(&mem_root, fields[field_name])) == NullS) @@ -884,13 +856,19 @@ Event_job_data::load_from_row(THD *thd, TABLE *table) if (!table) goto error; - if (table->s->fields != ET_FIELD_COUNT) + if (table->s->fields < ET_FIELD_COUNT) goto error; LEX_STRING tz_name; - load_string_fields(table->field, ET_FIELD_DB, &dbname, ET_FIELD_NAME, &name, - ET_FIELD_BODY, &body, ET_FIELD_DEFINER, &definer, - ET_FIELD_TIME_ZONE, &tz_name, ET_FIELD_COUNT); + if (load_string_fields(table->field, + ET_FIELD_DB, &dbname, + ET_FIELD_NAME, &name, + ET_FIELD_BODY, &body, + ET_FIELD_DEFINER, &definer, + ET_FIELD_TIME_ZONE, &tz_name, + ET_FIELD_COUNT)) + goto error; + if (load_time_zone(thd, tz_name)) goto error; @@ -936,19 +914,24 @@ Event_queue_element::load_from_row(THD *thd, TABLE *table) { char *ptr; TIME time; + LEX_STRING tz_name; DBUG_ENTER("Event_queue_element::load_from_row"); if (!table) goto error; - if (table->s->fields != ET_FIELD_COUNT) + if (table->s->fields < ET_FIELD_COUNT) + goto error; + + if (load_string_fields(table->field, + ET_FIELD_DB, &dbname, + ET_FIELD_NAME, &name, + ET_FIELD_DEFINER, &definer, + ET_FIELD_TIME_ZONE, &tz_name, + ET_FIELD_COUNT)) goto error; - LEX_STRING tz_name; - load_string_fields(table->field, ET_FIELD_DB, &dbname, ET_FIELD_NAME, &name, - ET_FIELD_DEFINER, &definer, - ET_FIELD_TIME_ZONE, &tz_name, ET_FIELD_COUNT); if (load_time_zone(thd, tz_name)) goto error; @@ -1021,23 +1004,8 @@ Event_queue_element::load_from_row(THD *thd, TABLE *table) goto error; DBUG_PRINT("load_from_row", ("Event [%s] is [%s]", name.str, ptr)); - - /* Set event status (ENABLED | SLAVESIDE_DISABLED | DISABLED) */ - switch (ptr[0]) - { - case 'E' : - status = Event_queue_element::ENABLED; - break; - case 'S' : - status = Event_queue_element::SLAVESIDE_DISABLED; - break; - case 'D' : - status = Event_queue_element::DISABLED; - break; - } - if ((ptr= get_field(&mem_root, table->field[ET_FIELD_ORIGINATOR])) == NullS) - goto error; - originator = table->field[ET_FIELD_ORIGINATOR]->val_int(); + status= (ptr[0]=='E'? Event_queue_element::ENABLED: + Event_queue_element::DISABLED); /* ToDo : Andrey . Find a way not to allocate ptr on event_mem_root */ if ((ptr= get_field(&mem_root, @@ -1080,7 +1048,11 @@ Event_timed::load_from_row(THD *thd, TABLE *table) if (Event_queue_element::load_from_row(thd, table)) goto error; - load_string_fields(table->field, ET_FIELD_BODY, &body, ET_FIELD_COUNT); + if (load_string_fields(table->field, + ET_FIELD_BODY, &body, + ET_FIELD_COUNT)) + goto error; + ptr= strchr(definer.str, '@'); @@ -1384,7 +1356,7 @@ Event_queue_element::compute_next_execution_time() (long) starts, (long) ends, (long) last_executed, (long) this)); - if (status != Event_queue_element::ENABLED) + if (status == Event_queue_element::DISABLED) { DBUG_PRINT("compute_next_execution_time", ("Event %s is DISABLED", name.str)); @@ -1635,10 +1607,8 @@ Event_queue_element::mark_last_executed(THD *thd) bool Event_queue_element::update_timing_fields(THD *thd) { - TABLE *table; - Field **fields; - Open_tables_state backup; - int ret= FALSE; + Event_db_repository *db_repository= Events::get_db_repository(); + int ret; DBUG_ENTER("Event_queue_element::update_timing_fields"); @@ -1648,53 +1618,13 @@ Event_queue_element::update_timing_fields(THD *thd) if (!(status_changed || last_executed_changed)) DBUG_RETURN(0); - thd->reset_n_backup_open_tables_state(&backup); - - if (events_event_db_repository.open_event_table(thd, TL_WRITE, &table)) - { - ret= TRUE; - goto done; - } - fields= table->field; - if ((ret= events_event_db_repository. - find_named_event(thd, dbname, name, table))) - goto done; - - store_record(table,record[1]); - /* Don't update create on row update. */ - table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; - - if (last_executed_changed) - { - TIME time; - my_tz_UTC->gmt_sec_to_TIME(&time, last_executed); - - fields[ET_FIELD_LAST_EXECUTED]->set_notnull(); - fields[ET_FIELD_LAST_EXECUTED]->store_time(&time, - MYSQL_TIMESTAMP_DATETIME); - last_executed_changed= FALSE; - } - if (status_changed) - { - fields[ET_FIELD_STATUS]->set_notnull(); - fields[ET_FIELD_STATUS]->store((longlong)status, TRUE); - status_changed= FALSE; - } - - /* - Turn off row binlogging of event timing updates. These are not used - for RBR of events replicated to the slave. - */ - if (thd->current_stmt_binlog_row_based) - thd->clear_current_stmt_binlog_row_based(); - - if ((table->file->ha_update_row(table->record[1], table->record[0]))) - ret= TRUE; - -done: - close_thread_tables(thd); - thd->restore_backup_open_tables_state(&backup); - + ret= db_repository->update_timing_fields_for_event(thd, + dbname, name, + last_executed_changed, + last_executed, + status_changed, + (ulonglong) status); + last_executed_changed= status_changed= FALSE; DBUG_RETURN(ret); } @@ -1778,8 +1708,6 @@ Event_timed::get_create_event(THD *thd, String *buf) if (status == Event_timed::ENABLED) buf->append(STRING_WITH_LEN("ENABLE")); - else if (status == Event_timed::SLAVESIDE_DISABLED) - buf->append(STRING_WITH_LEN("SLAVESIDE_DISABLE")); else buf->append(STRING_WITH_LEN("DISABLE")); diff --git a/sql/event_data_objects.h b/sql/event_data_objects.h index c0ae389f967..4d58484e5c2 100644 --- a/sql/event_data_objects.h +++ b/sql/event_data_objects.h @@ -18,7 +18,6 @@ #define EVEX_GET_FIELD_FAILED -2 #define EVEX_COMPILE_ERROR -3 -#define EVEX_GENERAL_ERROR -4 #define EVEX_BAD_PARAMS -5 #define EVEX_MICROSECOND_UNSUP -6 @@ -178,8 +177,6 @@ public: ulong sql_mode; - uint execution_count; - Event_job_data(); virtual ~Event_job_data(); diff --git a/sql/event_db_repository.cc b/sql/event_db_repository.cc index b6c9e4ea8e3..cc981c9cb69 100644 --- a/sql/event_db_repository.cc +++ b/sql/event_db_repository.cc @@ -18,12 +18,6 @@ #include "event_data_objects.h" #include "events.h" #include "sql_show.h" -#include "sp.h" -#include "sp_head.h" - - -static -time_t mysql_event_last_create_time= 0L; static const TABLE_FIELD_W_TYPE event_table_fields[ET_FIELD_COUNT] = @@ -132,26 +126,21 @@ const TABLE_FIELD_W_TYPE event_table_fields[ET_FIELD_COUNT] = }; -/* +/** Puts some data common to CREATE and ALTER EVENT into a row. - SYNOPSIS - mysql_event_fill_row() - thd THD - table The row to fill out - et Event's data - is_update CREATE EVENT or ALTER EVENT + Used both when an event is created and when it is altered. - RETURN VALUE - 0 OK - EVEX_GENERAL_ERROR Bad data - EVEX_GET_FIELD_FAILED Field count does not match. table corrupted? + @param thd THD + @param table The row to fill out + @param et Event's data + @param is_update CREATE EVENT or ALTER EVENT - DESCRIPTION - Used both when an event is created and when it is altered. + @retval FALSE success + @retval TRUE error */ -static int +static bool mysql_event_fill_row(THD *thd, TABLE *table, Event_parse_data *et, my_bool is_update) { @@ -165,6 +154,17 @@ mysql_event_fill_row(THD *thd, TABLE *table, Event_parse_data *et, DBUG_PRINT("info", ("name =[%s]", et->name.str)); DBUG_PRINT("info", ("body =[%s]", et->body.str)); + if (table->s->fields < ET_FIELD_COUNT) + { + /* + Safety: this can only happen if someone started the server + and then altered mysql.event. + */ + my_error(ER_COL_COUNT_DOESNT_MATCH_CORRUPTED, MYF(0), table->alias, + (int) ET_FIELD_COUNT, table->s->fields); + DBUG_RETURN(TRUE); + } + if (fields[f_num= ET_FIELD_DEFINER]-> store(et->definer.str, et->definer.length, scs)) goto err_truncate; @@ -271,11 +271,11 @@ mysql_event_fill_row(THD *thd, TABLE *table, Event_parse_data *et, goto err_truncate; } - DBUG_RETURN(0); + DBUG_RETURN(FALSE); err_truncate: my_error(ER_EVENT_DATA_TOO_LONG, MYF(0), fields[f_num]->field_name); - DBUG_RETURN(EVEX_GENERAL_ERROR); + DBUG_RETURN(TRUE); } @@ -389,40 +389,33 @@ Event_db_repository::table_scan_all_for_i_s(THD *thd, TABLE *schema_table, } -/* +/** Fills I_S.EVENTS with data loaded from mysql.event. Also used by SHOW EVENTS - SYNOPSIS - Event_db_repository::fill_schema_events() - thd Thread - tables The schema table - db If not NULL then get events only from this schema + The reason we reset and backup open tables here is that this + function may be called from any query that accesses + INFORMATION_SCHEMA - including a query that is issued from + a pre-locked statement, one that already has open and locked + tables. - RETURN VALUE - FALSE OK - TRUE Error + @retval FALSE success + @retval TRUE error */ -int +bool Event_db_repository::fill_schema_events(THD *thd, TABLE_LIST *tables, const char *db) { TABLE *schema_table= tables->table; TABLE *event_table= NULL; - Open_tables_state backup; int ret= 0; DBUG_ENTER("Event_db_repository::fill_schema_events"); DBUG_PRINT("info",("db=%s", db? db:"(null)")); - thd->reset_n_backup_open_tables_state(&backup); if (open_event_table(thd, TL_READ, &event_table)) - { - sql_print_error("Table mysql.event is damaged."); - thd->restore_backup_open_tables_state(&backup); - DBUG_RETURN(1); - } + DBUG_RETURN(TRUE); /* 1. SELECT I_S => use table scan. I_S.EVENTS does not guarantee order @@ -439,163 +432,100 @@ Event_db_repository::fill_schema_events(THD *thd, TABLE_LIST *tables, ret= table_scan_all_for_i_s(thd, schema_table, event_table); close_thread_tables(thd); - thd->restore_backup_open_tables_state(&backup); DBUG_PRINT("info", ("Return code=%d", ret)); DBUG_RETURN(ret); } -/* - Open mysql.event table for read +/** + Open mysql.event table for read. - SYNOPSIS - Events::open_event_table() - thd [in] Thread context - lock_type [in] How to lock the table - table [out] We will store the open table here + It's assumed that the caller knows what they are doing: + - whether it was necessary to reset-and-backup the open tables state + - whether the requested lock does not lead to a deadlock + - whether this open mode would work under LOCK TABLES, or inside a + stored function or trigger. - RETURN VALUE - 1 Cannot lock table - 2 The table is corrupted - different number of fields - 0 OK + @param[in] thd Thread context + @param[in] lock_type How to lock the table + @param[out] table We will store the open table here + + @retval TRUE open and lock failed - an error message is pushed into the + stack + @retval FALSE success */ -int +bool Event_db_repository::open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table) { TABLE_LIST tables; DBUG_ENTER("Event_db_repository::open_event_table"); - bzero((char*) &tables, sizeof(tables)); - tables.db= (char*) "mysql"; - tables.table_name= tables.alias= (char*) "event"; - tables.lock_type= lock_type; + tables.init_one_table("mysql", "event", lock_type); if (simple_open_n_lock_tables(thd, &tables)) - DBUG_RETURN(1); + DBUG_RETURN(TRUE); - if (table_check_intact(tables.table, ET_FIELD_COUNT, - event_table_fields, - &mysql_event_last_create_time, - ER_CANNOT_LOAD_FROM_TABLE)) - { - close_thread_tables(thd); - DBUG_RETURN(2); - } *table= tables.table; tables.table->use_all_columns(); - DBUG_RETURN(0); + DBUG_RETURN(FALSE); } -/* - Checks parameters which we got from the parsing phase. - - SYNOPSIS - check_parse_params() - thd Thread context - parse_data Event's data - - RETURN VALUE - FALSE OK - TRUE Error (reported) -*/ - -static int -check_parse_params(THD *thd, Event_parse_data *parse_data) -{ - DBUG_ENTER("check_parse_params"); - - if (parse_data->check_parse_data(thd)) - DBUG_RETURN(EVEX_BAD_PARAMS); - - if (!parse_data->dbname.str || - (thd->lex->sql_command == SQLCOM_ALTER_EVENT && thd->lex->spname && - !thd->lex->spname->m_db.str)) - { - my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0)); - DBUG_RETURN(EVEX_BAD_PARAMS); - } +/** + Creates an event record in mysql.event table. - if (check_access(thd, EVENT_ACL, parse_data->dbname.str, 0, 0, 0, - is_schema_db(parse_data->dbname.str)) || - (thd->lex->sql_command == SQLCOM_ALTER_EVENT && thd->lex->spname && - (check_access(thd, EVENT_ACL, thd->lex->spname->m_db.str, 0, 0, 0, - is_schema_db(thd->lex->spname->m_db.str))))) - DBUG_RETURN(EVEX_BAD_PARAMS); + Creates an event. Relies on mysql_event_fill_row which is shared with + ::update_event. - DBUG_RETURN(0); -} - - -/* - Creates an event in mysql.event - - SYNOPSIS - Event_db_repository::create_event() - thd [in] THD - parse_data [in] Object containing info about the event - create_if_not [in] Whether to generate anwarning in case event exists + @pre All semantic checks must be performed outside. This function + only creates a record on disk. + @pre The thread handle has no open tables. - RETURN VALUE - 0 OK - EVEX_GENERAL_ERROR Failure + @param[in,out] THD + @param[in] parse_data Parsed event definition + @param[in] create_if_not TRUE if IF NOT EXISTS clause was provided + to CREATE EVENT statement - DESCRIPTION - Creates an event. Relies on mysql_event_fill_row which is shared with - ::update_event. The name of the event is inside "et". + @retval FALSE success + @retval TRUE error */ bool Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data, my_bool create_if_not) { - int ret= 0; + int ret= 1; TABLE *table= NULL; - char old_db_buf[NAME_LEN+1]; - LEX_STRING old_db= { old_db_buf, sizeof(old_db_buf) }; - bool dbchanged= FALSE; DBUG_ENTER("Event_db_repository::create_event"); - if (check_parse_params(thd, parse_data)) - goto err; - if (parse_data->do_not_create) - goto ok; - DBUG_PRINT("info", ("open mysql.event for update")); + if (open_event_table(thd, TL_WRITE, &table)) - { - my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0)); - goto err; - } + goto end; DBUG_PRINT("info", ("name: %.*s", parse_data->name.length, parse_data->name.str)); DBUG_PRINT("info", ("check existance of an event with the same name")); - if (!find_named_event(thd, parse_data->dbname, parse_data->name, table)) + if (!find_named_event(parse_data->dbname, parse_data->name, table)) { if (create_if_not) { push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_EVENT_ALREADY_EXISTS, ER(ER_EVENT_ALREADY_EXISTS), parse_data->name.str); - goto ok; + ret= 0; } - my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), parse_data->name.str); - goto err; + else + my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), parse_data->name.str); + goto end; } - DBUG_PRINT("info", ("non-existant, go forward")); - - if ((ret= sp_use_new_db(thd, parse_data->dbname, &old_db, 0, &dbchanged))) - { - my_error(ER_BAD_DB_ERROR, MYF(0)); - goto err; - } + DBUG_PRINT("info", ("non-existent, go forward")); restore_record(table, s->default_values); // Get default values for fields @@ -605,7 +535,7 @@ Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data, table->field[ET_FIELD_DB]->char_length()) { my_error(ER_TOO_LONG_IDENT, MYF(0), parse_data->dbname.str); - goto err; + goto end; } if (system_charset_info->cset-> @@ -614,20 +544,13 @@ Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data, table->field[ET_FIELD_NAME]->char_length()) { my_error(ER_TOO_LONG_IDENT, MYF(0), parse_data->name.str); - goto err; + goto end; } if (parse_data->body.length > table->field[ET_FIELD_BODY]->field_length) { my_error(ER_TOO_LONG_BODY, MYF(0), parse_data->name.str); - goto err; - } - - if (!(parse_data->expression) && !(parse_data->execute_at)) - { - DBUG_PRINT("error", ("neither expression nor execute_at are set!")); - my_error(ER_EVENT_NEITHER_M_EXPR_NOR_M_AT, MYF(0)); - goto err; + goto end; } ((Field_timestamp *)table->field[ET_FIELD_CREATED])->set_time(); @@ -636,95 +559,71 @@ Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data, mysql_event_fill_row() calls my_error() in case of error so no need to handle it here */ - if ((ret= mysql_event_fill_row(thd, table, parse_data, FALSE))) - goto err; + if (mysql_event_fill_row(thd, table, parse_data, FALSE)) + goto end; table->field[ET_FIELD_STATUS]->store((longlong)parse_data->status, TRUE); - /* Close active transaction only if We are going to modify disk */ - if (end_active_trans(thd)) - goto err; - - if (table->file->ha_write_row(table->record[0])) + if ((ret= table->file->ha_write_row(table->record[0]))) { - my_error(ER_EVENT_STORE_FAILED, MYF(0), parse_data->name.str, ret); - goto err; + table->file->print_error(ret, MYF(0)); + goto end; } + ret= 0; -ok: - if (dbchanged) - (void) mysql_change_db(thd, old_db.str, 1); - /* - This statement may cause a spooky valgrind warning at startup - inside init_key_cache on my system (ahristov, 2006/08/10) - */ - close_thread_tables(thd); - DBUG_RETURN(FALSE); - -err: - if (dbchanged) - (void) mysql_change_db(thd, old_db.str, 1); +end: if (table) close_thread_tables(thd); - DBUG_RETURN(TRUE); + DBUG_RETURN(test(ret)); } -/* +/** Used to execute ALTER EVENT. Pendant to Events::update_event(). - SYNOPSIS - Event_db_repository::update_event() - thd THD - sp_name the name of the event to alter - et event's data + @param[in,out] thd thread handle + @param[in] parse_data parsed event definition + @paran[in[ new_dbname not NULL if ALTER EVENT RENAME + points at a new database name + @param[in] new_name not NULL if ALTER EVENT RENAME + points at a new event name - RETURN VALUE - FALSE OK - TRUE Error (reported) + @pre All semantic checks are performed outside this function, + it only updates the event definition on disk. + @pre We don't have any tables open in the given thread. - NOTES - sp_name is passed since this is the name of the event to - alter in case of RENAME TO. + @retval FALSE success + @retval TRUE error (reported) */ bool Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data, - LEX_STRING *new_dbname, LEX_STRING *new_name) + LEX_STRING *new_dbname, + LEX_STRING *new_name) { CHARSET_INFO *scs= system_charset_info; TABLE *table= NULL; + int ret= 1; DBUG_ENTER("Event_db_repository::update_event"); - if (open_event_table(thd, TL_WRITE, &table)) - { - my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0)); - goto err; - } + /* None or both must be set */ + DBUG_ASSERT(new_dbname && new_name || new_dbname == new_name); - if (check_parse_params(thd, parse_data) || parse_data->do_not_create) - goto err; + if (open_event_table(thd, TL_WRITE, &table)) + goto end; DBUG_PRINT("info", ("dbname: %s", parse_data->dbname.str)); DBUG_PRINT("info", ("name: %s", parse_data->name.str)); DBUG_PRINT("info", ("user: %s", parse_data->definer.str)); - if (new_dbname) - DBUG_PRINT("info", ("rename to: %s@%s", new_dbname->str, new_name->str)); /* first look whether we overwrite */ if (new_name) { - if (!sortcmp_lex_string(parse_data->name, *new_name, scs) && - !sortcmp_lex_string(parse_data->dbname, *new_dbname, scs)) - { - my_error(ER_EVENT_SAME_NAME, MYF(0), parse_data->name.str); - goto err; - } - - if (!find_named_event(thd, *new_dbname, *new_name, table)) + DBUG_PRINT("info", ("rename to: %s@%s", new_dbname->str, new_name->str)); + if (!find_named_event(*new_dbname, *new_name, table)) { my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), new_name->str); - goto err; + goto end; } } /* @@ -733,10 +632,10 @@ Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data, overwrite the key and SE will tell us that it cannot find the already found row (copied into record[1] later */ - if (find_named_event(thd, parse_data->dbname, parse_data->name, table)) + if (find_named_event(parse_data->dbname, parse_data->name, table)) { my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), parse_data->name.str); - goto err; + goto end; } store_record(table,record[1]); @@ -749,7 +648,7 @@ Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data, handle it here */ if (mysql_event_fill_row(thd, table, parse_data, TRUE)) - goto err; + goto end; if (new_dbname) { @@ -757,42 +656,32 @@ Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data, table->field[ET_FIELD_NAME]->store(new_name->str, new_name->length, scs); } - /* Close active transaction only if We are going to modify disk */ - if (end_active_trans(thd)) - goto err; - - int res; - if ((res= table->file->ha_update_row(table->record[1], table->record[0]))) + if ((ret= table->file->ha_update_row(table->record[1], table->record[0]))) { - my_error(ER_EVENT_STORE_FAILED, MYF(0), parse_data->name.str, res); - goto err; + table->file->print_error(ret, MYF(0)); + goto end; } + ret= 0; - /* close mysql.event or we crash later when loading the event from disk */ - close_thread_tables(thd); - DBUG_RETURN(FALSE); - -err: +end: if (table) close_thread_tables(thd); - DBUG_RETURN(TRUE); + DBUG_RETURN(test(ret)); } -/* - Drops an event +/** + Delete event record from mysql.event table. - SYNOPSIS - Event_db_repository::drop_event() - thd [in] THD - db [in] Database name - name [in] Event's name - drop_if_exists [in] If set and the event not existing => warning - onto the stack + @param[in,out] thd thread handle + @param[in] db Database name + @param[in] name Event name + @param[in] drop_if_exists DROP IF EXISTS clause was specified. + If set, and the event does not exist, + the error is downgraded to a warning. - RETURN VALUE - FALSE OK - TRUE Error (reported) + @retval FALSE success + @retval TRUE error (reported) */ bool @@ -800,66 +689,59 @@ Event_db_repository::drop_event(THD *thd, LEX_STRING db, LEX_STRING name, bool drop_if_exists) { TABLE *table= NULL; - Open_tables_state backup; - int ret; + int ret= 1; DBUG_ENTER("Event_db_repository::drop_event"); DBUG_PRINT("enter", ("%s@%s", db.str, name.str)); - thd->reset_n_backup_open_tables_state(&backup); - if ((ret= open_event_table(thd, TL_WRITE, &table))) - { - my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0)); - goto done; - } + if (open_event_table(thd, TL_WRITE, &table)) + goto end; - if (!(ret= find_named_event(thd, db, name, table))) + if (!find_named_event(db, name, table)) { - /* Close active transaction only if we are actually going to modify disk */ - if (!(ret= end_active_trans(thd)) && - (ret= table->file->ha_delete_row(table->record[0]))) - my_error(ER_EVENT_CANNOT_DELETE, MYF(0)); + if ((ret= table->file->ha_delete_row(table->record[0]))) + table->file->print_error(ret, MYF(0)); + goto end; } - else + + /* Event not found */ + if (!drop_if_exists) { - if (drop_if_exists) - { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, - ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST), - "Event", name.str); - ret= 0; - } else - my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name.str); + my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name.str); + goto end; } -done: + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST), + "Event", name.str); + ret= 0; + +end: if (table) close_thread_tables(thd); - thd->restore_backup_open_tables_state(&backup); - DBUG_RETURN(ret); + DBUG_RETURN(test(ret)); } -/* +/** Positions the internal pointer of `table` to the place where (db, name) is stored. - SYNOPSIS - Event_db_repository::find_named_event() - thd Thread - db Schema - name Event name - table Opened mysql.event + In case search succeeded, the table cursor points at the found row. - RETURN VALUE - FALSE OK - TRUE No such event + @param[in] db database name + @param[in] name event name + @param[in,out] table mysql.event table + + + @retval FALSE an event with such db/name key exists + @reval TRUE no record found or an error occured. */ bool -Event_db_repository::find_named_event(THD *thd, LEX_STRING db, LEX_STRING name, - TABLE *table) +Event_db_repository::find_named_event(LEX_STRING db, LEX_STRING name, + TABLE *table) { byte key[MAX_KEY_LENGTH]; DBUG_ENTER("Event_db_repository::find_named_event"); @@ -911,15 +793,15 @@ Event_db_repository::drop_schema_events(THD *thd, LEX_STRING schema) } -/* - Drops all events by field which has specific value of the field +/** + Drops all events which have a specific value of a field. - SYNOPSIS - Event_db_repository::drop_events_by_field() - thd Thread - table mysql.event TABLE - field Which field of the row to use for matching - field_value The value that should match + @pre The thread handle has no open tables. + + @param[in,out] thd Thread + @param[in,out] table mysql.event TABLE + @param[in] field Which field of the row to use for matching + @param[in] field_value The value that should match */ void @@ -934,16 +816,7 @@ Event_db_repository::drop_events_by_field(THD *thd, DBUG_PRINT("enter", ("field=%d field_value=%s", field, field_value.str)); if (open_event_table(thd, TL_WRITE, &table)) - { - /* - Currently being used only for DROP DATABASE - In this case we don't need - error message since the OK packet has been sent. But for DROP USER we - could need it. - - my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0)); - */ DBUG_VOID_RETURN; - } /* only enabled events are in memory, so we go now and delete the rest */ init_read_record(&read_record_info, thd, table, NULL, 1, 0); @@ -951,14 +824,20 @@ Event_db_repository::drop_events_by_field(THD *thd, { char *et_field= get_field(thd->mem_root, table->field[field]); - LEX_STRING et_field_lex= { et_field, strlen(et_field) }; - DBUG_PRINT("info", ("Current event %s name=%s", et_field, - get_field(thd->mem_root, table->field[ET_FIELD_NAME]))); - - if (!sortcmp_lex_string(et_field_lex, field_value, system_charset_info)) + /* et_field may be NULL if the table is corrupted or out of memory */ + if (et_field) { - DBUG_PRINT("info", ("Dropping")); - ret= table->file->ha_delete_row(table->record[0]); + LEX_STRING et_field_lex= { et_field, strlen(et_field) }; + DBUG_PRINT("info", ("Current event %s name=%s", et_field, + get_field(thd->mem_root, + table->field[ET_FIELD_NAME]))); + + if (!sortcmp_lex_string(et_field_lex, field_value, system_charset_info)) + { + DBUG_PRINT("info", ("Dropping")); + if ((ret= table->file->ha_delete_row(table->record[0]))) + table->file->print_error(ret, MYF(0)); + } } } end_read_record(&read_record_info); @@ -968,20 +847,14 @@ Event_db_repository::drop_events_by_field(THD *thd, } -/* +/** Looks for a named event in mysql.event and then loads it from - the table, compiles and inserts it into the cache. + the table. - SYNOPSIS - Event_db_repository::load_named_event() - thd [in] Thread context - dbname [in] Event's db name - name [in] Event's name - etn [out] The loaded event + @pre The given thread does not have open tables. - RETURN VALUE - FALSE OK - TRUE Error (reported) + @retval FALSE success + @retval TRUE error */ bool @@ -989,26 +862,169 @@ Event_db_repository::load_named_event(THD *thd, LEX_STRING dbname, LEX_STRING name, Event_basic *etn) { TABLE *table= NULL; - int ret= 0; - Open_tables_state backup; + bool ret; DBUG_ENTER("Event_db_repository::load_named_event"); DBUG_PRINT("enter",("thd: 0x%lx name: %*s", (long) thd, name.length, name.str)); - thd->reset_n_backup_open_tables_state(&backup); + if (!(ret= open_event_table(thd, TL_READ, &table))) + { + if ((ret= find_named_event(dbname, name, table))) + my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name.str); + else if ((ret= etn->load_from_row(thd, table))) + my_error(ER_CANNOT_LOAD_FROM_TABLE, MYF(0), "event"); - if ((ret= open_event_table(thd, TL_READ, &table))) - my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0)); - else if ((ret= find_named_event(thd, dbname, name, table))) - my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name.str); - else if ((ret= etn->load_from_row(thd, table))) - my_error(ER_CANNOT_LOAD_FROM_TABLE, MYF(0), "event"); + close_thread_tables(thd); + } + + + DBUG_RETURN(ret); +} + + +/** + Update the event record in mysql.event table with a changed status + and/or last execution time. + + @pre The thread handle does not have open tables. +*/ + +bool +Event_db_repository:: +update_timing_fields_for_event(THD *thd, + LEX_STRING event_db_name, + LEX_STRING event_name, + bool update_last_executed, + my_time_t last_executed, + bool update_status, + ulonglong status) +{ + TABLE *table= NULL; + Field **fields; + int ret= 1; + + DBUG_ENTER("Event_db_repository::update_timing_fields_for_event"); + + if (open_event_table(thd, TL_WRITE, &table)) + goto end; + + fields= table->field; + + if (find_named_event(event_db_name, event_name, table)) + goto end; + + store_record(table, record[1]); + /* Don't update create on row update. */ + table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; + + if (update_last_executed) + { + TIME time; + my_tz_UTC->gmt_sec_to_TIME(&time, last_executed); + fields[ET_FIELD_LAST_EXECUTED]->set_notnull(); + fields[ET_FIELD_LAST_EXECUTED]->store_time(&time, + MYSQL_TIMESTAMP_DATETIME); + } + if (update_status) + { + fields[ET_FIELD_STATUS]->set_notnull(); + fields[ET_FIELD_STATUS]->store(status, TRUE); + } + + if ((ret= table->file->ha_update_row(table->record[1], table->record[0]))) + { + table->file->print_error(ret, MYF(0)); + goto end; + } + + ret= 0; + +end: if (table) close_thread_tables(thd); - thd->restore_backup_open_tables_state(&backup); - /* In this case no memory was allocated so we don't need to clean */ + DBUG_RETURN(test(ret)); +} - DBUG_RETURN(ret); + +/** + Open mysql.db, mysql.user and mysql.event and check whether: + - mysql.db exists and is up to date (or from a newer version of MySQL), + - mysql.user has column Event_priv at an expected position, + - mysql.event exists and is up to date (or from a newer version of + MySQL) + + This function is called only when the server is started. + @pre The passed in thread handle has no open tables. + + @retval FALSE OK + @retval TRUE Error, an error message is output to the error log. +*/ + +bool +Event_db_repository::check_system_tables(THD *thd) +{ + TABLE_LIST tables; + int ret= FALSE; + const unsigned int event_priv_column_position= 29; + + DBUG_ENTER("Event_db_repository::check_system_tables"); + DBUG_PRINT("enter", ("thd: 0x%lx", (long) thd)); + + + /* Check mysql.db */ + tables.init_one_table("mysql", "db", TL_READ); + + if (simple_open_n_lock_tables(thd, &tables)) + { + ret= 1; + sql_print_error("Cannot open mysql.db"); + } + else + { + if (table_check_intact(tables.table, MYSQL_DB_FIELD_COUNT, + mysql_db_table_fields)) + ret= 1; + /* in case of an error, the message is printed inside table_check_intact */ + + close_thread_tables(thd); + } + /* Check mysql.user */ + tables.init_one_table("mysql", "user", TL_READ); + + if (simple_open_n_lock_tables(thd, &tables)) + { + ret= 1; + sql_print_error("Cannot open mysql.user"); + } + else + { + if (tables.table->s->fields < event_priv_column_position || + strncmp(tables.table->field[event_priv_column_position]->field_name, + STRING_WITH_LEN("Event_priv"))) + { + sql_print_error("mysql.user has no `Event_priv` column at position %d", + event_priv_column_position); + ret= 1; + } + close_thread_tables(thd); + } + /* Check mysql.event */ + tables.init_one_table("mysql", "event", TL_READ); + + if (simple_open_n_lock_tables(thd, &tables)) + { + ret= 1; + sql_print_error("Cannot open mysql.event"); + } + else + { + if (table_check_intact(tables.table, ET_FIELD_COUNT, event_table_fields)) + ret= 1; + /* in case of an error, the message is printed inside table_check_intact */ + close_thread_tables(thd); + } + + DBUG_RETURN(test(ret)); } diff --git a/sql/event_db_repository.h b/sql/event_db_repository.h index 10a10d7957a..64e19854933 100644 --- a/sql/event_db_repository.h +++ b/sql/event_db_repository.h @@ -15,7 +15,12 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#define EVEX_OPEN_TABLE_FAILED -1 +/* + @file + This is a private header file of Events module. Please do not include it + directly. All public declarations of Events module should be stored in + events.h and event_data_objects.h. +*/ enum enum_events_table_field { @@ -70,17 +75,28 @@ public: drop_schema_events(THD *thd, LEX_STRING schema); bool - find_named_event(THD *thd, LEX_STRING db, LEX_STRING name, TABLE *table); + find_named_event(LEX_STRING db, LEX_STRING name, TABLE *table); bool load_named_event(THD *thd, LEX_STRING dbname, LEX_STRING name, Event_basic *et); - int + bool open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table); - int + bool fill_schema_events(THD *thd, TABLE_LIST *tables, const char *db); + bool + update_timing_fields_for_event(THD *thd, + LEX_STRING event_db_name, + LEX_STRING event_name, + bool update_last_executed, + my_time_t last_executed, + bool update_status, + ulonglong status); +public: + static bool + check_system_tables(THD *thd); private: void drop_events_by_field(THD *thd, enum enum_events_table_field field, @@ -92,9 +108,7 @@ private: bool table_scan_all_for_i_s(THD *thd, TABLE *schema_table, TABLE *event_table); - static bool - check_system_tables(THD *thd); - +private: /* Prevent use of these */ Event_db_repository(const Event_db_repository &); void operator=(Event_db_repository &); diff --git a/sql/event_queue.cc b/sql/event_queue.cc index f958102e269..9b14d3cda7e 100644 --- a/sql/event_queue.cc +++ b/sql/event_queue.cc @@ -72,39 +72,21 @@ event_queue_element_compare_q(void *vptr, byte* a, byte *b) Event_queue::Event_queue() :mutex_last_unlocked_at_line(0), mutex_last_locked_at_line(0), mutex_last_attempted_lock_at_line(0), - mutex_queue_data_locked(FALSE), mutex_queue_data_attempting_lock(FALSE) + mutex_queue_data_locked(FALSE), + mutex_queue_data_attempting_lock(FALSE), + next_activation_at(0) { mutex_last_unlocked_in_func= mutex_last_locked_in_func= mutex_last_attempted_lock_in_func= ""; - next_activation_at= 0; -} - -/* - Inits mutexes. - - SYNOPSIS - Event_queue::init_mutexes() -*/ - -void -Event_queue::init_mutexes() -{ pthread_mutex_init(&LOCK_event_queue, MY_MUTEX_INIT_FAST); pthread_cond_init(&COND_queue_state, NULL); } -/* - Destroys mutexes. - - SYNOPSIS - Event_queue::deinit_mutexes() -*/ - -void -Event_queue::deinit_mutexes() +Event_queue::~Event_queue() { + deinit_queue(); pthread_mutex_destroy(&LOCK_event_queue); pthread_cond_destroy(&COND_queue_state); } @@ -176,34 +158,47 @@ Event_queue::deinit_queue() /** Adds an event to the queue. - SYNOPSIS - Event_queue::create_event() - dbname The schema of the new event - name The name of the new event + Compute the next execution time for an event, and if it is still + active, add it to the queue. Otherwise delete it. + The object is left intact in case of an error. Otherwise + the queue container assumes ownership of it. + + @param[in] thd thread handle + @param[in] new_element a new element to add to the queue + @param[out] created set to TRUE if no error and the element is + added to the queue, FALSE otherwise + + @retval TRUE an error occured. The value of created is undefined, + the element was not deleted. + @retval FALSE success */ -void -Event_queue::create_event(THD *thd, Event_queue_element *new_element) +bool +Event_queue::create_event(THD *thd, Event_queue_element *new_element, + bool *created) { DBUG_ENTER("Event_queue::create_event"); DBUG_PRINT("enter", ("thd: 0x%lx et=%s.%s", (long) thd, new_element->dbname.str, new_element->name.str)); - if ((new_element->status == Event_queue_element::DISABLED) - || (new_element->status == Event_queue_element::SLAVESIDE_DISABLED)) - delete new_element; - else + /* Will do nothing if the event is disabled */ + new_element->compute_next_execution_time(); + if (new_element->status != Event_queue_element::ENABLED) { - new_element->compute_next_execution_time(); - DBUG_PRINT("info", ("new event in the queue: 0x%lx", (long) new_element)); - - LOCK_QUEUE_DATA(); - queue_insert_safe(&queue, (byte *) new_element); - dbug_dump_queue(thd->query_start()); - pthread_cond_broadcast(&COND_queue_state); - UNLOCK_QUEUE_DATA(); + delete new_element; + *created= FALSE; + DBUG_RETURN(FALSE); } - DBUG_VOID_RETURN; + + DBUG_PRINT("info", ("new event in the queue: 0x%lx", (long) new_element)); + + LOCK_QUEUE_DATA(); + *created= (queue_insert_safe(&queue, (byte *) new_element) == FALSE); + dbug_dump_queue(thd->query_start()); + pthread_cond_broadcast(&COND_queue_state); + UNLOCK_QUEUE_DATA(); + + DBUG_RETURN(!*created); } diff --git a/sql/event_queue.h b/sql/event_queue.h index 95f52b7b588..04bb8b93b06 100644 --- a/sql/event_queue.h +++ b/sql/event_queue.h @@ -25,23 +25,16 @@ class Event_queue { public: Event_queue(); - - void - init_mutexes(); - - void - deinit_mutexes(); + ~Event_queue(); bool init_queue(THD *thd); - void - deinit_queue(); - /* Methods for queue management follow */ - void - create_event(THD *thd, Event_queue_element *new_element); + bool + create_event(THD *thd, Event_queue_element *new_element, + bool *created); void update_event(THD *thd, LEX_STRING dbname, LEX_STRING name, @@ -64,9 +57,23 @@ public: void dump_internal_status(); +private: void empty_queue(); -protected: + + void + deinit_queue(); + /* helper functions for working with mutexes & conditionals */ + void + lock_data(const char *func, uint line); + + void + unlock_data(const char *func, uint line); + + void + cond_wait(THD *thd, struct timespec *abstime, const char* msg, + const char *func, uint line); + void find_n_remove_event(LEX_STRING db, LEX_STRING name); @@ -98,16 +105,6 @@ protected: bool mutex_queue_data_attempting_lock; bool waiting_on_cond; - /* helper functions for working with mutexes & conditionals */ - void - lock_data(const char *func, uint line); - - void - unlock_data(const char *func, uint line); - - void - cond_wait(THD *thd, struct timespec *abstime, const char* msg, - const char *func, uint line); }; #endif /* _EVENT_QUEUE_H_ */ diff --git a/sql/event_scheduler.cc b/sql/event_scheduler.cc index d50ea932596..a9a93cbc74b 100644 --- a/sql/event_scheduler.cc +++ b/sql/event_scheduler.cc @@ -37,7 +37,6 @@ extern pthread_attr_t connection_attrib; Event_db_repository *Event_worker_thread::db_repository; -Events *Event_worker_thread::events_facade; static @@ -80,11 +79,11 @@ Event_worker_thread::print_warnings(THD *thd, Event_job_data *et) prefix.length(0); prefix.append("Event Scheduler: ["); - append_identifier(thd, &prefix, et->definer.str, et->definer.length); + prefix.append(et->definer.str, et->definer.length, system_charset_info); prefix.append("][", 2); - append_identifier(thd,&prefix, et->dbname.str, et->dbname.length); + prefix.append(et->dbname.str, et->dbname.length, system_charset_info); prefix.append('.'); - append_identifier(thd,&prefix, et->name.str, et->name.length); + prefix.append(et->name.str, et->name.length, system_charset_info); prefix.append("] ", 2); List_iterator_fast<MYSQL_ERROR> it(thd->warn_list); @@ -95,7 +94,6 @@ Event_worker_thread::print_warnings(THD *thd, Event_job_data *et) err_msg.length(0); err_msg.append(prefix); err_msg.append(err->msg, strlen(err->msg), system_charset_info); - err_msg.append("]"); DBUG_ASSERT(err->level < 3); (sql_print_message_handlers[err->level])("%*s", err_msg.length(), err_msg.c_ptr()); @@ -239,7 +237,7 @@ event_scheduler_thread(void *arg) } -/* +/** Function that executes an event in a child thread. Setups the environment for the event execution and cleans after that. @@ -266,7 +264,7 @@ event_worker_thread(void *arg) } -/* +/** Function that executes an event in a child thread. Setups the environment for the event execution and cleans after that. @@ -315,17 +313,27 @@ Event_worker_thread::run(THD *thd, Event_queue_element_for_exec *event) print_warnings(thd, job_data); - sql_print_information("Event Scheduler: " - "[%s.%s of %s] executed in thread %lu. " - "RetCode=%d", job_data->dbname.str, job_data->name.str, - job_data->definer.str, thd->thread_id, ret); - if (ret == EVEX_COMPILE_ERROR) + switch (ret) { + case 0: sql_print_information("Event Scheduler: " - "COMPILE ERROR for event %s.%s of %s", + "[%s].[%s.%s] executed successfully in thread %lu.", + job_data->definer.str, job_data->dbname.str, job_data->name.str, - job_data->definer.str); - else if (ret == EVEX_MICROSECOND_UNSUP) - sql_print_information("Event Scheduler: MICROSECOND is not supported"); + thd->thread_id); + break; + case EVEX_COMPILE_ERROR: + sql_print_information("Event Scheduler: " + "[%s].[%s.%s] event compilation failed.", + job_data->definer.str, + job_data->dbname.str, job_data->name.str); + break; + default: + sql_print_information("Event Scheduler: " + "[%s].[%s.%s] event execution failed.", + job_data->definer.str, + job_data->dbname.str, job_data->name.str); + break; + } end: delete job_data; @@ -349,66 +357,34 @@ end: problem. However, this comes at the price of introduction bi-directional association between class Events and class Event_worker_thread. */ - events_facade->drop_event(thd, event->dbname, event->name, FALSE); + Events::drop_event(thd, event->dbname, event->name, FALSE); } DBUG_PRINT("info", ("Done with Event %s.%s", event->dbname.str, event->name.str)); delete event; deinit_event_thread(thd); - pthread_exit(0); -} - - -/* - Performs initialization of the scheduler data, outside of the - threading primitives. - - SYNOPSIS - Event_scheduler::init_scheduler() -*/ - -void -Event_scheduler::init_scheduler(Event_queue *q) -{ - LOCK_DATA(); - queue= q; - started_events= 0; - scheduler_thd= NULL; - state= INITIALIZED; - UNLOCK_DATA(); + /* + Do not pthread_exit since we want local destructors for stack objects + to be invoked. + */ } -void -Event_scheduler::deinit_scheduler() {} - - -/* - Inits scheduler's threading primitives. - - SYNOPSIS - Event_scheduler::init_mutexes() -*/ - -void -Event_scheduler::init_mutexes() +Event_scheduler::Event_scheduler(Event_queue *queue_arg) + :state(UNINITIALIZED), + scheduler_thd(NULL), + queue(queue_arg), + started_events(0) { pthread_mutex_init(&LOCK_scheduler_state, MY_MUTEX_INIT_FAST); pthread_cond_init(&COND_state, NULL); } -/* - Deinits scheduler's threading primitives. - - SYNOPSIS - Event_scheduler::deinit_mutexes() -*/ - -void -Event_scheduler::deinit_mutexes() +Event_scheduler::~Event_scheduler() { + stop(); /* does nothing if not running */ pthread_mutex_destroy(&LOCK_scheduler_state); pthread_cond_destroy(&COND_state); } @@ -639,6 +615,9 @@ Event_scheduler::is_running() Stops the scheduler (again). Waits for acknowledgement from the scheduler that it has stopped - synchronous stopping. + Already running events will not be stopped. If the user needs + them stopped manual intervention is needed. + SYNOPSIS Event_scheduler::stop() diff --git a/sql/event_scheduler.h b/sql/event_scheduler.h index 74d53c4f63d..70635196745 100644 --- a/sql/event_scheduler.h +++ b/sql/event_scheduler.h @@ -15,7 +15,8 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -/* +/** + @file This file is internal to Events module. Please do not include it directly. All public declarations of Events module are in events.h and event_data_objects.h. @@ -41,10 +42,9 @@ class Event_worker_thread { public: static void - init(Events *events, Event_db_repository *db_repo) + init(Event_db_repository *db_repository_arg) { - db_repository= db_repo; - events_facade= events; + db_repository= db_repository_arg; } void @@ -55,15 +55,15 @@ private: print_warnings(THD *thd, Event_job_data *et); static Event_db_repository *db_repository; - static Events *events_facade; }; class Event_scheduler { public: - Event_scheduler():state(UNINITIALIZED){} - ~Event_scheduler(){} + Event_scheduler(Event_queue *event_queue_arg); + ~Event_scheduler(); + /* State changing methods follow */ @@ -80,17 +80,6 @@ public: bool run(THD *thd); - void - init_scheduler(Event_queue *queue); - - void - deinit_scheduler(); - - void - init_mutexes(); - - void - deinit_mutexes(); /* Information retrieving methods follow */ bool diff --git a/sql/events.cc b/sql/events.cc index 46111bcaa91..7c44116af55 100644 --- a/sql/events.cc +++ b/sql/events.cc @@ -19,7 +19,6 @@ #include "event_db_repository.h" #include "event_queue.h" #include "event_scheduler.h" -#include "sp_head.h" /* TODO list : @@ -66,7 +65,7 @@ static const char *opt_event_scheduler_state_names[]= { "OFF", "ON", "0", "1", "DISABLED", NullS }; -TYPELIB Events::opt_typelib= +const TYPELIB Events::opt_typelib= { array_elements(opt_event_scheduler_state_names)-1, "", @@ -82,7 +81,7 @@ TYPELIB Events::opt_typelib= */ static const char *var_event_scheduler_state_names[]= { "OFF", "ON", NullS }; -TYPELIB Events::var_typelib= +const TYPELIB Events::var_typelib= { array_elements(var_event_scheduler_state_names)-1, "", @@ -90,20 +89,13 @@ TYPELIB Events::var_typelib= NULL }; - -static -Event_queue events_event_queue; - -static -Event_scheduler events_event_scheduler; - - -Event_db_repository events_event_db_repository; - -Events Events::singleton; - -enum Events::enum_opt_event_scheduler Events::opt_event_scheduler= - Events::EVENTS_OFF; +Event_queue *Events::event_queue; +Event_scheduler *Events::scheduler; +Event_db_repository *Events::db_repository; +enum Events::enum_opt_event_scheduler +Events::opt_event_scheduler= Events::EVENTS_OFF; +pthread_mutex_t Events::LOCK_event_metadata; +bool Events::check_system_tables_error= FALSE; /* @@ -128,25 +120,89 @@ int sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs) } -/* - Accessor for the singleton instance. +/** + @brief Initialize the start up option of the Events scheduler. - SYNOPSIS - Events::get_instance() + Do not initialize the scheduler subsystem yet - the initialization + is split into steps as it has to fit into the common MySQL + initialization framework. + No locking as this is called only at start up. - RETURN VALUE - address + @param[in,out] argument The value of the argument. If this value + is found in the typelib, the argument is + updated. + + @retval TRUE unknown option value + @retval FALSE success */ -Events * -Events::get_instance() +bool +Events::set_opt_event_scheduler(char *argument) { - DBUG_ENTER("Events::get_instance"); - DBUG_RETURN(&singleton); + if (argument == NULL) + opt_event_scheduler= Events::EVENTS_DISABLED; + else + { + int type; + /* + type= 1 2 3 4 5 + (OFF | ON) - (0 | 1) (DISABLE ) + */ + const static enum enum_opt_event_scheduler type2state[]= + { EVENTS_OFF, EVENTS_ON, EVENTS_OFF, EVENTS_ON, EVENTS_DISABLED }; + + type= find_type(argument, &opt_typelib, 1); + + DBUG_ASSERT(type >= 0 && type <= 5); /* guaranteed by find_type */ + + if (type == 0) + { + fprintf(stderr, "Unknown option to event-scheduler: %s\n", argument); + return TRUE; + } + opt_event_scheduler= type2state[type-1]; + } + return FALSE; } -/* +/** + Return a string representation of the current scheduler mode. +*/ + +const char * +Events::get_opt_event_scheduler_str() +{ + const char *str; + + pthread_mutex_lock(&LOCK_event_metadata); + str= opt_typelib.type_names[(int) opt_event_scheduler]; + pthread_mutex_unlock(&LOCK_event_metadata); + + return str; +} + + +/** + Push an error into the error stack if the system tables are + not up to date. +*/ + +bool Events::check_if_system_tables_error() +{ + DBUG_ENTER("Events::check_if_system_tables_error"); + + if (check_system_tables_error) + { + my_error(ER_EVENTS_DB_ERROR, MYF(0)); + DBUG_RETURN(TRUE); + } + + DBUG_RETURN(FALSE); +} + + +/** Reconstructs interval expression from interval type and expression value that is in form of a value of the smalles entity: For @@ -278,52 +334,65 @@ common_1_lev_code: return 0; } -/* - Constructor of Events class. It's called when events.o - is loaded. Assigning addressed of static variables in this - object file. - SYNOPSIS - Events::Events() +/** + Create a new event. + + @param[in,out] thd THD + @param[in] parse_data Event's data from parsing stage + @param[in] if_not_exists Whether IF NOT EXISTS was + specified + In case there is an event with the same name (db) and + IF NOT EXISTS is specified, an warning is put into the stack. + @sa Events::drop_event for the notes about locking, pre-locking + and Events DDL. + + @retval FALSE OK + @retval TRUE Error (reported) */ -Events::Events() +bool +Events::create_event(THD *thd, Event_parse_data *parse_data, + bool if_not_exists) { - scheduler= &events_event_scheduler; - event_queue= &events_event_queue; - db_repository= &events_event_db_repository; -} + int ret; + DBUG_ENTER("Events::create_event"); + /* + Let's commit the transaction first - MySQL manual specifies + that a DDL issues an implicit commit, and it doesn't say "successful + DDL", so that an implicit commit is a property of any successfully + parsed DDL statement. + */ + if (end_active_trans(thd)) + DBUG_RETURN(TRUE); -/* - The function exported to the world for creating of events. + if (check_if_system_tables_error()) + DBUG_RETURN(TRUE); - SYNOPSIS - Events::create_event() - thd [in] THD - parse_data [in] Event's data from parsing stage - if_not_exists [in] Whether IF NOT EXISTS was specified in the DDL + /* + Perform semantic checks outside of Event_db_repository: + once CREATE EVENT is supported in prepared statements, the + checks will be moved to PREPARE phase. + */ + if (parse_data->check_parse_data(thd)) + DBUG_RETURN(TRUE); - RETURN VALUE - FALSE OK - TRUE Error (Reported) + /* At create, one of them must be set */ + DBUG_ASSERT(parse_data->expression || parse_data->execute_at); - NOTES - In case there is an event with the same name (db) and - IF NOT EXISTS is specified, an warning is put into the stack. -*/ + if (check_access(thd, EVENT_ACL, parse_data->dbname.str, 0, 0, 0, + is_schema_db(parse_data->dbname.str))) + DBUG_RETURN(TRUE); -bool -Events::create_event(THD *thd, Event_parse_data *parse_data, bool if_not_exists) -{ - int ret; - DBUG_ENTER("Events::create_event"); - if (unlikely(check_system_tables_error)) + if (check_db_dir_existence(parse_data->dbname.str)) { - my_error(ER_EVENTS_DB_ERROR, MYF(0)); + my_error(ER_BAD_DB_ERROR, MYF(0)); DBUG_RETURN(TRUE); } + if (parse_data->do_not_create) + DBUG_RETURN(FALSE); /* Turn off row binlogging of this statement and use statement-based so that all supporting tables are updated for CREATE EVENT command. @@ -334,8 +403,7 @@ Events::create_event(THD *thd, Event_parse_data *parse_data, bool if_not_exists) pthread_mutex_lock(&LOCK_event_metadata); /* On error conditions my_error() is called so no need to handle here */ - if (!(ret= db_repository->create_event(thd, parse_data, if_not_exists)) && - !parse_data->do_not_create) + if (!(ret= db_repository->create_event(thd, parse_data, if_not_exists))) { Event_queue_element *new_element; @@ -345,12 +413,17 @@ Events::create_event(THD *thd, Event_parse_data *parse_data, bool if_not_exists) parse_data->name, new_element))) { - DBUG_ASSERT(ret == OP_LOAD_ERROR); + db_repository->drop_event(thd, parse_data->dbname, parse_data->name, + TRUE); delete new_element; } - else /* Binlog the create event. */ + else { - event_queue->create_event(thd, new_element); + /* TODO: do not ignore the out parameter and a possible OOM error! */ + bool created; + if (event_queue) + event_queue->create_event(thd, new_element, &created); + /* Binlog the create event. */ if (mysql_bin_log.is_open() && (thd->query_length > 0)) { thd->clear_error(); @@ -365,37 +438,79 @@ Events::create_event(THD *thd, Event_parse_data *parse_data, bool if_not_exists) } -/* - The function exported to the world for alteration of events. - - SYNOPSIS - Events::update_event() - thd [in] THD - parse_data [in] Event's data from parsing stage - rename_to [in] Set in case of RENAME TO. - - RETURN VALUE - FALSE OK - TRUE Error - - NOTES - et contains data about dbname and event name. - new_name is the new name of the event, if not null this means - that RENAME TO was specified in the query +/** + Alter an event. + + @param[in,out] thd THD + @param[in] parse_data Event's data from parsing stage + @param[in] new_dbname A new schema name for the event. Set in the case of + ALTER EVENT RENAME, otherwise is NULL. + @param[in] new_name A new name for the event. Set in the case of + ALTER EVENT RENAME + + Parameter 'et' contains data about dbname and event name. + Parameter 'new_name' is the new name of the event, if not null + this means that RENAME TO was specified in the query + @sa Events::drop_event for the locking notes. + + @retval FALSE OK + @retval TRUE error (reported) */ bool -Events::update_event(THD *thd, Event_parse_data *parse_data, sp_name *rename_to) +Events::update_event(THD *thd, Event_parse_data *parse_data, + LEX_STRING *new_dbname, LEX_STRING *new_name) { int ret; Event_queue_element *new_element; + DBUG_ENTER("Events::update_event"); - LEX_STRING *new_dbname= rename_to ? &rename_to->m_db : NULL; - LEX_STRING *new_name= rename_to ? &rename_to->m_name : NULL; - if (unlikely(check_system_tables_error)) - { - my_error(ER_EVENTS_DB_ERROR, MYF(0)); + + /* + For consistency, implicit COMMIT should be the first thing in the + execution chain. + */ + if (end_active_trans(thd)) + DBUG_RETURN(TRUE); + + if (check_if_system_tables_error()) DBUG_RETURN(TRUE); + + if (parse_data->check_parse_data(thd) || parse_data->do_not_create) + DBUG_RETURN(TRUE); + + if (check_access(thd, EVENT_ACL, parse_data->dbname.str, 0, 0, 0, + is_schema_db(parse_data->dbname.str))) + DBUG_RETURN(TRUE); + + if (new_dbname) /* It's a rename */ + { + /* Check that the new and the old names differ. */ + if ( !sortcmp_lex_string(parse_data->dbname, *new_dbname, + system_charset_info) && + !sortcmp_lex_string(parse_data->name, *new_name, + system_charset_info)) + { + my_error(ER_EVENT_SAME_NAME, MYF(0), parse_data->name.str); + DBUG_RETURN(TRUE); + } + + /* + And the user has sufficient privileges to use the target database. + Do it before checking whether the database exists: we don't want + to tell the user that a database doesn't exist if they can not + access it. + */ + if (check_access(thd, EVENT_ACL, new_dbname->str, 0, 0, 0, + is_schema_db(new_dbname->str))) + DBUG_RETURN(TRUE); + + /* Check that the target database exists */ + if (check_db_dir_existence(new_dbname->str)) + { + my_error(ER_BAD_DB_ERROR, MYF(0)); + DBUG_RETURN(TRUE); + } } /* @@ -408,7 +523,8 @@ Events::update_event(THD *thd, Event_parse_data *parse_data, sp_name *rename_to) pthread_mutex_lock(&LOCK_event_metadata); /* On error conditions my_error() is called so no need to handle here */ - if (!(ret= db_repository->update_event(thd, parse_data, new_dbname, new_name))) + if (!(ret= db_repository->update_event(thd, parse_data, + new_dbname, new_name))) { LEX_STRING dbname= new_dbname ? *new_dbname : parse_data->dbname; LEX_STRING name= new_name ? *new_name : parse_data->name; @@ -421,10 +537,18 @@ Events::update_event(THD *thd, Event_parse_data *parse_data, sp_name *rename_to) DBUG_ASSERT(ret == OP_LOAD_ERROR); delete new_element; } - else /* Binlog the alter event. */ + else { - event_queue->update_event(thd, parse_data->dbname, parse_data->name, - new_element); + /* + TODO: check if an update actually has inserted an entry + into the queue. + If not, and the element is ON COMPLETION NOT PRESERVE, delete + it right away. + */ + if (event_queue) + event_queue->update_event(thd, parse_data->dbname, parse_data->name, + new_element); + /* Binlog the alter event. */ if (mysql_bin_log.is_open() && (thd->query_length > 0)) { thd->clear_error(); @@ -439,20 +563,28 @@ Events::update_event(THD *thd, Event_parse_data *parse_data, sp_name *rename_to) } -/* +/** Drops an event - SYNOPSIS - Events::drop_event() - thd [in] THD - dbname [in] Event's schema - name [in] Event's name - if_exists [in] When set and the event does not exist => - warning onto the stack - - RETURN VALUE - FALSE OK - TRUE Error (reported) + @param[in,out] thd THD + @param[in] dbname Event's schema + @param[in] name Event's name + @param[in] if_exists When this is set and the event does not exist + a warning is pushed into the warning stack. + Otherwise the operation produces an error. + + @note Similarly to DROP PROCEDURE, we do not allow DROP EVENT + under LOCK TABLES mode, unless table mysql.event is locked. To + ensure that, we do not reset & backup the open tables state in + this function - if in LOCK TABLES or pre-locking mode, this will + lead to an error 'Table mysql.event is not locked with LOCK + TABLES' unless it _is_ locked. In pre-locked mode there is + another barrier - DROP EVENT commits the current transaction, + and COMMIT/ROLLBACK is not allowed in stored functions and + triggers. + + @retval FALSE OK + @retval TRUE Error (reported) */ bool @@ -460,14 +592,30 @@ Events::drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name, bool if_exists) { int ret; DBUG_ENTER("Events::drop_event"); - if (unlikely(check_system_tables_error)) - { - my_error(ER_EVENTS_DB_ERROR, MYF(0)); + + /* + In MySQL, DDL must always commit: since mysql.* tables are + non-transactional, we must modify them outside a transaction + to not break atomicity. + But the second and more important reason to commit here + regardless whether we're actually changing mysql.event table + or not is replication: end_active_trans syncs the binary log, + and unless we run DDL in it's own transaction it may simply + never appear on the slave in case the outside transaction + rolls back. + */ + if (end_active_trans(thd)) DBUG_RETURN(TRUE); - } - /* - Turn off row binlogging of this statement and use statement-based so + if (check_if_system_tables_error()) + DBUG_RETURN(TRUE); + + if (check_access(thd, EVENT_ACL, dbname.str, 0, 0, 0, + is_schema_db(dbname.str))) + DBUG_RETURN(TRUE); + + /* + Turn off row binlogging of this statement and use statement-based so that all supporting tables are updated for DROP EVENT command. */ if (thd->current_stmt_binlog_row_based) @@ -477,7 +625,8 @@ Events::drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name, bool if_exists) /* On error conditions my_error() is called so no need to handle here */ if (!(ret= db_repository->drop_event(thd, dbname, name, if_exists))) { - event_queue->drop_event(thd, dbname, name); + if (event_queue) + event_queue->drop_event(thd, dbname, name); /* Binlog the drop event. */ if (mysql_bin_log.is_open() && (thd->query_length > 0)) { @@ -494,10 +643,12 @@ Events::drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name, bool if_exists) /** Drops all events from a schema - SYNOPSIS - Events::drop_schema_events() - thd Thread - db ASCIIZ schema name + @note We allow to drop all events in a schema even if the + scheduler is disabled. This is to not produce any warnings + in case of DROP DATABASE and a disabled scheduler. + + @param[in,out] thd Thread + @param[in] db ASCIIZ schema name */ void @@ -507,14 +658,15 @@ Events::drop_schema_events(THD *thd, char *db) DBUG_ENTER("Events::drop_schema_events"); DBUG_PRINT("enter", ("dropping events from %s", db)); - if (unlikely(check_system_tables_error)) - { - my_error(ER_EVENTS_DB_ERROR, MYF(0)); - DBUG_VOID_RETURN; - } + + /* + sic: no check if the scheduler is disabled or system tables + are damaged, as intended. + */ pthread_mutex_lock(&LOCK_event_metadata); - event_queue->drop_schema_events(thd, db_lex); + if (event_queue) + event_queue->drop_schema_events(thd, db_lex); db_repository->drop_schema_events(thd, db_lex); pthread_mutex_unlock(&LOCK_event_metadata); @@ -522,115 +674,137 @@ Events::drop_schema_events(THD *thd, char *db) } -/* - SHOW CREATE EVENT - - SYNOPSIS - Events::show_create_event() - thd Thread context - spn The name of the event (db, name) - - RETURN VALUE - FALSE OK - TRUE Error during writing to the wire +/** + A helper function to generate SHOW CREATE EVENT output from + a named event */ -bool -Events::show_create_event(THD *thd, LEX_STRING dbname, LEX_STRING name) +static bool +send_show_create_event(THD *thd, Event_timed *et, Protocol *protocol) { - CHARSET_INFO *scs= system_charset_info; - int ret; - Event_timed *et= new Event_timed(); + char show_str_buf[10 * STRING_BUFFER_USUAL_SIZE]; + String show_str(show_str_buf, sizeof(show_str_buf), system_charset_info); + List<Item> field_list; + LEX_STRING sql_mode; + const String *tz_name; - DBUG_ENTER("Events::show_create_event"); - DBUG_PRINT("enter", ("name: %s@%s", dbname.str, name.str)); - if (unlikely(check_system_tables_error)) - { - my_error(ER_EVENTS_DB_ERROR, MYF(0)); + DBUG_ENTER("send_show_create_event"); + + show_str.length(0); + if (et->get_create_event(thd, &show_str)) DBUG_RETURN(TRUE); - } - ret= db_repository->load_named_event(thd, dbname, name, et); + field_list.push_back(new Item_empty_string("Event", NAME_LEN)); - if (!ret) - { - Protocol *protocol= thd->protocol; - char show_str_buf[10 * STRING_BUFFER_USUAL_SIZE]; - String show_str(show_str_buf, sizeof(show_str_buf), scs); - List<Item> field_list; - byte *sql_mode_str; - ulong sql_mode_len=0; + if (sys_var_thd_sql_mode::symbolic_mode_representation(thd, et->sql_mode, + &sql_mode)) + DBUG_RETURN(TRUE); - show_str.length(0); - show_str.set_charset(system_charset_info); + field_list.push_back(new Item_empty_string("sql_mode", sql_mode.length)); - if (et->get_create_event(thd, &show_str)) - goto err; + tz_name= et->time_zone->get_name(); - field_list.push_back(new Item_empty_string("Event", NAME_LEN)); + field_list.push_back(new Item_empty_string("time_zone", + tz_name->length())); - sql_mode_str= - sys_var_thd_sql_mode::symbolic_mode_representation(thd, et->sql_mode, - &sql_mode_len); + field_list.push_back(new Item_empty_string("Create Event", + show_str.length())); - field_list.push_back(new Item_empty_string("sql_mode", sql_mode_len)); + if (protocol->send_fields(&field_list, + Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) + DBUG_RETURN(TRUE); - const String *tz_name= et->time_zone->get_name(); - field_list.push_back(new Item_empty_string("time_zone", - tz_name->length())); + protocol->prepare_for_resend(); - field_list.push_back(new Item_empty_string("Create Event", - show_str.length())); + protocol->store(et->name.str, et->name.length, system_charset_info); + protocol->store(sql_mode.str, sql_mode.length, system_charset_info); + protocol->store(tz_name->ptr(), tz_name->length(), system_charset_info); + protocol->store(show_str.c_ptr(), show_str.length(), system_charset_info); - if (protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS | - Protocol::SEND_EOF)) - goto err; + if (protocol->write()) + DBUG_RETURN(TRUE); - protocol->prepare_for_resend(); - protocol->store(et->name.str, et->name.length, scs); + send_eof(thd); - protocol->store((char*) sql_mode_str, sql_mode_len, scs); + DBUG_RETURN(FALSE); +} - protocol->store((char*) tz_name->ptr(), tz_name->length(), scs); - protocol->store(show_str.c_ptr(), show_str.length(), scs); - ret= protocol->write(); - send_eof(thd); - } - delete et; +/** + Implement SHOW CREATE EVENT statement + + thd Thread context + spn The name of the event (db, name) + + @retval FALSE OK + @retval TRUE error (reported) +*/ + +bool +Events::show_create_event(THD *thd, LEX_STRING dbname, LEX_STRING name) +{ + Open_tables_state open_tables_backup; + Event_timed et; + bool ret; + + DBUG_ENTER("Events::show_create_event"); + DBUG_PRINT("enter", ("name: %s@%s", dbname.str, name.str)); + + if (check_if_system_tables_error()) + DBUG_RETURN(TRUE); + + if (check_access(thd, EVENT_ACL, dbname.str, 0, 0, 0, + is_schema_db(dbname.str))) + DBUG_RETURN(TRUE); + + /* + We would like to allow SHOW CREATE EVENT under LOCK TABLES and + in pre-locked mode. mysql.event table is marked as a system table. + This flag reduces the set of its participation scenarios in LOCK TABLES + operation, and therefore an out-of-bound open of this table + for reading like the one below (sic, only for reading) is + more or less deadlock-free. For additional information about when a + deadlock can occur please refer to the description of 'system table' + flag. + */ + thd->reset_n_backup_open_tables_state(&open_tables_backup); + ret= db_repository->load_named_event(thd, dbname, name, &et); + thd->restore_backup_open_tables_state(&open_tables_backup); + + if (!ret) + ret= send_show_create_event(thd, &et, thd->protocol); + DBUG_RETURN(ret); -err: - delete et; - DBUG_RETURN(TRUE); } -/* - Proxy for Event_db_repository::fill_schema_events. - Callback for I_S from sql_show.cc +/** + Check access rights and fill INFORMATION_SCHEMA.events table. - SYNOPSIS - Events::fill_schema_events() - thd Thread context - tables The schema table + @param[in,out] thd Thread context + @param[in] table The temporary table to fill. cond Unused - RETURN VALUE - 0 OK - !0 Error + In MySQL INFORMATION_SCHEMA tables are temporary tables that are + created and filled on demand. In this function, we fill + INFORMATION_SCHEMA.events. It is a callback for I_S module, invoked from + sql_show.cc + + @return Has to be integer, as such is the requirement of the I_S API + @retval 0 success + @retval 1 an error, pushed into the error stack */ int Events::fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */) { char *db= NULL; + int ret; + Open_tables_state open_tables_backup; DBUG_ENTER("Events::fill_schema_events"); - Events *myself= get_instance(); - if (unlikely(myself->check_system_tables_error)) - { - my_error(ER_EVENTS_DB_ERROR, MYF(0)); - DBUG_RETURN(TRUE); - } + + if (check_if_system_tables_error()) + DBUG_RETURN(1); /* If it's SHOW EVENTS then thd->lex->select_lex.db is guaranteed not to @@ -644,7 +818,17 @@ Events::fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */) DBUG_RETURN(1); db= thd->lex->select_lex.db; } - DBUG_RETURN(myself->db_repository->fill_schema_events(thd, tables, db)); + /* + Reset and backup of the currently open tables in this thread + is a way to allow SELECTs from INFORMATION_SCHEMA.events under + LOCK TABLES and in pre-locked mode. See also + Events::show_create_event for additional comments. + */ + thd->reset_n_backup_open_tables_state(&open_tables_backup); + ret= db_repository->fill_schema_events(thd, tables, db); + thd->restore_backup_open_tables_state(&open_tables_backup); + + DBUG_RETURN(ret); } @@ -663,14 +847,17 @@ Events::fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */) */ bool -Events::init() +Events::init(my_bool opt_noacl) { THD *thd; bool res= FALSE; + DBUG_ENTER("Events::init"); - if (opt_event_scheduler == Events::EVENTS_DISABLED) - DBUG_RETURN(FALSE); + /* Disable the scheduler if running with --skip-grant-tables */ + if (opt_noacl) + opt_event_scheduler= EVENTS_DISABLED; + /* We need a temporary THD during boot */ if (!(thd= new THD())) @@ -686,30 +873,67 @@ Events::init() thd->thread_stack= (char*) &thd; thd->store_globals(); - if (check_system_tables(thd)) + /* + We will need Event_db_repository anyway, even if the scheduler is + disabled - to perform events DDL. + */ + if (!(db_repository= new Event_db_repository)) { - check_system_tables_error= TRUE; - sql_print_error("Event Scheduler: The system tables are damaged. " - "The scheduler subsystem will be unusable during this run."); + res= TRUE; /* fatal error: request unireg_abort */ goto end; } - check_system_tables_error= FALSE; - if (event_queue->init_queue(thd) || load_events_from_db(thd)) + /* + Since we allow event DDL even if the scheduler is disabled, + check the system tables, as we might need them. + */ + if (Event_db_repository::check_system_tables(thd)) { - sql_print_error("Event Scheduler: Error while loading from disk."); + sql_print_error("Event Scheduler: An error occurred when initializing " + "system tables.%s", + opt_event_scheduler == EVENTS_DISABLED ? + "" : " Disabling the Event Scheduler."); + + /* Disable the scheduler since the system tables are not up to date */ + opt_event_scheduler= EVENTS_DISABLED; + check_system_tables_error= TRUE; goto end; } - scheduler->init_scheduler(event_queue); + /* + Was disabled explicitly from the command line, or because we're running + with --skip-grant-tables, or because we have no system tables. + */ + if (opt_event_scheduler == Events::EVENTS_DISABLED) + goto end; + DBUG_ASSERT(opt_event_scheduler == Events::EVENTS_ON || opt_event_scheduler == Events::EVENTS_OFF); - if (opt_event_scheduler == Events::EVENTS_ON) - res= scheduler->start(); - Event_worker_thread::init(this, db_repository); + if (!(event_queue= new Event_queue) || + !(scheduler= new Event_scheduler(event_queue))) + { + res= TRUE; /* fatal error: request unireg_abort */ + goto end; + } + + if (event_queue->init_queue(thd) || load_events_from_db(thd) || + opt_event_scheduler == EVENTS_ON && scheduler->start()) + { + sql_print_error("Event Scheduler: Error while loading from disk."); + res= TRUE; /* fatal error: request unireg_abort */ + goto end; + } + Event_worker_thread::init(db_repository); + end: + if (res) + { + delete db_repository; + delete event_queue; + delete scheduler; + } delete thd; /* Remember that we don't have a THD */ my_pthread_setspecific_ptr(THR_THD, NULL); @@ -732,14 +956,18 @@ void Events::deinit() { DBUG_ENTER("Events::deinit"); - if (likely(!check_system_tables_error)) - { - scheduler->stop(); - scheduler->deinit_scheduler(); - event_queue->deinit_queue(); + if (opt_event_scheduler != EVENTS_DISABLED) + { + delete scheduler; + scheduler= NULL; /* safety */ + delete event_queue; + event_queue= NULL; /* safety */ } + delete db_repository; + db_repository= NULL; /* safety */ + DBUG_VOID_RETURN; } @@ -756,8 +984,6 @@ void Events::init_mutexes() { pthread_mutex_init(&LOCK_event_metadata, MY_MUTEX_INIT_FAST); - event_queue->init_mutexes(); - scheduler->init_mutexes(); } @@ -771,8 +997,6 @@ Events::init_mutexes() void Events::destroy_mutexes() { - event_queue->deinit_mutexes(); - scheduler->deinit_mutexes(); pthread_mutex_destroy(&LOCK_event_metadata); } @@ -795,283 +1019,163 @@ Events::dump_internal_status() puts("LLA = Last Locked At LUA = Last Unlocked At"); puts("WOC = Waiting On Condition DL = Data Locked"); - scheduler->dump_internal_status(); - event_queue->dump_internal_status(); + pthread_mutex_lock(&LOCK_event_metadata); + if (opt_event_scheduler == EVENTS_DISABLED) + puts("The Event Scheduler is disabled"); + else + { + scheduler->dump_internal_status(); + event_queue->dump_internal_status(); + } + pthread_mutex_unlock(&LOCK_event_metadata); DBUG_VOID_RETURN; } /** - Starts execution of events by the scheduler + Starts or stops the event scheduler thread. - SYNOPSIS - Events::start_execution_of_events() - - RETURN VALUE - FALSE OK - TRUE Error + @retval FALSE success + @retval TRUE error */ bool -Events::start_execution_of_events() +Events::start_or_stop_event_scheduler(enum_opt_event_scheduler start_or_stop) { - DBUG_ENTER("Events::start_execution_of_events"); - if (unlikely(check_system_tables_error)) - { - my_error(ER_EVENTS_DB_ERROR, MYF(0)); - DBUG_RETURN(TRUE); - } - DBUG_RETURN(scheduler->start()); -} + bool ret= FALSE; + DBUG_ENTER("Events::start_or_stop_event_scheduler"); -/* - Stops execution of events by the scheduler. - Already running events will not be stopped. If the user needs - them stopped manual intervention is needed. + DBUG_ASSERT(start_or_stop == Events::EVENTS_ON || + start_or_stop == Events::EVENTS_OFF); - SYNOPSIS - Events::stop_execution_of_events() - - RETURN VALUE - FALSE OK - TRUE Error -*/ - -bool -Events::stop_execution_of_events() -{ - DBUG_ENTER("Events::stop_execution_of_events"); - if (unlikely(check_system_tables_error)) - { - my_error(ER_EVENTS_DB_ERROR, MYF(0)); + /* + If the scheduler was disabled because there are no/bad + system tables, produce a more meaningful error message + than ER_OPTION_PREVENTS_STATEMENT + */ + if (check_if_system_tables_error()) DBUG_RETURN(TRUE); - } - DBUG_RETURN(scheduler->stop()); -} - - -/* - Checks whether the scheduler is running or not. - - SYNOPSIS - Events::is_started() - - RETURN VALUE - TRUE Yes - FALSE No -*/ - -bool -Events::is_execution_of_events_started() -{ - DBUG_ENTER("Events::is_execution_of_events_started"); - if (unlikely(check_system_tables_error)) - { - my_error(ER_EVENTS_DB_ERROR, MYF(0)); - DBUG_RETURN(FALSE); - } - DBUG_RETURN(scheduler->is_running()); -} + pthread_mutex_lock(&LOCK_event_metadata); - -/* - Opens mysql.db and mysql.user and checks whether: - 1. mysql.db has column Event_priv at column 20 (0 based); - 2. mysql.user has column Event_priv at column 29 (0 based); - - SYNOPSIS - Events::check_system_tables() - thd Thread - - RETURN VALUE - FALSE OK - TRUE Error -*/ - -bool -Events::check_system_tables(THD *thd) -{ - TABLE_LIST tables; - Open_tables_state backup; - bool ret= FALSE; - - DBUG_ENTER("Events::check_system_tables"); - DBUG_PRINT("enter", ("thd: 0x%lx", (long) thd)); - - thd->reset_n_backup_open_tables_state(&backup); - - bzero((char*) &tables, sizeof(tables)); - tables.db= (char*) "mysql"; - tables.table_name= tables.alias= (char*) "db"; - tables.lock_type= TL_READ; - - if ((ret= simple_open_n_lock_tables(thd, &tables))) + if (opt_event_scheduler == EVENTS_DISABLED) { - sql_print_error("Event Scheduler: Cannot open mysql.db"); + my_error(ER_OPTION_PREVENTS_STATEMENT, + MYF(0), "--event-scheduler=DISABLED or --skip-grant-tables"); ret= TRUE; + goto end; } - ret= table_check_intact(tables.table, MYSQL_DB_FIELD_COUNT, - mysql_db_table_fields, &mysql_db_table_last_check, - ER_CANNOT_LOAD_FROM_TABLE); - close_thread_tables(thd); - bzero((char*) &tables, sizeof(tables)); - tables.db= (char*) "mysql"; - tables.table_name= tables.alias= (char*) "user"; - tables.lock_type= TL_READ; - - if (simple_open_n_lock_tables(thd, &tables)) - { - sql_print_error("Event Scheduler: Cannot open mysql.user"); - ret= TRUE; - } + if (start_or_stop == EVENTS_ON) + ret= scheduler->start(); else + ret= scheduler->stop(); + + if (ret) { - if (tables.table->s->fields < 29 || - strncmp(tables.table->field[29]->field_name, - STRING_WITH_LEN("Event_priv"))) - { - sql_print_error("mysql.user has no `Event_priv` column at position %d", - 29); - ret= TRUE; - } - close_thread_tables(thd); + my_error(ER_EVENT_SET_VAR_ERROR, MYF(0)); + goto end; } - thd->restore_backup_open_tables_state(&backup); + opt_event_scheduler= start_or_stop; +end: + pthread_mutex_unlock(&LOCK_event_metadata); DBUG_RETURN(ret); } -/* - Loads all ENABLED events from mysql.event into the prioritized - queue. Called during scheduler main thread initialization. Compiles - the events. Creates Event_queue_element instances for every ENABLED event - from mysql.event. +/** + Loads all ENABLED events from mysql.event into a prioritized + queue. - SYNOPSIS - Events::load_events_from_db() - thd Thread context. Used for memory allocation in some cases. + This function is called during the server start up. It reads + every event, computes the next execution time, and if the event + needs execution, adds it to a prioritized queue. Otherwise, if + ON COMPLETION DROP is specified, the event is automatically + removed from the table. - RETURN VALUE - 0 OK - !0 Error (EVEX_OPEN_TABLE_FAILED, EVEX_MICROSECOND_UNSUP, - EVEX_COMPILE_ERROR) - in all these cases mysql.event was - tampered. + @param[in,out] thd Thread context. Used for memory allocation in some cases. - NOTES - Reports the error to the console + @retval FALSE success + @retval TRUE error, the load is aborted + + @note Reports the error to the console */ -int +bool Events::load_events_from_db(THD *thd) { TABLE *table; READ_RECORD read_record_info; - int ret= -1; + bool ret= TRUE; uint count= 0; - bool clean_the_queue= TRUE; DBUG_ENTER("Events::load_events_from_db"); DBUG_PRINT("enter", ("thd: 0x%lx", (long) thd)); - if ((ret= db_repository->open_event_table(thd, TL_READ, &table))) + if ((ret= db_repository->open_event_table(thd, TL_WRITE, &table))) { - sql_print_error("Event Scheduler: Table mysql.event is damaged. Can not open"); - DBUG_RETURN(EVEX_OPEN_TABLE_FAILED); + sql_print_error("Event Scheduler: Failed to open table mysql.event"); + DBUG_RETURN(TRUE); } - init_read_record(&read_record_info, thd, table ,NULL,1,0); + init_read_record(&read_record_info, thd, table, NULL, 0, 1); while (!(read_record_info.read_record(&read_record_info))) { Event_queue_element *et; + bool created; + bool drop_on_completion; + if (!(et= new Event_queue_element)) - { - DBUG_PRINT("info", ("Out of memory")); - break; - } + goto end; + DBUG_PRINT("info", ("Loading event from row.")); - if ((ret= et->load_from_row(thd, table))) + if (et->load_from_row(thd, table)) { sql_print_error("Event Scheduler: " "Error while reading from mysql.event. " "The table is probably corrupted"); - break; - } - if (et->status != Event_queue_element::ENABLED) - { - DBUG_PRINT("info",("%s is disabled",et->name.str)); delete et; - continue; + goto end; } + drop_on_completion= (et->on_completion == + Event_queue_element::ON_COMPLETION_DROP); + - /* let's find when to be executed */ - if (et->compute_next_execution_time()) + if (event_queue->create_event(thd, et, &created)) { - sql_print_error("Event Scheduler: Error while computing execution time of %s.%s." - " Skipping", et->dbname.str, et->name.str); - continue; + /* Out of memory */ + delete et; + goto end; } - + if (created) + count++; + else if (drop_on_completion) { - Event_job_data temp_job_data; - DBUG_PRINT("info", ("Event %s loaded from row. ", et->name.str)); - - temp_job_data.load_from_row(thd, table); - /* - We load only on scheduler root just to check whether the body - compiles. + If not created, a stale event - drop if immediately if + ON COMPLETION NOT PRESERVE */ - switch (ret= temp_job_data.compile(thd, thd->mem_root)) { - case EVEX_MICROSECOND_UNSUP: - sql_print_error("Event Scheduler: mysql.event is tampered. MICROSECOND is not " - "supported but found in mysql.event"); - break; - case EVEX_COMPILE_ERROR: - sql_print_error("Event Scheduler: Error while compiling %s.%s. Aborting load", - et->dbname.str, et->name.str); - break; - default: - break; + int rc= table->file->ha_delete_row(table->record[0]); + if (rc) + { + table->file->print_error(rc, MYF(0)); + goto end; } - thd->end_statement(); - thd->cleanup_after_query(); } - if (ret) - { - delete et; - goto end; - } - - DBUG_PRINT("load_events_from_db", ("Adding 0x%lx to the exec list.", - (long) et)); - event_queue->create_event(thd, et); - count++; } - clean_the_queue= FALSE; + sql_print_information("Event Scheduler: Loaded %d event%s", + count, (count == 1) ? "" : "s"); + ret= FALSE; + end: end_read_record(&read_record_info); - if (clean_the_queue) - { - event_queue->empty_queue(); - ret= -1; - } - else - { - ret= 0; - sql_print_information("Event Scheduler: Loaded %d event%s", - count, (count == 1)?"":"s"); - } - close_thread_tables(thd); - DBUG_PRINT("info", ("Status code %d. Loaded %d event(s)", ret, count)); DBUG_RETURN(ret); } diff --git a/sql/events.h b/sql/events.h index f97a0c5f57e..db0e947ab6c 100644 --- a/sql/events.h +++ b/sql/events.h @@ -15,7 +15,11 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -class sp_name; +/* + @file + A public interface of Events Scheduler module. +*/ + class Event_parse_data; class Event_db_repository; class Event_queue; @@ -40,6 +44,22 @@ sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs); /** @class Events -- a facade to the functionality of the Event Scheduler. + Every public operation against the scheduler has to be executed via the + interface provided by a static method of this class. No instance of this + class is ever created and it has no non-static data members. + + The life cycle of the Events module is the following: + + At server start up: + set_opt_event_scheduler() -> init_mutexes() -> init() + When the server is running: + create_event(), drop_event(), start_or_stop_event_scheduler(), etc + At shutdown: + deinit(), destroy_mutexes(). + + The peculiar initialization and shutdown cycle is an adaptation to the + outside server startup/shutdown framework and mimics the rest of MySQL + subsystems (ACL, time zone tables, etc). */ class Events @@ -53,47 +73,48 @@ public: EVENTS_DISABLED= 4 }; - static enum_opt_event_scheduler opt_event_scheduler; - static TYPELIB opt_typelib; - static TYPELIB var_typelib; + /* Possible values of @@event_scheduler variable */ + static const TYPELIB var_typelib; - bool - init(); + static bool + set_opt_event_scheduler(char *argument); - void - deinit(); + static const char * + get_opt_event_scheduler_str(); - void - init_mutexes(); + /* A hack needed for Event_queue_element */ + static Event_db_repository * + get_db_repository() { return db_repository; } - void - destroy_mutexes(); + static bool + init(my_bool opt_noacl); - bool - start_execution_of_events(); + static void + deinit(); - bool - stop_execution_of_events(); + static void + init_mutexes(); - bool - is_execution_of_events_started(); + static void + destroy_mutexes(); - static Events * - get_instance(); + static bool + start_or_stop_event_scheduler(enum enum_opt_event_scheduler start_or_stop); - bool + static bool create_event(THD *thd, Event_parse_data *parse_data, bool if_exists); - bool - update_event(THD *thd, Event_parse_data *parse_data, sp_name *rename_to); + static bool + update_event(THD *thd, Event_parse_data *parse_data, + LEX_STRING *new_dbname, LEX_STRING *new_name); - bool + static bool drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name, bool if_exists); - void + static void drop_schema_events(THD *thd, char *db); - bool + static bool show_create_event(THD *thd, LEX_STRING dbname, LEX_STRING name); /* Needed for both SHOW CREATE EVENT and INFORMATION_SCHEMA */ @@ -104,31 +125,28 @@ public: static int fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */); - void + static void dump_internal_status(); private: - bool - check_system_tables(THD *thd); + static bool check_if_system_tables_error(); - int + static bool load_events_from_db(THD *thd); - /* Singleton DP is used */ - Events(); - ~Events(){} - - /* Singleton instance */ - static Events singleton; - - Event_queue *event_queue; - Event_scheduler *scheduler; - Event_db_repository *db_repository; - - pthread_mutex_t LOCK_event_metadata; - - bool check_system_tables_error; +private: + /* Command line option names */ + static const TYPELIB opt_typelib; + static pthread_mutex_t LOCK_event_metadata; + static Event_queue *event_queue; + static Event_scheduler *scheduler; + static Event_db_repository *db_repository; + /* Current state of Event Scheduler */ + static enum enum_opt_event_scheduler opt_event_scheduler; + /* Set to TRUE if an error at start up */ + static bool check_system_tables_error; +private: /* Prevent use of these */ Events(const Events &); void operator=(Events &); diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 9bdf117f8f7..47f8e17dd93 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -885,7 +885,7 @@ static void close_connections(void) } (void) pthread_mutex_unlock(&LOCK_thread_count); // For unlink from list - Events::get_instance()->deinit(); + Events::deinit(); end_slave(); if (thread_count) @@ -1330,7 +1330,7 @@ static void clean_up_mutexes() (void) pthread_mutex_destroy(&LOCK_bytes_sent); (void) pthread_mutex_destroy(&LOCK_bytes_received); (void) pthread_mutex_destroy(&LOCK_user_conn); - Events::get_instance()->destroy_mutexes(); + Events::destroy_mutexes(); #ifdef HAVE_OPENSSL (void) pthread_mutex_destroy(&LOCK_des_key_file); #ifndef HAVE_YASSL @@ -3058,7 +3058,7 @@ static int init_thread_environment() (void) pthread_mutex_init(&LOCK_server_started, MY_MUTEX_INIT_FAST); (void) pthread_cond_init(&COND_server_started,NULL); sp_cache_init(); - Events::get_instance()->init_mutexes(); + Events::init_mutexes(); /* Parameter for threads created for connections */ (void) pthread_attr_init(&connection_attrib); (void) pthread_attr_setdetachstate(&connection_attrib, @@ -3844,21 +3844,15 @@ we force server id to 2, but this MySQL server will not act as a slave."); create_shutdown_thread(); create_maintenance_thread(); + if (Events::init(opt_noacl)) + unireg_abort(1); + sql_print_information(ER(ER_STARTUP),my_progname,server_version, ((unix_sock == INVALID_SOCKET) ? (char*) "" : mysqld_unix_port), mysqld_port, MYSQL_COMPILATION_COMMENT); - if (!opt_noacl) - { - if (Events::get_instance()->init()) - unireg_abort(1); - } - else - { - Events::opt_event_scheduler = Events::EVENTS_DISABLED; - } /* Signal threads waiting for server to be started */ pthread_mutex_lock(&LOCK_server_started); @@ -7585,32 +7579,8 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), } #endif case OPT_EVENT_SCHEDULER: - if (!argument) - Events::opt_event_scheduler= Events::EVENTS_DISABLED; - else - { - int type; - /* - type= 5 1 2 3 4 - (DISABLE ) - (OFF | ON) - (0 | 1) - */ - switch ((type=find_type(argument, &Events::opt_typelib, 1))) { - case 0: - fprintf(stderr, "Unknown option to event-scheduler: %s\n",argument); - exit(1); - case 5: /* OPT_DISABLED */ - Events::opt_event_scheduler= Events::EVENTS_DISABLED; - break; - case 2: /* OPT_ON */ - case 4: /* 1 */ - Events::opt_event_scheduler= Events::EVENTS_ON; - break; - case 1: /* OPT_OFF */ - case 3: /* 0 */ - Events::opt_event_scheduler= Events::EVENTS_OFF; - break; - } - } + if (Events::set_opt_event_scheduler(argument)) + exit(1); break; case (int) OPT_SKIP_NEW: opt_specialflag|= SPECIAL_NO_NEW_FUNC; diff --git a/sql/set_var.cc b/sql/set_var.cc index 7f55556c134..898d47e766e 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -1686,7 +1686,7 @@ byte *sys_var_thd_bool::value_ptr(THD *thd, enum_var_type type, } -bool sys_var::check_enum(THD *thd, set_var *var, TYPELIB *enum_names) +bool sys_var::check_enum(THD *thd, set_var *var, const TYPELIB *enum_names) { char buff[STRING_BUFFER_USUAL_SIZE]; const char *value; @@ -3643,21 +3643,18 @@ bool sys_var_thd_table_type::update(THD *thd, set_var *var) SYNOPSIS thd in thread handler val in sql_mode value - len out pointer on length of string - - RETURN - pointer to string with sql_mode representation + rep out pointer pointer to string with sql_mode representation */ -byte *sys_var_thd_sql_mode::symbolic_mode_representation(THD *thd, - ulonglong val, - ulong *len) +bool +sys_var_thd_sql_mode:: +symbolic_mode_representation(THD *thd, ulonglong val, LEX_STRING *rep) { - char buff[256]; + char buff[STRING_BUFFER_USUAL_SIZE*8]; String tmp(buff, sizeof(buff), &my_charset_latin1); - ulong length; tmp.length(0); + for (uint i= 0; val; val>>= 1, i++) { if (val & 1) @@ -3668,20 +3665,25 @@ byte *sys_var_thd_sql_mode::symbolic_mode_representation(THD *thd, } } - if ((length= tmp.length())) - length--; - *len= length; - return (byte*) thd->strmake(tmp.ptr(), length); + if (tmp.length()) + tmp.length(tmp.length() - 1); /* trim the trailing comma */ + + rep->str= thd->strmake(tmp.ptr(), tmp.length()); + + rep->length= rep->str ? tmp.length() : 0; + + return rep->length != tmp.length(); } byte *sys_var_thd_sql_mode::value_ptr(THD *thd, enum_var_type type, LEX_STRING *base) { + LEX_STRING sql_mode; ulonglong val= ((type == OPT_GLOBAL) ? global_system_variables.*offset : thd->variables.*offset); - ulong length_unused; - return symbolic_mode_representation(thd, val, &length_unused); + (void) symbolic_mode_representation(thd, val, &sql_mode); + return sql_mode.str; } @@ -4012,24 +4014,13 @@ sys_var_event_scheduler::update(THD *thd, set_var *var) int res; /* here start the thread if not running. */ DBUG_ENTER("sys_var_event_scheduler::update"); - if (Events::opt_event_scheduler == Events::EVENTS_DISABLED) - { - my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--event-scheduler=DISABLED or --skip-grant-tables"); - DBUG_RETURN(TRUE); - } - DBUG_PRINT("info", ("new_value: %d", (int) var->save_result.ulong_value)); - if (var->save_result.ulong_value == Events::EVENTS_ON) - res= Events::get_instance()->start_execution_of_events(); - else if (var->save_result.ulong_value == Events::EVENTS_OFF) - res= Events::get_instance()->stop_execution_of_events(); - else - { - assert(0); // Impossible - } - if (res) - my_error(ER_EVENT_SET_VAR_ERROR, MYF(0)); + enum Events::enum_opt_event_scheduler + new_state= + (enum Events::enum_opt_event_scheduler) var->save_result.ulong_value; + + res= Events::start_or_stop_event_scheduler(new_state); DBUG_RETURN((bool) res); } @@ -4038,15 +4029,7 @@ sys_var_event_scheduler::update(THD *thd, set_var *var) byte *sys_var_event_scheduler::value_ptr(THD *thd, enum_var_type type, LEX_STRING *base) { - int state; - if (Events::opt_event_scheduler == Events::EVENTS_DISABLED) - state= Events::EVENTS_DISABLED; // This should be DISABLED - else if (Events::get_instance()->is_execution_of_events_started()) - state= Events::EVENTS_ON; // This should be ON - else - state= Events::EVENTS_OFF; // This should be OFF - - return (byte*) Events::opt_typelib.type_names[state]; + return (byte *) Events::get_opt_event_scheduler_str(); } diff --git a/sql/set_var.h b/sql/set_var.h index eac03fce610..a3963171192 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -61,7 +61,7 @@ public: sys_vars++; } virtual bool check(THD *thd, set_var *var); - bool check_enum(THD *thd, set_var *var, TYPELIB *enum_names); + bool check_enum(THD *thd, set_var *var, const TYPELIB *enum_names); bool check_set(THD *thd, set_var *var, TYPELIB *enum_names); virtual bool update(THD *thd, set_var *var)=0; virtual void set_default(THD *thd_arg, enum_var_type type) {} @@ -450,8 +450,8 @@ public: } void set_default(THD *thd, enum_var_type type); byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base); - static byte *symbolic_mode_representation(THD *thd, ulonglong sql_mode, - ulong *length); + static bool symbolic_mode_representation(THD *thd, ulonglong sql_mode, + LEX_STRING *rep); }; diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index b5357493e05..5fb2074db6e 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -5884,8 +5884,8 @@ ER_COL_COUNT_DOESNT_MATCH_CORRUPTED eng "Column count of mysql.%s is wrong. Expected %d, found %d. The table is probably corrupted" ger "Spaltenanzahl von mysql.%s falsch. %d erwartet, aber %d gefunden. Tabelle ist wahrscheinlich beschädigt" ER_CANNOT_LOAD_FROM_TABLE - eng "Cannot load from mysql.%s. The table is probably corrupted. Please see the error log for details" - ger "Kann mysql.%s nicht einlesen. Tabelle ist wahrscheinlich beschädigt, siehe Fehlerlog" + eng "Cannot load from mysql.%s. The table is probably corrupted" + ger "Kann mysql.%s nicht einlesen. Tabelle ist wahrscheinlich beschädigt" ER_EVENT_CANNOT_DELETE eng "Failed to delete the event from mysql.event" ger "Löschen des Events aus mysql.event fehlgeschlagen" diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 8eeec741dcf..9bedc6fb38e 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -2104,8 +2104,7 @@ sp_head::show_create_procedure(THD *thd) String buffer(buff, sizeof(buff), system_charset_info); int res; List<Item> field_list; - byte *sql_mode_str; - ulong sql_mode_len; + LEX_STRING sql_mode; bool full_access; DBUG_ENTER("sp_head::show_create_procedure"); DBUG_PRINT("info", ("procedure %s", m_name.str)); @@ -2116,12 +2115,10 @@ sp_head::show_create_procedure(THD *thd) if (check_show_routine_access(thd, this, &full_access)) DBUG_RETURN(1); - sql_mode_str= - sys_var_thd_sql_mode::symbolic_mode_representation(thd, - m_sql_mode, - &sql_mode_len); + sys_var_thd_sql_mode::symbolic_mode_representation(thd, m_sql_mode, + &sql_mode); field_list.push_back(new Item_empty_string("Procedure", NAME_LEN)); - field_list.push_back(new Item_empty_string("sql_mode", sql_mode_len)); + field_list.push_back(new Item_empty_string("sql_mode", sql_mode.length)); // 1024 is for not to confuse old clients Item_empty_string *definition= new Item_empty_string("Create Procedure", max(buffer.length(),1024)); @@ -2133,7 +2130,7 @@ sp_head::show_create_procedure(THD *thd) DBUG_RETURN(1); protocol->prepare_for_resend(); protocol->store(m_name.str, m_name.length, system_charset_info); - protocol->store((char*) sql_mode_str, sql_mode_len, system_charset_info); + protocol->store((char*) sql_mode.str, sql_mode.length, system_charset_info); if (full_access) protocol->store(m_defstr.str, m_defstr.length, system_charset_info); else @@ -2176,23 +2173,18 @@ sp_head::show_create_function(THD *thd) String buffer(buff, sizeof(buff), system_charset_info); int res; List<Item> field_list; - byte *sql_mode_str; - ulong sql_mode_len; + LEX_STRING sql_mode; bool full_access; DBUG_ENTER("sp_head::show_create_function"); DBUG_PRINT("info", ("procedure %s", m_name.str)); - LINT_INIT(sql_mode_str); - LINT_INIT(sql_mode_len); if (check_show_routine_access(thd, this, &full_access)) DBUG_RETURN(1); - sql_mode_str= - sys_var_thd_sql_mode::symbolic_mode_representation(thd, - m_sql_mode, - &sql_mode_len); + sys_var_thd_sql_mode::symbolic_mode_representation(thd, m_sql_mode, + &sql_mode); field_list.push_back(new Item_empty_string("Function",NAME_LEN)); - field_list.push_back(new Item_empty_string("sql_mode", sql_mode_len)); + field_list.push_back(new Item_empty_string("sql_mode", sql_mode.length)); Item_empty_string *definition= new Item_empty_string("Create Function", max(buffer.length(),1024)); definition->maybe_null= TRUE; @@ -2203,7 +2195,7 @@ sp_head::show_create_function(THD *thd) DBUG_RETURN(1); protocol->prepare_for_resend(); protocol->store(m_name.str, m_name.length, system_charset_info); - protocol->store((char*) sql_mode_str, sql_mode_len, system_charset_info); + protocol->store(sql_mode.str, sql_mode.length, system_charset_info); if (full_access) protocol->store(m_defstr.str, m_defstr.length, system_charset_info); else diff --git a/sql/sql_db.cc b/sql/sql_db.cc index 4fd35b7e6e8..cc9dc4c1fec 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -953,7 +953,7 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) exit: (void)sp_drop_db_routines(thd, db); /* QQ Ignore errors for now */ - Events::get_instance()->drop_schema_events(thd, db); + Events::drop_schema_events(thd, db); /* If this database was the client's selected database, we silently change the client's selected database to nothing (to have an empty diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index e59c675a678..e2a36b232c9 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -26,7 +26,6 @@ #include "sp.h" #include "sp_cache.h" #include "events.h" -#include "event_data_objects.h" #include "sql_trigger.h" /* Used in error handling only */ @@ -3181,13 +3180,16 @@ end_with_restore_list: switch (lex->sql_command) { case SQLCOM_CREATE_EVENT: - res= Events::get_instance()-> - create_event(thd, lex->event_parse_data, - lex->create_info.options & HA_LEX_CREATE_IF_NOT_EXISTS); + { + bool if_not_exists= (lex->create_info.options & + HA_LEX_CREATE_IF_NOT_EXISTS); + res= Events::create_event(thd, lex->event_parse_data, if_not_exists); break; + } case SQLCOM_ALTER_EVENT: - res= Events::get_instance()->update_event(thd, lex->event_parse_data, - lex->spname); + res= Events::update_event(thd, lex->event_parse_data, + lex->spname ? &lex->spname->m_db : NULL, + lex->spname ? &lex->spname->m_name : NULL); break; default: DBUG_ASSERT(0); @@ -3205,39 +3207,16 @@ end_with_restore_list: } /* lex->unit.cleanup() is called outside, no need to call it here */ break; - case SQLCOM_DROP_EVENT: case SQLCOM_SHOW_CREATE_EVENT: - { - DBUG_ASSERT(lex->spname); - if (! lex->spname->m_db.str) - { - my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0)); - goto error; - } - 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) - { - my_error(ER_TOO_LONG_IDENT, MYF(0), lex->spname->m_name.str); - /* this jumps to the end of the function and skips own messaging */ - goto error; - } - - if (lex->sql_command == SQLCOM_SHOW_CREATE_EVENT) - res= Events::get_instance()->show_create_event(thd, lex->spname->m_db, - lex->spname->m_name); - else - { - if (!(res= Events::get_instance()->drop_event(thd, - lex->spname->m_db, - lex->spname->m_name, - lex->drop_if_exists))) - send_ok(thd); - } + res= Events::show_create_event(thd, lex->spname->m_db, + lex->spname->m_name); + break; + case SQLCOM_DROP_EVENT: + if (!(res= Events::drop_event(thd, + lex->spname->m_db, lex->spname->m_name, + lex->drop_if_exists))) + send_ok(thd); 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 445890adedb..4e89fb610cc 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -3761,8 +3761,7 @@ static bool store_trigger(THD *thd, TABLE *table, const char *db, LEX_STRING *definer_buffer) { CHARSET_INFO *cs= system_charset_info; - byte *sql_mode_str; - ulong sql_mode_len; + LEX_STRING sql_mode_rep; restore_record(table, s->default_values); table->field[1]->store(db, strlen(db), cs); @@ -3778,11 +3777,9 @@ static bool store_trigger(THD *thd, TABLE *table, const char *db, table->field[14]->store(STRING_WITH_LEN("OLD"), cs); table->field[15]->store(STRING_WITH_LEN("NEW"), cs); - sql_mode_str= - sys_var_thd_sql_mode::symbolic_mode_representation(thd, - sql_mode, - &sql_mode_len); - table->field[17]->store((const char*)sql_mode_str, sql_mode_len, cs); + sys_var_thd_sql_mode::symbolic_mode_representation(thd, sql_mode, + &sql_mode_rep); + table->field[17]->store(sql_mode_rep.str, sql_mode_rep.length, cs); table->field[18]->store((const char *)definer_buffer->str, definer_buffer->length, cs); return schema_table_store_record(thd, table); } @@ -4308,13 +4305,13 @@ copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table) CHARSET_INFO *scs= system_charset_info; TIME time; Event_timed et; - DBUG_ENTER("fill_events_copy_to_schema_tab"); + DBUG_ENTER("copy_event_to_schema_table"); restore_record(sch_table, s->default_values); if (et.load_from_row(thd, event_table)) { - my_error(ER_CANNOT_LOAD_FROM_TABLE, MYF(0)); + my_error(ER_CANNOT_LOAD_FROM_TABLE, MYF(0), event_table->alias); DBUG_RETURN(1); } @@ -4349,13 +4346,11 @@ copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table) /* SQL_MODE */ { - byte *sql_mode_str; - ulong sql_mode_len= 0; - sql_mode_str= - sys_var_thd_sql_mode::symbolic_mode_representation(thd, et.sql_mode, - &sql_mode_len); + LEX_STRING sql_mode; + sys_var_thd_sql_mode::symbolic_mode_representation(thd, et.sql_mode, + &sql_mode); sch_table->field[ISE_SQL_MODE]-> - store((const char*)sql_mode_str, sql_mode_len, scs); + store(sql_mode.str, sql_mode.length, scs); } int not_used=0; diff --git a/sql/sql_test.cc b/sql/sql_test.cc index 9e30cf5878c..d7573b42c5f 100644 --- a/sql/sql_test.cc +++ b/sql/sql_test.cc @@ -537,6 +537,6 @@ Estimated memory (with thread stack): %ld\n", (long) (thread_count * thread_stack + info.hblkhd + info.arena)); #endif - Events::get_instance()->dump_internal_status(); + Events::dump_internal_status(); puts(""); } diff --git a/sql/table.cc b/sql/table.cc index 71e510bf0ac..5e901318919 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -250,7 +250,7 @@ void free_table_share(TABLE_SHARE *share) Currently these are: help_category, help_keyword, help_relation, help_topic, - proc, + proc, event time_zone, time_zone_leap_second, time_zone_name, time_zone_transition, time_zone_transition_type @@ -283,7 +283,14 @@ inline bool is_system_table_name(const char *name, uint length) my_tolower(ci, name[0]) == 't' && my_tolower(ci, name[1]) == 'i' && my_tolower(ci, name[2]) == 'm' && - my_tolower(ci, name[3]) == 'e' + my_tolower(ci, name[3]) == 'e' || + + /* mysql.event table */ + my_tolower(ci, name[0]) == 'e' && + my_tolower(ci, name[1]) == 'v' && + my_tolower(ci, name[2]) == 'e' && + my_tolower(ci, name[3]) == 'n' && + my_tolower(ci, name[4]) == 't' ) ); } @@ -2432,153 +2439,143 @@ bool check_column_name(const char *name) } -/* +/** Checks whether a table is intact. Should be done *just* after the table has been opened. - - SYNOPSIS - table_check_intact() - table The table to check - table_f_count Expected number of columns in the table - table_def Expected structure of the table (column name and type) - last_create_time The table->file->create_time of the table in memory - we have checked last time - error_num ER_XXXX from the error messages file. When 0 no error - is sent to the client in case types does not match. - If different col number either - ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE or - ER_COL_COUNT_DOESNT_MATCH_CORRUPTED is used - - RETURNS - FALSE OK - TRUE There was an error + + @param[in] table The table to check + @param[in] table_f_count Expected number of columns in the table + @param[in] table_def Expected structure of the table (column name + and type) + + @retval FALSE OK + @retval TRUE There was an error. An error message is output + to the error log. We do not push an error + message into the error stack because this + function is currently only called at start up, + and such errors never reach the user. */ my_bool table_check_intact(TABLE *table, const uint table_f_count, - const TABLE_FIELD_W_TYPE *table_def, - time_t *last_create_time, int error_num) + const TABLE_FIELD_W_TYPE *table_def) { uint i; my_bool error= FALSE; my_bool fields_diff_count; DBUG_ENTER("table_check_intact"); - DBUG_PRINT("info",("table: %s expected_count: %d last_create_time: %ld", - table->alias, table_f_count, *last_create_time)); - - if ((fields_diff_count= (table->s->fields != table_f_count)) || - (*last_create_time != table->file->stats.create_time)) + DBUG_PRINT("info",("table: %s expected_count: %d", + table->alias, table_f_count)); + + fields_diff_count= (table->s->fields != table_f_count); + if (fields_diff_count) { - DBUG_PRINT("info", ("I am suspecting, checking table")); - if (fields_diff_count) + DBUG_PRINT("info", ("Column count has changed, checking the definition")); + + /* previous MySQL version */ + if (MYSQL_VERSION_ID > table->s->mysql_version) { - /* previous MySQL version */ - error= TRUE; - if (MYSQL_VERSION_ID > table->s->mysql_version) - { - my_error(ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE, MYF(0), table->alias, - table_f_count, table->s->fields, table->s->mysql_version, - MYSQL_VERSION_ID); - sql_print_error(ER(ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE), - table->alias, table_f_count, table->s->fields, - table->s->mysql_version, MYSQL_VERSION_ID); - DBUG_RETURN(error); + sql_print_error(ER(ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE), + table->alias, table_f_count, table->s->fields, + table->s->mysql_version, MYSQL_VERSION_ID); + DBUG_RETURN(TRUE); + } + else if (MYSQL_VERSION_ID == table->s->mysql_version) + { + sql_print_error(ER(ER_COL_COUNT_DOESNT_MATCH_CORRUPTED), table->alias, + table_f_count, table->s->fields); + DBUG_RETURN(TRUE); + } + /* + Something has definitely changed, but we're running an older + version of MySQL with new system tables. + Let's check column definitions. If a column was added at + the end of the table, then we don't care much since such change + is backward compatible. + */ + } + char buffer[STRING_BUFFER_USUAL_SIZE]; + for (i=0 ; i < table_f_count; i++, table_def++) + { + String sql_type(buffer, sizeof(buffer), system_charset_info); + sql_type.length(0); + if (i < table->s->fields) + { + Field *field= table->field[i]; - } - else if (MYSQL_VERSION_ID == table->s->mysql_version) - { - my_error(ER_COL_COUNT_DOESNT_MATCH_CORRUPTED,MYF(0), table->alias, - table_f_count, table->s->fields); - sql_print_error(ER(ER_COL_COUNT_DOESNT_MATCH_CORRUPTED), table->alias, - table_f_count, table->s->fields); - } - else + if (strncmp(field->field_name, table_def->name.str, + table_def->name.length)) { /* - Moving from newer mysql to older one -> let's say not an error but - will check the definition afterwards. If a column was added at the - end then we don't care much since it's not in the middle. + Name changes are not fatal, we use ordinal numbers to access columns. + Still this can be a sign of a tampered table, output an error + to the error log. */ - error= FALSE; + sql_print_error("Incorrect definition of table %s.%s: " + "expected column '%s' at position %d, found '%s'.", + table->s->db.str, table->alias, table_def->name.str, i, + field->field_name); } - } - /* definitely something has changed */ - char buffer[255]; - for (i=0 ; i < table_f_count; i++, table_def++) - { - String sql_type(buffer, sizeof(buffer), system_charset_info); - sql_type.length(0); + field->sql_type(sql_type); /* - Name changes are not fatal, we use sequence numbers => no problem - for us but this can show tampered table or broken table. - */ - if (i < table->s->fields) + Generally, if column types don't match, then something is + wrong. + + However, we only compare column definitions up to the + length of the original definition, since we consider the + following definitions compatible: + + 1. DATETIME and DATETIM + 2. INT(11) and INT(11 + 3. SET('one', 'two') and SET('one', 'two', 'more') + + For SETs or ENUMs, if the same prefix is there it's OK to + add more elements - they will get higher ordinal numbers and + the new table definition is backward compatible with the + original one. + */ + if (strncmp(sql_type.c_ptr_safe(), table_def->type.str, + table_def->type.length - 1)) { - Field *field= table->field[i]; - if (strncmp(field->field_name, table_def->name.str, - table_def->name.length)) - { - sql_print_error("(%s) Expected field %s at position %d, found %s", - table->alias, table_def->name.str, i, - field->field_name); - } - - /* - If the type does not match than something is really wrong - Check up to length - 1. Why? - 1. datetime -> datetim -> the same - 2. int(11) -> int(11 -> the same - 3. set('one','two') -> set('one','two' - so for sets if the same prefix is there it's ok if more are - added as part of the set. The same is valid for enum. So a new - table running on a old server will be valid. - */ - field->sql_type(sql_type); - if (strncmp(sql_type.c_ptr_safe(), table_def->type.str, - table_def->type.length - 1)) - { - sql_print_error("(%s) Expected field %s at position %d to have type " - "%s, found %s", table->alias, table_def->name.str, - i, table_def->type.str, sql_type.c_ptr_safe()); - error= TRUE; - } - else if (table_def->cset.str && !field->has_charset()) - { - sql_print_error("(%s) Expected field %s at position %d to have " - "character set '%s' but found no such", table->alias, - table_def->name.str, i, table_def->cset.str); - error= TRUE; - } - else if (table_def->cset.str && - strcmp(field->charset()->csname, table_def->cset.str)) - { - sql_print_error("(%s) Expected field %s at position %d to have " - "character set '%s' but found '%s'", table->alias, - table_def->name.str, i, table_def->cset.str, - field->charset()->csname); - error= TRUE; - } + sql_print_error("Incorrect definition of table %s.%s: " + "expected column '%s' at position %d to have type " + "%s, found type %s.", table->s->db.str, table->alias, + table_def->name.str, i, table_def->type.str, + sql_type.c_ptr_safe()); + error= TRUE; } - else + else if (table_def->cset.str && !field->has_charset()) + { + sql_print_error("Incorrect definition of table %s.%s: " + "expected the type of column '%s' at position %d " + "to have character set '%s' but the type has no " + "character set.", table->s->db.str, table->alias, + table_def->name.str, i, table_def->cset.str); + error= TRUE; + } + else if (table_def->cset.str && + strcmp(field->charset()->csname, table_def->cset.str)) { - sql_print_error("(%s) Expected field %s at position %d to have type %s " - " but no field found.", table->alias, - table_def->name.str, i, table_def->type.str); - error= TRUE; + sql_print_error("Incorrect definition of table %s.%s: " + "expected the type of column '%s' at position %d " + "to have character set '%s' but found " + "character set '%s'.", table->s->db.str, table->alias, + table_def->name.str, i, table_def->cset.str, + field->charset()->csname); + error= TRUE; } } - if (!error) - *last_create_time= table->file->stats.create_time; - else if (!fields_diff_count && error_num) - my_error(error_num,MYF(0), table->alias, table_f_count, table->s->fields); - } - else - { - DBUG_PRINT("info", ("Table seems ok without thorough checking.")); - *last_create_time= table->file->stats.create_time; + else + { + sql_print_error("Incorrect definition of table %s.%s: " + "expected column '%s' at position %d to have type %s " + " but the column is not found.", + table->s->db.str, table->alias, + table_def->name.str, i, table_def->type.str); + error= TRUE; + } } - - DBUG_RETURN(error); + DBUG_RETURN(error); } diff --git a/sql/table.h b/sql/table.h index e05b444251c..bb9ced2e450 100644 --- a/sql/table.h +++ b/sql/table.h @@ -689,6 +689,21 @@ class index_hint; typedef struct st_table_list { st_table_list() {} /* Remove gcc warning */ + + /** + Prepare TABLE_LIST that consists of one table instance to use in + simple_open_and_lock_tables + */ + inline void init_one_table(const char *db_name_arg, + const char *table_name_arg, + enum thr_lock_type lock_type_arg) + { + bzero((char*) this, sizeof(*this)); + db= (char*) db_name_arg; + table_name= alias= (char*) table_name_arg; + lock_type= lock_type_arg; + } + /* List of tables local to a subquery (used by SQL_LIST). Considers views as leaves (unlike 'next_leaf' below). Created at parse time @@ -1097,8 +1112,7 @@ typedef struct st_table_field_w_type my_bool table_check_intact(TABLE *table, const uint table_f_count, - const TABLE_FIELD_W_TYPE *table_def, - time_t *last_create_time, int error_num); + const TABLE_FIELD_W_TYPE *table_def); static inline my_bitmap_map *tmp_use_all_columns(TABLE *table, MY_BITMAP *bitmap) |