diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 458dae0489c029bd743c75c82f8e5102067e89bf..3a5efa2114e44a7bff0062ad301840c745c447f8 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -1835,10 +1835,10 @@ subquery_push_qual(Query *subquery, RangeTblEntry *rte, Index rti, Node *qual)
 		 * 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
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index 8f75948d0dd399e21fab2053064cfd43d7b38c43..b785c269a034c521b09d7d27c76537fe127d253c 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -502,25 +502,27 @@ rewriteRuleAction(Query *parsetree,
 	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
@@ -543,15 +545,15 @@ rewriteRuleAction(Query *parsetree,
 				   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,
@@ -1703,14 +1705,17 @@ CopyAndAddInvertedQual(Query *parsetree,
 	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);
 
diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c
index ef04c342a295f8f3912c4a714a43ced9b04c96e6..bea00e48e58a260a60dc5c7b3c87abdf0e228659 100644
--- a/src/backend/rewrite/rewriteManip.c
+++ b/src/backend/rewrite/rewriteManip.c
@@ -1323,12 +1323,16 @@ map_variable_attnos(Node *node,
 
 
 /*
- * 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.
@@ -1341,15 +1345,15 @@ typedef struct
 {
 	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)
@@ -1388,29 +1392,37 @@ ResolveNew_callback(Var *var,
 
 	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
 	{
@@ -1426,20 +1438,23 @@ ResolveNew_callback(Var *var,
 }
 
 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);
 }
diff --git a/src/include/rewrite/rewriteManip.h b/src/include/rewrite/rewriteManip.h
index e13331dcb5e41c8d872f9a65344fedcdfc966318..1a96c556c9f1354281a422b35ffc23a067860bcd 100644
--- a/src/include/rewrite/rewriteManip.h
+++ b/src/include/rewrite/rewriteManip.h
@@ -31,6 +31,13 @@ struct replace_rte_variables_context
 	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,
@@ -69,9 +76,12 @@ extern Node *map_variable_attnos(Node *node,
 					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 */