summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/config-win.h3
-rw-r--r--include/my_pthread.h9
-rw-r--r--include/mysql_com.h2
-rw-r--r--libmysqld/lib_sql.cc2
-rw-r--r--mysql-test/include/handler.inc68
-rw-r--r--mysql-test/r/handler_innodb.result40
-rw-r--r--mysql-test/r/handler_myisam.result40
-rw-r--r--mysql-test/r/log_tables.result4
-rw-r--r--mysql-test/r/sp-error.result21
-rw-r--r--mysql-test/r/sp.result176
-rw-r--r--mysql-test/t/information_schema.test2
-rw-r--r--mysql-test/t/log_tables.test56
-rw-r--r--mysql-test/t/sp-error.test23
-rw-r--r--mysql-test/t/sp.test290
-rw-r--r--mysql-test/t/upgrade.test6
-rw-r--r--mysys/my_winthread.c23
-rw-r--r--mysys/thr_mutex.c49
-rw-r--r--sql/item.cc4
-rw-r--r--sql/item_func.cc13
-rw-r--r--sql/log.cc43
-rw-r--r--sql/mysql_priv.h1
-rw-r--r--sql/net_serv.cc2
-rw-r--r--sql/protocol.cc2
-rw-r--r--sql/sql_base.cc12
-rw-r--r--sql/sql_class.cc19
-rw-r--r--sql/sql_connect.cc20
-rw-r--r--sql/sql_delete.cc8
-rw-r--r--sql/sql_handler.cc67
-rw-r--r--sql/sql_parse.cc7
-rw-r--r--sql/sql_yacc.yy18
-rw-r--r--tests/mysql_client_test.c273
31 files changed, 1217 insertions, 86 deletions
diff --git a/include/config-win.h b/include/config-win.h
index 2e64e165630..07b316dcbab 100644
--- a/include/config-win.h
+++ b/include/config-win.h
@@ -21,6 +21,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/include/my_pthread.h b/include/my_pthread.h
index eb390c2acc4..88abddc9e25 100644
--- a/include/my_pthread.h
+++ b/include/my_pthread.h
@@ -101,6 +101,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);
@@ -156,7 +157,7 @@ void pthread_exit(void *a); /* was #define pthread_exit(A) ExitThread(A)*/
#define pthread_equal(A,B) ((A) == (B))
#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))
@@ -472,7 +473,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,
@@ -495,12 +496,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/include/mysql_com.h b/include/mysql_com.h
index 5850d48fbf5..f76486b9ec2 100644
--- a/include/mysql_com.h
+++ b/include/mysql_com.h
@@ -203,7 +203,7 @@ typedef struct st_net {
unsigned char reading_or_writing;
char save_char;
my_bool no_send_ok; /* For SPs and other things that do multiple stmts */
- my_bool no_send_eof; /* For SPs' first version read-only cursors */
+ my_bool unused; /* Please remove with the next incompatible ABI change */
my_bool compress;
/*
Set if OK packet is already sent, and we do not need to send error
diff --git a/libmysqld/lib_sql.cc b/libmysqld/lib_sql.cc
index 9c26febe627..4e525f8447f 100644
--- a/libmysqld/lib_sql.cc
+++ b/libmysqld/lib_sql.cc
@@ -111,7 +111,7 @@ emb_advanced_command(MYSQL *mysql, enum enum_server_command command,
}
thd->net.no_send_error= 0;
- result= dispatch_command(command, thd, (char *) arg, arg_length + 1);
+ result= dispatch_command(command, thd, (char *) arg, arg_length);
thd->cur_data= 0;
if (!skip_check)
diff --git a/mysql-test/include/handler.inc b/mysql-test/include/handler.inc
index 437dd6ced4d..71647112126 100644
--- a/mysql-test/include/handler.inc
+++ b/mysql-test/include/handler.inc
@@ -498,3 +498,71 @@ handler t1_alias read a next;
handler t1_alias READ a next where inexistent > 0;
handler t1_alias close;
drop table t1;
+
+#
+# Bug#21587 FLUSH TABLES causes server crash when used with HANDLER statements
+#
+
+--disable_warnings
+drop table if exists t1,t2;
+--enable_warnings
+create table t1 (c1 int);
+create table t2 (c1 int);
+insert into t1 values (1);
+insert into t2 values (2);
+--echo connection: default
+handler t1 open;
+handler t1 read first;
+connect (flush,localhost,root,,);
+connection flush;
+--echo connection: flush
+--send flush tables;
+connection default;
+--echo connection: default
+let $wait_condition=
+ select count(*) = 1 from information_schema.processlist
+ where state = "Flushing tables";
+--source include/wait_condition.inc
+handler t2 open;
+handler t2 read first;
+handler t1 read next;
+handler t1 close;
+handler t2 close;
+connection flush;
+reap;
+connection default;
+drop table t1,t2;
+disconnect flush;
+
+#
+# Bug#31409 RENAME TABLE causes server crash or deadlock when used with HANDLER statements
+#
+
+--disable_warnings
+drop table if exists t1,t2;
+--enable_warnings
+create table t1 (c1 int);
+--echo connection: default
+handler t1 open;
+handler t1 read first;
+connect (flush,localhost,root,,);
+connection flush;
+--echo connection: flush
+--send rename table t1 to t2;
+connection default;
+--echo connection: default
+let $wait_condition=
+ select count(*) = 1 from information_schema.processlist
+ where state = "Waiting for table" and info = "rename table t1 to t2";
+--source include/wait_condition.inc
+handler t2 open;
+handler t2 read first;
+--error ER_NO_SUCH_TABLE
+handler t1 read next;
+handler t1 close;
+handler t2 close;
+connection flush;
+reap;
+connection default;
+drop table t2;
+disconnect flush;
diff --git a/mysql-test/r/handler_innodb.result b/mysql-test/r/handler_innodb.result
index 98b8922bc5f..e9e5c7dbdd5 100644
--- a/mysql-test/r/handler_innodb.result
+++ b/mysql-test/r/handler_innodb.result
@@ -535,3 +535,43 @@ handler t1_alias READ a next where inexistent > 0;
ERROR 42S22: Unknown column 'inexistent' in 'field list'
handler t1_alias close;
drop table t1;
+drop table if exists t1,t2;
+create table t1 (c1 int);
+create table t2 (c1 int);
+insert into t1 values (1);
+insert into t2 values (2);
+connection: default
+handler t1 open;
+handler t1 read first;
+c1
+1
+connection: flush
+flush tables;;
+connection: default
+handler t2 open;
+handler t2 read first;
+c1
+2
+handler t1 read next;
+c1
+1
+handler t1 close;
+handler t2 close;
+drop table t1,t2;
+drop table if exists t1,t2;
+create table t1 (c1 int);
+connection: default
+handler t1 open;
+handler t1 read first;
+c1
+connection: flush
+rename table t1 to t2;;
+connection: default
+handler t2 open;
+handler t2 read first;
+c1
+handler t1 read next;
+ERROR 42S02: Table 'test.t1' doesn't exist
+handler t1 close;
+handler t2 close;
+drop table t2;
diff --git a/mysql-test/r/handler_myisam.result b/mysql-test/r/handler_myisam.result
index 464b775b795..715e5ab03d6 100644
--- a/mysql-test/r/handler_myisam.result
+++ b/mysql-test/r/handler_myisam.result
@@ -535,3 +535,43 @@ handler t1_alias READ a next where inexistent > 0;
ERROR 42S22: Unknown column 'inexistent' in 'field list'
handler t1_alias close;
drop table t1;
+drop table if exists t1,t2;
+create table t1 (c1 int);
+create table t2 (c1 int);
+insert into t1 values (1);
+insert into t2 values (2);
+connection: default
+handler t1 open;
+handler t1 read first;
+c1
+1
+connection: flush
+flush tables;;
+connection: default
+handler t2 open;
+handler t2 read first;
+c1
+2
+handler t1 read next;
+c1
+1
+handler t1 close;
+handler t2 close;
+drop table t1,t2;
+drop table if exists t1,t2;
+create table t1 (c1 int);
+connection: default
+handler t1 open;
+handler t1 read first;
+c1
+connection: flush
+rename table t1 to t2;;
+connection: default
+handler t2 open;
+handler t2 read first;
+c1
+handler t1 read next;
+ERROR 42S02: Table 'test.t1' doesn't exist
+handler t1 close;
+handler t2 close;
+drop table t2;
diff --git a/mysql-test/r/log_tables.result b/mysql-test/r/log_tables.result
index 5e18e1273c0..c8857c582a9 100644
--- a/mysql-test/r/log_tables.result
+++ b/mysql-test/r/log_tables.result
@@ -270,6 +270,10 @@ use mysql;
lock tables general_log read local, help_category read local;
ERROR HY000: You can't use locks with log tables.
unlock tables;
+drop table if exists mysql.renamed_general_log;
+drop table if exists mysql.renamed_slow_log;
+drop table if exists mysql.general_log_new;
+drop table if exists mysql.slow_log_new;
use mysql;
RENAME TABLE general_log TO renamed_general_log;
ERROR HY000: Cannot rename 'general_log'. When logging enabled, rename to/from log table must rename two tables: the log table to an archive table and another table back to 'general_log'
diff --git a/mysql-test/r/sp-error.result b/mysql-test/r/sp-error.result
index bfcd64e89d3..46b8adb85b8 100644
--- a/mysql-test/r/sp-error.result
+++ b/mysql-test/r/sp-error.result
@@ -142,7 +142,10 @@ declare c cursor for insert into test.t1 values ("foo", 42);
open c;
close c;
end|
-ERROR 42000: Cursor statement must be a SELECT
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'insert into test.t1 values ("foo", 42);
+open c;
+close c;
+end' at line 3
create procedure p()
begin
declare x int;
@@ -1491,3 +1494,19 @@ ALTER DATABASE `#mysql50#upgrade-me` UPGRADE DATA DIRECTORY NAME;
RETURN 0;
END//
ERROR HY000: Can't drop or alter a DATABASE from within another stored routine
+DROP PROCEDURE IF EXISTS p1;
+CREATE PROCEDURE p1()
+BEGIN
+DECLARE c char(100);
+DECLARE cur1 CURSOR FOR SHOW TABLES;
+OPEN cur1;
+FETCH cur1 INTO c;
+select c;
+CLOSE cur1;
+END|
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'SHOW TABLES;
+OPEN cur1;
+FETCH cur1 INTO c;
+select c;
+CLOSE cur1;
+END' at line 4
diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result
index 21af61d39f6..4641866bb43 100644
--- a/mysql-test/r/sp.result
+++ b/mysql-test/r/sp.result
@@ -6620,6 +6620,182 @@ 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()
+<timestamp>
+
+#
+# - Cleanup.
+#
+
+DROP TABLE t1;
+DROP FUNCTION f1;
+
End of 5.0 tests
#
diff --git a/mysql-test/t/information_schema.test b/mysql-test/t/information_schema.test
index 6bcc14d4e49..9ad658645bd 100644
--- a/mysql-test/t/information_schema.test
+++ b/mysql-test/t/information_schema.test
@@ -953,7 +953,7 @@ BEGIN
DECLARE col1, col2, col3, col4, col6 CHAR(255);
DECLARE default_val VARCHAR(65532);
DECLARE done INT DEFAULT 0;
- DECLARE cur1 CURSOR FOR SHOW COLUMNS FROM bug23037;
+ DECLARE cur1 CURSOR FOR SELECT COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE, COLUMN_KEY, COLUMN_DEFAULT, EXTRA FROM INFORMATION_SCHEMA.COLUMNS where TABLE_NAME='bug23037';
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done = 1;
OPEN cur1;
FETCH cur1 INTO col1, col2, col3, col4, default_val, col6;
diff --git a/mysql-test/t/log_tables.test b/mysql-test/t/log_tables.test
index 89c7c255554..1d65c86295f 100644
--- a/mysql-test/t/log_tables.test
+++ b/mysql-test/t/log_tables.test
@@ -295,6 +295,13 @@ unlock tables;
# Bug #21785 Server crashes after rename of the log table
#
+--disable_warnings
+drop table if exists mysql.renamed_general_log;
+drop table if exists mysql.renamed_slow_log;
+drop table if exists mysql.general_log_new;
+drop table if exists mysql.slow_log_new;
+--enable_warnings
+
use mysql;
# Should result in error
--error ER_CANT_RENAME_LOG_TABLE
@@ -359,6 +366,55 @@ drop table renamed_general_log, renamed_slow_log;
use test;
#
+# Bug#27858 (Failing to log to a log table doesn't log anything to error log)
+#
+# This test works as expected, it's a negative test.
+# The message "[ERROR] Failed to write to mysql.general_log"
+# is printed to master.err when writing to the table mysql.general_log
+# failed.
+# However, this message is picked up by mysql-test-run.pl,
+# and reported as a test failure, which is a false negative.
+# There is no current way to *selectively* filter out these expected error conditions
+# (see mysql-test/lib/mtr_report.pl, mtr_report_stats()).
+# Instead of filtering all occurences of "Failed to write to
+# mysql.general_log", which could hide bugs when the error is not expected,
+# this test case is commented instead.
+# TODO: improve filtering of expected errors in master.err in
+# mysql-test-run.pl (based on the test name ?), and uncomment this test.
+
+# --disable_warnings
+# drop table if exists mysql.bad_general_log;
+# drop table if exists mysql.bad_slow_log;
+# drop table if exists mysql.general_log_hide;
+# drop table if exists mysql.slow_log_hide;
+# --enable_warnings
+#
+# create table mysql.bad_general_log (a int) engine= CSV;
+# create table mysql.bad_slow_log (a int) engine= CSV;
+#
+# # Rename does not perform checks on the table structure,
+# # exploiting this to force a failure to log
+# rename table mysql.general_log to mysql.general_log_hide, mysql.bad_general_log TO mysql.general_log;
+# rename table mysql.slow_log to mysql.slow_log_hide, mysql.bad_slow_log TO mysql.slow_log;
+#
+# # The following message should be printed in master.log:
+# # [ERROR] Failed to write to mysql.general_log
+# # TODO: See how verifying this could be automated
+#
+# flush tables;
+# select "logging this should fail";
+#
+# # Restore the log tables
+#
+# rename table mysql.general_log to mysql.bad_general_log, mysql.general_log_hide TO mysql.general_log;
+# rename table mysql.slow_log to mysql.bad_slow_log, mysql.slow_log_hide TO mysql.slow_log;
+#
+# flush tables;
+#
+# drop table mysql.bad_general_log;
+# drop table mysql.bad_slow_log;
+
+#
# Bug #21966 Strange warnings on repair of the log tables
#
diff --git a/mysql-test/t/sp-error.test b/mysql-test/t/sp-error.test
index c9145859405..ad516f028e6 100644
--- a/mysql-test/t/sp-error.test
+++ b/mysql-test/t/sp-error.test
@@ -196,7 +196,7 @@ select f(10)|
drop function f|
---error 1322
+--error ER_PARSE_ERROR
create procedure p()
begin
declare c cursor for insert into test.t1 values ("foo", 42);
@@ -2179,6 +2179,27 @@ delimiter ;//
#
+# Bug#29223 declare cursor c for SHOW .....
+#
+
+--disable_warnings
+DROP PROCEDURE IF EXISTS p1;
+--enable_warnings
+--delimiter |
+--error ER_PARSE_ERROR
+CREATE PROCEDURE p1()
+BEGIN
+ DECLARE c char(100);
+ DECLARE cur1 CURSOR FOR SHOW TABLES;
+
+ OPEN cur1;
+ FETCH cur1 INTO c;
+ select c;
+ CLOSE cur1;
+END|
+--delimiter ;
+
+#
# BUG#NNNN: New bug synopsis
#
#--disable_warnings
diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test
index 78ac419e451..e6c293cab5f 100644
--- a/mysql-test/t/sp.test
+++ b/mysql-test/t/sp.test
@@ -7581,6 +7581,296 @@ 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 <timestamp>
+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/mysql-test/t/upgrade.test b/mysql-test/t/upgrade.test
index 40bd17fc3a5..05f430b087b 100644
--- a/mysql-test/t/upgrade.test
+++ b/mysql-test/t/upgrade.test
@@ -1,5 +1,10 @@
-- source include/not_embedded.inc
+# Temporary disabled on windows,
+# because of --exec mkdir
+# TODO: implement Bug#31004 and remove this limitation
+--source include/not_windows.inc
+
--disable_warnings
drop database if exists `mysqltest1`;
drop database if exists `mysqltest-1`;
@@ -60,7 +65,6 @@ drop table t1;
# Bug#28360 (RENAME DATABASE destroys routines)
#
-
--disable_warnings
drop database if exists `tabc`;
drop database if exists `a-b-c`;
diff --git a/mysys/my_winthread.c b/mysys/my_winthread.c
index 27ccaef4f23..e94369bec32 100644
--- a/mysys/my_winthread.c
+++ b/mysys/my_winthread.c
@@ -40,6 +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
+ */
+int
+win_pthread_mutex_trylock(pthread_mutex_t *mutex)
+{
+ 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
diff --git a/mysys/thr_mutex.c b/mysys/thr_mutex.c
index e7a927e562a..49003553f0b 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",
diff --git a/sql/item.cc b/sql/item.cc
index d8080d62c39..a498a681fc4 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -4456,12 +4456,14 @@ Field *Item::tmp_table_field_from_field_type(TABLE *table, bool fixed_length)
else
field= new Field_blob(max_length, maybe_null, name, collation.collation);
break; // Blob handled outside of case
+#ifdef HAVE_SPATIAL
case MYSQL_TYPE_GEOMETRY:
field= new Field_geom(max_length, maybe_null, name, table->s,
(Field::geometry_type)
((type() == Item::TYPE_HOLDER) ?
((Item_type_holder *)this)->get_geometry_type() :
((Item_geometry_func *)this)->get_geometry_type()));
+#endif /* HAVE_SPATIAL */
}
if (field)
field->init(table);
@@ -6585,10 +6587,12 @@ Item_type_holder::Item_type_holder(THD *thd, Item *item)
if (Field::result_merge_type(fld_type) == INT_RESULT)
decimals= 0;
prev_decimal_int_part= item->decimal_int_part();
+#ifdef HAVE_SPATIAL
if (item->field_type() == MYSQL_TYPE_GEOMETRY)
geometry_type= (item->type() == Item::FIELD_ITEM) ?
((Item_field *)item)->get_geometry_type() :
(Field::geometry_type)((Item_geometry_func *)item)->get_geometry_type();
+#endif /* HAVE_SPATIAL */
}
diff --git a/sql/item_func.cc b/sql/item_func.cc
index efc42c1b73f..fded5fa1391 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -5592,8 +5592,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);
}
@@ -5601,8 +5606,12 @@ 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;
+ }
}
diff --git a/sql/log.cc b/sql/log.cc
index 95204e89d0e..9d6f6fa4c9b 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -387,10 +387,15 @@ bool Log_to_csv_event_handler::
if (table->field[1]->store(user_host, user_host_len, client_cs) ||
table->field[2]->store((longlong) thread_id, TRUE) ||
table->field[3]->store((longlong) server_id, TRUE) ||
- table->field[4]->store(command_type, command_type_len, client_cs) ||
- table->field[5]->store(sql_text, sql_text_len, client_cs))
+ table->field[4]->store(command_type, command_type_len, client_cs))
goto err;
+ /*
+ A positive return value in store() means truncation.
+ Still logging a message in the log in this case.
+ */
+ if (table->field[5]->store(sql_text, sql_text_len, client_cs) < 0)
+ goto err;
/* mark all fields as not null */
table->field[1]->set_notnull();
@@ -407,19 +412,14 @@ bool Log_to_csv_event_handler::
/* log table entries are not replicated */
if (table->file->ha_write_row(table->record[0]))
- {
- struct tm start;
- localtime_r(&event_time, &start);
-
- sql_print_error("%02d%02d%02d %2d:%02d:%02d - Failed to write to mysql.general_log",
- start.tm_year % 100, start.tm_mon + 1,
- start.tm_mday, start.tm_hour,
- start.tm_min, start.tm_sec);
- }
+ goto err;
result= FALSE;
err:
+ if (result)
+ sql_print_error("Failed to write to mysql.general_log");
+
if (need_rnd_end)
{
table->file->ha_rnd_end();
@@ -595,25 +595,24 @@ bool Log_to_csv_event_handler::
goto err;
table->field[9]->set_notnull();
- /* sql_text */
- if (table->field[10]->store(sql_text,sql_text_len, client_cs))
+ /*
+ Column sql_text.
+ A positive return value in store() means truncation.
+ Still logging a message in the log in this case.
+ */
+ if (table->field[10]->store(sql_text, sql_text_len, client_cs) < 0)
goto err;
/* log table entries are not replicated */
if (table->file->ha_write_row(table->record[0]))
- {
- struct tm start;
- localtime_r(&current_time, &start);
-
- sql_print_error("%02d%02d%02d %2d:%02d:%02d - Failed to write to mysql.slow_log",
- start.tm_year % 100, start.tm_mon + 1,
- start.tm_mday, start.tm_hour,
- start.tm_min, start.tm_sec);
- }
+ goto err;
result= FALSE;
err:
+ if (result)
+ sql_print_error("Failed to write to mysql.slow_log");
+
if (need_rnd_end)
{
table->file->ha_rnd_end();
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index 6a7e3e00f6f..79d4c16f9c1 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -917,7 +917,6 @@ void decrease_user_connections(USER_CONN *uc);
void thd_init_client_charset(THD *thd, uint cs_number);
bool setup_connection_thread_globals(THD *thd);
bool login_connection(THD *thd);
-void prepare_new_connection_state(THD* thd);
void end_connection(THD *thd);
bool mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create, bool silent);
diff --git a/sql/net_serv.cc b/sql/net_serv.cc
index 1f75d7ab1d7..18cf1ebae5b 100644
--- a/sql/net_serv.cc
+++ b/sql/net_serv.cc
@@ -123,7 +123,7 @@ my_bool my_net_init(NET *net, Vio* vio)
MYF(MY_WME))))
DBUG_RETURN(1);
net->buff_end=net->buff+net->max_packet;
- net->no_send_ok= net->no_send_eof= net->no_send_error= 0;
+ net->no_send_ok= net->no_send_error= 0;
net->error=0; net->return_errno=0; net->return_status=0;
net->pkt_nr=net->compress_pkt_nr=0;
net->write_pos=net->read_pos = net->buff;
diff --git a/sql/protocol.cc b/sql/protocol.cc
index 2ed241c4c98..9d473912ba3 100644
--- a/sql/protocol.cc
+++ b/sql/protocol.cc
@@ -346,7 +346,7 @@ send_eof(THD *thd)
{
NET *net= &thd->net;
DBUG_ENTER("send_eof");
- if (net->vio != 0 && !net->no_send_eof)
+ if (net->vio != 0)
{
write_eof_packet(thd, net);
VOID(net_flush(net));
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 337fde53dac..c4e90165ced 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -2466,7 +2466,13 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
DBUG_RETURN(0);
}
- /* close handler tables which are marked for flush */
+ /*
+ In order for the back off and re-start process to work properly,
+ handler tables having old versions (due to FLUSH TABLES or pending
+ name-lock) MUST be closed. This is specially important if a name-lock
+ is pending for any table of the handler_tables list, otherwise a
+ deadlock may occur.
+ */
if (thd->handler_tables)
mysql_ha_flush(thd, (TABLE_LIST*) NULL, MYSQL_HA_REOPEN_ON_USAGE, TRUE);
@@ -2533,6 +2539,10 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
table->db_stat == 0 signals wait_for_locked_table_names
that the tables in question are not used any more. See
table_is_used call for details.
+
+ Notice that HANDLER tables were already taken care of by
+ the earlier call to mysql_ha_flush() in this same critical
+ section.
*/
close_old_data_files(thd,thd->open_tables,0,0);
/*
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 66a51d5bb00..893ecf88a96 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -585,6 +585,12 @@ void THD::init(void)
if (variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES)
server_status|= SERVER_STATUS_NO_BACKSLASH_ESCAPES;
options= thd_startup_options;
+
+ if (variables.max_join_size == HA_POS_ERROR)
+ options |= OPTION_BIG_SELECTS;
+ else
+ options &= ~OPTION_BIG_SELECTS;
+
transaction.all.modified_non_trans_table= transaction.stmt.modified_non_trans_table= FALSE;
open_options=ha_open_options;
update_lock_default= (variables.low_priority_updates ?
@@ -692,6 +698,7 @@ void THD::cleanup(void)
pthread_mutex_lock(&LOCK_user_locks);
item_user_lock_release(ull);
pthread_mutex_unlock(&LOCK_user_locks);
+ ull= NULL;
}
cleanup_done=1;
@@ -1416,7 +1423,14 @@ bool select_to_file::send_eof()
if (my_close(file,MYF(MY_WME)))
error= 1;
if (!error)
+ {
+ /*
+ In order to remember the value of affected rows for ROW_COUNT()
+ function, SELECT INTO has to have an own SQLCOM.
+ TODO: split from SQLCOM_SELECT
+ */
::send_ok(thd,row_count);
+ }
file= -1;
return error;
}
@@ -2331,6 +2345,11 @@ bool select_dumpvar::send_eof()
if (! row_count)
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_SP_FETCH_NO_DATA, ER(ER_SP_FETCH_NO_DATA));
+ /*
+ In order to remember the value of affected rows for ROW_COUNT()
+ function, SELECT INTO has to have an own SQLCOM.
+ TODO: split from SQLCOM_SELECT
+ */
::send_ok(thd,row_count);
return 0;
}
diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc
index 6bb0f62d843..094bef9324e 100644
--- a/sql/sql_connect.cc
+++ b/sql/sql_connect.cc
@@ -933,7 +933,7 @@ bool login_connection(THD *thd)
NET *net= &thd->net;
Security_context *sctx= thd->security_ctx;
DBUG_ENTER("login_connection");
- DBUG_PRINT("info", ("handle_one_connection called by thread %lu",
+ DBUG_PRINT("info", ("login_connection called by thread %lu",
thd->thread_id));
net->no_send_error= 0;
@@ -973,21 +973,29 @@ void end_connection(THD *thd)
plugin_thdvar_cleanup(thd);
if (thd->user_connect)
decrease_user_connections(thd->user_connect);
+
+ if (thd->killed ||
+ net->error && net->vio != 0 && net->report_error)
+ {
+ statistic_increment(aborted_threads,&LOCK_status);
+ }
+
if (net->error && net->vio != 0 && net->report_error)
{
- Security_context *sctx= thd->security_ctx;
if (!thd->killed && thd->variables.log_warnings > 1)
+ {
+ Security_context *sctx= thd->security_ctx;
+
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);
}
@@ -995,7 +1003,7 @@ void end_connection(THD *thd)
Initialize THD to handle queries
*/
-void prepare_new_connection_state(THD* thd)
+static void prepare_new_connection_state(THD* thd)
{
Security_context *sctx= thd->security_ctx;
diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc
index 303918f42a2..4c57fad8d87 100644
--- a/sql/sql_delete.cc
+++ b/sql/sql_delete.cc
@@ -142,7 +142,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
{
free_underlaid_joins(thd, select_lex);
thd->row_count_func= 0;
- send_ok(thd); // No matching records
+ send_ok(thd, (ha_rows) thd->row_count_func); // No matching records
DBUG_RETURN(0);
}
#endif
@@ -159,7 +159,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
delete select;
free_underlaid_joins(thd, select_lex);
thd->row_count_func= 0;
- send_ok(thd,0L);
+ send_ok(thd, (ha_rows) thd->row_count_func);
/*
We don't need to call reset_auto_increment in this case, because
mysql_truncate always gives a NULL conds argument, hence we never
@@ -386,7 +386,7 @@ cleanup:
if (error < 0 || (thd->lex->ignore && !thd->is_fatal_error))
{
thd->row_count_func= deleted;
- send_ok(thd,deleted);
+ send_ok(thd, (ha_rows) thd->row_count_func);
DBUG_PRINT("info",("%ld records deleted",(long) deleted));
}
DBUG_RETURN(error >= 0 || thd->net.report_error);
@@ -889,7 +889,7 @@ bool multi_delete::send_eof()
if (!local_error)
{
thd->row_count_func= deleted;
- ::send_ok(thd, deleted);
+ ::send_ok(thd, (ha_rows) thd->row_count_func);
}
return 0;
}
diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc
index 1adce48c539..ed7e30c1fef 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;
DBUG_ENTER("mysql_ha_open");
DBUG_PRINT("enter",("'%s'.'%s' as '%s' reopen: %d",
tables->db, tables->table_name, tables->alias,
@@ -216,16 +212,37 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
}
/*
+ Save and reset the open_tables list so that open_tables() won't
+ be able to access (or know about) the previous list. And on return
+ from open_tables(), thd->open_tables will contain only the opened
+ table.
+
+ The thd->handler_tables list is kept as-is to avoid deadlocks if
+ open_table(), called by open_tables(), needs to back-off because
+ of a pending name-lock on the table being opened.
+
+ See open_table() back-off comments for more details.
+ */
+ backup_open_tables= thd->open_tables;
+ thd->open_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 */
+ if (thd->open_tables)
+ {
+ thd->open_tables->next= thd->handler_tables;
+ thd->handler_tables= thd->open_tables;
+ }
+
+ thd->open_tables= backup_open_tables;
if (error)
goto err;
@@ -351,7 +368,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;
MYSQL_LOCK *lock;
List<Item> list;
Protocol *protocol= thd->protocol;
@@ -361,7 +378,7 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables,
uint num_rows;
uchar *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 +392,7 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables,
List_iterator<Item> it(list);
it++;
+retry:
if ((hash_tables= (TABLE_LIST*) hash_search(&thd->handler_tables_hash,
(uchar*) tables->alias,
strlen(tables->alias) + 1)))
@@ -428,9 +446,34 @@ 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, &not_used);
- HANDLER_TABLES_HACK(thd);
+ /* save open_tables state */
+ backup_open_tables= thd->open_tables;
+ /*
+ mysql_lock_tables() needs thd->open_tables to be set correctly to
+ be able to handle aborts properly. When the abort happens, it's
+ safe to not protect thd->handler_tables because it won't close any
+ tables.
+ */
+ thd->open_tables= thd->handler_tables;
+
+ lock= mysql_lock_tables(thd, &tables->table, 1,
+ MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN, &need_reopen);
+
+ /* restore previous context */
+ thd->open_tables= backup_open_tables;
+
+ if (need_reopen)
+ {
+ mysql_ha_close_table(thd, tables);
+ hash_tables->table= NULL;
+ /*
+ The lock might have been aborted, we need to manually reset
+ thd->some_tables_deleted because handler's tables are closed
+ in a non-standard way. Otherwise we might loop indefinitely.
+ */
+ thd->some_tables_deleted= 0;
+ goto retry;
+ }
if (!lock)
goto err0; // mysql_lock_tables() printed error message already
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 10c59d6a374..695e923e816 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -86,7 +86,6 @@ const char *xa_state_names[]={
"NON-EXISTING", "ACTIVE", "IDLE", "PREPARED"
};
-
static void unlock_locked_tables(THD *thd)
{
if (thd->locked_tables)
@@ -681,12 +680,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);
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 39175297753..0989e6e6b24 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -2540,25 +2540,13 @@ sp_decl:
sp_cursor_stmt:
{
Lex->sphead->reset_lex(YYTHD);
-
- /*
- We use statement here just be able to get a better
- error message. Using 'select' works too, but will then
- result in a generic "syntax error" if a non-select
- statement is given.
- */
}
- statement
+ select
{
LEX *lex= Lex;
- if (lex->sql_command != SQLCOM_SELECT &&
- !(sql_command_flags[lex->sql_command] & CF_STATUS_COMMAND))
- {
- my_message(ER_SP_BAD_CURSOR_QUERY, ER(ER_SP_BAD_CURSOR_QUERY),
- MYF(0));
- MYSQL_YYABORT;
- }
+ DBUG_ASSERT(lex->sql_command == SQLCOM_SELECT);
+
if (lex->result)
{
my_message(ER_SP_BAD_CURSOR_SELECT, ER(ER_SP_BAD_CURSOR_SELECT),
diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c
index eff8df8109a..801f2896107 100644
--- a/tests/mysql_client_test.c
+++ b/tests/mysql_client_test.c
@@ -16819,6 +16819,277 @@ static void test_bug30472()
mysql_close(&con);
}
+static void bug20023_change_user(MYSQL *con)
+{
+ DIE_IF(mysql_change_user(con,
+ opt_user,
+ opt_password,
+ opt_db ? opt_db : "test"));
+}
+
+static bool query_int_variable(MYSQL *con,
+ const char *var_name,
+ int *var_value)
+{
+ MYSQL_RES *rs;
+ MYSQL_ROW row;
+
+ char query_buffer[MAX_TEST_QUERY_LENGTH];
+
+ bool is_null;
+
+ my_snprintf(query_buffer,
+ sizeof (query_buffer),
+ "SELECT %s",
+ (const char *) var_name);
+
+ DIE_IF(mysql_query(con, query_buffer));
+ DIE_UNLESS(rs= mysql_store_result(con));
+ DIE_UNLESS(row= mysql_fetch_row(rs));
+
+ is_null= row[0] == NULL;
+
+ if (!is_null)
+ *var_value= atoi(row[0]);
+
+ mysql_free_result(rs);
+
+ return is_null;
+}
+
+static void test_bug20023()
+{
+ MYSQL con;
+
+ int sql_big_selects_orig;
+ int max_join_size_orig;
+
+ int sql_big_selects_2;
+ int sql_big_selects_3;
+ int sql_big_selects_4;
+ int sql_big_selects_5;
+
+ char query_buffer[MAX_TEST_QUERY_LENGTH];
+
+ /* Create a new connection. */
+
+ DIE_UNLESS(mysql_init(&con));
+
+ DIE_UNLESS(mysql_real_connect(&con,
+ opt_host,
+ opt_user,
+ opt_password,
+ opt_db ? opt_db : "test",
+ opt_port,
+ opt_unix_socket,
+ CLIENT_FOUND_ROWS));
+
+ /***********************************************************************
+ Remember original SQL_BIG_SELECTS, MAX_JOIN_SIZE values.
+ ***********************************************************************/
+
+ query_int_variable(&con,
+ "@@session.sql_big_selects",
+ &sql_big_selects_orig);
+
+ query_int_variable(&con,
+ "@@global.max_join_size",
+ &max_join_size_orig);
+
+ /***********************************************************************
+ Test that COM_CHANGE_USER resets the SQL_BIG_SELECTS to the initial value.
+ ***********************************************************************/
+
+ /* Issue COM_CHANGE_USER. */
+
+ bug20023_change_user(&con);
+
+ /* Query SQL_BIG_SELECTS. */
+
+ query_int_variable(&con,
+ "@@session.sql_big_selects",
+ &sql_big_selects_2);
+
+ /* Check that SQL_BIG_SELECTS is reset properly. */
+
+ DIE_UNLESS(sql_big_selects_orig == sql_big_selects_2);
+
+ /***********************************************************************
+ Test that if MAX_JOIN_SIZE set to non-default value,
+ SQL_BIG_SELECTS will be 0.
+ ***********************************************************************/
+
+ /* Set MAX_JOIN_SIZE to some non-default value. */
+
+ DIE_IF(mysql_query(&con, "SET @@global.max_join_size = 10000"));
+ DIE_IF(mysql_query(&con, "SET @@session.max_join_size = default"));
+
+ /* Issue COM_CHANGE_USER. */
+
+ bug20023_change_user(&con);
+
+ /* Query SQL_BIG_SELECTS. */
+
+ query_int_variable(&con,
+ "@@session.sql_big_selects",
+ &sql_big_selects_3);
+
+ /* Check that SQL_BIG_SELECTS is 0. */
+
+ DIE_UNLESS(sql_big_selects_3 == 0);
+
+ /***********************************************************************
+ Test that if MAX_JOIN_SIZE set to default value,
+ SQL_BIG_SELECTS will be 1.
+ ***********************************************************************/
+
+ /* Set MAX_JOIN_SIZE to the default value (-1). */
+
+ DIE_IF(mysql_query(&con, "SET @@global.max_join_size = -1"));
+ DIE_IF(mysql_query(&con, "SET @@session.max_join_size = default"));
+
+ /* Issue COM_CHANGE_USER. */
+
+ bug20023_change_user(&con);
+
+ /* Query SQL_BIG_SELECTS. */
+
+ query_int_variable(&con,
+ "@@session.sql_big_selects",
+ &sql_big_selects_4);
+
+ /* Check that SQL_BIG_SELECTS is 1. */
+
+ DIE_UNLESS(sql_big_selects_4 == 1);
+
+ /***********************************************************************
+ Restore MAX_JOIN_SIZE.
+ Check that SQL_BIG_SELECTS will be the original one.
+ ***********************************************************************/
+
+ /* Restore MAX_JOIN_SIZE. */
+
+ my_snprintf(query_buffer,
+ sizeof (query_buffer),
+ "SET @@global.max_join_size = %d",
+ (int) max_join_size_orig);
+
+ DIE_IF(mysql_query(&con, query_buffer));
+ DIE_IF(mysql_query(&con, "SET @@session.max_join_size = default"));
+
+ /* Issue COM_CHANGE_USER. */
+
+ bug20023_change_user(&con);
+
+ /* Query SQL_BIG_SELECTS. */
+
+ query_int_variable(&con,
+ "@@session.sql_big_selects",
+ &sql_big_selects_5);
+
+ /* Check that SQL_BIG_SELECTS is 1. */
+
+ DIE_UNLESS(sql_big_selects_5 == sql_big_selects_orig);
+
+ /***********************************************************************
+ That's it. Cleanup.
+ ***********************************************************************/
+
+ mysql_close(&con);
+}
+
+static void bug31418_impl()
+{
+ MYSQL con;
+
+ bool is_null;
+ int rc;
+
+ /* Create a new connection. */
+
+ DIE_UNLESS(mysql_init(&con));
+
+ DIE_UNLESS(mysql_real_connect(&con,
+ opt_host,
+ opt_user,
+ opt_password,
+ opt_db ? opt_db : "test",
+ opt_port,
+ opt_unix_socket,
+ CLIENT_FOUND_ROWS));
+
+ /***********************************************************************
+ Check that lock is free:
+ - IS_FREE_LOCK() should return 1;
+ - IS_USED_LOCK() should return NULL;
+ ***********************************************************************/
+
+ is_null= query_int_variable(&con,
+ "IS_FREE_LOCK('bug31418')",
+ &rc);
+ DIE_UNLESS(!is_null && rc);
+
+ is_null= query_int_variable(&con,
+ "IS_USED_LOCK('bug31418')",
+ &rc);
+ DIE_UNLESS(is_null);
+
+ /***********************************************************************
+ Acquire lock and check the lock status (the lock must be in use):
+ - IS_FREE_LOCK() should return 0;
+ - IS_USED_LOCK() should return non-zero thread id;
+ ***********************************************************************/
+
+ query_int_variable(&con, "GET_LOCK('bug31418', 1)", &rc);
+ DIE_UNLESS(rc);
+
+ is_null= query_int_variable(&con,
+ "IS_FREE_LOCK('bug31418')",
+ &rc);
+ DIE_UNLESS(!is_null && !rc);
+
+ is_null= query_int_variable(&con,
+ "IS_USED_LOCK('bug31418')",
+ &rc);
+ DIE_UNLESS(!is_null && rc);
+
+ /***********************************************************************
+ Issue COM_CHANGE_USER command and check the lock status
+ (the lock must be free):
+ - IS_FREE_LOCK() should return 1;
+ - IS_USED_LOCK() should return NULL;
+ **********************************************************************/
+
+ bug20023_change_user(&con);
+
+ is_null= query_int_variable(&con,
+ "IS_FREE_LOCK('bug31418')",
+ &rc);
+ DIE_UNLESS(!is_null && rc);
+
+ is_null= query_int_variable(&con,
+ "IS_USED_LOCK('bug31418')",
+ &rc);
+ DIE_UNLESS(is_null);
+
+ /***********************************************************************
+ That's it. Cleanup.
+ ***********************************************************************/
+
+ mysql_close(&con);
+}
+
+static void test_bug31418()
+{
+ /* Run test case for BUG#31418 for three different connections. */
+
+ bug31418_impl();
+
+ bug31418_impl();
+
+ bug31418_impl();
+}
+
/*
Read and parse arguments and MySQL options from my.cnf
@@ -17115,6 +17386,8 @@ static struct my_tests_st my_tests[]= {
{ "test_bug29306", test_bug29306 },
{ "test_change_user", test_change_user },
{ "test_bug30472", test_bug30472 },
+ { "test_bug20023", test_bug20023 },
+ { "test_bug31418", test_bug31418 },
{ 0, 0 }
};