diff --git a/src/backend/executor/nodeLockRows.c b/src/backend/executor/nodeLockRows.c
index 68208d5c9a7dd3cafdf0611036452c64a08dd39a..4ebcaffe699a40d99b66b05331ffa17e9756da7d 100644
--- a/src/backend/executor/nodeLockRows.c
+++ b/src/backend/executor/nodeLockRows.c
@@ -262,29 +262,15 @@ lnext:
 	 */
 	if (epq_needed)
 	{
-		int			i;
-
 		/* Initialize EPQ machinery */
 		EvalPlanQualBegin(&node->lr_epqstate, estate);
 
 		/*
-		 * Transfer already-fetched tuples into the EPQ state, and make sure
-		 * its test tuples for other tables are reset to NULL.
-		 */
-		for (i = 0; i < node->lr_ntables; i++)
-		{
-			EvalPlanQualSetTuple(&node->lr_epqstate,
-								 i + 1,
-								 node->lr_curtuples[i]);
-			/* freeing this tuple is now the responsibility of EPQ */
-			node->lr_curtuples[i] = NULL;
-		}
-
-		/*
-		 * Next, fetch a copy of any rows that were successfully locked
-		 * without any update having occurred.  (We do this in a separate pass
-		 * so as to avoid overhead in the common case where there are no
-		 * concurrent updates.)
+		 * Transfer any already-fetched tuples into the EPQ state, and fetch a
+		 * copy of any rows that were successfully locked without any update
+		 * having occurred.  (We do this in a separate pass so as to avoid
+		 * overhead in the common case where there are no concurrent updates.)
+		 * Make sure any inactive child rels have NULL test tuples in EPQ.
 		 */
 		foreach(lc, node->lr_arowMarks)
 		{
@@ -293,15 +279,25 @@ lnext:
 			HeapTupleData tuple;
 			Buffer		buffer;
 
-			/* ignore non-active child tables */
+			/* skip non-active child tables, but clear their test tuples */
 			if (!erm->ermActive)
 			{
 				Assert(erm->rti != erm->prti);	/* check it's child table */
+				EvalPlanQualSetTuple(&node->lr_epqstate, erm->rti, NULL);
 				continue;
 			}
 
-			if (EvalPlanQualGetTuple(&node->lr_epqstate, erm->rti) != NULL)
-				continue;		/* it was updated and fetched above */
+			/* was tuple updated and fetched above? */
+			if (node->lr_curtuples[erm->rti - 1] != NULL)
+			{
+				/* yes, so set it as the EPQ test tuple for this rel */
+				EvalPlanQualSetTuple(&node->lr_epqstate,
+									 erm->rti,
+									 node->lr_curtuples[erm->rti - 1]);
+				/* freeing this tuple is now the responsibility of EPQ */
+				node->lr_curtuples[erm->rti - 1] = NULL;
+				continue;
+			}
 
 			/* foreign tables should have been fetched above */
 			Assert(erm->relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE);
diff --git a/src/test/isolation/expected/eval-plan-qual.out b/src/test/isolation/expected/eval-plan-qual.out
index 457461e355caa1c8c41089d2a35b225d2a58be2e..5898d94ff17f34c17cdcf220d183e74b5836d408 100644
--- a/src/test/isolation/expected/eval-plan-qual.out
+++ b/src/test/isolation/expected/eval-plan-qual.out
@@ -144,3 +144,22 @@ accountid      balance
 
 checking       1050           
 savings        600            
+
+starting permutation: updateforss readforss c1 c2
+step updateforss: 
+	UPDATE table_a SET value = 'newTableAValue' WHERE id = 1;
+	UPDATE table_b SET value = 'newTableBValue' WHERE id = 1;
+
+step readforss: 
+	SELECT ta.id AS ta_id, ta.value AS ta_value,
+		(SELECT ROW(tb.id, tb.value)
+		 FROM table_b tb WHERE ta.id = tb.id) AS tb_row
+	FROM table_a ta
+	WHERE ta.id = 1 FOR UPDATE OF ta;
+ <waiting ...>
+step c1: COMMIT;
+step readforss: <... completed>
+ta_id          ta_value       tb_row         
+
+1              newTableAValue (1,tableBValue)
+step c2: COMMIT;
diff --git a/src/test/isolation/specs/eval-plan-qual.spec b/src/test/isolation/specs/eval-plan-qual.spec
index a391466722f477141b6e455358bdb663c4d0085e..de481a3cecebd85b7084b1acb0d57941f40b5083 100644
--- a/src/test/isolation/specs/eval-plan-qual.spec
+++ b/src/test/isolation/specs/eval-plan-qual.spec
@@ -16,12 +16,18 @@ setup
  INSERT INTO c1 SELECT 0, a / 3, a % 3 FROM generate_series(0, 9) a;
  INSERT INTO c2 SELECT 1, a / 3, a % 3 FROM generate_series(0, 9) a;
  INSERT INTO c3 SELECT 2, a / 3, a % 3 FROM generate_series(0, 9) a;
+
+ CREATE TABLE table_a (id integer, value text);
+ CREATE TABLE table_b (id integer, value text);
+ INSERT INTO table_a VALUES (1, 'tableAValue');
+ INSERT INTO table_b VALUES (1, 'tableBValue');
 }
 
 teardown
 {
  DROP TABLE accounts;
  DROP TABLE p CASCADE;
+ DROP TABLE table_a, table_b;
 }
 
 session "s1"
@@ -64,6 +70,15 @@ step "lockwithvalues"	{
 	  FOR UPDATE OF a1;
 }
 
+# these tests exercise EvalPlanQual with a SubLink sub-select (which should be
+# unaffected by any EPQ recheck behavior in the outer query); cf bug #14034
+
+step "updateforss"	{
+	UPDATE table_a SET value = 'newTableAValue' WHERE id = 1;
+	UPDATE table_b SET value = 'newTableBValue' WHERE id = 1;
+}
+
+
 session "s2"
 setup		{ BEGIN ISOLATION LEVEL READ COMMITTED; }
 step "wx2"	{ UPDATE accounts SET balance = balance + 450 WHERE accountid = 'checking'; }
@@ -81,6 +96,13 @@ step "returningp1" {
 	WITH u AS ( UPDATE p SET b = b WHERE a > 0 RETURNING * )
 	  SELECT * FROM u;
 }
+step "readforss"	{
+	SELECT ta.id AS ta_id, ta.value AS ta_value,
+		(SELECT ROW(tb.id, tb.value)
+		 FROM table_b tb WHERE ta.id = tb.id) AS tb_row
+	FROM table_a ta
+	WHERE ta.id = 1 FOR UPDATE OF ta;
+}
 step "c2"	{ COMMIT; }
 
 session "s3"
@@ -95,3 +117,4 @@ permutation "readp1" "writep1" "readp2" "c1" "c2"
 permutation "writep2" "returningp1" "c1" "c2"
 permutation "wx2" "partiallock" "c2" "c1" "read"
 permutation "wx2" "lockwithvalues" "c2" "c1" "read"
+permutation "updateforss" "readforss" "c1" "c2"