Commit 2d8869d2 authored by Georgi Kodinov's avatar Georgi Kodinov

Bug #49734: Crash on EXPLAIN EXTENDED UNION ... ORDER BY <any non-const-function>

Several problems fixed : 
1. Non constant expressions in UNION ... ORDER BY were not correctly cleaned up
in st_select_lex_unit::cleanup() causing crashes in EXPLAIN EXTENDED because of
fields quoted by these expressions pointing to the already freed temporary table
used to calculate the UNION.
Fixed by correctly cleaning up expressions of any depth.

2. Subqueries in the order by part of UNION ... ORDER BY ... caused a crash in 
EXPLAIN EXTENDED because of a transformation attempt made during EXPLAIN EXTENDED
execution. Fixed by not doing the transformation when in EXPLAIN.

3. Fulltext functions caused crash when in the ORDER BY part of an un-parenthesized
UNION that gets "promoted" to be valid for the whole union, e.g. 
SELECT * FROM t1 UNION SELECT * FROM t2 ORDER BY MATCHES (a) AGAINST ('abc' IN BOOLEAN MODE).
This is a case that demonstrates a more general problem of parts of the query being
moved to another level. When doing such transformation late in the optimization run
when most of the flags about the contents of the query are already aggregated it's possible 
to "split" the flags so that they correctly reflect the new queries after the transformation.
In specific the ST_SELECT_LEX::ftfunc_list is holding all the free text function for all the 
parts of the second SELECT in the UNION and we don't know what part of that is in the ORDER BY
that we're to move to the UNION level and what part is about the other parts of the second SELECT.
Fixed by throwing and error when such statements are about to be processed by adding a check 
for the presence of MATCH() inside the ORDER BY clause that's going to get promoted to UNION.
To workaround this new limitation one must parenthesize the UNION SELECTs and provide a real 
global ORDER BY for the UNION outside of the parenthesis.
parent c248859e
...@@ -126,7 +126,7 @@ group by ...@@ -126,7 +126,7 @@ group by
a.text, b.id, b.betreff a.text, b.id, b.betreff
order by order by
match(b.betreff) against ('+abc' in boolean mode) desc; match(b.betreff) against ('+abc' in boolean mode) desc;
ERROR 42S22: Unknown column 'b.betreff' in 'order clause' ERROR 42000: Incorrect usage/placement of 'MATCH()'
select a.text, b.id, b.betreff select a.text, b.id, b.betreff
from from
t2 a inner join t3 b on a.id = b.forum inner join t2 a inner join t3 b on a.id = b.forum inner join
...@@ -142,7 +142,7 @@ where ...@@ -142,7 +142,7 @@ where
match(c.beitrag) against ('+abc' in boolean mode) match(c.beitrag) against ('+abc' in boolean mode)
order by order by
match(b.betreff) against ('+abc' in boolean mode) desc; match(b.betreff) against ('+abc' in boolean mode) desc;
ERROR 42S22: Unknown column 'b.betreff' in 'order clause' ERROR 42000: Incorrect usage/placement of 'MATCH()'
select a.text, b.id, b.betreff select a.text, b.id, b.betreff
from from
t2 a inner join t3 b on a.id = b.forum inner join t2 a inner join t3 b on a.id = b.forum inner join
...@@ -158,7 +158,7 @@ where ...@@ -158,7 +158,7 @@ where
match(c.beitrag) against ('+abc' in boolean mode) match(c.beitrag) against ('+abc' in boolean mode)
order by order by
match(betreff) against ('+abc' in boolean mode) desc; match(betreff) against ('+abc' in boolean mode) desc;
text id betreff ERROR 42000: Incorrect usage/placement of 'MATCH()'
(select b.id, b.betreff from t3 b) union (select b.id, b.betreff from t3 b) union
(select b.id, b.betreff from t3 b) (select b.id, b.betreff from t3 b)
order by match(betreff) against ('+abc' in boolean mode) desc; order by match(betreff) against ('+abc' in boolean mode) desc;
......
...@@ -1588,3 +1588,66 @@ Warnings: ...@@ -1588,3 +1588,66 @@ Warnings:
Note 1003 select '0' AS `a` from `test`.`t1` union select '0' AS `a` from `test`.`t1` order by `a` Note 1003 select '0' AS `a` from `test`.`t1` union select '0' AS `a` from `test`.`t1` order by `a`
DROP TABLE t1; DROP TABLE t1;
End of 5.0 tests End of 5.0 tests
#
# Bug #49734: Crash on EXPLAIN EXTENDED UNION ... ORDER BY
# <any non-const-function>
#
CREATE TABLE t1 (a VARCHAR(10), FULLTEXT KEY a (a));
INSERT INTO t1 VALUES (1),(2);
CREATE TABLE t2 (b INT);
INSERT INTO t2 VALUES (1),(2);
# Should not crash
EXPLAIN EXTENDED
SELECT * FROM t1 UNION SELECT * FROM t1 ORDER BY a + 12;
id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY t1 ALL NULL NULL NULL NULL 2 100.00
2 UNION t1 ALL NULL NULL NULL NULL 2 100.00
NULL UNION RESULT <union1,2> ALL NULL NULL NULL NULL NULL NULL Using filesort
Warnings:
Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` union select `test`.`t1`.`a` AS `a` from `test`.`t1` order by (`a` + 12)
# Should not crash
SELECT * FROM t1 UNION SELECT * FROM t1 ORDER BY a + 12;
a
1
2
# Should not crash
EXPLAIN EXTENDED
SELECT * FROM t1 UNION SELECT * FROM t1
ORDER BY MATCH(a) AGAINST ('+abc' IN BOOLEAN MODE);
ERROR 42000: Incorrect usage/placement of 'MATCH()'
# Should not crash
SELECT * FROM t1 UNION SELECT * FROM t1
ORDER BY MATCH(a) AGAINST ('+abc' IN BOOLEAN MODE);
ERROR 42000: Incorrect usage/placement of 'MATCH()'
# Should not crash
(SELECT * FROM t1) UNION (SELECT * FROM t1)
ORDER BY MATCH(a) AGAINST ('+abc' IN BOOLEAN MODE);
a
1
2
# Should not crash
EXPLAIN EXTENDED
SELECT * FROM t1 UNION SELECT * FROM t1
ORDER BY (SELECT a FROM t2 WHERE b = 12);
id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY t1 ALL NULL NULL NULL NULL 2 100.00
2 UNION t1 ALL NULL NULL NULL NULL 2 100.00
3 SUBQUERY t2 ALL NULL NULL NULL NULL 2 100.00 Using where
NULL UNION RESULT <union1,2> ALL NULL NULL NULL NULL NULL NULL Using filesort
Warnings:
Note 1276 Field or reference 'test.t1.a' of SELECT #3 was resolved in SELECT #2
Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` union select `test`.`t1`.`a` AS `a` from `test`.`t1` order by (select `test`.`t1`.`a` AS `a` from `test`.`t2` where (`test`.`t2`.`b` = 12))
# Should not crash
SELECT * FROM t1 UNION SELECT * FROM t1
ORDER BY (SELECT a FROM t2 WHERE b = 12);
a
1
2
# Should not crash
SELECT * FROM t2 UNION SELECT * FROM t2
ORDER BY (SELECT * FROM t1 WHERE MATCH(a) AGAINST ('+abc' IN BOOLEAN MODE));
b
1
2
DROP TABLE t1,t2;
End of 5.1 tests
...@@ -80,7 +80,7 @@ CREATE TABLE t3 ( ...@@ -80,7 +80,7 @@ CREATE TABLE t3 (
FULLTEXT KEY betreff (betreff) FULLTEXT KEY betreff (betreff)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=996 ; ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=996 ;
--error 1054 --error ER_CANT_USE_OPTION_HERE
select a.text, b.id, b.betreff select a.text, b.id, b.betreff
from from
t2 a inner join t3 b on a.id = b.forum inner join t2 a inner join t3 b on a.id = b.forum inner join
...@@ -100,7 +100,7 @@ group by ...@@ -100,7 +100,7 @@ group by
order by order by
match(b.betreff) against ('+abc' in boolean mode) desc; match(b.betreff) against ('+abc' in boolean mode) desc;
--error 1054 --error ER_CANT_USE_OPTION_HERE
select a.text, b.id, b.betreff select a.text, b.id, b.betreff
from from
t2 a inner join t3 b on a.id = b.forum inner join t2 a inner join t3 b on a.id = b.forum inner join
...@@ -117,6 +117,7 @@ where ...@@ -117,6 +117,7 @@ where
order by order by
match(b.betreff) against ('+abc' in boolean mode) desc; match(b.betreff) against ('+abc' in boolean mode) desc;
--error ER_CANT_USE_OPTION_HERE
select a.text, b.id, b.betreff select a.text, b.id, b.betreff
from from
t2 a inner join t3 b on a.id = b.forum inner join t2 a inner join t3 b on a.id = b.forum inner join
......
...@@ -1102,3 +1102,56 @@ DROP TABLE t1; ...@@ -1102,3 +1102,56 @@ DROP TABLE t1;
--echo End of 5.0 tests --echo End of 5.0 tests
--echo #
--echo # Bug #49734: Crash on EXPLAIN EXTENDED UNION ... ORDER BY
--echo # <any non-const-function>
--echo #
CREATE TABLE t1 (a VARCHAR(10), FULLTEXT KEY a (a));
INSERT INTO t1 VALUES (1),(2);
CREATE TABLE t2 (b INT);
INSERT INTO t2 VALUES (1),(2);
--echo # Should not crash
EXPLAIN EXTENDED
SELECT * FROM t1 UNION SELECT * FROM t1 ORDER BY a + 12;
--echo # Should not crash
SELECT * FROM t1 UNION SELECT * FROM t1 ORDER BY a + 12;
--echo # Should not crash
--error ER_CANT_USE_OPTION_HERE
EXPLAIN EXTENDED
SELECT * FROM t1 UNION SELECT * FROM t1
ORDER BY MATCH(a) AGAINST ('+abc' IN BOOLEAN MODE);
--echo # Should not crash
--error ER_CANT_USE_OPTION_HERE
SELECT * FROM t1 UNION SELECT * FROM t1
ORDER BY MATCH(a) AGAINST ('+abc' IN BOOLEAN MODE);
--echo # Should not crash
(SELECT * FROM t1) UNION (SELECT * FROM t1)
ORDER BY MATCH(a) AGAINST ('+abc' IN BOOLEAN MODE);
--echo # Should not crash
EXPLAIN EXTENDED
SELECT * FROM t1 UNION SELECT * FROM t1
ORDER BY (SELECT a FROM t2 WHERE b = 12);
--echo # Should not crash
SELECT * FROM t1 UNION SELECT * FROM t1
ORDER BY (SELECT a FROM t2 WHERE b = 12);
--echo # Should not crash
SELECT * FROM t2 UNION SELECT * FROM t2
ORDER BY (SELECT * FROM t1 WHERE MATCH(a) AGAINST ('+abc' IN BOOLEAN MODE));
DROP TABLE t1,t2;
--echo End of 5.1 tests
...@@ -967,6 +967,23 @@ class Item { ...@@ -967,6 +967,23 @@ class Item {
return FALSE; return FALSE;
} }
/**
Find a function of a given type
@param arg the function type to search (enum Item_func::Functype)
@return
@retval TRUE the function type we're searching for is found
@retval FALSE the function type wasn't found
@description
This function can be used (together with Item::walk()) to find functions
in an item tree fragment.
*/
virtual bool find_function_processor (uchar *arg)
{
return FALSE;
}
/* /*
For SP local variable returns pointer to Item representing its For SP local variable returns pointer to Item representing its
current value and pointer to current Item otherwise. current value and pointer to current Item otherwise.
......
...@@ -212,6 +212,11 @@ class Item_func :public Item_result_field ...@@ -212,6 +212,11 @@ class Item_func :public Item_result_field
{ {
return has_timestamp_args(); return has_timestamp_args();
} }
virtual bool find_function_processor (uchar *arg)
{
return functype() == *(Functype *) arg;
}
}; };
......
...@@ -519,7 +519,7 @@ JOIN::prepare(Item ***rref_pointer_array, ...@@ -519,7 +519,7 @@ JOIN::prepare(Item ***rref_pointer_array,
thd->lex->allow_sum_func= save_allow_sum_func; thd->lex->allow_sum_func= save_allow_sum_func;
} }
if (!thd->lex->view_prepare_mode) if (!thd->lex->view_prepare_mode && !(select_options & SELECT_DESCRIBE))
{ {
Item_subselect *subselect; Item_subselect *subselect;
/* Is it subselect? */ /* Is it subselect? */
......
...@@ -335,6 +335,35 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, ...@@ -335,6 +335,35 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
} }
} }
/*
Disable the usage of fulltext searches in the last union branch.
This is a temporary 5.x limitation because of the way the fulltext
search functions are handled by the optimizer.
This is manifestation of the more general problems of "taking away"
parts of a SELECT statement post-fix_fields(). This is generally not
doable since various flags are collected in various places (e.g.
SELECT_LEX) that carry information about the presence of certain
expressions or constructs in the parts of the query.
When part of the query is taken away it's not clear how to "divide"
the meaning of these accumulated flags and what to carry over to the
recipient query (SELECT_LEX).
*/
if (global_parameters->ftfunc_list->elements &&
global_parameters->order_list.elements &&
global_parameters != fake_select_lex)
{
ORDER *ord;
Item_func::Functype ft= Item_func::FT_FUNC;
for (ord= (ORDER*)global_parameters->order_list.first; ord; ord= ord->next)
if ((*ord->item)->walk (&Item::find_function_processor, FALSE,
(uchar *) &ft))
{
my_error (ER_CANT_USE_OPTION_HERE, MYF(0), "MATCH()");
goto err;
}
}
create_options= (first_sl->options | thd_arg->options | create_options= (first_sl->options | thd_arg->options |
TMP_TABLE_ALL_COLUMNS); TMP_TABLE_ALL_COLUMNS);
/* /*
...@@ -669,7 +698,7 @@ bool st_select_lex_unit::cleanup() ...@@ -669,7 +698,7 @@ bool st_select_lex_unit::cleanup()
{ {
ORDER *ord; ORDER *ord;
for (ord= (ORDER*)global_parameters->order_list.first; ord; ord= ord->next) for (ord= (ORDER*)global_parameters->order_list.first; ord; ord= ord->next)
(*ord->item)->cleanup(); (*ord->item)->walk (&Item::cleanup_processor, 0, 0);
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment