本文可以结合我的上一篇文章“结果发送”结合来看。因为上一篇文章着重针对COUNT操作的结果发送,文本继续以COUNT操作为例阐述MySQL的聚合操作。
简要来说调用关系:
JOIN::optimize-->
JOIN::make_join_plan()-->
JOIN::estimate_rowcount()-->
add_group_and_distinct_keys()-->
is_indexed_agg_distinct()-->
JOIN::make_sum_func_list()
make_sum_func_list函数如下:
bool JOIN::make_sum_func_list(List<Item> &field_list,
List<Item> &send_result_set_metadata,
bool before_group_by, bool recompute)
{
List_iterator_fast<Item> it(field_list);
Item_sum **func;
Item *item;
DBUG_ENTER("make_sum_func_list");
if (*sum_funcs && !recompute)
DBUG_RETURN(FALSE); /* We have already initialized sum_funcs. */
func= sum_funcs;
while ((item=it++))
{
if (item->type() == Item::SUM_FUNC_ITEM && !item->const_item() &&
(!((Item_sum*) item)->depended_from() ||
((Item_sum *)item)->depended_from() == select_lex))
*func++= (Item_sum*) item;
}
if (before_group_by && rollup.state == ROLLUP::STATE_INITED)
{
rollup.state= ROLLUP::STATE_READY;
if (rollup_make_fields(field_list, send_result_set_metadata, &func))
DBUG_RETURN(TRUE); // Should never happen
}
else if (rollup.state == ROLLUP::STATE_NONE)
{
for (uint i=0 ; i <= send_group_parts ;i++)
sum_funcs_end[i]= func;
}
else if (rollup.state == ROLLUP::STATE_READY)
DBUG_RETURN(FALSE); // Don't put end marker
*func=0; // End marker
DBUG_RETURN(FALSE);
}
其中,*func++= (Item_sum*) item
是赋值操作,即把对应的item直接转化成item_sum类赋值给func,即JOIN的sum_funcs(func= sum_funcs)。
这里的item是传入的field_list(join->all_fields),这个field_list是在yacc parse而来,即yacc.cc parse的时候就new出了对应的item_sum_count。
对MySQL的改动如何来做
因为我们加入了新的线程来并发执行,所以会在optimizer里new出新的JOIN。从JOIN的constructor可以看到:all_fields(select->all_fields),是直接通过select赋值的。如果新的线程对应的JOIN不改动的话,默认就要共享all_fields这个变量,肯定是不行的。
因此我们在创建JOIN后,要重置对应的sum_funcs和sum_funcs_end。我们可以参照make_sum_func_list的方法进行重置:
item_sum** func;
func= sum_funcs;
while ((item=it++))
{
if (item->type() == Item::SUM_FUNC_ITEM && !item->const_item() &&
(!((Item_sum*) item)->depended_from() ||
((Item_sum *)item)->depended_from() == select_lex))
*func++= (Item_sum*) item;
}
使用item_sum_count自带的copy_or_same函数,来创建新的sum_funcs。同时也要reset sum_funcs_end:
sum_funcs_end= (Item_sum***) (sum_funcs + func_count + 1);
还有一点需要注意的是,在end_send_group中,如果结果为0会有如下调用,其中的no_rows_in_result()原代码用的是fields来调用,这样会把别的线程结果同时清零。我们需要把这里进行改动
// Calculate aggregate functions for no rows
List_iterator_fast<Item> it(*fields);
Item *item;
while ((item= it++))
item->no_rows_in_result();
类型判断
很多时候我们要判断聚合函数的类型。那么首先我们要先判断当前操作是否是聚合操作,然后再判断聚合操作的类型。
因为
Item类有一个纯虚函数: virtual enum Type type() const =0;
其对应的Type在该类中定义为:
enum Type {INVALID_ITEM= 0,
FIELD_ITEM, FUNC_ITEM, SUM_FUNC_ITEM, STRING_ITEM,
INT_ITEM, REAL_ITEM, NULL_ITEM, VARBIN_ITEM,
COPY_STR_ITEM, FIELD_AVG_ITEM, DEFAULT_VALUE_ITEM,
PROC_ITEM,COND_ITEM, REF_ITEM, FIELD_STD_ITEM,
FIELD_VARIANCE_ITEM, INSERT_VALUE_ITEM,
SUBSELECT_ITEM, ROW_ITEM, CACHE_ITEM, TYPE_HOLDER,
PARAM_ITEM, TRIGGER_FIELD_ITEM, DECIMAL_ITEM,
XPATH_NODESET, XPATH_NODESET_CMP,
VIEW_FIXER_ITEM};
进一步,Item_sum从Item派生出来,除了type()函数外,他还带有一个函数virtual enum Sumfunctype sum_func () const=0;
。 在Item_sum中定义了Sumfunctype的类型:
enum Sumfunctype
{ COUNT_FUNC, COUNT_DISTINCT_FUNC, SUM_FUNC, SUM_DISTINCT_FUNC, AVG_FUNC,
AVG_DISTINCT_FUNC, MIN_FUNC, MAX_FUNC, STD_FUNC,
VARIANCE_FUNC, SUM_BIT_FUNC, UDF_SUM_FUNC, GROUP_CONCAT_FUNC
};
我们可以通过这些类型来判读当前具体的聚合操作。初次之外,我们还可以利用lex的一些helper函数来进行辅助判断,如is_single_grouped():
/**
@return true if this query block is implicitly grouped and returns exactly
one row, which happens when it does not have a HAVING clause.
*/
bool is_single_grouped() const
{
return m_agg_func_used &&
group_list.elements == 0 &&
m_having_cond == NULL;
}
该函数返回true的条件是没有group by,也没有have。