* This step also ensures that when we are pushing into a setop tree,
* each component query gets its own copy of the qual.
*/
- qual = ResolveNew(qual, rti, 0, rte,
- subquery->targetList,
- CMD_SELECT, 0,
- &subquery->hasSubLinks);
+ qual = ReplaceVarsFromTargetList(qual, rti, 0, rte,
+ subquery->targetList,
+ REPLACEVARS_REPORT_ERROR, 0,
+ &subquery->hasSubLinks);
/*
* Now attach the qual to the proper place: normally WHERE, but if the
AddQual(sub_action, parsetree->jointree->quals);
/*
- * Rewrite new.attribute w/ right hand side of target-list entry for
+ * Rewrite new.attribute with right hand side of target-list entry for
* appropriate field name in insert/update.
*
- * KLUGE ALERT: since ResolveNew returns a mutated copy, we can't just
- * apply it to sub_action; we have to remember to update the sublink
- * inside rule_action, too.
+ * KLUGE ALERT: since ReplaceVarsFromTargetList returns a mutated copy, we
+ * can't just apply it to sub_action; we have to remember to update the
+ * sublink inside rule_action, too.
*/
if ((event == CMD_INSERT || event == CMD_UPDATE) &&
sub_action->commandType != CMD_UTILITY)
{
- sub_action = (Query *) ResolveNew((Node *) sub_action,
- new_varno,
- 0,
- rt_fetch(new_varno,
- sub_action->rtable),
- parsetree->targetList,
- event,
- current_varno,
- NULL);
+ sub_action = (Query *)
+ ReplaceVarsFromTargetList((Node *) sub_action,
+ new_varno,
+ 0,
+ rt_fetch(new_varno, sub_action->rtable),
+ parsetree->targetList,
+ (event == CMD_UPDATE) ?
+ REPLACEVARS_CHANGE_VARNO :
+ REPLACEVARS_SUBSTITUTE_NULL,
+ current_varno,
+ NULL);
if (sub_action_ptr)
*sub_action_ptr = sub_action;
else
errmsg("cannot have RETURNING lists in multiple rules")));
*returning_flag = true;
rule_action->returningList = (List *)
- ResolveNew((Node *) parsetree->returningList,
- parsetree->resultRelation,
- 0,
- rt_fetch(parsetree->resultRelation,
- parsetree->rtable),
- rule_action->returningList,
- CMD_SELECT,
- 0,
- &rule_action->hasSubLinks);
+ ReplaceVarsFromTargetList((Node *) parsetree->returningList,
+ parsetree->resultRelation,
+ 0,
+ rt_fetch(parsetree->resultRelation,
+ parsetree->rtable),
+ rule_action->returningList,
+ REPLACEVARS_REPORT_ERROR,
+ 0,
+ &rule_action->hasSubLinks);
/*
* There could have been some SubLinks in parsetree's returningList,
ChangeVarNodes(new_qual, PRS2_OLD_VARNO, rt_index, 0);
/* Fix references to NEW */
if (event == CMD_INSERT || event == CMD_UPDATE)
- new_qual = ResolveNew(new_qual,
- PRS2_NEW_VARNO,
- 0,
- rt_fetch(rt_index, parsetree->rtable),
- parsetree->targetList,
- event,
- rt_index,
- &parsetree->hasSubLinks);
+ new_qual = ReplaceVarsFromTargetList(new_qual,
+ PRS2_NEW_VARNO,
+ 0,
+ rt_fetch(rt_index,
+ parsetree->rtable),
+ parsetree->targetList,
+ (event == CMD_UPDATE) ?
+ REPLACEVARS_CHANGE_VARNO :
+ REPLACEVARS_SUBSTITUTE_NULL,
+ rt_index,
+ &parsetree->hasSubLinks);
/* And attach the fixed qual */
AddInvertedQual(parsetree, new_qual);
/*
- * ResolveNew - replace Vars with corresponding items from a targetlist
+ * ReplaceVarsFromTargetList - replace Vars with items from a targetlist
*
* Vars matching target_varno and sublevels_up are replaced by the
* entry with matching resno from targetlist, if there is one.
- * If not, we either change the unmatched Var's varno to update_varno
- * (when event == CMD_UPDATE) or replace it with a constant NULL.
+ *
+ * If there is no matching resno for such a Var, the action depends on the
+ * nomatch_option:
+ * REPLACEVARS_REPORT_ERROR: throw an error
+ * REPLACEVARS_CHANGE_VARNO: change Var's varno to nomatch_varno
+ * REPLACEVARS_SUBSTITUTE_NULL: replace Var with a NULL Const of same type
*
* The caller must also provide target_rte, the RTE describing the target
* relation. This is needed to handle whole-row Vars referencing the target.
{
RangeTblEntry *target_rte;
List *targetlist;
- int event;
- int update_varno;
-} ResolveNew_context;
+ ReplaceVarsNoMatchOption nomatch_option;
+ int nomatch_varno;
+} ReplaceVarsFromTargetList_context;
static Node *
-ResolveNew_callback(Var *var,
- replace_rte_variables_context *context)
+ReplaceVarsFromTargetList_callback(Var *var,
+ replace_rte_variables_context *context)
{
- ResolveNew_context *rcon = (ResolveNew_context *) context->callback_arg;
+ ReplaceVarsFromTargetList_context *rcon = (ReplaceVarsFromTargetList_context *) context->callback_arg;
TargetEntry *tle;
if (var->varattno == InvalidAttrNumber)
if (tle == NULL || tle->resjunk)
{
- /* Failed to find column in insert/update tlist */
- if (rcon->event == CMD_UPDATE)
- {
- /* For update, just change unmatched var's varno */
- var = (Var *) copyObject(var);
- var->varno = rcon->update_varno;
- var->varnoold = rcon->update_varno;
- return (Node *) var;
- }
- else
+ /* Failed to find column in targetlist */
+ switch (rcon->nomatch_option)
{
- /* Otherwise replace unmatched var with a null */
- /* need coerce_to_domain in case of NOT NULL domain constraint */
- return coerce_to_domain((Node *) makeNullConst(var->vartype,
- var->vartypmod,
- var->varcollid),
- InvalidOid, -1,
- var->vartype,
- COERCE_IMPLICIT_CAST,
- -1,
- false,
- false);
+ case REPLACEVARS_REPORT_ERROR:
+ /* fall through, throw error below */
+ break;
+
+ case REPLACEVARS_CHANGE_VARNO:
+ var = (Var *) copyObject(var);
+ var->varno = rcon->nomatch_varno;
+ var->varnoold = rcon->nomatch_varno;
+ return (Node *) var;
+
+ case REPLACEVARS_SUBSTITUTE_NULL:
+ /*
+ * If Var is of domain type, we should add a CoerceToDomain
+ * node, in case there is a NOT NULL domain constraint.
+ */
+ return coerce_to_domain((Node *) makeNullConst(var->vartype,
+ var->vartypmod,
+ var->varcollid),
+ InvalidOid, -1,
+ var->vartype,
+ COERCE_IMPLICIT_CAST,
+ -1,
+ false,
+ false);
}
+ elog(ERROR, "could not find replacement targetlist entry for attno %d",
+ var->varattno);
+ return NULL; /* keep compiler quiet */
}
else
{
}
Node *
-ResolveNew(Node *node, int target_varno, int sublevels_up,
- RangeTblEntry *target_rte,
- List *targetlist, int event, int update_varno,
- bool *outer_hasSubLinks)
+ReplaceVarsFromTargetList(Node *node,
+ int target_varno, int sublevels_up,
+ RangeTblEntry *target_rte,
+ List *targetlist,
+ ReplaceVarsNoMatchOption nomatch_option,
+ int nomatch_varno,
+ bool *outer_hasSubLinks)
{
- ResolveNew_context context;
+ ReplaceVarsFromTargetList_context context;
context.target_rte = target_rte;
context.targetlist = targetlist;
- context.event = event;
- context.update_varno = update_varno;
+ context.nomatch_option = nomatch_option;
+ context.nomatch_varno = nomatch_varno;
return replace_rte_variables(node, target_varno, sublevels_up,
- ResolveNew_callback,
+ ReplaceVarsFromTargetList_callback,
(void *) &context,
outer_hasSubLinks);
}
bool inserted_sublink; /* have we inserted a SubLink? */
};
+typedef enum ReplaceVarsNoMatchOption
+{
+ REPLACEVARS_REPORT_ERROR, /* throw error if no match */
+ REPLACEVARS_CHANGE_VARNO, /* change the Var's varno, nothing else */
+ REPLACEVARS_SUBSTITUTE_NULL /* replace with a NULL Const */
+} ReplaceVarsNoMatchOption;
+
extern void OffsetVarNodes(Node *node, int offset, int sublevels_up);
extern void ChangeVarNodes(Node *node, int old_varno, int new_varno,
const AttrNumber *attno_map, int map_length,
bool *found_whole_row);
-extern Node *ResolveNew(Node *node, int target_varno, int sublevels_up,
- RangeTblEntry *target_rte,
- List *targetlist, int event, int update_varno,
- bool *outer_hasSubLinks);
+extern Node *ReplaceVarsFromTargetList(Node *node,
+ int target_varno, int sublevels_up,
+ RangeTblEntry *target_rte,
+ List *targetlist,
+ ReplaceVarsNoMatchOption nomatch_option,
+ int nomatch_varno,
+ bool *outer_hasSubLinks);
#endif /* REWRITEMANIP_H */