From c0f4063df31fa63125203863f546c3674e02fadc Mon Sep 17 00:00:00 2001 From: bsrikanth-mariadb Date: Tue, 7 Jul 2026 10:21:30 +0530 Subject: [PATCH 1/2] MDEV-39916: Crash when pushing "NOT col" from HAVING into WHERE "NOT column" rewrites itself into "column = 0" in fix_fields(). However the rewrite is not done when the "column" is wrapped in an Item_ref. Later, pushdown_from_having_into_where() will strip the Item_ref object and will call Item_func_not(Item_field())->fix_fields(thd, ref=NULL); This call will attempt the rewrite and crash when writing into NULL pointer. Fixed this in two places: * Make Item_func_not::fix_fields() use real_item() and so recognize fields wrapped in Item_ref-like objects. * Make pushdown_from_having_into_where() pass the right pointer as fix_fields() argument. --- mysql-test/main/having_cond_pushdown.result | 12 ++++++++++++ mysql-test/main/having_cond_pushdown.test | 14 ++++++++++++++ sql/item_cmpfunc.cc | 4 +++- sql/sql_lex.cc | 7 ++++--- 4 files changed, 33 insertions(+), 4 deletions(-) diff --git a/mysql-test/main/having_cond_pushdown.result b/mysql-test/main/having_cond_pushdown.result index 6b488c2b78812..fc96cdd995eb0 100644 --- a/mysql-test/main/having_cond_pushdown.result +++ b/mysql-test/main/having_cond_pushdown.result @@ -6090,4 +6090,16 @@ EXPLAIN } } drop table t1, t2; +# +# MDEV-39916 Crash with having filter when getting pushed down +# +CREATE TABLE t1 (a INT); +INSERT INTO t1 VALUES (0), (1); +SELECT a +FROM t1 +GROUP BY a +HAVING (NOT a) AND TRUE; +a +0 +DROP TABLE t1; # End of 10.11 tests diff --git a/mysql-test/main/having_cond_pushdown.test b/mysql-test/main/having_cond_pushdown.test index 507c5f15bfbf2..5aa19ec0c6376 100644 --- a/mysql-test/main/having_cond_pushdown.test +++ b/mysql-test/main/having_cond_pushdown.test @@ -1657,4 +1657,18 @@ execute stmt; drop table t1, t2; +--echo # +--echo # MDEV-39916 Crash with having filter when getting pushed down +--echo # + +CREATE TABLE t1 (a INT); +INSERT INTO t1 VALUES (0), (1); + +SELECT a +FROM t1 +GROUP BY a +HAVING (NOT a) AND TRUE; + +DROP TABLE t1; + --echo # End of 10.11 tests diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 5f2c471de7fba..cb04be003b31f 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -6676,7 +6676,9 @@ Item *Item_func_not::neg_transformer(THD *thd) /* NOT(x) -> x */ bool Item_func_not::fix_fields(THD *thd, Item **ref) { args[0]->under_not(this); - if (args[0]->type() == FIELD_ITEM) + if (args[0]->fix_fields_if_needed(thd, &args[0])) + return true; + if (args[0]->real_item()->type() == FIELD_ITEM) { /* replace "NOT " with " == 0" */ Query_arena backup, *arena; diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 8a5b1a14bcca7..b0db02ab63f80 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -11574,7 +11574,7 @@ Item *st_select_lex::pushdown_from_having_into_where(THD *thd, Item *having) list of all its conjuncts saved in attach_to_conds. Otherwise, the condition is put into attach_to_conds as the only its element. */ - List_iterator_fast it(attach_to_conds); + List_iterator it(attach_to_conds); Item *item; check_cond_extraction_for_grouping_fields(thd, having); if (build_pushable_cond_for_having_pushdown(thd, having)) @@ -11638,8 +11638,9 @@ Item *st_select_lex::pushdown_from_having_into_where(THD *thd, Item *having) &Item::field_transformer_for_having_pushdown, (uchar *)this); - if (item->walk(&Item::cleanup_excluding_immutables_processor, 0, STOP_PTR) - || item->fix_fields(thd, NULL)) + if (item->walk(&Item::cleanup_excluding_immutables_processor, 0, + STOP_PTR) || + item->fix_fields(thd, it.ref())) { attach_to_conds.empty(); goto exit; From 5552e25d168753ab5e8b2226590947dac873f64f Mon Sep 17 00:00:00 2001 From: bsrikanth-mariadb Date: Mon, 13 Jul 2026 21:40:09 +0530 Subject: [PATCH 2/2] MDEV-39916: Crash when pushing "NOT col" from HAVING into WHERE Add comments near "List attach_to_conds", inside class st_select_lex, stating that the conditions from having clause into where optimization are added to this list. --- mysql-test/main/having_cond_pushdown.result | 2 +- mysql-test/main/having_cond_pushdown.test | 2 +- sql/sql_lex.h | 6 ++++++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/mysql-test/main/having_cond_pushdown.result b/mysql-test/main/having_cond_pushdown.result index fc96cdd995eb0..7afb7e7fbaffc 100644 --- a/mysql-test/main/having_cond_pushdown.result +++ b/mysql-test/main/having_cond_pushdown.result @@ -6091,7 +6091,7 @@ EXPLAIN } drop table t1, t2; # -# MDEV-39916 Crash with having filter when getting pushed down +# MDEV-39916: Crash when pushing "NOT col" from HAVING into WHERE # CREATE TABLE t1 (a INT); INSERT INTO t1 VALUES (0), (1); diff --git a/mysql-test/main/having_cond_pushdown.test b/mysql-test/main/having_cond_pushdown.test index 5aa19ec0c6376..9452c8cba94c8 100644 --- a/mysql-test/main/having_cond_pushdown.test +++ b/mysql-test/main/having_cond_pushdown.test @@ -1658,7 +1658,7 @@ execute stmt; drop table t1, t2; --echo # ---echo # MDEV-39916 Crash with having filter when getting pushed down +--echo # MDEV-39916: Crash when pushing "NOT col" from HAVING into WHERE --echo # CREATE TABLE t1 (a INT); diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 8f8d6ce092737..0eea5ae771245 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -1180,7 +1180,13 @@ class st_select_lex: public st_select_lex_node /* List of references to fields referenced from inner selects */ List inner_refs_list; + + /* + Pushdown from HAVING into WHERE optimization: conditions from the HAVING + clause that should be added into the WHERE. + */ List attach_to_conds; + /* Saved values of the WHERE and HAVING clauses*/ Item::cond_result cond_value, having_value; /*