diff options
-rw-r--r-- | client/mysqldump.c | 16 | ||||
-rw-r--r-- | mysql-test/r/rpl_events.result | 2 | ||||
-rw-r--r-- | mysql-test/r/sp.result | 90 | ||||
-rw-r--r-- | mysql-test/t/mysqldump.test | 7 | ||||
-rw-r--r-- | mysql-test/t/sp.test | 38 | ||||
-rw-r--r-- | sql/event_data_objects.cc | 464 | ||||
-rw-r--r-- | sql/event_data_objects.h | 15 | ||||
-rw-r--r-- | sql/event_scheduler.cc | 54 | ||||
-rw-r--r-- | sql/events.cc | 9 | ||||
-rw-r--r-- | sql/item_func.cc | 2 | ||||
-rw-r--r-- | sql/sp_head.cc | 50 | ||||
-rw-r--r-- | sql/sql_class.cc | 96 | ||||
-rw-r--r-- | sql/sql_class.h | 12 | ||||
-rw-r--r-- | sql/sql_trigger.cc | 15 |
14 files changed, 457 insertions, 413 deletions
diff --git a/client/mysqldump.c b/client/mysqldump.c index 1506fb0bf19..d556e0136df 100644 --- a/client/mysqldump.c +++ b/client/mysqldump.c @@ -830,11 +830,15 @@ static int get_options(int *argc, char ***argv) (hash_get_key) get_table_key, (hash_free_key) free_table_ent, 0)) return(EX_EOM); - /* Don't copy cluster internal log tables */ + /* Don't copy internal log tables */ if (my_hash_insert(&ignore_table, (byte*) my_strdup("mysql.apply_status", MYF(MY_WME))) || my_hash_insert(&ignore_table, - (byte*) my_strdup("mysql.schema", MYF(MY_WME)))) + (byte*) my_strdup("mysql.schema", MYF(MY_WME))) || + my_hash_insert(&ignore_table, + (byte*) my_strdup("mysql.general_log", MYF(MY_WME))) || + my_hash_insert(&ignore_table, + (byte*) my_strdup("mysql.slow_log", MYF(MY_WME)))) return(EX_EOM); if ((ho_error= handle_options(argc, argv, my_long_options, get_one_option))) @@ -3354,8 +3358,12 @@ static int dump_all_tables_in_db(char *database) init_dynamic_string_checked(&query, "LOCK TABLES ", 256, 1024); for (numrows= 0 ; (table= getTableName(1)) ; numrows++) { - dynstr_append_checked(&query, quote_name(table, table_buff, 1)); - dynstr_append_checked(&query, " READ /*!32311 LOCAL */,"); + char *end= strmov(afterdot, table); + if (include_table(hash_key,end - hash_key)) + { + dynstr_append_checked(&query, quote_name(table, table_buff, 1)); + dynstr_append_checked(&query, " READ /*!32311 LOCAL */,"); + } } if (numrows && mysql_real_query(mysql, query.str, query.length-1)) DB_error(mysql, "when using LOCK TABLES"); diff --git a/mysql-test/r/rpl_events.result b/mysql-test/r/rpl_events.result index bff1a814a6d..debfcce0072 100644 --- a/mysql-test/r/rpl_events.result +++ b/mysql-test/r/rpl_events.result @@ -34,7 +34,6 @@ id c ts affected rows: 2 SELECT db, name, status, originator FROM mysql.event WHERE db = 'test' AND name = 'justonce'; db name status originator -test justonce SLAVESIDE_DISABLED 1 DROP EVENT IF EXISTS test.slave_once; CREATE EVENT test.slave_once ON SCHEDULE EVERY 5 MINUTE DO INSERT INTO t1(c) VALUES ('from slave_once'); @@ -111,7 +110,6 @@ id c ts affected rows: 2 SELECT db, name, status, originator FROM mysql.event WHERE db = 'test' AND name = 'justonce'; db name status originator -test justonce SLAVESIDE_DISABLED 1 DROP EVENT IF EXISTS test.slave_once; CREATE EVENT test.slave_once ON SCHEDULE EVERY 5 MINUTE DO INSERT INTO t1(c) VALUES ('from slave_once'); diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index 44abc11029a..1134f1f561a 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -6061,21 +6061,6 @@ SUM(f2) bug25373(f1) 21.300000071526 NULL DROP FUNCTION bug25373| DROP TABLE t3| -DROP DATABASE IF EXISTS mysqltest1| -DROP DATABASE IF EXISTS mysqltest2| -CREATE DATABASE mysqltest1| -CREATE DATABASE mysqltest2| -CREATE PROCEDURE mysqltest1.p1() -DROP DATABASE mysqltest2| -use mysqltest2| -CALL mysqltest1.p1()| -Warnings: -Note 1049 Unknown database 'mysqltest2' -SELECT DATABASE()| -DATABASE() -NULL -DROP DATABASE mysqltest1| -use test| drop function if exists bug20777| drop table if exists examplebug20777| create function bug20777(f1 bigint unsigned) returns bigint unsigned @@ -6160,30 +6145,55 @@ select bug20777(18446744073709551613)+1; bug20777(18446744073709551613)+1 18446744073709551614 drop function bug20777; +DROP FUNCTION IF EXISTS bug5274_f1| +DROP FUNCTION IF EXISTS bug5274_f2| +CREATE FUNCTION bug5274_f1(p1 CHAR) RETURNS CHAR +RETURN CONCAT(p1, p1)| +CREATE FUNCTION bug5274_f2() RETURNS CHAR +BEGIN +DECLARE v1 INT DEFAULT 0; +DECLARE v2 CHAR DEFAULT 'x'; +WHILE v1 < 30 DO +SET v1 = v1 + 1; +SET v2 = bug5274_f1(v2); +END WHILE; +RETURN v2; +END| +SELECT bug5274_f2()| +bug5274_f2() +x +Warnings: +Warning 1265 Data truncated for column 'bug5274_f1' at row 1 +Warning 1265 Data truncated for column 'bug5274_f1' at row 1 +Warning 1265 Data truncated for column 'bug5274_f1' at row 1 +Warning 1265 Data truncated for column 'bug5274_f1' at row 1 +Warning 1265 Data truncated for column 'bug5274_f1' at row 1 +Warning 1265 Data truncated for column 'bug5274_f1' at row 1 +Warning 1265 Data truncated for column 'bug5274_f1' at row 1 +Warning 1265 Data truncated for column 'bug5274_f1' at row 1 +Warning 1265 Data truncated for column 'bug5274_f1' at row 1 +Warning 1265 Data truncated for column 'bug5274_f1' at row 1 +Warning 1265 Data truncated for column 'bug5274_f1' at row 1 +Warning 1265 Data truncated for column 'bug5274_f1' at row 1 +Warning 1265 Data truncated for column 'bug5274_f1' at row 1 +Warning 1265 Data truncated for column 'bug5274_f1' at row 1 +Warning 1265 Data truncated for column 'bug5274_f1' at row 1 +Warning 1265 Data truncated for column 'bug5274_f1' at row 1 +Warning 1265 Data truncated for column 'bug5274_f1' at row 1 +Warning 1265 Data truncated for column 'bug5274_f1' at row 1 +Warning 1265 Data truncated for column 'bug5274_f1' at row 1 +Warning 1265 Data truncated for column 'bug5274_f1' at row 1 +Warning 1265 Data truncated for column 'bug5274_f1' at row 1 +Warning 1265 Data truncated for column 'bug5274_f1' at row 1 +Warning 1265 Data truncated for column 'bug5274_f1' at row 1 +Warning 1265 Data truncated for column 'bug5274_f1' at row 1 +Warning 1265 Data truncated for column 'bug5274_f1' at row 1 +Warning 1265 Data truncated for column 'bug5274_f1' at row 1 +Warning 1265 Data truncated for column 'bug5274_f1' at row 1 +Warning 1265 Data truncated for column 'bug5274_f1' at row 1 +Warning 1265 Data truncated for column 'bug5274_f1' at row 1 +Warning 1265 Data truncated for column 'bug5274_f1' at row 1 +DROP FUNCTION bug5274_f1| +DROP FUNCTION bug5274_f2| End of 5.0 tests. drop table t1,t2; -CREATE TABLE t1 (a int auto_increment primary key) engine=MyISAM; -CREATE TABLE t2 (a int auto_increment primary key, b int) engine=innodb; -set @a=0; -CREATE function bug27354() RETURNS int deterministic -begin -insert into t1 values (null); -set @a=@a+1; -return @a; -end| -update t2 set b=1 where a=bug27354(); -select count(t_1.a),count(t_2.a) from t1 as t_1, t2 as t_2 /* must be 0,0 */; -count(t_1.a) count(t_2.a) -0 0 -insert into t2 values (1,1),(2,2),(3,3); -update t2 set b=-b where a=bug27354(); -select * from t2 /* must return 1,-1 ... */; -a b -1 -1 -2 -2 -3 -3 -select count(*) from t1 /* must be 3 */; -count(*) -3 -drop table t1,t2; -drop function bug27354; diff --git a/mysql-test/t/mysqldump.test b/mysql-test/t/mysqldump.test index 49adda73f2d..ba19fa30663 100644 --- a/mysql-test/t/mysqldump.test +++ b/mysql-test/t/mysqldump.test @@ -1612,6 +1612,13 @@ drop view v1; drop table t1; drop database mysqldump_test_db; +# +# BUG#26121 mysqldump includes LOCK TABLES general_log WRITE +# +--exec $MYSQL_DUMP --all-databases > $MYSQLTEST_VARDIR/tmp/bug26121.sql +--exec $MYSQL < $MYSQLTEST_VARDIR/tmp/bug26121.sql +--remove_file $MYSQLTEST_VARDIR/tmp/bug26121.sql + --echo # --echo # End of 5.1 tests --echo # diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index 591677cd834..626a963e340 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -7109,6 +7109,44 @@ select bug20777(18446744073709551613)+1; drop function bug20777; delimiter |; + +# +# BUG#5274: Stored procedure crash if length of CHAR variable too great. +# + +# Prepare. + +--disable_warnings +DROP FUNCTION IF EXISTS bug5274_f1| +DROP FUNCTION IF EXISTS bug5274_f2| +--enable_warnings + +# Test. + +CREATE FUNCTION bug5274_f1(p1 CHAR) RETURNS CHAR + RETURN CONCAT(p1, p1)| + +CREATE FUNCTION bug5274_f2() RETURNS CHAR +BEGIN + DECLARE v1 INT DEFAULT 0; + DECLARE v2 CHAR DEFAULT 'x'; + + WHILE v1 < 30 DO + SET v1 = v1 + 1; + SET v2 = bug5274_f1(v2); + END WHILE; + + RETURN v2; +END| + +SELECT bug5274_f2()| + +# Cleanup. + +DROP FUNCTION bug5274_f1| +DROP FUNCTION bug5274_f2| + + ### --echo End of 5.0 tests. 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; -} diff --git a/sql/event_data_objects.h b/sql/event_data_objects.h index eb851c98065..8e03ab19602 100644 --- a/sql/event_data_objects.h +++ b/sql/event_data_objects.h @@ -17,7 +17,6 @@ #define EVEX_GET_FIELD_FAILED -2 -#define EVEX_COMPILE_ERROR -3 #define EVEX_BAD_PARAMS -5 #define EVEX_MICROSECOND_UNSUP -6 @@ -169,8 +168,6 @@ public: class Event_job_data : public Event_basic { public: - sp_head *sphead; - LEX_STRING body; LEX_STRING definer_user; LEX_STRING definer_host; @@ -178,19 +175,17 @@ public: ulong sql_mode; Event_job_data(); - virtual ~Event_job_data(); virtual int load_from_row(THD *thd, TABLE *table); - int + bool execute(THD *thd, bool drop); - - int - compile(THD *thd, MEM_ROOT *mem_root); private: - int - get_fake_create_event(String *buf); + bool + construct_sp_sql(THD *thd, String *sp_sql); + bool + construct_drop_event_sql(THD *thd, String *sp_sql); Event_job_data(const Event_job_data &); /* Prevent use of these */ void operator=(Event_job_data &); diff --git a/sql/event_scheduler.cc b/sql/event_scheduler.cc index 0603a299079..5844fecc227 100644 --- a/sql/event_scheduler.cc +++ b/sql/event_scheduler.cc @@ -277,8 +277,7 @@ Event_worker_thread::run(THD *thd, Event_queue_element_for_exec *event) { /* needs to be first for thread_stack */ char my_stack; - int ret; - Event_job_data *job_data= NULL; + Event_job_data job_data; bool res; thd->thread_stack= &my_stack; // remember where our stack is @@ -291,60 +290,43 @@ Event_worker_thread::run(THD *thd, Event_queue_element_for_exec *event) if (res) goto end; - if (!(job_data= new Event_job_data())) - goto end; - else if ((ret= db_repository-> - load_named_event(thd, event->dbname, event->name, job_data))) + if ((res= db_repository->load_named_event(thd, event->dbname, event->name, + &job_data))) { - DBUG_PRINT("error", ("Got %d from load_named_event", ret)); + DBUG_PRINT("error", ("Got error from load_named_event")); goto end; } sql_print_information("Event Scheduler: " - "[%s.%s of %s] executing in thread %lu. ", - job_data->dbname.str, job_data->name.str, - job_data->definer.str, thd->thread_id); + "[%s].[%s.%s] started in thread %lu.", + job_data.definer.str, + job_data.dbname.str, job_data.name.str, + thd->thread_id); thd->enable_slow_log= TRUE; - ret= job_data->execute(thd, event->dropped); + res= job_data.execute(thd, event->dropped); - print_warnings(thd, job_data); + print_warnings(thd, &job_data); - switch (ret) { - case 0: + if (res) + sql_print_information("Event Scheduler: " + "[%s].[%s.%s] event execution failed.", + job_data.definer.str, + job_data.dbname.str, job_data.name.str); + else sql_print_information("Event Scheduler: " "[%s].[%s.%s] executed successfully in thread %lu.", - job_data->definer.str, - job_data->dbname.str, job_data->name.str, + job_data.definer.str, + job_data.dbname.str, job_data.name.str, 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; - DBUG_PRINT("info", ("Done with Event %s.%s", event->dbname.str, event->name.str)); delete event; deinit_event_thread(thd); - /* - Do not pthread_exit since we want local destructors for stack objects - to be invoked. - */ } diff --git a/sql/events.cc b/sql/events.cc index 7a6323a9a63..7838972a5d6 100644 --- a/sql/events.cc +++ b/sql/events.cc @@ -424,7 +424,8 @@ Events::create_event(THD *thd, Event_parse_data *parse_data, 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)) + DBUG_ASSERT(thd->query && thd->query_length); + if (mysql_bin_log.is_open()) { thd->clear_error(); thd->binlog_query(THD::MYSQL_QUERY_TYPE, @@ -549,7 +550,8 @@ Events::update_event(THD *thd, Event_parse_data *parse_data, 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)) + DBUG_ASSERT(thd->query && thd->query_length); + if (mysql_bin_log.is_open()) { thd->clear_error(); thd->binlog_query(THD::MYSQL_QUERY_TYPE, @@ -628,7 +630,8 @@ Events::drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name, bool if_exists) if (event_queue) event_queue->drop_event(thd, dbname, name); /* Binlog the drop event. */ - if (mysql_bin_log.is_open() && (thd->query_length > 0)) + DBUG_ASSERT(thd->query && thd->query_length); + if (mysql_bin_log.is_open()) { thd->clear_error(); thd->binlog_query(THD::MYSQL_QUERY_TYPE, diff --git a/sql/item_func.cc b/sql/item_func.cc index c4c35a990fb..dfddbba70f4 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -5352,7 +5352,7 @@ Item_func_sp::fix_fields(THD *thd, Item **ref) Security_context *save_secutiry_ctx; res= set_routine_security_ctx(thd, m_sp, false, &save_secutiry_ctx); if (!res) - sp_restore_security_context(thd, save_secutiry_ctx); + m_sp->m_security_ctx.restore_security_context(thd, save_secutiry_ctx); #endif /* ! NO_EMBEDDED_ACCESS_CHECKS */ } diff --git a/sql/sp_head.cc b/sql/sp_head.cc index a2ce5111a25..04a7b2574a4 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -1245,7 +1245,11 @@ set_routine_security_ctx(THD *thd, sp_head *sp, bool is_proc, Security_context **save_ctx) { *save_ctx= 0; - if (sp_change_security_context(thd, sp, save_ctx)) + if (sp->m_chistics->suid != SP_IS_NOT_SUID && + sp->m_security_ctx.change_security_context(thd, &sp->m_definer_user, + &sp->m_definer_host, + &sp->m_db, + save_ctx)) return TRUE; /* @@ -1262,7 +1266,7 @@ set_routine_security_ctx(THD *thd, sp_head *sp, bool is_proc, check_routine_access(thd, EXECUTE_ACL, sp->m_db.str, sp->m_name.str, is_proc, FALSE)) { - sp_restore_security_context(thd, *save_ctx); + sp->m_security_ctx.restore_security_context(thd, *save_ctx); *save_ctx= 0; return TRUE; } @@ -1573,7 +1577,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, } #ifndef NO_EMBEDDED_ACCESS_CHECKS - sp_restore_security_context(thd, save_security_ctx); + m_security_ctx.restore_security_context(thd, save_security_ctx); #endif err_with_cleanup: @@ -1791,7 +1795,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) #ifndef NO_EMBEDDED_ACCESS_CHECKS if (save_security_ctx) - sp_restore_security_context(thd, save_security_ctx); + m_security_ctx.restore_security_context(thd, save_security_ctx); #endif if (!save_spcont) @@ -3431,44 +3435,6 @@ sp_instr_set_case_expr::opt_move(uint dst, List<sp_instr> *bp) /* ------------------------------------------------------------------ */ -/* - Security context swapping -*/ - -#ifndef NO_EMBEDDED_ACCESS_CHECKS -bool -sp_change_security_context(THD *thd, sp_head *sp, Security_context **backup) -{ - *backup= 0; - if (sp->m_chistics->suid != SP_IS_NOT_SUID && - (strcmp(sp->m_definer_user.str, - thd->security_ctx->priv_user) || - my_strcasecmp(system_charset_info, sp->m_definer_host.str, - thd->security_ctx->priv_host))) - { - if (acl_getroot_no_password(&sp->m_security_ctx, sp->m_definer_user.str, - sp->m_definer_host.str, - sp->m_definer_host.str, - sp->m_db.str)) - { - my_error(ER_NO_SUCH_USER, MYF(0), sp->m_definer_user.str, - sp->m_definer_host.str); - return TRUE; - } - *backup= thd->security_ctx; - thd->security_ctx= &sp->m_security_ctx; - } - return FALSE; -} - -void -sp_restore_security_context(THD *thd, Security_context *backup) -{ - if (backup) - thd->security_ctx= backup; -} - -#endif /* NO_EMBEDDED_ACCESS_CHECKS */ /* Structure that represent all instances of one table diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 4ee36f5864c..fd44817811e 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -2125,6 +2125,102 @@ bool Security_context::set_user(char *user_arg) return user == 0; } +#ifndef NO_EMBEDDED_ACCESS_CHECKS +/** + Initialize this security context from the passed in credentials + and activate it in the current thread. + + @param[out] backup Save a pointer to the current security context + in the thread. In case of success it points to the + saved old context, otherwise it points to NULL. + + + During execution of a statement, multiple security contexts may + be needed: + - the security context of the authenticated user, used as the + default security context for all top-level statements + - in case of a view or a stored program, possibly the security + context of the definer of the routine, if the object is + defined with SQL SECURITY DEFINER option. + + The currently "active" security context is parameterized in THD + member security_ctx. By default, after a connection is + established, this member points at the "main" security context + - the credentials of the authenticated user. + + Later, if we would like to execute some sub-statement or a part + of a statement under credentials of a different user, e.g. + definer of a procedure, we authenticate this user in a local + instance of Security_context by means of this method (and + ultimately by means of acl_getroot_no_password), and make the + local instance active in the thread by re-setting + thd->security_ctx pointer. + + Note, that the life cycle and memory management of the "main" and + temporary security contexts are different. + For the main security context, the memory for user/host/ip is + allocated on system heap, and the THD class frees this memory in + its destructor. The only case when contents of the main security + context may change during its life time is when someone issued + CHANGE USER command. + Memory management of a "temporary" security context is + responsibility of the module that creates it. + + @retval TRUE there is no user with the given credentials. The erro + is reported in the thread. + @retval FALSE success +*/ + +bool +Security_context:: +change_security_context(THD *thd, + LEX_STRING *definer_user, + LEX_STRING *definer_host, + LEX_STRING *db, + Security_context **backup) +{ + bool needs_change; + + DBUG_ENTER("Security_context::change_security_context"); + + DBUG_ASSERT(definer_user->str && definer_host->str); + + *backup= NULL; + /* + The current security context may have NULL members + if we have just started the thread and not authenticated + any user. This use case is currently in events worker thread. + */ + needs_change= (thd->security_ctx->priv_user == NULL || + strcmp(definer_user->str, thd->security_ctx->priv_user) || + thd->security_ctx->priv_host == NULL || + my_strcasecmp(system_charset_info, definer_host->str, + thd->security_ctx->priv_host)); + if (needs_change) + { + if (acl_getroot_no_password(this, definer_user->str, definer_host->str, + definer_host->str, db->str)) + { + my_error(ER_NO_SUCH_USER, MYF(0), definer_user->str, + definer_host->str); + DBUG_RETURN(TRUE); + } + *backup= thd->security_ctx; + thd->security_ctx= this; + } + + DBUG_RETURN(FALSE); +} + + +void +Security_context::restore_security_context(THD *thd, + Security_context *backup) +{ + if (backup) + thd->security_ctx= backup; +} +#endif /**************************************************************************** Handling of open and locked tables states. diff --git a/sql/sql_class.h b/sql/sql_class.h index 3d53c19633e..48a88087fd3 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -658,6 +658,18 @@ public: } bool set_user(char *user_arg); + +#ifndef NO_EMBEDDED_ACCESS_CHECKS + bool + change_security_context(THD *thd, + LEX_STRING *definer_user, + LEX_STRING *definer_host, + LEX_STRING *db, + Security_context **backup); + + void + restore_security_context(THD *thd, Security_context *backup); +#endif }; diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index 66132efb8e4..7b4deba527a 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -1543,9 +1543,16 @@ bool Table_triggers_list::process_triggers(THD *thd, trg_event_type event, old_field= trigger_table->field; } #ifndef NO_EMBEDDED_ACCESS_CHECKS - Security_context *save_ctx; + Security_context *sctx= &sp_trigger->m_security_ctx; + Security_context *save_ctx= NULL; - if (sp_change_security_context(thd, sp_trigger, &save_ctx)) + + if (sp_trigger->m_chistics->suid != SP_IS_NOT_SUID && + sctx->change_security_context(thd, + &sp_trigger->m_definer_user, + &sp_trigger->m_definer_host, + &sp_trigger->m_db, + &save_ctx)) return TRUE; /* @@ -1570,7 +1577,7 @@ bool Table_triggers_list::process_triggers(THD *thd, trg_event_type event, thd->security_ctx->priv_user, thd->security_ctx->host_or_ip, trigger_table->s->table_name.str); - sp_restore_security_context(thd, save_ctx); + sctx->restore_security_context(thd, save_ctx); return TRUE; } #endif // NO_EMBEDDED_ACCESS_CHECKS @@ -1582,7 +1589,7 @@ bool Table_triggers_list::process_triggers(THD *thd, trg_event_type event, thd->restore_sub_statement_state(&statement_state); #ifndef NO_EMBEDDED_ACCESS_CHECKS - sp_restore_security_context(thd, save_ctx); + sctx->restore_security_context(thd, save_ctx); #endif // NO_EMBEDDED_ACCESS_CHECKS } |