v31-0002-Add-rowmark-junk-targetlist-entries-in-query_pla.patch

application/octet-stream

Filename: v31-0002-Add-rowmark-junk-targetlist-entries-in-query_pla.patch
Type: application/octet-stream
Part: 3
Message: Re: speeding up planning with partitions

Patch

Format: format-patch
Series: patch v31-0002
Subject: Add rowmark "junk" targetlist entries in query_planner
File+
contrib/postgres_fdw/expected/postgres_fdw.out 38 38
src/backend/optimizer/plan/planagg.c 1 1
src/backend/optimizer/plan/planmain.c 25 3
src/backend/optimizer/plan/planner.c 11 10
src/backend/optimizer/prep/preptlist.c 86 66
src/include/optimizer/planmain.h 1 1
src/include/optimizer/prep.h 2 0
src/test/regress/expected/with.out 4 4
From d6ee2fa450d0fe705f9cf0fcdd87726822203415 Mon Sep 17 00:00:00 2001
From: amit <amitlangote09@gmail.com>
Date: Tue, 12 Mar 2019 10:47:37 +0900
Subject: [PATCH v31 2/8] Add rowmark "junk" targetlist entries in
 query_planner

We'd like to postpone adding inheritance children to query until
query_planner has finished distributing quals to individual
baserels, which means also delaying adding child row marks.  As
things stand today, which "junk" Vars to add to the targetlist for
a given parent row mark is not clear until alll the child row marks
have been seen.  So, we must delay adding "junk" Vars until the
children are added.

Since this changes the order in which expressions are added to the
individual baserels' targetlists, so does the order of expressions
in the Paths to scan them and Paths built on top of them.  That is
why there are regresion test expected output changes.
---
 .../postgres_fdw/expected/postgres_fdw.out    |  76 ++++-----
 src/backend/optimizer/plan/planagg.c          |   2 +-
 src/backend/optimizer/plan/planmain.c         |  28 +++-
 src/backend/optimizer/plan/planner.c          |  21 +--
 src/backend/optimizer/prep/preptlist.c        | 152 ++++++++++--------
 src/include/optimizer/planmain.h              |   2 +-
 src/include/optimizer/prep.h                  |   2 +
 src/test/regress/expected/with.out            |   8 +-
 8 files changed, 168 insertions(+), 123 deletions(-)

diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out
index 42108bd3d4..c912e95fe1 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -5577,25 +5577,25 @@ UPDATE ft2 SET c3 = 'baz'
    Output: ft2.c1, ft2.c2, ft2.c3, ft2.c4, ft2.c5, ft2.c6, ft2.c7, ft2.c8, ft4.c1, ft4.c2, ft4.c3, ft5.c1, ft5.c2, ft5.c3
    Remote SQL: UPDATE "S 1"."T 1" SET c3 = $2 WHERE ctid = $1 RETURNING "C 1", c2, c3, c4, c5, c6, c7, c8
    ->  Nested Loop
-         Output: ft2.c1, ft2.c2, NULL::integer, 'baz'::text, ft2.c4, ft2.c5, ft2.c6, ft2.c7, ft2.c8, ft2.ctid, ft4.*, ft5.*, ft4.c1, ft4.c2, ft4.c3, ft5.c1, ft5.c2, ft5.c3
+         Output: ft2.c1, ft2.c2, NULL::integer, 'baz'::text, ft2.c4, ft2.c5, ft2.c6, ft2.c7, ft2.c8, ft2.ctid, ft4.c1, ft4.c2, ft4.c3, ft5.c1, ft5.c2, ft5.c3, ft4.*, ft5.*
          Join Filter: (ft2.c2 === ft4.c1)
          ->  Foreign Scan on public.ft2
                Output: ft2.c1, ft2.c2, ft2.c4, ft2.c5, ft2.c6, ft2.c7, ft2.c8, ft2.ctid
                Remote SQL: SELECT "C 1", c2, c4, c5, c6, c7, c8, ctid FROM "S 1"."T 1" WHERE (("C 1" > 2000)) FOR UPDATE
          ->  Foreign Scan
