From 032986055f55f7af9079c22aba5062ab88f55328 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). --- 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 ff72ea59b8e..37b7fe2320f 100644 --- a/mysql-test/main/derived_cond_pushdown.result +++ b/mysql-test/main/derived_cond_pushdown.result @@ -21808,3 +21808,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 8a5a7266e07..077e1b4367f 100644 --- a/mysql-test/main/derived_cond_pushdown.test +++ b/mysql-test/main/derived_cond_pushdown.test @@ -4372,3 +4372,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 7c78c61216b..7ecffc20e9f 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -1947,17 +1947,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) { @@ -1965,7 +1965,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(); } @@ -1979,7 +1979,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)) @@ -2003,16 +2003,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; } @@ -2040,15 +2040,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; } @@ -2056,7 +2056,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; @@ -2082,9 +2082,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 8d3a6f7d51e..f380628550b 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -1159,7 +1159,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 @@ -1168,8 +1167,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