Skip to content

Commit

Permalink
Make marking/testing of top level item uniform
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
montywi authored and cvicentiu committed Jul 19, 2021
1 parent f069aa1 commit fcbb2a1
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 93 deletions.
44 changes: 23 additions & 21 deletions sql/item.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
};


Expand Down Expand Up @@ -1319,6 +1319,25 @@ class Item :public Value_source,
{
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
{
Expand Down Expand Up @@ -2027,25 +2046,6 @@ class Item :public Value_source,
{
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
*/
Expand Down Expand Up @@ -4420,6 +4420,8 @@ class Item_bool_static :public Item_bool
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); }
};
Expand Down
45 changes: 23 additions & 22 deletions sql/item_cmpfunc.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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 &
Expand Down Expand Up @@ -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))) */
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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))) */
Expand Down Expand Up @@ -4796,17 +4795,18 @@ 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
*/
}


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);
Expand Down Expand Up @@ -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();

/*
Expand All @@ -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 ..."
Expand Down Expand Up @@ -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 ..."
Expand Down Expand Up @@ -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
*/
Expand All @@ -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;
Expand Down
41 changes: 14 additions & 27 deletions sql/item_cmpfunc.h
Original file line number Diff line number Diff line change
Expand Up @@ -411,7 +411,6 @@ class Item_in_optimizer: public Item_bool_func
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> &parameters) 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,
Expand Down Expand Up @@ -631,12 +630,8 @@ class Item_func_xor :public Item_bool_func

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
Expand Down Expand Up @@ -755,11 +750,10 @@ class Item_func_nop_all :public Item_func_not_all

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; }
Expand All @@ -770,7 +764,6 @@ class Item_func_eq :public Item_bool_rowready_func2
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,
Expand Down Expand Up @@ -956,15 +949,12 @@ class Item_func_opt_neg :public Item_bool_func
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;
Expand Down Expand Up @@ -2800,11 +2790,9 @@ class Item_is_not_null_test :public Item_func_isnull

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
Expand All @@ -2814,10 +2802,9 @@ class Item_func_isnotnull :public Item_func_null_predicate
}
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); }
};
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -3185,8 +3174,6 @@ class Item_cond :public Item_bool_func
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;
Expand Down Expand Up @@ -3542,7 +3529,7 @@ class Item_cond_and final :public Item_cond
}
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;
Expand Down
Loading

0 comments on commit fcbb2a1

Please sign in to comment.