From 86f36719dba6641b4437536ee54e647c12282332 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
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     | 222 +++++----------------
 src/include/optimizer/clauses.h      |  12 +-
 3 files changed, 270 insertions(+), 250 deletions(-)

diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 6b6389267ca..3427302c8de 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 fd57067cdd2..d31f13d56d2 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 <nodes/relation.h>
-
+#include "nodes/relation.h"
 #include "nodes/primnodes.h"
 #include "nodes/plannodes.h"
 #include "nodes/nodeFuncs.h"
@@ -27,43 +26,40 @@
 
 #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);
 }
 
 /*
@@ -75,92 +71,22 @@ pull_varnos(Node *me)
 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.
@@ -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))
-	{
-
-		/*
-		 * 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);
 }
 
 /*
diff --git a/src/include/optimizer/clauses.h b/src/include/optimizer/clauses.h
index 78605c20c96..3cd74b1e250 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 <nodes/relation.h>
 
 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 */
-- 
GitLab