-               Output: ft4.*, ft4.c1, ft4.c2, ft4.c3, ft5.*, ft5.c1, ft5.c2, ft5.c3
+               Output: ft4.c1, ft4.c2, ft4.c3, ft4.*, ft5.c1, ft5.c2, ft5.c3, ft5.*
                Relations: (public.ft4) INNER JOIN (public.ft5)
-               Remote SQL: SELECT CASE WHEN (r2.*)::text IS NOT NULL THEN ROW(r2.c1, r2.c2, r2.c3) END, r2.c1, r2.c2, r2.c3, CASE WHEN (r3.*)::text IS NOT NULL THEN ROW(r3.c1, r3.c2, r3.c3) END, r3.c1, r3.c2, r3.c3 FROM ("S 1"."T 3" r2 INNER JOIN "S 1"."T 4" r3 ON (((r2.c1 = r3.c1))))
+               Remote SQL: SELECT r2.c1, r2.c2, r2.c3, CASE WHEN (r2.*)::text IS NOT NULL THEN ROW(r2.c1, r2.c2, r2.c3) END, r3.c1, r3.c2, r3.c3, CASE WHEN (r3.*)::text IS NOT NULL THEN ROW(r3.c1, r3.c2, r3.c3) END FROM ("S 1"."T 3" r2 INNER JOIN "S 1"."T 4" r3 ON (((r2.c1 = r3.c1))))
                ->  Hash Join
-                     Output: ft4.*, ft4.c1, ft4.c2, ft4.c3, ft5.*, ft5.c1, ft5.c2, ft5.c3
+                     Output: ft4.c1, ft4.c2, ft4.c3, ft4.*, ft5.c1, ft5.c2, ft5.c3, ft5.*
                      Hash Cond: (ft4.c1 = ft5.c1)
                      ->  Foreign Scan on public.ft4
-                           Output: ft4.*, ft4.c1, ft4.c2, ft4.c3
+                           Output: ft4.c1, ft4.c2, ft4.c3, ft4.*
                            Remote SQL: SELECT c1, c2, c3 FROM "S 1"."T 3"
                      ->  Hash
-                           Output: ft5.*, ft5.c1, ft5.c2, ft5.c3
+                           Output: ft5.c1, ft5.c2, ft5.c3, ft5.*
                            ->  Foreign Scan on public.ft5
-                                 Output: ft5.*, ft5.c1, ft5.c2, ft5.c3
+                                 Output: ft5.c1, ft5.c2, ft5.c3, ft5.*
                                  Remote SQL: SELECT c1, c2, c3 FROM "S 1"."T 4"
 (24 rows)
 
@@ -5626,16 +5626,16 @@ DELETE FROM ft2
          ->  Nested Loop
                Output: ft2.ctid, ft4.*, ft5.*, ft4.c1, ft5.c1
                ->  Nested Loop
-                     Output: ft2.ctid, ft4.*, ft4.c1
+                     Output: ft2.ctid, ft4.c1, ft4.*
                      Join Filter: (ft2.c2 = ft4.c1)
                      ->  Foreign Scan on public.ft2
                            Output: ft2.ctid, ft2.c2
                            Remote SQL: SELECT c2, ctid FROM "S 1"."T 1" WHERE (("C 1" > 2000)) FOR UPDATE
                      ->  Foreign Scan on public.ft4
-                           Output: ft4.*, ft4.c1
+                           Output: ft4.c1, ft4.*
                            Remote SQL: SELECT c1, c2, c3 FROM "S 1"."T 3"
                ->  Foreign Scan on public.ft5
-                     Output: ft5.*, ft5.c1
+                     Output: ft5.c1, ft5.*
                      Remote SQL: SELECT c1, c2, c3 FROM "S 1"."T 4"
 (22 rows)
 
