aboutsummaryrefslogtreecommitdiff
path: root/src/backend/optimizer/plan/setrefs.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer/plan/setrefs.c')
-rw-r--r--src/backend/optimizer/plan/setrefs.c234
1 files changed, 183 insertions, 51 deletions
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 85ba9d1ca1e..4348bfef601 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -30,11 +30,21 @@
#include "utils/syscache.h"
+typedef enum
+{
+ NRM_EQUAL, /* expect exact match of nullingrels */
+ NRM_SUBSET, /* actual Var may have a subset of input */
+ NRM_SUPERSET /* actual Var may have a superset of input */
+} NullingRelsMatch;
+
typedef struct
{
int varno; /* RT index of Var */
AttrNumber varattno; /* attr number of Var */
AttrNumber resno; /* TLE position of Var */
+#ifdef USE_ASSERT_CHECKING
+ Bitmapset *varnullingrels; /* Var's varnullingrels */
+#endif
} tlist_vinfo;
typedef struct
@@ -60,6 +70,7 @@ typedef struct
indexed_tlist *inner_itlist;
Index acceptable_rel;
int rtoffset;
+ NullingRelsMatch nrm_match;
double num_exec;
} fix_join_expr_context;
@@ -69,6 +80,7 @@ typedef struct
indexed_tlist *subplan_itlist;
int newvarno;
int rtoffset;
+ NullingRelsMatch nrm_match;
double num_exec;
} fix_upper_expr_context;
@@ -159,7 +171,12 @@ static indexed_tlist *build_tlist_index(List *tlist);
static Var *search_indexed_tlist_for_var(Var *var,
indexed_tlist *itlist,
int newvarno,
- int rtoffset);
+ int rtoffset,
+ NullingRelsMatch nrm_match);
+static Var *search_indexed_tlist_for_phv(PlaceHolderVar *phv,
+ indexed_tlist *itlist,
+ int newvarno,
+ NullingRelsMatch nrm_match);
static Var *search_indexed_tlist_for_non_var(Expr *node,
indexed_tlist *itlist,
int newvarno);
@@ -172,14 +189,18 @@ static List *fix_join_expr(PlannerInfo *root,
indexed_tlist *outer_itlist,
indexed_tlist *inner_itlist,
Index acceptable_rel,
- int rtoffset, double num_exec);
+ int rtoffset,
+ NullingRelsMatch nrm_match,
+ double num_exec);
static Node *fix_join_expr_mutator(Node *node,
fix_join_expr_context *context);
static Node *fix_upper_expr(PlannerInfo *root,
Node *node,
indexed_tlist *subplan_itlist,
int newvarno,
- int rtoffset, double num_exec);
+ int rtoffset,
+ NullingRelsMatch nrm_match,
+ double num_exec);
static Node *fix_upper_expr_mutator(Node *node,
fix_upper_expr_context *context);
static List *set_returning_clause_references(PlannerInfo *root,
@@ -1118,13 +1139,13 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
fix_join_expr(root, splan->onConflictSet,
NULL, itlist,
linitial_int(splan->resultRelations),
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NRM_EQUAL, NUM_EXEC_QUAL(plan));
splan->onConflictWhere = (Node *)
fix_join_expr(root, (List *) splan->onConflictWhere,
NULL, itlist,
linitial_int(splan->resultRelations),
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NRM_EQUAL, NUM_EXEC_QUAL(plan));
pfree(itlist);
@@ -1181,6 +1202,7 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
NULL, itlist,
resultrel,
rtoffset,
+ NRM_EQUAL,
NUM_EXEC_TLIST(plan));
/* Fix quals too. */
@@ -1189,6 +1211,7 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
NULL, itlist,
resultrel,
rtoffset,
+ NRM_EQUAL,
NUM_EXEC_QUAL(plan));
}
}
@@ -1334,6 +1357,7 @@ set_indexonlyscan_references(PlannerInfo *root,
index_itlist,
INDEX_VAR,
rtoffset,
+ NRM_EQUAL,
NUM_EXEC_TLIST((Plan *) plan));
plan->scan.plan.qual = (List *)
fix_upper_expr(root,
@@ -1341,6 +1365,7 @@ set_indexonlyscan_references(PlannerInfo *root,
index_itlist,
INDEX_VAR,
rtoffset,
+ NRM_EQUAL,
NUM_EXEC_QUAL((Plan *) plan));
plan->recheckqual = (List *)
fix_upper_expr(root,
@@ -1348,6 +1373,7 @@ set_indexonlyscan_references(PlannerInfo *root,
index_itlist,
INDEX_VAR,
rtoffset,
+ NRM_EQUAL,
NUM_EXEC_QUAL((Plan *) plan));
/* indexqual is already transformed to reference index columns */
plan->indexqual = fix_scan_list(root, plan->indexqual,
@@ -1554,6 +1580,7 @@ set_foreignscan_references(PlannerInfo *root,
itlist,
INDEX_VAR,
rtoffset,
+ NRM_EQUAL,
NUM_EXEC_TLIST((Plan *) fscan));
fscan->scan.plan.qual = (List *)
fix_upper_expr(root,
@@ -1561,6 +1588,7 @@ set_foreignscan_references(PlannerInfo *root,
itlist,
INDEX_VAR,
rtoffset,
+ NRM_EQUAL,
NUM_EXEC_QUAL((Plan *) fscan));
fscan->fdw_exprs = (List *)
fix_upper_expr(root,
@@ -1568,6 +1596,7 @@ set_foreignscan_references(PlannerInfo *root,
itlist,
INDEX_VAR,
rtoffset,
+ NRM_EQUAL,
NUM_EXEC_QUAL((Plan *) fscan));
fscan->fdw_recheck_quals = (List *)
fix_upper_expr(root,
@@ -1575,6 +1604,7 @@ set_foreignscan_references(PlannerInfo *root,
itlist,
INDEX_VAR,
rtoffset,
+ NRM_EQUAL,
NUM_EXEC_QUAL((Plan *) fscan));
pfree(itlist);
/* fdw_scan_tlist itself just needs fix_scan_list() adjustments */
@@ -1603,6 +1633,7 @@ set_foreignscan_references(PlannerInfo *root,
}
fscan->fs_relids = offset_relid_set(fscan->fs_relids, rtoffset);
+ fscan->fs_base_relids = offset_relid_set(fscan->fs_base_relids, rtoffset);
/* Adjust resultRelation if it's valid */
if (fscan->resultRelation > 0)
@@ -1635,6 +1666,7 @@ set_customscan_references(PlannerInfo *root,
itlist,
INDEX_VAR,
rtoffset,
+ NRM_EQUAL,
NUM_EXEC_TLIST((Plan *) cscan));
cscan->scan.plan.qual = (List *)
fix_upper_expr(root,
@@ -1642,6 +1674,7 @@ set_customscan_references(PlannerInfo *root,
itlist,
INDEX_VAR,
rtoffset,
+ NRM_EQUAL,
NUM_EXEC_QUAL((Plan *) cscan));
cscan->custom_exprs = (List *)
fix_upper_expr(root,
@@ -1649,6 +1682,7 @@ set_customscan_references(PlannerInfo *root,
itlist,
INDEX_VAR,
rtoffset,
+ NRM_EQUAL,
NUM_EXEC_QUAL((Plan *) cscan));
pfree(itlist);
/* custom_scan_tlist itself just needs fix_scan_list() adjustments */
@@ -1835,6 +1869,7 @@ set_hash_references(PlannerInfo *root, Plan *plan, int rtoffset)
outer_itlist,
OUTER_VAR,
rtoffset,
+ NRM_EQUAL,
NUM_EXEC_QUAL(plan));
/* Hash doesn't project */
@@ -2170,6 +2205,7 @@ fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context)
/* At scan level, we should always just evaluate the contained expr */
PlaceHolderVar *phv = (PlaceHolderVar *) node;
+ Assert(phv->phnullingrels == NULL);
return fix_scan_expr_mutator((Node *) phv->phexpr, context);
}
if (IsA(node, AlternativeSubPlan))
@@ -2227,6 +2263,7 @@ set_join_references(PlannerInfo *root, Join *join, int rtoffset)
inner_itlist,
(Index) 0,
rtoffset,
+ NRM_EQUAL,
NUM_EXEC_QUAL((Plan *) join));
/* Now do join-type-specific stuff */
@@ -2239,11 +2276,21 @@ set_join_references(PlannerInfo *root, Join *join, int rtoffset)
{
NestLoopParam *nlp = (NestLoopParam *) lfirst(lc);
+ /*
+ * Because we don't reparameterize parameterized paths to match
+ * the outer-join level at which they are used, Vars seen in the
+ * NestLoopParam expression may have nullingrels that are just a
+ * subset of those in the Vars actually available from the outer
+ * side. Not checking this exactly is a bit grotty, but the work
+ * needed to make things match up perfectly seems well out of
+ * proportion to the value.
+ */
nlp->paramval = (Var *) fix_upper_expr(root,
(Node *) nlp->paramval,
outer_itlist,
OUTER_VAR,
rtoffset,
+ NRM_SUBSET,
NUM_EXEC_TLIST(outer_plan));
/* Check we replaced any PlaceHolderVar with simple Var */
if (!(IsA(nlp->paramval, Var) &&
@@ -2261,6 +2308,7 @@ set_join_references(PlannerInfo *root, Join *join, int rtoffset)
inner_itlist,
(Index) 0,
rtoffset,
+ NRM_EQUAL,
NUM_EXEC_QUAL((Plan *) join));
}
else if (IsA(join, HashJoin))
@@ -2273,6 +2321,7 @@ set_join_references(PlannerInfo *root, Join *join, int rtoffset)
inner_itlist,
(Index) 0,
rtoffset,
+ NRM_EQUAL,
NUM_EXEC_QUAL((Plan *) join));
/*
@@ -2284,45 +2333,27 @@ set_join_references(PlannerInfo *root, Join *join, int rtoffset)
outer_itlist,
OUTER_VAR,
rtoffset,
+ NRM_EQUAL,
NUM_EXEC_QUAL((Plan *) join));
}
/*
* Now we need to fix up the targetlist and qpqual, which are logically
- * above the join. This means they should not re-use any input expression
- * that was computed in the nullable side of an outer join. Vars and
- * PlaceHolderVars are fine, so we can implement this restriction just by
- * clearing has_non_vars in the indexed_tlist structs.
- *
- * XXX This is a grotty workaround for the fact that we don't clearly
- * distinguish between a Var appearing below an outer join and the "same"
- * Var appearing above it. If we did, we'd not need to hack the matching
- * rules this way.
+ * above the join. This means that, if it's not an inner join, any Vars
+ * and PHVs appearing here should have nullingrels that include the
+ * effects of the outer join, ie they will have nullingrels equal to the
+ * input Vars' nullingrels plus the bit added by the outer join. We don't
+ * currently have enough info available here to identify what that should
+ * be, so we just tell fix_join_expr to accept superset nullingrels
+ * matches instead of exact ones.
*/
- switch (join->jointype)
- {
- case JOIN_LEFT:
- case JOIN_SEMI:
- case JOIN_ANTI:
- inner_itlist->has_non_vars = false;
- break;
- case JOIN_RIGHT:
- outer_itlist->has_non_vars = false;
- break;
- case JOIN_FULL:
- outer_itlist->has_non_vars = false;
- inner_itlist->has_non_vars = false;
- break;
- default:
- break;
- }
-
join->plan.targetlist = fix_join_expr(root,
join->plan.targetlist,
outer_itlist,
inner_itlist,
(Index) 0,
rtoffset,
+ (join->jointype == JOIN_INNER ? NRM_EQUAL : NRM_SUPERSET),
NUM_EXEC_TLIST((Plan *) join));
join->plan.qual = fix_join_expr(root,
join->plan.qual,
@@ -2330,6 +2361,7 @@ set_join_references(PlannerInfo *root, Join *join, int rtoffset)
inner_itlist,
(Index) 0,
rtoffset,
+ (join->jointype == JOIN_INNER ? NRM_EQUAL : NRM_SUPERSET),
NUM_EXEC_QUAL((Plan *) join));
pfree(outer_itlist);
@@ -2384,6 +2416,7 @@ set_upper_references(PlannerInfo *root, Plan *plan, int rtoffset)
subplan_itlist,
OUTER_VAR,
rtoffset,
+ NRM_EQUAL,
NUM_EXEC_TLIST(plan));
}
else
@@ -2392,6 +2425,7 @@ set_upper_references(PlannerInfo *root, Plan *plan, int rtoffset)
subplan_itlist,
OUTER_VAR,
rtoffset,
+ NRM_EQUAL,
NUM_EXEC_TLIST(plan));
tle = flatCopyTargetEntry(tle);
tle->expr = (Expr *) newexpr;
@@ -2405,6 +2439,7 @@ set_upper_references(PlannerInfo *root, Plan *plan, int rtoffset)
subplan_itlist,
OUTER_VAR,
rtoffset,
+ NRM_EQUAL,
NUM_EXEC_QUAL(plan));
pfree(subplan_itlist);
@@ -2605,7 +2640,7 @@ set_dummy_tlist_references(Plan *plan, int rtoffset)
* tlist_member() searches.
*
* The result of this function is an indexed_tlist struct to pass to
- * search_indexed_tlist_for_var() or search_indexed_tlist_for_non_var().
+ * search_indexed_tlist_for_var() and siblings.
* When done, the indexed_tlist may be freed with a single pfree().
*/
static indexed_tlist *
@@ -2637,6 +2672,9 @@ build_tlist_index(List *tlist)
vinfo->varno = var->varno;
vinfo->varattno = var->varattno;
vinfo->resno = tle->resno;
+#ifdef USE_ASSERT_CHECKING
+ vinfo->varnullingrels = var->varnullingrels;
+#endif
vinfo++;
}
else if (tle->expr && IsA(tle->expr, PlaceHolderVar))
@@ -2689,6 +2727,9 @@ build_tlist_index_other_vars(List *tlist, int ignore_rel)
vinfo->varno = var->varno;
vinfo->varattno = var->varattno;
vinfo->resno = tle->resno;
+#ifdef USE_ASSERT_CHECKING
+ vinfo->varnullingrels = var->varnullingrels;
+#endif
vinfo++;
}
}
@@ -2708,10 +2749,17 @@ build_tlist_index_other_vars(List *tlist, int ignore_rel)
* modified varno/varattno (to wit, newvarno and the resno of the TLE entry).
* Also ensure that varnosyn is incremented by rtoffset.
* If no match, return NULL.
+ *
+ * In debugging builds, we cross-check the varnullingrels of the subplan
+ * output Var based on nrm_match. Most call sites should pass NRM_EQUAL
+ * indicating we expect an exact match. However, there are places where
+ * we haven't cleaned things up completely, and we have to settle for
+ * allowing subset or superset matches.
*/
static Var *
search_indexed_tlist_for_var(Var *var, indexed_tlist *itlist,
- int newvarno, int rtoffset)
+ int newvarno, int rtoffset,
+ NullingRelsMatch nrm_match)
{
int varno = var->varno;
AttrNumber varattno = var->varattno;
@@ -2727,6 +2775,27 @@ search_indexed_tlist_for_var(Var *var, indexed_tlist *itlist,
/* Found a match */
Var *newvar = copyVar(var);
+ /*
+ * Assert that we kept all the nullingrels machinations straight.
+ *
+ * XXX we skip the check for system columns and whole-row Vars.
+ * That's because such Vars might be row identity Vars, which are
+ * generated without any varnullingrels. It'd be hard to do
+ * otherwise, since they're normally made very early in planning,
+ * when we haven't looked at the jointree yet and don't know which
+ * joins might null such Vars. Doesn't seem worth the expense to
+ * make them fully valid. (While it's slightly annoying that we
+ * thereby lose checking for user-written references to such
+ * columns, it seems unlikely that a bug in nullingrels logic
+ * would affect only system columns.)
+ */
+ Assert(varattno <= 0 ||
+ (nrm_match == NRM_SUBSET ?
+ bms_is_subset(var->varnullingrels, vinfo->varnullingrels) :
+ nrm_match == NRM_SUPERSET ?
+ bms_is_subset(vinfo->varnullingrels, var->varnullingrels) :
+ bms_equal(vinfo->varnullingrels, var->varnullingrels)));
+
newvar->varno = newvarno;
newvar->varattno = vinfo->resno;
if (newvar->varnosyn > 0)
@@ -2739,15 +2808,63 @@ search_indexed_tlist_for_var(Var *var, indexed_tlist *itlist,
}
/*
- * search_indexed_tlist_for_non_var --- find a non-Var in an indexed tlist
+ * search_indexed_tlist_for_phv --- find a PlaceHolderVar in an indexed tlist
*
* If a match is found, return a Var constructed to reference the tlist item.
* If no match, return NULL.
*
- * NOTE: it is a waste of time to call this unless itlist->has_ph_vars or
- * itlist->has_non_vars. Furthermore, set_join_references() relies on being
- * able to prevent matching of non-Vars by clearing itlist->has_non_vars,
- * so there's a correctness reason not to call it unless that's set.
+ * Cross-check phnullingrels as in search_indexed_tlist_for_var.
+ *
+ * NOTE: it is a waste of time to call this unless itlist->has_ph_vars.
+ */
+static Var *
+search_indexed_tlist_for_phv(PlaceHolderVar *phv,
+ indexed_tlist *itlist, int newvarno,
+ NullingRelsMatch nrm_match)
+{
+ ListCell *lc;
+
+ foreach(lc, itlist->tlist)
+ {
+ TargetEntry *tle = (TargetEntry *) lfirst(lc);
+
+ if (tle->expr && IsA(tle->expr, PlaceHolderVar))
+ {
+ PlaceHolderVar *subphv = (PlaceHolderVar *) tle->expr;
+ Var *newvar;
+
+ /*
+ * Analogously to search_indexed_tlist_for_var, we match on phid
+ * only. We don't use equal(), partially for speed but mostly
+ * because phnullingrels might not be exactly equal.
+ */
+ if (phv->phid != subphv->phid)
+ continue;
+
+ /* Assert that we kept all the nullingrels machinations straight */
+ Assert(nrm_match == NRM_SUBSET ?
+ bms_is_subset(phv->phnullingrels, subphv->phnullingrels) :
+ nrm_match == NRM_SUPERSET ?
+ bms_is_subset(subphv->phnullingrels, phv->phnullingrels) :
+ bms_equal(subphv->phnullingrels, phv->phnullingrels));
+
+ /* Found a matching subplan output expression */
+ newvar = makeVarFromTargetEntry(newvarno, tle);
+ newvar->varnosyn = 0; /* wasn't ever a plain Var */
+ newvar->varattnosyn = 0;
+ return newvar;
+ }
+ }
+ return NULL; /* no match */
+}
+
+/*
+ * search_indexed_tlist_for_non_var --- find a non-Var/PHV in an indexed tlist
+ *
+ * If a match is found, return a Var constructed to reference the tlist item.
+ * If no match, return NULL.
+ *
+ * NOTE: it is a waste of time to call this unless itlist->has_non_vars.
*/
static Var *
search_indexed_tlist_for_non_var(Expr *node,
@@ -2854,6 +2971,7 @@ search_indexed_tlist_for_sortgroupref(Expr *node,
* 'acceptable_rel' is either zero or the rangetable index of a relation
* whose Vars may appear in the clause without provoking an error
* 'rtoffset': how much to increment varnos by
+ * 'nrm_match': as for search_indexed_tlist_for_var()
* 'num_exec': estimated number of executions of expression
*
* Returns the new expression tree. The original clause structure is
@@ -2866,6 +2984,7 @@ fix_join_expr(PlannerInfo *root,
indexed_tlist *inner_itlist,
Index acceptable_rel,
int rtoffset,
+ NullingRelsMatch nrm_match,
double num_exec)
{
fix_join_expr_context context;
@@ -2875,6 +2994,7 @@ fix_join_expr(PlannerInfo *root,
context.inner_itlist = inner_itlist;
context.acceptable_rel = acceptable_rel;
context.rtoffset = rtoffset;
+ context.nrm_match = nrm_match;
context.num_exec = num_exec;
return (List *) fix_join_expr_mutator((Node *) clauses, &context);
}
@@ -2896,7 +3016,8 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
newvar = search_indexed_tlist_for_var(var,
context->outer_itlist,
OUTER_VAR,
- context->rtoffset);
+ context->rtoffset,
+ context->nrm_match);
if (newvar)
return (Node *) newvar;
}
@@ -2907,7 +3028,8 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
newvar = search_indexed_tlist_for_var(var,
context->inner_itlist,
INNER_VAR,
- context->rtoffset);
+ context->rtoffset,
+ context->nrm_match);
if (newvar)
return (Node *) newvar;
}
@@ -2932,22 +3054,25 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
/* See if the PlaceHolderVar has bubbled up from a lower plan node */
if (context->outer_itlist && context->outer_itlist->has_ph_vars)
{
- newvar = search_indexed_tlist_for_non_var((Expr *) phv,
- context->outer_itlist,
- OUTER_VAR);
+ newvar = search_indexed_tlist_for_phv(phv,
+ context->outer_itlist,
+ OUTER_VAR,
+ context->nrm_match);
if (newvar)
return (Node *) newvar;
}
if (context->inner_itlist && context->inner_itlist->has_ph_vars)
{
- newvar = search_indexed_tlist_for_non_var((Expr *) phv,
- context->inner_itlist,
- INNER_VAR);
+ newvar = search_indexed_tlist_for_phv(phv,
+ context->inner_itlist,
+ INNER_VAR,
+ context->nrm_match);
if (newvar)
return (Node *) newvar;
}
/* If not supplied by input plans, evaluate the contained expr */
+ /* XXX can we assert something about phnullingrels? */
return fix_join_expr_mutator((Node *) phv->phexpr, context);
}
/* Try matching more complex expressions too, if tlists have any */
@@ -3006,6 +3131,7 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
* 'subplan_itlist': indexed target list for subplan (or index)
* 'newvarno': varno to use for Vars referencing tlist elements
* 'rtoffset': how much to increment varnos by
+ * 'nrm_match': as for search_indexed_tlist_for_var()
* 'num_exec': estimated number of executions of expression
*
* The resulting tree is a copy of the original in which all Var nodes have
@@ -3018,6 +3144,7 @@ fix_upper_expr(PlannerInfo *root,
indexed_tlist *subplan_itlist,
int newvarno,
int rtoffset,
+ NullingRelsMatch nrm_match,
double num_exec)
{
fix_upper_expr_context context;
@@ -3026,6 +3153,7 @@ fix_upper_expr(PlannerInfo *root,
context.subplan_itlist = subplan_itlist;
context.newvarno = newvarno;
context.rtoffset = rtoffset;
+ context.nrm_match = nrm_match;
context.num_exec = num_exec;
return fix_upper_expr_mutator(node, &context);
}
@@ -3044,7 +3172,8 @@ fix_upper_expr_mutator(Node *node, fix_upper_expr_context *context)
newvar = search_indexed_tlist_for_var(var,
context->subplan_itlist,
context->newvarno,
- context->rtoffset);
+ context->rtoffset,
+ context->nrm_match);
if (!newvar)
elog(ERROR, "variable not found in subplan target list");
return (Node *) newvar;
@@ -3056,13 +3185,15 @@ fix_upper_expr_mutator(Node *node, fix_upper_expr_context *context)
/* See if the PlaceHolderVar has bubbled up from a lower plan node */
if (context->subplan_itlist->has_ph_vars)
{
- newvar = search_indexed_tlist_for_non_var((Expr *) phv,
- context->subplan_itlist,
- context->newvarno);
+ newvar = search_indexed_tlist_for_phv(phv,
+ context->subplan_itlist,
+ context->newvarno,
+ context->nrm_match);
if (newvar)
return (Node *) newvar;
}
/* If not supplied by input plan, evaluate the contained expr */
+ /* XXX can we assert something about phnullingrels? */
return fix_upper_expr_mutator((Node *) phv->phexpr, context);
}
/* Try matching more complex expressions too, if tlist has any */
@@ -3169,6 +3300,7 @@ set_returning_clause_references(PlannerInfo *root,
NULL,
resultRelation,
rtoffset,
+ NRM_EQUAL,
NUM_EXEC_TLIST(topplan));
pfree(itlist);