Commit 632f2307 authored by Alexander Barkov's avatar Alexander Barkov

MDEV-7950 Item_func::type() takes 0.26% in OLTP RO

Step#5: changing the function remove_eq_conds() into a virtual method in Item.
It removes 6 virtual calls for Item_func::type(), and adds only 2
virtual calls for Item***::remove_eq_conds().
parent fb3e9352
......@@ -1135,6 +1135,8 @@ public:
DBUG_ASSERT(!cond_equal_ref || !cond_equal_ref[0]);
return this;
}
virtual COND *remove_eq_conds(THD *thd, Item::cond_result *cond_value,
bool top_level);
/*
Checks whether the item is:
- a simple equality (field=field_item or field=constant_item), or
......
......@@ -409,6 +409,8 @@ public:
Item_bool_func::cleanup();
cmp.cleanup();
}
COND *remove_eq_conds(THD *thd, Item::cond_result *cond_value,
bool top_level);
friend class Arg_comparator;
};
......@@ -1467,6 +1469,8 @@ public:
const_item_cache= args[0]->const_item();
}
}
COND *remove_eq_conds(THD *thd, Item::cond_result *cond_value,
bool top_level);
table_map not_null_tables() const { return 0; }
Item *neg_transformer(THD *thd);
};
......@@ -1775,6 +1779,8 @@ public:
COND *build_equal_items(THD *thd, COND_EQUAL *inherited,
bool link_item_fields,
COND_EQUAL **cond_equal_ref);
COND *remove_eq_conds(THD *thd, Item::cond_result *cond_value,
bool top_level);
virtual void print(String *str, enum_query_type query_type);
void split_sum_func(THD *thd, Item **ref_pointer_array, List<Item> &fields);
friend int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves,
......
......@@ -372,7 +372,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
if (conds)
{
Item::cond_result result;
conds= remove_eq_conds(thd, conds, &result);
conds= conds->remove_eq_conds(thd, &result, true);
if (result == Item::COND_FALSE) // Impossible where
{
limit= 0;
......
......@@ -3862,7 +3862,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
COND_EQUAL *orig_cond_equal = join->cond_equal;
conds->update_used_tables();
conds= remove_eq_conds(join->thd, conds, &join->cond_value);
conds= conds->remove_eq_conds(join->thd, &join->cond_value, true);
if (conds && conds->type() == Item::COND_ITEM &&
((Item_cond*) conds)->functype() == Item_func::COND_AND_FUNC)
join->cond_equal= &((Item_cond_and*) conds)->m_cond_equal;
......@@ -14708,7 +14708,7 @@ optimize_cond(JOIN *join, COND *conds,
Remove all and-levels where CONST item != CONST item
*/
DBUG_EXECUTE("where",print_where(conds,"after const change", QT_ORDINARY););
conds= remove_eq_conds(thd, conds, cond_value);
conds= conds->remove_eq_conds(thd, cond_value, true);
if (conds && conds->type() == Item::COND_ITEM &&
((Item_cond*) conds)->functype() == Item_func::COND_AND_FUNC)
*cond_equal= &((Item_cond_and*) conds)->m_cond_equal;
......@@ -14945,14 +14945,13 @@ bool cond_is_datetime_is_null(Item *cond)
=> SELECT * FROM t1 WHERE (b = 5) AND (a = 5)
*/
static COND *
internal_remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
COND *
Item_cond::remove_eq_conds(THD *thd, Item::cond_result *cond_value,
bool top_level)
{
if (cond->type() == Item::COND_ITEM)
{
bool and_level= ((Item_cond*) cond)->functype()
== Item_func::COND_AND_FUNC;
List<Item> *cond_arg_list= ((Item_cond*) cond)->argument_list();
bool and_level= functype() == Item_func::COND_AND_FUNC;
List<Item> *cond_arg_list= argument_list();
if (and_level)
{
......@@ -14964,7 +14963,7 @@ internal_remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
So it's easier to do it at one pass through the list of the equalities.
*/
List<Item_equal> *cond_equalities=
&((Item_cond_and *) cond)->m_cond_equal.current_level;
&((Item_cond_and *) this)->m_cond_equal.current_level;
cond_arg_list->disjoin((List<Item> *) cond_equalities);
List_iterator<Item_equal> it(*cond_equalities);
Item_equal *eq_item;
......@@ -14990,7 +14989,7 @@ internal_remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
while ((item=li++))
{
Item *new_item=internal_remove_eq_conds(thd, item, &tmp_cond_value);
Item *new_item= item->remove_eq_conds(thd, &tmp_cond_value, false);
if (!new_item)
{
/* This can happen only when item is converted to TRUE or FALSE */
......@@ -15008,8 +15007,7 @@ internal_remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
if (new_item->type() == Item::COND_ITEM &&
item->type() == Item::COND_ITEM)
{
DBUG_ASSERT(((Item_cond *) cond)->functype() ==
((Item_cond *) new_item)->functype());
DBUG_ASSERT(functype() == ((Item_cond *) new_item)->functype());
List<Item> *new_item_arg_list=
((Item_cond *) new_item)->argument_list();
if (and_level)
......@@ -15056,8 +15054,7 @@ internal_remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
else
{
if (new_item->type() == Item::COND_ITEM &&
((Item_cond*) new_item)->functype() ==
((Item_cond*) cond)->functype())
((Item_cond*) new_item)->functype() == functype())
{
List<Item> *new_item_arg_list=
((Item_cond *) new_item)->argument_list();
......@@ -15073,7 +15070,7 @@ internal_remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
}
}
if (*cond_value == Item::COND_UNDEF)
*cond_value=tmp_cond_value;
*cond_value= tmp_cond_value;
switch (tmp_cond_value) {
case Item::COND_OK: // Not TRUE or FALSE
if (and_level || *cond_value == Item::COND_FALSE)
......@@ -15082,7 +15079,7 @@ internal_remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
case Item::COND_FALSE:
if (and_level)
{
*cond_value=tmp_cond_value;
*cond_value= tmp_cond_value;
return (COND*) 0; // Always false
}
break;
......@@ -15097,6 +15094,7 @@ internal_remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
break; /* purecov: deadcode */
}
}
COND *cond= this;
if (!new_equalities.is_empty())
{
DBUG_ASSERT(and_level);
......@@ -15106,7 +15104,7 @@ internal_remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
These multiple equalities are to be merged into the
multiple equalities of cond_arg_list.
*/
COND_EQUAL *cond_equal= &((Item_cond_and *) cond)->m_cond_equal;
COND_EQUAL *cond_equal= &((Item_cond_and *) this)->m_cond_equal;
List<Item_equal> *cond_equalities= &cond_equal->current_level;
cond_arg_list->disjoin((List<Item> *) cond_equalities);
Item_equal *equality;
......@@ -15131,7 +15129,7 @@ internal_remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
the all AND/OR levels of cond
*/
bool is_simplifiable_cond= false;
propagate_new_equalities(thd, cond, cond_equalities,
propagate_new_equalities(thd, this, cond_equalities,
cond_equal->upper_levels,
&is_simplifiable_cond);
/*
......@@ -15141,7 +15139,7 @@ internal_remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
*/
if (is_simplifiable_cond)
{
if (!(cond= internal_remove_eq_conds(thd, cond, cond_value)))
if (!(cond= cond->remove_eq_conds(thd, cond_value, false)))
return cond;
}
should_fix_fields= 1;
......@@ -15158,72 +15156,46 @@ internal_remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
((Item_cond*) cond)->argument_list()->empty();
return item;
}
}
else if (cond_is_datetime_is_null(cond))
{
/* fix to replace 'NULL' dates with '0' (shreeve@uci.edu) */
/*
See BUG#12594011
Documentation says that
SELECT datetime_notnull d FROM t1 WHERE d IS NULL
shall return rows where d=='0000-00-00'
Thus, for DATE and DATETIME columns defined as NOT NULL,
"date_notnull IS NULL" has to be modified to
"date_notnull IS NULL OR date_notnull == 0" (if outer join)
"date_notnull == 0" (otherwise)
*/
Item **args= ((Item_func_isnull*) cond)->arguments();
Field *field=((Item_field*) args[0])->field;
Item *item0= new(thd->mem_root) Item_int((longlong)0, 1);
Item *eq_cond= new(thd->mem_root) Item_func_eq(args[0], item0);
if (!eq_cond)
*cond_value= Item::COND_OK;
return cond;
}
if (field->table->pos_in_table_list->is_inner_table_of_outer_join())
{
// outer join: transform "col IS NULL" to "col IS NULL or col=0"
Item *or_cond= new(thd->mem_root) Item_cond_or(eq_cond, cond);
if (!or_cond)
return cond;
cond= or_cond;
}
else
COND *
Item::remove_eq_conds(THD *thd, Item::cond_result *cond_value, bool top_level)
{
if (const_item() && !is_expensive())
{
// not outer join: transform "col IS NULL" to "col=0"
cond= eq_cond;
*cond_value= eval_const_cond(this) ? Item::COND_TRUE : Item::COND_FALSE;
return (COND*) 0;
}
*cond_value= Item::COND_OK;
return this; // Point at next and level
}
cond->fix_fields(thd, &cond);
if (cond->const_item() && !cond->is_expensive())
COND *
Item_bool_func2::remove_eq_conds(THD *thd, Item::cond_result *cond_value,
bool top_level)
{
if (const_item() && !is_expensive())
{
*cond_value= eval_const_cond(cond) ? Item::COND_TRUE : Item::COND_FALSE;
*cond_value= eval_const_cond(this) ? Item::COND_TRUE : Item::COND_FALSE;
return (COND*) 0;
}
}
else if (cond->const_item() && !cond->is_expensive())
if ((*cond_value= eq_cmp_result()) != Item::COND_OK)
{
*cond_value= eval_const_cond(cond) ? Item::COND_TRUE : Item::COND_FALSE;
return (COND*) 0;
}
else if ((*cond_value= cond->eq_cmp_result()) != Item::COND_OK)
{ // boolan compare function
Item *left_item= ((Item_func*) cond)->arguments()[0];
Item *right_item= ((Item_func*) cond)->arguments()[1];
if (left_item->eq(right_item,1))
if (args[0]->eq(args[1], true))
{
if (!left_item->maybe_null ||
((Item_func*) cond)->functype() == Item_func::EQUAL_FUNC)
if (!args[0]->maybe_null || functype() == Item_func::EQUAL_FUNC)
return (COND*) 0; // Compare of identical items
}
}
*cond_value=Item::COND_OK;
return cond; // Point at next and level
*cond_value= Item::COND_OK;
return this; // Point at next and level
}
/**
Remove const and eq items. Return new item, or NULL if no condition
cond_value is set to according:
......@@ -15245,11 +15217,66 @@ internal_remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
*/
COND *
remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
Item_func_isnull::remove_eq_conds(THD *thd, Item::cond_result *cond_value,
bool top_level)
{
if (cond->type() == Item::FUNC_ITEM &&
((Item_func*) cond)->functype() == Item_func::ISNULL_FUNC)
if (args[0]->type() == Item::FIELD_ITEM)
{
Field *field= ((Item_field*) args[0])->field;
if (((field->type() == MYSQL_TYPE_DATE) ||
(field->type() == MYSQL_TYPE_DATETIME)) &&
(field->flags & NOT_NULL_FLAG))
{
/* fix to replace 'NULL' dates with '0' (shreeve@uci.edu) */
/*
See BUG#12594011
Documentation says that
SELECT datetime_notnull d FROM t1 WHERE d IS NULL
shall return rows where d=='0000-00-00'
Thus, for DATE and DATETIME columns defined as NOT NULL,
"date_notnull IS NULL" has to be modified to
"date_notnull IS NULL OR date_notnull == 0" (if outer join)
"date_notnull == 0" (otherwise)
*/
Item *item0= new(thd->mem_root) Item_int((longlong)0, 1);
Item *eq_cond= new(thd->mem_root) Item_func_eq(args[0], item0);
if (!eq_cond)
return this;
COND *cond= this;
if (field->table->pos_in_table_list->is_inner_table_of_outer_join())
{
// outer join: transform "col IS NULL" to "col IS NULL or col=0"
Item *or_cond= new(thd->mem_root) Item_cond_or(eq_cond, this);
if (!or_cond)
return this;
cond= or_cond;
}
else
{
// not outer join: transform "col IS NULL" to "col=0"
cond= eq_cond;
}
cond->fix_fields(thd, &cond);
/*
Note: although args[0] is a field, cond can still be a constant
(in case field is a part of a dependent subquery).
Note: we call cond->Item::remove_eq_conds() non-virtually (statically)
for performance purpose.
A non-qualified call, i.e. just cond->remove_eq_conds(),
would call Item_bool_func2::remove_eq_conds() instead, which would
try to do some extra job to detect if args[0] and args[1] are
equivalent items. We know they are not (we have field=0 here).
*/
return cond->Item::remove_eq_conds(thd, cond_value, false);
}
/*
Handles this special case for some ODBC applications:
The are requesting the row that was just updated with a auto_increment
......@@ -15258,28 +15285,30 @@ remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
SELECT * from table_name where auto_increment_column IS NULL
This will be changed to:
SELECT * from table_name where auto_increment_column = LAST_INSERT_ID
Note, this substitution is done if the NULL test is the only condition!
If the NULL test is a part of a more complex condition, it is not
substituted and is treated normally:
WHERE auto_increment IS NULL AND something_else
*/
Item_func_isnull *func=(Item_func_isnull*) cond;
Item **args= func->arguments();
if (args[0]->type() == Item::FIELD_ITEM)
if (top_level) // "auto_increment_column IS NULL" is the only condition
{
Field *field=((Item_field*) args[0])->field;
if (field->flags & AUTO_INCREMENT_FLAG && !field->table->maybe_null &&
(thd->variables.option_bits & OPTION_AUTO_IS_NULL) &&
(thd->first_successful_insert_id_in_prev_stmt > 0 &&
thd->substitute_null_with_insert_id))
{
#ifdef HAVE_QUERY_CACHE
#ifdef HAVE_QUERY_CACHE
query_cache_abort(&thd->query_cache_tls);
#endif
COND *new_cond;
#endif
COND *new_cond, *cond= this;
if ((new_cond= new Item_func_eq(args[0],
new Item_int("last_insert_id()",
thd->read_first_successful_insert_id_in_prev_stmt(),
MY_INT64_NUM_DECIMAL_DIGITS))))
{
cond=new_cond;
cond= new_cond;
/*
Item_func_eq can't be fixed after creation so we do not check
cond->fixed, also it do not need tables so we use 0 as second
......@@ -15298,7 +15327,7 @@ remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
}
}
}
return internal_remove_eq_conds(thd, cond, cond_value); // Scan all the condition
return Item::remove_eq_conds(thd, cond_value, top_level);
}
......
......@@ -1803,7 +1803,6 @@ bool cp_buffer_from_ref(THD *thd, TABLE *table, TABLE_REF *ref);
bool error_if_full_join(JOIN *join);
int report_error(TABLE *table, int error);
int safe_index_read(JOIN_TAB *tab);
COND *remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value);
int get_quick_record(SQL_SELECT *select);
SORT_FIELD * make_unireg_sortorder(THD *thd, ORDER *order, uint *length,
SORT_FIELD *sortorder);
......
......@@ -377,7 +377,7 @@ int mysql_update(THD *thd,
if (conds)
{
Item::cond_result cond_value;
conds= remove_eq_conds(thd, conds, &cond_value);
conds= conds->remove_eq_conds(thd, &cond_value, true);
if (cond_value == Item::COND_FALSE)
{
limit= 0; // Impossible WHERE
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment