Amit Langote писал 2021-06-01 15:47:

Perhaps, we can get away with adding the wholerow Var with the target
relation's reltype when the target foreign table is not a "child"
relation, but the root target relation itself.  Maybe like the
attached?


Hi.

I think the patch fixes this issue, but it still preserves chances to get RECORD in fetch_more_data()
(at least with combination with asymmetric partition-wise join).

What about the following patch?
--
Best regards,
Alexander Pyhalov,
Postgres Professional
From 806a9b4fbf376e8fa75669229c8401ea76dd1cb2 Mon Sep 17 00:00:00 2001
From: Alexander Pyhalov <a.pyha...@postgrespro.ru>
Date: Tue, 1 Jun 2021 12:00:08 +0300
Subject: [PATCH] [PGPRO-4769] Fix foreign update

Tags: shardman
---
 .../postgres_fdw/expected/postgres_fdw.out    | 66 +++++++++----------
 src/backend/optimizer/util/appendinfo.c       | 53 ++++++++++++---
 src/include/optimizer/appendinfo.h            |  2 +
 3 files changed, 78 insertions(+), 43 deletions(-)

diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out
index 7df30010f25..d424a1a5365 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -7258,19 +7258,19 @@ select * from bar where f1 in (select f1 from foo) for share;
 -- Check UPDATE with inherited target and an inherited source table
 explain (verbose, costs off)
 update bar set f2 = f2 + 100 where f1 in (select f1 from foo);
-                                              QUERY PLAN                                               
--------------------------------------------------------------------------------------------------------
+                                             QUERY PLAN                                              
+-----------------------------------------------------------------------------------------------------
  Update on public.bar
    Update on public.bar bar_1
    Foreign Update on public.bar2 bar_2
      Remote SQL: UPDATE public.loct2 SET f2 = $2 WHERE ctid = $1
    ->  Hash Join
-         Output: (bar.f2 + 100), foo.ctid, bar.tableoid, bar.ctid, (NULL::record), foo.*, foo.tableoid
+         Output: (bar.f2 + 100), foo.ctid, bar.tableoid, bar.ctid, (NULL::bar2), foo.*, foo.tableoid
          Inner Unique: true
          Hash Cond: (bar.f1 = foo.f1)
          ->  Append
                ->  Seq Scan on public.bar bar_1
-                     Output: bar_1.f2, bar_1.f1, bar_1.tableoid, bar_1.ctid, NULL::record
+                     Output: bar_1.f2, bar_1.f1, bar_1.tableoid, bar_1.ctid, NULL::bar2
                ->  Foreign Scan on public.bar2 bar_2
                      Output: bar_2.f2, bar_2.f1, bar_2.tableoid, bar_2.ctid, bar_2.*
                      Remote SQL: SELECT f1, f2, f3, ctid FROM public.loct2 FOR UPDATE
@@ -7305,21 +7305,21 @@ update bar set f2 = f2 + 100
 from
   ( select f1 from foo union all select f1+3 from foo ) ss
 where bar.f1 = ss.f1;
-                                           QUERY PLAN                                           
-------------------------------------------------------------------------------------------------
+                                          QUERY PLAN                                          
+----------------------------------------------------------------------------------------------
  Update on public.bar
    Update on public.bar bar_1
    Foreign Update on public.bar2 bar_2
      Remote SQL: UPDATE public.loct2 SET f2 = $2 WHERE ctid = $1
    ->  Merge Join
-         Output: (bar.f2 + 100), (ROW(foo.f1)), bar.tableoid, bar.ctid, (NULL::record)
+         Output: (bar.f2 + 100), (ROW(foo.f1)), bar.tableoid, bar.ctid, (NULL::bar2)
          Merge Cond: (bar.f1 = foo.f1)
          ->  Sort
-               Output: bar.f2, bar.f1, bar.tableoid, bar.ctid, (NULL::record)
+               Output: bar.f2, bar.f1, bar.tableoid, bar.ctid, (NULL::bar2)
                Sort Key: bar.f1
                ->  Append
                      ->  Seq Scan on public.bar bar_1
-                           Output: bar_1.f2, bar_1.f1, bar_1.tableoid, bar_1.ctid, NULL::record
+                           Output: bar_1.f2, bar_1.f1, bar_1.tableoid, bar_1.ctid, NULL::bar2
                      ->  Foreign Scan on public.bar2 bar_2
                            Output: bar_2.f2, bar_2.f1, bar_2.tableoid, bar_2.ctid, bar_2.*
                            Remote SQL: SELECT f1, f2, f3, ctid FROM public.loct2 FOR UPDATE
@@ -7496,10 +7496,10 @@ update bar set f2 = f2 + 100 returning *;
    Update on public.bar bar_1
    Foreign Update on public.bar2 bar_2
    ->  Result
