*
*
* 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
#include "optimizer/internal.h"
#include "optimizer/var.h"
-static bool agg_clause(Node *clause);
+
+static bool fix_opid_walker(Node *node, void *context);
Expr *
return NULL;
}
-/*****************************************************************************
- * AGG clause functions
- *****************************************************************************/
-
-static bool
-agg_clause(Node *clause)
-{
- return (clause != NULL && nodeTag(clause) == T_Aggref);
-}
-
/*****************************************************************************
* FUNC clause functions
*****************************************************************************/
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);
}
/*
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);
}
/*
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);
}
/*
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;
*relids = varno_list;
*vars = var_list;
- return;
}
/*
NumRelids(Node *clause)
{
List *vars = pull_var_clause(clause);
- List *i = NIL;
List *var_list = NIL;
+ List *i;
foreach(i, vars)
{
* 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)
/*
* 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;
}
*attno2 = _SELEC_VALUE_UNKNOWN_;
}
+/*--------------------
+ * CommuteClause: commute a binary operator clause
+ *--------------------
+ */
void
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;
+}
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
-#include <nodes/relation.h>
-
+#include "nodes/relation.h"
#include "nodes/primnodes.h"
#include "nodes/plannodes.h"
#include "nodes/nodeFuncs.h"
#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
- *
- * Descends down part of a parsetree (qual or tlist),
+ * pull_varnos
*
- * 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;
+ pull_varnos_walker(node, &result);
+ return result;
+}
- switch (nodeTag(me))
+static bool
+pull_varnos_walker(Node *node, List **listptr)
+{
+ if (node == NULL)
+ return false;
+ if (IsA(node, Var))
{
- 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;
+ Var *var = (Var *) node;
+ if (!intMember(var->varno, *listptr))
+ *listptr = lconsi(var->varno, *listptr);
+ return false;
}
- return result;
+ return expression_tree_walker(node, pull_varnos_walker, (void *) listptr);
}
/*
bool
contain_var_clause(Node *clause)
{
- List *temp;
-
- 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 contain_var_clause_walker(clause, NULL);
+}
- 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.
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))
- {
-
- /*
- * 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))
- {
- 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));
- }
- else if (case_clause(clause))
- {
- foreach(temp, ((CaseExpr *) clause)->args)
- {
- CaseWhen *when = (CaseWhen *) lfirst(temp);
+ pull_var_clause_walker(clause, &result);
+ return result;
+}
- 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
+static bool
+pull_var_clause_walker(Node *node, List **listptr)
+{
+ if (node == NULL)
+ return false;
+ if (IsA(node, Var))
{
- elog(ERROR, "pull_var_clause: Cannot handle node type %d",
- nodeTag(clause));
+ *listptr = lappend(*listptr, node);
+ return false;
}
-
- return retval;
+ return expression_tree_walker(node, pull_var_clause_walker,
+ (void *) listptr);
}
/*