mirror of
https://github.com/MariaDB/server.git
synced 2025-08-01 03:47:19 +03:00
Merge mysql.com:/home/psergey/mysql-5.0-bug8804-r12
into mysql.com:/home/psergey/mysql-5.1-bug8804-r12-merge
This commit is contained in:
@ -506,72 +506,88 @@ err:
|
||||
|
||||
|
||||
/*
|
||||
test if it is known for optimisation IN subquery
|
||||
Remove the predicates pushed down into the subquery
|
||||
|
||||
SYNOPSIS
|
||||
JOIN::test_in_subselect()
|
||||
where - pointer for variable in which conditions should be
|
||||
stored if subquery is known
|
||||
JOIN::remove_subq_pushed_predicates()
|
||||
where IN Must be NULL
|
||||
OUT The remaining WHERE condition, or NULL
|
||||
|
||||
RETURN
|
||||
1 - known
|
||||
0 - unknown
|
||||
DESCRIPTION
|
||||
Given that this join will be executed using (unique|index)_subquery,
|
||||
without "checking NULL", remove the predicates that were pushed down
|
||||
into the subquery.
|
||||
|
||||
We can remove the equalities that will be guaranteed to be true by the
|
||||
fact that subquery engine will be using index lookup.
|
||||
|
||||
If the subquery compares scalar values, we can remove the condition that
|
||||
was wrapped into trig_cond (it will be checked when needed by the subquery
|
||||
engine)
|
||||
|
||||
If the subquery compares row values, we need to keep the wrapped
|
||||
equalities in the WHERE clause: when the left (outer) tuple has both NULL
|
||||
and non-NULL values, we'll do a full table scan and will rely on the
|
||||
equalities corresponding to non-NULL parts of left tuple to filter out
|
||||
non-matching records.
|
||||
*/
|
||||
|
||||
bool JOIN::test_in_subselect(Item **where)
|
||||
void JOIN::remove_subq_pushed_predicates(Item **where)
|
||||
{
|
||||
if (conds->type() == Item::FUNC_ITEM &&
|
||||
((Item_func *)this->conds)->functype() == Item_func::EQ_FUNC &&
|
||||
((Item_func *)conds)->arguments()[0]->type() == Item::REF_ITEM &&
|
||||
((Item_func *)conds)->arguments()[1]->type() == Item::FIELD_ITEM)
|
||||
{
|
||||
join_tab->info= "Using index";
|
||||
*where= 0;
|
||||
return 1;
|
||||
return;
|
||||
}
|
||||
if (conds->type() == Item::COND_ITEM &&
|
||||
((class Item_func *)this->conds)->functype() ==
|
||||
Item_func::COND_AND_FUNC)
|
||||
{
|
||||
if ((*where= remove_additional_cond(conds)))
|
||||
join_tab->info= "Using index; Using where";
|
||||
else
|
||||
join_tab->info= "Using index";
|
||||
return 1;
|
||||
*where= remove_additional_cond(conds);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Check if the passed HAVING clause is a clause added by subquery optimizer
|
||||
Index lookup-based subquery: save some flags for EXPLAIN output
|
||||
|
||||
SYNOPSIS
|
||||
is_having_subq_predicates()
|
||||
having Having clause
|
||||
save_index_subquery_explain_info()
|
||||
join_tab Subquery's join tab (there is only one as index lookup is
|
||||
only used for subqueries that are single-table SELECTs)
|
||||
where Subquery's WHERE clause
|
||||
|
||||
RETURN
|
||||
TRUE The passed HAVING clause was added by the subquery optimizer
|
||||
FALSE Otherwise
|
||||
DESCRIPTION
|
||||
For index lookup-based subquery (i.e. one executed with
|
||||
subselect_uniquesubquery_engine or subselect_indexsubquery_engine),
|
||||
check its EXPLAIN output row should contain
|
||||
"Using index" (TAB_INFO_FULL_SCAN_ON_NULL)
|
||||
"Using Where" (TAB_INFO_USING_WHERE)
|
||||
"Full scan on NULL key" (TAB_INFO_FULL_SCAN_ON_NULL)
|
||||
and set appropriate flags in join_tab->packed_info.
|
||||
*/
|
||||
|
||||
bool is_having_subq_predicates(Item *having)
|
||||
static void save_index_subquery_explain_info(JOIN_TAB *join_tab, Item* where)
|
||||
{
|
||||
if (having->type() == Item::FUNC_ITEM)
|
||||
join_tab->packed_info= TAB_INFO_HAVE_VALUE;
|
||||
if (join_tab->table->used_keys.is_set(join_tab->ref.key))
|
||||
join_tab->packed_info |= TAB_INFO_USING_INDEX;
|
||||
if (where)
|
||||
join_tab->packed_info |= TAB_INFO_USING_WHERE;
|
||||
for (uint i = 0; i < join_tab->ref.key_parts; i++)
|
||||
{
|
||||
if (((Item_func *) having)->functype() == Item_func::ISNOTNULLTEST_FUNC)
|
||||
return TRUE;
|
||||
if (((Item_func *) having)->functype() == Item_func::TRIG_COND_FUNC)
|
||||
if (join_tab->ref.cond_guards[i])
|
||||
{
|
||||
having= ((Item_func*)having)->arguments()[0];
|
||||
if (((Item_func *) having)->functype() == Item_func::ISNOTNULLTEST_FUNC)
|
||||
return TRUE;
|
||||
join_tab->packed_info |= TAB_INFO_FULL_SCAN_ON_NULL;
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
global select optimisation.
|
||||
return 0 - success
|
||||
@ -1027,51 +1043,47 @@ JOIN::optimize()
|
||||
if (join_tab[0].type == JT_EQ_REF &&
|
||||
join_tab[0].ref.items[0]->name == in_left_expr_name)
|
||||
{
|
||||
if (test_in_subselect(&where))
|
||||
{
|
||||
join_tab[0].type= JT_UNIQUE_SUBQUERY;
|
||||
error= 0;
|
||||
DBUG_RETURN(unit->item->
|
||||
change_engine(new
|
||||
subselect_uniquesubquery_engine(thd,
|
||||
join_tab,
|
||||
unit->item,
|
||||
where)));
|
||||
}
|
||||
remove_subq_pushed_predicates(&where);
|
||||
save_index_subquery_explain_info(join_tab, where);
|
||||
join_tab[0].type= JT_UNIQUE_SUBQUERY;
|
||||
error= 0;
|
||||
DBUG_RETURN(unit->item->
|
||||
change_engine(new
|
||||
subselect_uniquesubquery_engine(thd,
|
||||
join_tab,
|
||||
unit->item,
|
||||
where)));
|
||||
}
|
||||
else if (join_tab[0].type == JT_REF &&
|
||||
join_tab[0].ref.items[0]->name == in_left_expr_name)
|
||||
{
|
||||
if (test_in_subselect(&where))
|
||||
{
|
||||
join_tab[0].type= JT_INDEX_SUBQUERY;
|
||||
error= 0;
|
||||
DBUG_RETURN(unit->item->
|
||||
change_engine(new
|
||||
subselect_indexsubquery_engine(thd,
|
||||
join_tab,
|
||||
unit->item,
|
||||
where,
|
||||
0)));
|
||||
}
|
||||
remove_subq_pushed_predicates(&where);
|
||||
save_index_subquery_explain_info(join_tab, where);
|
||||
join_tab[0].type= JT_INDEX_SUBQUERY;
|
||||
error= 0;
|
||||
DBUG_RETURN(unit->item->
|
||||
change_engine(new
|
||||
subselect_indexsubquery_engine(thd,
|
||||
join_tab,
|
||||
unit->item,
|
||||
where,
|
||||
NULL,
|
||||
0)));
|
||||
}
|
||||
} else if (join_tab[0].type == JT_REF_OR_NULL &&
|
||||
join_tab[0].ref.items[0]->name == in_left_expr_name &&
|
||||
is_having_subq_predicates(having))
|
||||
having->name == in_having_cond)
|
||||
{
|
||||
join_tab[0].type= JT_INDEX_SUBQUERY;
|
||||
error= 0;
|
||||
|
||||
if ((conds= remove_additional_cond(conds)))
|
||||
join_tab->info= "Using index; Using where";
|
||||
else
|
||||
join_tab->info= "Using index";
|
||||
|
||||
conds= remove_additional_cond(conds);
|
||||
save_index_subquery_explain_info(join_tab, conds);
|
||||
DBUG_RETURN(unit->item->
|
||||
change_engine(new subselect_indexsubquery_engine(thd,
|
||||
join_tab,
|
||||
unit->item,
|
||||
conds,
|
||||
having,
|
||||
1)));
|
||||
}
|
||||
|
||||
@ -2585,9 +2597,7 @@ typedef struct key_field_t { // Used when finding key fields
|
||||
when val IS NULL.
|
||||
*/
|
||||
bool null_rejecting;
|
||||
|
||||
/* TRUE<=> This ref access is an outer subquery reference access */
|
||||
bool outer_ref;
|
||||
bool *cond_guard; /* See KEYUSE::cond_guard */
|
||||
} KEY_FIELD;
|
||||
|
||||
/* Values in optimize */
|
||||
@ -2889,7 +2899,7 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, Item_func *cond,
|
||||
cond->functype() == Item_func::MULT_EQUAL_FUNC) &&
|
||||
((*value)->type() == Item::FIELD_ITEM) &&
|
||||
((Item_field*)*value)->field->maybe_null());
|
||||
(*key_fields)->outer_ref= FALSE;
|
||||
(*key_fields)->cond_guard= NULL;
|
||||
(*key_fields)++;
|
||||
}
|
||||
|
||||
@ -2986,8 +2996,9 @@ add_key_fields(JOIN *join, KEY_FIELD **key_fields, uint *and_level,
|
||||
}
|
||||
|
||||
/*
|
||||
Subquery optimization: check if the encountered condition is one
|
||||
added by condition push down into subquery.
|
||||
Subquery optimization: Conditions that are pushed down into subqueries
|
||||
are wrapped into Item_func_trig_cond. We process the wrapped condition
|
||||
but need to set cond_guard for KEYUSE elements generated from it.
|
||||
*/
|
||||
{
|
||||
if (cond->type() == Item::FUNC_ITEM &&
|
||||
@ -3004,7 +3015,7 @@ add_key_fields(JOIN *join, KEY_FIELD **key_fields, uint *and_level,
|
||||
sargables);
|
||||
// Indicate that this ref access candidate is for subquery lookup:
|
||||
for (; save != *key_fields; save++)
|
||||
save->outer_ref= TRUE;
|
||||
save->cond_guard= ((Item_func_trig_cond*)cond)->get_trig_var();
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -3184,7 +3195,7 @@ add_key_part(DYNAMIC_ARRAY *keyuse_array,KEY_FIELD *key_field)
|
||||
keyuse.used_tables=key_field->val->used_tables();
|
||||
keyuse.optimize= key_field->optimize & KEY_OPTIMIZE_REF_OR_NULL;
|
||||
keyuse.null_rejecting= key_field->null_rejecting;
|
||||
keyuse.outer_ref= key_field->outer_ref;
|
||||
keyuse.cond_guard= key_field->cond_guard;
|
||||
VOID(insert_dynamic(keyuse_array,(gptr) &keyuse));
|
||||
}
|
||||
}
|
||||
@ -5144,7 +5155,8 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse,
|
||||
if (!(j->ref.key_buff= (byte*) thd->calloc(ALIGN_SIZE(length)*2)) ||
|
||||
!(j->ref.key_copy= (store_key**) thd->alloc((sizeof(store_key*) *
|
||||
(keyparts+1)))) ||
|
||||
!(j->ref.items= (Item**) thd->alloc(sizeof(Item*)*keyparts)))
|
||||
!(j->ref.items= (Item**) thd->alloc(sizeof(Item*)*keyparts)) ||
|
||||
!(j->ref.cond_guards= (bool**) thd->alloc(sizeof(uint*)*keyparts)))
|
||||
{
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
@ -5159,6 +5171,8 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse,
|
||||
if (ftkey)
|
||||
{
|
||||
j->ref.items[0]=((Item_func*)(keyuse->val))->key_item();
|
||||
/* Predicates pushed down into subquery can't be used FT access */
|
||||
j->ref.cond_guards[0]= NULL;
|
||||
if (keyuse->used_tables)
|
||||
DBUG_RETURN(TRUE); // not supported yet. SerG
|
||||
|
||||
@ -5175,6 +5189,7 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse,
|
||||
|
||||
uint maybe_null= test(keyinfo->key_part[i].null_bit);
|
||||
j->ref.items[i]=keyuse->val; // Save for cond removal
|
||||
j->ref.cond_guards[i]= keyuse->cond_guard;
|
||||
if (keyuse->null_rejecting)
|
||||
j->ref.null_rejecting |= 1 << i;
|
||||
keyuse_uses_no_tables= keyuse_uses_no_tables && !keyuse->used_tables;
|
||||
@ -7832,7 +7847,7 @@ change_cond_ref_to_const(THD *thd, I_List<COND_CMP> *save_list,
|
||||
|
||||
SYNOPSIS
|
||||
remove_additional_cond()
|
||||
conds - condition for processing
|
||||
conds Condition for processing
|
||||
|
||||
RETURN VALUES
|
||||
new conditions
|
||||
@ -11145,7 +11160,9 @@ int rr_sequential(READ_RECORD *info);
|
||||
int init_read_record_seq(JOIN_TAB *tab)
|
||||
{
|
||||
tab->read_record.read_record= rr_sequential;
|
||||
return tab->read_record.file->ha_rnd_init(1);
|
||||
if (tab->read_record.file->ha_rnd_init(1))
|
||||
return 1;
|
||||
return (*tab->read_record.read_record)(&tab->read_record);
|
||||
}
|
||||
|
||||
static int
|
||||
@ -15160,6 +15177,24 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
|
||||
|
||||
if (tab->info)
|
||||
item_list.push_back(new Item_string(tab->info,strlen(tab->info),cs));
|
||||
else if (tab->packed_info & TAB_INFO_HAVE_VALUE)
|
||||
{
|
||||
if (tab->packed_info & TAB_INFO_USING_INDEX)
|
||||
extra.append(STRING_WITH_LEN("; Using index"));
|
||||
if (tab->packed_info & TAB_INFO_USING_WHERE)
|
||||
extra.append(STRING_WITH_LEN("; Using where"));
|
||||
if (tab->packed_info & TAB_INFO_FULL_SCAN_ON_NULL)
|
||||
extra.append(STRING_WITH_LEN("; Full scan on NULL key"));
|
||||
/* Skip initial "; "*/
|
||||
const char *str= extra.ptr();
|
||||
uint32 len= extra.length();
|
||||
if (len)
|
||||
{
|
||||
str += 2;
|
||||
len -= 2;
|
||||
}
|
||||
item_list.push_back(new Item_string(str, len, cs));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION ||
|
||||
@ -15218,6 +15253,15 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
|
||||
}
|
||||
if (distinct & test_all_bits(used_tables,thd->used_tables))
|
||||
extra.append(STRING_WITH_LEN("; Distinct"));
|
||||
|
||||
for (uint part= 0; part < tab->ref.key_parts; part++)
|
||||
{
|
||||
if (tab->ref.cond_guards[part])
|
||||
{
|
||||
extra.append(STRING_WITH_LEN("; Full scan on NULL key"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Skip initial "; "*/
|
||||
const char *str= extra.ptr();
|
||||
|
Reference in New Issue
Block a user