-         Output: (bar.f2 + 100), bar.tableoid, bar.ctid, (NULL::record)
+         Output: (bar.f2 + 100), bar.tableoid, bar.ctid, (NULL::bar2)
          ->  Append
                ->  Seq Scan on public.bar bar_1
-                     Output: bar_1.f2, bar_1.tableoid, bar_1.ctid, NULL::record
+                     Output: bar_1.f2, bar_1.tableoid, bar_1.ctid, NULL::bar2
                ->  Foreign Update on public.bar2 bar_2
                      Remote SQL: UPDATE public.loct2 SET f2 = (f2 + 100) RETURNING f1, f2
 (11 rows)
@@ -7531,10 +7531,10 @@ update bar set f2 = f2 + 100;
    Foreign Update on public.bar2 bar_2
      Remote SQL: UPDATE public.loct2 SET f1 = $2, f2 = $3, f3 = $4 WHERE ctid = $1 RETURNING f1, f2, f3
    ->  Result
-         Output: (bar.f2 + 100), bar.tableoid, bar.ctid, (NULL::record)
+         Output: (bar.f2 + 100), bar.tableoid, bar.ctid, (NULL::bar2)
          ->  Append
                ->  Seq Scan on public.bar bar_1
-                     Output: bar_1.f2, bar_1.tableoid, bar_1.ctid, NULL::record
+                     Output: bar_1.f2, bar_1.tableoid, bar_1.ctid, NULL::bar2
                ->  Foreign Scan on public.bar2 bar_2
                      Output: bar_2.f2, bar_2.tableoid, bar_2.ctid, bar_2.*
                      Remote SQL: SELECT f1, f2, f3, ctid FROM public.loct2 FOR UPDATE
@@ -7563,7 +7563,7 @@ delete from bar where f2 < 400;
      Remote SQL: DELETE FROM public.loct2 WHERE ctid = $1 RETURNING f1, f2, f3
    ->  Append
          ->  Seq Scan on public.bar bar_1
-               Output: bar_1.tableoid, bar_1.ctid, NULL::record
+               Output: bar_1.tableoid, bar_1.ctid, NULL::bar2
                Filter: (bar_1.f2 < 400)
          ->  Foreign Scan on public.bar2 bar_2
                Output: bar_2.tableoid, bar_2.ctid, bar_2.*
@@ -7599,19 +7599,19 @@ analyze remt1;
 analyze remt2;
 explain (verbose, costs off)
 update parent set b = parent.b || remt2.b from remt2 where parent.a = remt2.a returning *;
-                                                   QUERY PLAN                                                   
-----------------------------------------------------------------------------------------------------------------
+                                                  QUERY PLAN                                                   
+---------------------------------------------------------------------------------------------------------------
  Update on public.parent
    Output: parent_1.a, parent_1.b, remt2.a, remt2.b
    Update on public.parent parent_1
    Foreign Update on public.remt1 parent_2
      Remote SQL: UPDATE public.loct1 SET b = $2 WHERE ctid = $1 RETURNING a, b
    ->  Nested Loop
-         Output: (parent.b || remt2.b), remt2.*, remt2.a, remt2.b, parent.tableoid, parent.ctid, (NULL::record)
+         Output: (parent.b || remt2.b), remt2.*, remt2.a, remt2.b, parent.tableoid, parent.ctid, (NULL::remt1)
          Join Filter: (parent.a = remt2.a)
          ->  Append
                ->  Seq Scan on public.parent parent_1
-                     Output: parent_1.b, parent_1.a, parent_1.tableoid, parent_1.ctid, NULL::record
+                     Output: parent_1.b, parent_1.a, parent_1.tableoid, parent_1.ctid, NULL::remt1
                ->  Foreign Scan on public.remt1 parent_2
                      Output: parent_2.b, parent_2.a, parent_2.tableoid, parent_2.ctid, parent_2.*
                      Remote SQL: SELECT a, b, ctid FROM public.loct1 FOR UPDATE
@@ -7871,7 +7871,7 @@ update utrtest set a = 1 where a = 1 or a = 2 returning *;
          ->  Foreign Update on public.remp utrtest_1
                Remote SQL: UPDATE public.loct SET a = 1 WHERE (((a = 1) OR (a = 2))) RETURNING a, b
          ->  Seq Scan on public.locp utrtest_2
-               Output: 1, utrtest_2.tableoid, utrtest_2.ctid, NULL::record
+               Output: 1, utrtest_2.tableoid, utrtest_2.ctid, NULL::utrtest
                Filter: ((utrtest_2.a = 1) OR (utrtest_2.a = 2))
 (10 rows)
 
@@ -7910,8 +7910,8 @@ insert into utrtest values (2, 'qux');
 -- with a direct modification plan
 explain (verbose, costs off)
 update utrtest set a = 1 returning *;
