diff --git a/src/backend/executor/nodeResultCache.c b/src/backend/executor/nodeResultCache.c
index 919238d1ff1..471900346f1 100644
--- a/src/backend/executor/nodeResultCache.c
+++ b/src/backend/executor/nodeResultCache.c
@@ -760,7 +760,7 @@ ExecResultCache(PlanState *pstate)
 				/*
 				 * Validate if the planner properly set the singlerow flag. It
 				 * should only set that if each cache entry can, at most,
-				 * return 1 row.  XXX maybe this should be an Assert?
+				 * return 1 row.
 				 */
 				if (unlikely(entry->complete))
 					elog(ERROR, "cache entry already complete");
diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c
index 4c30c655640..d9d48827a9a 100644
--- a/src/backend/optimizer/path/joinpath.c
+++ b/src/backend/optimizer/path/joinpath.c
@@ -503,6 +503,37 @@ get_resultcache_path(PlannerInfo *root, RelOptInfo *innerrel,
 								 jointype == JOIN_ANTI))
 		return NULL;
 
+	/*
+	 * Result Cache normally marks cache entries as complete when it runs out
+	 * of tuples to read from its subplan.  However, with unique joins, Nested
+	 * Loop will skip to the next outer tuple after finding the first matching
+	 * inner tuple.  This means that we may not read the inner side of the
+	 * join to completion which leaves no opportunity to mark the cache entry
+	 * as complete.  To work around that, when the join is unique we
+	 * automatically mark cache entries as complete after fetching the first
+	 * tuple.  This works when the entire join condition is parameterized.
+	 * Otherwise, when the parameterization is only a subset of the join
+	 * condition, we can't be sure which part of it causes the join to be
+	 * unique.  This means there are no guarantees that only 1 tuple will be
+	 * read.  We cannot mark the cache entry as complete after reading the
+	 * first tuple without that guarantee.  This means the scope of Result
+	 * Cache's usefulness is limited to only outer rows that have no join
+	 * partner as this is the only case where Nested Loop would exhaust the
+	 * inner scan of a unique join.  Since the scope is limited to that, we
+	 * just don't bother making a result cache path in this case.
+	 *
+	 * Lateral vars needn't be considered here as they're not considered when
+	 * determining if the join is unique.
+	 *
+	 * XXX this could be enabled if the remaining join quals were made part of
+	 * the inner scan's filter instead of the join filter.  Maybe it's worth
+	 * considering doing that?
+	 */
+	if (extra->inner_unique &&
+		list_length(inner_path->param_info->ppi_clauses) <
+		list_length(extra->restrictlist))
+		return NULL;
+
 	/*
 	 * We can't use a result cache if there are volatile functions in the
 	 * inner rel's target list or restrict list.  A cache hit could reduce the