aboutsummaryrefslogtreecommitdiff
path: root/src/backend/optimizer/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer/util')
-rw-r--r--src/backend/optimizer/util/clauses.c11
-rw-r--r--src/backend/optimizer/util/relnode.c150
-rw-r--r--src/backend/optimizer/util/var.c375
3 files changed, 496 insertions, 40 deletions
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 8a4bcf4d9c4..6585fb1905d 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.93 2002/01/03 18:01:59 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.94 2002/03/12 00:51:50 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -1808,12 +1808,8 @@ expression_tree_walker(Node *node,
return true;
if (walker(join->quals, context))
return true;
- if (walker((Node *) join->colvars, context))
- return true;
-
/*
- * alias clause, using list, colnames list are deemed
- * uninteresting.
+ * alias clause, using list are deemed uninteresting.
*/
}
break;
@@ -2186,8 +2182,7 @@ expression_tree_mutator(Node *node,
MUTATE(newnode->larg, join->larg, Node *);
MUTATE(newnode->rarg, join->rarg, Node *);
MUTATE(newnode->quals, join->quals, Node *);
- MUTATE(newnode->colvars, join->colvars, List *);
- /* We do not mutate alias, using, or colnames by default */
+ /* We do not mutate alias or using by default */
return (Node *) newnode;
}
break;
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 457826f5d31..0fad16fbdd0 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.35 2001/10/25 05:49:34 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.36 2002/03/12 00:51:51 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -40,26 +40,26 @@ static void subbuild_joinrel_joinlist(RelOptInfo *joinrel,
/*
* build_base_rel
- * Returns relation entry corresponding to 'relid', creating a new one
- * if necessary. This is for base relations.
+ * Construct a new base relation RelOptInfo, and put it in the query's
+ * base_rel_list.
*/
-RelOptInfo *
+void
build_base_rel(Query *root, int relid)
{
List *rels;
RelOptInfo *rel;
- /* Already made? */
+ /* Rel should not exist already */
foreach(rels, root->base_rel_list)
{
rel = (RelOptInfo *) lfirst(rels);
/* length(rel->relids) == 1 for all members of base_rel_list */
if (lfirsti(rel->relids) == relid)
- return rel;
+ elog(ERROR, "build_base_rel: rel already exists");
}
- /* It should not exist as an "other" rel */
+ /* It should not exist as an "other" rel, either */
foreach(rels, root->other_rel_list)
{
rel = (RelOptInfo *) lfirst(rels);
@@ -73,14 +73,12 @@ build_base_rel(Query *root, int relid)
/* and add it to the list */
root->base_rel_list = lcons(rel, root->base_rel_list);
-
- return rel;
}
/*
* build_other_rel
* Returns relation entry corresponding to 'relid', creating a new one
- * if necessary. This is for 'other' relations, which are just like
+ * if necessary. This is for 'other' relations, which are much like
* base relations except that they live in a different list.
*/
RelOptInfo *
@@ -111,6 +109,10 @@ build_other_rel(Query *root, int relid)
/* No existing RelOptInfo for this other rel, so make a new one */
rel = make_base_rel(root, relid);
+ /* if it's not a join rel, must be a child rel */
+ if (rel->reloptkind == RELOPT_BASEREL)
+ rel->reloptkind = RELOPT_OTHER_CHILD_REL;
+
/* and add it to the list */
root->other_rel_list = lcons(rel, root->other_rel_list);
@@ -127,8 +129,9 @@ static RelOptInfo *
make_base_rel(Query *root, int relid)
{
RelOptInfo *rel = makeNode(RelOptInfo);
- Oid relationObjectId;
+ RangeTblEntry *rte = rt_fetch(relid, root->rtable);
+ rel->reloptkind = RELOPT_BASEREL;
rel->relids = makeListi1(relid);
rel->rows = 0;
rel->width = 0;
@@ -142,29 +145,40 @@ make_base_rel(Query *root, int relid)
rel->pages = 0;
rel->tuples = 0;
rel->subplan = NULL;
+ rel->joinrti = 0;
+ rel->joinrteids = NIL;
rel->baserestrictinfo = NIL;
rel->baserestrictcost = 0;
rel->outerjoinset = NIL;
rel->joininfo = NIL;
rel->innerjoin = NIL;
- /* Check rtable to see if it's a plain relation or a subquery */
- relationObjectId = getrelid(relid, root->rtable);
-
- if (relationObjectId != InvalidOid)
- {
- /* Plain relation --- retrieve statistics from the system catalogs */
- bool indexed;
-
- get_relation_info(relationObjectId,
- &indexed, &rel->pages, &rel->tuples);
- if (indexed)
- rel->indexlist = find_secondary_indexes(relationObjectId);
- }
- else
+ /* Check type of rtable entry */
+ switch (rte->rtekind)
{
- /* subquery --- mark it as such for later processing */
- rel->issubquery = true;
+ case RTE_RELATION:
+ {
+ /* Table --- retrieve statistics from the system catalogs */
+ bool indexed;
+
+ get_relation_info(rte->relid,
+ &indexed, &rel->pages, &rel->tuples);
+ if (indexed)
+ rel->indexlist = find_secondary_indexes(rte->relid);
+ break;
+ }
+ case RTE_SUBQUERY:
+ /* Subquery --- mark it as such for later processing */
+ rel->issubquery = true;
+ break;
+ case RTE_JOIN:
+ /* Join --- must be an otherrel */
+ rel->reloptkind = RELOPT_OTHER_JOIN_REL;
+ break;
+ default:
+ elog(ERROR, "make_base_rel: unsupported RTE kind %d",
+ (int) rte->rtekind);
+ break;
}
return rel;
@@ -204,6 +218,47 @@ find_base_rel(Query *root, int relid)
}
/*
+ * find_other_rel
+ * Find an otherrel entry, if one exists for the given relid.
+ * Return NULL if no entry.
+ */
+RelOptInfo *
+find_other_rel(Query *root, int relid)
+{
+ List *rels;
+
+ foreach(rels, root->other_rel_list)
+ {
+ RelOptInfo *rel = (RelOptInfo *) lfirst(rels);
+
+ if (lfirsti(rel->relids) == relid)
+ return rel;
+ }
+ return NULL;
+}
+
+/*
+ * find_other_rel_for_join
+ * Look for an otherrel for a join RTE matching the given baserel set.
+ * Return NULL if no entry.
+ */
+RelOptInfo *
+find_other_rel_for_join(Query *root, List *relids)
+{
+ List *rels;
+
+ foreach(rels, root->other_rel_list)
+ {
+ RelOptInfo *rel = (RelOptInfo *) lfirst(rels);
+
+ if (rel->reloptkind == RELOPT_OTHER_JOIN_REL
+ && sameseti(relids, rel->outerjoinset))
+ return rel;
+ }
+ return NULL;
+}
+
+/*
* find_join_rel
* Returns relation entry corresponding to 'relids' (a list of RT indexes),
* or NULL if none exists. This is for join relations.
@@ -252,6 +307,7 @@ build_join_rel(Query *root,
{
List *joinrelids;
RelOptInfo *joinrel;
+ RelOptInfo *joinrterel;
List *restrictlist;
List *new_outer_tlist;
List *new_inner_tlist;
@@ -286,6 +342,7 @@ build_join_rel(Query *root,
* Nope, so make one.
*/
joinrel = makeNode(RelOptInfo);
+ joinrel->reloptkind = RELOPT_JOINREL;
joinrel->relids = joinrelids;
joinrel->rows = 0;
joinrel->width = 0;
@@ -299,24 +356,38 @@ build_join_rel(Query *root,
joinrel->pages = 0;
joinrel->tuples = 0;
joinrel->subplan = NULL;
+ joinrel->joinrti = 0;
+ joinrel->joinrteids = nconc(listCopy(outer_rel->joinrteids),
+ inner_rel->joinrteids);
joinrel->baserestrictinfo = NIL;
joinrel->baserestrictcost = 0;
joinrel->outerjoinset = NIL;
joinrel->joininfo = NIL;
joinrel->innerjoin = NIL;
+ /* Is there a join RTE matching this join? */
+ joinrterel = find_other_rel_for_join(root, joinrelids);
+ if (joinrterel)
+ {
+ /* Yes, remember its RT index */
+ joinrel->joinrti = lfirsti(joinrterel->relids);
+ joinrel->joinrteids = lconsi(joinrel->joinrti, joinrel->joinrteids);
+ }
+
/*
* Create a new tlist by removing irrelevant elements from both tlists
* of the outer and inner join relations and then merging the results
* together.
*
+ * XXX right now we don't remove any irrelevant elements, we just
+ * append the two tlists together. Someday consider pruning vars from the
+ * join's targetlist if they are needed only to evaluate restriction
+ * clauses of this join, and will never be accessed at higher levels of
+ * the plantree.
+ *
* NOTE: the tlist order for a join rel will depend on which pair of
* outer and inner rels we first try to build it from. But the
* contents should be the same regardless.
- *
- * XXX someday: consider pruning vars from the join's targetlist if they
- * are needed only to evaluate restriction clauses of this join, and
- * will never be accessed at higher levels of the plantree.
*/
new_outer_tlist = new_join_tlist(outer_rel->targetlist, 1);
new_inner_tlist = new_join_tlist(inner_rel->targetlist,
@@ -324,6 +395,23 @@ build_join_rel(Query *root,
joinrel->targetlist = nconc(new_outer_tlist, new_inner_tlist);
/*
+ * If there are any alias variables attached to the matching join RTE,
+ * attach them to the tlist too, so that they will be evaluated for use
+ * at higher plan levels.
+ */
+ if (joinrterel)
+ {
+ List *jrtetl;
+
+ foreach(jrtetl, joinrterel->targetlist)
+ {
+ TargetEntry *jrtete = lfirst(jrtetl);
+
+ add_var_to_tlist(joinrel, (Var *) jrtete->expr);
+ }
+ }
+
+ /*
* Construct restrict and join clause lists for the new joinrel. (The
* caller might or might not need the restrictlist, but I need it
* anyway for set_joinrel_size_estimates().)
diff --git a/src/backend/optimizer/util/var.c b/src/backend/optimizer/util/var.c
index 2521ffb2b6c..568e024d20b 100644
--- a/src/backend/optimizer/util/var.c
+++ b/src/backend/optimizer/util/var.c
@@ -8,15 +8,18 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.33 2001/10/25 05:49:34 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.34 2002/03/12 00:51:51 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
+#include "nodes/makefuncs.h"
#include "nodes/plannodes.h"
#include "optimizer/clauses.h"
#include "optimizer/var.h"
+#include "parser/parsetree.h"
+#include "parser/parse_coerce.h"
typedef struct
@@ -38,6 +41,12 @@ typedef struct
bool includeUpperVars;
} pull_var_clause_context;
+typedef struct
+{
+ Query *root;
+ int expandRTI;
+} flatten_join_alias_vars_context;
+
static bool pull_varnos_walker(Node *node,
pull_varnos_context *context);
static bool contain_var_reference_walker(Node *node,
@@ -45,6 +54,10 @@ static bool contain_var_reference_walker(Node *node,
static bool contain_var_clause_walker(Node *node, void *context);
static bool pull_var_clause_walker(Node *node,
pull_var_clause_context *context);
+static Node *flatten_join_alias_vars_mutator(Node *node,
+ flatten_join_alias_vars_context *context);
+static Node *flatten_join_alias_var(Var *var, Query *root, int expandRTI);
+static Node *find_jointree_item(Node *jtnode, int rtindex);
/*
@@ -297,3 +310,363 @@ pull_var_clause_walker(Node *node, pull_var_clause_context *context)
return expression_tree_walker(node, pull_var_clause_walker,
(void *) context);
}
+
+
+/*
+ * flatten_join_alias_vars
+ * Whereever possible, replace Vars that reference JOIN outputs with
+ * references to the original relation variables instead. This allows
+ * quals involving such vars to be pushed down. Vars that cannot be
+ * simplified to non-join Vars are replaced by COALESCE expressions
+ * if they have varno = expandRTI, and are left as JOIN RTE references
+ * otherwise. (Pass expandRTI = 0 to prevent all COALESCE expansion.)
+ *
+ * Upper-level vars (with varlevelsup > 0) are ignored; normally there
+ * should not be any by the time this routine is called.
+ *
+ * Does not examine subqueries, therefore must only be used after reduction
+ * of sublinks to subplans!
+ */
+Node *
+flatten_join_alias_vars(Node *node, Query *root, int expandRTI)
+{
+ flatten_join_alias_vars_context context;
+
+ context.root = root;
+ context.expandRTI = expandRTI;
+
+ return flatten_join_alias_vars_mutator(node, &context);
+}
+
+static Node *
+flatten_join_alias_vars_mutator(Node *node,
+ flatten_join_alias_vars_context *context)
+{
+ if (node == NULL)
+ return NULL;
+ if (IsA(node, Var))
+ {
+ Var *var = (Var *) node;
+
+ if (var->varlevelsup != 0)
+ return node; /* no need to copy, really */
+ return flatten_join_alias_var(var, context->root, context->expandRTI);
+ }
+ return expression_tree_mutator(node, flatten_join_alias_vars_mutator,
+ (void *) context);
+}
+
+static Node *
+flatten_join_alias_var(Var *var, Query *root, int expandRTI)
+{
+ Index varno = var->varno;
+ AttrNumber varattno = var->varattno;
+ Oid vartype = var->vartype;
+ int32 vartypmod = var->vartypmod;
+ JoinExpr *jexpr = NULL;
+
+ /*
+ * Loop to cope with joins of joins
+ */
+ for (;;)
+ {
+ RangeTblEntry *rte = rt_fetch(varno, root->rtable);
+ Index leftrti,
+ rightrti;
+ AttrNumber leftattno,
+ rightattno;
+ RangeTblEntry *subrte;
+ Oid subtype;
+ int32 subtypmod;
+
+ if (rte->rtekind != RTE_JOIN)
+ break; /* reached a non-join RTE */
+ /*
+ * Find the RT indexes of the left and right children of the
+ * join node. We have to search the join tree to do this,
+ * which is a major pain in the neck --- but keeping RT indexes
+ * in other RT entries is worse, because it makes modifying
+ * querytrees difficult. (Perhaps we can improve on the
+ * rangetable/jointree datastructure someday.) One thing we
+ * can do is avoid repeated searches while tracing a single
+ * variable down to its baserel.
+ */
+ if (jexpr == NULL)
+ jexpr = (JoinExpr *)
+ find_jointree_item((Node *) root->jointree, varno);
+ if (jexpr == NULL ||
+ !IsA(jexpr, JoinExpr) ||
+ jexpr->rtindex != varno)
+ elog(ERROR, "flatten_join_alias_var: failed to find JoinExpr");
+ if (IsA(jexpr->larg, RangeTblRef))
+ leftrti = ((RangeTblRef *) jexpr->larg)->rtindex;
+ else if (IsA(jexpr->larg, JoinExpr))
+ leftrti = ((JoinExpr *) jexpr->larg)->rtindex;
+ else
+ {
+ elog(ERROR, "flatten_join_alias_var: unexpected subtree type");
+ leftrti = 0; /* keep compiler quiet */
+ }
+ if (IsA(jexpr->rarg, RangeTblRef))
+ rightrti = ((RangeTblRef *) jexpr->rarg)->rtindex;
+ else if (IsA(jexpr->rarg, JoinExpr))
+ rightrti = ((JoinExpr *) jexpr->rarg)->rtindex;
+ else
+ {
+ elog(ERROR, "flatten_join_alias_var: unexpected subtree type");
+ rightrti = 0; /* keep compiler quiet */
+ }
+ /*
+ * See if the join var is from the left side, the right side,
+ * or both (ie, it is a USING/NATURAL JOIN merger column).
+ */
+ Assert(varattno > 0);
+ leftattno = (AttrNumber) nthi(varattno-1, rte->joinleftcols);
+ rightattno = (AttrNumber) nthi(varattno-1, rte->joinrightcols);
+ if (leftattno && rightattno)
+ {
+ /*
+ * Var is a merge var. If a left or right join, we can replace
+ * it by the left or right input var respectively; we only need
+ * a COALESCE for a full join. However, beware of the possibility
+ * that there's been a type promotion to make the input vars
+ * compatible; do not replace a var by one of a different type!
+ */
+ if (rte->jointype == JOIN_INNER ||
+ rte->jointype == JOIN_LEFT)
+ {
+ subrte = rt_fetch(leftrti, root->rtable);
+ get_rte_attribute_type(subrte, leftattno,
+ &subtype, &subtypmod);
+ if (vartype == subtype && vartypmod == subtypmod)
+ {
+ varno = leftrti;
+ varattno = leftattno;
+ jexpr = (JoinExpr *) jexpr->larg;
+ continue;
+ }
+ }
+ if (rte->jointype == JOIN_INNER ||
+ rte->jointype == JOIN_RIGHT)
+ {
+ subrte = rt_fetch(rightrti, root->rtable);
+ get_rte_attribute_type(subrte, rightattno,
+ &subtype, &subtypmod);
+ if (vartype == subtype && vartypmod == subtypmod)
+ {
+ varno = rightrti;
+ varattno = rightattno;
+ jexpr = (JoinExpr *) jexpr->rarg;
+ continue;
+ }
+ }
+ /*
+ * This var cannot be substituted directly, only with a COALESCE.
+ * Do so only if it belongs to the particular join indicated by
+ * the caller.
+ */
+ if (varno != expandRTI)
+ break;
+ {
+ Node *l_var,
+ *r_var;
+ CaseExpr *c = makeNode(CaseExpr);
+ CaseWhen *w = makeNode(CaseWhen);
+ NullTest *n = makeNode(NullTest);
+
+ subrte = rt_fetch(leftrti, root->rtable);
+ get_rte_attribute_type(subrte, leftattno,
+ &subtype, &subtypmod);
+ l_var = (Node *) makeVar(leftrti,
+ leftattno,
+ subtype,
+ subtypmod,
+ 0);
+ if (subtype != vartype)
+ {
+ l_var = coerce_type(NULL, l_var, subtype,
+ vartype, vartypmod);
+ l_var = coerce_type_typmod(NULL, l_var,
+ vartype, vartypmod);
+ }
+ else if (subtypmod != vartypmod)
+ l_var = coerce_type_typmod(NULL, l_var,
+ vartype, vartypmod);
+
+ subrte = rt_fetch(rightrti, root->rtable);
+ get_rte_attribute_type(subrte, rightattno,
+ &subtype, &subtypmod);
+ r_var = (Node *) makeVar(rightrti,
+ rightattno,
+ subtype,
+ subtypmod,
+ 0);
+ if (subtype != vartype)
+ {
+ r_var = coerce_type(NULL, r_var, subtype,
+ vartype, vartypmod);
+ r_var = coerce_type_typmod(NULL, r_var,
+ vartype, vartypmod);
+ }
+ else if (subtypmod != vartypmod)
+ r_var = coerce_type_typmod(NULL, r_var,
+ vartype, vartypmod);
+
+ n->arg = l_var;
+ n->nulltesttype = IS_NOT_NULL;
+ w->expr = (Node *) n;
+ w->result = l_var;
+ c->casetype = vartype;
+ c->args = makeList1(w);
+ c->defresult = r_var;
+ return (Node *) c;
+ }
+ }
+ else if (leftattno)
+ {
+ /* Here we do not need to check the type */
+ varno = leftrti;
+ varattno = leftattno;
+ jexpr = (JoinExpr *) jexpr->larg;
+ }
+ else
+ {
+ Assert(rightattno);
+ /* Here we do not need to check the type */
+ varno = rightrti;
+ varattno = rightattno;
+ jexpr = (JoinExpr *) jexpr->rarg;
+ }
+ }
+
+ /*
+ * When we fall out of the loop, we've reached the base Var.
+ */
+ return (Node *) makeVar(varno,
+ varattno,
+ vartype,
+ vartypmod,
+ 0);
+}
+
+/*
+ * Given a join alias Var, construct Vars for the two input vars it directly
+ * depends on. Note that this should *only* be called for merger alias Vars.
+ * In practice it is only used for Vars that got past flatten_join_alias_vars.
+ */
+void
+build_join_alias_subvars(Query *root, Var *aliasvar,
+ Var **leftsubvar, Var **rightsubvar)
+{
+ Index varno = aliasvar->varno;
+ AttrNumber varattno = aliasvar->varattno;
+ RangeTblEntry *rte;
+ JoinExpr *jexpr;
+ Index leftrti,
+ rightrti;
+ AttrNumber leftattno,
+ rightattno;
+ RangeTblEntry *subrte;
+ Oid subtype;
+ int32 subtypmod;
+
+ Assert(aliasvar->varlevelsup == 0);
+ rte = rt_fetch(varno, root->rtable);
+ Assert(rte->rtekind == RTE_JOIN);
+
+ /*
+ * Find the RT indexes of the left and right children of the
+ * join node.
+ */
+ jexpr = (JoinExpr *) find_jointree_item((Node *) root->jointree, varno);
+ if (jexpr == NULL ||
+ !IsA(jexpr, JoinExpr) ||
+ jexpr->rtindex != varno)
+ elog(ERROR, "build_join_alias_subvars: failed to find JoinExpr");
+ if (IsA(jexpr->larg, RangeTblRef))
+ leftrti = ((RangeTblRef *) jexpr->larg)->rtindex;
+ else if (IsA(jexpr->larg, JoinExpr))
+ leftrti = ((JoinExpr *) jexpr->larg)->rtindex;
+ else
+ {
+ elog(ERROR, "build_join_alias_subvars: unexpected subtree type");
+ leftrti = 0; /* keep compiler quiet */
+ }
+ if (IsA(jexpr->rarg, RangeTblRef))
+ rightrti = ((RangeTblRef *) jexpr->rarg)->rtindex;
+ else if (IsA(jexpr->rarg, JoinExpr))
+ rightrti = ((JoinExpr *) jexpr->rarg)->rtindex;
+ else
+ {
+ elog(ERROR, "build_join_alias_subvars: unexpected subtree type");
+ rightrti = 0; /* keep compiler quiet */
+ }
+
+ Assert(varattno > 0);
+ leftattno = (AttrNumber) nthi(varattno-1, rte->joinleftcols);
+ rightattno = (AttrNumber) nthi(varattno-1, rte->joinrightcols);
+ if (!(leftattno && rightattno))
+ elog(ERROR, "build_join_alias_subvars: non-merger variable");
+
+ subrte = rt_fetch(leftrti, root->rtable);
+ get_rte_attribute_type(subrte, leftattno,
+ &subtype, &subtypmod);
+ *leftsubvar = makeVar(leftrti,
+ leftattno,
+ subtype,
+ subtypmod,
+ 0);
+
+ subrte = rt_fetch(rightrti, root->rtable);
+ get_rte_attribute_type(subrte, rightattno,
+ &subtype, &subtypmod);
+ *rightsubvar = makeVar(rightrti,
+ rightattno,
+ subtype,
+ subtypmod,
+ 0);
+}
+
+/*
+ * Find jointree item matching the specified RT index
+ */
+static Node *
+find_jointree_item(Node *jtnode, int rtindex)
+{
+ if (jtnode == NULL)
+ return NULL;
+ if (IsA(jtnode, RangeTblRef))
+ {
+ if (((RangeTblRef *) jtnode)->rtindex == rtindex)
+ return jtnode;
+ }
+ else if (IsA(jtnode, FromExpr))
+ {
+ FromExpr *f = (FromExpr *) jtnode;
+ List *l;
+
+ foreach(l, f->fromlist)
+ {
+ jtnode = find_jointree_item(lfirst(l), rtindex);
+ if (jtnode)
+ return jtnode;
+ }
+ }
+ else if (IsA(jtnode, JoinExpr))
+ {
+ JoinExpr *j = (JoinExpr *) jtnode;
+
+ if (j->rtindex == rtindex)
+ return jtnode;
+ jtnode = find_jointree_item(j->larg, rtindex);
+ if (jtnode)
+ return jtnode;
+ jtnode = find_jointree_item(j->rarg, rtindex);
+ if (jtnode)
+ return jtnode;
+ }
+ else
+ elog(ERROR, "find_jointree_item: unexpected node type %d",
+ nodeTag(jtnode));
+ return NULL;
+}