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