-                                QUERY PLAN                                 
----------------------------------------------------------------------------
+                                 QUERY PLAN                                 
+----------------------------------------------------------------------------
  Update on public.utrtest
    Output: utrtest_1.a, utrtest_1.b
    Foreign Update on public.remp utrtest_1
@@ -7920,7 +7920,7 @@ update utrtest set a = 1 returning *;
          ->  Foreign Update on public.remp utrtest_1
                Remote SQL: UPDATE public.loct SET a = 1 RETURNING a, b
          ->  Seq Scan on public.locp utrtest_2
-               Output: 1, utrtest_2.tableoid, utrtest_2.ctid, NULL::record
+               Output: 1, utrtest_2.tableoid, utrtest_2.ctid, NULL::utrtest
 (9 rows)
 
 update utrtest set a = 1 returning *;
@@ -7946,7 +7946,7 @@ update utrtest set a = 1 from (values (1), (2)) s(x) where a = s.x returning *;
                      Output: utrtest_1.a, utrtest_1.tableoid, utrtest_1.ctid, utrtest_1.*
                      Remote SQL: SELECT a, b, ctid FROM public.loct FOR UPDATE
                ->  Seq Scan on public.locp utrtest_2
-                     Output: utrtest_2.a, utrtest_2.tableoid, utrtest_2.ctid, NULL::record
+                     Output: utrtest_2.a, utrtest_2.tableoid, utrtest_2.ctid, NULL::utrtest
          ->  Hash
                Output: "*VALUES*".*, "*VALUES*".column1
                ->  Values Scan on "*VALUES*"
@@ -7970,15 +7970,15 @@ insert into utrtest values (3, 'xyzzy');
 -- with a direct modification plan
 explain (verbose, costs off)
 update utrtest set a = 3 returning *;
-                                QUERY PLAN                                 
----------------------------------------------------------------------------
+                                 QUERY PLAN                                 
+----------------------------------------------------------------------------
  Update on public.utrtest
    Output: utrtest_1.a, utrtest_1.b
    Update on public.locp utrtest_1
    Foreign Update on public.remp utrtest_2
    ->  Append
          ->  Seq Scan on public.locp utrtest_1
-               Output: 3, utrtest_1.tableoid, utrtest_1.ctid, NULL::record
+               Output: 3, utrtest_1.tableoid, utrtest_1.ctid, NULL::utrtest
          ->  Foreign Update on public.remp utrtest_2
                Remote SQL: UPDATE public.loct SET a = 3 RETURNING a, b
 (9 rows)
@@ -7988,19 +7988,19 @@ ERROR:  cannot route tuples into foreign table to be updated "remp"
 -- with a non-direct modification plan
 explain (verbose, costs off)
 update utrtest set a = 3 from (values (2), (3)) s(x) where a = s.x returning *;
-                                             QUERY PLAN                                              
------------------------------------------------------------------------------------------------------
+                                              QUERY PLAN                                              
+------------------------------------------------------------------------------------------------------
  Update on public.utrtest
    Output: utrtest_1.a, utrtest_1.b, "*VALUES*".column1
    Update on public.locp utrtest_1
    Foreign Update on public.remp utrtest_2
      Remote SQL: UPDATE public.loct SET a = $2 WHERE ctid = $1 RETURNING a, b
    ->  Hash Join
-         Output: 3, "*VALUES*".*, "*VALUES*".column1, utrtest.tableoid, utrtest.ctid, (NULL::record)
+         Output: 3, "*VALUES*".*, "*VALUES*".column1, utrtest.tableoid, utrtest.ctid, (NULL::utrtest)
          Hash Cond: (utrtest.a = "*VALUES*".column1)
          ->  Append
                ->  Seq Scan on public.locp utrtest_1
-                     Output: utrtest_1.a, utrtest_1.tableoid, utrtest_1.ctid, NULL::record
+                     Output: utrtest_1.a, utrtest_1.tableoid, utrtest_1.ctid, NULL::utrtest
                ->  Foreign Scan on public.remp utrtest_2
                      Output: utrtest_2.a, utrtest_2.tableoid, utrtest_2.ctid, utrtest_2.*
                      Remote SQL: SELECT a, b, ctid FROM public.loct FOR UPDATE
@@ -10157,8 +10157,8 @@ RESET enable_hashjoin;
 -- Test that UPDATE/DELETE with inherited target works with async_capable enabled
 EXPLAIN (VERBOSE, COSTS OFF)
 UPDATE async_pt SET c = c || c WHERE b = 0 RETURNING *;
-                                                QUERY PLAN                                                
-----------------------------------------------------------------------------------------------------------
+                                                 QUERY PLAN                                                 
+------------------------------------------------------------------------------------------------------------
  Update on public.async_pt
    Output: async_pt_1.a, async_pt_1.b, async_pt_1.c
    Foreign Update on public.async_p1 async_pt_1
