* in check constraints; it would fail to examine the contents of
* subselects.
*/
- varList = pull_var_clause(expr, PVC_REJECT_PLACEHOLDERS);
+ varList = pull_var_clause(expr,
+ PVC_REJECT_AGGREGATES,
+ PVC_REJECT_PLACEHOLDERS);
keycount = list_length(varList);
if (keycount > 0)
List *vars;
char *colname;
- vars = pull_var_clause(expr, PVC_REJECT_PLACEHOLDERS);
+ vars = pull_var_clause(expr,
+ PVC_REJECT_AGGREGATES,
+ PVC_REJECT_PLACEHOLDERS);
/* eliminate duplicates */
vars = list_union(NIL, vars);
* subselects in WHEN clauses; it would fail to examine the contents
* of subselects.
*/
- varList = pull_var_clause(whenClause, PVC_REJECT_PLACEHOLDERS);
+ varList = pull_var_clause(whenClause,
+ PVC_REJECT_AGGREGATES,
+ PVC_REJECT_PLACEHOLDERS);
foreach(lc, varList)
{
Var *var = (Var *) lfirst(lc);
/*
* It would be unsafe to push down window function calls, but at least for
- * the moment we could never see any in a qual anyhow.
+ * the moment we could never see any in a qual anyhow. (The same applies
+ * to aggregates, which we check for in pull_var_clause below.)
*/
Assert(!contain_window_function(qual));
* Examine all Vars used in clause; since it's a restriction clause, all
* such Vars must refer to subselect output columns.
*/
- vars = pull_var_clause(qual, PVC_INCLUDE_PLACEHOLDERS);
+ vars = pull_var_clause(qual,
+ PVC_REJECT_AGGREGATES,
+ PVC_INCLUDE_PLACEHOLDERS);
foreach(vl, vars)
{
Var *var = (Var *) lfirst(vl);
{
EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
List *vars = pull_var_clause((Node *) cur_em->em_expr,
+ PVC_RECURSE_AGGREGATES,
PVC_INCLUDE_PLACEHOLDERS);
add_vars_to_targetlist(root, vars, ec->ec_relids);
continue;
sortexpr = em->em_expr;
exprvars = pull_var_clause((Node *) sortexpr,
+ PVC_RECURSE_AGGREGATES,
PVC_INCLUDE_PLACEHOLDERS);
foreach(k, exprvars)
{
build_base_rel_tlists(PlannerInfo *root, List *final_tlist)
{
List *tlist_vars = pull_var_clause((Node *) final_tlist,
+ PVC_RECURSE_AGGREGATES,
PVC_INCLUDE_PLACEHOLDERS);
if (tlist_vars != NIL)
*/
if (bms_membership(relids) == BMS_MULTIPLE)
{
- List *vars = pull_var_clause(clause, PVC_INCLUDE_PLACEHOLDERS);
+ List *vars = pull_var_clause(clause,
+ PVC_RECURSE_AGGREGATES,
+ PVC_INCLUDE_PLACEHOLDERS);
add_vars_to_targetlist(root, vars, relids);
list_free(vars);
* step. That's handled internally by make_sort_from_pathkeys,
* but we need the copyObject steps here to ensure that each plan
* node has a separately modifiable tlist.
+ *
+ * Note: it's essential here to use PVC_INCLUDE_AGGREGATES so that
+ * Vars mentioned only in aggregate expressions aren't pulled out
+ * as separate targetlist entries. Otherwise we could be putting
+ * ungrouped Vars directly into an Agg node's tlist, resulting in
+ * undefined behavior.
*/
- window_tlist = flatten_tlist(tlist);
- if (parse->hasAggs)
- window_tlist = add_to_flat_tlist(window_tlist,
- pull_agg_clause((Node *) tlist));
+ window_tlist = flatten_tlist(tlist,
+ PVC_INCLUDE_AGGREGATES,
+ PVC_INCLUDE_PLACEHOLDERS);
window_tlist = add_volatile_sort_exprs(window_tlist, tlist,
activeWindows);
result_plan->targetlist = (List *) copyObject(window_tlist);
}
/*
- * Otherwise, start with a "flattened" tlist (having just the vars
- * mentioned in the targetlist and HAVING qual --- but not upper-level
- * Vars; they will be replaced by Params later on). Note this includes
- * vars used in resjunk items, so we are covering the needs of ORDER BY
- * and window specifications.
+ * Otherwise, start with a "flattened" tlist (having just the Vars
+ * mentioned in the targetlist and HAVING qual). Note this includes Vars
+ * used in resjunk items, so we are covering the needs of ORDER BY and
+ * window specifications. Vars used within Aggrefs will be pulled out
+ * here, too.
*/
- sub_tlist = flatten_tlist(tlist);
- extravars = pull_var_clause(parse->havingQual, PVC_INCLUDE_PLACEHOLDERS);
+ sub_tlist = flatten_tlist(tlist,
+ PVC_RECURSE_AGGREGATES,
+ PVC_INCLUDE_PLACEHOLDERS);
+ extravars = pull_var_clause(parse->havingQual,
+ PVC_RECURSE_AGGREGATES,
+ PVC_INCLUDE_PLACEHOLDERS);
sub_tlist = add_to_flat_tlist(sub_tlist, extravars);
list_free(extravars);
*need_tlist_eval = false; /* only eval if not flat tlist */
ListCell *l;
vars = pull_var_clause((Node *) parse->returningList,
+ PVC_RECURSE_AGGREGATES,
PVC_INCLUDE_PLACEHOLDERS);
foreach(l, vars)
{
} inline_error_callback_arg;
static bool contain_agg_clause_walker(Node *node, void *context);
-static bool pull_agg_clause_walker(Node *node, List **context);
static bool count_agg_clauses_walker(Node *node,
count_agg_clauses_context *context);
static bool find_window_functions_walker(Node *node, WindowFuncLists *lists);
return expression_tree_walker(node, contain_agg_clause_walker, context);
}
-/*
- * pull_agg_clause
- * Recursively search for Aggref nodes within a clause.
- *
- * Returns a List of all Aggrefs found.
- *
- * This does not descend into subqueries, and so should be used only after
- * reduction of sublinks to subplans, or in contexts where it's known there
- * are no subqueries. There mustn't be outer-aggregate references either.
- */
-List *
-pull_agg_clause(Node *clause)
-{
- List *result = NIL;
-
- (void) pull_agg_clause_walker(clause, &result);
- return result;
-}
-
-static bool
-pull_agg_clause_walker(Node *node, List **context)
-{
- if (node == NULL)
- return false;
- if (IsA(node, Aggref))
- {
- Assert(((Aggref *) node)->agglevelsup == 0);
- *context = lappend(*context, node);
- return false; /* no need to descend into arguments */
- }
- Assert(!IsA(node, SubLink));
- return expression_tree_walker(node, pull_agg_clause_walker,
- (void *) context);
-}
-
/*
* count_agg_clauses
* Recursively count the Aggref nodes in an expression tree, and
* pull_var_clause does more than we need here, but it'll do and it's
* convenient to use.
*/
- vars = pull_var_clause(qual, PVC_INCLUDE_PLACEHOLDERS);
+ vars = pull_var_clause(qual,
+ PVC_RECURSE_AGGREGATES,
+ PVC_INCLUDE_PLACEHOLDERS);
foreach(vl, vars)
{
PlaceHolderVar *phv = (PlaceHolderVar *) lfirst(vl);
if (bms_membership(eval_at) == BMS_MULTIPLE)
{
List *vars = pull_var_clause((Node *) phinfo->ph_var->phexpr,
+ PVC_RECURSE_AGGREGATES,
PVC_INCLUDE_PLACEHOLDERS);
add_vars_to_targetlist(root, vars, eval_at);
* flatten_tlist
* Create a target list that only contains unique variables.
*
- * Note that Vars with varlevelsup > 0 are not included in the output
- * tlist. We expect that those will eventually be replaced with Params,
- * but that probably has not happened at the time this routine is called.
+ * Aggrefs and PlaceHolderVars in the input are treated according to
+ * aggbehavior and phbehavior, for which see pull_var_clause().
*
* 'tlist' is the current target list
*
* Copying the Var nodes is probably overkill, but be safe for now.
*/
List *
-flatten_tlist(List *tlist)
+flatten_tlist(List *tlist, PVCAggregateBehavior aggbehavior,
+ PVCPlaceHolderBehavior phbehavior)
{
List *vlist = pull_var_clause((Node *) tlist,
- PVC_INCLUDE_PLACEHOLDERS);
+ aggbehavior,
+ phbehavior);
List *new_tlist;
new_tlist = add_to_flat_tlist(NIL, vlist);
typedef struct
{
List *varlist;
- PVCPlaceHolderBehavior behavior;
+ PVCAggregateBehavior aggbehavior;
+ PVCPlaceHolderBehavior phbehavior;
} pull_var_clause_context;
typedef struct
* pull_var_clause
* Recursively pulls all Var nodes from an expression clause.
*
- * PlaceHolderVars are handled according to 'behavior':
+ * Aggrefs are handled according to 'aggbehavior':
+ * PVC_REJECT_AGGREGATES throw error if Aggref found
+ * PVC_INCLUDE_AGGREGATES include Aggrefs in output list
+ * PVC_RECURSE_AGGREGATES recurse into Aggref arguments
+ * Vars within an Aggref's expression are included only in the last case.
+ *
+ * PlaceHolderVars are handled according to 'phbehavior':
* PVC_REJECT_PLACEHOLDERS throw error if PlaceHolderVar found
* PVC_INCLUDE_PLACEHOLDERS include PlaceHolderVars in output list
- * PVC_RECURSE_PLACEHOLDERS recurse into PlaceHolderVar argument
+ * PVC_RECURSE_PLACEHOLDERS recurse into PlaceHolderVar arguments
* Vars within a PHV's expression are included only in the last case.
*
* CurrentOfExpr nodes are ignored in all cases.
*
- * Upper-level vars (with varlevelsup > 0) are not included.
- * (These probably represent errors too, but we don't complain.)
+ * Upper-level vars (with varlevelsup > 0) should not be seen here,
+ * likewise for upper-level Aggrefs and PlaceHolderVars.
*
* Returns list of nodes found. Note the nodes themselves are not
* copied, only referenced.
* of sublinks to subplans!
*/
List *
-pull_var_clause(Node *node, PVCPlaceHolderBehavior behavior)
+pull_var_clause(Node *node, PVCAggregateBehavior aggbehavior,
+ PVCPlaceHolderBehavior phbehavior)
{
pull_var_clause_context context;
context.varlist = NIL;
- context.behavior = behavior;
+ context.aggbehavior = aggbehavior;
+ context.phbehavior = phbehavior;
pull_var_clause_walker(node, &context);
return context.varlist;
return false;
if (IsA(node, Var))
{
- if (((Var *) node)->varlevelsup == 0)
- context->varlist = lappend(context->varlist, node);
+ if (((Var *) node)->varlevelsup != 0)
+ elog(ERROR, "Upper-level Var found where not expected");
+ context->varlist = lappend(context->varlist, node);
return false;
}
- if (IsA(node, PlaceHolderVar))
+ else if (IsA(node, Aggref))
+ {
+ if (((Aggref *) node)->agglevelsup != 0)
+ elog(ERROR, "Upper-level Aggref found where not expected");
+ switch (context->aggbehavior)
+ {
+ case PVC_REJECT_AGGREGATES:
+ elog(ERROR, "Aggref found where not expected");
+ break;
+ case PVC_INCLUDE_AGGREGATES:
+ context->varlist = lappend(context->varlist, node);
+ /* we do NOT descend into the contained expression */
+ return false;
+ case PVC_RECURSE_AGGREGATES:
+ /* ignore the aggregate, look at its argument instead */
+ break;
+ }
+ }
+ else if (IsA(node, PlaceHolderVar))
{
- switch (context->behavior)
+ if (((PlaceHolderVar *) node)->phlevelsup != 0)
+ elog(ERROR, "Upper-level PlaceHolderVar found where not expected");
+ switch (context->phbehavior)
{
case PVC_REJECT_PLACEHOLDERS:
elog(ERROR, "PlaceHolderVar found where not expected");
break;
case PVC_INCLUDE_PLACEHOLDERS:
- if (((PlaceHolderVar *) node)->phlevelsup == 0)
- context->varlist = lappend(context->varlist, node);
+ context->varlist = lappend(context->varlist, node);
/* we do NOT descend into the contained expression */
return false;
case PVC_RECURSE_PLACEHOLDERS:
* PlaceHolderVar doesn't change the number of groups, which boils
* down to ignoring the possible addition of nulls to the result set).
*/
- varshere = pull_var_clause(groupexpr, PVC_RECURSE_PLACEHOLDERS);
+ varshere = pull_var_clause(groupexpr,
+ PVC_RECURSE_AGGREGATES,
+ PVC_RECURSE_PLACEHOLDERS);
/*
* If we find any variable-free GROUP BY item, then either it is a
extern List *make_ands_implicit(Expr *clause);
extern bool contain_agg_clause(Node *clause);
-extern List *pull_agg_clause(Node *clause);
extern void count_agg_clauses(PlannerInfo *root, Node *clause,
AggClauseCosts *costs);
#ifndef TLIST_H
#define TLIST_H
-#include "nodes/relation.h"
+#include "optimizer/var.h"
extern TargetEntry *tlist_member(Node *node, List *targetlist);
extern TargetEntry *tlist_member_ignore_relabel(Node *node, List *targetlist);
-extern List *flatten_tlist(List *tlist);
+extern List *flatten_tlist(List *tlist, PVCAggregateBehavior aggbehavior,
+ PVCPlaceHolderBehavior phbehavior);
extern List *add_to_flat_tlist(List *tlist, List *exprs);
extern List *get_tlist_exprs(List *tlist, bool includeJunk);
#include "nodes/relation.h"
+typedef enum
+{
+ PVC_REJECT_AGGREGATES, /* throw error if Aggref found */
+ PVC_INCLUDE_AGGREGATES, /* include Aggrefs in output list */
+ PVC_RECURSE_AGGREGATES /* recurse into Aggref arguments */
+} PVCAggregateBehavior;
+
typedef enum
{
PVC_REJECT_PLACEHOLDERS, /* throw error if PlaceHolderVar found */
PVC_INCLUDE_PLACEHOLDERS, /* include PlaceHolderVars in output list */
- PVC_RECURSE_PLACEHOLDERS /* recurse into PlaceHolderVar argument */
+ PVC_RECURSE_PLACEHOLDERS /* recurse into PlaceHolderVar arguments */
} PVCPlaceHolderBehavior;
extern Relids pull_varnos(Node *node);
extern int locate_var_of_level(Node *node, int levelsup);
extern int locate_var_of_relation(Node *node, int relid, int levelsup);
extern int find_minimum_var_level(Node *node);
-extern List *pull_var_clause(Node *node, PVCPlaceHolderBehavior behavior);
+extern List *pull_var_clause(Node *node, PVCAggregateBehavior aggbehavior,
+ PVCPlaceHolderBehavior phbehavior);
extern Node *flatten_join_alias_vars(PlannerInfo *root, Node *node);
#endif /* VAR_H */
11 | develop | 5200 | 500 | 200 | 500 | 200
(10 rows)
+-- window function over ungrouped agg over empty row set (bug before 9.1)
+SELECT SUM(COUNT(f1)) OVER () FROM int4_tbl WHERE f1=42;
+ sum
+-----
+ 0
+(1 row)
+
-- test non-default frame specifications
SELECT four, ten,
sum(ten) over (partition by four order by ten),
THEN 200 END AS depadj FROM empsalary
)s;
+-- window function over ungrouped agg over empty row set (bug before 9.1)
+SELECT SUM(COUNT(f1)) OVER () FROM int4_tbl WHERE f1=42;
+
-- test non-default frame specifications
SELECT four, ten,
sum(ten) over (partition by four order by ten),