commit f9c753286554cc2fb6e96dcfe6641247216d7107
Author: Jeevan Chalke <jeevan.chalke@enterprisedb.com>
Date:   Tue Jul 11 14:39:58 2023 +0530

    Conditionally free the path in add_path()
    
    The passed-in path or already added path in the rel is freed if we
    reject it or are not needed anymore.  However, that path may have some
    references elsewhere, and thus it is not safe to always free.  We have
    that case when add_path() is called to add an ordered path that is
    already sorted but got rejected.  To avoid this, add the free_path
    argument to add_path() which is used for conditionally freeing the
    path.
    
    To avoid API breakage, add a wrapper instead, named add_path_extended.
    
    Reported by me, fix suggested by Alvaro Herrera.

diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 0e12fde..d0d7c3e 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -5162,7 +5162,7 @@ create_ordered_paths(PlannerInfo *root,
 			sorted_path = apply_projection_to_path(root, ordered_rel,
 												   sorted_path, target);
 
-		add_path(ordered_rel, sorted_path);
+		add_path_extended(ordered_rel, sorted_path, false);
 	}
 
 	/*
@@ -5212,7 +5212,7 @@ create_ordered_paths(PlannerInfo *root,
 				path = apply_projection_to_path(root, ordered_rel,
 												path, target);
 
-			add_path(ordered_rel, path);
+			add_path_extended(ordered_rel, path, false);
 		}
 
 		/*
@@ -5271,7 +5271,7 @@ create_ordered_paths(PlannerInfo *root,
 					sorted_path = apply_projection_to_path(root, ordered_rel,
 														   sorted_path, target);
 
-				add_path(ordered_rel, sorted_path);
+				add_path_extended(ordered_rel, sorted_path, false);
 			}
 		}
 	}
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index 5f55968..262084a 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -362,6 +362,16 @@ set_cheapest(RelOptInfo *parent_rel)
 
 /*
  * add_path
+ *	  See add_path_extended()
+ */
+void
+add_path(RelOptInfo *parent_rel, Path *new_path)
+{
+	add_path_extended(parent_rel, new_path, true);
+}
+
+/*
+ * add_path_extended
  *	  Consider a potential implementation path for the specified parent rel,
  *	  and add it to the rel's pathlist if it is worthy of consideration.
  *	  A path is worthy if it has a better sort order (better pathkeys) or
@@ -415,11 +425,12 @@ set_cheapest(RelOptInfo *parent_rel)
  *
  * 'parent_rel' is the relation entry to which the path corresponds.
  * 'new_path' is a potential path for parent_rel.
+ * 'free_path' if true, pfree() the path if rejected or not needed.
  *
  * Returns nothing, but modifies parent_rel->pathlist.
  */
 void
-add_path(RelOptInfo *parent_rel, Path *new_path)
+add_path_extended(RelOptInfo *parent_rel, Path *new_path, bool free_path)
 {
 	bool		accept_new = true;	/* unless we find a superior old path */
 	int			insert_at = 0;	/* where to insert new item */
@@ -590,7 +601,7 @@ add_path(RelOptInfo *parent_rel, Path *new_path)
 			/*
 			 * Delete the data pointed-to by the deleted cell, if possible
 			 */
-			if (!IsA(old_path, IndexPath))
+			if (free_path && !IsA(old_path, IndexPath))
 				pfree(old_path);
 		}
 		else
@@ -618,7 +629,7 @@ add_path(RelOptInfo *parent_rel, Path *new_path)
 	else
 	{
 		/* Reject and recycle the new path */
-		if (!IsA(new_path, IndexPath))
+		if (free_path && !IsA(new_path, IndexPath))
 			pfree(new_path);
 	}
 }
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index 001e75b..567e70e 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -27,6 +27,8 @@ extern int	compare_fractional_path_costs(Path *path1, Path *path2,
 										  double fraction);
 extern void set_cheapest(RelOptInfo *parent_rel);
 extern void add_path(RelOptInfo *parent_rel, Path *new_path);
+extern void add_path_extended(RelOptInfo *parent_rel, Path *new_path,
+							  bool free_path);
 extern bool add_path_precheck(RelOptInfo *parent_rel,
 							  Cost startup_cost, Cost total_cost,
 							  List *pathkeys, Relids required_outer);
