From 62b3f7d20505408c8668cff706bca72d696da1da Mon Sep 17 00:00:00 2001
From: Nils Dijk <me@thanod.nl>
Date: Fri, 24 Dec 2021 12:31:32 +0000
Subject: [PATCH 3/3] add path_compare_hook to let extensions compare paths

---
 src/backend/optimizer/util/pathnode.c | 24 ++++++++++++++++++++++++
 src/include/optimizer/pathnode.h      | 14 ++++++++++++++
 2 files changed, 38 insertions(+)

diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index f333069872..96247f2029 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -42,6 +42,8 @@ typedef enum
 	CONTINUE					/* compare with next path */
 } AddPathDecision;
 
+path_compare_hook_type path_compare_hook = NULL;
+
 /*
  * STD_FUZZ_FACTOR is the normal fuzz factor for compare_path_costs_fuzzily.
  * XXX is it worth making this user-controllable?  It provides a tradeoff
@@ -507,6 +509,14 @@ path_compare(Path *path1, Path *path2)
 	if (cmp == PATHS_DIFFERENT)
 		return cmp;
 
+	if (unlikely(path_compare_hook))
+	{
+		/* since we combine a result form an extension use a safe combine */
+		cmp = path_comparison_combine(cmp, path_compare_hook(path1, path2));
+		if (cmp == PATHS_DIFFERENT)
+			return cmp;
+	}
+
 	/* Keep compatibility with the original decision tree from add_path */
 	if (cmp != PATHS_EQUAL)
 	{
@@ -746,6 +756,20 @@ add_path_precheck(RelOptInfo *parent_rel,
 	bool		consider_startup;
 	ListCell   *p1;
 
+	if (path_compare_hook)
+	{
+		/*
+		 * When an extension has installed a hook for comparing paths we can't
+		 * perform any precheck to quickly decline a hypothetical path. If we
+		 * would reject a path based on the parameters passed in we don't
+		 * allow extensions to make a differentiation on alternative
+		 * dimensions.
+		 *
+		 * Instead we return early and allow the path to be created.
+		 */
+		return true;
+	}
+
 	/* Pretend parameterized paths have no pathkeys, per add_path policy */
 	new_path_pathkeys = required_outer ? NIL : pathkeys;
 
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index 1ee4a91ab8..87b89e9dce 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -29,6 +29,20 @@ typedef enum
 	PATHS_DIFFERENT = 3			/* no path is better on all dimensions */
 } PathComparison;
 
+/* mask of bits used to express the state of PathComparison */
+#define PATH_COMPARISON_MASK 3
+
+static inline PathComparison
+path_comparison_combine(PathComparison c1, PathComparison c2)
+{
+	/* safely combine path comparisons and keep them within expected range */
+	return (c1 | c2) & PATH_COMPARISON_MASK;
+}
+
+/* Hook for plugins to get control in add_paths_to_joinrel() */
+typedef PathComparison(*path_compare_hook_type) (Path *path1, Path *path2);
+extern PGDLLIMPORT path_compare_hook_type path_compare_hook;
+
 extern int	compare_path_costs(Path *path1, Path *path2,
 							   CostSelector criterion);
 extern int	compare_fractional_path_costs(Path *path1, Path *path2,
-- 
2.32.0 (Apple Git-132)