@@ -10170,7 +10170,7 @@ UPDATE async_pt SET c = c || c WHERE b = 0 RETURNING *;
          ->  Foreign Update on public.async_p2 async_pt_2
                Remote SQL: UPDATE public.base_tbl2 SET c = (c || c) WHERE ((b = 0)) RETURNING a, b, c
          ->  Seq Scan on public.async_p3 async_pt_3
-               Output: (async_pt_3.c || async_pt_3.c), async_pt_3.tableoid, async_pt_3.ctid, NULL::record
+               Output: (async_pt_3.c || async_pt_3.c), async_pt_3.tableoid, async_pt_3.ctid, NULL::async_pt
                Filter: (async_pt_3.b = 0)
 (13 rows)
 
diff --git a/src/backend/optimizer/util/appendinfo.c b/src/backend/optimizer/util/appendinfo.c
index af46f581ac1..6d440af41d2 100644
--- a/src/backend/optimizer/util/appendinfo.c
+++ b/src/backend/optimizer/util/appendinfo.c
@@ -16,6 +16,7 @@
 
 #include "access/htup_details.h"
 #include "access/table.h"
+#include "catalog/partition.h"
 #include "foreign/fdwapi.h"
 #include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
@@ -752,7 +753,7 @@ find_appinfos_by_relids(PlannerInfo *root, Relids relids, int *nappinfos)
  *
  * The Var must be equal(), aside from varno, to any other row-identity
  * column with the same rowid_name.  Thus, for example, "wholerow"
- * row identities had better use vartype == RECORDOID.
+ * row identities had better use vartype of the base relation.
  *
  * rtindex is currently redundant with rowid_var->varno, but we specify
  * it as a separate parameter in case this is ever generalized to support
@@ -846,6 +847,46 @@ add_row_identity_var(PlannerInfo *root, Var *orig_var,
 	root->processed_tlist = lappend(root->processed_tlist, tle);
 }
 
+/*
+ * add_row_identity_var_wholerow
+ *		Wrapper over add_row_identity_var() to register a "wholerow"
+ *		row-identity column
+ *
+ * This has been made into a function unlike for other row-identity Vars,
+ * because the vartype that must be assigned to the Var differs based on
+ * whether it's being added for the root or for a child target relation.
+ * FDWs should call this for example instead of making the Var by themselves.
+ */
+void
+add_row_identity_var_wholerow(PlannerInfo *root, Index rtindex,
+							  Relation target_relation)
+{
+	Oid			reloid;
+	Relation	r;
+	Var		   *var;
+
+	r = target_relation;
+	reloid = RelationGetRelid(target_relation);
+
+	while (RelationGetForm(r)->relispartition)
+	{
+		reloid = get_partition_parent(reloid, false);
+		if (r != target_relation)
+			table_close(r, AccessShareLock);
+		r = table_open(reloid, AccessShareLock);
+	}
+
+	var = makeVar(rtindex,
+				  InvalidAttrNumber,
+				  RelationGetForm(r)->reltype,
+				  -1,
+				  InvalidOid,
+				  0);
+	if (r != target_relation)
+		table_close(r, AccessShareLock);
+	add_row_identity_var(root, var, rtindex, "wholerow");
+}
+
 /*
  * add_row_identity_columns
  *
@@ -909,15 +950,7 @@ add_row_identity_columns(PlannerInfo *root, Index rtindex,
 			(target_relation->trigdesc &&
 			 (target_relation->trigdesc->trig_delete_after_row ||
 			  target_relation->trigdesc->trig_delete_before_row)))
-		{
-			var = makeVar(rtindex,
-						  InvalidAttrNumber,
-						  RECORDOID,
-						  -1,
-						  InvalidOid,
-						  0);
-			add_row_identity_var(root, var, rtindex, "wholerow");
-		}
+			add_row_identity_var_wholerow(root, rtindex, target_relation);
 	}
 }
 
diff --git a/src/include/optimizer/appendinfo.h b/src/include/optimizer/appendinfo.h
index 39d04d9cc02..97442a4ee17 100644
--- a/src/include/optimizer/appendinfo.h
+++ b/src/include/optimizer/appendinfo.h
@@ -42,6 +42,8 @@ extern AppendRelInfo **find_appinfos_by_relids(PlannerInfo *root,
 											   Relids relids, int *nappinfos);
 extern void add_row_identity_var(PlannerInfo *root, Var *rowid_var,
 								 Index rtindex, const char *rowid_name);
+extern void add_row_identity_var_wholerow(PlannerInfo *root, Index rtindex,
+							  Relation target_relation);
 extern void add_row_identity_columns(PlannerInfo *root, Index rtindex,
 									 RangeTblEntry *target_rte,
 									 Relation target_relation);
-- 
2.25.1

Reply via email to