@@ -7128,15 +7128,15 @@ select * from bar where f1 in (select f1 from foo) for update;
                      Output: bar2.f1, bar2.f2, bar2.ctid, bar2.*, bar2.tableoid
                      Remote SQL: SELECT f1, f2, f3, ctid FROM public.loct2 FOR UPDATE
          ->  Hash
-               Output: foo.ctid, foo.*, foo.tableoid, foo.f1
+               Output: foo.f1, foo.ctid, foo.*, foo.tableoid
                ->  HashAggregate
-                     Output: foo.ctid, foo.*, foo.tableoid, foo.f1
+                     Output: foo.f1, foo.ctid, foo.*, foo.tableoid
                      Group Key: foo.f1
                      ->  Append
                            ->  Seq Scan on public.foo
-                                 Output: foo.ctid, foo.*, foo.tableoid, foo.f1
+                                 Output: foo.f1, foo.ctid, foo.*, foo.tableoid
                            ->  Foreign Scan on public.foo2
-                                 Output: foo2.ctid, foo2.*, foo2.tableoid, foo2.f1
+                                 Output: foo2.f1, foo2.ctid, foo2.*, foo2.tableoid
                                  Remote SQL: SELECT f1, f2, f3, ctid FROM public.loct1
 (23 rows)
 
@@ -7166,15 +7166,15 @@ select * from bar where f1 in (select f1 from foo) for share;
                      Output: bar2.f1, bar2.f2, bar2.ctid, bar2.*, bar2.tableoid
                      Remote SQL: SELECT f1, f2, f3, ctid FROM public.loct2 FOR SHARE
          ->  Hash
-               Output: foo.ctid, foo.*, foo.tableoid, foo.f1
+               Output: foo.f1, foo.ctid, foo.*, foo.tableoid
                ->  HashAggregate
-                     Output: foo.ctid, foo.*, foo.tableoid, foo.f1
+                     Output: foo.f1, foo.ctid, foo.*, foo.tableoid
                      Group Key: foo.f1
                      ->  Append
                            ->  Seq Scan on public.foo
-                                 Output: foo.ctid, foo.*, foo.tableoid, foo.f1
+                                 Output: foo.f1, foo.ctid, foo.*, foo.tableoid
                            ->  Foreign Scan on public.foo2
-                                 Output: foo2.ctid, foo2.*, foo2.tableoid, foo2.f1
+                                 Output: foo2.f1, foo2.ctid, foo2.*, foo2.tableoid
                                  Remote SQL: SELECT f1, f2, f3, ctid FROM public.loct1
 (23 rows)
 
@@ -7203,15 +7203,15 @@ update bar set f2 = f2 + 100 where f1 in (select f1 from foo);
          ->  Seq Scan on public.bar
                Output: bar.f1, bar.f2, bar.ctid
          ->  Hash
-               Output: foo.ctid, foo.*, foo.tableoid, foo.f1
+               Output: foo.f1, foo.ctid, foo.*, foo.tableoid
                ->  HashAggregate
-                     Output: foo.ctid, foo.*, foo.tableoid, foo.f1
+                     Output: foo.f1, foo.ctid, foo.*, foo.tableoid
                      Group Key: foo.f1
                      ->  Append
                            ->  Seq Scan on public.foo
-                                 Output: foo.ctid, foo.*, foo.tableoid, foo.f1
+                                 Output: foo.f1, foo.ctid, foo.*, foo.tableoid
                            ->  Foreign Scan on public.foo2
-                                 Output: foo2.ctid, foo2.*, foo2.tableoid, foo2.f1
+                                 Output: foo2.f1, foo2.ctid, foo2.*, foo2.tableoid
                                  Remote SQL: SELECT f1, f2, f3, ctid FROM public.loct1
    ->  Hash Join
          Output: bar2.f1, (bar2.f2 + 100), bar2.f3, bar2.ctid, foo.ctid, foo.*, foo.tableoid
