From 86f36719dba6641b4437536ee54e647c12282332 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Sat, 19 Jun 1999 03:41:45 +0000 Subject: [PATCH] Create a generic expression-tree-walker subroutine, which will gradually replace all of the boilerplate tree-walk-recursion code that currently exists in O(N) slightly different forms in N subroutines. I've had it with adding missing cases to these subroutines... --- src/backend/optimizer/util/clauses.c | 286 ++++++++++++++++++++------- src/backend/optimizer/util/var.c | 226 +++++---------------- src/include/optimizer/clauses.h | 12 +- 3 files changed, 272 insertions(+), 252 deletions(-) diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 6b6389267c..3427302c8d 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.35 1999/05/25 22:41:46 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.36 1999/06/19 03:41:45 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -35,7 +35,8 @@ #include "optimizer/internal.h" #include "optimizer/var.h" -static bool agg_clause(Node *clause); + +static bool fix_opid_walker(Node *node, void *context); Expr * @@ -139,16 +140,6 @@ get_rightop(Expr *clause) return NULL; } -/***************************************************************************** - * AGG clause functions - *****************************************************************************/ - -static bool -agg_clause(Node *clause) -{ - return (clause != NULL && nodeTag(clause) == T_Aggref); -} - /***************************************************************************** * FUNC clause functions *****************************************************************************/ @@ -163,7 +154,8 @@ bool is_funcclause(Node *clause) { return (clause != NULL && - nodeTag(clause) == T_Expr && ((Expr *) clause)->opType == FUNC_EXPR); + nodeTag(clause) == T_Expr && + ((Expr *) clause)->opType == FUNC_EXPR); } /* @@ -235,7 +227,8 @@ bool not_clause(Node *clause) { return (clause != NULL && - nodeTag(clause) == T_Expr && ((Expr *) clause)->opType == NOT_EXPR); + nodeTag(clause) == T_Expr && + ((Expr *) clause)->opType == NOT_EXPR); } /* @@ -283,7 +276,8 @@ bool and_clause(Node *clause) { return (clause != NULL && - nodeTag(clause) == T_Expr && ((Expr *) clause)->opType == AND_EXPR); + nodeTag(clause) == T_Expr && + ((Expr *) clause)->opType == AND_EXPR); } /* @@ -374,20 +368,20 @@ clause_get_relids_vars(Node *clause, Relids *relids, List **vars) List *clvars = pull_var_clause(clause); List *var_list = NIL; List *varno_list = NIL; - List *i = NIL; + List *i; foreach(i, clvars) { Var *var = (Var *) lfirst(i); List *vi; + Assert(var->varlevelsup == 0); if (!intMember(var->varno, varno_list)) varno_list = lappendi(varno_list, var->varno); foreach(vi, var_list) { Var *in_list = (Var *) lfirst(vi); - Assert(var->varlevelsup == 0); if (in_list->varno == var->varno && in_list->varattno == var->varattno) break; @@ -398,7 +392,6 @@ clause_get_relids_vars(Node *clause, Relids *relids, List **vars) *relids = varno_list; *vars = var_list; - return; } /* @@ -411,8 +404,8 @@ int NumRelids(Node *clause) { List *vars = pull_var_clause(clause); - List *i = NIL; List *var_list = NIL; + List *i; foreach(i, vars) { @@ -431,6 +424,8 @@ NumRelids(Node *clause) * Returns t iff the clause is a 'not' clause or if any of the * subclauses within an 'or' clause contain 'not's. * + * NOTE that only the top-level AND/OR structure is searched for NOTs; + * we are not interested in buried substructure. */ bool contains_not(Node *clause) @@ -524,81 +519,40 @@ qual_clause_p(Node *clause) /* * fix_opid - * Calculate the opfid from the opno... + * Calculate opid field from opno for each Oper node in given tree. * * Returns nothing. - * */ void fix_opid(Node *clause) { - if (clause == NULL || single_node(clause)) - ; - else if (or_clause(clause) || and_clause(clause)) - fix_opids(((Expr *) clause)->args); - else if (is_funcclause(clause)) - fix_opids(((Expr *) clause)->args); - else if (IsA(clause, ArrayRef)) - { - ArrayRef *aref = (ArrayRef *) clause; - - fix_opids(aref->refupperindexpr); - fix_opids(aref->reflowerindexpr); - fix_opid(aref->refexpr); - fix_opid(aref->refassgnexpr); - } - else if (not_clause(clause)) - fix_opid((Node *) get_notclausearg((Expr *) clause)); - else if (is_opclause(clause)) - { - replace_opid((Oper *) ((Expr *) clause)->oper); - fix_opid((Node *) get_leftop((Expr *) clause)); - fix_opid((Node *) get_rightop((Expr *) clause)); - } - else if (agg_clause(clause)) - fix_opid(((Aggref *) clause)->target); - else if (is_subplan(clause) && - ((SubPlan *) ((Expr *) clause)->oper)->sublink->subLinkType != EXISTS_SUBLINK) - { - List *lst; - - foreach(lst, ((SubPlan *) ((Expr *) clause)->oper)->sublink->oper) - { - replace_opid((Oper *) ((Expr *) lfirst(lst))->oper); - fix_opid((Node *) get_leftop((Expr *) lfirst(lst))); - } - } - else if (case_clause(clause)) - { - List *lst; - - fix_opid(((CaseExpr *) clause)->defresult); - - /* Run through the WHEN clauses... */ - foreach(lst, ((CaseExpr *) clause)->args) - { - fix_opid(((CaseWhen *) lfirst(lst))->expr); - fix_opid(((CaseWhen *) lfirst(lst))->result); - } - } + /* This tree walk requires no special setup, so away we go... */ + fix_opid_walker(clause, NULL); +} +static bool +fix_opid_walker (Node *node, void *context) +{ + if (node == NULL) + return false; + if (is_opclause(node)) + replace_opid((Oper *) ((Expr *) node)->oper); + return expression_tree_walker(node, fix_opid_walker, context); } /* * fix_opids - * Calculate the opfid from the opno for all the clauses... + * Calculate the opid from the opno for all the clauses... * * Returns its argument. * + * XXX This could and should be merged with fix_opid. + * */ List * fix_opids(List *clauses) { - List *clause; - - foreach(clause, clauses) - fix_opid(lfirst(clause)); - + fix_opid((Node *) clauses); return clauses; } @@ -771,6 +725,10 @@ get_rels_atts(Node *clause, *attno2 = _SELEC_VALUE_UNKNOWN_; } +/*-------------------- + * CommuteClause: commute a binary operator clause + *-------------------- + */ void CommuteClause(Node *clause) { @@ -805,3 +763,179 @@ CommuteClause(Node *clause) lfirst(((Expr *) clause)->args) = lsecond(((Expr *) clause)->args); lsecond(((Expr *) clause)->args) = temp; } + + +/*-------------------- + * Standard expression-tree walking support + * + * We used to have near-duplicate code in many different routines that + * understood how to recurse through an expression node tree. That was + * a pain to maintain, and we frequently had bugs due to some particular + * routine neglecting to support a particular node type. In most cases, + * these routines only actually care about certain node types, and don't + * care about other types except insofar as they have to recurse through + * non-primitive node types. Therefore, we now provide generic tree-walking + * logic to consolidate the redundant "boilerplate" code. + * + * expression_tree_walker() is designed to support routines that traverse + * a tree in a read-only fashion (although it will also work for routines + * that modify nodes in-place but never add or delete nodes). A walker + * routine should look like this: + * + * bool my_walker (Node *node, my_struct *context) + * { + * if (node == NULL) + * return false; + * // check for nodes that special work is required for, eg: + * if (IsA(node, Var)) + * { + * ... do special actions for Var nodes + * } + * else if (IsA(node, ...)) + * { + * ... do special actions for other node types + * } + * // for any node type not specially processed, do: + * return expression_tree_walker(node, my_walker, (void *) context); + * } + * + * The "context" argument points to a struct that holds whatever context + * information the walker routine needs (it can be used to return data + * gathered by the walker, too). This argument is not touched by + * expression_tree_walker, but it is passed down to recursive sub-invocations + * of my_walker. The tree walk is started from a setup routine that + * fills in the appropriate context struct, calls my_walker with the top-level + * node of the tree, and then examines the results. + * + * The walker routine should return "false" to continue the tree walk, or + * "true" to abort the walk and immediately return "true" to the top-level + * caller. This can be used to short-circuit the traversal if the walker + * has found what it came for. + * + * The node types handled by expression_tree_walker include all those + * normally found in target lists and qualifier clauses during the planning + * stage. In particular, it handles List nodes since a cnf-ified qual clause + * will have List structure at the top level, and it handles TargetEntry nodes + * so that a scan of a target list can be handled without additional code. + * (But only the "expr" part of a TargetEntry is examined, unless the walker + * chooses to process TargetEntry nodes specially.) + * + * expression_tree_walker will handle a SUBPLAN_EXPR node by recursing into + * the args and slink->oper lists (which belong to the outer plan), but it + * will *not* visit the inner plan, since that's typically what expression + * tree walkers want. A walker that wants to visit the subplan can force + * appropriate behavior by recognizing subplan nodes and doing the right + * thing. + * + * Bare SubLink nodes (without a SUBPLAN_EXPR) will trigger an error unless + * detected and processed by the walker. We expect that walkers used before + * sublink processing is done will handle them properly. (XXX Maybe ignoring + * them would be better default behavior?) + *-------------------- + */ + +bool +expression_tree_walker(Node *node, bool (*walker) (), void *context) +{ + List *temp; + + /* + * The walker has already visited the current node, + * and so we need only recurse into any sub-nodes it has. + * + * We assume that the walker is not interested in List nodes per se, + * so when we expect a List we just recurse directly to self without + * bothering to call the walker. + */ + if (node == NULL) + return false; + switch (nodeTag(node)) + { + case T_Ident: + case T_Const: + case T_Var: + case T_Param: + /* primitive node types with no subnodes */ + break; + case T_Expr: + { + Expr *expr = (Expr *) node; + if (expr->opType == SUBPLAN_EXPR) + { + /* examine args list (params to be passed to subplan) */ + if (expression_tree_walker((Node *) expr->args, + walker, context)) + return true; + /* examine oper list as well */ + if (expression_tree_walker( + (Node *) ((SubPlan *) expr->oper)->sublink->oper, + walker, context)) + return true; + /* but not the subplan itself */ + } + else + { + /* for other Expr node types, just examine args list */ + if (expression_tree_walker((Node *) expr->args, + walker, context)) + return true; + } + } + break; + case T_Aggref: + return walker(((Aggref *) node)->target, context); + case T_Iter: + return walker(((Iter *) node)->iterexpr, context); + case T_ArrayRef: + { + ArrayRef *aref = (ArrayRef *) node; + /* recurse directly for upper/lower array index lists */ + if (expression_tree_walker((Node *) aref->refupperindexpr, + walker, context)) + return true; + if (expression_tree_walker((Node *) aref->reflowerindexpr, + walker, context)) + return true; + /* walker must see the refexpr and refassgnexpr, however */ + if (walker(aref->refexpr, context)) + return true; + if (walker(aref->refassgnexpr, context)) + return true; + } + break; + case T_CaseExpr: + { + CaseExpr *caseexpr = (CaseExpr *) node; + /* we assume walker doesn't care about CaseWhens, either */ + foreach(temp, caseexpr->args) + { + CaseWhen *when = (CaseWhen *) lfirst(temp); + Assert(IsA(when, CaseWhen)); + if (walker(when->expr, context)) + return true; + if (walker(when->result, context)) + return true; + } + /* caseexpr->arg should be null, but we'll check it anyway */ + if (walker(caseexpr->arg, context)) + return true; + if (walker(caseexpr->defresult, context)) + return true; + } + break; + case T_List: + foreach(temp, (List *) node) + { + if (walker((Node *) lfirst(temp), context)) + return true; + } + break; + case T_TargetEntry: + return walker(((TargetEntry *) node)->expr, context); + default: + elog(ERROR, "expression_tree_walker: Unexpected node type %d", + nodeTag(node)); + break; + } + return false; +} diff --git a/src/backend/optimizer/util/var.c b/src/backend/optimizer/util/var.c index fd57067cdd..d31f13d56d 100644 --- a/src/backend/optimizer/util/var.c +++ b/src/backend/optimizer/util/var.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.19 1999/05/25 16:10:05 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.20 1999/06/19 03:41:45 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -15,8 +15,7 @@ #include "postgres.h" -#include - +#include "nodes/relation.h" #include "nodes/primnodes.h" #include "nodes/plannodes.h" #include "nodes/nodeFuncs.h" @@ -27,45 +26,42 @@ #include "parser/parsetree.h" + +static bool pull_varnos_walker(Node *node, List **listptr); +static bool contain_var_clause_walker(Node *node, void *context); +static bool pull_var_clause_walker(Node *node, List **listptr); + + /* - * find_varnos + * pull_varnos * - * Descends down part of a parsetree (qual or tlist), - * - * XXX assumes varno's are always integers, which shouldn't be true... - * (though it currently is, see primnodes.h) + * Create a list of all the distinct varnos present in a parsetree + * (tlist or qual). */ List * -pull_varnos(Node *me) +pull_varnos(Node *node) { - List *i, - *result = NIL; + List *result = NIL; - if (me == NULL) - return NIL; - - switch (nodeTag(me)) - { - case T_List: - foreach(i, (List *) me) - result = nconc(result, pull_varnos(lfirst(i))); - break; - case T_ArrayRef: - foreach(i, ((ArrayRef *) me)->refupperindexpr) - result = nconc(result, pull_varnos(lfirst(i))); - foreach(i, ((ArrayRef *) me)->reflowerindexpr) - result = nconc(result, pull_varnos(lfirst(i))); - result = nconc(result, pull_varnos(((ArrayRef *) me)->refassgnexpr)); - break; - case T_Var: - result = lconsi(((Var *) me)->varno, NIL); - break; - default: - break; - } + pull_varnos_walker(node, &result); return result; } +static bool +pull_varnos_walker(Node *node, List **listptr) +{ + if (node == NULL) + return false; + if (IsA(node, Var)) + { + Var *var = (Var *) node; + if (!intMember(var->varno, *listptr)) + *listptr = lconsi(var->varno, *listptr); + return false; + } + return expression_tree_walker(node, pull_varnos_walker, (void *) listptr); +} + /* * contain_var_clause * Recursively scan a clause to discover whether it contains any Var nodes. @@ -75,92 +71,22 @@ pull_varnos(Node *me) bool contain_var_clause(Node *clause) { - List *temp; + return contain_var_clause_walker(clause, NULL); +} - if (clause == NULL) - return FALSE; - else if (IsA(clause, Var)) - return TRUE; - else if (single_node(clause)) - return FALSE; - else if (IsA(clause, Iter)) - return contain_var_clause(((Iter *) clause)->iterexpr); - else if (is_subplan(clause)) - { - foreach(temp, ((Expr *) clause)->args) - { - if (contain_var_clause(lfirst(temp))) - return TRUE; - } - /* Also check left sides of Oper-s */ - foreach(temp, ((SubPlan *) ((Expr *) clause)->oper)->sublink->oper) - { - if (contain_var_clause(lfirst(((Expr *) lfirst(temp))->args))) - return TRUE; - } - return FALSE; - } - else if (IsA(clause, Expr)) - { - - /* - * Recursively scan the arguments of an expression. NOTE: this - * must come after is_subplan() case since subplan is a kind of - * Expr node. - */ - foreach(temp, ((Expr *) clause)->args) - { - if (contain_var_clause(lfirst(temp))) - return TRUE; - } - return FALSE; - } - else if (IsA(clause, Aggref)) - return contain_var_clause(((Aggref *) clause)->target); - else if (IsA(clause, ArrayRef)) - { - foreach(temp, ((ArrayRef *) clause)->refupperindexpr) - { - if (contain_var_clause(lfirst(temp))) - return TRUE; - } - foreach(temp, ((ArrayRef *) clause)->reflowerindexpr) - { - if (contain_var_clause(lfirst(temp))) - return TRUE; - } - if (contain_var_clause(((ArrayRef *) clause)->refexpr)) - return TRUE; - if (contain_var_clause(((ArrayRef *) clause)->refassgnexpr)) - return TRUE; - return FALSE; - } - else if (case_clause(clause)) - { - foreach(temp, ((CaseExpr *) clause)->args) - { - CaseWhen *when = (CaseWhen *) lfirst(temp); - - if (contain_var_clause(when->expr)) - return TRUE; - if (contain_var_clause(when->result)) - return TRUE; - } - return (contain_var_clause(((CaseExpr *) clause)->defresult)); - } - else - { - elog(ERROR, "contain_var_clause: Cannot handle node type %d", - nodeTag(clause)); - } - - return FALSE; +static bool +contain_var_clause_walker(Node *node, void *context) +{ + if (node == NULL) + return false; + if (IsA(node, Var)) + return true; /* abort the tree traversal and return true */ + return expression_tree_walker(node, contain_var_clause_walker, context); } /* * pull_var_clause - * Recursively pulls all var nodes from a clause by pulling vars from the - * left and right operands of the clause. + * Recursively pulls all var nodes from an expression clause. * * Returns list of varnodes found. Note the varnodes themselves are not * copied, only referenced. @@ -168,68 +94,24 @@ contain_var_clause(Node *clause) List * pull_var_clause(Node *clause) { - List *retval = NIL; - List *temp; + List *result = NIL; - if (clause == NULL) - return NIL; - else if (IsA(clause, Var)) - retval = lcons(clause, NIL); - else if (single_node(clause)) - retval = NIL; - else if (IsA(clause, Iter)) - retval = pull_var_clause(((Iter *) clause)->iterexpr); - else if (is_subplan(clause)) - { - foreach(temp, ((Expr *) clause)->args) - retval = nconc(retval, pull_var_clause(lfirst(temp))); - /* Also get Var-s from left sides of Oper-s */ - foreach(temp, ((SubPlan *) ((Expr *) clause)->oper)->sublink->oper) - retval = nconc(retval, - pull_var_clause(lfirst(((Expr *) lfirst(temp))->args))); - } - else if (IsA(clause, Expr)) - { + pull_var_clause_walker(clause, &result); + return result; +} - /* - * Recursively scan the arguments of an expression. NOTE: this - * must come after is_subplan() case since subplan is a kind of - * Expr node. - */ - foreach(temp, ((Expr *) clause)->args) - retval = nconc(retval, pull_var_clause(lfirst(temp))); - } - else if (IsA(clause, Aggref)) - retval = pull_var_clause(((Aggref *) clause)->target); - else if (IsA(clause, ArrayRef)) +static bool +pull_var_clause_walker(Node *node, List **listptr) +{ + if (node == NULL) + return false; + if (IsA(node, Var)) { - foreach(temp, ((ArrayRef *) clause)->refupperindexpr) - retval = nconc(retval, pull_var_clause(lfirst(temp))); - foreach(temp, ((ArrayRef *) clause)->reflowerindexpr) - retval = nconc(retval, pull_var_clause(lfirst(temp))); - retval = nconc(retval, - pull_var_clause(((ArrayRef *) clause)->refexpr)); - retval = nconc(retval, - pull_var_clause(((ArrayRef *) clause)->refassgnexpr)); + *listptr = lappend(*listptr, node); + return false; } - else if (case_clause(clause)) - { - foreach(temp, ((CaseExpr *) clause)->args) - { - CaseWhen *when = (CaseWhen *) lfirst(temp); - - retval = nconc(retval, pull_var_clause(when->expr)); - retval = nconc(retval, pull_var_clause(when->result)); - } - retval = nconc(retval, pull_var_clause(((CaseExpr *) clause)->defresult)); - } - else - { - elog(ERROR, "pull_var_clause: Cannot handle node type %d", - nodeTag(clause)); - } - - return retval; + return expression_tree_walker(node, pull_var_clause_walker, + (void *) listptr); } /* diff --git a/src/include/optimizer/clauses.h b/src/include/optimizer/clauses.h index 78605c20c9..3cd74b1e25 100644 --- a/src/include/optimizer/clauses.h +++ b/src/include/optimizer/clauses.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: clauses.h,v 1.18 1999/05/25 22:43:03 momjian Exp $ + * $Id: clauses.h,v 1.19 1999/06/19 03:41:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -17,6 +17,7 @@ #include extern Expr *make_clause(int type, Node *oper, List *args); + extern bool is_opclause(Node *clause); extern Expr *make_opclause(Oper *op, Var *leftop, Var *rightop); extern Var *get_leftop(Expr *clause); @@ -51,8 +52,11 @@ extern void get_rels_atts(Node *clause, int *relid1, AttrNumber *attno1, int *relid2, AttrNumber *attno2); extern void CommuteClause(Node *clause); -#define is_subplan(clause) ((Node*) clause != NULL && \ - nodeTag((Node*) clause) == T_Expr && \ - ((Expr *) clause)->opType == SUBPLAN_EXPR) +extern bool expression_tree_walker(Node *node, bool (*walker) (), + void *context); + +#define is_subplan(clause) ((Node*) (clause) != NULL && \ + nodeTag((Node*) (clause)) == T_Expr && \ + ((Expr *) (clause))->opType == SUBPLAN_EXPR) #endif /* CLAUSES_H */