Commit a5e4c349 authored by Galina Shalygina's avatar Galina Shalygina

MDEV-32608: Expression with constant subquery causes a crash in pushdown

from HAVING

The bug is caused by refixing of the constant subquery in pushdown from
HAVING into WHERE optimization.

Similarly to MDEV-29363 in the problematic query two references of the
constant subquery are used. After the pushdown one of the references of the
subquery is pushed into WHERE-clause and the second one remains as the part
of the HAVING-clause.
Before the represented fix, the constant subquery reference that was going to
be pushed into WHERE was cleaned up and fixed. That caused the changes of
the subquery itself and, therefore, changes for the second reference that
remained in HAVING. These changes caused a crash.

To fix this problem all constant objects that are going to be pushed into
WHERE should be marked with an IMMUTABLE_FL flag. Objects marked with this
flag are not cleaned up or fixed in the pushdown optimization.

Approved by Igor Babaev <igor@mariadb.com>
parent eaf7c0cb
......@@ -5092,4 +5092,91 @@ SELECT * FROM v1
GROUP BY a HAVING a = (a IS NULL OR a IS NULL);
a
DROP VIEW v1;
#
# MDEV-32608: Expression with constant subquery causes a crash
# in pushdown from HAVING
#
CREATE TABLE t1 (a INT, b INT);
INSERT INTO t1 VALUES (2, 1), (3, 2);
EXPLAIN FORMAT=JSON SELECT * FROM t1
GROUP BY b
HAVING (SELECT MAX(b) FROM t1) = a AND a + b = 3;
EXPLAIN
{
"query_block": {
"select_id": 1,
"having_condition": "t1.a = (subquery#2)",
"filesort": {
"sort_key": "t1.b",
"temporary_table": {
"table": {
"table_name": "t1",
"access_type": "ALL",
"rows": 2,
"filtered": 100,
"attached_condition": "(subquery#2) + t1.b = 3"
},
"subqueries": [
{
"query_block": {
"select_id": 2,
"table": {
"table_name": "t1",
"access_type": "ALL",
"rows": 2,
"filtered": 100
}
}
}
]
}
}
}
}
SELECT * FROM t1
GROUP BY b
HAVING (SELECT MAX(b) FROM t1) = a AND a + b = 3;
a b
2 1
EXPLAIN FORMAT=JSON SELECT * FROM t1
GROUP BY b
HAVING (SELECT MAX(b) FROM t1) = a AND a > b;
EXPLAIN
{
"query_block": {
"select_id": 1,
"having_condition": "t1.a = (subquery#2)",
"filesort": {
"sort_key": "t1.b",
"temporary_table": {
"table": {
"table_name": "t1",
"access_type": "ALL",
"rows": 2,
"filtered": 100,
"attached_condition": "(subquery#2) > t1.b"
},
"subqueries": [
{
"query_block": {
"select_id": 2,
"table": {
"table_name": "t1",
"access_type": "ALL",
"rows": 2,
"filtered": 100
}
}
}
]
}
}
}
}
SELECT * FROM t1
GROUP BY b
HAVING (SELECT MAX(b) FROM t1) = a AND a > b;
a b
2 1
DROP TABLE t1;
End of 10.5 tests
......@@ -1558,4 +1558,30 @@ SELECT * FROM v1
DROP VIEW v1;
--echo #
--echo # MDEV-32608: Expression with constant subquery causes a crash
--echo # in pushdown from HAVING
--echo #
CREATE TABLE t1 (a INT, b INT);
INSERT INTO t1 VALUES (2, 1), (3, 2);
let $q=
SELECT * FROM t1
GROUP BY b
HAVING (SELECT MAX(b) FROM t1) = a AND a + b = 3;
eval EXPLAIN FORMAT=JSON $q;
eval $q;
let $q=
SELECT * FROM t1
GROUP BY b
HAVING (SELECT MAX(b) FROM t1) = a AND a > b;
eval EXPLAIN FORMAT=JSON $q;
eval $q;
DROP TABLE t1;
--echo End of 10.5 tests
......@@ -1272,6 +1272,25 @@ bool Item::eq(const Item *item, bool binary_cmp) const
}
Item *Item::multiple_equality_transformer(THD *thd, uchar *arg)
{
if (const_item())
{
/*
Mark constant item in the condition with the IMMUTABLE_FL flag.
It is needed to prevent cleanup of the sub-items of this item and following
fix_fields() call that can cause a crash on this step of the optimization.
This flag will be removed at the end of the pushdown optimization by
remove_immutable_flag_processor processor.
*/
int new_flag= IMMUTABLE_FL;
this->walk(&Item::set_extraction_flag_processor, false,
(void*)&new_flag);
}
return this;
}
Item *Item::safe_charset_converter(THD *thd, CHARSET_INFO *tocs)
{
if (!needs_charset_converter(tocs))
......
......@@ -2295,8 +2295,7 @@ class Item: public Value_source,
{ return this; }
virtual Item *field_transformer_for_having_pushdown(THD *thd, uchar *arg)
{ return this; }
virtual Item *multiple_equality_transformer(THD *thd, uchar *arg)
{ return this; }
virtual Item *multiple_equality_transformer(THD *thd, uchar *arg);
virtual bool expr_cache_is_needed(THD *) { return FALSE; }
virtual Item *safe_charset_converter(THD *thd, CHARSET_INFO *tocs);
bool needs_charset_converter(uint32 length, CHARSET_INFO *tocs) const
......
......@@ -10902,7 +10902,11 @@ void mark_or_conds_to_avoid_pushdown(Item *cond)
(if cond is marked with FULL_EXTRACTION_FL or
cond is an AND condition and some of its parts are marked with
FULL_EXTRACTION_FL)
In this case condition is transformed and pushed into attach_to_conds
In this case condition is transformed with multiple_equality_transformer
transformer. It transforms all multiple equalities in the extracted
condition into the set of equalities.
After that the transformed condition is attached into attach_to_conds
list.
2. Part of some other condition c1 that can't be entirely pushed
(if с1 isn't marked with any flag).
......@@ -10919,10 +10923,6 @@ void mark_or_conds_to_avoid_pushdown(Item *cond)
In this case build_pushable_cond() is called for c1.
This method builds a clone of the c1 part that can be pushed.
Transformation mentioned above is made with multiple_equality_transformer
transformer. It transforms all multiple equalities in the extracted
condition into the set of equalities.
@note
Conditions that can be pushed are collected in attach_to_conds in this way:
1. if cond is an AND condition its parts that can be pushed into WHERE
......
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