+ /*
+ * 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.
+ */
+ plan->lefttree = set_plan_refs(glob, plan->lefttree, rtoffset);
+ plan->righttree = set_plan_refs(glob, plan->righttree, rtoffset);
+
+ return plan;
+}
+
+/*
+ * set_subqueryscan_references
+ * Do set_plan_references processing on a SubqueryScan
+ *
+ * We try to strip out the SubqueryScan entirely; if we can't, we have
+ * to do the normal processing on it.
+ */
+static Plan *
+set_subqueryscan_references(PlannerGlobal *glob,
+ SubqueryScan *plan,
+ int rtoffset)
+{
+ Plan *result;
+
+ /* First, recursively process the subplan */
+ plan->subplan = set_plan_references(glob, plan->subplan,
+ plan->subrtable, plan->subrowmark);
+
+ /* subrtable/subrowmark are no longer needed in the plan tree */
+ plan->subrtable = NIL;
+ plan->subrowmark = NIL;
+
+ if (trivial_subqueryscan(plan))
+ {
+ /*
+ * We can omit the SubqueryScan node and just pull up the subplan.
+ */
+ ListCell *lp,
+ *lc;
+
+ result = plan->subplan;
+
+ /* We have to be sure we don't lose any initplans */
+ result->initPlan = list_concat(plan->scan.plan.initPlan,
+ result->initPlan);
+
+ /*
+ * We also have to transfer the SubqueryScan's result-column names
+ * into the subplan, else columns sent to client will be improperly
+ * labeled if this is the topmost plan level. Copy the "source
+ * column" information too.
+ */
+ forboth(lp, plan->scan.plan.targetlist, lc, result->targetlist)
+ {
+ TargetEntry *ptle = (TargetEntry *) lfirst(lp);
+ TargetEntry *ctle = (TargetEntry *) lfirst(lc);
+
+ ctle->resname = ptle->resname;
+ ctle->resorigtbl = ptle->resorigtbl;
+ ctle->resorigcol = ptle->resorigcol;
+ }
+ }
+ else
+ {
+ /*
+ * 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_upper_references() here, because a SubqueryScan will
+ * always have been created with correct references to its subplan's
+ * outputs to begin with.
+ */
+ plan->scan.scanrelid += rtoffset;
+ plan->scan.plan.targetlist =
+ fix_scan_list(glob, plan->scan.plan.targetlist, rtoffset);
+ plan->scan.plan.qual =
+ fix_scan_list(glob, plan->scan.plan.qual, rtoffset);
+
+ result = (Plan *) plan;
+ }
+
+ return result;
+}
+
+/*
+ * trivial_subqueryscan
+ * Detect whether a SubqueryScan can be deleted from the plan tree.
+ *
+ * We can delete it if it has no qual to check and the targetlist just
+ * regurgitates the output of the child plan.
+ */
+static bool
+trivial_subqueryscan(SubqueryScan *plan)
+{
+ int attrno;
+ ListCell *lp,
+ *lc;
+
+ if (plan->scan.plan.qual != NIL)
+ return false;
+
+ if (list_length(plan->scan.plan.targetlist) !=
+ list_length(plan->subplan->targetlist))
+ return false; /* tlists not same length */
+
+ attrno = 1;
+ forboth(lp, plan->scan.plan.targetlist, lc, plan->subplan->targetlist)
+ {
+ TargetEntry *ptle = (TargetEntry *) lfirst(lp);
+ TargetEntry *ctle = (TargetEntry *) lfirst(lc);
+
+ if (ptle->resjunk != ctle->resjunk)
+ return false; /* tlist doesn't match junk status */
+
+ /*
+ * We accept either a Var referencing the corresponding element of the
+ * subplan tlist, or a Const equaling the subplan element. See
+ * generate_setop_tlist() for motivation.
+ */
+ if (ptle->expr && IsA(ptle->expr, Var))
+ {
+ Var *var = (Var *) ptle->expr;
+
+ Assert(var->varno == plan->scan.scanrelid);
+ Assert(var->varlevelsup == 0);
+ if (var->varattno != attrno)
+ return false; /* out of order */
+ }
+ else if (ptle->expr && IsA(ptle->expr, Const))
+ {
+ if (!equal(ptle->expr, ctle->expr))
+ return false;
+ }
+ else
+ return false;
+
+ attrno++;
+ }
+
+ return true;
+}
+
+/*
+ * copyVar
+ * Copy a Var node.
+ *
+ * fix_scan_expr and friends do this enough times that it's worth having
+ * a bespoke routine instead of using the generic copyObject() function.
+ */
+static inline Var *
+copyVar(Var *var)
+{
+ Var *newvar = (Var *) palloc(sizeof(Var));
+
+ *newvar = *var;
+ return newvar;
+}
+
+/*
+ * fix_expr_common
+ * Do generic set_plan_references processing on an expression node
+ *
+ * This is code that is common to all variants of expression-fixing.
+ * We must look up operator opcode info for OpExpr and related nodes,
+ * add OIDs from regclass Const nodes into glob->relationOids,
+ * and add catalog TIDs for user-defined functions into glob->invalItems.
+ *
+ * We assume it's okay to update opcode info in-place. So this could possibly
+ * scribble on the planner's input data structures, but it's OK.
+ */
+static void
+fix_expr_common(PlannerGlobal *glob, Node *node)
+{
+ /* We assume callers won't call us on a NULL pointer */
+ if (IsA(node, Aggref))
+ {
+ record_plan_function_dependency(glob,
+ ((Aggref *) node)->aggfnoid);
+ }
+ else if (IsA(node, WindowFunc))
+ {
+ record_plan_function_dependency(glob,
+ ((WindowFunc *) node)->winfnoid);
+ }
+ else if (IsA(node, FuncExpr))
+ {
+ record_plan_function_dependency(glob,
+ ((FuncExpr *) node)->funcid);
+ }
+ else if (IsA(node, OpExpr))
+ {
+ set_opfuncid((OpExpr *) node);
+ record_plan_function_dependency(glob,
+ ((OpExpr *) node)->opfuncid);
+ }
+ else if (IsA(node, DistinctExpr))
+ {
+ set_opfuncid((OpExpr *) node); /* rely on struct equivalence */
+ record_plan_function_dependency(glob,
+ ((DistinctExpr *) node)->opfuncid);
+ }
+ else if (IsA(node, NullIfExpr))
+ {
+ set_opfuncid((OpExpr *) node); /* rely on struct equivalence */
+ record_plan_function_dependency(glob,
+ ((NullIfExpr *) node)->opfuncid);
+ }
+ else if (IsA(node, ScalarArrayOpExpr))
+ {
+ set_sa_opfuncid((ScalarArrayOpExpr *) node);
+ record_plan_function_dependency(glob,
+ ((ScalarArrayOpExpr *) node)->opfuncid);
+ }
+ else if (IsA(node, ArrayCoerceExpr))
+ {
+ if (OidIsValid(((ArrayCoerceExpr *) node)->elemfuncid))
+ record_plan_function_dependency(glob,
+ ((ArrayCoerceExpr *) node)->elemfuncid);
+ }
+ else if (IsA(node, Const))
+ {
+ Const *con = (Const *) node;
+
+ /* Check for regclass reference */
+ if (ISREGCLASSCONST(con))
+ glob->relationOids =
+ lappend_oid(glob->relationOids,
+ DatumGetObjectId(con->constvalue));
+ }
+}
+
+/*
+ * fix_scan_expr
+ * Do set_plan_references processing on a scan-level expression
+ *
+ * This consists of incrementing all Vars' varnos by rtoffset,
+ * looking up operator opcode info for OpExpr and related nodes,
+ * and adding OIDs from regclass Const nodes into glob->relationOids.
+ */
+static Node *
+fix_scan_expr(PlannerGlobal *glob, Node *node, int rtoffset)
+{
+ fix_scan_expr_context context;
+
+ context.glob = glob;
+ context.rtoffset = rtoffset;
+
+ if (rtoffset != 0 || glob->lastPHId != 0)
+ {
+ return fix_scan_expr_mutator(node, &context);
+ }
+ else
+ {
+ /*
+ * If rtoffset == 0, we don't need to change any Vars, and if there
+ * are no placeholders anywhere we won't need to remove them. Then
+ * it's OK to just scribble on the input node tree instead of copying
+ * (since the only change, filling in any unset opfuncid fields, is
+ * harmless). This saves just enough cycles to be noticeable on
+ * trivial queries.
+ */
+ (void) fix_scan_expr_walker(node, &context);
+ return node;
+ }
+}
+
+static Node *
+fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context)
+{
+ if (node == NULL)
+ return NULL;
+ if (IsA(node, Var))
+ {
+ Var *var = copyVar((Var *) node);
+
+ Assert(var->varlevelsup == 0);
+
+ /*
+ * 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->rtoffset;
+ return (Node *) var;
+ }
+ if (IsA(node, CurrentOfExpr))
+ {
+ CurrentOfExpr *cexpr = (CurrentOfExpr *) copyObject(node);
+
+ Assert(cexpr->cvarno != INNER);
+ Assert(cexpr->cvarno != OUTER);
+ cexpr->cvarno += context->rtoffset;
+ return (Node *) cexpr;
+ }
+ if (IsA(node, PlaceHolderVar))
+ {
+ /* At scan level, we should always just evaluate the contained expr */
+ PlaceHolderVar *phv = (PlaceHolderVar *) node;
+
+ return fix_scan_expr_mutator((Node *) phv->phexpr, context);
+ }
+ fix_expr_common(context->glob, node);
+ return expression_tree_mutator(node, fix_scan_expr_mutator,
+ (void *) context);
+}
+
+static bool
+fix_scan_expr_walker(Node *node, fix_scan_expr_context *context)
+{
+ if (node == NULL)
+ return false;
+ Assert(!IsA(node, PlaceHolderVar));
+ fix_expr_common(context->glob, node);
+ return expression_tree_walker(node, fix_scan_expr_walker,
+ (void *) context);
+}
+
+/*
+ * set_join_references
+ * 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. Also perform opcode lookup for these
+ * expressions. and add regclass OIDs to glob->relationOids.
+ *
+ * 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.
+ */
+static void
+set_join_references(PlannerGlobal *glob, Join *join, int rtoffset)
+{
+ Plan *outer_plan = join->plan.lefttree;
+ Plan *inner_plan = join->plan.righttree;
+ indexed_tlist *outer_itlist;
+ indexed_tlist *inner_itlist;
+
+ outer_itlist = build_tlist_index(outer_plan->targetlist);
+ inner_itlist = build_tlist_index(inner_plan->targetlist);
+
+ /* All join plans have tlist, qual, and joinqual */
+ join->plan.targetlist = fix_join_expr(glob,
+ join->plan.targetlist,
+ outer_itlist,
+ inner_itlist,
+ (Index) 0,
+ rtoffset);
+ join->plan.qual = fix_join_expr(glob,
+ join->plan.qual,
+ outer_itlist,
+ inner_itlist,
+ (Index) 0,
+ rtoffset);
+ join->joinqual = fix_join_expr(glob,
+ 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(glob, inner_plan, outer_itlist);
+ }
+ else if (IsA(join, MergeJoin))
+ {
+ MergeJoin *mj = (MergeJoin *) join;
+
+ mj->mergeclauses = fix_join_expr(glob,
+ mj->mergeclauses,
+ outer_itlist,
+ inner_itlist,
+ (Index) 0,
+ rtoffset);
+ }
+ else if (IsA(join, HashJoin))
+ {
+ HashJoin *hj = (HashJoin *) join;
+
+ hj->hashclauses = fix_join_expr(glob,
+ hj->hashclauses,
+ outer_itlist,
+ inner_itlist,
+ (Index) 0,
+ rtoffset);
+ }
+
+ pfree(outer_itlist);
+ pfree(inner_itlist);
+}
+
+/*
+ * set_inner_join_references
+ * Handle join references appearing in an inner indexscan's quals
+ *
+ * To handle bitmap-scan plan trees, we have to be able to recurse down
+ * 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(PlannerGlobal *glob, Plan *inner_plan,
+ indexed_tlist *outer_itlist)
+{
+ if (IsA(inner_plan, IndexScan))
+ {
+ /*
+ * An index is being used to reduce the number of tuples scanned in
+ * the inner relation. If there are join clauses being used with the
+ * index, we must update their outer-rel var nodes to refer to the
+ * outer side of the join.
+ */
+ IndexScan *innerscan = (IndexScan *) inner_plan;
+ List *indexqualorig = innerscan->indexqualorig;
+
+ /* No work needed if indexqual refers only to its own rel... */
+ if (NumRelids((Node *) indexqualorig) > 1)
+ {
+ Index innerrel = innerscan->scan.scanrelid;
+
+ /* only refs to outer vars get changed in the inner qual */
+ innerscan->indexqualorig = fix_join_expr(glob,
+ indexqualorig,
+ outer_itlist,
+ NULL,
+ innerrel,
+ 0);
+ innerscan->indexqual = fix_join_expr(glob,
+ innerscan->indexqual,
+ outer_itlist,
+ NULL,
+ innerrel,
+ 0);
+
+ /*
+ * We must fix the inner qpqual too, if it has join clauses (this
+ * could happen if special operators are involved: some indexquals
+ * may get rechecked as qpquals).
+ */
+ if (NumRelids((Node *) inner_plan->qual) > 1)
+ inner_plan->qual = fix_join_expr(glob,
+ inner_plan->qual,
+ outer_itlist,
+ NULL,
+ innerrel,
+ 0);
+ }
+ }
+ else if (IsA(inner_plan, BitmapIndexScan))
+ {
+ /*
+ * Same, but index is being used within a bitmap plan.
+ */
+ BitmapIndexScan *innerscan = (BitmapIndexScan *) inner_plan;
+ List *indexqualorig = innerscan->indexqualorig;
+
+ /* No work needed if indexqual refers only to its own rel... */
+ if (NumRelids((Node *) indexqualorig) > 1)
+ {
+ Index innerrel = innerscan->scan.scanrelid;
+
+ /* only refs to outer vars get changed in the inner qual */
+ innerscan->indexqualorig = fix_join_expr(glob,
+ indexqualorig,
+ outer_itlist,
+ NULL,
+ innerrel,
+ 0);
+ innerscan->indexqual = fix_join_expr(glob,
+ innerscan->indexqual,
+ outer_itlist,
+ NULL,
+ innerrel,
+ 0);
+ /* no need to fix inner qpqual */
+ Assert(inner_plan->qual == NIL);
+ }
+ }
+ else if (IsA(inner_plan, BitmapHeapScan))
+ {
+ /*
+ * The inner side is a bitmap scan plan. Fix the top node, and
+ * recurse to get the lower nodes.
+ *
+ * Note: create_bitmap_scan_plan removes clauses from bitmapqualorig
+ * if they are duplicated in qpqual, so must test these independently.
+ */
+ BitmapHeapScan *innerscan = (BitmapHeapScan *) inner_plan;
+ Index innerrel = innerscan->scan.scanrelid;
+ List *bitmapqualorig = innerscan->bitmapqualorig;
+
+ /* only refs to outer vars get changed in the inner qual */
+ if (NumRelids((Node *) bitmapqualorig) > 1)
+ innerscan->bitmapqualorig = fix_join_expr(glob,
+ bitmapqualorig,
+ outer_itlist,
+ NULL,
+ innerrel,
+ 0);
+
+ /*
+ * We must fix the inner qpqual too, if it has join clauses (this
+ * could happen if special operators are involved: some indexquals may
+ * get rechecked as qpquals).
+ */
+ if (NumRelids((Node *) inner_plan->qual) > 1)
+ inner_plan->qual = fix_join_expr(glob,
+ inner_plan->qual,
+ outer_itlist,
+ NULL,
+ innerrel,
+ 0);
+
+ /* Now recurse */
+ set_inner_join_references(glob, inner_plan->lefttree, outer_itlist);
+ }
+ else if (IsA(inner_plan, BitmapAnd))
+ {
+ /* All we need do here is recurse */
+ BitmapAnd *innerscan = (BitmapAnd *) inner_plan;
+ ListCell *l;
+
+ foreach(l, innerscan->bitmapplans)
+ {
+ set_inner_join_references(glob, (Plan *) lfirst(l), outer_itlist);
+ }
+ }
+ else if (IsA(inner_plan, BitmapOr))