static void check_ungrouped_columns()

in src/backend/parser/cypher_parse_agg.c [57:268]


static void check_ungrouped_columns(Node *node, ParseState *pstate, Query *qry,
                                    List *groupClauses, List *groupClauseVars,
                                    bool have_non_var_grouping,
                                    List **func_grouped_rels);
static bool check_ungrouped_columns_walker(Node *node,
                                           check_ungrouped_columns_context *context);
static void finalize_grouping_exprs(Node *node, ParseState *pstate, Query *qry,
                                    List *groupClauses, PlannerInfo *root,
                                    bool have_non_var_grouping);
static bool finalize_grouping_exprs_walker(Node *node,
                                           check_ungrouped_columns_context *context);
static List *expand_groupingset_node(GroupingSet *gs);
static List * expand_grouping_sets(List *groupingSets, int limit);

/*
 * From PG's parseCheckAggregates
 *
 * Check for aggregates where they shouldn't be and improper grouping.
 * This function should be called after the target list and qualifications
 * are finalized.
 *
 * Misplaced aggregates are now mostly detected in transformAggregateCall,
 * but it seems more robust to check for aggregates in recursive queries
 * only after everything is finalized.  In any case it's hard to detect
 * improper grouping on-the-fly, so we have to make another pass over the
 * query for that.
 */
void parse_check_aggregates(ParseState *pstate, Query *qry)
{
    List *gset_common = NIL;
    List *groupClauses = NIL;
    List *groupClauseCommonVars = NIL;
    bool have_non_var_grouping;
    List *func_grouped_rels = NIL;
    ListCell *l;
    bool hasJoinRTEs;
    bool hasSelfRefRTEs;
    PlannerInfo *root = NULL;
    Node *clause;

    /* This should only be called if we found aggregates or grouping */
    Assert(pstate->p_hasAggs || qry->groupClause || qry->havingQual || qry->groupingSets);

    /*
     * If we have grouping sets, expand them and find the intersection of all
     * sets.
     */
    if (qry->groupingSets)
    {
        /*
         * The limit of 4096 is arbitrary and exists simply to avoid resource
         * issues from pathological constructs.
         */
        List *gsets = expand_grouping_sets(qry->groupingSets, 4096);

        if (!gsets)
            ereport(ERROR,
                    (errcode(ERRCODE_STATEMENT_TOO_COMPLEX),
                     errmsg("too many grouping sets present (maximum 4096)"),
                     parser_errposition(pstate,
                                        qry->groupClause ?
                                        exprLocation((Node *) qry->groupClause) :
                                        exprLocation((Node *) qry->groupingSets))));

        /*
         * The intersection will often be empty, so help things along by
         * seeding the intersect with the smallest set.
         */
        gset_common = linitial(gsets);

        if (gset_common)
        {
            for_each_cell(l, gsets, lnext(gsets, list_head(gsets)))
            {
                gset_common = list_intersection_int(gset_common, lfirst(l));
                if (!gset_common)
                    break;
            }
        }

        /*
         * If there was only one grouping set in the expansion, AND if the
         * groupClause is non-empty (meaning that the grouping set is not
         * empty either), then we can ditch the grouping set and pretend we
         * just had a normal GROUP BY.
         */
        if (list_length(gsets) == 1 && qry->groupClause)
            qry->groupingSets = NIL;
    }

    /*
     * Scan the range table to see if there are JOIN or self-reference CTE
     * entries.  We'll need this info below.
     */
    hasJoinRTEs = hasSelfRefRTEs = false;
    foreach(l, pstate->p_rtable)
    {
        RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);

        if (rte->rtekind == RTE_JOIN)
            hasJoinRTEs = true;
        else if (rte->rtekind == RTE_CTE && rte->self_reference)
            hasSelfRefRTEs = true;
    }

    /*
     * Build a list of the acceptable GROUP BY expressions for use by
     * check_ungrouped_columns().
     *
     * We get the TLE, not just the expr, because GROUPING wants to know the
     * sortgroupref.
     */
    foreach(l, qry->groupClause)
    {
        SortGroupClause *grpcl = (SortGroupClause *) lfirst(l);
        TargetEntry *expr;

        expr = get_sortgroupclause_tle(grpcl, qry->targetList);
        if (expr == NULL)
            continue; /* probably cannot happen */

        groupClauses = lcons(expr, groupClauses);
    }

    /*
     * If there are join alias vars involved, we have to flatten them to the
     * underlying vars, so that aliased and unaliased vars will be correctly
     * taken as equal.  We can skip the expense of doing this if no rangetable
     * entries are RTE_JOIN kind. We use the planner's flatten_join_alias_vars
     * routine to do the flattening; it wants a PlannerInfo root node, which
     * fortunately can be mostly dummy.
     */
    if (hasJoinRTEs)
    {
        root = makeNode(PlannerInfo);
        root->parse = qry;
        root->planner_cxt = CurrentMemoryContext;
        root->hasJoinRTEs = true;

        groupClauses = (List *) flatten_join_alias_vars(root, qry,
                                                        (Node *) groupClauses);
    }

    /*
     * Detect whether any of the grouping expressions aren't simple Vars; if
     * they're all Vars then we don't have to work so hard in the recursive
     * scans.  (Note we have to flatten aliases before this.)
     *
     * Track Vars that are included in all grouping sets separately in
     * groupClauseCommonVars, since these are the only ones we can use to
     * check for functional dependencies.
     */
    have_non_var_grouping = false;
    foreach(l, groupClauses)
    {
        TargetEntry *tle = lfirst(l);

        if (!IsA(tle->expr, Var))
        {
            have_non_var_grouping = true;
        }
        else if (!qry->groupingSets ||
                 list_member_int(gset_common, tle->ressortgroupref))
        {
            groupClauseCommonVars = lappend(groupClauseCommonVars, tle->expr);
        }
    }

    /*
     * Check the targetlist and HAVING clause for ungrouped variables.
     *
     * Note: because we check resjunk tlist elements as well as regular ones,
     * this will also find ungrouped variables that came from ORDER BY and
     * WINDOW clauses.  For that matter, it's also going to examine the
     * grouping expressions themselves --- but they'll all pass the test ...
     *
     * We also finalize GROUPING expressions, but for that we need to traverse
     * the original (unflattened) clause in order to modify nodes.
     */
    clause = (Node *) qry->targetList;
    finalize_grouping_exprs(clause, pstate, qry, groupClauses, root,
                            have_non_var_grouping);
    if (hasJoinRTEs)
    {
        clause = flatten_join_alias_vars(root, qry, clause);
    }
    check_ungrouped_columns(clause, pstate, qry, groupClauses,
                            groupClauseCommonVars, have_non_var_grouping,
                            &func_grouped_rels);

    clause = (Node *) qry->havingQual;
    finalize_grouping_exprs(clause, pstate, qry, groupClauses, root,
                            have_non_var_grouping);
    if (hasJoinRTEs)
    {
        clause = flatten_join_alias_vars(root, qry, clause);
    }
    check_ungrouped_columns(clause, pstate, qry, groupClauses,
                            groupClauseCommonVars, have_non_var_grouping,
                            &func_grouped_rels);

    /*
     * Per spec, aggregates can't appear in a recursive term.
     */
    if (pstate->p_hasAggs && hasSelfRefRTEs)
    {
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_RECURSION),
                 errmsg("aggregate functions are not allowed in a recursive query's recursive term"),
                 parser_errposition(pstate, locate_agg_of_level((Node *) qry, 0))));
    }
}