From e9695c968d2705b0c8986dfbb3fd2409e22acb63 Mon Sep 17 00:00:00 2001
From: Alexander Pyhalov
Date: Wed, 27 Dec 2023 17:31:23 +0300
Subject: [PATCH 6/6] postgres_fdw: fix partition-wise DML
---
contrib/postgres_fdw/deparse.c | 12 +-
.../postgres_fdw/expected/postgres_fdw.out | 160 ++++++++++++++++++
contrib/postgres_fdw/postgres_fdw.c | 98 ++++++++++-
contrib/postgres_fdw/sql/postgres_fdw.sql | 38 +++++
src/backend/optimizer/util/appendinfo.c | 32 +++-
src/include/optimizer/appendinfo.h | 1 +
6 files changed, 323 insertions(+), 18 deletions(-)
diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c
index f879ff3100f..d4c657fa4d5 100644
--- a/contrib/postgres_fdw/deparse.c
+++ b/contrib/postgres_fdw/deparse.c
@@ -2309,7 +2309,7 @@ deparseDirectUpdateSql(StringInfo buf, PlannerInfo *root,
appendStringInfoString(buf, "UPDATE ");
deparseRelation(buf, rel);
- if (foreignrel->reloptkind == RELOPT_JOINREL)
+ if (IS_JOIN_REL(foreignrel))
appendStringInfo(buf, " %s%d", REL_ALIAS_PREFIX, rtindex);
appendStringInfoString(buf, " SET ");
@@ -2336,7 +2336,7 @@ deparseDirectUpdateSql(StringInfo buf, PlannerInfo *root,
reset_transmission_modes(nestlevel);
- if (foreignrel->reloptkind == RELOPT_JOINREL)
+ if (IS_JOIN_REL(foreignrel))
{
List *ignore_conds = NIL;
@@ -2352,7 +2352,7 @@ deparseDirectUpdateSql(StringInfo buf, PlannerInfo *root,
if (additional_conds != NIL)
list_free_deep(additional_conds);
- if (foreignrel->reloptkind == RELOPT_JOINREL)
+ if (IS_JOIN_REL(foreignrel))
deparseExplicitTargetList(returningList, true, retrieved_attrs,
&context);
else
@@ -2417,10 +2417,10 @@ deparseDirectDeleteSql(StringInfo buf, PlannerInfo *root,
appendStringInfoString(buf, "DELETE FROM ");
deparseRelation(buf, rel);
- if (foreignrel->reloptkind == RELOPT_JOINREL)
+ if (IS_JOIN_REL(foreignrel))
appendStringInfo(buf, " %s%d", REL_ALIAS_PREFIX, rtindex);
- if (foreignrel->reloptkind == RELOPT_JOINREL)
+ if (IS_JOIN_REL(foreignrel))
{
List *ignore_conds = NIL;
@@ -2435,7 +2435,7 @@ deparseDirectDeleteSql(StringInfo buf, PlannerInfo *root,
if (additional_conds != NIL)
list_free_deep(additional_conds);
- if (foreignrel->reloptkind == RELOPT_JOINREL)
+ if (IS_JOIN_REL(foreignrel))
deparseExplicitTargetList(returningList, true, retrieved_attrs,
&context);
else
diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out
index 2eb21505fb0..918fce5e8df 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -10136,6 +10136,166 @@ SELECT t1.a, t2.b FROM fprt1 t1 INNER JOIN fprt2 t2 ON (t1.a = t2.b) WHERE t1.a
(4 rows)
reset enable_sort;
+-- test partition-wise DML
+EXPLAIN (COSTS OFF, VERBOSE)
+UPDATE fprt1 SET b=fprt1.b+1 FROM fprt2 WHERE fprt1.a = fprt2.b AND fprt2.a % 25 = 0;
+ QUERY PLAN
+----------------------------------------------------------------------------------------------------------------------------------------------
+ Update on public.fprt1
+ Foreign Update on public.ftprt1_p1 fprt1_1
+ Foreign Update on public.ftprt1_p2 fprt1_2
+ -> Append
+ -> Foreign Update
+ Remote SQL: UPDATE public.fprt1_p1 r3 SET b = (r3.b + 1) FROM public.fprt2_p1 r5 WHERE ((r3.a = r5.b)) AND (((r5.a % 25) = 0))
+ -> Foreign Update
+ Remote SQL: UPDATE public.fprt1_p2 r4 SET b = (r4.b + 1) FROM public.fprt2_p2 r6 WHERE ((r4.a = r6.b)) AND (((r6.a % 25) = 0))
+(8 rows)
+
+UPDATE fprt1 SET b=fprt1.b+1 FROM fprt2 WHERE fprt1.a = fprt2.b AND fprt2.a % 25 = 0;
+EXPLAIN (COSTS OFF, VERBOSE)
+UPDATE fprt1 SET b=(fprt1.a+fprt2.b)/2 FROM fprt2 WHERE fprt1.a = fprt2.b AND fprt2.a % 25 = 0;
+ QUERY PLAN
+-------------------------------------------------------------------------------------------------------------------------------------------------------
+ Update on public.fprt1
+ Foreign Update on public.ftprt1_p1 fprt1_1
+ Foreign Update on public.ftprt1_p2 fprt1_2
+ -> Append
+ -> Foreign Update
+ Remote SQL: UPDATE public.fprt1_p1 r3 SET b = ((r3.a + r5.b) / 2) FROM public.fprt2_p1 r5 WHERE ((r3.a = r5.b)) AND (((r5.a % 25) = 0))
+ -> Foreign Update
+ Remote SQL: UPDATE public.fprt1_p2 r4 SET b = ((r4.a + r6.b) / 2) FROM public.fprt2_p2 r6 WHERE ((r4.a = r6.b)) AND (((r6.a % 25) = 0))
+(8 rows)
+
+UPDATE fprt1 SET b=(fprt1.a+fprt2.b)/2 FROM fprt2 WHERE fprt1.a = fprt2.b AND fprt2.a % 25 = 0;
+-- returning whole row references
+EXPLAIN (COSTS OFF)
+UPDATE fprt1 SET b=fprt1.b+1 FROM fprt2 WHERE fprt1.a = fprt2.b AND fprt2.a % 25 = 0 RETURNING fprt1, fprt2;
+ QUERY PLAN
+---------------------------------------
+ Update on fprt1
+ Foreign Update on ftprt1_p1 fprt1_1
+ Foreign Update on ftprt1_p2 fprt1_2
+ -> Append
+ -> Foreign Update
+ -> Foreign Update
+(6 rows)
+
+UPDATE fprt1 SET b=fprt1.b+1 FROM fprt2 WHERE fprt1.a = fprt2.b AND fprt2.a % 25 = 0 RETURNING fprt1, fprt2;
+ fprt1 | fprt2
+----------------+----------------
+ (0,1,0000) | (0,0,0000)
+ (150,151,0003) | (150,150,0003)
+ (250,251,0005) | (250,250,0005)
+ (400,401,0008) | (400,400,0008)
+(4 rows)
+
+-- tableoids are returned correctly
+EXPLAIN (COSTS OFF)
+UPDATE fprt1 t1 SET b = t1.b + 1 FROM fprt2 t2 WHERE t1.a = t2.b AND t1.a % 25 = 0 RETURNING t2.tableoid::regclass;
+ QUERY PLAN
+------------------------------------
+ Update on fprt1 t1
+ Foreign Update on ftprt1_p1 t1_1
+ Foreign Update on ftprt1_p2 t1_2
+ -> Append
+ -> Foreign Update
+ -> Foreign Update
+(6 rows)
+
+UPDATE fprt1 t1 SET b = t1.b + 1 FROM fprt2 t2 WHERE t1.a = t2.b AND t1.a % 25 = 0 RETURNING t2.tableoid::regclass;
+ tableoid
+-----------
+ ftprt2_p1
+ ftprt2_p1
+ ftprt2_p2
+ ftprt2_p2
+(4 rows)
+
+-- join of several tables
+EXPLAIN (VERBOSE, COSTS OFF)
+UPDATE fprt1 t1 SET b = t1.b + 1 FROM fprt2 t2, fprt1 t3 WHERE t1.a = t2.b AND t2.b = t3.a AND t1.a % 25 = 0 RETURNING *;
+ QUERY PLAN
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ Update on public.fprt1 t1
+ Output: t1_1.a, t1_1.b, t1_1.c, t2.a, t2.b, t2.c, t3.a, t3.b, t3.c
+ Foreign Update on public.ftprt1_p1 t1_1
+ Foreign Update on public.ftprt1_p2 t1_2
+ -> Append
+ -> Foreign Update
+ Remote SQL: UPDATE public.fprt1_p1 r4 SET b = (r4.b + 1) FROM (public.fprt2_p1 r6 INNER JOIN public.fprt1_p1 r8 ON (TRUE)) WHERE ((r4.a = r8.a)) AND ((r4.a = r6.b)) AND (((r4.a % 25) = 0)) RETURNING r4.a, r4.b, r4.c, r6.a, r6.b, r6.c, r8.a, r8.b, r8.c
+ -> Foreign Update
+ Remote SQL: UPDATE public.fprt1_p2 r5 SET b = (r5.b + 1) FROM (public.fprt2_p2 r7 INNER JOIN public.fprt1_p2 r9 ON (TRUE)) WHERE ((r5.a = r9.a)) AND ((r5.a = r7.b)) AND (((r5.a % 25) = 0)) RETURNING r5.a, r5.b, r5.c, r7.a, r7.b, r7.c, r9.a, r9.b, r9.c
+(9 rows)
+
+UPDATE fprt1 t1 SET b = t1.b + 1 FROM fprt2 t2, fprt1 t3 WHERE t1.a = t2.b AND t2.b = t3.a AND t1.a % 25 = 0 RETURNING *;
+ a | b | c | a | b | c | a | b | c
+-----+-----+------+-----+-----+------+-----+-----+------
+ 0 | 3 | 0000 | 0 | 0 | 0000 | 0 | 2 | 0000
+ 150 | 153 | 0003 | 150 | 150 | 0003 | 150 | 152 | 0003
+ 250 | 253 | 0005 | 250 | 250 | 0005 | 250 | 252 | 0005
+ 400 | 403 | 0008 | 400 | 400 | 0008 | 400 | 402 | 0008
+(4 rows)
+
+-- left join
+EXPLAIN (VERBOSE, COSTS OFF)
+UPDATE fprt1 t1 SET b = t1.b + 1 FROM fprt2 t2 LEFT JOIN fprt1 t3 ON (t2.b = t3.a) WHERE t1.a = t2.b AND t1.a % 25 = 0 RETURNING *;
+ QUERY PLAN
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ Update on public.fprt1 t1
+ Output: t1_1.a, t1_1.b, t1_1.c, t2.a, t2.b, t2.c, t3.a, t3.b, t3.c
+ Foreign Update on public.ftprt1_p1 t1_1
+ Foreign Update on public.ftprt1_p2 t1_2
+ -> Append
+ -> Foreign Update
+ Remote SQL: UPDATE public.fprt1_p1 r5 SET b = (r5.b + 1) FROM (public.fprt2_p1 r7 LEFT JOIN public.fprt1_p1 r9 ON (((r7.b = r9.a)))) WHERE ((r5.a = r7.b)) AND (((r5.a % 25) = 0)) RETURNING r5.a, r5.b, r5.c, r7.a, r7.b, r7.c, r9.a, r9.b, r9.c
+ -> Foreign Update
+ Remote SQL: UPDATE public.fprt1_p2 r6 SET b = (r6.b + 1) FROM (public.fprt2_p2 r8 LEFT JOIN public.fprt1_p2 r10 ON (((r8.b = r10.a)))) WHERE ((r6.a = r8.b)) AND (((r6.a % 25) = 0)) RETURNING r6.a, r6.b, r6.c, r8.a, r8.b, r8.c, r10.a, r10.b, r10.c
+(9 rows)
+
+UPDATE fprt1 t1 SET b = t1.b + 1 FROM fprt2 t2 LEFT JOIN fprt1 t3 ON (t2.b = t3.a) WHERE t1.a = t2.b AND t1.a % 25 = 0 RETURNING *;
+ a | b | c | a | b | c | a | b | c
+-----+-----+------+-----+-----+------+-----+-----+------
+ 0 | 4 | 0000 | 0 | 0 | 0000 | 0 | 3 | 0000
+ 150 | 154 | 0003 | 150 | 150 | 0003 | 150 | 153 | 0003
+ 250 | 254 | 0005 | 250 | 250 | 0005 | 250 | 253 | 0005
+ 400 | 404 | 0008 | 400 | 400 | 0008 | 400 | 403 | 0008
+(4 rows)
+
+-- delete
+EXPLAIN (COSTS OFF)
+DELETE FROM fprt1 USING fprt2 WHERE fprt1.a = fprt2.b AND fprt2.a % 30 = 29;
+ QUERY PLAN
+---------------------------------------
+ Delete on fprt1
+ Foreign Delete on ftprt1_p1 fprt1_1
+ Foreign Delete on ftprt1_p2 fprt1_2
+ -> Append
+ -> Foreign Delete
+ -> Foreign Delete
+(6 rows)
+
+DELETE FROM fprt1 USING fprt2 WHERE fprt1.a = fprt2.b AND fprt2.a % 30 = 29;
+EXPLAIN (COSTS OFF)
+DELETE FROM fprt1 USING fprt2 WHERE fprt1.a = fprt2.b AND fprt2.a % 25 = 0 RETURNING *;
+ QUERY PLAN
+---------------------------------------
+ Delete on fprt1
+ Foreign Delete on ftprt1_p1 fprt1_1
+ Foreign Delete on ftprt1_p2 fprt1_2
+ -> Append
+ -> Foreign Delete
+ -> Foreign Delete
+(6 rows)
+
+DELETE FROM fprt1 USING fprt2 WHERE fprt1.a = fprt2.b AND fprt2.a % 25 = 0 RETURNING *;
+ a | b | c | a | b | c
+-----+-----+------+-----+-----+------
+ 0 | 4 | 0000 | 0 | 0 | 0000
+ 150 | 154 | 0003 | 150 | 150 | 0003
+ 250 | 254 | 0005 | 250 | 250 | 0005
+ 400 | 404 | 0008 | 400 | 400 | 0008
+(4 rows)
+
RESET enable_partitionwise_join;
-- ===================================================================
-- test partitionwise aggregates
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 90bf35fd90c..8caa3abaf48 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -245,6 +245,9 @@ typedef struct PgFdwDirectModifyState
AttrNumber oidAttno; /* attnum of input oid column */
bool hasSystemCols; /* are there system columns of resultRel? */
+ List *tidAttnos; /* attnos of output tid columns */
+ List *tidVarnos; /* varnos of output tid columns */
+
/* working memory context */
MemoryContext temp_cxt; /* context for per-tuple temporary data */
} PgFdwDirectModifyState;
@@ -2465,6 +2468,7 @@ postgresPlanDirectModify(PlannerInfo *root,
List *params_list = NIL;
List *returningList = NIL;
List *retrieved_attrs = NIL;
+ Relids child_relids = NULL;
/*
* Decide whether it is safe to modify a foreign table directly.
@@ -2496,6 +2500,12 @@ postgresPlanDirectModify(PlannerInfo *root,
foreignrel = find_join_rel(root, fscan->fs_relids);
/* We should have a rel for this foreign join. */
Assert(foreignrel);
+
+ /*
+ * If it's a child joinrel, record relids to translate.
+ */
+ if (IS_OTHER_REL(foreignrel))
+ child_relids = foreignrel->relids;
}
else
foreignrel = root->simple_rel_array[resultRelation];
@@ -2515,7 +2525,7 @@ postgresPlanDirectModify(PlannerInfo *root,
* The expressions of concern are the first N columns of the processed
* targetlist, where N is the length of the rel's update_colnos.
*/
- get_translated_update_targetlist(root, resultRelation,
+ get_translated_update_targetlist(root, resultRelation, child_relids,
&processed_tlist, &targetAttrs);
forboth(lc, processed_tlist, lc2, targetAttrs)
{
@@ -2568,8 +2578,17 @@ postgresPlanDirectModify(PlannerInfo *root,
* node below.
*/
if (fscan->scan.scanrelid == 0)
+ {
+ if (foreignrel->reloptkind == RELOPT_OTHER_JOINREL)
+ {
+ returningList = (List *) adjust_appendrel_attrs_multilevel(root,
+ (Node *) returningList,
+ foreignrel,
+ foreignrel->top_parent);
+ }
returningList = build_remote_returning(resultRelation, rel,
returningList);
+ }
}
/*
@@ -3244,7 +3263,7 @@ estimate_path_cost_size(PlannerInfo *root,
/* Shouldn't get here unless we have LIMIT */
Assert(fpextra->has_limit);
Assert(foreignrel->reloptkind == RELOPT_BASEREL ||
- foreignrel->reloptkind == RELOPT_JOINREL);
+ IS_JOIN_REL(foreignrel));
startup_cost += foreignrel->reltarget->cost.startup;
run_cost += foreignrel->reltarget->cost.per_tuple * rows;
}
@@ -4432,7 +4451,7 @@ build_remote_returning(Index rtindex, Relation rel, List *returningList)
Assert(returningList);
- vars = pull_var_clause((Node *) returningList, PVC_INCLUDE_PLACEHOLDERS);
+ vars = pull_var_clause((Node *) returningList, PVC_INCLUDE_PLACEHOLDERS | PVC_INCLUDE_CONVERTROWTYPES);
/*
* If there's a whole-row reference to the target relation, then we'll
@@ -4442,6 +4461,9 @@ build_remote_returning(Index rtindex, Relation rel, List *returningList)
{
Var *var = (Var *) lfirst(lc);
+ while (IsA(var, ConvertRowtypeExpr))
+ var = (Var *) (((ConvertRowtypeExpr *) var)->arg);
+
if (IsA(var, Var) &&
var->varno == rtindex &&
var->varattno == InvalidAttrNumber)
@@ -4496,6 +4518,29 @@ build_remote_returning(Index rtindex, Relation rel, List *returningList)
var->varattno != SelfItemPointerAttributeNumber)
continue; /* don't need it */
+ /*
+ * Also no need in whole-row references to the target relation under
+ * ConvertRowtypeExpr
+ */
+ if (IsA(var, ConvertRowtypeExpr))
+ {
+ ConvertRowtypeExpr *cre = (ConvertRowtypeExpr *) var;
+ Var *cre_var;
+
+ while (IsA(cre->arg, ConvertRowtypeExpr))
+ cre = (ConvertRowtypeExpr *) cre->arg;
+
+
+ cre_var = (Var *) cre->arg;
+
+ if (IsA(cre_var, Var) &&
+ cre_var->varno == rtindex)
+ {
+ Assert(cre_var->varattno == InvalidAttrNumber);
+ continue;
+ }
+ }
+
if (tlist_member((Expr *) var, tlist))
continue; /* already got it */
@@ -4701,15 +4746,26 @@ init_returning_filter(PgFdwDirectModifyState *dmstate,
palloc0(resultTupType->natts * sizeof(AttrNumber));
dmstate->ctidAttno = dmstate->oidAttno = 0;
+ dmstate->tidAttnos = dmstate->tidVarnos = NIL;
i = 1;
dmstate->hasSystemCols = false;
foreach(lc, fdw_scan_tlist)
{
TargetEntry *tle = (TargetEntry *) lfirst(lc);
- Var *var = (Var *) tle->expr;
+ Expr *expr;
+ Var *var;
+
+ expr = tle->expr;
+
+ while (IsA(expr, ConvertRowtypeExpr))
+ {
+ ConvertRowtypeExpr *cre = castNode(ConvertRowtypeExpr, expr);
- Assert(IsA(var, Var));
+ expr = cre->arg;
+ }
+
+ var = castNode(Var, expr);
/*
* If the Var is a column of the target relation to be retrieved from
@@ -4738,10 +4794,15 @@ init_returning_filter(PgFdwDirectModifyState *dmstate,
* relation either.
*/
Assert(attrno > 0);
-
dmstate->attnoMap[attrno - 1] = i;
}
}
+ /* Record tableoid attributes and corresponding varnos */
+ if (var->varattno == TableOidAttributeNumber)
+ {
+ dmstate->tidAttnos = lappend_int(dmstate->tidAttnos, i);
+ dmstate->tidVarnos = lappend_int(dmstate->tidVarnos, var->varno);
+ }
i++;
}
}
@@ -4762,6 +4823,8 @@ apply_returning_filter(PgFdwDirectModifyState *dmstate,
Datum *old_values;
bool *old_isnull;
int i;
+ ListCell *lc1,
+ *lc2;
/*
* Use the return tuple slot as a place to store the result tuple.
@@ -4801,6 +4864,29 @@ apply_returning_filter(PgFdwDirectModifyState *dmstate,
}
}
+ /*
+ * Set tableoid attributes
+ */
+ forboth(lc1, dmstate->tidAttnos, lc2, dmstate->tidVarnos)
+ {
+ AttrNumber attno;
+ Index varno;
+ RangeTblEntry *rte;
+
+ attno = lfirst_int(lc1);
+ varno = lfirst_int(lc2);
+
+ rte = list_nth(estate->es_range_table, varno - 1);
+ Assert(rte->rtekind == RTE_RELATION);
+ Assert(rte->relkind == RELKIND_FOREIGN_TABLE);
+
+ /* Attributes numbering in init_returning_filter() starts with 1 */
+ Assert(attno >= 1);
+
+ slot->tts_values[attno - 1] = ObjectIdGetDatum(rte->relid);
+ slot->tts_isnull[attno - 1] = false;
+ }
+
/*
* Build the virtual tuple.
*/
diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql
index 36ad85d4af8..3c719aaaf8f 100644
--- a/contrib/postgres_fdw/sql/postgres_fdw.sql
+++ b/contrib/postgres_fdw/sql/postgres_fdw.sql
@@ -3156,6 +3156,44 @@ SELECT t1.a, t2.b FROM fprt1 t1 INNER JOIN fprt2 t2 ON (t1.a = t2.b) WHERE t1.a
SELECT t1.a, t2.b FROM fprt1 t1 INNER JOIN fprt2 t2 ON (t1.a = t2.b) WHERE t1.a % 25 = 0 ORDER BY t2.a FOR UPDATE OF t1;
reset enable_sort;
+-- test partition-wise DML
+EXPLAIN (COSTS OFF, VERBOSE)
+UPDATE fprt1 SET b=fprt1.b+1 FROM fprt2 WHERE fprt1.a = fprt2.b AND fprt2.a % 25 = 0;
+UPDATE fprt1 SET b=fprt1.b+1 FROM fprt2 WHERE fprt1.a = fprt2.b AND fprt2.a % 25 = 0;
+
+EXPLAIN (COSTS OFF, VERBOSE)
+UPDATE fprt1 SET b=(fprt1.a+fprt2.b)/2 FROM fprt2 WHERE fprt1.a = fprt2.b AND fprt2.a % 25 = 0;
+UPDATE fprt1 SET b=(fprt1.a+fprt2.b)/2 FROM fprt2 WHERE fprt1.a = fprt2.b AND fprt2.a % 25 = 0;
+
+-- returning whole row references
+EXPLAIN (COSTS OFF)
+UPDATE fprt1 SET b=fprt1.b+1 FROM fprt2 WHERE fprt1.a = fprt2.b AND fprt2.a % 25 = 0 RETURNING fprt1, fprt2;
+UPDATE fprt1 SET b=fprt1.b+1 FROM fprt2 WHERE fprt1.a = fprt2.b AND fprt2.a % 25 = 0 RETURNING fprt1, fprt2;
+
+-- tableoids are returned correctly
+EXPLAIN (COSTS OFF)
+UPDATE fprt1 t1 SET b = t1.b + 1 FROM fprt2 t2 WHERE t1.a = t2.b AND t1.a % 25 = 0 RETURNING t2.tableoid::regclass;
+UPDATE fprt1 t1 SET b = t1.b + 1 FROM fprt2 t2 WHERE t1.a = t2.b AND t1.a % 25 = 0 RETURNING t2.tableoid::regclass;
+
+-- join of several tables
+EXPLAIN (VERBOSE, COSTS OFF)
+UPDATE fprt1 t1 SET b = t1.b + 1 FROM fprt2 t2, fprt1 t3 WHERE t1.a = t2.b AND t2.b = t3.a AND t1.a % 25 = 0 RETURNING *;
+UPDATE fprt1 t1 SET b = t1.b + 1 FROM fprt2 t2, fprt1 t3 WHERE t1.a = t2.b AND t2.b = t3.a AND t1.a % 25 = 0 RETURNING *;
+
+-- left join
+EXPLAIN (VERBOSE, COSTS OFF)
+UPDATE fprt1 t1 SET b = t1.b + 1 FROM fprt2 t2 LEFT JOIN fprt1 t3 ON (t2.b = t3.a) WHERE t1.a = t2.b AND t1.a % 25 = 0 RETURNING *;
+UPDATE fprt1 t1 SET b = t1.b + 1 FROM fprt2 t2 LEFT JOIN fprt1 t3 ON (t2.b = t3.a) WHERE t1.a = t2.b AND t1.a % 25 = 0 RETURNING *;
+
+-- delete
+EXPLAIN (COSTS OFF)
+DELETE FROM fprt1 USING fprt2 WHERE fprt1.a = fprt2.b AND fprt2.a % 30 = 29;
+DELETE FROM fprt1 USING fprt2 WHERE fprt1.a = fprt2.b AND fprt2.a % 30 = 29;
+
+EXPLAIN (COSTS OFF)
+DELETE FROM fprt1 USING fprt2 WHERE fprt1.a = fprt2.b AND fprt2.a % 25 = 0 RETURNING *;
+DELETE FROM fprt1 USING fprt2 WHERE fprt1.a = fprt2.b AND fprt2.a % 25 = 0 RETURNING *;
+
RESET enable_partitionwise_join;
diff --git a/src/backend/optimizer/util/appendinfo.c b/src/backend/optimizer/util/appendinfo.c
index 49897226371..29be8af6cc8 100644
--- a/src/backend/optimizer/util/appendinfo.c
+++ b/src/backend/optimizer/util/appendinfo.c
@@ -691,7 +691,7 @@ adjust_inherited_attnums_multilevel(PlannerInfo *root, List *attnums,
* relied on for this purpose.)
*/
void
-get_translated_update_targetlist(PlannerInfo *root, Index relid,
+get_translated_update_targetlist(PlannerInfo *root, Index relid, Relids child_joinrel_relids,
List **processed_tlist, List **update_colnos)
{
/* This is pretty meaningless for commands other than UPDATE. */
@@ -709,11 +709,31 @@ get_translated_update_targetlist(PlannerInfo *root, Index relid,
else
{
Assert(bms_is_member(relid, root->all_result_relids));
- *processed_tlist = (List *)
- adjust_appendrel_attrs_multilevel(root,
- (Node *) root->processed_tlist,
- find_base_rel(root, relid),
- find_base_rel(root, root->parse->resultRelation));
+
+ /*
+ * UPDATE targetlist corresponds to SET clause and so can contain
+ * arbitrary expressions, including those which contain references not
+ * only to result relation, but also to other vars. So in case of join
+ * we should also translate these vars to their parents.
+ */
+ if (bms_is_empty(child_joinrel_relids))
+ {
+ *processed_tlist = (List *)
+ adjust_appendrel_attrs_multilevel(root,
+ (Node *) root->processed_tlist,
+ find_base_rel(root, relid),
+ find_base_rel(root, root->parse->resultRelation));
+ }
+ else
+ {
+ RelOptInfo *child_joinrel = find_join_rel(root, child_joinrel_relids);
+
+ *processed_tlist = (List *)
+ adjust_appendrel_attrs_multilevel(root,
+ (Node *) root->processed_tlist,
+ child_joinrel,
+ child_joinrel->top_parent);
+ }
if (update_colnos)
*update_colnos =
adjust_inherited_attnums_multilevel(root, root->update_colnos,
diff --git a/src/include/optimizer/appendinfo.h b/src/include/optimizer/appendinfo.h
index cc12c9c743d..4f73bb97a50 100644
--- a/src/include/optimizer/appendinfo.h
+++ b/src/include/optimizer/appendinfo.h
@@ -36,6 +36,7 @@ extern List *adjust_inherited_attnums_multilevel(PlannerInfo *root,
Index child_relid,
Index top_parent_relid);
extern void get_translated_update_targetlist(PlannerInfo *root, Index relid,
+ Relids child_joinrel_relids,
List **processed_tlist,
List **update_colnos);
extern AppendRelInfo **find_appinfos_by_relids(PlannerInfo *root,
--
2.34.1