From d9ac8005e2a296633a3fff9bbd26322778a8e305 Mon Sep 17 00:00:00 2001 From: Rex Johnston Date: Mon, 19 Oct 2026 09:35:25 +1300 Subject: [PATCH] MDEV-36295 INTERVAL function returns inconsistent results when used in a WHERE clause A query like select * from (select a, b from t1 group by a, b) d where interval(d.a, 3, 7) = 1 returned no rows when the condition was pushed into the derived table, although the row (5,20) matches, as condition_pushdown_for_derived=off shows. Item_func_interval kept its first argument, the Item_row, in a member of its own as well as in args[0]. Condition pushdown copies the condition with deep_copy(). The copy of the node got new arguments but kept the member, so it went on reading the original row: the INTERVAL() pushed into the derived table compared the original outer column, and the pushed condition rejected every row. Item_func_interval now reads the row through args[0] alone, so a copy cannot point anywhere else. This commit was prepared with Claude Code (Opus 5), which found the stale member, made Item_func_interval read the row through args[0], wrote the test and confirmed in a build of the parent commit that it fails without the change, and ran main and the SQL-layer suites in normal and --ps-protocol mode on the series that contains it. --- mysql-test/main/derived_cond_pushdown.result | 10 ++++++ mysql-test/main/derived_cond_pushdown.test | 11 +++++++ sql/item_cmpfunc.cc | 34 ++++++++++---------- sql/item_cmpfunc.h | 4 +-- 4 files changed, 40 insertions(+), 19 deletions(-) diff --git a/mysql-test/main/derived_cond_pushdown.result b/mysql-test/main/derived_cond_pushdown.result index 96a17ceb398..7cc41ede690 100644 --- a/mysql-test/main/derived_cond_pushdown.result +++ b/mysql-test/main/derived_cond_pushdown.result @@ -23789,3 +23789,13 @@ id id id 0 3 3 drop table t1,t2,t3; # End of 10.11 tests +# +# MDEV-36295 INTERVAL() pushed into a derived table +# +create table t1 (a int, b int); +insert into t1 values (1,10),(5,20),(9,30); +select * from (select a, b from t1 group by a, b) d where interval(d.a, 3, 7) = 1; +a b +5 20 +drop table t1; +# End of 13.2 tests diff --git a/mysql-test/main/derived_cond_pushdown.test b/mysql-test/main/derived_cond_pushdown.test index d55ef44f6a2..9659486d5fc 100644 --- a/mysql-test/main/derived_cond_pushdown.test +++ b/mysql-test/main/derived_cond_pushdown.test @@ -4579,3 +4579,14 @@ select * from (select id from t1 union select id from t2) dt1, drop table t1,t2,t3; --echo # End of 10.11 tests + +--echo # +--echo # MDEV-36295 INTERVAL() pushed into a derived table +--echo # + +create table t1 (a int, b int); +insert into t1 values (1,10),(5,20),(9,30); +select * from (select a, b from t1 group by a, b) d where interval(d.a, 3, 7) = 1; +drop table t1; + +--echo # End of 13.2 tests diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 1c42127ffcf..0162eff2306 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -2014,17 +2014,17 @@ bool Item_func_opt_neg::eq(const Item *item, const Eq_config &config) const bool Item_func_interval::fix_length_and_dec(THD *thd) { - uint rows= row->cols(); + uint rows= row()->cols(); for (uint i= 0 ; i < rows; i++) { - if (row->element_index(i)->check_cols(1)) + if (row()->element_index(i)->check_cols(1)) return true; } - use_decimal_comparison= ((row->element_index(0)->result_type() == + use_decimal_comparison= ((row()->element_index(0)->result_type() == DECIMAL_RESULT) || - (row->element_index(0)->result_type() == + (row()->element_index(0)->result_type() == INT_RESULT)); if (rows > 8) { @@ -2032,7 +2032,7 @@ bool Item_func_interval::fix_length_and_dec(THD *thd) for (uint i= 1; not_null_consts && i < rows; i++) { - Item *el= row->element_index(i); + Item *el= row()->element_index(i); not_null_consts&= el->const_item() && !el->is_null(); } @@ -2046,7 +2046,7 @@ bool Item_func_interval::fix_length_and_dec(THD *thd) { for (uint i= 1; i < rows; i++) { - Item *el= row->element_index(i); + Item *el= row()->element_index(i); interval_range *range= intervals + (i-1); if ((el->result_type() == DECIMAL_RESULT) || (el->result_type() == INT_RESULT)) @@ -2070,16 +2070,16 @@ bool Item_func_interval::fix_length_and_dec(THD *thd) { for (uint i= 1; i < rows; i++) { - intervals[i-1].dbl= row->element_index(i)->val_real(); + intervals[i-1].dbl= row()->element_index(i)->val_real(); } } } } base_flags&= ~item_base_t::MAYBE_NULL; max_length= 2; - used_tables_and_const_cache_join(row); - not_null_tables_cache= row->not_null_tables(); - with_flags|= row->with_flags; + used_tables_and_const_cache_join(row()); + not_null_tables_cache= row()->not_null_tables(); + with_flags|= row()->with_flags; return false; } @@ -2107,15 +2107,15 @@ longlong Item_func_interval::val_int() if (use_decimal_comparison) { - dec= row->element_index(0)->val_decimal(&dec_buf); - if (row->element_index(0)->null_value) + dec= row()->element_index(0)->val_decimal(&dec_buf); + if (row()->element_index(0)->null_value) return -1; my_decimal2double(E_DEC_FATAL_ERROR, dec, &value); } else { - value= row->element_index(0)->val_real(); - if (row->element_index(0)->null_value) + value= row()->element_index(0)->val_real(); + if (row()->element_index(0)->null_value) return -1; } @@ -2123,7 +2123,7 @@ longlong Item_func_interval::val_int() { // Use binary search to find interval uint start,end; start= 0; - end= row->cols()-2; + end= row()->cols()-2; while (start != end) { uint mid= (start + end + 1) / 2; @@ -2149,9 +2149,9 @@ longlong Item_func_interval::val_int() value < range->dbl) ? 0 : start + 1; } - for (i=1 ; i < row->cols() ; i++) + for (i=1 ; i < row()->cols() ; i++) { - Item *el= row->element_index(i); + Item *el= row()->element_index(i); if (use_decimal_comparison && ((el->result_type() == DECIMAL_RESULT) || (el->result_type() == INT_RESULT))) diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index cf3418401f0..4084c71195f 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -1213,7 +1213,6 @@ struct interval_range class Item_func_interval :public Item_long_func { - Item_row *row; bool use_decimal_comparison; interval_range *intervals; bool check_arguments() const override @@ -1222,8 +1221,9 @@ class Item_func_interval :public Item_long_func } public: Item_func_interval(THD *thd, Item_row *a): - Item_long_func(thd, a), row(a), intervals(0) + Item_long_func(thd, a), intervals(0) { } + Item_row *row() const { return static_cast(args[0]); } longlong val_int() override; bool fix_length_and_dec(THD *thd) override; LEX_CSTRING func_name_cstring() const override