diff options
Diffstat (limited to 'src/backend/parser/parse_agg.c')
-rw-r--r-- | src/backend/parser/parse_agg.c | 63 |
1 files changed, 53 insertions, 10 deletions
diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c index c36c5fb08c6..a7c34bc8930 100644 --- a/src/backend/parser/parse_agg.c +++ b/src/backend/parser/parse_agg.c @@ -8,12 +8,13 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_agg.c,v 1.93 2010/03/17 16:52:38 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_agg.c,v 1.94 2010/08/07 02:44:07 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" +#include "catalog/pg_constraint.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" #include "optimizer/tlist.h" @@ -29,13 +30,16 @@ typedef struct { ParseState *pstate; + Query *qry; List *groupClauses; bool have_non_var_grouping; + List **func_grouped_rels; int sublevels_up; } check_ungrouped_columns_context; -static void check_ungrouped_columns(Node *node, ParseState *pstate, - List *groupClauses, bool have_non_var_grouping); +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); @@ -293,6 +297,7 @@ parseCheckAggregates(ParseState *pstate, Query *qry) { List *groupClauses = NIL; bool have_non_var_grouping; + List *func_grouped_rels = NIL; ListCell *l; bool hasJoinRTEs; bool hasSelfRefRTEs; @@ -408,14 +413,16 @@ parseCheckAggregates(ParseState *pstate, Query *qry) clause = (Node *) qry->targetList; if (hasJoinRTEs) clause = flatten_join_alias_vars(root, clause); - check_ungrouped_columns(clause, pstate, - groupClauses, have_non_var_grouping); + check_ungrouped_columns(clause, pstate, qry, + groupClauses, have_non_var_grouping, + &func_grouped_rels); clause = (Node *) qry->havingQual; if (hasJoinRTEs) clause = flatten_join_alias_vars(root, clause); - check_ungrouped_columns(clause, pstate, - groupClauses, have_non_var_grouping); + check_ungrouped_columns(clause, pstate, qry, + groupClauses, have_non_var_grouping, + &func_grouped_rels); /* * Per spec, aggregates can't appear in a recursive term. @@ -535,14 +542,17 @@ parseCheckWindowFuncs(ParseState *pstate, Query *qry) * way more pain than the feature seems worth. */ static void -check_ungrouped_columns(Node *node, ParseState *pstate, - List *groupClauses, bool have_non_var_grouping) +check_ungrouped_columns(Node *node, ParseState *pstate, Query *qry, + List *groupClauses, bool have_non_var_grouping, + List **func_grouped_rels) { check_ungrouped_columns_context context; 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); } @@ -619,10 +629,43 @@ check_ungrouped_columns_walker(Node *node, } } - /* Found an ungrouped local variable; generate error message */ + /* + * Check whether the Var is known functionally dependent on the GROUP + * BY columns. If so, we can allow the Var to be used, because the + * grouping is really a no-op for this table. However, this deduction + * depends on one or more constraints of the table, so we have to add + * those constraints to the query's constraintDeps list, because it's + * not semantically valid anymore if the constraint(s) get dropped. + * (Therefore, this check must be the last-ditch effort before raising + * error: we don't want to add dependencies unnecessarily.) + * + * Because this is a pretty expensive check, and will have the same + * outcome for all columns of a table, we remember which RTEs we've + * already proven functional dependency for in the func_grouped_rels + * list. This test also prevents us from adding duplicate entries + * to the constraintDeps list. + */ + if (list_member_int(*context->func_grouped_rels, var->varno)) + return false; /* previously proven acceptable */ + Assert(var->varno > 0 && (int) var->varno <= list_length(context->pstate->p_rtable)); rte = rt_fetch(var->varno, context->pstate->p_rtable); + if (rte->rtekind == RTE_RELATION) + { + if (check_functional_grouping(rte->relid, + var->varno, + 0, + context->groupClauses, + &context->qry->constraintDeps)) + { + *context->func_grouped_rels = + lappend_int(*context->func_grouped_rels, var->varno); + return false; /* acceptable */ + } + } + + /* Found an ungrouped local variable; generate error message */ attname = get_rte_attribute_name(rte, var->varattno); if (context->sublevels_up == 0) ereport(ERROR, |