aboutsummaryrefslogtreecommitdiff
path: root/src/backend/optimizer/plan/setrefs.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2007-02-22 22:00:26 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2007-02-22 22:00:26 +0000
commiteab6b8b27eba54b8fd0ad3f64989d12200979c96 (patch)
treeca4fad45c65c60b7900df6e373bd5999993ea2b1 /src/backend/optimizer/plan/setrefs.c
parent849000c7823d604270094aea76fc530b6cfe9466 (diff)
downloadpostgresql-eab6b8b27eba54b8fd0ad3f64989d12200979c96.tar.gz
postgresql-eab6b8b27eba54b8fd0ad3f64989d12200979c96.zip
Turn the rangetable used by the executor into a flat list, and avoid storing
useless substructure for its RangeTblEntry nodes. (I chose to keep using the same struct node type and just zero out the link fields for unneeded info, rather than making a separate ExecRangeTblEntry type --- it seemed too fragile to have two different rangetable representations.) Along the way, put subplans into a list in the toplevel PlannedStmt node, and have SubPlan nodes refer to them by list index instead of direct pointers. Vadim wanted to do that years ago, but I never understood what he was on about until now. It makes things a *whole* lot more robust, because we can stop worrying about duplicate processing of subplans during expression tree traversals. That's been a constant source of bugs, and it's finally gone. There are some consequent simplifications yet to be made, like not using a separate EState for subplans in the executor, but I'll tackle that later.
Diffstat (limited to 'src/backend/optimizer/plan/setrefs.c')
-rw-r--r--src/backend/optimizer/plan/setrefs.c1044
1 files changed, 511 insertions, 533 deletions
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 3d9f5486bcc..aaa383742e7 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.130 2007/02/19 02:23:12 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.131 2007/02/22 22:00:24 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -42,46 +42,58 @@ typedef struct
typedef struct
{
+ int rtoffset;
+} fix_scan_expr_context;
+
+typedef struct
+{
indexed_tlist *outer_itlist;
indexed_tlist *inner_itlist;
Index acceptable_rel;
-} join_references_context;
+ int rtoffset;
+} fix_join_expr_context;
typedef struct
{
indexed_tlist *subplan_itlist;
Index subvarno;
-} replace_vars_with_subplan_refs_context;
+ int rtoffset;
+} fix_upper_expr_context;
+
+#define fix_scan_list(lst, rtoffset) \
+ ((List *) fix_scan_expr((Node *) (lst), rtoffset))
-static Plan *set_subqueryscan_references(SubqueryScan *plan, List *rtable);
+static Plan *set_plan_refs(PlannerGlobal *glob, Plan *plan, int rtoffset);
+static Plan *set_subqueryscan_references(PlannerGlobal *glob,
+ SubqueryScan *plan,
+ int rtoffset);
static bool trivial_subqueryscan(SubqueryScan *plan);
-static void adjust_plan_varnos(Plan *plan, int rtoffset);
-static void adjust_expr_varnos(Node *node, int rtoffset);
-static bool adjust_expr_varnos_walker(Node *node, int *context);
-static void fix_expr_references(Plan *plan, Node *node);
-static bool fix_expr_references_walker(Node *node, void *context);
-static void set_join_references(Join *join);
+static Node *fix_scan_expr(Node *node, int rtoffset);
+static Node *fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context);
+static void set_join_references(Join *join, int rtoffset);
static void set_inner_join_references(Plan *inner_plan,
indexed_tlist *outer_itlist);
-static void set_uppernode_references(Plan *plan, Index subvarno);
+static void set_upper_references(Plan *plan, Index subvarno, int rtoffset);
static indexed_tlist *build_tlist_index(List *tlist);
static Var *search_indexed_tlist_for_var(Var *var,
indexed_tlist *itlist,
- Index newvarno);
+ Index newvarno,
+ int rtoffset);
static Var *search_indexed_tlist_for_non_var(Node *node,
indexed_tlist *itlist,
Index newvarno);
-static List *join_references(List *clauses,
- indexed_tlist *outer_itlist,
- indexed_tlist *inner_itlist,
- Index acceptable_rel);
-static Node *join_references_mutator(Node *node,
- join_references_context *context);
-static Node *replace_vars_with_subplan_refs(Node *node,
- indexed_tlist *subplan_itlist,
- Index subvarno);
-static Node *replace_vars_with_subplan_refs_mutator(Node *node,
- replace_vars_with_subplan_refs_context *context);
+static List *fix_join_expr(List *clauses,
+ indexed_tlist *outer_itlist,
+ indexed_tlist *inner_itlist,
+ Index acceptable_rel, int rtoffset);
+static Node *fix_join_expr_mutator(Node *node,
+ fix_join_expr_context *context);
+static Node *fix_upper_expr(Node *node,
+ indexed_tlist *subplan_itlist,
+ Index subvarno,
+ int rtoffset);
+static Node *fix_upper_expr_mutator(Node *node,
+ fix_upper_expr_context *context);
static bool fix_opfuncids_walker(Node *node, void *context);
@@ -96,34 +108,87 @@ static bool fix_opfuncids_walker(Node *node, void *context);
*
* This is the final processing pass of the planner/optimizer. The plan
* tree is complete; we just have to adjust some representational details
- * for the convenience of the executor. We update Vars in upper plan nodes
- * to refer to the outputs of their subplans, and we compute regproc OIDs
- * for operators (ie, we look up the function that implements each op).
+ * for the convenience of the executor:
+ *
+ * 1. We flatten the various subquery rangetables into a single list, and
+ * zero out RangeTblEntry fields that are not useful to the executor.
+ *
+ * 2. We adjust Vars in scan nodes to be consistent with the flat rangetable.
+ *
+ * 3. We adjust Vars in upper plan nodes to refer to the outputs of their
+ * subplans.
+ *
+ * 4. We compute regproc OIDs for operators (ie, we look up the function
+ * that implements each op).
*
* We also perform one final optimization step, which is to delete
* SubqueryScan plan nodes that aren't doing anything useful (ie, have
* no qual and a no-op targetlist). The reason for doing this last is that
* it can't readily be done before set_plan_references, because it would
- * break set_uppernode_references: the Vars in the subquery's top tlist
- * won't match up with the Vars in the outer plan tree. The SubqueryScan
+ * break set_upper_references: the Vars in the subquery's top tlist
+ * wouldn't match up with the Vars in the outer plan tree. The SubqueryScan
* serves a necessary function as a buffer between outer query and subquery
- * variable numbering ... but the executor doesn't care about that, only the
- * planner.
+ * variable numbering ... but after we've flattened the rangetable this is
+ * no longer a problem, since there's only one rtindex namespace.
*
* set_plan_references recursively traverses the whole plan tree.
*
+ * Inputs:
+ * glob: global data for planner run
+ * plan: the topmost node of the plan
+ * rtable: the rangetable for the current subquery
+ *
* The return value is normally the same Plan node passed in, but can be
* different when the passed-in Plan is a SubqueryScan we decide isn't needed.
*
- * Note: to delete a SubqueryScan, we have to renumber Vars in its child nodes
- * and append the modified subquery rangetable to the outer rangetable.
- * Therefore "rtable" is an in/out argument and really should be declared
- * "List **". But in the interest of notational simplicity we don't do that.
- * (Since rtable can't be NIL if there's a SubqueryScan, the list header
- * address won't change when we append a subquery rangetable.)
+ * The flattened rangetable entries are appended to glob->finalrtable.
+ *
+ * Notice that we modify Plan nodes in-place, but use expression_tree_mutator
+ * to process targetlist and qual expressions. We can assume that the Plan
+ * nodes were just built by the planner and are not multiply referenced, but
+ * it's not so safe to assume that for expression tree nodes.
*/
Plan *
-set_plan_references(Plan *plan, List *rtable)
+set_plan_references(PlannerGlobal *glob, Plan *plan, List *rtable)
+{
+ int rtoffset = list_length(glob->finalrtable);
+ ListCell *lc;
+
+ /*
+ * In the flat rangetable, we zero out substructure pointers that are
+ * not needed by the executor; this reduces the storage space and
+ * copying cost for cached plans.
+ */
+ foreach(lc, rtable)
+ {
+ RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
+ RangeTblEntry *newrte;
+
+ /* flat copy to duplicate all the scalar fields */
+ newrte = (RangeTblEntry *) palloc(sizeof(RangeTblEntry));
+ memcpy(newrte, rte, sizeof(RangeTblEntry));
+
+ /* zap unneeded sub-structure (we keep only the eref Alias) */
+ newrte->subquery = NULL;
+ newrte->funcexpr = NULL;
+ newrte->funccoltypes = NIL;
+ newrte->funccoltypmods = NIL;
+ newrte->values_lists = NIL;
+ newrte->joinaliasvars = NIL;
+ newrte->alias = NULL;
+
+ glob->finalrtable = lappend(glob->finalrtable, newrte);
+ }
+
+ /* Now fix the Plan tree */
+ return set_plan_refs(glob, plan, rtoffset);
+}
+
+/*
+ * set_plan_refs: recurse through the Plan nodes of a single subquery level
+ */
+static Plan *
+set_plan_refs(PlannerGlobal *glob, Plan *plan, int rtoffset)
{
ListCell *l;
@@ -136,73 +201,109 @@ set_plan_references(Plan *plan, List *rtable)
switch (nodeTag(plan))
{
case T_SeqScan:
- fix_expr_references(plan, (Node *) plan->targetlist);
- fix_expr_references(plan, (Node *) plan->qual);
+ {
+ SeqScan *splan = (SeqScan *) plan;
+
+ splan->scanrelid += rtoffset;
+ splan->plan.targetlist =
+ fix_scan_list(splan->plan.targetlist, rtoffset);
+ splan->plan.qual =
+ fix_scan_list(splan->plan.qual, rtoffset);
+ }
break;
case T_IndexScan:
- fix_expr_references(plan, (Node *) plan->targetlist);
- fix_expr_references(plan, (Node *) plan->qual);
- fix_expr_references(plan,
- (Node *) ((IndexScan *) plan)->indexqual);
- fix_expr_references(plan,
- (Node *) ((IndexScan *) plan)->indexqualorig);
+ {
+ IndexScan *splan = (IndexScan *) plan;
+
+ splan->scan.scanrelid += rtoffset;
+ splan->scan.plan.targetlist =
+ fix_scan_list(splan->scan.plan.targetlist, rtoffset);
+ splan->scan.plan.qual =
+ fix_scan_list(splan->scan.plan.qual, rtoffset);
+ splan->indexqual =
+ fix_scan_list(splan->indexqual, rtoffset);
+ splan->indexqualorig =
+ fix_scan_list(splan->indexqualorig, rtoffset);
+ }
break;
case T_BitmapIndexScan:
- /* no need to fix targetlist and qual */
- Assert(plan->targetlist == NIL);
- Assert(plan->qual == NIL);
- fix_expr_references(plan,
- (Node *) ((BitmapIndexScan *) plan)->indexqual);
- fix_expr_references(plan,
- (Node *) ((BitmapIndexScan *) plan)->indexqualorig);
+ {
+ BitmapIndexScan *splan = (BitmapIndexScan *) plan;
+
+ splan->scan.scanrelid += rtoffset;
+ /* no need to fix targetlist and qual */
+ Assert(splan->scan.plan.targetlist == NIL);
+ Assert(splan->scan.plan.qual == NIL);
+ splan->indexqual =
+ fix_scan_list(splan->indexqual, rtoffset);
+ splan->indexqualorig =
+ fix_scan_list(splan->indexqualorig, rtoffset);
+ }
break;
case T_BitmapHeapScan:
- fix_expr_references(plan, (Node *) plan->targetlist);
- fix_expr_references(plan, (Node *) plan->qual);
- fix_expr_references(plan,
- (Node *) ((BitmapHeapScan *) plan)->bitmapqualorig);
+ {
+ BitmapHeapScan *splan = (BitmapHeapScan *) plan;
+
+ splan->scan.scanrelid += rtoffset;
+ splan->scan.plan.targetlist =
+ fix_scan_list(splan->scan.plan.targetlist, rtoffset);
+ splan->scan.plan.qual =
+ fix_scan_list(splan->scan.plan.qual, rtoffset);
+ splan->bitmapqualorig =
+ fix_scan_list(splan->bitmapqualorig, rtoffset);
+ }
break;
case T_TidScan:
- fix_expr_references(plan, (Node *) plan->targetlist);
- fix_expr_references(plan, (Node *) plan->qual);
- fix_expr_references(plan, (Node *) ((TidScan *) plan)->tidquals);
+ {
+ TidScan *splan = (TidScan *) plan;
+
+ splan->scan.scanrelid += rtoffset;
+ splan->scan.plan.targetlist =
+ fix_scan_list(splan->scan.plan.targetlist, rtoffset);
+ splan->scan.plan.qual =
+ fix_scan_list(splan->scan.plan.qual, rtoffset);
+ splan->tidquals =
+ fix_scan_list(splan->tidquals, rtoffset);
+ }
break;
case T_SubqueryScan:
/* Needs special treatment, see comments below */
- return set_subqueryscan_references((SubqueryScan *) plan, rtable);
+ return set_subqueryscan_references(glob,
+ (SubqueryScan *) plan,
+ rtoffset);
case T_FunctionScan:
- fix_expr_references(plan, (Node *) plan->targetlist);
- fix_expr_references(plan, (Node *) plan->qual);
- fix_expr_references(plan, ((FunctionScan *) plan)->funcexpr);
+ {
+ FunctionScan *splan = (FunctionScan *) plan;
+
+ splan->scan.scanrelid += rtoffset;
+ splan->scan.plan.targetlist =
+ fix_scan_list(splan->scan.plan.targetlist, rtoffset);
+ splan->scan.plan.qual =
+ fix_scan_list(splan->scan.plan.qual, rtoffset);
+ splan->funcexpr =
+ fix_scan_expr(splan->funcexpr, rtoffset);
+ }
break;
case T_ValuesScan:
- fix_expr_references(plan, (Node *) plan->targetlist);
- fix_expr_references(plan, (Node *) plan->qual);
- fix_expr_references(plan,
- (Node *) ((ValuesScan *) plan)->values_lists);
+ {
+ ValuesScan *splan = (ValuesScan *) plan;
+
+ splan->scan.scanrelid += rtoffset;
+ splan->scan.plan.targetlist =
+ fix_scan_list(splan->scan.plan.targetlist, rtoffset);
+ splan->scan.plan.qual =
+ fix_scan_list(splan->scan.plan.qual, rtoffset);
+ splan->values_lists =
+ fix_scan_list(splan->values_lists, rtoffset);
+ }
break;
+
case T_NestLoop:
- set_join_references((Join *) plan);
- fix_expr_references(plan, (Node *) plan->targetlist);
- fix_expr_references(plan, (Node *) plan->qual);
- fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual);
- break;
case T_MergeJoin:
- set_join_references((Join *) plan);
- fix_expr_references(plan, (Node *) plan->targetlist);
- fix_expr_references(plan, (Node *) plan->qual);
- fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual);
- fix_expr_references(plan,
- (Node *) ((MergeJoin *) plan)->mergeclauses);
- break;
case T_HashJoin:
- set_join_references((Join *) plan);
- fix_expr_references(plan, (Node *) plan->targetlist);
- fix_expr_references(plan, (Node *) plan->qual);
- fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual);
- fix_expr_references(plan,
- (Node *) ((HashJoin *) plan)->hashclauses);
+ set_join_references((Join *) plan, rtoffset);
break;
+
case T_Hash:
case T_Material:
case T_Sort:
@@ -211,75 +312,113 @@ set_plan_references(Plan *plan, List *rtable)
/*
* These plan types don't actually bother to evaluate their
- * targetlists (because they just return their unmodified input
- * tuples). The optimizer is lazy about creating really valid
- * targetlists for them --- it tends to just put in a pointer to
- * the child plan node's tlist. Hence, we leave the tlist alone.
- * In particular, we do not want to process subplans in the tlist,
- * since we will likely end up reprocessing subplans that also
- * appear in lower levels of the plan tree!
- *
+ * targetlists, because they just return their unmodified input
+ * tuples. Even though the targetlist won't be used by the
+ * executor, we fix it up for possible use by EXPLAIN (not to
+ * mention ease of debugging --- wrong varnos are very confusing).
+ */
+ plan->targetlist =
+ fix_scan_list(plan->targetlist, rtoffset);
+ /*
* Since these plan types don't check quals either, we should not
* find any qual expression attached to them.
*/
Assert(plan->qual == NIL);
break;
case T_Limit:
-
- /*
- * Like the plan types above, Limit doesn't evaluate its tlist or
- * quals. It does have live expressions for limit/offset,
- * however.
- */
- Assert(plan->qual == NIL);
- fix_expr_references(plan, ((Limit *) plan)->limitOffset);
- fix_expr_references(plan, ((Limit *) plan)->limitCount);
+ {
+ Limit *splan = (Limit *) plan;
+
+ /*
+ * Like the plan types above, Limit doesn't evaluate its tlist
+ * or quals. It does have live expressions for limit/offset,
+ * however.
+ */
+ splan->plan.targetlist =
+ fix_scan_list(splan->plan.targetlist, rtoffset);
+ Assert(splan->plan.qual == NIL);
+ splan->limitOffset =
+ fix_scan_expr(splan->limitOffset, rtoffset);
+ splan->limitCount =
+ fix_scan_expr(splan->limitCount, rtoffset);
+ }
break;
case T_Agg:
case T_Group:
- set_uppernode_references(plan, (Index) 0);
- fix_expr_references(plan, (Node *) plan->targetlist);
- fix_expr_references(plan, (Node *) plan->qual);
+ set_upper_references(plan, (Index) 0, rtoffset);
break;
case T_Result:
-
- /*
- * Result may or may not have a subplan; no need to fix up subplan
- * references if it hasn't got one...
- *
- * XXX why does Result use a different subvarno from Agg/Group?
- */
- if (plan->lefttree != NULL)
- set_uppernode_references(plan, (Index) OUTER);
- fix_expr_references(plan, (Node *) plan->targetlist);
- fix_expr_references(plan, (Node *) plan->qual);
- fix_expr_references(plan, ((Result *) plan)->resconstantqual);
+ {
+ Result *splan = (Result *) plan;
+
+ /*
+ * Result may or may not have a subplan; if not, it's more
+ * like a scan node than an upper node.
+ *
+ * XXX why does Result use a different subvarno from Agg/Group?
+ */
+ if (splan->plan.lefttree != NULL)
+ set_upper_references(plan, (Index) OUTER, rtoffset);
+ else
+ {
+ splan->plan.targetlist =
+ fix_scan_list(splan->plan.targetlist, rtoffset);
+ splan->plan.qual =
+ fix_scan_list(splan->plan.qual, rtoffset);
+ }
+ /* resconstantqual can't contain any subplan variable refs */
+ splan->resconstantqual =
+ fix_scan_expr(splan->resconstantqual, rtoffset);
+ }
break;
case T_Append:
-
- /*
- * Append, like Sort et al, doesn't actually evaluate its
- * targetlist or check quals, and we haven't bothered to give it
- * its own tlist copy. So, don't fix targetlist/qual. But do
- * recurse into child plans.
- */
- Assert(plan->qual == NIL);
- foreach(l, ((Append *) plan)->appendplans)
- lfirst(l) = set_plan_references((Plan *) lfirst(l), rtable);
+ {
+ Append *splan = (Append *) plan;
+
+ /*
+ * Append, like Sort et al, doesn't actually evaluate its
+ * targetlist or check quals.
+ */
+ splan->plan.targetlist =
+ fix_scan_list(splan->plan.targetlist, rtoffset);
+ Assert(splan->plan.qual == NIL);
+ foreach(l, splan->appendplans)
+ {
+ lfirst(l) = set_plan_refs(glob,
+ (Plan *) lfirst(l),
+ rtoffset);
+ }
+ }
break;
case T_BitmapAnd:
- /* BitmapAnd works like Append, but has no tlist */
- Assert(plan->targetlist == NIL);
- Assert(plan->qual == NIL);
- foreach(l, ((BitmapAnd *) plan)->bitmapplans)
- lfirst(l) = set_plan_references((Plan *) lfirst(l), rtable);
+ {
+ BitmapAnd *splan = (BitmapAnd *) plan;
+
+ /* BitmapAnd works like Append, but has no tlist */
+ Assert(splan->plan.targetlist == NIL);
+ Assert(splan->plan.qual == NIL);
+ foreach(l, splan->bitmapplans)
+ {
+ lfirst(l) = set_plan_refs(glob,
+ (Plan *) lfirst(l),
+ rtoffset);
+ }
+ }
break;
case T_BitmapOr:
- /* BitmapOr works like Append, but has no tlist */
- Assert(plan->targetlist == NIL);
- Assert(plan->qual == NIL);
- foreach(l, ((BitmapOr *) plan)->bitmapplans)
- lfirst(l) = set_plan_references((Plan *) lfirst(l), rtable);
+ {
+ BitmapOr *splan = (BitmapOr *) plan;
+
+ /* BitmapOr works like Append, but has no tlist */
+ Assert(splan->plan.targetlist == NIL);
+ Assert(splan->plan.qual == NIL);
+ foreach(l, splan->bitmapplans)
+ {
+ lfirst(l) = set_plan_refs(glob,
+ (Plan *) lfirst(l),
+ rtoffset);
+ }
+ }
break;
default:
elog(ERROR, "unrecognized node type: %d",
@@ -288,25 +427,15 @@ set_plan_references(Plan *plan, List *rtable)
}
/*
- * Now recurse into child plans and initplans, if any
+ * Now recurse into child plans, if any
*
* NOTE: it is essential that we recurse into child plans AFTER we set
* subplan references in this plan's tlist and quals. If we did the
* reference-adjustments bottom-up, then we would fail to match this
* plan's var nodes against the already-modified nodes of the children.
- * Fortunately, that consideration doesn't apply to SubPlan nodes; else
- * we'd need two passes over the expression trees.
*/
- plan->lefttree = set_plan_references(plan->lefttree, rtable);
- plan->righttree = set_plan_references(plan->righttree, rtable);
-
- foreach(l, plan->initPlan)
- {
- SubPlan *sp = (SubPlan *) lfirst(l);
-
- Assert(IsA(sp, SubPlan));
- sp->plan = set_plan_references(sp->plan, sp->rtable);
- }
+ plan->lefttree = set_plan_refs(glob, plan->lefttree, rtoffset);
+ plan->righttree = set_plan_refs(glob, plan->righttree, rtoffset);
return plan;
}
@@ -319,54 +448,29 @@ set_plan_references(Plan *plan, List *rtable)
* to do the normal processing on it.
*/
static Plan *
-set_subqueryscan_references(SubqueryScan *plan, List *rtable)
+set_subqueryscan_references(PlannerGlobal *glob,
+ SubqueryScan *plan,
+ int rtoffset)
{
Plan *result;
- RangeTblEntry *rte;
- ListCell *l;
/* First, recursively process the subplan */
- rte = rt_fetch(plan->scan.scanrelid, rtable);
- Assert(rte->rtekind == RTE_SUBQUERY);
- plan->subplan = set_plan_references(plan->subplan,
- rte->subquery->rtable);
-
- /*
- * We have to process any initplans too; set_plan_references can't do it
- * for us because of the possibility of double-processing.
- */
- foreach(l, plan->scan.plan.initPlan)
- {
- SubPlan *sp = (SubPlan *) lfirst(l);
+ plan->subplan = set_plan_references(glob, plan->subplan, plan->subrtable);
- Assert(IsA(sp, SubPlan));
- sp->plan = set_plan_references(sp->plan, sp->rtable);
- }
+ /* subrtable is no longer needed in the plan tree */
+ plan->subrtable = NIL;
if (trivial_subqueryscan(plan))
{
/*
- * We can omit the SubqueryScan node and just pull up the subplan. We
- * have to merge its rtable into the outer rtable, which means
- * adjusting varnos throughout the subtree.
+ * We can omit the SubqueryScan node and just pull up the subplan.
*/
- int rtoffset = list_length(rtable);
- List *sub_rtable;
ListCell *lp,
*lc;
- sub_rtable = copyObject(rte->subquery->rtable);
- rtable = list_concat(rtable, sub_rtable);
-
- /*
- * we have to copy the subplan to make sure there are no duplicately
- * linked nodes in it, else adjust_plan_varnos might increment some
- * varnos twice
- */
- result = copyObject(plan->subplan);
-
- adjust_plan_varnos(result, rtoffset);
+ result = plan->subplan;
+ /* We have to be sure we don't lose any initplans */
result->initPlan = list_concat(plan->scan.plan.initPlan,
result->initPlan);
@@ -391,14 +495,17 @@ set_subqueryscan_references(SubqueryScan *plan, List *rtable)
/*
* Keep the SubqueryScan node. We have to do the processing that
* set_plan_references would otherwise have done on it. Notice we do
- * not do set_uppernode_references() here, because a SubqueryScan will
+ * not do set_upper_references() here, because a SubqueryScan will
* always have been created with correct references to its subplan's
* outputs to begin with.
*/
- result = (Plan *) plan;
+ plan->scan.scanrelid += rtoffset;
+ plan->scan.plan.targetlist =
+ fix_scan_list(plan->scan.plan.targetlist, rtoffset);
+ plan->scan.plan.qual =
+ fix_scan_list(plan->scan.plan.qual, rtoffset);
- fix_expr_references(result, (Node *) result->targetlist);
- fix_expr_references(result, (Node *) result->qual);
+ result = (Plan *) plan;
}
return result;
@@ -463,261 +570,72 @@ trivial_subqueryscan(SubqueryScan *plan)
}
/*
- * adjust_plan_varnos
- * Offset varnos and other rangetable indexes in a plan tree by rtoffset.
- */
-static void
-adjust_plan_varnos(Plan *plan, int rtoffset)
-{
- ListCell *l;
-
- if (plan == NULL)
- return;
-
- /*
- * Plan-type-specific fixes
- */
- switch (nodeTag(plan))
- {
- case T_SeqScan:
- ((SeqScan *) plan)->scanrelid += rtoffset;
- adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
- adjust_expr_varnos((Node *) plan->qual, rtoffset);
- break;
- case T_IndexScan:
- ((IndexScan *) plan)->scan.scanrelid += rtoffset;
- adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
- adjust_expr_varnos((Node *) plan->qual, rtoffset);
- adjust_expr_varnos((Node *) ((IndexScan *) plan)->indexqual,
- rtoffset);
- adjust_expr_varnos((Node *) ((IndexScan *) plan)->indexqualorig,
- rtoffset);
- break;
- case T_BitmapIndexScan:
- ((BitmapIndexScan *) plan)->scan.scanrelid += rtoffset;
- /* no need to fix targetlist and qual */
- Assert(plan->targetlist == NIL);
- Assert(plan->qual == NIL);
- adjust_expr_varnos((Node *) ((BitmapIndexScan *) plan)->indexqual,
- rtoffset);
- adjust_expr_varnos((Node *) ((BitmapIndexScan *) plan)->indexqualorig,
- rtoffset);
- break;
- case T_BitmapHeapScan:
- ((BitmapHeapScan *) plan)->scan.scanrelid += rtoffset;
- adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
- adjust_expr_varnos((Node *) plan->qual, rtoffset);
- adjust_expr_varnos((Node *) ((BitmapHeapScan *) plan)->bitmapqualorig,
- rtoffset);
- break;
- case T_TidScan:
- ((TidScan *) plan)->scan.scanrelid += rtoffset;
- adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
- adjust_expr_varnos((Node *) plan->qual, rtoffset);
- adjust_expr_varnos((Node *) ((TidScan *) plan)->tidquals,
- rtoffset);
- break;
- case T_SubqueryScan:
- ((SubqueryScan *) plan)->scan.scanrelid += rtoffset;
- adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
- adjust_expr_varnos((Node *) plan->qual, rtoffset);
- /* we should not recurse into the subquery! */
- break;
- case T_FunctionScan:
- ((FunctionScan *) plan)->scan.scanrelid += rtoffset;
- adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
- adjust_expr_varnos((Node *) plan->qual, rtoffset);
- adjust_expr_varnos(((FunctionScan *) plan)->funcexpr,
- rtoffset);
- break;
- case T_ValuesScan:
- ((ValuesScan *) plan)->scan.scanrelid += rtoffset;
- adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
- adjust_expr_varnos((Node *) plan->qual, rtoffset);
- adjust_expr_varnos((Node *) ((ValuesScan *) plan)->values_lists,
- rtoffset);
- break;
- case T_NestLoop:
- adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
- adjust_expr_varnos((Node *) plan->qual, rtoffset);
- adjust_expr_varnos((Node *) ((Join *) plan)->joinqual, rtoffset);
- break;
- case T_MergeJoin:
- adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
- adjust_expr_varnos((Node *) plan->qual, rtoffset);
- adjust_expr_varnos((Node *) ((Join *) plan)->joinqual, rtoffset);
- adjust_expr_varnos((Node *) ((MergeJoin *) plan)->mergeclauses,
- rtoffset);
- break;
- case T_HashJoin:
- adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
- adjust_expr_varnos((Node *) plan->qual, rtoffset);
- adjust_expr_varnos((Node *) ((Join *) plan)->joinqual, rtoffset);
- adjust_expr_varnos((Node *) ((HashJoin *) plan)->hashclauses,
- rtoffset);
- break;
- case T_Hash:
- case T_Material:
- case T_Sort:
- case T_Unique:
- case T_SetOp:
-
- /*
- * Even though the targetlist won't be used by the executor, we
- * fix it up for possible use by EXPLAIN (not to mention ease of
- * debugging --- wrong varnos are very confusing).
- */
- adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
- Assert(plan->qual == NIL);
- break;
- case T_Limit:
-
- /*
- * Like the plan types above, Limit doesn't evaluate its tlist or
- * quals. It does have live expressions for limit/offset,
- * however.
- */
- adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
- Assert(plan->qual == NIL);
- adjust_expr_varnos(((Limit *) plan)->limitOffset, rtoffset);
- adjust_expr_varnos(((Limit *) plan)->limitCount, rtoffset);
- break;
- case T_Agg:
- case T_Group:
- adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
- adjust_expr_varnos((Node *) plan->qual, rtoffset);
- break;
- case T_Result:
- adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
- adjust_expr_varnos((Node *) plan->qual, rtoffset);
- adjust_expr_varnos(((Result *) plan)->resconstantqual, rtoffset);
- break;
- case T_Append:
- adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
- Assert(plan->qual == NIL);
- foreach(l, ((Append *) plan)->appendplans)
- adjust_plan_varnos((Plan *) lfirst(l), rtoffset);
- break;
- case T_BitmapAnd:
- /* BitmapAnd works like Append, but has no tlist */
- Assert(plan->targetlist == NIL);
- Assert(plan->qual == NIL);
- foreach(l, ((BitmapAnd *) plan)->bitmapplans)
- adjust_plan_varnos((Plan *) lfirst(l), rtoffset);
- break;
- case T_BitmapOr:
- /* BitmapOr works like Append, but has no tlist */
- Assert(plan->targetlist == NIL);
- Assert(plan->qual == NIL);
- foreach(l, ((BitmapOr *) plan)->bitmapplans)
- adjust_plan_varnos((Plan *) lfirst(l), rtoffset);
- break;
- default:
- elog(ERROR, "unrecognized node type: %d",
- (int) nodeTag(plan));
- break;
- }
-
- /*
- * Now recurse into child plans.
- *
- * We don't need to (and in fact mustn't) recurse into subqueries, so no
- * need to examine initPlan list.
- */
- adjust_plan_varnos(plan->lefttree, rtoffset);
- adjust_plan_varnos(plan->righttree, rtoffset);
-}
-
-/*
- * adjust_expr_varnos
- * Offset varnos of Vars in an expression by rtoffset.
+ * fix_scan_expr
+ * Do set_plan_references processing on a scan-level expression
*
- * This is different from the rewriter's OffsetVarNodes in that it has to
- * work on an already-planned expression tree; in particular, we should not
- * disturb INNER and OUTER references. On the other hand, we don't have to
- * recurse into subqueries nor deal with outer-level Vars, so it's pretty
- * simple.
+ * This consists of incrementing all Vars' varnos by rtoffset and
+ * looking up operator opcode info for OpExpr and related nodes.
*/
-static void
-adjust_expr_varnos(Node *node, int rtoffset)
+static Node *
+fix_scan_expr(Node *node, int rtoffset)
{
- /* This tree walk requires no special setup, so away we go... */
- adjust_expr_varnos_walker(node, &rtoffset);
+ fix_scan_expr_context context;
+
+ context.rtoffset = rtoffset;
+ return fix_scan_expr_mutator(node, &context);
}
-static bool
-adjust_expr_varnos_walker(Node *node, int *context)
+static Node *
+fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context)
{
if (node == NULL)
- return false;
+ return NULL;
if (IsA(node, Var))
{
- Var *var = (Var *) node;
+ Var *var = (Var *) copyObject(node);
Assert(var->varlevelsup == 0);
- if (var->varno > 0 && var->varno != INNER && var->varno != OUTER)
- var->varno += *context;
+ /*
+ * We should not see any Vars marked INNER, but in a nestloop inner
+ * scan there could be OUTER Vars. Leave them alone.
+ */
+ Assert(var->varno != INNER);
+ if (var->varno > 0 && var->varno != OUTER)
+ var->varno += context->rtoffset;
if (var->varnoold > 0)
- var->varnoold += *context;
- return false;
+ var->varnoold += context->rtoffset;
+ return (Node *) var;
}
- return expression_tree_walker(node, adjust_expr_varnos_walker,
- (void *) context);
-}
-
-/*
- * fix_expr_references
- * Do final cleanup on expressions (targetlists or quals).
- *
- * This consists of looking up operator opcode info for OpExpr nodes
- * and recursively performing set_plan_references on subplans.
- *
- * The Plan argument is currently unused, but might be needed again someday.
- */
-static void
-fix_expr_references(Plan *plan, Node *node)
-{
- /* This tree walk requires no special setup, so away we go... */
- fix_expr_references_walker(node, NULL);
-}
-
-static bool
-fix_expr_references_walker(Node *node, void *context)
-{
- if (node == NULL)
- return false;
+ /*
+ * Since we update opcode info in-place, this part could possibly
+ * scribble on the planner's input data structures, but it's OK.
+ */
if (IsA(node, OpExpr))
set_opfuncid((OpExpr *) node);
else if (IsA(node, DistinctExpr))
set_opfuncid((OpExpr *) node); /* rely on struct equivalence */
- else if (IsA(node, ScalarArrayOpExpr))
- set_sa_opfuncid((ScalarArrayOpExpr *) node);
else if (IsA(node, NullIfExpr))
set_opfuncid((OpExpr *) node); /* rely on struct equivalence */
- else if (IsA(node, SubPlan))
- {
- SubPlan *sp = (SubPlan *) node;
-
- sp->plan = set_plan_references(sp->plan, sp->rtable);
- }
- return expression_tree_walker(node, fix_expr_references_walker, context);
+ else if (IsA(node, ScalarArrayOpExpr))
+ set_sa_opfuncid((ScalarArrayOpExpr *) node);
+ return expression_tree_mutator(node, fix_scan_expr_mutator,
+ (void *) context);
}
/*
* set_join_references
- * Modifies the target list and quals of a join node to reference its
+ * Modify the target list and quals of a join node to reference its
* subplans, by setting the varnos to OUTER or INNER and setting attno
* values to the result domain number of either the corresponding outer
- * or inner join tuple item.
+ * or inner join tuple item. Also perform opcode lookup for these
+ * expressions.
*
* In the case of a nestloop with inner indexscan, we will also need to
* apply the same transformation to any outer vars appearing in the
* quals of the child indexscan. set_inner_join_references does that.
- *
- * 'join' is a join plan node
*/
static void
-set_join_references(Join *join)
+set_join_references(Join *join, int rtoffset)
{
Plan *outer_plan = join->plan.lefttree;
Plan *inner_plan = join->plan.righttree;
@@ -728,43 +646,47 @@ set_join_references(Join *join)
inner_itlist = build_tlist_index(inner_plan->targetlist);
/* All join plans have tlist, qual, and joinqual */
- join->plan.targetlist = join_references(join->plan.targetlist,
- outer_itlist,
- inner_itlist,
- (Index) 0);
- join->plan.qual = join_references(join->plan.qual,
- outer_itlist,
- inner_itlist,
- (Index) 0);
- join->joinqual = join_references(join->joinqual,
- outer_itlist,
- inner_itlist,
- (Index) 0);
+ join->plan.targetlist = fix_join_expr(join->plan.targetlist,
+ outer_itlist,
+ inner_itlist,
+ (Index) 0,
+ rtoffset);
+ join->plan.qual = fix_join_expr(join->plan.qual,
+ outer_itlist,
+ inner_itlist,
+ (Index) 0,
+ rtoffset);
+ join->joinqual = fix_join_expr(join->joinqual,
+ outer_itlist,
+ inner_itlist,
+ (Index) 0,
+ rtoffset);
/* Now do join-type-specific stuff */
if (IsA(join, NestLoop))
{
/* This processing is split out to handle possible recursion */
- set_inner_join_references(inner_plan,
- outer_itlist);
+ set_inner_join_references(inner_plan, outer_itlist);
}
else if (IsA(join, MergeJoin))
{
MergeJoin *mj = (MergeJoin *) join;
- mj->mergeclauses = join_references(mj->mergeclauses,
- outer_itlist,
- inner_itlist,
- (Index) 0);
+ mj->mergeclauses = fix_join_expr(mj->mergeclauses,
+ outer_itlist,
+ inner_itlist,
+ (Index) 0,
+ rtoffset);
}
else if (IsA(join, HashJoin))
{
HashJoin *hj = (HashJoin *) join;
- hj->hashclauses = join_references(hj->hashclauses,
- outer_itlist,
- inner_itlist,
- (Index) 0);
+ hj->hashclauses = fix_join_expr(hj->hashclauses,
+ outer_itlist,
+ inner_itlist,
+ (Index) 0,
+ rtoffset);
}
pfree(outer_itlist);
@@ -779,6 +701,10 @@ set_join_references(Join *join)
* to the bottom BitmapIndexScan nodes; likewise, appendrel indexscans
* require recursing through Append nodes. This is split out as a separate
* function so that it can recurse.
+ *
+ * Note we do *not* apply any rtoffset for non-join Vars; this is because
+ * the quals will be processed again by fix_scan_expr when the set_plan_refs
+ * recursion reaches the inner indexscan, and so we'd have done it twice.
*/
static void
set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist)
@@ -800,14 +726,16 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist)
Index innerrel = innerscan->scan.scanrelid;
/* only refs to outer vars get changed in the inner qual */
- innerscan->indexqualorig = join_references(indexqualorig,
- outer_itlist,
- NULL,
- innerrel);
- innerscan->indexqual = join_references(innerscan->indexqual,
- outer_itlist,
- NULL,
- innerrel);
+ innerscan->indexqualorig = fix_join_expr(indexqualorig,
+ outer_itlist,
+ NULL,
+ innerrel,
+ 0);
+ innerscan->indexqual = fix_join_expr(innerscan->indexqual,
+ outer_itlist,
+ NULL,
+ innerrel,
+ 0);
/*
* We must fix the inner qpqual too, if it has join clauses (this
@@ -815,10 +743,11 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist)
* may get rechecked as qpquals).
*/
if (NumRelids((Node *) inner_plan->qual) > 1)
- inner_plan->qual = join_references(inner_plan->qual,
- outer_itlist,
- NULL,
- innerrel);
+ inner_plan->qual = fix_join_expr(inner_plan->qual,
+ outer_itlist,
+ NULL,
+ innerrel,
+ 0);
}
}
else if (IsA(inner_plan, BitmapIndexScan))
@@ -835,14 +764,16 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist)
Index innerrel = innerscan->scan.scanrelid;
/* only refs to outer vars get changed in the inner qual */
- innerscan->indexqualorig = join_references(indexqualorig,
- outer_itlist,
- NULL,
- innerrel);
- innerscan->indexqual = join_references(innerscan->indexqual,
- outer_itlist,
- NULL,
- innerrel);
+ innerscan->indexqualorig = fix_join_expr(indexqualorig,
+ outer_itlist,
+ NULL,
+ innerrel,
+ 0);
+ innerscan->indexqual = fix_join_expr(innerscan->indexqual,
+ outer_itlist,
+ NULL,
+ innerrel,
+ 0);
/* no need to fix inner qpqual */
Assert(inner_plan->qual == NIL);
}
@@ -862,10 +793,11 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist)
/* only refs to outer vars get changed in the inner qual */
if (NumRelids((Node *) bitmapqualorig) > 1)
- innerscan->bitmapqualorig = join_references(bitmapqualorig,
- outer_itlist,
- NULL,
- innerrel);
+ innerscan->bitmapqualorig = fix_join_expr(bitmapqualorig,
+ outer_itlist,
+ NULL,
+ innerrel,
+ 0);
/*
* We must fix the inner qpqual too, if it has join clauses (this
@@ -873,14 +805,14 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist)
* get rechecked as qpquals).
*/
if (NumRelids((Node *) inner_plan->qual) > 1)
- inner_plan->qual = join_references(inner_plan->qual,
- outer_itlist,
- NULL,
- innerrel);
+ inner_plan->qual = fix_join_expr(inner_plan->qual,
+ outer_itlist,
+ NULL,
+ innerrel,
+ 0);
/* Now recurse */
- set_inner_join_references(inner_plan->lefttree,
- outer_itlist);
+ set_inner_join_references(inner_plan->lefttree, outer_itlist);
}
else if (IsA(inner_plan, BitmapAnd))
{
@@ -890,8 +822,7 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist)
foreach(l, innerscan->bitmapplans)
{
- set_inner_join_references((Plan *) lfirst(l),
- outer_itlist);
+ set_inner_join_references((Plan *) lfirst(l), outer_itlist);
}
}
else if (IsA(inner_plan, BitmapOr))
@@ -902,10 +833,20 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist)
foreach(l, innerscan->bitmapplans)
{
- set_inner_join_references((Plan *) lfirst(l),
- outer_itlist);
+ set_inner_join_references((Plan *) lfirst(l), outer_itlist);
}
}
+ else if (IsA(inner_plan, TidScan))
+ {
+ TidScan *innerscan = (TidScan *) inner_plan;
+ Index innerrel = innerscan->scan.scanrelid;
+
+ innerscan->tidquals = fix_join_expr(innerscan->tidquals,
+ outer_itlist,
+ NULL,
+ innerrel,
+ 0);
+ }
else if (IsA(inner_plan, Append))
{
/*
@@ -917,8 +858,7 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist)
foreach(l, appendplan->appendplans)
{
- set_inner_join_references((Plan *) lfirst(l),
- outer_itlist);
+ set_inner_join_references((Plan *) lfirst(l), outer_itlist);
}
}
else if (IsA(inner_plan, Result))
@@ -929,22 +869,13 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist)
if (result->plan.lefttree)
set_inner_join_references(result->plan.lefttree, outer_itlist);
}
- else if (IsA(inner_plan, TidScan))
- {
- TidScan *innerscan = (TidScan *) inner_plan;
- Index innerrel = innerscan->scan.scanrelid;
-
- innerscan->tidquals = join_references(innerscan->tidquals,
- outer_itlist,
- NULL,
- innerrel);
- }
}
/*
- * set_uppernode_references
+ * set_upper_references
* Update the targetlist and quals of an upper-level plan node
* to refer to the tuples returned by its lefttree subplan.
+ * Also perform opcode lookup for these expressions.
*
* This is used for single-input plan types like Agg, Group, Result.
*
@@ -958,7 +889,7 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist)
* the expression.
*/
static void
-set_uppernode_references(Plan *plan, Index subvarno)
+set_upper_references(Plan *plan, Index subvarno, int rtoffset)
{
Plan *subplan = plan->lefttree;
indexed_tlist *subplan_itlist;
@@ -976,9 +907,10 @@ set_uppernode_references(Plan *plan, Index subvarno)
TargetEntry *tle = (TargetEntry *) lfirst(l);
Node *newexpr;
- newexpr = replace_vars_with_subplan_refs((Node *) tle->expr,
- subplan_itlist,
- subvarno);
+ newexpr = fix_upper_expr((Node *) tle->expr,
+ subplan_itlist,
+ subvarno,
+ rtoffset);
tle = flatCopyTargetEntry(tle);
tle->expr = (Expr *) newexpr;
output_targetlist = lappend(output_targetlist, tle);
@@ -986,9 +918,10 @@ set_uppernode_references(Plan *plan, Index subvarno)
plan->targetlist = output_targetlist;
plan->qual = (List *)
- replace_vars_with_subplan_refs((Node *) plan->qual,
- subplan_itlist,
- subvarno);
+ fix_upper_expr((Node *) plan->qual,
+ subplan_itlist,
+ subvarno,
+ rtoffset);
pfree(subplan_itlist);
}
@@ -1096,10 +1029,12 @@ build_tlist_index_other_vars(List *tlist, Index ignore_rel)
*
* If a match is found, return a copy of the given Var with suitably
* modified varno/varattno (to wit, newvarno and the resno of the TLE entry).
+ * Also ensure that varnoold is incremented by rtoffset.
* If no match, return NULL.
*/
static Var *
-search_indexed_tlist_for_var(Var *var, indexed_tlist *itlist, Index newvarno)
+search_indexed_tlist_for_var(Var *var, indexed_tlist *itlist,
+ Index newvarno, int rtoffset)
{
Index varno = var->varno;
AttrNumber varattno = var->varattno;
@@ -1117,6 +1052,8 @@ search_indexed_tlist_for_var(Var *var, indexed_tlist *itlist, Index newvarno)
newvar->varno = newvarno;
newvar->varattno = vinfo->resno;
+ if (newvar->varnoold > 0)
+ newvar->varnoold += rtoffset;
return newvar;
}
vinfo++;
@@ -1157,22 +1094,23 @@ search_indexed_tlist_for_non_var(Node *node,
}
/*
- * join_references
- * Creates a new set of targetlist entries or join qual clauses by
+ * fix_join_expr
+ * Create a new set of targetlist entries or join qual clauses by
* changing the varno/varattno values of variables in the clauses
* to reference target list values from the outer and inner join
- * relation target lists.
+ * relation target lists. Also perform opcode lookup.
*
* This is used in two different scenarios: a normal join clause, where
* all the Vars in the clause *must* be replaced by OUTER or INNER references;
* and an indexscan being used on the inner side of a nestloop join.
* In the latter case we want to replace the outer-relation Vars by OUTER
- * references, but not touch the Vars of the inner relation. (We also
- * implement RETURNING clause fixup using this second scenario.)
+ * references, while Vars of the inner relation should be adjusted by rtoffset.
+ * (We also implement RETURNING clause fixup using this second scenario.)
*
* For a normal join, acceptable_rel should be zero so that any failure to
* match a Var will be reported as an error. For the indexscan case,
- * pass inner_itlist = NULL and acceptable_rel = the ID of the inner relation.
+ * pass inner_itlist = NULL and acceptable_rel = the (not-offseted-yet) ID
+ * of the inner relation.
*
* 'clauses' is the targetlist or list of join clauses
* 'outer_itlist' is the indexed target list of the outer join relation
@@ -1180,27 +1118,29 @@ search_indexed_tlist_for_non_var(Node *node,
* or NULL
* 'acceptable_rel' is either zero or the rangetable index of a relation
* whose Vars may appear in the clause without provoking an error.
+ * 'rtoffset' is what to add to varno for Vars of acceptable_rel.
*
* Returns the new expression tree. The original clause structure is
* not modified.
*/
static List *
-join_references(List *clauses,
- indexed_tlist *outer_itlist,
- indexed_tlist *inner_itlist,
- Index acceptable_rel)
+fix_join_expr(List *clauses,
+ indexed_tlist *outer_itlist,
+ indexed_tlist *inner_itlist,
+ Index acceptable_rel,
+ int rtoffset)
{
- join_references_context context;
+ fix_join_expr_context context;
context.outer_itlist = outer_itlist;
context.inner_itlist = inner_itlist;
context.acceptable_rel = acceptable_rel;
- return (List *) join_references_mutator((Node *) clauses, &context);
+ context.rtoffset = rtoffset;
+ return (List *) fix_join_expr_mutator((Node *) clauses, &context);
}
static Node *
-join_references_mutator(Node *node,
- join_references_context *context)
+fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
{
Var *newvar;
@@ -1213,21 +1153,28 @@ join_references_mutator(Node *node,
/* First look for the var in the input tlists */
newvar = search_indexed_tlist_for_var(var,
context->outer_itlist,
- OUTER);
+ OUTER,
+ context->rtoffset);
if (newvar)
return (Node *) newvar;
if (context->inner_itlist)
{
newvar = search_indexed_tlist_for_var(var,
context->inner_itlist,
- INNER);
+ INNER,
+ context->rtoffset);
if (newvar)
return (Node *) newvar;
}
- /* Return the Var unmodified, if it's for acceptable_rel */
+ /* If it's for acceptable_rel, adjust and return it */
if (var->varno == context->acceptable_rel)
- return (Node *) copyObject(var);
+ {
+ var = (Var *) copyObject(var);
+ var->varno += context->rtoffset;
+ var->varnoold += context->rtoffset;
+ return (Node *) var;
+ }
/* No referent found for Var */
elog(ERROR, "variable not found in subplan target lists");
@@ -1249,16 +1196,30 @@ join_references_mutator(Node *node,
if (newvar)
return (Node *) newvar;
}
+ /*
+ * Since we update opcode info in-place, this part could possibly
+ * scribble on the planner's input data structures, but it's OK.
+ */
+ if (IsA(node, OpExpr))
+ set_opfuncid((OpExpr *) node);
+ else if (IsA(node, DistinctExpr))
+ set_opfuncid((OpExpr *) node); /* rely on struct equivalence */
+ else if (IsA(node, NullIfExpr))
+ set_opfuncid((OpExpr *) node); /* rely on struct equivalence */
+ else if (IsA(node, ScalarArrayOpExpr))
+ set_sa_opfuncid((ScalarArrayOpExpr *) node);
return expression_tree_mutator(node,
- join_references_mutator,
+ fix_join_expr_mutator,
(void *) context);
}
/*
- * replace_vars_with_subplan_refs
- * This routine modifies an expression tree so that all Var nodes
- * reference target nodes of a subplan. It is used to fix up
- * target and qual expressions of non-join upper-level plan nodes.
+ * fix_upper_expr
+ * Modifies an expression tree so that all Var nodes reference outputs
+ * of a subplan. Also performs opcode lookup.
+ *
+ * This is used to fix up target and qual expressions of non-join upper-level
+ * plan nodes.
*
* An error is raised if no matching var can be found in the subplan tlist
* --- so this routine should only be applied to nodes whose subplans'
@@ -1273,26 +1234,28 @@ join_references_mutator(Node *node,
* 'node': the tree to be fixed (a target item or qual)
* 'subplan_itlist': indexed target list for subplan
* 'subvarno': varno to be assigned to all Vars
+ * 'rtoffset': how much to increment varnoold by
*
* The resulting tree is a copy of the original in which all Var nodes have
* varno = subvarno, varattno = resno of corresponding subplan target.
* The original tree is not modified.
*/
static Node *
-replace_vars_with_subplan_refs(Node *node,
- indexed_tlist *subplan_itlist,
- Index subvarno)
+fix_upper_expr(Node *node,
+ indexed_tlist *subplan_itlist,
+ Index subvarno,
+ int rtoffset)
{
- replace_vars_with_subplan_refs_context context;
+ fix_upper_expr_context context;
context.subplan_itlist = subplan_itlist;
context.subvarno = subvarno;
- return replace_vars_with_subplan_refs_mutator(node, &context);
+ context.rtoffset = rtoffset;
+ return fix_upper_expr_mutator(node, &context);
}
static Node *
-replace_vars_with_subplan_refs_mutator(Node *node,
- replace_vars_with_subplan_refs_context *context)
+fix_upper_expr_mutator(Node *node, fix_upper_expr_context *context)
{
Var *newvar;
@@ -1304,7 +1267,8 @@ replace_vars_with_subplan_refs_mutator(Node *node,
newvar = search_indexed_tlist_for_var(var,
context->subplan_itlist,
- context->subvarno);
+ context->subvarno,
+ context->rtoffset);
if (!newvar)
elog(ERROR, "variable not found in subplan target list");
return (Node *) newvar;
@@ -1318,8 +1282,20 @@ replace_vars_with_subplan_refs_mutator(Node *node,
if (newvar)
return (Node *) newvar;
}
+ /*
+ * Since we update opcode info in-place, this part could possibly
+ * scribble on the planner's input data structures, but it's OK.
+ */
+ if (IsA(node, OpExpr))
+ set_opfuncid((OpExpr *) node);
+ else if (IsA(node, DistinctExpr))
+ set_opfuncid((OpExpr *) node); /* rely on struct equivalence */
+ else if (IsA(node, NullIfExpr))
+ set_opfuncid((OpExpr *) node); /* rely on struct equivalence */
+ else if (IsA(node, ScalarArrayOpExpr))
+ set_sa_opfuncid((ScalarArrayOpExpr *) node);
return expression_tree_mutator(node,
- replace_vars_with_subplan_refs_mutator,
+ fix_upper_expr_mutator,
(void *) context);
}
@@ -1335,12 +1311,15 @@ replace_vars_with_subplan_refs_mutator(Node *node,
* adjusted RETURNING list, result-table Vars will still have their
* original varno, but Vars for other rels will have varno OUTER.
*
- * We also must apply fix_expr_references to the list.
+ * We also must perform opcode lookup.
*
* 'rlist': the RETURNING targetlist to be fixed
* 'topplan': the top Plan node for the query (not yet passed through
* set_plan_references)
- * 'resultRelation': RT index of the query's result relation
+ * 'resultRelation': RT index of the associated result relation
+ *
+ * Note: we assume that result relations will have rtoffset zero, that is,
+ * they are not coming from a subplan.
*/
List *
set_returning_clause_references(List *rlist,
@@ -1350,20 +1329,19 @@ set_returning_clause_references(List *rlist,
indexed_tlist *itlist;
/*
- * We can perform the desired Var fixup by abusing the join_references
+ * We can perform the desired Var fixup by abusing the fix_join_expr
* machinery that normally handles inner indexscan fixup. We search the
* top plan's targetlist for Vars of non-result relations, and use
- * join_references to convert RETURNING Vars into references to those
+ * fix_join_expr to convert RETURNING Vars into references to those
* tlist entries, while leaving result-rel Vars as-is.
*/
itlist = build_tlist_index_other_vars(topplan->targetlist, resultRelation);
- rlist = join_references(rlist,
- itlist,
- NULL,
- resultRelation);
-
- fix_expr_references(topplan, (Node *) rlist);
+ rlist = fix_join_expr(rlist,
+ itlist,
+ NULL,
+ resultRelation,
+ 0);
pfree(itlist);
@@ -1399,10 +1377,10 @@ fix_opfuncids_walker(Node *node, void *context)
set_opfuncid((OpExpr *) node);
else if (IsA(node, DistinctExpr))
set_opfuncid((OpExpr *) node); /* rely on struct equivalence */
- else if (IsA(node, ScalarArrayOpExpr))
- set_sa_opfuncid((ScalarArrayOpExpr *) node);
else if (IsA(node, NullIfExpr))
set_opfuncid((OpExpr *) node); /* rely on struct equivalence */
+ else if (IsA(node, ScalarArrayOpExpr))
+ set_sa_opfuncid((ScalarArrayOpExpr *) node);
return expression_tree_walker(node, fix_opfuncids_walker, context);
}