mirror of
https://github.com/postgres/postgres.git
synced 2025-12-04 12:02:48 +03:00
Fix ON CONFLICT bugs that manifest when used in rules.
Specifically the tlist and rti of the pseudo "excluded" relation weren't properly treated by expression_tree_walker, which lead to errors when excluded was referenced inside a rule because the varnos where not properly adjusted. Similar omissions in OffsetVarNodes and expression_tree_mutator had less impact, but should obviously be fixed nonetheless. A couple tests of for ON CONFLICT UPDATE into INSERT rule bearing relations have been added. In passing I updated a couple comments.
This commit is contained in:
@@ -1675,6 +1675,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
|
||||
ExecSetSlotDescriptor(mtstate->mt_existing,
|
||||
resultRelInfo->ri_RelationDesc->rd_att);
|
||||
|
||||
/* carried forward solely for the benefit of explain */
|
||||
mtstate->mt_excludedtlist = node->exclRelTlist;
|
||||
|
||||
/* create target slot for UPDATE SET projection */
|
||||
|
||||
@@ -1922,6 +1922,8 @@ expression_tree_walker(Node *node,
|
||||
return true;
|
||||
if (walker(onconflict->onConflictWhere, context))
|
||||
return true;
|
||||
if (walker(onconflict->exclRelTlist, context))
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case T_JoinExpr:
|
||||
@@ -2642,6 +2644,7 @@ expression_tree_mutator(Node *node,
|
||||
MUTATE(newnode->arbiterWhere, oc->arbiterWhere, Node *);
|
||||
MUTATE(newnode->onConflictSet, oc->onConflictSet, List *);
|
||||
MUTATE(newnode->onConflictWhere, oc->onConflictWhere, Node *);
|
||||
MUTATE(newnode->exclRelTlist, oc->exclRelTlist, List *);
|
||||
|
||||
return (Node *) newnode;
|
||||
}
|
||||
|
||||
@@ -740,9 +740,9 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
|
||||
|
||||
/*
|
||||
* We treat ModifyTable with ON CONFLICT as a form of 'pseudo
|
||||
* join', where the inner side is the EXLUDED tuple. Therefore
|
||||
* use fix_join_expr to setup the relevant variables to
|
||||
* INNER_VAR. We explicitly don't create any OUTER_VARs as
|
||||
* join', where the inner side is the EXCLUDED tuple.
|
||||
* Therefore use fix_join_expr to setup the relevant variables
|
||||
* to INNER_VAR. We explicitly don't create any OUTER_VARs as
|
||||
* those are already used by RETURNING and it seems better to
|
||||
* be non-conflicting.
|
||||
*/
|
||||
@@ -763,6 +763,9 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
|
||||
NULL, itlist,
|
||||
linitial_int(splan->resultRelations),
|
||||
rtoffset);
|
||||
|
||||
splan->exclRelTlist =
|
||||
fix_scan_list(root, splan->exclRelTlist, rtoffset);
|
||||
}
|
||||
|
||||
splan->nominalRelation += rtoffset;
|
||||
|
||||
@@ -426,9 +426,9 @@ OffsetVarNodes(Node *node, int offset, int sublevels_up)
|
||||
/*
|
||||
* If we are starting at a Query, and sublevels_up is zero, then we
|
||||
* must also fix rangetable indexes in the Query itself --- namely
|
||||
* resultRelation and rowMarks entries. sublevels_up cannot be zero
|
||||
* when recursing into a subquery, so there's no need to have the same
|
||||
* logic inside OffsetVarNodes_walker.
|
||||
* resultRelation, exclRelIndex and rowMarks entries. sublevels_up
|
||||
* cannot be zero when recursing into a subquery, so there's no need
|
||||
* to have the same logic inside OffsetVarNodes_walker.
|
||||
*/
|
||||
if (sublevels_up == 0)
|
||||
{
|
||||
@@ -436,6 +436,10 @@ OffsetVarNodes(Node *node, int offset, int sublevels_up)
|
||||
|
||||
if (qry->resultRelation)
|
||||
qry->resultRelation += offset;
|
||||
|
||||
if (qry->onConflict && qry->onConflict->exclRelIndex)
|
||||
qry->onConflict->exclRelIndex += offset;
|
||||
|
||||
foreach(l, qry->rowMarks)
|
||||
{
|
||||
RowMarkClause *rc = (RowMarkClause *) lfirst(l);
|
||||
@@ -617,6 +621,11 @@ ChangeVarNodes(Node *node, int rt_index, int new_index, int sublevels_up)
|
||||
|
||||
if (qry->resultRelation == rt_index)
|
||||
qry->resultRelation = new_index;
|
||||
|
||||
/* this is unlikely to ever be used, but ... */
|
||||
if (qry->onConflict && qry->onConflict->exclRelIndex == rt_index)
|
||||
qry->onConflict->exclRelIndex = new_index;
|
||||
|
||||
foreach(l, qry->rowMarks)
|
||||
{
|
||||
RowMarkClause *rc = (RowMarkClause *) lfirst(l);
|
||||
|
||||
Reference in New Issue
Block a user