From db50f37ae044487e88cf0275b97424ca748f92d1 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 13 Sep 2007 17:30:44 +0400 Subject: [PATCH 1/5] Bug#16918: Aborted_clients > Connections. The problem was that aborted_threads variable was updated twice when a client connection had been aborted. The fix is to refactor a code to have aborted_threads updated only in one place. sql/mysql_priv.h: Make do_command() a private function. sql/sql_parse.cc: 1. Make do_command() a private function; 2. Update aborted_threads in the only one place. --- sql/mysql_priv.h | 1 - sql/sql_parse.cc | 27 +++++++++++++++++---------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 44eb5590a28..24d02ee6948 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -726,7 +726,6 @@ pthread_handler_t handle_bootstrap(void *arg); void end_thread(THD *thd,bool put_in_cache); void flush_thread_cache(); bool mysql_execute_command(THD *thd); -bool do_command(THD *thd); bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length); void log_slow_statement(THD *thd); diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 58f5ffc5235..99a61d95619 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -93,6 +93,8 @@ const char *xa_state_names[]={ "NON-EXISTING", "ACTIVE", "IDLE", "PREPARED" }; +static bool do_command(THD *thd); + #ifdef __WIN__ static void test_signal(int sig_ptr) { @@ -1199,23 +1201,28 @@ pthread_handler_t handle_one_connection(void *arg) } if (thd->user_connect) decrease_user_connections(thd->user_connect); + + if (thd->killed || + net->vio && net->error && net->report_error) + { + statistic_increment(aborted_threads, &LOCK_status); + } + if (net->error && net->vio != 0 && net->report_error) { if (!thd->killed && thd->variables.log_warnings > 1) - sql_print_warning(ER(ER_NEW_ABORTING_CONNECTION), + { + sql_print_warning(ER(ER_NEW_ABORTING_CONNECTION), thd->thread_id,(thd->db ? thd->db : "unconnected"), sctx->user ? sctx->user : "unauthenticated", sctx->host_or_ip, (net->last_errno ? ER(net->last_errno) : ER(ER_UNKNOWN_ERROR))); + } + net_send_error(thd, net->last_errno, NullS); - statistic_increment(aborted_threads,&LOCK_status); } - else if (thd->killed) - { - statistic_increment(aborted_threads,&LOCK_status); - } - + end_thread: close_connection(thd, 0, 1); end_thread(thd,1); @@ -1550,12 +1557,12 @@ bool do_command(THD *thd) DBUG_PRINT("info",("Got error %d reading command from socket %s", net->error, vio_description(net->vio))); + /* Check if we can continue without closing the connection */ + if (net->error != 3) - { - statistic_increment(aborted_threads,&LOCK_status); DBUG_RETURN(TRUE); // We have to close it. - } + net_send_error(thd, net->last_errno, NullS); net->error= 0; DBUG_RETURN(FALSE); From 2b9b71d0e3044572bc289701ae2a4055640a2bbb Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 1 Oct 2007 15:11:15 +0200 Subject: [PATCH 2/5] BUG#30992 (Wrong implementation of pthread_mutex_trylock()): Adding support for correct handling of pthread_mutex_trylock() on Win32 systems as well as when using the safe mutexes. include/my_pthread.h: Adding win_pthread_mutex_trylock() function as wrapper function for Windows implementation of pthread_mutex_trylock(). Adding flag to safe_mutex_lock() that is shall not abort if the mutex is already locked and instead return EBUSY since this is the POSIX behaviour. mysys/my_winthread.c: Added Win32 wrappper function to handle pthread_mutex_trylock(). mysys/thr_mutex.c: Added parameter 'try_lock' to safe_mutex_lock() to allow it to do double- duty as a pthread_mutex_trylock() in addition to working as pthread_mutex_lock(). The code needs to return EBUSY instead of throwing an error in the event that the mutex is re-locked (we do not have recursive mutexes), but it also requires some special handling to avoid race conditions when calling the real pthread_mutex_{lock,trylock}(). --- include/my_pthread.h | 9 ++++---- mysys/my_winthread.c | 25 ++++++++++++++++++++++ mysys/thr_mutex.c | 49 +++++++++++++++++++++++++++++++++++++------- 3 files changed, 72 insertions(+), 11 deletions(-) diff --git a/include/my_pthread.h b/include/my_pthread.h index d64b5348666..e28bb9df1f3 100644 --- a/include/my_pthread.h +++ b/include/my_pthread.h @@ -135,6 +135,7 @@ struct timespec { void win_pthread_init(void); int win_pthread_setspecific(void *A,void *B,uint length); +int win_pthread_mutex_trylock(pthread_mutex_t *mutex); int pthread_create(pthread_t *,pthread_attr_t *,pthread_handler,void *); int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr); int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); @@ -195,7 +196,7 @@ extern int pthread_mutex_destroy (pthread_mutex_t *); #else #define pthread_mutex_init(A,B) (InitializeCriticalSection(A),0) #define pthread_mutex_lock(A) (EnterCriticalSection(A),0) -#define pthread_mutex_trylock(A) (WaitForSingleObject((A), 0) == WAIT_TIMEOUT) +#define pthread_mutex_trylock(A) win_pthread_mutex_trylock((A)) #define pthread_mutex_unlock(A) LeaveCriticalSection(A) #define pthread_mutex_destroy(A) DeleteCriticalSection(A) #define my_pthread_setprio(A,B) SetThreadPriority(GetCurrentThread(), (B)) @@ -593,7 +594,7 @@ typedef struct st_safe_mutex_info_t int safe_mutex_init(safe_mutex_t *mp, const pthread_mutexattr_t *attr, const char *file, uint line); -int safe_mutex_lock(safe_mutex_t *mp,const char *file, uint line); +int safe_mutex_lock(safe_mutex_t *mp, my_bool try_lock, const char *file, uint line); int safe_mutex_unlock(safe_mutex_t *mp,const char *file, uint line); int safe_mutex_destroy(safe_mutex_t *mp,const char *file, uint line); int safe_cond_wait(pthread_cond_t *cond, safe_mutex_t *mp,const char *file, @@ -616,12 +617,12 @@ void safe_mutex_end(FILE *file); #undef pthread_cond_timedwait #undef pthread_mutex_trylock #define pthread_mutex_init(A,B) safe_mutex_init((A),(B),__FILE__,__LINE__) -#define pthread_mutex_lock(A) safe_mutex_lock((A),__FILE__,__LINE__) +#define pthread_mutex_lock(A) safe_mutex_lock((A), FALSE, __FILE__, __LINE__) #define pthread_mutex_unlock(A) safe_mutex_unlock((A),__FILE__,__LINE__) #define pthread_mutex_destroy(A) safe_mutex_destroy((A),__FILE__,__LINE__) #define pthread_cond_wait(A,B) safe_cond_wait((A),(B),__FILE__,__LINE__) #define pthread_cond_timedwait(A,B,C) safe_cond_timedwait((A),(B),(C),__FILE__,__LINE__) -#define pthread_mutex_trylock(A) pthread_mutex_lock(A) +#define pthread_mutex_trylock(A) safe_mutex_lock((A), TRUE, __FILE__, __LINE__) #define pthread_mutex_t safe_mutex_t #define safe_mutex_assert_owner(mp) \ DBUG_ASSERT((mp)->count > 0 && \ diff --git a/mysys/my_winthread.c b/mysys/my_winthread.c index 27ccaef4f23..e9fface0521 100644 --- a/mysys/my_winthread.c +++ b/mysys/my_winthread.c @@ -40,6 +40,31 @@ void win_pthread_init(void) pthread_mutex_init(&THR_LOCK_thread,MY_MUTEX_INIT_FAST); } +/** + Adapter to @c pthread_mutex_trylock() + + @retval 0 Mutex was acquired + @retval EBUSY Mutex was already locked by a thread + @retval EINVAL Mutex could not be acquired due to other error + */ +int +win_pthread_mutex_trylock(pthread_mutex_t *mutex) +{ + switch (WaitForSingleObject(mutex, 0)) { + case WAIT_TIMEOUT: + return EBUSY; + + default: + case WAIT_FAILURE: + return EINVAL; + + case WAIT_OBJECT_0: + case WAIT_ABANDONED: /* The mutex was acquired because it was + * abandoned */ + return 0; + } +} + /* ** We have tried to use '_beginthreadex' instead of '_beginthread' here ** but in this case the program leaks about 512 characters for each diff --git a/mysys/thr_mutex.c b/mysys/thr_mutex.c index 425e5fce459..53ee907e0a3 100644 --- a/mysys/thr_mutex.c +++ b/mysys/thr_mutex.c @@ -91,7 +91,7 @@ int safe_mutex_init(safe_mutex_t *mp, } -int safe_mutex_lock(safe_mutex_t *mp,const char *file, uint line) +int safe_mutex_lock(safe_mutex_t *mp, my_bool try_lock, const char *file, uint line) { int error; if (!mp->file) @@ -104,15 +104,50 @@ int safe_mutex_lock(safe_mutex_t *mp,const char *file, uint line) } pthread_mutex_lock(&mp->global); - if (mp->count > 0 && pthread_equal(pthread_self(),mp->thread)) + if (mp->count > 0) { - fprintf(stderr,"safe_mutex: Trying to lock mutex at %s, line %d, when the mutex was already locked at %s, line %d in thread %s\n", - file,line,mp->file, mp->line, my_thread_name()); - fflush(stderr); - abort(); + if (try_lock) + { + pthread_mutex_unlock(&mp->global); + return EBUSY; + } + else if (pthread_equal(pthread_self(),mp->thread)) + { + fprintf(stderr, + "safe_mutex: Trying to lock mutex at %s, line %d, when the" + " mutex was already locked at %s, line %d in thread %s\n", + file,line,mp->file, mp->line, my_thread_name()); + fflush(stderr); + abort(); + } } pthread_mutex_unlock(&mp->global); - error=pthread_mutex_lock(&mp->mutex); + + /* + If we are imitating trylock(), we need to take special + precautions. + + - We cannot use pthread_mutex_lock() only since another thread can + overtake this thread and take the lock before this thread + causing pthread_mutex_trylock() to hang. In this case, we should + just return EBUSY. Hence, we use pthread_mutex_trylock() to be + able to return immediately. + + - We cannot just use trylock() and continue execution below, since + this would generate an error and abort execution if the thread + was overtaken and trylock() returned EBUSY . In this case, we + instead just return EBUSY, since this is the expected behaviour + of trylock(). + */ + if (try_lock) + { + error= pthread_mutex_trylock(&mp->mutex); + if (error == EBUSY) + return error; + } + else + error= pthread_mutex_lock(&mp->mutex); + if (error || (error=pthread_mutex_lock(&mp->global))) { fprintf(stderr,"Got error %d when trying to lock mutex at %s, line %d\n", From 662fb20f1e03a5d4146a67b5e73c828f5ab476e4 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 3 Oct 2007 21:38:32 +0200 Subject: [PATCH 3/5] Bug#30992 Wrong implementation of pthread_mutex_trylock() It's not possible to use WaitForSingleObject to wait on a CRITICAL_SECTION, instead use the TryEnterCriticalSection function. - if "mutex" was already taken => return EBUSY - if "mutex" was aquired => return 0 include/config-win.h: Make windows.h define TryEnterCriticalSection mysys/my_winthread.c: Use the TryEnterCriticalSection function to implement pthread_mutex_trylock Prevent recursive behaviour by looking at the RecursionCount variable in the CRITICAL_SECTION struct and return EBUSY from function if the mutex was already locked once. --- include/config-win.h | 3 +++ mysys/my_winthread.c | 22 ++++++++++------------ 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/include/config-win.h b/include/config-win.h index 279be7aa5e4..f0804a368bc 100644 --- a/include/config-win.h +++ b/include/config-win.h @@ -19,6 +19,9 @@ /* We have to do this define before including windows.h to get the AWE API functions */ #define _WIN32_WINNT 0x0500 +#else +/* Get NT 4.0 functions */ +#define _WIN32_WINNT 0x0400 #endif #if defined(_MSC_VER) && _MSC_VER >= 1400 diff --git a/mysys/my_winthread.c b/mysys/my_winthread.c index e9fface0521..e94369bec32 100644 --- a/mysys/my_winthread.c +++ b/mysys/my_winthread.c @@ -40,31 +40,29 @@ void win_pthread_init(void) pthread_mutex_init(&THR_LOCK_thread,MY_MUTEX_INIT_FAST); } + /** Adapter to @c pthread_mutex_trylock() @retval 0 Mutex was acquired @retval EBUSY Mutex was already locked by a thread - @retval EINVAL Mutex could not be acquired due to other error */ int win_pthread_mutex_trylock(pthread_mutex_t *mutex) { - switch (WaitForSingleObject(mutex, 0)) { - case WAIT_TIMEOUT: - return EBUSY; - - default: - case WAIT_FAILURE: - return EINVAL; - - case WAIT_OBJECT_0: - case WAIT_ABANDONED: /* The mutex was acquired because it was - * abandoned */ + if (TryEnterCriticalSection(mutex)) + { + /* Don't allow recursive lock */ + if (mutex->RecursionCount > 1){ + LeaveCriticalSection(mutex); + return EBUSY; + } return 0; } + return EBUSY; } + /* ** We have tried to use '_beginthreadex' instead of '_beginthread' here ** but in this case the program leaks about 512 characters for each From 4f14c13a1c6021b0f7225daad7fae2d0b4b71d94 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 4 Oct 2007 17:19:14 +0400 Subject: [PATCH 4/5] Fix for BUG#31035: select from function, group by result crasher. This actually, fix for the patch for bug-27354. The problem with the patch was that Item_func_sp::used_tables() was updated, but Item_func_sp::const_item() was not. So, for Item_func_sp, we had the following inconsistency: - used_tables() returned RAND_TABLE, which means that the item can produce "random" results; - but const_item() returned TRUE, which means that the item is a constant one. The fix is to change Item_func_sp::const_item() behaviour: it must return TRUE (an item is a constant one) only if a stored function is deterministic and each of its arguments (if any) is a constant item. mysql-test/r/sp.result: Update result file. mysql-test/t/sp.test: Add test cases for BUG-31035, BUG-31191, BUG-31226 sql/item_func.cc: Make Item::const_item() be consistent with Item::used_tables(). --- mysql-test/r/sp.result | 176 +++++++++++++++++++++++++ mysql-test/t/sp.test | 290 +++++++++++++++++++++++++++++++++++++++++ sql/item_func.cc | 13 +- 3 files changed, 477 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index a6c429a31fb..efc12b402ce 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -6367,4 +6367,180 @@ DROP TABLE t1; DROP PROCEDURE p1; DROP PROCEDURE p2; + +# +# Bug#31035. +# + +# +# - Prepare. +# + +DROP TABLE IF EXISTS t1; +DROP FUNCTION IF EXISTS f1; +DROP FUNCTION IF EXISTS f2; +DROP FUNCTION IF EXISTS f3; +DROP FUNCTION IF EXISTS f4; + +# +# - Create required objects. +# + +CREATE TABLE t1(c1 INT); + +INSERT INTO t1 VALUES (1), (2), (3); + +CREATE FUNCTION f1() +RETURNS INT +NOT DETERMINISTIC +RETURN 1; + +CREATE FUNCTION f2(p INT) +RETURNS INT +NOT DETERMINISTIC +RETURN 1; + +CREATE FUNCTION f3() +RETURNS INT +DETERMINISTIC +RETURN 1; + +CREATE FUNCTION f4(p INT) +RETURNS INT +DETERMINISTIC +RETURN 1; + +# +# - Check. +# + +SELECT f1() AS a FROM t1 GROUP BY a; +a +1 + +SELECT f2(@a) AS a FROM t1 GROUP BY a; +a +1 + +SELECT f3() AS a FROM t1 GROUP BY a; +a +1 + +SELECT f4(0) AS a FROM t1 GROUP BY a; +a +1 + +SELECT f4(@a) AS a FROM t1 GROUP BY a; +a +1 + +# +# - Cleanup. +# + +DROP TABLE t1; +DROP FUNCTION f1; +DROP FUNCTION f2; +DROP FUNCTION f3; +DROP FUNCTION f4; + +# +# Bug#31191. +# + +# +# - Prepare. +# + +DROP TABLE IF EXISTS t1; +DROP TABLE IF EXISTS t2; +DROP FUNCTION IF EXISTS f1; + +# +# - Create required objects. +# + +CREATE TABLE t1 ( +id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, +barcode INT(8) UNSIGNED ZEROFILL nOT NULL, +PRIMARY KEY (id), +UNIQUE KEY barcode (barcode) +); + +INSERT INTO t1 (id, barcode) VALUES (1, 12345678); +INSERT INTO t1 (id, barcode) VALUES (2, 12345679); + +CREATE TABLE test.t2 ( +id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, +barcode BIGINT(11) UNSIGNED ZEROFILL NOT NULL, +PRIMARY KEY (id) +); + +INSERT INTO test.t2 (id, barcode) VALUES (1, 12345106708); +INSERT INTO test.t2 (id, barcode) VALUES (2, 12345106709); + +CREATE FUNCTION f1(p INT(8)) +RETURNS BIGINT(11) UNSIGNED +READS SQL DATA +RETURN FLOOR(p/1000)*1000000 + 100000 + FLOOR((p MOD 1000)/10)*100 + (p MOD 10); + +# +# - Check. +# + +SELECT DISTINCT t1.barcode, f1(t1.barcode) +FROM t1 +INNER JOIN t2 +ON f1(t1.barcode) = t2.barcode +WHERE t1.barcode=12345678; +barcode f1(t1.barcode) +12345678 12345106708 + +# +# - Cleanup. +# + +DROP TABLE t1; +DROP TABLE t2; +DROP FUNCTION f1; + +# +# Bug#31226. +# + +# +# - Prepare. +# + +DROP TABLE IF EXISTS t1; +DROP FUNCTION IF EXISTS f1; + +# +# - Create required objects. +# + +CREATE TABLE t1(id INT); + +INSERT INTO t1 VALUES (1), (2), (3); + +CREATE FUNCTION f1() +RETURNS DATETIME +NOT DETERMINISTIC NO SQL +RETURN NOW(); + +# +# - Check. +# + +SELECT f1() FROM t1 GROUP BY 1; +f1() + + +# +# - Cleanup. +# + +DROP TABLE t1; +DROP FUNCTION f1; + End of 5.0 tests diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index 8dd99c2b2f1..6413cf2e89d 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -7354,4 +7354,294 @@ DROP TABLE t1; DROP PROCEDURE p1; DROP PROCEDURE p2; +########################################################################### + +# +# Bug#31035: select from function, group by result crasher. +# + +########################################################################### + +--echo + +--echo # +--echo # Bug#31035. +--echo # + +--echo + +--echo # +--echo # - Prepare. +--echo # + +--echo + +--disable_warnings +DROP TABLE IF EXISTS t1; +DROP FUNCTION IF EXISTS f1; +DROP FUNCTION IF EXISTS f2; +DROP FUNCTION IF EXISTS f3; +DROP FUNCTION IF EXISTS f4; +--enable_warnings + +--echo + +--echo # +--echo # - Create required objects. +--echo # + +--echo + +CREATE TABLE t1(c1 INT); + +--echo + +INSERT INTO t1 VALUES (1), (2), (3); + +--echo + +CREATE FUNCTION f1() + RETURNS INT + NOT DETERMINISTIC + RETURN 1; + +--echo + +CREATE FUNCTION f2(p INT) + RETURNS INT + NOT DETERMINISTIC + RETURN 1; + +--echo + +CREATE FUNCTION f3() + RETURNS INT + DETERMINISTIC + RETURN 1; + +--echo + +CREATE FUNCTION f4(p INT) + RETURNS INT + DETERMINISTIC + RETURN 1; + +--echo + +--echo # +--echo # - Check. +--echo # + +--echo + +# Not deterministic function, no arguments. + +SELECT f1() AS a FROM t1 GROUP BY a; + +--echo + +# Not deterministic function, non-constant argument. + +SELECT f2(@a) AS a FROM t1 GROUP BY a; + +--echo + +# Deterministic function, no arguments. + +SELECT f3() AS a FROM t1 GROUP BY a; + +--echo + +# Deterministic function, constant argument. + +SELECT f4(0) AS a FROM t1 GROUP BY a; + +--echo + +# Deterministic function, non-constant argument. + +SELECT f4(@a) AS a FROM t1 GROUP BY a; + +--echo + +--echo # +--echo # - Cleanup. +--echo # + +--echo + +DROP TABLE t1; +DROP FUNCTION f1; +DROP FUNCTION f2; +DROP FUNCTION f3; +DROP FUNCTION f4; + +--echo + +########################################################################### + +# +# Bug#31191: JOIN in combination with stored function crashes the server. +# + +########################################################################### + +--echo # +--echo # Bug#31191. +--echo # + +--echo + +--echo # +--echo # - Prepare. +--echo # + +--echo + +--disable_warnings +DROP TABLE IF EXISTS t1; +DROP TABLE IF EXISTS t2; +DROP FUNCTION IF EXISTS f1; +--enable_warnings + +--echo + +--echo # +--echo # - Create required objects. +--echo # + +--echo + +CREATE TABLE t1 ( + id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, + barcode INT(8) UNSIGNED ZEROFILL nOT NULL, + PRIMARY KEY (id), + UNIQUE KEY barcode (barcode) +); + +--echo + +INSERT INTO t1 (id, barcode) VALUES (1, 12345678); +INSERT INTO t1 (id, barcode) VALUES (2, 12345679); + +--echo + +CREATE TABLE test.t2 ( + id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, + barcode BIGINT(11) UNSIGNED ZEROFILL NOT NULL, + PRIMARY KEY (id) +); + +--echo + +INSERT INTO test.t2 (id, barcode) VALUES (1, 12345106708); +INSERT INTO test.t2 (id, barcode) VALUES (2, 12345106709); + +--echo + +CREATE FUNCTION f1(p INT(8)) + RETURNS BIGINT(11) UNSIGNED + READS SQL DATA + RETURN FLOOR(p/1000)*1000000 + 100000 + FLOOR((p MOD 1000)/10)*100 + (p MOD 10); + +--echo + +--echo # +--echo # - Check. +--echo # + +--echo + +SELECT DISTINCT t1.barcode, f1(t1.barcode) +FROM t1 +INNER JOIN t2 +ON f1(t1.barcode) = t2.barcode +WHERE t1.barcode=12345678; + +--echo + +--echo # +--echo # - Cleanup. +--echo # + +--echo + +DROP TABLE t1; +DROP TABLE t2; +DROP FUNCTION f1; + +--echo + +########################################################################### + +# +# Bug#31226: Group by function crashes mysql. +# + +########################################################################### + +--echo # +--echo # Bug#31226. +--echo # + +--echo + +--echo # +--echo # - Prepare. +--echo # + +--echo + +--disable_warnings +DROP TABLE IF EXISTS t1; +DROP FUNCTION IF EXISTS f1; +--enable_warnings + +--echo + +--echo # +--echo # - Create required objects. +--echo # + +--echo + +CREATE TABLE t1(id INT); + +--echo + +INSERT INTO t1 VALUES (1), (2), (3); + +--echo + +CREATE FUNCTION f1() + RETURNS DATETIME + NOT DETERMINISTIC NO SQL + RETURN NOW(); + +--echo + +--echo # +--echo # - Check. +--echo # + +--echo + +--replace_column 1 +SELECT f1() FROM t1 GROUP BY 1; + +--echo + +--echo # +--echo # - Cleanup. +--echo # + +--echo + +DROP TABLE t1; +DROP FUNCTION f1; + +--echo + +########################################################################### + --echo End of 5.0 tests diff --git a/sql/item_func.cc b/sql/item_func.cc index d03d497dfd0..f7103c22581 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -5583,8 +5583,13 @@ Item_func_sp::fix_fields(THD *thd, Item **ref) #endif /* ! NO_EMBEDDED_ACCESS_CHECKS */ } + if (!m_sp->m_chistics->detistic) - used_tables_cache |= RAND_TABLE_BIT; + { + used_tables_cache |= RAND_TABLE_BIT; + const_item_cache= FALSE; + } + DBUG_RETURN(res); } @@ -5592,6 +5597,10 @@ Item_func_sp::fix_fields(THD *thd, Item **ref) void Item_func_sp::update_used_tables() { Item_func::update_used_tables(); + if (!m_sp->m_chistics->detistic) - used_tables_cache |= RAND_TABLE_BIT; + { + used_tables_cache |= RAND_TABLE_BIT; + const_item_cache= FALSE; + } } From f5dd3491f23cc98a528e06ab7f68869ad69f602b Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 4 Oct 2007 17:34:41 -0300 Subject: [PATCH 5/5] Bug#21587 FLUSH TABLES causes server crash when used with HANDLER statements This bug is a symptom of the way handler's tables are managed. The most different aspect, compared to the conventional behavior, is that the handler's tables are long lived, meaning that their lifetimes are not bounded by the duration of the command that opened them. For this effect the handler code uses its own list (handler_tables instead of open_tables) to hold open handler tables so that the tables won't be closed at the end of the command/statement. Besides the handler_tables list, there is a hash (handler_tables_hash) which is used to associate handler aliases to tables and to refresh the tables upon demand (flush tables). The current implementation doesn't work properly with refreshed tables -- more precisely when flush commands are issued by other initiators. This happens because when a handler open or read statement is being processed, the associated table has to be opened or locked and, for this matter, the open_tables and handler_tables lists are swapped so that the new table being opened is inserted into the handler_tables list. But when opening or locking the table, if the refresh version is different from the thread refresh version then all used tables in the open_tables list (now handler_tables) are refreshed. In the "refreshing" process the handler tables are flushed (closed) without being properly unlinked from the handler hash. The current implementation also fails to properly discard handlers of dropped tables, but this and other problems are going to be addressed in the fixes for bugs 31397 and 31409. The chosen approach tries to properly save and restore the table state so that no table is flushed during the table open and lock operations. The logic is almost the same as before with the list swapping, but with a working glue code. The test case for this bug is going to be committed into 5.1 because it requires a test feature only avaiable in 5.1 (wait_condition). sql/sql_handler.cc: Backup and reset the open_tables and handler_table lists when opening a new handler table and merge the opened table into the handler_tables list afterwards. When locking, do the same but possibly close the table if a refresh is pending. --- sql/sql_handler.cc | 53 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 12 deletions(-) diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index 9aefa71647e..89090c9fa63 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -65,11 +65,6 @@ static enum enum_ha_read_modes rkey_to_rnext[]= { RNEXT_SAME, RNEXT, RPREV, RNEXT, RPREV, RNEXT, RPREV, RPREV }; -#define HANDLER_TABLES_HACK(thd) { \ - TABLE *tmp=thd->open_tables; \ - thd->open_tables=thd->handler_tables; \ - thd->handler_tables=tmp; } - static int mysql_ha_flush_table(THD *thd, TABLE **table_ptr, uint mode_flags); @@ -187,6 +182,7 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) char *db, *name, *alias; uint dblen, namelen, aliaslen, counter; int error; + TABLE *backup_open_tables, *backup_handler_tables; DBUG_ENTER("mysql_ha_open"); DBUG_PRINT("enter",("'%s'.'%s' as '%s' reopen: %d", tables->db, tables->table_name, tables->alias, @@ -215,18 +211,31 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) } } + /* save open_ and handler_ tables state */ + backup_open_tables= thd->open_tables; + backup_handler_tables= thd->handler_tables; + + /* no pre-opened tables */ + thd->open_tables= NULL; + /* to avoid flushes */ + thd->handler_tables= NULL; + /* open_tables() will set 'tables->table' if successful. It must be NULL for a real open when calling open_tables(). */ DBUG_ASSERT(! tables->table); - HANDLER_TABLES_HACK(thd); /* for now HANDLER can be used only for real TABLES */ tables->required_type= FRMTYPE_TABLE; error= open_tables(thd, &tables, &counter, 0); - HANDLER_TABLES_HACK(thd); + /* restore the state and merge the opened table into handler_tables list */ + thd->handler_tables= thd->open_tables ? + thd->open_tables->next= backup_handler_tables, + thd->open_tables : backup_handler_tables; + thd->open_tables= backup_open_tables; + if (error) goto err; @@ -351,7 +360,7 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables, ha_rows select_limit_cnt, ha_rows offset_limit_cnt) { TABLE_LIST *hash_tables; - TABLE *table; + TABLE *table, *backup_open_tables, *backup_handler_tables; MYSQL_LOCK *lock; List list; Protocol *protocol= thd->protocol; @@ -361,7 +370,7 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables, uint num_rows; byte *key; uint key_len; - bool not_used; + bool need_reopen; DBUG_ENTER("mysql_ha_read"); DBUG_PRINT("enter",("'%s'.'%s' as '%s'", tables->db, tables->table_name, tables->alias)); @@ -375,6 +384,7 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables, List_iterator it(list); it++; +retry: if ((hash_tables= (TABLE_LIST*) hash_search(&thd->handler_tables_hash, (byte*) tables->alias, strlen(tables->alias) + 1))) @@ -427,9 +437,28 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables, } tables->table=table; - HANDLER_TABLES_HACK(thd); - lock= mysql_lock_tables(thd, &tables->table, 1, 0, ¬_used); - HANDLER_TABLES_HACK(thd); + /* save open_ and handler_ tables state */ + backup_open_tables= thd->open_tables; + backup_handler_tables= thd->handler_tables; + + /* no pre-opened tables */ + thd->open_tables= NULL; + /* to avoid flushes */ + thd->handler_tables= NULL; + + lock= mysql_lock_tables(thd, &tables->table, 1, + MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN, &need_reopen); + + /* restore previous context */ + thd->handler_tables= backup_handler_tables; + thd->open_tables= backup_open_tables; + + if (need_reopen) + { + mysql_ha_close_table(thd, tables); + hash_tables->table= NULL; + goto retry; + } if (!lock) goto err0; // mysql_lock_tables() printed error message already