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.
This commit is contained in:
parent
bc1f457194
commit
fc7514151f
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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; }
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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:
|
||||
|
Loading…
x
Reference in New Issue
Block a user