@@ -7221,15 +7221,15 @@ update bar set f2 = f2 + 100 where f1 in (select f1 from foo);
                Output: bar2.f1, bar2.f2, bar2.f3, bar2.ctid
                Remote SQL: SELECT f1, f2, f3, ctid FROM public.loct2 FOR UPDATE
          ->  Hash
-               Output: foo.ctid, foo.*, foo.tableoid, foo.f1
+               Output: foo.f1, foo.ctid, foo.*, foo.tableoid
                ->  HashAggregate
-                     Output: foo.ctid, foo.*, foo.tableoid, foo.f1
+                     Output: foo.f1, foo.ctid, foo.*, foo.tableoid
                      Group Key: foo.f1
                      ->  Append
                            ->  Seq Scan on public.foo
-                                 Output: foo.ctid, foo.*, foo.tableoid, foo.f1
+                                 Output: foo.f1, foo.ctid, foo.*, foo.tableoid
                            ->  Foreign Scan on public.foo2
-                                 Output: foo2.ctid, foo2.*, foo2.tableoid, foo2.f1
+                                 Output: foo2.f1, foo2.ctid, foo2.*, foo2.tableoid
                                  Remote SQL: SELECT f1, f2, f3, ctid FROM public.loct1
 (39 rows)
 
@@ -7262,14 +7262,14 @@ where bar.f1 = ss.f1;
          Hash Cond: (foo.f1 = bar.f1)
          ->  Append
                ->  Seq Scan on public.foo
-                     Output: ROW(foo.f1), foo.f1
+                     Output: foo.f1, ROW(foo.f1)
                ->  Foreign Scan on public.foo2
-                     Output: ROW(foo2.f1), foo2.f1
+                     Output: foo2.f1, ROW(foo2.f1)
                      Remote SQL: SELECT f1 FROM public.loct1
                ->  Seq Scan on public.foo foo_1
-                     Output: ROW((foo_1.f1 + 3)), (foo_1.f1 + 3)
+                     Output: (foo_1.f1 + 3), ROW((foo_1.f1 + 3))
                ->  Foreign Scan on public.foo2 foo2_1
-                     Output: ROW((foo2_1.f1 + 3)), (foo2_1.f1 + 3)
+                     Output: (foo2_1.f1 + 3), ROW((foo2_1.f1 + 3))
                      Remote SQL: SELECT f1 FROM public.loct1
          ->  Hash
                Output: bar.f1, bar.f2, bar.ctid
@@ -7285,18 +7285,18 @@ where bar.f1 = ss.f1;
                      Output: bar2.f1, bar2.f2, bar2.f3, bar2.ctid
                      Remote SQL: SELECT f1, f2, f3, ctid FROM public.loct2 FOR UPDATE
          ->  Sort
-               Output: (ROW(foo.f1)), foo.f1
+               Output: foo.f1, (ROW(foo.f1))
                Sort Key: foo.f1
                ->  Append
                      ->  Seq Scan on public.foo
-                           Output: ROW(foo.f1), foo.f1
+                           Output: foo.f1, ROW(foo.f1)
                      ->  Foreign Scan on public.foo2
-                           Output: ROW(foo2.f1), foo2.f1
+                           Output: foo2.f1, ROW(foo2.f1)
                            Remote SQL: SELECT f1 FROM public.loct1
                      ->  Seq Scan on public.foo foo_1
-                           Output: ROW((foo_1.f1 + 3)), (foo_1.f1 + 3)
+                           Output: (foo_1.f1 + 3), ROW((foo_1.f1 + 3))
                      ->  Foreign Scan on public.foo2 foo2_1
-                           Output: ROW((foo2_1.f1 + 3)), (foo2_1.f1 + 3)
+                           Output: (foo2_1.f1 + 3), ROW((foo2_1.f1 + 3))
                            Remote SQL: SELECT f1 FROM public.loct1
 (45 rows)
 
@@ -7559,12 +7559,12 @@ update parent set b = parent.b || remt2.b from remt2 where parent.a = remt2.a re
    Update on public.parent
    Foreign Update on public.remt1
    ->  Nested Loop
