1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-27 18:02:13 +03:00

Merge bk-internal.mysql.com:/home/bk/mysql-5.0

into  zippy.cornsilk.net:/home/cmiller/work/mysql/mysql-5.0-maint
This commit is contained in:
cmiller@zippy.cornsilk.net
2007-01-31 16:23:05 -05:00
117 changed files with 3696 additions and 659 deletions

View File

@ -513,72 +513,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
@ -1017,51 +1033,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)));
}
@ -2557,9 +2569,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 */
@ -2858,7 +2868,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)++;
}
@ -2955,25 +2965,26 @@ 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 &&
((Item_func*)cond)->functype() == Item_func::TRIG_COND_FUNC)
{
cond= ((Item_func*)cond)->arguments()[0];
Item *cond_arg= ((Item_func*)cond)->arguments()[0];
if (!join->group_list && !join->order &&
join->unit->item &&
join->unit->item->substype() == Item_subselect::IN_SUBS &&
!join->unit->first_select()->next_select())
{
KEY_FIELD *save= *key_fields;
add_key_fields(join, key_fields, and_level, cond, usable_tables,
add_key_fields(join, key_fields, and_level, cond_arg, usable_tables,
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;
}
@ -3153,7 +3164,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));
}
}
@ -4992,7 +5003,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);
}
@ -5007,6 +5019,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
@ -5023,6 +5037,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;
@ -7653,7 +7668,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
@ -8768,8 +8783,7 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
if (type != Item::FIELD_ITEM &&
item->real_item()->type() == Item::FIELD_ITEM &&
(item->type() != Item::REF_ITEM ||
!((Item_ref *) item)->depended_from))
!((Item_ref *) item)->depended_from)
{
orig_item= item;
item= item->real_item();
@ -10914,7 +10928,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
@ -12265,7 +12281,7 @@ static int
create_sort_index(THD *thd, JOIN *join, ORDER *order,
ha_rows filesort_limit, ha_rows select_limit)
{
uint length;
uint length= 0;
ha_rows examined_rows;
TABLE *table;
SQL_SELECT *select;
@ -12286,8 +12302,10 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order,
!(join->select_options & SELECT_BIG_RESULT)) &&
test_if_skip_sort_order(tab,order,select_limit,0))
DBUG_RETURN(0);
for (ORDER *ord= join->order; ord; ord= ord->next)
length++;
if (!(join->sortorder=
make_unireg_sortorder(order,&length,join->sortorder)))
make_unireg_sortorder(order, &length, join->sortorder)))
goto err; /* purecov: inspected */
table->sort.io_cache=(IO_CACHE*) my_malloc(sizeof(IO_CACHE),
@ -12693,8 +12711,10 @@ SORT_FIELD *make_unireg_sortorder(ORDER *order, uint *length,
for (ORDER *tmp = order; tmp; tmp=tmp->next)
count++;
if (!sortorder)
sortorder= (SORT_FIELD*) sql_alloc(sizeof(SORT_FIELD)*(count+1));
pos=sort=sortorder;
sortorder= (SORT_FIELD*) sql_alloc(sizeof(SORT_FIELD) *
(max(count, *length) + 1));
pos= sort= sortorder;
if (!pos)
return 0;
@ -13217,49 +13237,83 @@ setup_group(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables,
bool *hidden_group_fields)
{
*hidden_group_fields=0;
ORDER *ord;
if (!order)
return 0; /* Everything is ok */
if (thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY)
{
Item *item;
List_iterator<Item> li(fields);
while ((item=li++))
item->marker=0; /* Marker that field is not used */
}
uint org_fields=all_fields.elements;
thd->where="group statement";
for (; order; order=order->next)
for (ord= order; ord; ord= ord->next)
{
if (find_order_in_list(thd, ref_pointer_array, tables, order, fields,
if (find_order_in_list(thd, ref_pointer_array, tables, ord, fields,
all_fields, TRUE))
return 1;
(*order->item)->marker=1; /* Mark found */
if ((*order->item)->with_sum_func)
(*ord->item)->marker= UNDEF_POS; /* Mark found */
if ((*ord->item)->with_sum_func)
{
my_error(ER_WRONG_GROUP_FIELD, MYF(0), (*order->item)->full_name());
my_error(ER_WRONG_GROUP_FIELD, MYF(0), (*ord->item)->full_name());
return 1;
}
}
if (thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY)
{
/* Don't allow one to use fields that is not used in GROUP BY */
Item *item;
List_iterator<Item> li(fields);
/*
Don't allow one to use fields that is not used in GROUP BY
For each select a list of field references that aren't under an
aggregate function is created. Each field in this list keeps the
position of the select list expression which it belongs to.
while ((item=li++))
First we check an expression from the select list against the GROUP BY
list. If it's found there then it's ok. It's also ok if this expression
is a constant or an aggregate function. Otherwise we scan the list
of non-aggregated fields and if we'll find at least one field reference
that belongs to this expression and doesn't occur in the GROUP BY list
we throw an error. If there are no fields in the created list for a
select list expression this means that all fields in it are used under
aggregate functions.
*/
Item *item;
Item_field *field;
int cur_pos_in_select_list= 0;
List_iterator<Item> li(fields);
List_iterator<Item_field> naf_it(thd->lex->current_select->non_agg_fields);
field= naf_it++;
while (field && (item=li++))
{
if (item->type() != Item::SUM_FUNC_ITEM && !item->marker &&
!item->const_item())
if (item->type() != Item::SUM_FUNC_ITEM && item->marker >= 0 &&
!item->const_item() &&
!(item->real_item()->type() == Item::FIELD_ITEM &&
item->used_tables() & OUTER_REF_TABLE_BIT))
{
/*
TODO: change ER_WRONG_FIELD_WITH_GROUP to more detailed
ER_NON_GROUPING_FIELD_USED
*/
my_error(ER_WRONG_FIELD_WITH_GROUP, MYF(0), item->full_name());
return 1;
while (field)
{
/* Skip fields from previous expressions. */
if (field->marker < cur_pos_in_select_list)
goto next_field;
/* Found a field from the next expression. */
if (field->marker > cur_pos_in_select_list)
break;
/*
Check whether the field occur in the GROUP BY list.
Throw the error later if the field isn't found.
*/
for (ord= order; ord; ord= ord->next)
if ((*ord->item)->eq((Item*)field, 0))
goto next_field;
/*
TODO: change ER_WRONG_FIELD_WITH_GROUP to more detailed
ER_NON_GROUPING_FIELD_USED
*/
my_error(ER_WRONG_FIELD_WITH_GROUP, MYF(0), field->full_name());
return 1;
next_field:
field= naf_it++;
}
}
cur_pos_in_select_list++;
}
}
if (org_fields != all_fields.elements)
@ -13385,10 +13439,12 @@ count_field_types(TMP_TABLE_PARAM *param, List<Item> &fields,
param->quick_group=1;
while ((field=li++))
{
Item::Type type=field->real_item()->type();
if (type == Item::FIELD_ITEM)
Item::Type type=field->type();
Item::Type real_type= field->real_item()->type();
if (type == Item::FIELD_ITEM || (real_type == Item::FIELD_ITEM &&
!((Item_ref *) field)->depended_from))
param->field_count++;
else if (type == Item::SUM_FUNC_ITEM)
else if (real_type == Item::SUM_FUNC_ITEM)
{
if (! field->const_item())
{
@ -14822,6 +14878,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 ||
@ -14880,6 +14954,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();