in src/backend/parser/cypher_parse_agg.c [510:652]
static bool finalize_grouping_exprs_walker(Node *node,
check_ungrouped_columns_context *context)
{
ListCell *gl;
if (node == NULL)
return false;
if (IsA(node, Const) || IsA(node, Param))
return false; /* constants are always acceptable */
if (IsA(node, Aggref))
{
Aggref *agg = (Aggref *) node;
if ((int) agg->agglevelsup == context->sublevels_up)
{
/*
* If we find an aggregate call of the original level, do not
* recurse into its normal arguments, ORDER BY arguments, or
* filter; GROUPING exprs of this level are not allowed there. But
* check direct arguments as though they weren't in an aggregate.
*/
bool result;
Assert(!context->in_agg_direct_args);
context->in_agg_direct_args = true;
result = finalize_grouping_exprs_walker((Node *) agg->aggdirectargs,
context);
context->in_agg_direct_args = false;
return result;
}
/*
* We can skip recursing into aggregates of higher levels altogether,
* since they could not possibly contain exprs of concern to us (see
* transformAggregateCall). We do need to look at aggregates of lower
* levels, however.
*/
if ((int) agg->agglevelsup > context->sublevels_up)
return false;
}
if (IsA(node, GroupingFunc))
{
GroupingFunc *grp = (GroupingFunc *) node;
/*
* We only need to check GroupingFunc nodes at the exact level to
* which they belong, since they cannot mix levels in arguments.
*/
if ((int) grp->agglevelsup == context->sublevels_up)
{
ListCell *lc;
List *ref_list = NIL;
foreach(lc, grp->args)
{
Node *expr = lfirst(lc);
Index ref = 0;
if (context->root)
{
expr = flatten_join_alias_vars(context->root,
(Query *)context->root,
expr);
}
/*
* Each expression must match a grouping entry at the current
* query level. Unlike the general expression case, we don't
* allow functional dependencies or outer references.
*/
if (IsA(expr, Var))
{
Var *var = (Var *) expr;
if (var->varlevelsup == context->sublevels_up)
{
foreach(gl, context->groupClauses)
{
TargetEntry *tle = lfirst(gl);
Var *gvar = (Var *) tle->expr;
if (IsA(gvar, Var) &&
gvar->varno == var->varno &&
gvar->varattno == var->varattno &&
gvar->varlevelsup == 0)
{
ref = tle->ressortgroupref;
break;
}
}
}
}
else if (context->have_non_var_grouping &&
context->sublevels_up == 0)
{
foreach(gl, context->groupClauses)
{
TargetEntry *tle = lfirst(gl);
if (equal(expr, tle->expr))
{
ref = tle->ressortgroupref;
break;
}
}
}
if (ref == 0)
ereport(ERROR,
(errcode(ERRCODE_GROUPING_ERROR),
errmsg("arguments to GROUPING must be grouping expressions of the associated query level"),
parser_errposition(context->pstate,
exprLocation(expr))));
ref_list = lappend_int(ref_list, ref);
}
grp->refs = ref_list;
}
if ((int) grp->agglevelsup > context->sublevels_up)
return false;
}
if (IsA(node, Query))
{
/* Recurse into subselects */
bool result;
context->sublevels_up++;
result = query_tree_walker((Query *) node,
finalize_grouping_exprs_walker,
(void *) context, 0);
context->sublevels_up--;
return result;
}
return expression_tree_walker(node, finalize_grouping_exprs_walker,
(void *) context);
}