int rtlength; /* used only for assertion checks */
} nullingrel_info;
+/* Options for wrapping an expression for identification purposes */
+typedef enum ReplaceWrapOption
+{
+ REPLACE_WRAP_NONE, /* no expressions need to be wrapped */
+ REPLACE_WRAP_ALL, /* all expressions need to be wrapped */
+ REPLACE_WRAP_VARFREE, /* variable-free expressions need to be
+ * wrapped */
+} ReplaceWrapOption;
+
typedef struct pullup_replace_vars_context
{
PlannerInfo *root;
* target_rte->lateral) */
bool *outer_hasSubLinks; /* -> outer query's hasSubLinks */
int varno; /* varno of subquery */
- bool wrap_non_vars; /* do we need all non-Var outputs to be PHVs? */
+ ReplaceWrapOption wrap_option; /* do we need certain outputs to be PHVs? */
Node **rv_cache; /* cache for results with PHVs */
} pullup_replace_vars_context;
rvcontext.outer_hasSubLinks = NULL;
rvcontext.varno = rt_index;
/* this flag will be set below, if needed */
- rvcontext.wrap_non_vars = false;
+ rvcontext.wrap_option = REPLACE_WRAP_NONE;
/* initialize cache array with indexes 0 .. length(tlist) */
rvcontext.rv_cache = palloc0((list_length(tlist) + 1) *
sizeof(Node *));
/*
* If the query uses grouping sets, we need a PlaceHolderVar for
- * anything that's not a simple Var. Again, this ensures that
- * expressions retain their separate identity so that they will
- * match grouping set columns when appropriate. (It'd be
- * sufficient to wrap values used in grouping set columns, and do
- * so only in non-aggregated portions of the tlist and havingQual,
- * but that would require a lot of infrastructure that
- * pullup_replace_vars hasn't currently got.)
+ * each expression of the relation's targetlist items. (See
+ * comments in pull_up_simple_subquery().)
*/
if (parse->groupingSets)
- rvcontext.wrap_non_vars = true;
+ rvcontext.wrap_option = REPLACE_WRAP_ALL;
/*
* Apply pullup variable replacement throughout the query tree.
rvcontext.outer_hasSubLinks = &parse->hasSubLinks;
rvcontext.varno = varno;
/* this flag will be set below, if needed */
- rvcontext.wrap_non_vars = false;
+ rvcontext.wrap_option = REPLACE_WRAP_NONE;
/* initialize cache array with indexes 0 .. length(tlist) */
rvcontext.rv_cache = palloc0((list_length(subquery->targetList) + 1) *
sizeof(Node *));
/*
* If the parent query uses grouping sets, we need a PlaceHolderVar for
- * anything that's not a simple Var. This ensures that expressions retain
- * their separate identity so that they will match grouping set columns
- * when appropriate. (It'd be sufficient to wrap values used in grouping
- * set columns, and do so only in non-aggregated portions of the tlist and
- * havingQual, but that would require a lot of infrastructure that
- * pullup_replace_vars hasn't currently got.)
+ * each expression of the subquery's targetlist items. This ensures that
+ * expressions retain their separate identity so that they will match
+ * grouping set columns when appropriate. (It'd be sufficient to wrap
+ * values used in grouping set columns, and do so only in non-aggregated
+ * portions of the tlist and havingQual, but that would require a lot of
+ * infrastructure that pullup_replace_vars hasn't currently got.)
*/
if (parse->groupingSets)
- rvcontext.wrap_non_vars = true;
+ rvcontext.wrap_option = REPLACE_WRAP_ALL;
/*
* Replace all of the top query's references to the subquery's outputs
rvcontext.nullinfo = NULL;
rvcontext.outer_hasSubLinks = &parse->hasSubLinks;
rvcontext.varno = varno;
- rvcontext.wrap_non_vars = false;
+ rvcontext.wrap_option = REPLACE_WRAP_NONE;
/* initialize cache array with indexes 0 .. length(tlist) */
rvcontext.rv_cache = palloc0((list_length(tlist) + 1) *
sizeof(Node *));
rvcontext.outer_hasSubLinks = &parse->hasSubLinks;
rvcontext.varno = ((RangeTblRef *) jtnode)->rtindex;
/* this flag will be set below, if needed */
- rvcontext.wrap_non_vars = false;
+ rvcontext.wrap_option = REPLACE_WRAP_NONE;
/* initialize cache array with indexes 0 .. length(tlist) */
rvcontext.rv_cache = palloc0((list_length(rvcontext.targetlist) + 1) *
sizeof(Node *));
/*
* If the parent query uses grouping sets, we need a PlaceHolderVar for
- * anything that's not a simple Var. (See comments in
+ * each expression of the subquery's targetlist items. (See comments in
* pull_up_simple_subquery().)
*/
if (parse->groupingSets)
- rvcontext.wrap_non_vars = true;
+ rvcontext.wrap_option = REPLACE_WRAP_ALL;
/*
* Replace all of the top query's references to the RTE's output with
*/
if (containing_appendrel)
{
- bool save_wrap_non_vars = rvcontext->wrap_non_vars;
+ ReplaceWrapOption save_wrap_option = rvcontext->wrap_option;
- rvcontext->wrap_non_vars = false;
+ rvcontext->wrap_option = REPLACE_WRAP_NONE;
containing_appendrel->translated_vars = (List *)
pullup_replace_vars((Node *) containing_appendrel->translated_vars,
rvcontext);
- rvcontext->wrap_non_vars = save_wrap_non_vars;
+ rvcontext->wrap_option = save_wrap_option;
return;
}
else if (IsA(jtnode, JoinExpr))
{
JoinExpr *j = (JoinExpr *) jtnode;
- bool save_wrap_non_vars = context->wrap_non_vars;
+ ReplaceWrapOption save_wrap_option = context->wrap_option;
replace_vars_in_jointree(j->larg, context);
replace_vars_in_jointree(j->rarg, context);
/*
- * Use PHVs within the join quals of a full join. Otherwise, we
- * cannot identify which side of the join a pulled-up var-free
- * expression came from, which can lead to failure to make a plan at
- * all because none of the quals appear to be mergeable or hashable
- * conditions.
+ * Use PHVs within the join quals of a full join for variable-free
+ * expressions. Otherwise, we cannot identify which side of the join
+ * a pulled-up variable-free expression came from, which can lead to
+ * failure to make a plan at all because none of the quals appear to
+ * be mergeable or hashable conditions.
*/
if (j->jointype == JOIN_FULL)
- context->wrap_non_vars = true;
+ context->wrap_option = REPLACE_WRAP_VARFREE;
j->quals = pullup_replace_vars(j->quals, context);
- context->wrap_non_vars = save_wrap_non_vars;
+ context->wrap_option = save_wrap_option;
}
else
elog(ERROR, "unrecognized node type: %d",
* We need a PlaceHolderVar if the Var-to-be-replaced has nonempty
* varnullingrels (unless we find below that the replacement expression is
* a Var or PlaceHolderVar that we can just add the nullingrels to). We
- * also need one if the caller has instructed us that all non-Var/PHV
+ * also need one if the caller has instructed us that certain expression
* replacements need to be wrapped for identification purposes.
*/
- need_phv = (var->varnullingrels != NULL) || rcon->wrap_non_vars;
+ need_phv = (var->varnullingrels != NULL) ||
+ (rcon->wrap_option != REPLACE_WRAP_NONE);
/*
* If PlaceHolderVars are needed, we cache the modified expressions in
{
bool wrap;
- if (varattno == InvalidAttrNumber)
+ if (rcon->wrap_option == REPLACE_WRAP_ALL)
+ {
+ /* Caller told us to wrap all expressions in a PlaceHolderVar */
+ wrap = true;
+ }
+ else if (varattno == InvalidAttrNumber)
{
/*
* Insert PlaceHolderVar for whole-tuple reference. Notice
}
}
}
- else if (rcon->wrap_non_vars)
- {
- /* Caller told us to wrap all non-Vars in a PlaceHolderVar */
- wrap = true;
- }
else
{
/*
* This analysis could be tighter: in particular, a non-strict
* construct hidden within a lower-level PlaceHolderVar is not
* reason to add another PHV. But for now it doesn't seem
- * worth the code to be more exact.
+ * worth the code to be more exact. This is also why it's
+ * preferable to handle bare PHVs in the above branch, rather
+ * than this branch. We also prefer to handle bare Vars in a
+ * separate branch, as it's cheaper this way and parallels the
+ * handling of PHVs.
*
* For a LATERAL subquery, we have to check the actual var
* membership of the node, but if it's non-lateral then any