1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-06 07:49:08 +03:00

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...
This commit is contained in:
Tom Lane
1999-06-19 03:41:45 +00:00
parent d30c4b0562
commit 86f36719db
3 changed files with 272 additions and 252 deletions

View File

@@ -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;
}