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 */