mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 12:02:42 +01:00
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>
This commit is contained in:
parent
eaf7c0cbea
commit
a5e4c34991
5 changed files with 138 additions and 7 deletions
|
@ -5092,4 +5092,91 @@ SELECT * FROM v1
|
||||||
GROUP BY a HAVING a = (a IS NULL OR a IS NULL);
|
GROUP BY a HAVING a = (a IS NULL OR a IS NULL);
|
||||||
a
|
a
|
||||||
DROP VIEW v1;
|
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
|
End of 10.5 tests
|
||||||
|
|
|
@ -1558,4 +1558,30 @@ SELECT * FROM v1
|
||||||
|
|
||||||
DROP VIEW 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
|
--echo End of 10.5 tests
|
||||||
|
|
19
sql/item.cc
19
sql/item.cc
|
@ -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)
|
Item *Item::safe_charset_converter(THD *thd, CHARSET_INFO *tocs)
|
||||||
{
|
{
|
||||||
if (!needs_charset_converter(tocs))
|
if (!needs_charset_converter(tocs))
|
||||||
|
|
|
@ -2295,8 +2295,7 @@ public:
|
||||||
{ return this; }
|
{ return this; }
|
||||||
virtual Item *field_transformer_for_having_pushdown(THD *thd, uchar *arg)
|
virtual Item *field_transformer_for_having_pushdown(THD *thd, uchar *arg)
|
||||||
{ return this; }
|
{ return this; }
|
||||||
virtual Item *multiple_equality_transformer(THD *thd, uchar *arg)
|
virtual Item *multiple_equality_transformer(THD *thd, uchar *arg);
|
||||||
{ return this; }
|
|
||||||
virtual bool expr_cache_is_needed(THD *) { return FALSE; }
|
virtual bool expr_cache_is_needed(THD *) { return FALSE; }
|
||||||
virtual Item *safe_charset_converter(THD *thd, CHARSET_INFO *tocs);
|
virtual Item *safe_charset_converter(THD *thd, CHARSET_INFO *tocs);
|
||||||
bool needs_charset_converter(uint32 length, CHARSET_INFO *tocs) const
|
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
|
(if cond is marked with FULL_EXTRACTION_FL or
|
||||||
cond is an AND condition and some of its parts are marked with
|
cond is an AND condition and some of its parts are marked with
|
||||||
FULL_EXTRACTION_FL)
|
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.
|
list.
|
||||||
2. Part of some other condition c1 that can't be entirely pushed
|
2. Part of some other condition c1 that can't be entirely pushed
|
||||||
(if с1 isn't marked with any flag).
|
(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.
|
In this case build_pushable_cond() is called for c1.
|
||||||
This method builds a clone of the c1 part that can be pushed.
|
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
|
@note
|
||||||
Conditions that can be pushed are collected in attach_to_conds in this way:
|
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
|
1. if cond is an AND condition its parts that can be pushed into WHERE
|
||||||
|
|
Loading…
Reference in a new issue