diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c index a944a4d..6465d28 100644 --- a/src/backend/parser/parse_agg.c +++ b/src/backend/parser/parse_agg.c @@ -45,20 +45,22 @@ typedef struct } check_ungrouped_columns_context; static int check_agg_arguments(ParseState *pstate, List *args); static bool check_agg_arguments_walker(Node *node, check_agg_arguments_context *context); static void check_ungrouped_columns(Node *node, ParseState *pstate, Query *qry, List *groupClauses, bool have_non_var_grouping, List **func_grouped_rels); static bool check_ungrouped_columns_walker(Node *node, check_ungrouped_columns_context *context); +static bool check_functional_grouping_recurse(RangeTblEntry *rte, Var *var, + List *grouping_columns); /* * transformAggregateCall - * Finish initial transformation of an aggregate call * * parse_func.c has recognized the function as an aggregate, and has set up * all the fields of the Aggref except args, aggorder, aggdistinct and * agglevelsup. The passed-in args list has been through standard expression * transformation, while the passed-in aggorder list hasn't been transformed @@ -786,20 +788,71 @@ check_ungrouped_columns(Node *node, ParseState *pstate, Query *qry, context.pstate = pstate; context.qry = qry; context.groupClauses = groupClauses; context.have_non_var_grouping = have_non_var_grouping; context.func_grouped_rels = func_grouped_rels; context.sublevels_up = 0; check_ungrouped_columns_walker(node, &context); } + +static bool +check_functional_grouping_recurse(RangeTblEntry *rte, Var *var, + List *grouping_columns) +{ + TargetEntry *tle; + ListCell *lcell; + List *sq_grouping_columns = NIL; + ParseState *pstate; + bool have_non_var_grouping; + List *func_grouped_rels = NIL; + + /* Assumed that the rte corresponds to the varno in passed in var node */ + Assert(rte->rtekind == RTE_SUBQUERY); + /* + * Fetch those grouping columns which entirely belong to this subquery, i.e + * Var nodes which have the same varno as the given Var node. + */ + have_non_var_grouping = false; + foreach(lcell, grouping_columns) + { + Var *grouping_var = lfirst(lcell); + if (IsA(grouping_var, Var) && + grouping_var->varno == var->varno) + { + tle = get_tle_by_resno(rte->subquery->targetList, grouping_var->varattno); + Assert(tle && tle->expr); + sq_grouping_columns = lappend(sq_grouping_columns, tle->expr); + if (!IsA(tle->expr, Var)) + have_non_var_grouping = true; + } + } + + /* Obtain the expression corresponding var node */ + tle = get_tle_by_resno(rte->subquery->targetList, var->varattno); + Assert(tle && tle->expr); + + + pstate = make_parsestate(NULL); + /* Is this correct way to set the p_rtable from ParseState? */ + pstate->p_rtable = rte->subquery->rtable; + /* + * The function would throw error, in case the expression is found to be + * ungrouped, otherwise we are good to go + */ + check_ungrouped_columns((Node *)tle->expr, pstate, rte->subquery, + sq_grouping_columns, have_non_var_grouping, &func_grouped_rels); + return true; + +} + static bool check_ungrouped_columns_walker(Node *node, check_ungrouped_columns_context *context) { ListCell *gl; if (node == NULL) return false; if (IsA(node, Const) || IsA(node, Param)) @@ -893,20 +946,30 @@ check_ungrouped_columns_walker(Node *node, var->varno, 0, context->groupClauses, &context->qry->constraintDeps)) { *context->func_grouped_rels = lappend_int(*context->func_grouped_rels, var->varno); return false; /* acceptable */ } } + else if (rte->rtekind == RTE_SUBQUERY) + { + if (check_functional_grouping_recurse(rte, var, + context->groupClauses)) + { + *context->func_grouped_rels = + lappend_int(*context->func_grouped_rels, var->varno); + return false; + } + } /* Found an ungrouped local variable; generate error message */ attname = get_rte_attribute_name(rte, var->varattno); if (context->sublevels_up == 0) ereport(ERROR, (errcode(ERRCODE_GROUPING_ERROR), errmsg("column \"%s.%s\" must appear in the GROUP BY clause or be used in an aggregate function", rte->eref->aliasname, attname), parser_errposition(context->pstate, var->location))); else