From 51ff281efe80797e307e9d25efaa1c7bebc17597 Mon Sep 17 00:00:00 2001 From: Dmitry Lenev Date: Thu, 7 Oct 2010 20:01:17 +0400 Subject: Fix for bug#57061 "User without privilege on routine can discover its existence". The problem was that user without any privileges on routine was able to find out whether it existed or not. DROP FUNCTION and DROP PROCEDURE statements were checking if routine being dropped existed and reported ER_SP_DOES_NOT_EXIST error/warning before checking if user had enough privileges to drop it. This patch solves this problem by changing code not to check if routine exists before checking if user has enough privileges to drop it. Moreover we no longer perform this check using a separate call instead we rely on sp_drop_routine() returning SP_KEY_NOT_FOUND if routine doesn't exist. This change also simplifies one of upcoming patches refactoring global read lock implementation. --- mysql-test/r/grant.result | 2 - mysql-test/r/sp-security.result | 29 +++++++++- .../suite/funcs_1/r/innodb_storedproc_06.result | 4 +- .../suite/funcs_1/r/memory_storedproc_06.result | 4 +- .../suite/funcs_1/r/myisam_storedproc_06.result | 4 +- .../suite/funcs_1/storedproc/storedproc_06.inc | 10 ++-- mysql-test/t/grant.test | 5 -- mysql-test/t/sp-security.test | 37 ++++++++++++- sql/sp.cc | 32 ----------- sql/sp.h | 3 - sql/sql_parse.cc | 64 +++++++++------------- 11 files changed, 100 insertions(+), 94 deletions(-) diff --git a/mysql-test/r/grant.result b/mysql-test/r/grant.result index 84cac386b57..37074e9a32f 100644 --- a/mysql-test/r/grant.result +++ b/mysql-test/r/grant.result @@ -1448,8 +1448,6 @@ CREATE USER 'userbug33464'@'localhost'; GRANT CREATE ROUTINE ON dbbug33464.* TO 'userbug33464'@'localhost'; userbug33464@localhost dbbug33464 -DROP PROCEDURE IF EXISTS sp3; -DROP FUNCTION IF EXISTS fn1; CREATE PROCEDURE sp3(v1 char(20)) BEGIN SELECT * from dbbug33464.t6 where t6.f2= 'xyz'; diff --git a/mysql-test/r/sp-security.result b/mysql-test/r/sp-security.result index 4ea26d1021a..c09579b13eb 100644 --- a/mysql-test/r/sp-security.result +++ b/mysql-test/r/sp-security.result @@ -44,7 +44,7 @@ ERROR 42000: SELECT command denied to user 'user1'@'localhost' for table 't1' create procedure db1_secret.dummy() begin end; ERROR 42000: Access denied for user 'user1'@'localhost' to database 'db1_secret' drop procedure db1_secret.dummy; -ERROR 42000: PROCEDURE db1_secret.dummy does not exist +ERROR 42000: alter routine command denied to user 'user1'@'localhost' for routine 'db1_secret.dummy' drop procedure db1_secret.stamp; ERROR 42000: alter routine command denied to user 'user1'@'localhost' for routine 'db1_secret.stamp' drop function db1_secret.db; @@ -58,7 +58,7 @@ ERROR 42000: SELECT command denied to user ''@'localhost' for table 't1' create procedure db1_secret.dummy() begin end; ERROR 42000: Access denied for user ''@'%' to database 'db1_secret' drop procedure db1_secret.dummy; -ERROR 42000: PROCEDURE db1_secret.dummy does not exist +ERROR 42000: alter routine command denied to user ''@'%' for routine 'db1_secret.dummy' drop procedure db1_secret.stamp; ERROR 42000: alter routine command denied to user ''@'%' for routine 'db1_secret.stamp' drop function db1_secret.db; @@ -567,3 +567,28 @@ DROP USER 'tester'; DROP USER 'Tester'; DROP DATABASE B48872; End of 5.0 tests. +# +# Test for bug#57061 "User without privilege on routine can discover +# its existence." +# +drop database if exists mysqltest_db; +create database mysqltest_db; +# Create user with no privileges on mysqltest_db database. +create user bug57061_user@localhost; +create function mysqltest_db.f1() returns int return 0; +create procedure mysqltest_db.p1() begin end; +# Connect as user 'bug57061_user@localhost' +# Attempt to drop routine on which user doesn't have privileges +# should result in the same 'access denied' type of error whether +# routine exists or not. +drop function if exists mysqltest_db.f_does_not_exist; +ERROR 42000: alter routine command denied to user 'bug57061_user'@'localhost' for routine 'mysqltest_db.f_does_not_exist' +drop procedure if exists mysqltest_db.p_does_not_exist; +ERROR 42000: alter routine command denied to user 'bug57061_user'@'localhost' for routine 'mysqltest_db.p_does_not_exist' +drop function if exists mysqltest_db.f1; +ERROR 42000: alter routine command denied to user 'bug57061_user'@'localhost' for routine 'mysqltest_db.f1' +drop procedure if exists mysqltest_db.p1; +ERROR 42000: alter routine command denied to user 'bug57061_user'@'localhost' for routine 'mysqltest_db.p1' +# Connection 'default'. +drop user bug57061_user@localhost; +drop database mysqltest_db; diff --git a/mysql-test/suite/funcs_1/r/innodb_storedproc_06.result b/mysql-test/suite/funcs_1/r/innodb_storedproc_06.result index ee1548fe012..f19030834c8 100644 --- a/mysql-test/suite/funcs_1/r/innodb_storedproc_06.result +++ b/mysql-test/suite/funcs_1/r/innodb_storedproc_06.result @@ -110,10 +110,10 @@ Ensure that root always has the GRANT CREATE ROUTINE privilege. -------------------------------------------------------------------------------- grant create routine on db_storedproc_1.* to 'user_1'@'localhost'; flush privileges; +DROP PROCEDURE IF EXISTS db_storedproc_1.sp3; +DROP FUNCTION IF EXISTS db_storedproc_1.fn1; user_1@localhost db_storedproc_1 -DROP PROCEDURE IF EXISTS sp3; -DROP FUNCTION IF EXISTS fn1; CREATE PROCEDURE sp3(v1 char(20)) BEGIN SELECT * from db_storedproc_1.t6 where t6.f2= 'xyz'; diff --git a/mysql-test/suite/funcs_1/r/memory_storedproc_06.result b/mysql-test/suite/funcs_1/r/memory_storedproc_06.result index 096cbd1261e..0a117c830ee 100644 --- a/mysql-test/suite/funcs_1/r/memory_storedproc_06.result +++ b/mysql-test/suite/funcs_1/r/memory_storedproc_06.result @@ -111,10 +111,10 @@ Ensure that root always has the GRANT CREATE ROUTINE privilege. -------------------------------------------------------------------------------- grant create routine on db_storedproc_1.* to 'user_1'@'localhost'; flush privileges; +DROP PROCEDURE IF EXISTS db_storedproc_1.sp3; +DROP FUNCTION IF EXISTS db_storedproc_1.fn1; user_1@localhost db_storedproc_1 -DROP PROCEDURE IF EXISTS sp3; -DROP FUNCTION IF EXISTS fn1; CREATE PROCEDURE sp3(v1 char(20)) BEGIN SELECT * from db_storedproc_1.t6 where t6.f2= 'xyz'; diff --git a/mysql-test/suite/funcs_1/r/myisam_storedproc_06.result b/mysql-test/suite/funcs_1/r/myisam_storedproc_06.result index 096cbd1261e..0a117c830ee 100644 --- a/mysql-test/suite/funcs_1/r/myisam_storedproc_06.result +++ b/mysql-test/suite/funcs_1/r/myisam_storedproc_06.result @@ -111,10 +111,10 @@ Ensure that root always has the GRANT CREATE ROUTINE privilege. -------------------------------------------------------------------------------- grant create routine on db_storedproc_1.* to 'user_1'@'localhost'; flush privileges; +DROP PROCEDURE IF EXISTS db_storedproc_1.sp3; +DROP FUNCTION IF EXISTS db_storedproc_1.fn1; user_1@localhost db_storedproc_1 -DROP PROCEDURE IF EXISTS sp3; -DROP FUNCTION IF EXISTS fn1; CREATE PROCEDURE sp3(v1 char(20)) BEGIN SELECT * from db_storedproc_1.t6 where t6.f2= 'xyz'; diff --git a/mysql-test/suite/funcs_1/storedproc/storedproc_06.inc b/mysql-test/suite/funcs_1/storedproc/storedproc_06.inc index 4ecca63351d..0695a0724d8 100644 --- a/mysql-test/suite/funcs_1/storedproc/storedproc_06.inc +++ b/mysql-test/suite/funcs_1/storedproc/storedproc_06.inc @@ -117,15 +117,15 @@ create user 'user_1'@'localhost'; grant create routine on db_storedproc_1.* to 'user_1'@'localhost'; flush privileges; +--disable_warnings +DROP PROCEDURE IF EXISTS db_storedproc_1.sp3; +DROP FUNCTION IF EXISTS db_storedproc_1.fn1; +--enable_warnings + # disconnect default; connect (user2, localhost, user_1, , db_storedproc_1); --source suite/funcs_1/include/show_connection.inc ---disable_warnings -DROP PROCEDURE IF EXISTS sp3; -DROP FUNCTION IF EXISTS fn1; ---enable_warnings - delimiter //; CREATE PROCEDURE sp3(v1 char(20)) BEGIN diff --git a/mysql-test/t/grant.test b/mysql-test/t/grant.test index e73f45a6c53..aad0c42a5b3 100644 --- a/mysql-test/t/grant.test +++ b/mysql-test/t/grant.test @@ -1419,11 +1419,6 @@ GRANT CREATE ROUTINE ON dbbug33464.* TO 'userbug33464'@'localhost'; connect (connbug33464, localhost, userbug33464, , dbbug33464); --source suite/funcs_1/include/show_connection.inc ---disable_warnings -DROP PROCEDURE IF EXISTS sp3; -DROP FUNCTION IF EXISTS fn1; ---enable_warnings - delimiter //; CREATE PROCEDURE sp3(v1 char(20)) BEGIN diff --git a/mysql-test/t/sp-security.test b/mysql-test/t/sp-security.test index 96f82c92248..d7ea829bf50 100644 --- a/mysql-test/t/sp-security.test +++ b/mysql-test/t/sp-security.test @@ -82,7 +82,7 @@ select * from db1_secret.t1; # ...and not this --error ER_DBACCESS_DENIED_ERROR create procedure db1_secret.dummy() begin end; ---error ER_SP_DOES_NOT_EXIST +--error ER_PROCACCESS_DENIED_ERROR drop procedure db1_secret.dummy; --error ER_PROCACCESS_DENIED_ERROR drop procedure db1_secret.stamp; @@ -106,7 +106,7 @@ select * from db1_secret.t1; # ...and not this --error ER_DBACCESS_DENIED_ERROR create procedure db1_secret.dummy() begin end; ---error ER_SP_DOES_NOT_EXIST +--error ER_PROCACCESS_DENIED_ERROR drop procedure db1_secret.dummy; --error ER_PROCACCESS_DENIED_ERROR drop procedure db1_secret.stamp; @@ -926,6 +926,39 @@ DROP DATABASE B48872; --echo End of 5.0 tests. + +--echo # +--echo # Test for bug#57061 "User without privilege on routine can discover +--echo # its existence." +--echo # +--disable_warnings +drop database if exists mysqltest_db; +--enable_warnings +create database mysqltest_db; +--echo # Create user with no privileges on mysqltest_db database. +create user bug57061_user@localhost; +create function mysqltest_db.f1() returns int return 0; +create procedure mysqltest_db.p1() begin end; +--echo # Connect as user 'bug57061_user@localhost' +connect (conn1, localhost, bug57061_user,,); +--echo # Attempt to drop routine on which user doesn't have privileges +--echo # should result in the same 'access denied' type of error whether +--echo # routine exists or not. +--error ER_PROCACCESS_DENIED_ERROR +drop function if exists mysqltest_db.f_does_not_exist; +--error ER_PROCACCESS_DENIED_ERROR +drop procedure if exists mysqltest_db.p_does_not_exist; +--error ER_PROCACCESS_DENIED_ERROR +drop function if exists mysqltest_db.f1; +--error ER_PROCACCESS_DENIED_ERROR +drop procedure if exists mysqltest_db.p1; +--echo # Connection 'default'. +connection default; +disconnect conn1; +drop user bug57061_user@localhost; +drop database mysqltest_db; + + # Wait till all disconnects are completed --source include/wait_until_count_sessions.inc diff --git a/sql/sp.cc b/sql/sp.cc index 8821dc9365d..87eb40c29ac 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -1636,38 +1636,6 @@ sp_exist_routines(THD *thd, TABLE_LIST *routines, bool any) } -/** - Check if a routine exists in the mysql.proc table, without actually - parsing the definition. (Used for dropping). - - @param thd thread context - @param name name of procedure - - @retval - 0 Success - @retval - non-0 Error; SP_OPEN_TABLE_FAILED or SP_KEY_NOT_FOUND -*/ - -int -sp_routine_exists_in_table(THD *thd, int type, sp_name *name) -{ - TABLE *table; - int ret; - Open_tables_backup open_tables_state_backup; - - if (!(table= open_proc_table_for_read(thd, &open_tables_state_backup))) - ret= SP_OPEN_TABLE_FAILED; - else - { - if ((ret= db_find_routine_aux(thd, type, name, table)) != SP_OK) - ret= SP_KEY_NOT_FOUND; - close_system_tables(thd, &open_tables_state_backup); - } - return ret; -} - - extern "C" uchar* sp_sroutine_key(const uchar *ptr, size_t *plen, my_bool first) { diff --git a/sql/sp.h b/sql/sp.h index 10e70261b86..5d0490586a1 100644 --- a/sql/sp.h +++ b/sql/sp.h @@ -100,9 +100,6 @@ sp_cache_routine(THD *thd, int type, sp_name *name, bool sp_exist_routines(THD *thd, TABLE_LIST *procs, bool any); -int -sp_routine_exists_in_table(THD *thd, int type, sp_name *name); - bool sp_show_create_routine(THD *thd, int type, sp_name *name); diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 2f8a72ee25c..0f3338cb6a6 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -4023,49 +4023,39 @@ create_sp_error: int sp_result; int type= (lex->sql_command == SQLCOM_DROP_PROCEDURE ? TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION); + char *db= lex->spname->m_db.str; + char *name= lex->spname->m_name.str; - /* - @todo: here we break the metadata locking protocol by - looking up the information about the routine without - a metadata lock. Rewrite this piece to make sp_drop_routine - return whether the routine existed or not. - */ - sp_result= sp_routine_exists_in_table(thd, type, lex->spname); - thd->warning_info->opt_clear_warning_info(thd->query_id); - if (sp_result == SP_OK) - { - char *db= lex->spname->m_db.str; - char *name= lex->spname->m_name.str; - - if (check_routine_access(thd, ALTER_PROC_ACL, db, name, - lex->sql_command == SQLCOM_DROP_PROCEDURE, 0)) - goto error; + if (check_routine_access(thd, ALTER_PROC_ACL, db, name, + lex->sql_command == SQLCOM_DROP_PROCEDURE, 0)) + goto error; - /* Conditionally writes to binlog */ - sp_result= sp_drop_routine(thd, type, lex->spname); + /* Conditionally writes to binlog */ + sp_result= sp_drop_routine(thd, type, lex->spname); #ifndef NO_EMBEDDED_ACCESS_CHECKS - /* - We're going to issue an implicit REVOKE statement. - It takes metadata locks and updates system tables. - Make sure that sp_create_routine() did not leave any - locks in the MDL context, so there is no risk to - deadlock. - */ - close_mysql_tables(thd); + /* + We're going to issue an implicit REVOKE statement. + It takes metadata locks and updates system tables. + Make sure that sp_create_routine() did not leave any + locks in the MDL context, so there is no risk to + deadlock. + */ + close_mysql_tables(thd); - if (sp_automatic_privileges && !opt_noacl && - sp_revoke_privileges(thd, db, name, - lex->sql_command == SQLCOM_DROP_PROCEDURE)) - { - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, - ER_PROC_AUTO_REVOKE_FAIL, - ER(ER_PROC_AUTO_REVOKE_FAIL)); - /* If this happens, an error should have been reported. */ - goto error; - } -#endif + if (sp_result != SP_KEY_NOT_FOUND && + sp_automatic_privileges && !opt_noacl && + sp_revoke_privileges(thd, db, name, + lex->sql_command == SQLCOM_DROP_PROCEDURE)) + { + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_PROC_AUTO_REVOKE_FAIL, + ER(ER_PROC_AUTO_REVOKE_FAIL)); + /* If this happens, an error should have been reported. */ + goto error; } +#endif + res= sp_result; switch (sp_result) { case SP_OK: -- cgit v1.2.1 From 148260044e41fca95c49a23d990d9e875085ebb9 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Wed, 13 Oct 2010 12:48:08 +0400 Subject: Fix Bug#56443 remove last 'mysqld_show_column_type' remains --- sql/sql_show.h | 1 - 1 file changed, 1 deletion(-) diff --git a/sql/sql_show.h b/sql/sql_show.h index d1323ede8c1..de8f2525baa 100644 --- a/sql/sql_show.h +++ b/sql/sql_show.h @@ -104,7 +104,6 @@ bool mysqld_show_storage_engines(THD *thd); bool mysqld_show_authors(THD *thd); bool mysqld_show_contributors(THD *thd); bool mysqld_show_privileges(THD *thd); -bool mysqld_show_column_types(THD *thd); char *make_backup_log_name(char *buff, const char *name, const char* log_ext); void calc_sum_of_all_status(STATUS_VAR *to); void append_definer(THD *thd, String *buffer, const LEX_STRING *definer_user, -- cgit v1.2.1 From 8007ef52f1fde8a52004195f0d19c13d668f3c2b Mon Sep 17 00:00:00 2001 From: Dmitry Lenev Date: Wed, 13 Oct 2010 15:21:45 +0400 Subject: Fix for bug #57422 "rpl_row_sp003 sporadically fails under heavy load". rpl_row_sp003.test has sporadically failed when run on machine under heavy load or on slow hardware. This patch fixes races in the test which were causing these failures and also removes unnecessary 100 second wait from it. --- mysql-test/extra/rpl_tests/rpl_row_sp003.test | 18 ++++++++++++++++-- mysql-test/suite/rpl/r/rpl_row_sp003.result | 10 +++++++++- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/mysql-test/extra/rpl_tests/rpl_row_sp003.test b/mysql-test/extra/rpl_tests/rpl_row_sp003.test index 7bc326a3791..d2c2ea0caf3 100644 --- a/mysql-test/extra/rpl_tests/rpl_row_sp003.test +++ b/mysql-test/extra/rpl_tests/rpl_row_sp003.test @@ -35,10 +35,23 @@ connection master1; send CALL test.p1(); connection master; -# To make sure tha the call on master1 arrived at the get_lock -sleep 1; +# Make sure that the call on master1 arrived at the get_lock. +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = 'User lock' and + info = 'SELECT get_lock("test", 100)'; +--source include/wait_condition.inc CALL test.p2(); SELECT release_lock("test"); + +connection master1; +# Reap CALL test.p1() to ensure that it has fully completed +# before doing any selects on test.t1. +--reap +# Release lock acquired by it. +SELECT release_lock("test"); + +connection master; SELECT * FROM test.t1; #show binlog events; --source include/wait_for_ndb_to_binlog.inc @@ -51,6 +64,7 @@ DROP TABLE IF EXISTS test.t1; eval CREATE TABLE test.t1(a INT,PRIMARY KEY(a))ENGINE=$engine_type; CALL test.p2(); CALL test.p1(); +SELECT release_lock("test"); SELECT * FROM test.t1; sync_slave_with_master; diff --git a/mysql-test/suite/rpl/r/rpl_row_sp003.result b/mysql-test/suite/rpl/r/rpl_row_sp003.result index df3e2a7ceed..c3e2dc57740 100644 --- a/mysql-test/suite/rpl/r/rpl_row_sp003.result +++ b/mysql-test/suite/rpl/r/rpl_row_sp003.result @@ -26,6 +26,11 @@ CALL test.p2(); SELECT release_lock("test"); release_lock("test") 1 +get_lock("test", 100) +1 +SELECT release_lock("test"); +release_lock("test") +1 SELECT * FROM test.t1; a 5 @@ -37,7 +42,10 @@ CREATE TABLE test.t1(a INT,PRIMARY KEY(a))ENGINE=INNODB; CALL test.p2(); CALL test.p1(); get_lock("test", 100) -0 +1 +SELECT release_lock("test"); +release_lock("test") +1 SELECT * FROM test.t1; a 8 -- cgit v1.2.1 From 4eb324693f904ae5cefbeab7152ec11da147088a Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Wed, 13 Oct 2010 16:15:28 +0200 Subject: Bug #55930 Assertion `thd->transaction.stmt.is_empty() || thd->in_sub_stmt || (thd->state.. OPTIMIZE TABLE is not directly supported by InnoDB. Instead, recreate and analyze of the table is done. After recreate, the table is closed and locks are released before the table is reopened and locks re-acquired for the analyze phase. This assertion was triggered if OPTIMIZE TABLE failed to acquire thr_lock locks before starting the analyze phase. The assertion tests (among other things) that there no active statement transaction. However, as part of acquiring the thr_lock lock, external_lock() is called for InnoDB tables and this causes a statement transaction to be started. If thr_multi_lock() later fails (e.g. due to timeout), the failure handling code causes this assert to be triggered. This patch fixes the problem by doing rollback of the current statement transaction in case open_ltable (used by OPTIMIZE TABLE) fails to acquire thr_lock locks. Test case added to lock_sync.test. --- mysql-test/r/lock_sync.result | 34 ++++++++++++++++++++++++++ mysql-test/t/lock_sync.test | 55 +++++++++++++++++++++++++++++++++++++++++++ sql/sql_base.cc | 3 +++ 3 files changed, 92 insertions(+) diff --git a/mysql-test/r/lock_sync.result b/mysql-test/r/lock_sync.result index 3682f0df26a..726b754eaa8 100644 --- a/mysql-test/r/lock_sync.result +++ b/mysql-test/r/lock_sync.result @@ -704,3 +704,37 @@ SET DEBUG_SYNC="now SIGNAL query"; # Connection default DROP EVENT e2; SET DEBUG_SYNC="RESET"; +# +# Bug#55930 Assertion `thd->transaction.stmt.is_empty() || +# thd->in_sub_stmt || (thd->state.. +# +DROP TABLE IF EXISTS t1; +CREATE TABLE t1(a INT) engine=InnoDB; +INSERT INTO t1 VALUES (1), (2); +# Connection con1 +SET SESSION lock_wait_timeout= 1; +SET DEBUG_SYNC= 'ha_admin_open_ltable SIGNAL opti_recreate WAIT_FOR opti_analyze'; +# Sending: +OPTIMIZE TABLE t1; +# Connection con2 +SET DEBUG_SYNC= 'now WAIT_FOR opti_recreate'; +SET DEBUG_SYNC= 'after_lock_tables_takes_lock SIGNAL thrlock WAIT_FOR release_thrlock'; +# Sending: +INSERT INTO t1 VALUES (3); +# Connection default +SET DEBUG_SYNC= 'now WAIT_FOR thrlock'; +SET DEBUG_SYNC= 'now SIGNAL opti_analyze'; +# Connection con1 +# Reaping: OPTIMIZE TABLE t1 +Table Op Msg_type Msg_text +test.t1 optimize note Table does not support optimize, doing recreate + analyze instead +test.t1 optimize error Lock wait timeout exceeded; try restarting transaction +test.t1 optimize status Operation failed +Warnings: +Error 1205 Lock wait timeout exceeded; try restarting transaction +SET DEBUG_SYNC= 'now SIGNAL release_thrlock'; +# Connection con2 +# Reaping: INSERT INTO t1 VALUES (3) +# Connection default +DROP TABLE t1; +SET DEBUG_SYNC= 'RESET'; diff --git a/mysql-test/t/lock_sync.test b/mysql-test/t/lock_sync.test index 49f8f59ec5a..7131e2cde31 100644 --- a/mysql-test/t/lock_sync.test +++ b/mysql-test/t/lock_sync.test @@ -1023,6 +1023,61 @@ DROP EVENT e2; SET DEBUG_SYNC="RESET"; +--echo # +--echo # Bug#55930 Assertion `thd->transaction.stmt.is_empty() || +--echo # thd->in_sub_stmt || (thd->state.. +--echo # + +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings + +CREATE TABLE t1(a INT) engine=InnoDB; +INSERT INTO t1 VALUES (1), (2); + +connect (con1, localhost, root); +connect (con2, localhost, root); + +--echo # Connection con1 +connection con1; +SET SESSION lock_wait_timeout= 1; +SET DEBUG_SYNC= 'ha_admin_open_ltable SIGNAL opti_recreate WAIT_FOR opti_analyze'; +--echo # Sending: +--send OPTIMIZE TABLE t1 + +--echo # Connection con2 +connection con2; +SET DEBUG_SYNC= 'now WAIT_FOR opti_recreate'; +SET DEBUG_SYNC= 'after_lock_tables_takes_lock SIGNAL thrlock WAIT_FOR release_thrlock'; +--echo # Sending: +--send INSERT INTO t1 VALUES (3) + +--echo # Connection default +connection default; +SET DEBUG_SYNC= 'now WAIT_FOR thrlock'; +SET DEBUG_SYNC= 'now SIGNAL opti_analyze'; + +--echo # Connection con1 +connection con1; +--echo # Reaping: OPTIMIZE TABLE t1 +--reap +SET DEBUG_SYNC= 'now SIGNAL release_thrlock'; +disconnect con1; +--source include/wait_until_disconnected.inc + +--echo # Connection con2 +connection con2; +--echo # Reaping: INSERT INTO t1 VALUES (3) +--reap +disconnect con2; +--source include/wait_until_disconnected.inc + +--echo # Connection default +connection default; +DROP TABLE t1; +SET DEBUG_SYNC= 'RESET'; + + # Check that all connections opened by test cases in this file are really # gone so execution of other tests won't be affected by their presence. --source include/wait_until_count_sessions.inc diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 8305283cd17..8090d8a9288 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -5319,7 +5319,10 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type, end: if (table == NULL) + { + trans_rollback_stmt(thd); close_thread_tables(thd); + } thd_proc_info(thd, 0); DBUG_RETURN(table); } -- cgit v1.2.1 From 7774a0048a78dc4c5e9a641ef5547aac270f697f Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Thu, 14 Oct 2010 11:02:37 +0200 Subject: Follow-up for Bug #55930 Assertion `thd->transaction.stmt.is_empty() || thd->in_sub_stmt || (thd->state.. Don't rollback statement transactions if we are in a sub-statement. This could for example happen for open_ltable() when opening the general log during execution of a stored procedure. --- sql/sql_base.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 8090d8a9288..fe7ec83b845 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -5320,7 +5320,8 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type, end: if (table == NULL) { - trans_rollback_stmt(thd); + if (!thd->in_sub_stmt) + trans_rollback_stmt(thd); close_thread_tables(thd); } thd_proc_info(thd, 0); -- cgit v1.2.1 From efcb38e71e75800b4bf3bd0e2952511b3252bd98 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Thu, 14 Oct 2010 20:56:56 +0400 Subject: A fix and a test case for Bug#56540 "Exception (crash) in sql_show.cc during rqg_info_schema test on Windows". Ensure we do not access freed memory when filling information_schema.views when one of the views could not be properly opened. --- mysql-test/r/information_schema.result | 44 ++++++++++++++++++++++++++++ mysql-test/t/information_schema.test | 53 ++++++++++++++++++++++++++++++++++ sql/sql_base.cc | 10 ++++++- sql/sql_show.cc | 25 +++++++++++----- 4 files changed, 124 insertions(+), 8 deletions(-) diff --git a/mysql-test/r/information_schema.result b/mysql-test/r/information_schema.result index aa47b8c437e..bab60774b32 100644 --- a/mysql-test/r/information_schema.result +++ b/mysql-test/r/information_schema.result @@ -1807,3 +1807,47 @@ USING (TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME) WHERE COLUMNS.TABLE_SCHEMA = 'test' AND COLUMNS.TABLE_NAME = 't1'; TABLE_SCHEMA TABLE_NAME COLUMN_NAME CONSTRAINT_CATALOG CONSTRAINT_SCHEMA CONSTRAINT_NAME TABLE_CATALOG ORDINAL_POSITION POSITION_IN_UNIQUE_CONSTRAINT REFERENCED_TABLE_SCHEMA REFERENCED_TABLE_NAME REFERENCED_COLUMN_NAME TABLE_CATALOG ORDINAL_POSITION COLUMN_DEFAULT IS_NULLABLE DATA_TYPE CHARACTER_MAXIMUM_LENGTH CHARACTER_OCTET_LENGTH NUMERIC_PRECISION NUMERIC_SCALE CHARACTER_SET_NAME COLLATION_NAME COLUMN_TYPE COLUMN_KEY EXTRA PRIVILEGES COLUMN_COMMENT +# +# A test case for Bug#56540 "Exception (crash) in sql_show.cc +# during rqg_info_schema test on Windows" +# Ensure that we never access memory of a closed table, +# in particular, never access table->field[] array. +# Before the fix, the below test case, produced +# valgrind errors. +# +drop table if exists t1; +drop view if exists v1; +create table t1 (a int, b int); +create view v1 as select t1.a, t1.b from t1; +alter table t1 change b c int; +lock table t1 read; +# --> connection con1 +flush tables; +# --> connection default +select * from information_schema.views; +TABLE_CATALOG def +TABLE_SCHEMA test +TABLE_NAME v1 +VIEW_DEFINITION select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` +CHECK_OPTION NONE +IS_UPDATABLE +DEFINER root@localhost +SECURITY_TYPE DEFINER +CHARACTER_SET_CLIENT latin1 +COLLATION_CONNECTION latin1_swedish_ci +Warnings: +Level Warning +Code 1356 +Message View 'test.v1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them +unlock tables; +# +# Cleanup. +# +# --> connection con1 +# Reaping 'flush tables' +# --> connection default +drop table t1; +drop view v1; +# +# End of 5.5 tests +# diff --git a/mysql-test/t/information_schema.test b/mysql-test/t/information_schema.test index f5fab966bdd..bc73e8411ca 100644 --- a/mysql-test/t/information_schema.test +++ b/mysql-test/t/information_schema.test @@ -1555,3 +1555,56 @@ WHERE COLUMNS.TABLE_SCHEMA = 'test' AND COLUMNS.TABLE_NAME = 't1'; +--echo # +--echo # A test case for Bug#56540 "Exception (crash) in sql_show.cc +--echo # during rqg_info_schema test on Windows" +--echo # Ensure that we never access memory of a closed table, +--echo # in particular, never access table->field[] array. +--echo # Before the fix, the below test case, produced +--echo # valgrind errors. +--echo # + +--disable_warnings +drop table if exists t1; +drop view if exists v1; +--enable_warnings + +create table t1 (a int, b int); +create view v1 as select t1.a, t1.b from t1; +alter table t1 change b c int; +lock table t1 read; +connect(con1, localhost, root,,); +--echo # --> connection con1 +connection con1; +send flush tables; +--echo # --> connection default +connection default; +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table flush" and + info = "flush tables"; +--source include/wait_condition.inc +--vertical_results +select * from information_schema.views; +--horizontal_results +unlock tables; + +--echo # +--echo # Cleanup. +--echo # + +--echo # --> connection con1 +connection con1; +--echo # Reaping 'flush tables' +reap; +disconnect con1; +--source include/wait_until_disconnected.inc +--echo # --> connection default +connection default; +drop table t1; +drop view v1; + + +--echo # +--echo # End of 5.5 tests +--echo # diff --git a/sql/sql_base.cc b/sql/sql_base.cc index fe7ec83b845..8caae3ee406 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -2902,8 +2902,12 @@ retry_share: */ if (check_and_update_table_version(thd, table_list, share)) goto err_unlock; - if (table_list->i_s_requested_object & OPEN_TABLE_ONLY) + if (table_list->i_s_requested_object & OPEN_TABLE_ONLY) + { + my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db, + table_list->table_name); goto err_unlock; + } /* Open view */ if (open_new_frm(thd, share, alias, @@ -2931,7 +2935,11 @@ retry_share: */ if (table_list->i_s_requested_object & OPEN_VIEW_ONLY) + { + my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db, + table_list->table_name); goto err_unlock; + } if (!(flags & MYSQL_OPEN_IGNORE_FLUSH)) { diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 16deb50b17c..a25cfcdedc8 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -4934,18 +4934,29 @@ static int get_schema_views_record(THD *thd, TABLE_LIST *tables, else table->field[4]->store(STRING_WITH_LEN("NONE"), cs); - if (table->pos_in_table_list->table_open_method & - OPEN_FULL_TABLE) + /* + Only try to fill in the information about view updatability + if it is requested as part of the top-level query (i.e. + it's select * from i_s.views, as opposed to, say, select + security_type from i_s.views). Do not try to access the + underlying tables if there was an error when opening the + view: all underlying tables are released back to the table + definition cache on error inside open_normal_and_derived_tables(). + If a field is not assigned explicitly, it defaults to NULL. + */ + if (res == FALSE && + table->pos_in_table_list->table_open_method & OPEN_FULL_TABLE) { updatable_view= 0; if (tables->algorithm != VIEW_ALGORITHM_TMPTABLE) { /* - We should use tables->view->select_lex.item_list here and - can not use Field_iterator_view because the view always uses - temporary algorithm during opening for I_S and - TABLE_LIST fields 'field_translation' & 'field_translation_end' - are uninitialized is this case. + We should use tables->view->select_lex.item_list here + and can not use Field_iterator_view because the view + always uses temporary algorithm during opening for I_S + and TABLE_LIST fields 'field_translation' + & 'field_translation_end' are uninitialized is this + case. */ List *fields= &tables->view->select_lex.item_list; List_iterator it(*fields); -- cgit v1.2.1 From 3d9cddabf828ae1a070d621db3414f6c409ac21b Mon Sep 17 00:00:00 2001 From: Dmitry Shulga Date: Thu, 21 Oct 2010 15:41:13 +0700 Subject: Fixed bug#45445 - cannot execute procedures with thread_stack set to 128k. --- sql/sp.cc | 6 ++++++ sql/sp_head.cc | 11 +++++++---- sql/sql_parse.cc | 18 ++++++++++++++---- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/sql/sp.cc b/sql/sp.cc index 87eb40c29ac..7385a6ffcae 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -779,6 +779,9 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, int ret= 0; + if (check_stack_overrun(thd, STACK_MIN_SIZE, (uchar*)&ret)) + return TRUE; + thd->lex= &newlex; newlex.current_select= NULL; @@ -1505,6 +1508,9 @@ sp_find_routine(THD *thd, int type, sp_name *name, sp_cache **cp, (int) name->m_name.length, name->m_name.str, type, cache_only)); + if (check_stack_overrun(thd, STACK_MIN_SIZE, (uchar*)&depth)) + return NULL; + if ((sp= sp_cache_lookup(cp, name))) { ulong level; diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 1fd4e9302c4..379e81d406e 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -1233,11 +1233,8 @@ sp_head::execute(THD *thd) The same with db_load_routine() required circa 7k bytes and 14k bytes accordingly. Hence, here we book the stack with some reasonable margin. - - Reverting back to 8 * STACK_MIN_SIZE until further fix. - 8 * STACK_MIN_SIZE is required on some exotic platforms. */ - if (check_stack_overrun(thd, 8 * STACK_MIN_SIZE, (uchar*)&old_packet)) + if (check_stack_overrun(thd, STACK_MIN_SIZE, (uchar*)&old_packet)) DBUG_RETURN(TRUE); /* init per-instruction memroot */ @@ -2902,6 +2899,9 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp, It's merged with the saved parent's value at the exit of this func. */ bool parent_modified_non_trans_table= thd->transaction.stmt.modified_non_trans_table; + if (check_stack_overrun(thd, STACK_MIN_SIZE, (uchar*)&parent_modified_non_trans_table)) + DBUG_RETURN(TRUE); + thd->transaction.stmt.modified_non_trans_table= FALSE; DBUG_ASSERT(!thd->derived_tables); DBUG_ASSERT(thd->change_list.is_empty()); @@ -3057,6 +3057,9 @@ sp_instr_stmt::execute(THD *thd, uint *nextp) DBUG_ENTER("sp_instr_stmt::execute"); DBUG_PRINT("info", ("command: %d", m_lex_keeper.sql_command())); + if (check_stack_overrun(thd, STACK_MIN_SIZE, (uchar*)&res)) + DBUG_RETURN(TRUE); + query= thd->query(); query_length= thd->query_length(); #if defined(ENABLED_PROFILING) diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index b322c74cb40..4ed22e3a355 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -5118,10 +5118,17 @@ bool check_stack_overrun(THD *thd, long margin, if ((stack_used=used_stack(thd->thread_stack,(char*) &stack_used)) >= (long) (my_thread_stack_size - margin)) { - char ebuff[MYSQL_ERRMSG_SIZE]; - my_snprintf(ebuff, sizeof(ebuff), ER(ER_STACK_OVERRUN_NEED_MORE), - stack_used, my_thread_stack_size, margin); - my_message(ER_STACK_OVERRUN_NEED_MORE, ebuff, MYF(ME_FATALERROR)); + /* + Do not use stack for the message buffer to ensure correct + behaviour in cases we have close to no stack left. + */ + char* ebuff= new char[MYSQL_ERRMSG_SIZE]; + if (ebuff) { + my_snprintf(ebuff, MYSQL_ERRMSG_SIZE, ER(ER_STACK_OVERRUN_NEED_MORE), + stack_used, my_thread_stack_size, margin); + my_message(ER_STACK_OVERRUN_NEED_MORE, ebuff, MYF(ME_FATALERROR)); + delete [] ebuff; + } return 1; } #ifndef DBUG_OFF @@ -7210,6 +7217,9 @@ bool parse_sql(THD *thd, Object_creation_ctx *backup_ctx= NULL; + if (check_stack_overrun(thd, 2 * STACK_MIN_SIZE, (uchar*)&backup_ctx)) + return TRUE; + if (creation_ctx) backup_ctx= creation_ctx->set_n_backup(thd); -- cgit v1.2.1