From 1b64516756174c5de34ad77e54e3ecf164863ec4 Mon Sep 17 00:00:00 2001 From: Magne Mahre Date: Mon, 10 Jan 2011 13:16:50 +0100 Subject: [PATCH 1/4] Bug#57986 ORDER BY clause is not used after a UNION, if embedded in a SELECT An ORDER BY clause was bound to the incorrect (sub-)statement when used in a UNION context. In a query like: SELECT * FROM a UNION SELECT * FROM b ORDER BY c the result of SELECT * FROM b is sorted, and then combined with a. The correct behaviour is that the ORDER BY clause should be applied on the final set. Similar behaviour was seen on LIMIT clauses as well. In a UNION statement, there will be a select_lex object for each of the two selects, and a select_lex_unit object that describes the UNION itself. Similarly, the same behaviour was also seen on derived tables. The bug was caused by using a grammar rule for ORDER BY and LIMIT that bound these elements to thd->lex->current_select, which points to the last of the two selects, instead of to the fake_select_lex member of the master select_lex_unit object. sql/sql_yacc.yy: Need to use (opt_)union_order_or_limit to bind to the correct select_lex object. --- mysql-test/r/union.result | 100 ++++++++++++++++++++++++++++++++++++++ mysql-test/t/union.test | 54 ++++++++++++++++++++ sql/sql_yacc.yy | 9 +++- 3 files changed, 161 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/union.result b/mysql-test/r/union.result index 33fc4333d1c..7f0ec2275d7 100644 --- a/mysql-test/r/union.result +++ b/mysql-test/r/union.result @@ -1644,3 +1644,103 @@ b 2 DROP TABLE t1,t2; End of 5.1 tests +# +# Bug#57986 ORDER BY clause is not used after a UNION, +# if embedded in a SELECT +# +CREATE TABLE t1 (c1 VARCHAR(10) NOT NULL, c2 INT NOT NULL); +CREATE TABLE t2 (c1 VARCHAR(10) NOT NULL, c2 INT NOT NULL); +INSERT INTO t1 (c1, c2) VALUES ('t1a', 1), ('t1a', 2), ('t1a', 3), ('t1b', 2), ('t1b', 1); +INSERT INTO t2 (c1, c2) VALUES ('t2a', 1), ('t2a', 2), ('t2a', 3), ('t2b', 2), ('t2b', 1); +SELECT * FROM t1 UNION SELECT * FROM t2 ORDER BY c2, c1; +c1 c2 +t1a 1 +t1b 1 +t2a 1 +t2b 1 +t1a 2 +t1b 2 +t2a 2 +t2b 2 +t1a 3 +t2a 3 +SELECT * FROM t1 UNION (SELECT * FROM t2) ORDER BY c2, c1; +c1 c2 +t1a 1 +t1b 1 +t2a 1 +t2b 1 +t1a 2 +t1b 2 +t2a 2 +t2b 2 +t1a 3 +t2a 3 +SELECT * FROM t1 UNION (SELECT * FROM t2 ORDER BY c2, c1); +c1 c2 +t1a 1 +t1a 2 +t1a 3 +t1b 2 +t1b 1 +t2a 1 +t2a 2 +t2a 3 +t2b 2 +t2b 1 +SELECT c1, c2 FROM ( +SELECT c1, c2 FROM t1 +UNION +(SELECT c1, c2 FROM t2) +ORDER BY c2, c1 +) AS res; +c1 c2 +t1a 1 +t1b 1 +t2a 1 +t2b 1 +t1a 2 +t1b 2 +t2a 2 +t2b 2 +t1a 3 +t2a 3 +SELECT c1, c2 FROM ( +SELECT c1, c2 FROM t1 +UNION +(SELECT c1, c2 FROM t2) +ORDER BY c2 DESC, c1 LIMIT 1 +) AS res; +c1 c2 +t1a 3 +SELECT c1, c2 FROM ( +SELECT c1, c2 FROM t1 +UNION +(SELECT c1, c2 FROM t2 ORDER BY c2 DESC, c1 LIMIT 1) +) AS res; +c1 c2 +t1a 1 +t1a 2 +t1a 3 +t1b 2 +t1b 1 +t2a 3 +SELECT c1, c2 FROM ( +SELECT c1, c2 FROM t1 +UNION +SELECT c1, c2 FROM t2 +ORDER BY c2 DESC, c1 DESC LIMIT 1 +) AS res; +c1 c2 +t2a 3 +SELECT c1, c2 FROM ( +( +(SELECT c1, c2 FROM t1) +UNION +(SELECT c1, c2 FROM t2) +) +ORDER BY c2 DESC, c1 ASC LIMIT 1 +) AS res; +c1 c2 +t1a 3 +DROP TABLE t1, t2; diff --git a/mysql-test/t/union.test b/mysql-test/t/union.test index 596fc5f41ef..694f1bab15c 100644 --- a/mysql-test/t/union.test +++ b/mysql-test/t/union.test @@ -1117,3 +1117,57 @@ DROP TABLE t1,t2; --echo End of 5.1 tests + +--echo # +--echo # Bug#57986 ORDER BY clause is not used after a UNION, +--echo # if embedded in a SELECT +--echo # + +CREATE TABLE t1 (c1 VARCHAR(10) NOT NULL, c2 INT NOT NULL); +CREATE TABLE t2 (c1 VARCHAR(10) NOT NULL, c2 INT NOT NULL); + + +INSERT INTO t1 (c1, c2) VALUES ('t1a', 1), ('t1a', 2), ('t1a', 3), ('t1b', 2), ('t1b', 1); +INSERT INTO t2 (c1, c2) VALUES ('t2a', 1), ('t2a', 2), ('t2a', 3), ('t2b', 2), ('t2b', 1); + +SELECT * FROM t1 UNION SELECT * FROM t2 ORDER BY c2, c1; +SELECT * FROM t1 UNION (SELECT * FROM t2) ORDER BY c2, c1; +SELECT * FROM t1 UNION (SELECT * FROM t2 ORDER BY c2, c1); + +SELECT c1, c2 FROM ( + SELECT c1, c2 FROM t1 + UNION + (SELECT c1, c2 FROM t2) + ORDER BY c2, c1 +) AS res; + +SELECT c1, c2 FROM ( + SELECT c1, c2 FROM t1 + UNION + (SELECT c1, c2 FROM t2) + ORDER BY c2 DESC, c1 LIMIT 1 +) AS res; + +SELECT c1, c2 FROM ( + SELECT c1, c2 FROM t1 + UNION + (SELECT c1, c2 FROM t2 ORDER BY c2 DESC, c1 LIMIT 1) +) AS res; + +SELECT c1, c2 FROM ( + SELECT c1, c2 FROM t1 + UNION + SELECT c1, c2 FROM t2 + ORDER BY c2 DESC, c1 DESC LIMIT 1 +) AS res; + +SELECT c1, c2 FROM ( + ( + (SELECT c1, c2 FROM t1) + UNION + (SELECT c1, c2 FROM t2) + ) + ORDER BY c2 DESC, c1 ASC LIMIT 1 +) AS res; + +DROP TABLE t1, t2; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 9aa938437b1..c283747156a 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -9396,7 +9396,7 @@ table_factor: ; select_derived_union: - select_derived opt_order_clause opt_limit_clause + select_derived opt_union_order_or_limit | select_derived_union UNION_SYM union_option @@ -9412,7 +9412,7 @@ select_derived_union: */ Lex->pop_context(); } - opt_order_clause opt_limit_clause + opt_union_order_or_limit ; /* The equivalent of select_init2 for nested queries. */ @@ -13862,6 +13862,11 @@ union_opt: | union_order_or_limit { $$= 1; } ; +opt_union_order_or_limit: + /* Empty */ + | union_order_or_limit + ; + union_order_or_limit: { THD *thd= YYTHD; From 90650edf697d498d0969f4bd9b81301ffc633dfa Mon Sep 17 00:00:00 2001 From: Magne Mahre Date: Mon, 10 Jan 2011 13:43:12 +0100 Subject: [PATCH 2/4] Bug#58970 Problem Subquery (without referencing a table) and Order By When having a UNION statement in a subquery, with no referenced tables (or only a reference to the virtual table 'dual'), the UNION did not allow an ORDER BY clause. i.e: SELECT(SELECT 1 AS a UNION SELECT 0 AS a ORDER BY a) AS b or SELECT(SELECT 1 AS a FROM dual UNION SELECT 0 as a ORDER BY a) AS b In addition, an ORDER BY / LIMIT clause was not accepted in subqueries even for single SELECT statements with no referenced tables (or with 'dual' as table reference) i.e: SELECT(SELECT 1 AS a ORDER BY a) AS b or SELECT(SELECT 1 AS a FROM dual ORDER BY a) AS b The fix was to allow an optional ORDER BY/LIMIT clause to the grammar for these cases. See also: Bug#57986 --- mysql-test/r/union.result | 25 +++++++++++++++++++++++++ mysql-test/t/union.test | 13 +++++++++++++ sql/sql_yacc.yy | 3 ++- 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/union.result b/mysql-test/r/union.result index 7f0ec2275d7..1f3422c1767 100644 --- a/mysql-test/r/union.result +++ b/mysql-test/r/union.result @@ -1744,3 +1744,28 @@ ORDER BY c2 DESC, c1 ASC LIMIT 1 c1 c2 t1a 3 DROP TABLE t1, t2; +# +# Bug #58970 Problem Subquery (without referencing a table) +# and Order By +# +SELECT(SELECT 0 AS a UNION SELECT 1 AS a ORDER BY a ASC LIMIT 1) AS dev; +dev +0 +SELECT(SELECT 0 AS a UNION SELECT 1 AS a ORDER BY a DESC LIMIT 1) AS dev; +dev +1 +SELECT(SELECT 0 AS a FROM dual UNION SELECT 1 AS a FROM dual ORDER BY a ASC LIMIT 1) AS dev; +dev +0 +SELECT(SELECT 0 AS a FROM dual UNION SELECT 1 AS a FROM dual ORDER BY a DESC LIMIT 1) AS dev; +dev +1 +SELECT(SELECT 1 AS a ORDER BY a) AS dev; +dev +1 +SELECT(SELECT 1 AS a LIMIT 1) AS dev; +dev +1 +SELECT(SELECT 1 AS a FROM dual ORDER BY a DESC LIMIT 1) AS dev; +dev +1 diff --git a/mysql-test/t/union.test b/mysql-test/t/union.test index 694f1bab15c..c6599517e90 100644 --- a/mysql-test/t/union.test +++ b/mysql-test/t/union.test @@ -1171,3 +1171,16 @@ SELECT c1, c2 FROM ( ) AS res; DROP TABLE t1, t2; + +--echo # +--echo # Bug #58970 Problem Subquery (without referencing a table) +--echo # and Order By +--echo # + +SELECT(SELECT 0 AS a UNION SELECT 1 AS a ORDER BY a ASC LIMIT 1) AS dev; +SELECT(SELECT 0 AS a UNION SELECT 1 AS a ORDER BY a DESC LIMIT 1) AS dev; +SELECT(SELECT 0 AS a FROM dual UNION SELECT 1 AS a FROM dual ORDER BY a ASC LIMIT 1) AS dev; +SELECT(SELECT 0 AS a FROM dual UNION SELECT 1 AS a FROM dual ORDER BY a DESC LIMIT 1) AS dev; +SELECT(SELECT 1 AS a ORDER BY a) AS dev; +SELECT(SELECT 1 AS a LIMIT 1) AS dev; +SELECT(SELECT 1 AS a FROM dual ORDER BY a DESC LIMIT 1) AS dev; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index c283747156a..397afd26d8d 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -13914,7 +13914,7 @@ query_specification: ; query_expression_body: - query_specification + query_specification opt_union_order_or_limit | query_expression_body UNION_SYM union_option { @@ -13922,6 +13922,7 @@ query_expression_body: MYSQL_YYABORT; } query_specification + opt_union_order_or_limit { Lex->pop_context(); $$= $1; From 6bbfe7c62ae1a5473ed3b3a86af77981cb169f4f Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Mon, 10 Jan 2011 14:12:23 +0100 Subject: [PATCH 3/4] Bug #58933 Assertion `thd- >is_error()' fails on shutdown with ongoing OPTIMIZE TABLE OPTIMIZE TABLE for InnoDB tables is handled as recreate + analyze. The triggered assert checked that an error had been reported if either recreate or analyze failed. However the assert failed to take into account that they could have failed because OPTIMIZE TABLE had been victim of KILL QUERY, KILL CONNECTION or server shutdown. This patch adjusts the assert to take this possibility into account. The problem was only noticeable on debug versions of the server. Test case added to innodb_mysql_sync.test. --- mysql-test/r/innodb_mysql_sync.result | 24 ++++++++++++++++ mysql-test/t/innodb_mysql_sync.test | 41 +++++++++++++++++++++++++++ sql/sql_admin.cc | 4 +-- 3 files changed, 67 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/innodb_mysql_sync.result b/mysql-test/r/innodb_mysql_sync.result index 43a98829d4e..d0ba7b0f2e9 100644 --- a/mysql-test/r/innodb_mysql_sync.result +++ b/mysql-test/r/innodb_mysql_sync.result @@ -66,3 +66,27 @@ SELECT ((@id := id) - id) FROM t2; KILL @id; SET DEBUG_SYNC= "now SIGNAL killed"; DROP TABLE t1, t2; +SET DEBUG_SYNC= "RESET"; +# +# Bug#58933 Assertion `thd- >is_error()' fails on shutdown with ongoing +# OPTIMIZE TABLE +# +DROP TABLE IF EXISTS t1; +CREATE TABLE t1 (a INT) ENGINE=InnoDB; +INSERT INTO t1 VALUES (1), (2); +# Connection con1 +SET DEBUG_SYNC= 'ha_admin_open_ltable SIGNAL waiting WAIT_FOR killed'; +# Sending: +OPTIMIZE TABLE t1; +# Connection default +SET DEBUG_SYNC= 'now WAIT_FOR waiting'; +KILL QUERY ID; +SET DEBUG_SYNC= 'now SIGNAL killed'; +# 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 status Operation failed +# Connection default +DROP TABLE t1; +SET DEBUG_SYNC= 'RESET'; diff --git a/mysql-test/t/innodb_mysql_sync.test b/mysql-test/t/innodb_mysql_sync.test index 07f75afec40..22c6f3874bc 100644 --- a/mysql-test/t/innodb_mysql_sync.test +++ b/mysql-test/t/innodb_mysql_sync.test @@ -104,6 +104,47 @@ SELECT ((@id := id) - id) FROM t2; KILL @id; SET DEBUG_SYNC= "now SIGNAL killed"; DROP TABLE t1, t2; +disconnect con1; +--source include/wait_until_count_sessions.inc +SET DEBUG_SYNC= "RESET"; + + +--echo # +--echo # Bug#58933 Assertion `thd- >is_error()' fails on shutdown with ongoing +--echo # OPTIMIZE TABLE +--echo # + +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings + +CREATE TABLE t1 (a INT) ENGINE=InnoDB; +INSERT INTO t1 VALUES (1), (2); + +--echo # Connection con1 +connect (con1,localhost,root); +let $ID= `SELECT connection_id()`; +SET DEBUG_SYNC= 'ha_admin_open_ltable SIGNAL waiting WAIT_FOR killed'; +--echo # Sending: +--send OPTIMIZE TABLE t1 + +--echo # Connection default +connection default; +SET DEBUG_SYNC= 'now WAIT_FOR waiting'; +--replace_result $ID ID +eval KILL QUERY $ID; +SET DEBUG_SYNC= 'now SIGNAL killed'; + +--echo # Connection con1 +connection con1; +--echo # Reaping: OPTIMIZE TABLE t1 +--reap + +--echo # Connection default +connection default; +DROP TABLE t1; +SET DEBUG_SYNC= 'RESET'; +disconnect con1; # Check that all connections opened by test cases in this file are really diff --git a/sql/sql_admin.cc b/sql/sql_admin.cc index f648d219fac..eb6853751ee 100644 --- a/sql/sql_admin.cc +++ b/sql/sql_admin.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2010 Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2010, 2011 Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -728,7 +728,7 @@ send_result_message: protocol->store(operator_name, system_charset_info); if (result_code) // either mysql_recreate_table or analyze failed { - DBUG_ASSERT(thd->is_error()); + DBUG_ASSERT(thd->is_error() || thd->killed); if (thd->is_error()) { const char *err_msg= thd->stmt_da->message(); From 296d494e661ec181763a72b32154d0f29493259c Mon Sep 17 00:00:00 2001 From: Magne Mahre Date: Mon, 10 Jan 2011 15:18:20 +0100 Subject: [PATCH 4/4] Bug#51631 general-log flag doesn't accept "on" as a value in the my.cnf, works as command Different parsing mechanisms are used for command line/my.cnf options and the SQL commands. The former only accepted numeric arguments, and regarded all numbers different from 0 as 'true'. Any other argument was parsed as 'false' . This patch adds the words 'true' and 'on' as valid truth values for boolean option arguments. A test case is not provided, as the fix is simple and does not warrant a separate test file (no existing suitable test file was found) (backported from mysql-trunk) --- mysys/my_getopt.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/mysys/my_getopt.c b/mysys/my_getopt.c index 5e66d2fc189..51c45ff1309 100644 --- a/mysys/my_getopt.c +++ b/mysys/my_getopt.c @@ -602,6 +602,24 @@ static char *check_struct_option(char *cur_arg, char *key_name) } } +/** + Parse a boolean command line argument + + "ON", "TRUE" and "1" will return true, + other values will return false. + + @param[in] argument The value argument + @return boolean value +*/ +static my_bool get_bool_argument(const char *argument) +{ + if (!my_strcasecmp(&my_charset_latin1, argument, "true") || + !my_strcasecmp(&my_charset_latin1, argument, "on")) + return 1; + else + return (my_bool) atoi(argument); +} + /* function: setval @@ -629,7 +647,7 @@ static int setval(const struct my_option *opts, void *value, char *argument, switch ((opts->var_type & GET_TYPE_MASK)) { case GET_BOOL: /* If argument differs from 0, enable option, else disable */ - *((my_bool*) value)= (my_bool) atoi(argument) != 0; + *((my_bool*) value)= get_bool_argument(argument); break; case GET_INT: *((int*) value)= (int) getopt_ll(argument, opts, &err);