summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMonty <monty@mariadb.org>2021-07-03 03:39:15 +0300
committerVicențiu Ciorbaru <cvicentiu@gmail.com>2021-07-19 17:05:51 +0300
commitfcbb2a1df1366dfbf8e1b0db7a03e8927a1d1d37 (patch)
tree28f731261a55c13766c79169417a85ff37a6c85e
parentf069aa1dc24d2d835505b8c4567a704cf80c42f1 (diff)
downloadmariadb-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.h44
-rw-r--r--sql/item_cmpfunc.cc45
-rw-r--r--sql/item_cmpfunc.h41
-rw-r--r--sql/item_subselect.cc34
-rw-r--r--sql/item_subselect.h5
-rw-r--r--sql/sql_select.cc6
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> &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,
@@ -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)