diff options
author | Monty <monty@mariadb.org> | 2021-07-03 03:39:15 +0300 |
---|---|---|
committer | Vicențiu Ciorbaru <cvicentiu@gmail.com> | 2021-07-19 17:05:51 +0300 |
commit | fcbb2a1df1366dfbf8e1b0db7a03e8927a1d1d37 (patch) | |
tree | 28f731261a55c13766c79169417a85ff37a6c85e | |
parent | f069aa1dc24d2d835505b8c4567a704cf80c42f1 (diff) | |
download | mariadb-git-fcbb2a1df1366dfbf8e1b0db7a03e8927a1d1d37.tar.gz |
Make marking/testing of top level item uniform
There where several different implementations of is_top_level_item(),
with different variable names and tests. In some cases the code used
'is_top_level_item()' as a test, in other cases it accessed the variable
directrly. This patch makes all usage of 'top_level_item' uniform.
The new implementation stores the 'is_tol_level_item()' flag as part
of base_flags. This saves 7 bytes in all items that previously stored
the flag in it's own bool.
I had to keep 'top_level_item()' virtual to ensure that Item_bool_const
item's will not be updated. 'is_top_level_item()' is not virtual
anymore.
-rw-r--r-- | sql/item.h | 44 | ||||
-rw-r--r-- | sql/item_cmpfunc.cc | 45 | ||||
-rw-r--r-- | sql/item_cmpfunc.h | 41 | ||||
-rw-r--r-- | sql/item_subselect.cc | 34 | ||||
-rw-r--r-- | sql/item_subselect.h | 5 | ||||
-rw-r--r-- | sql/sql_select.cc | 6 |
6 files changed, 82 insertions, 93 deletions
diff --git a/sql/item.h b/sql/item.h index 6f57323fe33..00f617b9a99 100644 --- a/sql/item.h +++ b/sql/item.h @@ -769,8 +769,8 @@ enum class item_base_t : item_flags_t FIXED= (1<<2), // Was fixed with fix_fields(). IS_EXPLICIT_NAME= (1<<3), // The name of this Item was set by the user // (or was auto generated otherwise) - IS_IN_WITH_CYCLE= (1<<4) // This item is in CYCLE clause - // of WITH. + IS_IN_WITH_CYCLE= (1<<4), // This item is in CYCLE clause of WITH. + AT_TOP_LEVEL= (1<<5) // At top (AND) level of item tree }; @@ -1319,6 +1319,25 @@ public: { set_maybe_null(maybe_null_arg); } + /* + Mark the item that it is a top level item, or part of a top level AND item, + for WHERE and ON clauses: + Example: ... WHERE a=5 AND b=6; Both a=5 and b=6 are top level items + + This is used to indicate that there is no distinction between if the + value of the item is FALSE or NULL.. + This enables Item_cond_and and subquery related items to do special + "top level" optimizations. + */ + virtual void top_level_item() + { + base_flags|= item_base_t::AT_TOP_LEVEL; + } + /* + Return TRUE if this item of top WHERE level (AND/OR) + */ + bool is_top_level_item() const + { return (bool) (base_flags & item_base_t::AT_TOP_LEVEL); } void set_typelib(const TYPELIB *typelib) override { @@ -2027,25 +2046,6 @@ public: { return type_handler()->Item_update_null_value(this); } - - /* - Inform the item that there will be no distinction between its result - being FALSE or NULL. - - NOTE - This function will be called for eg. Items that are top-level AND-parts - of the WHERE clause. Items implementing this function (currently - Item_cond_and and subquery-related item) enable special optimizations - when they are "top level". - */ - virtual void top_level_item() {} - /* - Return TRUE if it is item of top WHERE level (AND/OR) and it is - important, return FALSE if it not important (we can not use to simplify - calculations) or not top level - */ - virtual bool is_top_level_item() const - { return FALSE; /* not important */} /* return IN/ALL/ANY subquery or NULL */ @@ -4420,6 +4420,8 @@ public: Item_bool_static(const char *str_arg, longlong i): Item_bool(str_arg, i) {}; + /* Don't mark static items as top level item */ + virtual void top_level_item() override {} void set_join_tab_idx(uint8 join_tab_idx_arg) override { DBUG_ASSERT(0); } }; diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 227e65c9d94..22756d543cb 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -1065,14 +1065,14 @@ int Arg_comparator::compare_row() // NULL was compared switch (((Item_func*)owner)->functype()) { case Item_func::NE_FUNC: - break; // NE never aborts on NULL even if abort_on_null is set + break; // NE never aborts on NULL case Item_func::LT_FUNC: case Item_func::LE_FUNC: case Item_func::GT_FUNC: case Item_func::GE_FUNC: return -1; // <, <=, > and >= always fail on NULL case Item_func::EQ_FUNC: - if (((Item_func_eq*)owner)->abort_on_null) + if (owner->is_top_level_item()) return -1; // We do not need correct NULL returning break; default: @@ -1189,12 +1189,6 @@ longlong Item_func_truth::val_int() } -bool Item_in_optimizer::is_top_level_item() const -{ - return args[1]->is_top_level_item(); -} - - void Item_in_optimizer::fix_after_pullout(st_select_lex *new_parent, Item **ref, bool merge) { @@ -1379,7 +1373,8 @@ bool Item_in_optimizer::fix_fields(THD *thd, Item **ref) } base_flags|= (item_base_t::FIXED | - (args[1]->base_flags & item_base_t::MAYBE_NULL)); + (args[1]->base_flags & (item_base_t::MAYBE_NULL | + item_base_t::AT_TOP_LEVEL))); with_flags|= (item_with_t::SUBQUERY | args[1]->with_flags | (args[0]->with_flags & @@ -2065,7 +2060,7 @@ bool Item_func_between::eval_not_null_tables(void *opt_arg) return 1; /* not_null_tables_cache == union(T1(e),T1(e1),T1(e2)) */ - if (pred_level && !negated) + if (is_top_level_item() && !negated) return 0; /* not_null_tables_cache == union(T1(e), intersection(T1(e1),T1(e2))) */ @@ -2467,6 +2462,10 @@ bool Item_func_if::fix_fields(THD *thd, Item **ref) { DBUG_ASSERT(fixed() == 0); + /* + Mark that we don't care if args[0] is NULL or FALSE, we regard both cases as + false. + */ args[0]->top_level_item(); if (Item_func::fix_fields(thd, ref)) @@ -4342,7 +4341,7 @@ Item_func_in::eval_not_null_tables(void *opt_arg) return 1; /* not_null_tables_cache == union(T1(e),union(T1(ei))) */ - if (pred_level && negated) + if (is_top_level_item() && negated) return 0; /* not_null_tables_cache = union(T1(e),intersection(T1(ei))) */ @@ -4796,9 +4795,10 @@ bool Item_func_bit_and::fix_length_and_dec() Item_cond::Item_cond(THD *thd, Item_cond *item) :Item_bool_func(thd, item), - abort_on_null(item->abort_on_null), and_tables_cache(item->and_tables_cache) { + base_flags|= (item->base_flags & item_base_t::AT_TOP_LEVEL); + /* item->list will be copied by copy_andor_arguments() call */ @@ -4806,7 +4806,7 @@ Item_cond::Item_cond(THD *thd, Item_cond *item) Item_cond::Item_cond(THD *thd, Item *i1, Item *i2): - Item_bool_func(thd), abort_on_null(0) + Item_bool_func(thd) { list.push_back(i1, thd->mem_root); list.push_back(i2, thd->mem_root); @@ -4874,7 +4874,7 @@ Item_cond::fix_fields(THD *thd, Item **ref) ((Item_cond*) item)->list.empty(); item= *li.ref(); // new current item } - if (abort_on_null) + if (is_top_level_item()) item->top_level_item(); /* @@ -4901,7 +4901,7 @@ Item_cond::fix_fields(THD *thd, Item **ref) if (item->can_eval_in_optimize() && !item->with_sp_var() && !cond_has_datetime_is_null(item)) { - if (item->eval_const_cond() == is_and_cond && top_level()) + if (item->eval_const_cond() == is_and_cond && is_top_level_item()) { /* a. This is "... AND true_cond AND ..." @@ -4958,7 +4958,7 @@ Item_cond::eval_not_null_tables(void *opt_arg) if (item->can_eval_in_optimize() && !item->with_sp_var() && !cond_has_datetime_is_null(item)) { - if (item->eval_const_cond() == is_and_cond && top_level()) + if (item->eval_const_cond() == is_and_cond && is_top_level_item()) { /* a. This is "... AND true_cond AND ..." @@ -5393,17 +5393,18 @@ void Item_cond_and::mark_as_condition_AND_part(TABLE_LIST *embedding) Evaluation of AND(expr, expr, expr ...). @note - abort_if_null is set for AND expressions for which we don't care if the - result is NULL or 0. This is set for: + There are AND expressions for which we don't care if the + result is NULL or 0. This is the case for: - WHERE clause - HAVING clause - IF(expression) + For these we mark them as "top_level_items" @retval 1 If all expressions are true @retval - 0 If all expressions are false or if we find a NULL expression and - 'abort_on_null' is set. + 0 If any of the expressions are false or if we find a NULL expression and + this is a top_level_item. @retval NULL if all expression are either 1 or NULL */ @@ -5419,8 +5420,8 @@ longlong Item_cond_and::val_int() { if (!item->val_bool()) { - if (abort_on_null || !(null_value= item->null_value)) - return 0; // return FALSE + if (is_top_level_item() || !(null_value= item->null_value)) + return 0; } } return null_value ? 0 : 1; diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index bc7441c2530..3767c2172e8 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -411,7 +411,6 @@ public: void set_join_tab_idx(uint8 join_tab_idx_arg) override { args[1]->set_join_tab_idx(join_tab_idx_arg); } void get_cache_parameters(List<Item> ¶meters) override; - bool is_top_level_item() const override; bool eval_not_null_tables(void *opt_arg) override; bool find_not_null_fields(table_map allowed) override; void fix_after_pullout(st_select_lex *new_parent, Item **ref, @@ -631,12 +630,8 @@ public: class Item_func_not :public Item_bool_func { - bool abort_on_null; public: - Item_func_not(THD *thd, Item *a): - Item_bool_func(thd, a), abort_on_null(FALSE) {} - void top_level_item() override { abort_on_null= 1; } - bool is_top_level_item() const override { return abort_on_null; } + Item_func_not(THD *thd, Item *a): Item_bool_func(thd, a) {} longlong val_int() override; enum Functype functype() const override { return NOT_FUNC; } LEX_CSTRING func_name_cstring() const override @@ -755,11 +750,10 @@ public: class Item_func_eq :public Item_bool_rowready_func2 { - bool abort_on_null; public: Item_func_eq(THD *thd, Item *a, Item *b): Item_bool_rowready_func2(thd, a, b), - abort_on_null(false), in_equality_no(UINT_MAX) + in_equality_no(UINT_MAX) {} longlong val_int() override; enum Functype functype() const override { return EQ_FUNC; } @@ -770,7 +764,6 @@ public: static LEX_CSTRING name= {STRING_WITH_LEN("=") }; return name; } - void top_level_item() override { abort_on_null= true; } Item *negated_item(THD *thd) override; COND *build_equal_items(THD *thd, COND_EQUAL *inherited, bool link_item_fields, @@ -956,15 +949,12 @@ protected: DTCollation cmp_collation; public: bool negated; /* <=> the item represents NOT <func> */ - bool pred_level; /* <=> [NOT] <func> is used on a predicate level */ public: Item_func_opt_neg(THD *thd, Item *a, Item *b, Item *c): - Item_bool_func(thd, a, b, c), negated(0), pred_level(0) {} + Item_bool_func(thd, a, b, c), negated(0) {} Item_func_opt_neg(THD *thd, List<Item> &list): - Item_bool_func(thd, list), negated(0), pred_level(0) {} + Item_bool_func(thd, list), negated(0) {} public: - void top_level_item() override { pred_level= 1; } - bool is_top_level_item() const override { return pred_level; } Item *neg_transformer(THD *thd) override { negated= !negated; @@ -2800,11 +2790,9 @@ public: class Item_func_isnotnull :public Item_func_null_predicate { - bool abort_on_null; public: Item_func_isnotnull(THD *thd, Item *a): - Item_func_null_predicate(thd, a), abort_on_null(0) - { } + Item_func_null_predicate(thd, a) {} longlong val_int() override; enum Functype functype() const override { return ISNOTNULL_FUNC; } LEX_CSTRING func_name_cstring() const override @@ -2814,10 +2802,9 @@ public: } enum precedence precedence() const override { return CMP_PRECEDENCE; } table_map not_null_tables() const override - { return abort_on_null ? not_null_tables_cache : 0; } + { return is_top_level_item() ? not_null_tables_cache : 0; } Item *neg_transformer(THD *thd) override; void print(String *str, enum_query_type query_type) override; - void top_level_item() override { abort_on_null=1; } Item *get_copy(THD *thd) override { return get_item_copy<Item_func_isnotnull>(thd, this); } }; @@ -3128,17 +3115,19 @@ class Item_cond :public Item_bool_func { protected: List<Item> list; - bool abort_on_null; table_map and_tables_cache; public: - /* Item_cond() is only used to create top level items */ - Item_cond(THD *thd): Item_bool_func(thd), abort_on_null(1) - { const_item_cache=0; } + Item_cond(THD *thd): Item_bool_func(thd) + { + /* Item_cond() is only used to create top level items */ + top_level_item(); + const_item_cache=0; + } Item_cond(THD *thd, Item *i1, Item *i2); Item_cond(THD *thd, Item_cond *item); Item_cond(THD *thd, List<Item> &nlist): - Item_bool_func(thd), list(nlist), abort_on_null(0) {} + Item_bool_func(thd), list(nlist) {} bool add(Item *item, MEM_ROOT *root) { DBUG_ASSERT(item); @@ -3185,8 +3174,6 @@ public: List<Item> &fields, uint flags) override; friend int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves, COND **conds); - void top_level_item() override { abort_on_null=1; } - bool top_level() { return abort_on_null; } void copy_andor_arguments(THD *thd, Item_cond *item); bool walk(Item_processor processor, bool walk_subquery, void *arg) override; Item *transform(THD *thd, Item_transformer transformer, uchar *arg) override; @@ -3542,7 +3529,7 @@ public: } enum precedence precedence() const override { return AND_PRECEDENCE; } table_map not_null_tables() const override - { return abort_on_null ? not_null_tables_cache: and_tables_cache; } + { return is_top_level_item() ? not_null_tables_cache: and_tables_cache; } Item *copy_andor_structure(THD *thd) override; Item *neg_transformer(THD *thd) override; void mark_as_condition_AND_part(TABLE_LIST *embedding) override; diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 381f015ca96..cd41cedde0d 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -1587,7 +1587,7 @@ bool Item_singlerow_subselect::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t Item_exists_subselect::Item_exists_subselect(THD *thd, st_select_lex *select_lex): - Item_subselect(thd), upper_not(NULL), abort_on_null(0), + Item_subselect(thd), upper_not(NULL), emb_on_expr_nest(NULL), optimizer(0), exists_transformed(0) { DBUG_ENTER("Item_exists_subselect::Item_exists_subselect"); @@ -1672,7 +1672,6 @@ Item_allany_subselect::Item_allany_subselect(THD *thd, Item * left_exp, func= func_creator(all_arg); init(select_lex, new (thd->mem_root) select_exists_subselect(thd, this)); max_columns= 1; - abort_on_null= 0; reset(); //if test_limit will fail then error will be reported to client test_limit(select_lex->master_unit()); @@ -2247,8 +2246,11 @@ bool Item_allany_subselect::is_maxmin_applicable(JOIN *join) Check if max/min optimization applicable: It is top item of WHERE condition. */ - return (abort_on_null || (upper_item && upper_item->is_top_level_item())) && - !(join->select_lex->master_unit()->uncacheable & ~UNCACHEABLE_EXPLAIN) && !func->eqne_op(); + return ((is_top_level_item() || + (upper_item && upper_item->is_top_level_item())) && + !(join->select_lex->master_unit()->uncacheable & + ~UNCACHEABLE_EXPLAIN) && + !func->eqne_op()); } @@ -2318,7 +2320,7 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN *join, ref_pointer_array[0], {STRING_WITH_LEN("<ref>")}, field_name)); - if (!abort_on_null && left_expr->maybe_null()) + if (!is_top_level_item() && left_expr->maybe_null()) { /* We can encounter "NULL IN (SELECT ...)". Wrap the added condition @@ -2351,7 +2353,7 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN *join, Item *orig_item= item; item= func->create(thd, expr, item); - if (!abort_on_null && orig_item->maybe_null()) + if (!is_top_level_item() && orig_item->maybe_null()) { having= new (thd->mem_root) Item_is_not_null_test(thd, this, having); if (left_expr->maybe_null()) @@ -2373,7 +2375,7 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN *join, If we may encounter NULL IN (SELECT ...) and care whether subquery result is NULL or FALSE, wrap condition in a trig_cond. */ - if (!abort_on_null && left_expr->maybe_null()) + if (!is_top_level_item() && left_expr->maybe_null()) { disable_cond_guard_for_const_null_left_expr(0); if (!(item= new (thd->mem_root) Item_func_trig_cond(thd, item, @@ -2403,7 +2405,7 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN *join, &select_lex->ref_pointer_array[0], no_matter_name, field_name)); - if (!abort_on_null && left_expr->maybe_null()) + if (!is_top_level_item() && left_expr->maybe_null()) { disable_cond_guard_for_const_null_left_expr(0); if (!(new_having= new (thd->mem_root) @@ -2602,7 +2604,7 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join, list_ref)); Item *col_item= new (thd->mem_root) Item_cond_or(thd, item_eq, item_isnull); - if (!abort_on_null && left_expr->element_index(i)->maybe_null() && + if (!is_top_level_item() && left_expr->element_index(i)->maybe_null() && get_cond_guard(i)) { disable_cond_guard_for_const_null_left_expr(i); @@ -2621,7 +2623,7 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join, ref_pointer_array[i], no_matter_name, list_ref)); - if (!abort_on_null && left_expr->element_index(i)->maybe_null() && + if (!is_top_level_item() && left_expr->element_index(i)->maybe_null() && get_cond_guard(i) ) { disable_cond_guard_for_const_null_left_expr(i); @@ -2662,7 +2664,7 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join, ref_pointer_array[i], no_matter_name, list_ref)); - if (!abort_on_null && select_lex->ref_pointer_array[i]->maybe_null()) + if (!is_top_level_item() && select_lex->ref_pointer_array[i]->maybe_null()) { Item *having_col_item= new (thd->mem_root) @@ -2694,7 +2696,7 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join, } *having_item= and_items(thd, *having_item, having_col_item); } - if (!abort_on_null && left_expr->element_index(i)->maybe_null() && + if (!is_top_level_item() && left_expr->element_index(i)->maybe_null() && get_cond_guard(i)) { if (!(item= new (thd->mem_root) @@ -3663,7 +3665,7 @@ bool Item_in_subselect::init_cond_guards() { DBUG_ASSERT(thd); uint cols_num= left_expr->cols(); - if (!abort_on_null && !pushed_cond_guards && + if (!is_top_level_item() && !pushed_cond_guards && (left_expr->maybe_null() || cols_num > 1)) { if (!(pushed_cond_guards= (bool*)thd->alloc(sizeof(bool) * cols_num))) @@ -4245,9 +4247,9 @@ bool subselect_uniquesubquery_engine::copy_ref_key(bool skip_constants) - NULL if select produces empty row set - FALSE otherwise. - In some cases (IN subselect is a top level item, i.e. abort_on_null==TRUE) - the caller doesn't distinguish between NULL and FALSE result and we just - return FALSE. + In some cases (IN subselect is a top level item, i.e. + is_top_level_item() == TRUE, the caller doesn't distinguish between NULL and + FALSE result and we just return FALSE. Otherwise we make a full table scan to see if there is at least one matching row. diff --git a/sql/item_subselect.h b/sql/item_subselect.h index 426b76f8067..54e4f8f0ced 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -373,7 +373,6 @@ class Item_exists_subselect :public Item_subselect protected: Item_func_not *upper_not; bool value; /* value of this item (boolean: exists/not-exists) */ - bool abort_on_null; void init_length_and_dec(); bool select_prepare_to_be_in(); @@ -397,7 +396,7 @@ public: Item_exists_subselect(THD *thd_arg, st_select_lex *select_lex); Item_exists_subselect(THD *thd_arg): - Item_subselect(thd_arg), upper_not(NULL), abort_on_null(0), + Item_subselect(thd_arg), upper_not(NULL), emb_on_expr_nest(NULL), optimizer(0), exists_transformed(0) {} @@ -424,8 +423,6 @@ public: bool fix_length_and_dec() override; void print(String *str, enum_query_type query_type) override; bool select_transformer(JOIN *join) override; - void top_level_item() override { abort_on_null=1; } - bool is_top_level_item() const override { return abort_on_null; } bool exists2in_processor(void *opt_arg) override; Item* expr_cache_insert_transformer(THD *thd, uchar *unused) override; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index b3a3db328d1..80d9cafe814 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -16362,9 +16362,9 @@ static void update_const_equal_items(THD *thd, COND *cond, JOIN_TAB *tab, Item *item; while ((item= li++)) update_const_equal_items(thd, item, tab, - (((Item_cond*) cond)->top_level() && - ((Item_cond*) cond)->functype() == - Item_func::COND_AND_FUNC)); + cond->is_top_level_item() && + ((Item_cond*) cond)->functype() == + Item_func::COND_AND_FUNC); } else if (cond->type() == Item::FUNC_ITEM && ((Item_func*) cond)->functype() == Item_func::MULT_EQUAL_FUNC) |