-         Output: parent.a, (parent.b || remt2.b), parent.ctid, remt2.*, remt2.a, remt2.b
+         Output: parent.a, (parent.b || remt2.b), parent.ctid, remt2.a, remt2.b, remt2.*
          Join Filter: (parent.a = remt2.a)
          ->  Seq Scan on public.parent
                Output: parent.a, parent.b, parent.ctid
          ->  Foreign Scan on public.remt2
-               Output: remt2.b, remt2.*, remt2.a
+               Output: remt2.b, remt2.a, remt2.*
                Remote SQL: SELECT a, b FROM public.loct2
    ->  Foreign Update
          Remote SQL: UPDATE public.loct1 r4 SET b = (r4.b || r2.b) FROM public.loct2 r2 WHERE ((r4.a = r2.a)) RETURNING r4.a, r4.b, r2.a, r2.b
@@ -7591,7 +7591,7 @@ delete from parent using remt2 where parent.a = remt2.a returning parent;
          ->  Seq Scan on public.parent
                Output: parent.ctid, parent.a
          ->  Foreign Scan on public.remt2
-               Output: remt2.*, remt2.a
+               Output: remt2.a, remt2.*
                Remote SQL: SELECT a, b FROM public.loct2
    ->  Foreign Delete
          Remote SQL: DELETE FROM public.loct1 r4 USING public.loct2 r2 WHERE ((r4.a = r2.a)) RETURNING r4.a, r4.b
