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: ...@@ -1135,6 +1135,8 @@ public:
DBUG_ASSERT(!cond_equal_ref || !cond_equal_ref[0]); DBUG_ASSERT(!cond_equal_ref || !cond_equal_ref[0]);
return this; return this;
} }
virtual COND *remove_eq_conds(THD *thd, Item::cond_result *cond_value,
bool top_level);
/* /*
Checks whether the item is: Checks whether the item is:
- a simple equality (field=field_item or field=constant_item), or - a simple equality (field=field_item or field=constant_item), or
......
...@@ -409,6 +409,8 @@ public: ...@@ -409,6 +409,8 @@ public:
Item_bool_func::cleanup(); Item_bool_func::cleanup();
cmp.cleanup(); cmp.cleanup();
} }
COND *remove_eq_conds(THD *thd, Item::cond_result *cond_value,
bool top_level);
friend class Arg_comparator; friend class Arg_comparator;
}; };
...@@ -1467,6 +1469,8 @@ public: ...@@ -1467,6 +1469,8 @@ public:
const_item_cache= args[0]->const_item(); 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; } table_map not_null_tables() const { return 0; }
Item *neg_transformer(THD *thd); Item *neg_transformer(THD *thd);
}; };
...@@ -1775,6 +1779,8 @@ public: ...@@ -1775,6 +1779,8 @@ public:
COND *build_equal_items(THD *thd, COND_EQUAL *inherited, COND *build_equal_items(THD *thd, COND_EQUAL *inherited,
bool link_item_fields, bool link_item_fields,
COND_EQUAL **cond_equal_ref); 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); virtual void print(String *str, enum_query_type query_type);
void split_sum_func(THD *thd, Item **ref_pointer_array, List<Item> &fields); 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, 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, ...@@ -372,7 +372,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
if (conds) if (conds)
{ {
Item::cond_result result; 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 if (result == Item::COND_FALSE) // Impossible where
{ {
limit= 0; limit= 0;
......
...@@ -3862,7 +3862,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list, ...@@ -3862,7 +3862,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
COND_EQUAL *orig_cond_equal = join->cond_equal; COND_EQUAL *orig_cond_equal = join->cond_equal;
conds->update_used_tables(); 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 && if (conds && conds->type() == Item::COND_ITEM &&
((Item_cond*) conds)->functype() == Item_func::COND_AND_FUNC) ((Item_cond*) conds)->functype() == Item_func::COND_AND_FUNC)
join->cond_equal= &((Item_cond_and*) conds)->m_cond_equal; join->cond_equal= &((Item_cond_and*) conds)->m_cond_equal;
...@@ -14708,7 +14708,7 @@ optimize_cond(JOIN *join, COND *conds, ...@@ -14708,7 +14708,7 @@ optimize_cond(JOIN *join, COND *conds,
Remove all and-levels where CONST item != CONST item Remove all and-levels where CONST item != CONST item
*/ */
DBUG_EXECUTE("where",print_where(conds,"after const change", QT_ORDINARY);); 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 && if (conds && conds->type() == Item::COND_ITEM &&
((Item_cond*) conds)->functype() == Item_func::COND_AND_FUNC) ((Item_cond*) conds)->functype() == Item_func::COND_AND_FUNC)
*cond_equal= &((Item_cond_and*) conds)->m_cond_equal; *cond_equal= &((Item_cond_and*) conds)->m_cond_equal;
...@@ -14945,14 +14945,13 @@ bool cond_is_datetime_is_null(Item *cond) ...@@ -14945,14 +14945,13 @@ bool cond_is_datetime_is_null(Item *cond)
=> SELECT * FROM t1 WHERE (b = 5) AND (a = 5) => 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= functype() == Item_func::COND_AND_FUNC;
{ List<Item> *cond_arg_list= argument_list();
bool and_level= ((Item_cond*) cond)->functype()
== Item_func::COND_AND_FUNC;
List<Item> *cond_arg_list= ((Item_cond*) cond)->argument_list();
if (and_level) if (and_level)
{ {
...@@ -14964,7 +14963,7 @@ internal_remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value) ...@@ -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. So it's easier to do it at one pass through the list of the equalities.
*/ */
List<Item_equal> *cond_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); cond_arg_list->disjoin((List<Item> *) cond_equalities);
List_iterator<Item_equal> it(*cond_equalities); List_iterator<Item_equal> it(*cond_equalities);
Item_equal *eq_item; Item_equal *eq_item;
...@@ -14990,7 +14989,7 @@ internal_remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value) ...@@ -14990,7 +14989,7 @@ internal_remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
while ((item=li++)) 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) if (!new_item)
{ {
/* This can happen only when item is converted to TRUE or FALSE */ /* 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) ...@@ -15008,8 +15007,7 @@ internal_remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
if (new_item->type() == Item::COND_ITEM && if (new_item->type() == Item::COND_ITEM &&
item->type() == Item::COND_ITEM) item->type() == Item::COND_ITEM)
{ {
DBUG_ASSERT(((Item_cond *) cond)->functype() == DBUG_ASSERT(functype() == ((Item_cond *) new_item)->functype());
((Item_cond *) new_item)->functype());
List<Item> *new_item_arg_list= List<Item> *new_item_arg_list=
((Item_cond *) new_item)->argument_list(); ((Item_cond *) new_item)->argument_list();
if (and_level) if (and_level)
...@@ -15056,8 +15054,7 @@ internal_remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value) ...@@ -15056,8 +15054,7 @@ internal_remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
else else
{ {
if (new_item->type() == Item::COND_ITEM && if (new_item->type() == Item::COND_ITEM &&
((Item_cond*) new_item)->functype() == ((Item_cond*) new_item)->functype() == functype())
((Item_cond*) cond)->functype())
{ {
List<Item> *new_item_arg_list= List<Item> *new_item_arg_list=
((Item_cond *) new_item)->argument_list(); ((Item_cond *) new_item)->argument_list();
...@@ -15073,7 +15070,7 @@ internal_remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value) ...@@ -15073,7 +15070,7 @@ internal_remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
} }
} }
if (*cond_value == Item::COND_UNDEF) if (*cond_value == Item::COND_UNDEF)
*cond_value=tmp_cond_value; *cond_value= tmp_cond_value;
switch (tmp_cond_value) { switch (tmp_cond_value) {
case Item::COND_OK: // Not TRUE or FALSE case Item::COND_OK: // Not TRUE or FALSE
if (and_level || *cond_value == Item::COND_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) ...@@ -15082,7 +15079,7 @@ internal_remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
case Item::COND_FALSE: case Item::COND_FALSE:
if (and_level) if (and_level)
{ {
*cond_value=tmp_cond_value; *cond_value= tmp_cond_value;
return (COND*) 0; // Always false return (COND*) 0; // Always false
} }
break; break;
...@@ -15097,6 +15094,7 @@ internal_remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value) ...@@ -15097,6 +15094,7 @@ internal_remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
break; /* purecov: deadcode */ break; /* purecov: deadcode */
} }
} }
COND *cond= this;
if (!new_equalities.is_empty()) if (!new_equalities.is_empty())
{ {
DBUG_ASSERT(and_level); DBUG_ASSERT(and_level);
...@@ -15106,7 +15104,7 @@ internal_remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value) ...@@ -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 These multiple equalities are to be merged into the
multiple equalities of cond_arg_list. 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; List<Item_equal> *cond_equalities= &cond_equal->current_level;
cond_arg_list->disjoin((List<Item> *) cond_equalities); cond_arg_list->disjoin((List<Item> *) cond_equalities);
Item_equal *equality; Item_equal *equality;
...@@ -15131,7 +15129,7 @@ internal_remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value) ...@@ -15131,7 +15129,7 @@ internal_remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
the all AND/OR levels of cond the all AND/OR levels of cond
*/ */
bool is_simplifiable_cond= false; bool is_simplifiable_cond= false;
propagate_new_equalities(thd, cond, cond_equalities, propagate_new_equalities(thd, this, cond_equalities,
cond_equal->upper_levels, cond_equal->upper_levels,
&is_simplifiable_cond); &is_simplifiable_cond);
/* /*
...@@ -15141,7 +15139,7 @@ internal_remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value) ...@@ -15141,7 +15139,7 @@ internal_remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
*/ */
if (is_simplifiable_cond) 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; return cond;
} }
should_fix_fields= 1; should_fix_fields= 1;
...@@ -15158,72 +15156,46 @@ internal_remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value) ...@@ -15158,72 +15156,46 @@ internal_remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
((Item_cond*) cond)->argument_list()->empty(); ((Item_cond*) cond)->argument_list()->empty();
return item; return item;
} }
} *cond_value= Item::COND_OK;
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)
return cond; return cond;
}
if (field->table->pos_in_table_list->is_inner_table_of_outer_join())
{ COND *
// outer join: transform "col IS NULL" to "col IS NULL or col=0" Item::remove_eq_conds(THD *thd, Item::cond_result *cond_value, bool top_level)
Item *or_cond= new(thd->mem_root) Item_cond_or(eq_cond, cond); {
if (!or_cond) if (const_item() && !is_expensive())
return cond;
cond= or_cond;
}
else
{ {
// not outer join: transform "col IS NULL" to "col=0" *cond_value= eval_const_cond(this) ? Item::COND_TRUE : Item::COND_FALSE;
cond= eq_cond; 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; return (COND*) 0;
} }
} if ((*cond_value= eq_cmp_result()) != Item::COND_OK)
else if (cond->const_item() && !cond->is_expensive())
{ {
*cond_value= eval_const_cond(cond) ? Item::COND_TRUE : Item::COND_FALSE; if (args[0]->eq(args[1], true))
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 (!left_item->maybe_null || if (!args[0]->maybe_null || functype() == Item_func::EQUAL_FUNC)
((Item_func*) cond)->functype() == Item_func::EQUAL_FUNC)
return (COND*) 0; // Compare of identical items return (COND*) 0; // Compare of identical items
} }
} }
*cond_value=Item::COND_OK; *cond_value= Item::COND_OK;
return cond; // Point at next and level return this; // Point at next and level
} }
/** /**
Remove const and eq items. Return new item, or NULL if no condition Remove const and eq items. Return new item, or NULL if no condition
cond_value is set to according: cond_value is set to according:
...@@ -15245,11 +15217,66 @@ internal_remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value) ...@@ -15245,11 +15217,66 @@ internal_remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
*/ */
COND * 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 && if (args[0]->type() == Item::FIELD_ITEM)
((Item_func*) cond)->functype() == Item_func::ISNULL_FUNC)
{ {
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: Handles this special case for some ODBC applications:
The are requesting the row that was just updated with a auto_increment 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) ...@@ -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 SELECT * from table_name where auto_increment_column IS NULL
This will be changed to: This will be changed to:
SELECT * from table_name where auto_increment_column = LAST_INSERT_ID 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; if (top_level) // "auto_increment_column IS NULL" is the only condition
Item **args= func->arguments();
if (args[0]->type() == Item::FIELD_ITEM)
{ {
Field *field=((Item_field*) args[0])->field;
if (field->flags & AUTO_INCREMENT_FLAG && !field->table->maybe_null && if (field->flags & AUTO_INCREMENT_FLAG && !field->table->maybe_null &&
(thd->variables.option_bits & OPTION_AUTO_IS_NULL) && (thd->variables.option_bits & OPTION_AUTO_IS_NULL) &&
(thd->first_successful_insert_id_in_prev_stmt > 0 && (thd->first_successful_insert_id_in_prev_stmt > 0 &&
thd->substitute_null_with_insert_id)) thd->substitute_null_with_insert_id))
{ {
#ifdef HAVE_QUERY_CACHE #ifdef HAVE_QUERY_CACHE
query_cache_abort(&thd->query_cache_tls); query_cache_abort(&thd->query_cache_tls);
#endif #endif
COND *new_cond; COND *new_cond, *cond= this;
if ((new_cond= new Item_func_eq(args[0], if ((new_cond= new Item_func_eq(args[0],
new Item_int("last_insert_id()", new Item_int("last_insert_id()",
thd->read_first_successful_insert_id_in_prev_stmt(), thd->read_first_successful_insert_id_in_prev_stmt(),
MY_INT64_NUM_DECIMAL_DIGITS)))) 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 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 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) ...@@ -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); ...@@ -1803,7 +1803,6 @@ bool cp_buffer_from_ref(THD *thd, TABLE *table, TABLE_REF *ref);
bool error_if_full_join(JOIN *join); bool error_if_full_join(JOIN *join);
int report_error(TABLE *table, int error); int report_error(TABLE *table, int error);
int safe_index_read(JOIN_TAB *tab); 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); int get_quick_record(SQL_SELECT *select);
SORT_FIELD * make_unireg_sortorder(THD *thd, ORDER *order, uint *length, SORT_FIELD * make_unireg_sortorder(THD *thd, ORDER *order, uint *length,
SORT_FIELD *sortorder); SORT_FIELD *sortorder);
......
...@@ -377,7 +377,7 @@ int mysql_update(THD *thd, ...@@ -377,7 +377,7 @@ int mysql_update(THD *thd,
if (conds) if (conds)
{ {
Item::cond_result cond_value; 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) if (cond_value == Item::COND_FALSE)
{ {
limit= 0; // Impossible WHERE 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