From e0cf7be1682feb8dbca80adefbaac06ef8315ba4 Mon Sep 17 00:00:00 2001
From: Alexander Pyhalov <a.pyhalov@postgrespro.ru>
Date: Tue, 9 Aug 2022 14:47:16 +0300
Subject: [PATCH] postgres_fdw should avoid sorted paths which don't provide
 all required vars

---
 contrib/postgres_fdw/postgres_fdw.c | 56 +++++++++++++++++++++++++++++
 1 file changed, 56 insertions(+)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 16320170cee..e21ddd7485f 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -541,6 +541,7 @@ static void merge_fdw_options(PgFdwRelationInfo *fpinfo,
 							  const PgFdwRelationInfo *fpinfo_o,
 							  const PgFdwRelationInfo *fpinfo_i);
 static int	get_batch_size_option(Relation rel);
+static bool path_provides_local_vars(Path *path, PgFdwRelationInfo *fpinfo);
 
 
 /*
@@ -5775,6 +5776,54 @@ foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype,
 	return true;
 }
 
+/*
+ * Determines if all vars, used by fpinfo->local_conds,
+ * are provided by path.
+ */
+static bool
+path_provides_local_vars(Path *path, PgFdwRelationInfo *fpinfo)
+{
+	List	   *vars_provided;
+	ListCell   *lc1;
+	ListCell   *lc2;
+	ListCell   *lc3;
+
+	if (path == NULL)
+		return false;
+
+	/* Get all vars, provided by path */
+	vars_provided = pull_var_clause((Node *) path->pathtarget->exprs, PVC_INCLUDE_PLACEHOLDERS);
+
+	foreach(lc1, fpinfo->local_conds)
+	{
+		RestrictInfo *ri = (RestrictInfo *) lfirst(lc1);
+		List	   *vars_used;
+
+		/* Get vars, used by this restriction clause */
+		vars_used = pull_var_clause((Node *) ri->clause, PVC_INCLUDE_PLACEHOLDERS);
+		foreach(lc2, vars_used)
+		{
+			Var		   *used = lfirst(lc2);
+			bool		found = false;
+
+			foreach(lc3, vars_provided)
+			{
+				Var		   *provided = lfirst(lc3);
+
+				if (provided->varno == used->varno && provided->varattno == used->varattno)
+				{
+					found = true;
+					break;
+				}
+			}
+			if (!found)
+				return false;
+		}
+	}
+
+	return true;
+}
+
 static void
 add_paths_with_pathkeys_for_rel(PlannerInfo *root, RelOptInfo *rel,
 								Path *epq_path)
@@ -5812,6 +5861,13 @@ add_paths_with_pathkeys_for_rel(PlannerInfo *root, RelOptInfo *rel,
 								 useful_pathkeys,
 								 -1.0);
 
+		/*
+		 * Sorted path should provide all vars needed by local conditions or
+		 * we'll not be able to use it
+		 */
+		if (sorted_epq_path != NULL && !path_provides_local_vars(sorted_epq_path, (PgFdwRelationInfo *) rel->fdw_private))
+			return;
+
 		if (IS_SIMPLE_REL(rel))
 			add_path(rel, (Path *)
 					 create_foreignscan_path(root, rel,
-- 
2.34.1

