summaryrefslogtreecommitdiff
path: root/sql/event_data_objects.cc
diff options
context:
space:
mode:
authorunknown <malff/marcsql@weblab.(none)>2007-04-20 15:31:41 -0600
committerunknown <malff/marcsql@weblab.(none)>2007-04-20 15:31:41 -0600
commite3ccf3c6638f17163d93f5f071a71e0e6feb203a (patch)
tree7cc9d8ee328658227b1d4c61228a800baadcd68d /sql/event_data_objects.cc
parent51e016f1845b962a705af589aa7af599462c1743 (diff)
parente2c47cc462816ca465e76b9ed82df4b4adc31120 (diff)
downloadmariadb-git-e3ccf3c6638f17163d93f5f071a71e0e6feb203a.tar.gz
Merge weblab.(none):/home/marcsql/TREE/mysql-5.1-base
into weblab.(none):/home/marcsql/TREE/mysql-5.1-rt-merge client/mysqldump.c: Auto merged mysql-test/t/sp.test: Auto merged sql/event_data_objects.cc: Auto merged sql/item_func.cc: Auto merged sql/sp_head.cc: Auto merged sql/sql_class.cc: Auto merged sql/sql_class.h: Auto merged
Diffstat (limited to 'sql/event_data_objects.cc')
-rw-r--r--sql/event_data_objects.cc464
1 files changed, 193 insertions, 271 deletions
diff --git a/sql/event_data_objects.cc b/sql/event_data_objects.cc
index 56ac7c1a88d..f5847e603e8 100644
--- a/sql/event_data_objects.cc
+++ b/sql/event_data_objects.cc
@@ -23,14 +23,6 @@
#define EVEX_MAX_INTERVAL_VALUE 1000000000L
-static bool
-event_change_security_context(THD *thd, LEX_STRING user, LEX_STRING host,
- LEX_STRING db, Security_context *backup);
-
-static void
-event_restore_security_context(THD *thd, Security_context *backup);
-
-
/*
Initiliazes dbname and name of an Event_queue_element_for_exec
object
@@ -816,27 +808,10 @@ Event_timed::~Event_timed()
*/
Event_job_data::Event_job_data()
- :sphead(NULL), sql_mode(0)
-{
-}
-
-
-/*
- Destructor
-
- SYNOPSIS
- Event_timed::~Event_timed()
-*/
-
-Event_job_data::~Event_job_data()
+ :sql_mode(0)
{
- DBUG_ENTER("Event_job_data::~Event_job_data");
- delete sphead;
- sphead= NULL;
- DBUG_VOID_RETURN;
}
-
/*
Init all member variables
@@ -1769,234 +1744,242 @@ Event_timed::get_create_event(THD *thd, String *buf)
}
-/*
- Get SHOW CREATE EVENT as string
+/**
+ Get an artificial stored procedure to parse as an event definition.
+*/
- SYNOPSIS
- Event_job_data::get_create_event(THD *thd, String *buf)
- thd Thread
- buf String*, should be already allocated. CREATE EVENT goes inside.
+bool
+Event_job_data::construct_sp_sql(THD *thd, String *sp_sql)
+{
+ LEX_STRING buffer;
+ const uint STATIC_SQL_LENGTH= 44;
- RETURN VALUE
- 0 OK
- EVEX_MICROSECOND_UNSUP Error (for now if mysql.event has been
- tampered and MICROSECONDS interval or
- derivative has been put there.
+ DBUG_ENTER("Event_job_data::construct_sp_sql");
+
+ /*
+ Allocate a large enough buffer on the thread execution memory
+ root to avoid multiple [re]allocations on system heap
+ */
+ buffer.length= STATIC_SQL_LENGTH + name.length + body.length;
+ if (! (buffer.str= (char*) thd->alloc(buffer.length)))
+ DBUG_RETURN(TRUE);
+
+ sp_sql->set(buffer.str, buffer.length, system_charset_info);
+ sp_sql->length(0);
+
+
+ sp_sql->append(C_STRING_WITH_LEN("CREATE "));
+ sp_sql->append(C_STRING_WITH_LEN("PROCEDURE "));
+ /*
+ Let's use the same name as the event name to perhaps produce a
+ better error message in case it is a part of some parse error.
+ We're using append_identifier here to successfully parse
+ events with reserved names.
+ */
+ append_identifier(thd, sp_sql, name.str, name.length);
+
+ /*
+ The default SQL security of a stored procedure is DEFINER. We
+ have already activated the security context of the event, so
+ let's execute the procedure with the invoker rights to save on
+ resets of security contexts.
+ */
+ sp_sql->append(C_STRING_WITH_LEN("() SQL SECURITY INVOKER "));
+
+ sp_sql->append(body.str, body.length);
+
+ DBUG_RETURN(thd->is_fatal_error);
+}
+
+
+/**
+ Get DROP EVENT statement to binlog the drop of ON COMPLETION NOT
+ PRESERVE event.
*/
-int
-Event_job_data::get_fake_create_event(String *buf)
+bool
+Event_job_data::construct_drop_event_sql(THD *thd, String *sp_sql)
{
- DBUG_ENTER("Event_job_data::get_create_event");
- /* FIXME: "EVERY 3337 HOUR" is asking for trouble. */
- buf->append(STRING_WITH_LEN("CREATE EVENT anonymous ON SCHEDULE "
- "EVERY 3337 HOUR DO "));
- buf->append(body.str, body.length);
+ LEX_STRING buffer;
+ const uint STATIC_SQL_LENGTH= 14;
- DBUG_RETURN(0);
-}
+ DBUG_ENTER("Event_job_data::construct_drop_event_sql");
+ buffer.length= STATIC_SQL_LENGTH + name.length*2 + dbname.length*2;
+ if (! (buffer.str= (char*) thd->alloc(buffer.length)))
+ DBUG_RETURN(TRUE);
-/*
- Executes the event (the underlying sp_head object);
+ sp_sql->set(buffer.str, buffer.length, system_charset_info);
+ sp_sql->length(0);
- SYNOPSIS
- Event_job_data::execute()
- thd THD
+ sp_sql->append(C_STRING_WITH_LEN("DROP EVENT "));
+ append_identifier(thd, sp_sql, dbname.str, dbname.length);
+ sp_sql->append('.');
+ append_identifier(thd, sp_sql, name.str, name.length);
- RETURN VALUE
- 0 success
- -99 No rights on this.dbname.str
- others retcodes of sp_head::execute_procedure()
+ DBUG_RETURN(thd->is_fatal_error);
+}
+
+/**
+ Compiles and executes the event (the underlying sp_head object)
+
+ @retval TRUE error (reported to the error log)
+ @retval FALSE success
*/
-int
+bool
Event_job_data::execute(THD *thd, bool drop)
{
- Security_context save_ctx;
- /* this one is local and not needed after exec */
- int ret= 0;
+ String sp_sql;
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ Security_context event_sctx, *save_sctx= NULL;
+#endif
+ CHARSET_INFO *charset_connection;
+ List<Item> empty_item_list;
+ bool ret= TRUE;
DBUG_ENTER("Event_job_data::execute");
- DBUG_PRINT("info", ("EXECUTING %s.%s", dbname.str, name.str));
- if ((ret= compile(thd, NULL)))
- goto done;
+ mysql_reset_thd_for_next_command(thd);
- event_change_security_context(thd, definer_user, definer_host, dbname,
- &save_ctx);
/*
- THD::~THD will clean this or if there is DROP DATABASE in the
- SP then it will be free there. It should not point to our buffer
- which is allocated on a mem_root.
+ MySQL parser currently assumes that current database is either
+ present in THD or all names in all statements are fully specified.
+ And yet not fully specified names inside stored programs must be
+ be supported, even if the current database is not set:
+ CREATE PROCEDURE db1.p1() BEGIN CREATE TABLE t1; END//
+ -- in this example t1 should be always created in db1 and the statement
+ must parse even if there is no current database.
+
+ To support this feature and still address the parser limitation,
+ we need to set the current database here.
+ We don't have to call mysql_change_db, since the checks performed
+ in it are unnecessary for the purpose of parsing, and
+ mysql_change_db will be invoked anyway later, to activate the
+ procedure database before it's executed.
*/
- thd->db= my_strdup(dbname.str, MYF(0));
- thd->db_length= dbname.length;
- if (!check_access(thd, EVENT_ACL,dbname.str, 0, 0, 0,is_schema_db(dbname.str)))
- {
- List<Item> empty_item_list;
- empty_item_list.empty();
- if (thd->enable_slow_log)
- sphead->m_flags|= sp_head::LOG_SLOW_STATEMENTS;
- sphead->m_flags|= sp_head::LOG_GENERAL_LOG;
-
- /* Execute the event in its time zone. */
- thd->variables.time_zone= time_zone;
+ thd->set_db(dbname.str, dbname.length);
- ret= sphead->execute_procedure(thd, &empty_item_list);
- }
- else
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ if (event_sctx.change_security_context(thd,
+ &definer_user, &definer_host,
+ &dbname, &save_sctx))
{
- DBUG_PRINT("error", ("%s@%s has no rights on %s", definer_user.str,
- definer_host.str, dbname.str));
- ret= -99;
+ sql_print_error("Event Scheduler: "
+ "[%s].[%s.%s] execution failed, "
+ "failed to authenticate the user.",
+ definer.str, dbname.str, name.str);
+ goto end;
}
- if (drop)
+#endif
+
+ if (check_access(thd, EVENT_ACL, dbname.str,
+ 0, 0, 0, is_schema_db(dbname.str)))
{
- sql_print_information("Event Scheduler: Dropping %s.%s",
- dbname.str, name.str);
/*
- We must do it here since here we're under the right authentication
- ID of the event definer
+ This aspect of behavior is defined in the worklog,
+ and this is how triggers work too: if TRIGGER
+ privilege is revoked from trigger definer,
+ triggers are not executed.
*/
- if (Events::drop_event(thd, dbname, name, FALSE))
- ret= 1;
+ sql_print_error("Event Scheduler: "
+ "[%s].[%s.%s] execution failed, "
+ "user no longer has EVENT privilege.",
+ definer.str, dbname.str, name.str);
+ goto end;
}
- event_restore_security_context(thd, &save_ctx);
-done:
- thd->end_statement();
- thd->cleanup_after_query();
-
- DBUG_PRINT("info", ("EXECUTED %s.%s ret: %d", dbname.str, name.str, ret));
-
- DBUG_RETURN(ret);
-}
-
+ if (construct_sp_sql(thd, &sp_sql))
+ goto end;
-/*
- Compiles an event before it's execution. Compiles the anonymous
- sp_head object held by the event
-
- SYNOPSIS
- Event_job_data::compile()
- thd thread context, used for memory allocation mostly
- mem_root if != NULL then this memory root is used for allocs
- instead of thd->mem_root
-
- RETURN VALUE
- 0 success
- EVEX_COMPILE_ERROR error during compilation
- EVEX_MICROSECOND_UNSUP mysql.event was tampered
-*/
-
-int
-Event_job_data::compile(THD *thd, MEM_ROOT *mem_root)
-{
- int ret= 0;
- MEM_ROOT *tmp_mem_root= 0;
- LEX *old_lex= thd->lex, lex;
- char *old_db;
- int old_db_length;
- char *old_query;
- uint old_query_len;
- ulong old_sql_mode= thd->variables.sql_mode;
- char create_buf[15 * STRING_BUFFER_USUAL_SIZE];
- String show_create(create_buf, sizeof(create_buf), system_charset_info);
- CHARSET_INFO *old_character_set_client,
- *old_collation_connection,
- *old_character_set_results;
- Security_context save_ctx;
-
- DBUG_ENTER("Event_job_data::compile");
-
- show_create.length(0);
-
- switch (get_fake_create_event(&show_create)) {
- case EVEX_MICROSECOND_UNSUP:
- DBUG_RETURN(EVEX_MICROSECOND_UNSUP);
- case 0:
- break;
- default:
- DBUG_ASSERT(0);
- }
+ /*
+ Set up global thread attributes to reflect the properties of
+ this Event. We can simply reset these instead of usual
+ backup/restore employed in stored programs since we know that
+ this is a top level statement and the worker thread is
+ allocated exclusively to execute this event.
+ */
+ charset_connection= get_charset_by_csname("utf8",
+ MY_CS_PRIMARY, MYF(MY_WME));
+ thd->variables.character_set_client= charset_connection;
+ thd->variables.character_set_results= charset_connection;
+ thd->variables.collation_connection= charset_connection;
+ thd->update_charset();
- old_character_set_client= thd->variables.character_set_client;
- old_character_set_results= thd->variables.character_set_results;
- old_collation_connection= thd->variables.collation_connection;
+ thd->variables.sql_mode= sql_mode;
+ thd->variables.time_zone= time_zone;
- thd->variables.character_set_client=
- thd->variables.character_set_results=
- thd->variables.collation_connection=
- get_charset_by_csname("utf8", MY_CS_PRIMARY, MYF(MY_WME));
+ thd->query= sp_sql.c_ptr_safe();
+ thd->query_length= sp_sql.length();
- thd->update_charset();
+ lex_start(thd, thd->query, thd->query_length);
- DBUG_PRINT("info",("old_sql_mode: %lu new_sql_mode: %lu",old_sql_mode, sql_mode));
- thd->variables.sql_mode= this->sql_mode;
- /* Change the memory root for the execution time */
- if (mem_root)
- {
- tmp_mem_root= thd->mem_root;
- thd->mem_root= mem_root;
- }
- old_query_len= thd->query_length;
- old_query= thd->query;
- old_db= thd->db;
- old_db_length= thd->db_length;
- thd->db= dbname.str;
- thd->db_length= dbname.length;
-
- thd->query= show_create.c_ptr_safe();
- thd->query_length= show_create.length();
- DBUG_PRINT("info", ("query: %s",thd->query));
-
- event_change_security_context(thd, definer_user, definer_host, dbname,
- &save_ctx);
- thd->lex= &lex;
- mysql_init_query(thd, thd->query, thd->query_length);
- if (MYSQLparse((void *)thd) || thd->is_fatal_error)
+ if (MYSQLparse(thd) || thd->is_fatal_error)
{
- DBUG_PRINT("error", ("error during compile or thd->is_fatal_error: %d",
- thd->is_fatal_error));
- lex.unit.cleanup();
-
sql_print_error("Event Scheduler: "
"%serror during compilation of %s.%s",
thd->is_fatal_error ? "fatal " : "",
- dbname.str, name.str);
-
- ret= EVEX_COMPILE_ERROR;
- goto done;
+ (const char *) dbname.str, (const char *) name.str);
+ goto end;
}
- DBUG_PRINT("note", ("success compiling %s.%s", dbname.str, name.str));
- sphead= lex.sphead;
+ {
+ sp_head *sphead= thd->lex->sphead;
- sphead->set_definer(definer.str, definer.length);
- sphead->set_info(0, 0, &lex.sp_chistics, sql_mode);
- sphead->optimize();
- ret= 0;
-done:
+ DBUG_ASSERT(sphead);
- lex_end(&lex);
- event_restore_security_context(thd, &save_ctx);
- DBUG_PRINT("note", ("return old data on its place. set back NAMES"));
+ if (thd->enable_slow_log)
+ sphead->m_flags|= sp_head::LOG_SLOW_STATEMENTS;
+ sphead->m_flags|= sp_head::LOG_GENERAL_LOG;
- thd->lex= old_lex;
- thd->query= old_query;
- thd->query_length= old_query_len;
- thd->db= old_db;
+ sphead->set_info(0, 0, &thd->lex->sp_chistics, sql_mode);
+ sphead->optimize();
- thd->variables.sql_mode= old_sql_mode;
- thd->variables.character_set_client= old_character_set_client;
- thd->variables.character_set_results= old_character_set_results;
- thd->variables.collation_connection= old_collation_connection;
- thd->update_charset();
+ ret= sphead->execute_procedure(thd, &empty_item_list);
+ /*
+ There is no pre-locking and therefore there should be no
+ tables open and locked left after execute_procedure.
+ */
+ }
+
+end:
+ if (drop && !thd->is_fatal_error)
+ {
+ /*
+ We must do it here since here we're under the right authentication
+ ID of the event definer.
+ */
+ sql_print_information("Event Scheduler: Dropping %s.%s",
+ (const char *) dbname.str, (const char *) name.str);
+ /*
+ Construct a query for the binary log, to ensure the event is dropped
+ on the slave
+ */
+ if (construct_drop_event_sql(thd, &sp_sql))
+ ret= 1;
+ else
+ {
+ thd->query= sp_sql.c_ptr_safe();
+ thd->query_length= sp_sql.length();
+ if (Events::drop_event(thd, dbname, name, FALSE))
+ ret= 1;
+ }
+ }
+ if (thd->lex->sphead) /* NULL only if a parse error */
+ {
+ delete thd->lex->sphead;
+ thd->lex->sphead= NULL;
+ }
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ if (save_sctx)
+ event_sctx.restore_security_context(thd, save_sctx);
+#endif
+ lex_end(thd->lex);
+ thd->lex->unit.cleanup();
+ thd->end_statement();
+ thd->cleanup_after_query();
- /* Change the memory root for the execution time. */
- if (mem_root)
- thd->mem_root= tmp_mem_root;
+ DBUG_PRINT("info", ("EXECUTED %s.%s ret: %d", dbname.str, name.str, ret));
DBUG_RETURN(ret);
}
@@ -2042,64 +2025,3 @@ event_basic_identifier_equal(LEX_STRING db, LEX_STRING name, Event_basic *b)
return !sortcmp_lex_string(name, b->name, system_charset_info) &&
!sortcmp_lex_string(db, b->dbname, system_charset_info);
}
-
-
-/*
- Switches the security context.
-
- SYNOPSIS
- event_change_security_context()
- thd Thread
- user The user
- host The host of the user
- db The schema for which the security_ctx will be loaded
- backup Where to store the old context
-
- RETURN VALUE
- FALSE OK
- TRUE Error (generates error too)
-*/
-
-static bool
-event_change_security_context(THD *thd, LEX_STRING user, LEX_STRING host,
- LEX_STRING db, Security_context *backup)
-{
- DBUG_ENTER("event_change_security_context");
- DBUG_PRINT("info",("%s@%s@%s", user.str, host.str, db.str));
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
-
- *backup= thd->main_security_ctx;
- if (acl_getroot_no_password(&thd->main_security_ctx, user.str, host.str,
- host.str, db.str))
- {
- my_error(ER_NO_SUCH_USER, MYF(0), user.str, host.str);
- DBUG_RETURN(TRUE);
- }
- thd->security_ctx= &thd->main_security_ctx;
-#endif
- DBUG_RETURN(FALSE);
-}
-
-
-/*
- Restores the security context.
-
- SYNOPSIS
- event_restore_security_context()
- thd Thread
- backup Context to switch to
-*/
-
-static void
-event_restore_security_context(THD *thd, Security_context *backup)
-{
- DBUG_ENTER("event_restore_security_context");
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
- if (backup)
- {
- thd->main_security_ctx= *backup;
- thd->security_ctx= &thd->main_security_ctx;
- }
-#endif
- DBUG_VOID_RETURN;
-}