diff --git a/src/backend/optimizer/plan/planagg.c b/src/backend/optimizer/plan/planagg.c
index 86617099df..0e819a6e92 100644
--- a/src/backend/optimizer/plan/planagg.c
+++ b/src/backend/optimizer/plan/planagg.c
@@ -442,7 +442,7 @@ build_minmax_path(PlannerInfo *root, MinMaxAggInfo *mminfo,
 	subroot->tuple_fraction = 1.0;
 	subroot->limit_tuples = 1.0;
 
-	final_rel = query_planner(subroot, tlist, minmax_qp_callback, NULL);
+	final_rel = query_planner(subroot, &tlist, minmax_qp_callback, NULL);
 
 	/*
 	 * Since we didn't go through subquery_planner() to handle the subquery,
diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c
index 03c81772a3..42c2130096 100644
--- a/src/backend/optimizer/plan/planmain.c
+++ b/src/backend/optimizer/plan/planmain.c
@@ -29,6 +29,7 @@
 #include "optimizer/paths.h"
 #include "optimizer/placeholder.h"
 #include "optimizer/planmain.h"
+#include "optimizer/prep.h"
 
 
 /*
@@ -42,7 +43,8 @@
  * (grouping_planner) can choose among the surviving paths for the rel.
  *
  * root describes the query to plan
- * tlist is the target list the query should produce
+ * *tlist is the target list the query should produce (passed in as an output
+ * variable, because more entries may be added to it)
  *		(this is NOT necessarily root->parse->targetList!)
  * qp_callback is a function to compute query_pathkeys once it's safe to do so
  * qp_extra is optional extra data to pass to qp_callback
@@ -54,7 +56,7 @@
  * (We cannot construct canonical pathkeys until that's done.)
  */
 RelOptInfo *
-query_planner(PlannerInfo *root, List *tlist,
+query_planner(PlannerInfo *root, List **tlist,
 			  query_pathkeys_callback qp_callback, void *qp_extra)
 {
 	Query	   *parse = root->parse;
@@ -179,7 +181,7 @@ query_planner(PlannerInfo *root, List *tlist,
 	 * restrictions.  Finally, we form a target joinlist for make_one_rel() to
 	 * work from.
 	 */
-	build_base_rel_tlists(root, tlist);
+	build_base_rel_tlists(root, *tlist);
 
 	find_placeholders_in_jointree(root);
 
@@ -210,6 +212,26 @@ query_planner(PlannerInfo *root, List *tlist,
 	 */
 	add_other_rels_to_query(root, (Node *) root->parse->jointree);
 
+	/*
+	 * root->rowMarks should contain all row marks at this point, including
+	 * child row marks if any, so add "junk" Vars to the targetlist based on
+	 * the "parent" row marks.
+	 */
+	if (root->rowMarks)
+	{
+		List   *junk_vars;
+
+		/* More entries will added to *tlist. */
+		add_rowmark_junk_vars_to_targetlist(root, tlist, &junk_vars);
+		Assert(junk_vars != NIL);
+
+		/*
+		 * There can't be any PlaceHolderVars in junk_vars, so pass false for
+		 * 'create_new_ph'.
+		 */
+		add_vars_to_targetlist(root, junk_vars, bms_make_singleton(0), false);
+	}
+
 	/*
 	 * We have completed merging equivalence sets, so it's now possible to
 	 * generate pathkeys in canonical form; so compute query_pathkeys and
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 9bb068a52e..0db82453ac 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -25,6 +25,7 @@
 #include "access/table.h"
 #include "access/xact.h"
 #include "catalog/pg_constraint.h"
+#include "catalog/pg_inherits.h"
 #include "catalog/pg_proc.h"
 #include "catalog/pg_type.h"
 #include "executor/executor.h"
@@ -1843,15 +1844,6 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
 		/* Preprocess targetlist */
 		tlist = preprocess_targetlist(root);
 
-		/*
-		 * We are now done hacking up the query's targetlist.  Most of the
-		 * remaining planning work will be done with the PathTarget
-		 * representation of tlists, but save aside the full representation so
-		 * that we can transfer its decoration (resnames etc) to the topmost
-		 * tlist of the finished Plan.
-		 */
-		root->processed_tlist = tlist;
-
 		/*
 		 * Collect statistics about aggregates for estimating costs, and mark
 		 * all the aggregates with resolved aggtranstypes.  We must do this
@@ -1930,9 +1922,18 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
 		 * We also generate (in standard_qp_callback) pathkey representations
 		 * of the query's sort clause, distinct clause, etc.
 		 */
-		current_rel = query_planner(root, tlist,
+		current_rel = query_planner(root, &tlist,
 									standard_qp_callback, &qp_extra);
 
+		/*
+		 * We are now done hacking up the query's targetlist.  Most of the
+		 * remaining planning work will be done with the PathTarget
+		 * representation of tlists, but save aside the full representation so
+		 * that we can transfer its decoration (resnames etc) to the topmost
+		 * tlist of the finished Plan.
+		 */
+		root->processed_tlist = tlist;
+
 		/*
 		 * Convert the query's result tlist into PathTarget format.
 		 *
diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c
index 5392d1a561..1165382e82 100644
--- a/src/backend/optimizer/prep/preptlist.c
+++ b/src/backend/optimizer/prep/preptlist.c
@@ -76,7 +76,6 @@ preprocess_targetlist(PlannerInfo *root)
 	RangeTblEntry *target_rte = NULL;
 	Relation	target_relation = NULL;
 	List	   *tlist;
-	ListCell   *lc;
 
 	/*
 	 * If there is a result relation, open it so we can look for missing
@@ -118,71 +117,6 @@ preprocess_targetlist(PlannerInfo *root)
 		tlist = expand_targetlist(tlist, command_type,
 								  result_relation, target_relation);
 
-	/*
-	 * Add necessary junk columns for rowmarked rels.  These values are needed
-	 * for locking of rels selected FOR UPDATE/SHARE, and to do EvalPlanQual
-	 * rechecking.  See comments for PlanRowMark in plannodes.h.
-	 */
-	foreach(lc, root->rowMarks)
-	{
-		PlanRowMark *rc = (PlanRowMark *) lfirst(lc);
-		Var		   *var;
-		char		resname[32];
-		TargetEntry *tle;
-
-		/* child rels use the same junk attrs as their parents */
-		if (rc->rti != rc->prti)
-			continue;
-
-		if (rc->allMarkTypes & ~(1 << ROW_MARK_COPY))
-		{
-			/* Need to fetch TID */
-			var = makeVar(rc->rti,
-						  SelfItemPointerAttributeNumber,
-						  TIDOID,
-						  -1,
-						  InvalidOid,
-						  0);
-			snprintf(resname, sizeof(resname), "ctid%u", rc->rowmarkId);
-			tle = makeTargetEntry((Expr *) var,
-								  list_length(tlist) + 1,
-								  pstrdup(resname),
-								  true);
-			tlist = lappend(tlist, tle);
-		}
-		if (rc->allMarkTypes & (1 << ROW_MARK_COPY))
-		{
-			/* Need the whole row as a junk var */
-			var = makeWholeRowVar(rt_fetch(rc->rti, range_table),
-								  rc->rti,
-								  0,
-								  false);
-			snprintf(resname, sizeof(resname), "wholerow%u", rc->rowmarkId);
-			tle = makeTargetEntry((Expr *) var,
-								  list_length(tlist) + 1,
-								  pstrdup(resname),
-								  true);
-			tlist = lappend(tlist, tle);
-		}
-
-		/* If parent of inheritance tree, always fetch the tableoid too. */
-		if (rc->isParent)
-		{
-			var = makeVar(rc->rti,
-						  TableOidAttributeNumber,
-						  OIDOID,
-						  -1,
-						  InvalidOid,
-						  0);
-			snprintf(resname, sizeof(resname), "tableoid%u", rc->rowmarkId);
-			tle = makeTargetEntry((Expr *) var,
-								  list_length(tlist) + 1,
-								  pstrdup(resname),
-								  true);
-			tlist = lappend(tlist, tle);
-		}
-	}
-
 	/*
 	 * If the query has a RETURNING list, add resjunk entries for any Vars
 	 * used in RETURNING that belong to other relations.  We need to do this
@@ -434,3 +368,89 @@ get_plan_rowmark(List *rowmarks, Index rtindex)
 	}
 	return NULL;
 }
+
+/*
+ * add_rowmark_junk_vars_to_targetlist
+ *		Add "junk" targetlist entries needed for applying row marks contained
+ *		in root->rowMarks
+ *
+ * Vars that are added to the targetlist are also returned in *junk_vars for
+ * the caller's perusal.
+ */
+void
+add_rowmark_junk_vars_to_targetlist(PlannerInfo *root,
+									List **tlist, List **junk_vars)
+{
+	List	   *range_table = root->parse->rtable;
+	ListCell   *lc;
+
+	*junk_vars = NIL;
+
+	/*
+	 * Add necessary junk columns for rowmarked rels.  These values are needed
+	 * for locking of rels selected FOR UPDATE/SHARE, and to do EvalPlanQual
+	 * rechecking.  See comments for PlanRowMark in plannodes.h.
+	 */
+	foreach(lc, root->rowMarks)
+	{
+		PlanRowMark *rc = (PlanRowMark *) lfirst(lc);
+		Var		   *var;
+		char		resname[32];
+		TargetEntry *tle;
+
+		/* child rels use the same junk attrs as their parents */
+		if (rc->rti != rc->prti)
+			continue;
+
+		if (rc->allMarkTypes & ~(1 << ROW_MARK_COPY))
+		{
+			/* Need to fetch TID */
+			var = makeVar(rc->rti,
+						  SelfItemPointerAttributeNumber,
+						  TIDOID,
+						  -1,
+						  InvalidOid,
+						  0);
+			snprintf(resname, sizeof(resname), "ctid%u", rc->rowmarkId);
+			tle = makeTargetEntry((Expr *) var,
+								  list_length(*tlist) + 1,
+								  pstrdup(resname),
+								  true);
+			*tlist = lappend(*tlist, tle);
+			*junk_vars = lappend(*junk_vars, var);
+		}
+		if (rc->allMarkTypes & (1 << ROW_MARK_COPY))
+		{
+			/* Need the whole row as a junk var */
+			var = makeWholeRowVar(rt_fetch(rc->rti, range_table),
+								  rc->rti,
+								  0,
+								  false);
+			snprintf(resname, sizeof(resname), "wholerow%u", rc->rowmarkId);
+			tle = makeTargetEntry((Expr *) var,
+								  list_length(*tlist) + 1,
+								  pstrdup(resname),
+								  true);
+			*tlist = lappend(*tlist, tle);
+			*junk_vars = lappend(*junk_vars, var);
+		}
+
+		/* For inheritance cases, always fetch the tableoid too. */
+		if (rc->isParent)
+		{
+			var = makeVar(rc->rti,
+						  TableOidAttributeNumber,
+						  OIDOID,
+						  -1,
+						  InvalidOid,
+						  0);
+			snprintf(resname, sizeof(resname), "tableoid%u", rc->rowmarkId);
+			tle = makeTargetEntry((Expr *) var,
+								  list_length(*tlist) + 1,
+								  pstrdup(resname),
+								  true);
+			*tlist = lappend(*tlist, tle);
+			*junk_vars = lappend(*junk_vars, var);
+		}
+	}
+}
diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h
index 035caac500..a21783192c 100644
--- a/src/include/optimizer/planmain.h
+++ b/src/include/optimizer/planmain.h
@@ -27,7 +27,7 @@ typedef void (*query_pathkeys_callback) (PlannerInfo *root, void *extra);
 /*
  * prototypes for plan/planmain.c
  */
-extern RelOptInfo *query_planner(PlannerInfo *root, List *tlist,
+extern RelOptInfo *query_planner(PlannerInfo *root, List **tlist,
 			  query_pathkeys_callback qp_callback, void *qp_extra);
 
 /*
diff --git a/src/include/optimizer/prep.h b/src/include/optimizer/prep.h
index a9b2c9026c..cf7b3a9d06 100644
--- a/src/include/optimizer/prep.h
+++ b/src/include/optimizer/prep.h
@@ -37,6 +37,8 @@ extern Relids get_relids_for_join(Query *query, int joinrelid);
 extern List *preprocess_targetlist(PlannerInfo *root);
 
 extern PlanRowMark *get_plan_rowmark(List *rowmarks, Index rtindex);
+extern void add_rowmark_junk_vars_to_targetlist(PlannerInfo *root,
+							   List **tlist, List **junk_vars);
 
 /*
  * prototypes for prepunion.c
diff --git a/src/test/regress/expected/with.out b/src/test/regress/expected/with.out
index 2a2085556b..4f0a13dd0d 100644
--- a/src/test/regress/expected/with.out
+++ b/src/test/regress/expected/with.out
@@ -2199,28 +2199,28 @@ DELETE FROM a USING wcte WHERE aa = q2;
          ->  Seq Scan on public.a
                Output: a.ctid, a.aa
          ->  CTE Scan on wcte
-               Output: wcte.*, wcte.q2
+               Output: wcte.q2, wcte.*
    ->  Nested Loop
          Output: b.ctid, wcte.*
          Join Filter: (b.aa = wcte.q2)
          ->  Seq Scan on public.b
                Output: b.ctid, b.aa
          ->  CTE Scan on wcte
-               Output: wcte.*, wcte.q2
+               Output: wcte.q2, wcte.*
    ->  Nested Loop
          Output: c.ctid, wcte.*
          Join Filter: (c.aa = wcte.q2)
          ->  Seq Scan on public.c
                Output: c.ctid, c.aa
          ->  CTE Scan on wcte
-               Output: wcte.*, wcte.q2
+               Output: wcte.q2, wcte.*
    ->  Nested Loop
          Output: d.ctid, wcte.*
          Join Filter: (d.aa = wcte.q2)
          ->  Seq Scan on public.d
                Output: d.ctid, d.aa
          ->  CTE Scan on wcte
-               Output: wcte.*, wcte.q2
+               Output: wcte.q2, wcte.*
 (38 rows)
 
 -- error cases
-- 
2.17.2 (Apple Git-113)