From 4b04ce8085a9f106cfd43ac28727b3933a0b65fe Mon Sep 17 00:00:00 2001 From: "evgen@moonbone.local" <> Date: Thu, 20 Apr 2006 12:35:33 +0400 Subject: [PATCH 1/2] func_gconcat.test: Clean up test case for bug#14169 --- mysql-test/t/func_gconcat.test | 1 - 1 file changed, 1 deletion(-) diff --git a/mysql-test/t/func_gconcat.test b/mysql-test/t/func_gconcat.test index ed35c7534f6..8f50690dd8b 100644 --- a/mysql-test/t/func_gconcat.test +++ b/mysql-test/t/func_gconcat.test @@ -398,7 +398,6 @@ create table t1 (f1 int unsigned, f2 varchar(255)); insert into t1 values (1,repeat('a',255)),(2,repeat('b',255)); --enable_metadata select f2,group_concat(f1) from t1 group by f2; -# select f2,group_concat(f1) from t1 group by f2 order by 2; --disable_metadata drop table t1; From fc7514151f9daf5af9df7abd7abb61574bdebc80 Mon Sep 17 00:00:00 2001 From: "igor@rurik.mysql.com" <> Date: Thu, 20 Apr 2006 22:15:38 -0700 Subject: [PATCH 2/2] Fixed bug #18767. The bug caused wrong result sets for union constructs of the form (SELECT ... ORDER BY order_list1 [LIMIT n]) ORDER BY order_list2. For such queries order lists were concatenated and limit clause was completely neglected. --- mysql-test/r/order_by.result | 32 ++++++++++++++++ mysql-test/t/order_by.test | 14 +++++++ sql/sql_lex.h | 1 + sql/sql_parse.cc | 74 +++++++++++++++++++++++++++--------- sql/sql_select.cc | 2 +- sql/sql_union.cc | 2 +- sql/sql_yacc.yy | 24 ++++++++++-- 7 files changed, 127 insertions(+), 22 deletions(-) diff --git a/mysql-test/r/order_by.result b/mysql-test/r/order_by.result index ee8ca5f0328..8126e223f55 100644 --- a/mysql-test/r/order_by.result +++ b/mysql-test/r/order_by.result @@ -788,3 +788,35 @@ a 2 2 DROP TABLE t1; +CREATE TABLE t1 (a int, b int); +INSERT INTO t1 VALUES (1,30), (2,20), (1,10), (2,30), (1,20), (2,10); +(SELECT b,a FROM t1 ORDER BY a,b) ORDER BY b,a; +b a +10 1 +10 2 +20 1 +20 2 +30 1 +30 2 +(SELECT b FROM t1 ORDER BY b DESC) ORDER BY b ASC; +b +10 +10 +20 +20 +30 +30 +(SELECT b,a FROM t1 ORDER BY b,a) ORDER BY a,b; +b a +10 1 +20 1 +30 1 +10 2 +20 2 +30 2 +(SELECT b,a FROM t1 ORDER by b,a LIMIT 3) ORDER by a,b; +b a +10 1 +20 1 +10 2 +DROP TABLE t1; diff --git a/mysql-test/t/order_by.test b/mysql-test/t/order_by.test index 4b7ec84dc54..1664afc70f9 100644 --- a/mysql-test/t/order_by.test +++ b/mysql-test/t/order_by.test @@ -545,4 +545,18 @@ SELECT a FROM t1 ORDER BY a; (SELECT a FROM t1) ORDER BY a; DROP TABLE t1; +# +# Bug #18767: global ORDER BY applied to a SELECT with ORDER BY either was +# ignored or 'concatened' to the latter. + +CREATE TABLE t1 (a int, b int); +INSERT INTO t1 VALUES (1,30), (2,20), (1,10), (2,30), (1,20), (2,10); + +(SELECT b,a FROM t1 ORDER BY a,b) ORDER BY b,a; +(SELECT b FROM t1 ORDER BY b DESC) ORDER BY b ASC; +(SELECT b,a FROM t1 ORDER BY b,a) ORDER BY a,b; +(SELECT b,a FROM t1 ORDER by b,a LIMIT 3) ORDER by a,b; + +DROP TABLE t1; + # End of 4.1 tests diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 47908ba8685..bd79a194122 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -400,6 +400,7 @@ public: bool check_updateable(char *db, char *table); void print(String *str); + bool add_fake_select_lex(THD *thd); ulong init_prepare_fake_select_lex(THD *thd); int change_result(select_subselect *result, select_subselect *old_result); inline bool is_prepared() { return prepared; } diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 21a335637b9..e25a428aaa7 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -4189,23 +4189,9 @@ mysql_new_select(LEX *lex, bool move_down) else { select_lex->include_neighbour(lex->current_select); - SELECT_LEX_UNIT *unit= select_lex->master_unit(); - SELECT_LEX *fake= unit->fake_select_lex; - if (!fake) - { - /* - as far as we included SELECT_LEX for UNION unit should have - fake SELECT_LEX for UNION processing - */ - if (!(fake= unit->fake_select_lex= new(lex->thd->mem_root) SELECT_LEX())) - return 1; - fake->include_standalone(unit, - (SELECT_LEX_NODE**)&unit->fake_select_lex); - fake->select_number= INT_MAX; - fake->make_empty_select(); - fake->linkage= GLOBAL_OPTIONS_TYPE; - fake->select_limit= HA_POS_ERROR; - } + if (!select_lex->master_unit()->fake_select_lex && + select_lex->master_unit()->add_fake_select_lex(lex->thd)) + return 1; } select_lex->master_unit()->global_parameters= select_lex; @@ -4975,6 +4961,60 @@ void st_select_lex::set_lock_for_tables(thr_lock_type lock_type) } +/* + Create a fake SELECT_LEX for a unit + + SYNOPSIS: + add_fake_select_lex() + thd thread handle + + DESCRIPTION + The method create a fake SELECT_LEX object for a unit. + This object is created for any union construct containing a union + operation and also for any single select union construct of the form + (SELECT ... ORDER BY order_list [LIMIT n]) ORDER BY ... + or of the form + (SELECT ... ORDER BY LIMIT n) ORDER BY ... + + NOTES + The object is used to retrieve rows from the temporary table + where the result on the union is obtained. + + RETURN VALUES + 1 on failure to create the object + 0 on success +*/ + +bool st_select_lex_unit::add_fake_select_lex(THD *thd) +{ + SELECT_LEX *first_sl= first_select(); + DBUG_ENTER("add_fake_select_lex"); + DBUG_ASSERT(!fake_select_lex); + + if (!(fake_select_lex= new (thd->mem_root) SELECT_LEX())) + DBUG_RETURN(1); + fake_select_lex->include_standalone(this, + (SELECT_LEX_NODE**)&fake_select_lex); + fake_select_lex->select_number= INT_MAX; + fake_select_lex->make_empty_select(); + fake_select_lex->linkage= GLOBAL_OPTIONS_TYPE; + fake_select_lex->select_limit= HA_POS_ERROR; + + if (!first_sl->next_select()) + { + /* + This works only for + (SELECT ... ORDER BY list [LIMIT n]) ORDER BY order_list [LIMIT m], + (SELECT ... LIMIT n) ORDER BY order_list [LIMIT m] + just before the parser starts processing order_list + */ + global_parameters= fake_select_lex; + fake_select_lex->no_table_names_allowed= 1; + thd->lex->current_select= fake_select_lex; + } + DBUG_RETURN(0); +} + void add_join_on(TABLE_LIST *b,Item *expr) { if (expr) diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 91fc808058f..46dba61cfc5 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -171,7 +171,7 @@ int handle_select(THD *thd, LEX *lex, select_result *result) register SELECT_LEX *select_lex = &lex->select_lex; DBUG_ENTER("handle_select"); - if (select_lex->next_select()) + if (select_lex->next_select() || select_lex->master_unit()->fake_select_lex) res=mysql_union(thd, lex, result, &lex->unit); else res= mysql_select(thd, &select_lex->ref_pointer_array, diff --git a/sql/sql_union.cc b/sql/sql_union.cc index 8bb53f7b573..0948602bbb4 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -184,7 +184,7 @@ int st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, thd_arg->lex->current_select= sl= first_select= first_select_in_union(); found_rows_for_union= first_select->options & OPTION_FOUND_ROWS; - is_union= test(first_select->next_select()); + is_union= test(first_select->next_select() || fake_select_lex); /* Global option */ diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 05d95b57abb..162b4183c84 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -3790,15 +3790,33 @@ order_clause: ORDER_SYM BY { LEX *lex=Lex; - if (lex->current_select->linkage != GLOBAL_OPTIONS_TYPE && - lex->current_select->olap != - UNSPECIFIED_OLAP_TYPE) + SELECT_LEX *sel= lex->current_select; + SELECT_LEX_UNIT *unit= sel-> master_unit(); + if (sel->linkage != GLOBAL_OPTIONS_TYPE && + sel->olap != UNSPECIFIED_OLAP_TYPE) { net_printf(lex->thd, ER_WRONG_USAGE, "CUBE/ROLLUP", "ORDER BY"); YYABORT; } + if (lex->sql_command != SQLCOM_ALTER_TABLE && !unit->fake_select_lex) + { + /* + A query of the of the form (SELECT ...) ORDER BY order_list is + executed in the same way as the query + SELECT ... ORDER BY order_list + unless the SELECT construct contains ORDER BY or LIMIT clauses. + Otherwise we create a fake SELECT_LEX if it has not been created + yet. + */ + SELECT_LEX *first_sl= unit->first_select(); + if (!first_sl->next_select() && + (first_sl->order_list.elements || + first_sl->select_limit != HA_POS_ERROR) && + unit->add_fake_select_lex(lex->thd)) + YYABORT; + } } order_list; order_list: