From 114ecf8dab9414e89c684df1bf6ea1fc1fe1dacd Mon Sep 17 00:00:00 2001 From: Jaeheon Shim Date: Sat, 7 Mar 2026 23:19:52 -0600 Subject: [PATCH] MDEV-37713/MDEV-37714 Fold boolean literals in SELECT-list The order of evaluation of the expressions that appear in a SELECT-list is undefined. This change exploits this fact by recursively folding TRUE/FALSE literals in OR/AND expressions which may allow for skipping evaluation of some parts of the expression or even the whole expression. --- mysql-test/main/mdev_37713.result | 68 +++++++++++++++++++++++++++++++ mysql-test/main/mdev_37713.test | 33 +++++++++++++++ sql/item_cmpfunc.cc | 39 ++++++++++++++++++ sql/item_cmpfunc.h | 1 + sql/sql_base.cc | 17 ++++++++ 5 files changed, 158 insertions(+) create mode 100644 mysql-test/main/mdev_37713.result create mode 100644 mysql-test/main/mdev_37713.test diff --git a/mysql-test/main/mdev_37713.result b/mysql-test/main/mdev_37713.result new file mode 100644 index 0000000000000..58968eb9a4234 --- /dev/null +++ b/mysql-test/main/mdev_37713.result @@ -0,0 +1,68 @@ +SELECT TRUE AND TRUE; +TRUE AND TRUE +1 +SELECT TRUE AND FALSE; +TRUE AND FALSE +0 +SELECT FALSE AND TRUE; +FALSE AND TRUE +0 +SELECT FALSE AND FALSE; +FALSE AND FALSE +0 +SELECT TRUE OR TRUE; +TRUE OR TRUE +1 +SELECT TRUE OR FALSE; +TRUE OR FALSE +1 +SELECT TRUE OR TRUE; +TRUE OR TRUE +1 +SELECT FALSE OR FALSE; +FALSE OR FALSE +0 +SELECT TRUE AND NULL; +TRUE AND NULL +NULL +SELECT NULL AND TRUE; +NULL AND TRUE +NULL +SELECT FALSE AND NULL; +FALSE AND NULL +0 +SELECT NULL AND FALSE; +NULL AND FALSE +0 +SELECT TRUE OR NULL; +TRUE OR NULL +1 +SELECT NULL OR TRUE; +NULL OR TRUE +1 +SELECT FALSE OR NULL; +FALSE OR NULL +NULL +SELECT NULL OR FALSE; +NULL OR FALSE +NULL +SELECT NULL AND NULL; +NULL AND NULL +NULL +SELECT NULL OR NULL; +NULL OR NULL +NULL +SELECT NOT NULL OR TRUE, NOT NULL AND FALSE; +NOT NULL OR TRUE NOT NULL AND FALSE +1 0 +SELECT NULL AND TRUE, NULL OR FALSE; +NULL AND TRUE NULL OR FALSE +NULL NULL +PREPARE s FROM 'SELECT NOT NULL OR TRUE, NOT NULL AND FALSE'; +EXECUTE s; +NOT NULL OR TRUE NOT NULL AND FALSE +1 0 +EXECUTE s; +NOT NULL OR TRUE NOT NULL AND FALSE +1 0 +DEALLOCATE PREPARE s; diff --git a/mysql-test/main/mdev_37713.test b/mysql-test/main/mdev_37713.test new file mode 100644 index 0000000000000..d4587d78633a2 --- /dev/null +++ b/mysql-test/main/mdev_37713.test @@ -0,0 +1,33 @@ +# +# MDEV-37713/MDEV-37714 +# Test correctness of boolean folding +# + +SELECT TRUE AND TRUE; +SELECT TRUE AND FALSE; +SELECT FALSE AND TRUE; +SELECT FALSE AND FALSE; +SELECT TRUE OR TRUE; +SELECT TRUE OR FALSE; +SELECT TRUE OR TRUE; +SELECT FALSE OR FALSE; + +SELECT TRUE AND NULL; +SELECT NULL AND TRUE; +SELECT FALSE AND NULL; +SELECT NULL AND FALSE; +SELECT TRUE OR NULL; +SELECT NULL OR TRUE; +SELECT FALSE OR NULL; +SELECT NULL OR FALSE; + +SELECT NULL AND NULL; +SELECT NULL OR NULL; + +SELECT NOT NULL OR TRUE, NOT NULL AND FALSE; +SELECT NULL AND TRUE, NULL OR FALSE; + +PREPARE s FROM 'SELECT NOT NULL OR TRUE, NOT NULL AND FALSE'; +EXECUTE s; +EXECUTE s; +DEALLOCATE PREPARE s; \ No newline at end of file diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index d5a6fdd275f48..97d68be9450cb 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -5736,6 +5736,45 @@ bool Item_cond::excl_dep_on_grouping_fields(st_select_lex *sel) return true; } +Item *Item_cond::simplify_cond(THD *thd) +{ + List_iterator li(list); + Item *child; + while ((child= li++)) + { + if (child->type() == Item::COND_ITEM) + { + Item *new_child= static_cast(child)->simplify_cond(thd); + if (new_child != child) + li.replace(new_child); + } + } + bool is_and= functype() == Item_func::COND_AND_FUNC; + bool is_or= functype() == Item_func::COND_OR_FUNC; + if (is_and || is_or) + { + List_iterator li2(list); + while ((child= li2++)) + { + Item *real= child->real_item(); + if (real->is_bool_literal()) + { + bool v= real->val_bool(); + if (is_and && v) + li2.remove(); + if (is_and && !v) + return new (thd->mem_root) Item_bool(thd, false); + if (is_or && v) + return new (thd->mem_root) Item_bool(thd, true); + if (is_or && !v) + li2.remove(); + } + } + if (list.is_empty()) + return new (thd->mem_root) Item_bool(thd, is_and); + } + return this; +} void Item_cond_and::mark_as_condition_AND_part(TABLE_LIST *embedding) { diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 4fa07ccc8c971..1353a086db541 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -3450,6 +3450,7 @@ class Item_cond :public Item_bool_func Item *deep_copy(THD *thd) const override; bool excl_dep_on_table(table_map tab_map) override; bool excl_dep_on_grouping_fields(st_select_lex *sel) override; + Item *simplify_cond(THD *thd); private: void merge_sub_condition(List_iterator& li); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 059cf17e75bf6..badf9a823818e 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -8223,6 +8223,23 @@ bool setup_fields(THD *thd, Ref_ptr_array ref_pointer_array, DBUG_RETURN(TRUE); /* purecov: inspected */ } item= *(it.ref()); // Item might have changed in fix_fields() + /* + If item is a SELECT-list COND_ITEM, rewrite it on the first time this + query is optimized to fold boolean expressions + */ + if (thd->lex->current_select->context_analysis_place == SELECT_LIST && + item->type() == Item::COND_ITEM && + thd->lex->current_select->first_cond_optimization) + { + Query_arena_stmt on_stmt_arena(thd); + Item *new_item= static_cast(item)->simplify_cond(thd); + if (new_item != item) + { + new_item->share_name_with(item); + it.replace(new_item); + item= new_item; + } + } if (!ref.is_null()) { ref[0]= item;