From 0394a86c27cf5e82ce0574f951392428f80fe4b7 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Sat, 29 Mar 2025 14:51:37 -0400
Subject: [PATCH v11 2/7] Provide a post-rewrite callback hook in plancache.c.

SQL-language functions sometimes want to modify the targetlist of
the query that returns their result.  If they're to use the plan
cache, it needs to be possible to do that over again when a
replan occurs.  Invent a callback hook to make that happen.

I chose to provide a separate function SetPostRewriteHook to
install such hooks.  An alternative API could be to add two
more arguments to CompleteCachedPlan.  I didn't do so because
I felt that few callers will want this, but there's a case that
that way would be cleaner.

Author: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/8216639.NyiUUSuA9g@aivenlaptop
---
 src/backend/utils/cache/plancache.c | 33 +++++++++++++++++++++++++++++
 src/include/utils/plancache.h       |  8 +++++++
 src/tools/pgindent/typedefs.list    |  1 +
 3 files changed, 42 insertions(+)

diff --git a/src/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c
index 5983927a4c2..3b681647060 100644
--- a/src/backend/utils/cache/plancache.c
+++ b/src/backend/utils/cache/plancache.c
@@ -219,6 +219,8 @@ CreateCachedPlan(RawStmt *raw_parse_tree,
 	plansource->num_params = 0;
 	plansource->parserSetup = NULL;
 	plansource->parserSetupArg = NULL;
+	plansource->postRewrite = NULL;
+	plansource->postRewriteArg = NULL;
 	plansource->cursor_options = 0;
 	plansource->fixed_result = false;
 	plansource->resultDesc = NULL;
@@ -316,6 +318,8 @@ CreateOneShotCachedPlan(RawStmt *raw_parse_tree,
 	plansource->num_params = 0;
 	plansource->parserSetup = NULL;
 	plansource->parserSetupArg = NULL;
+	plansource->postRewrite = NULL;
+	plansource->postRewriteArg = NULL;
 	plansource->cursor_options = 0;
 	plansource->fixed_result = false;
 	plansource->resultDesc = NULL;
@@ -485,6 +489,29 @@ CompleteCachedPlan(CachedPlanSource *plansource,
 	plansource->is_valid = true;
 }
 
+/*
+ * SetPostRewriteHook: set a hook to modify post-rewrite query trees
+ *
+ * Some callers have a need to modify the query trees between rewriting and
+ * planning.  In the initial call to CompleteCachedPlan, it's assumed such
+ * work was already done on the querytree_list.  However, if we're forced
+ * to replan, it will need to be done over.  The caller can set this hook
+ * to provide code to make that happen.
+ *
+ * postRewriteArg is just passed verbatim to the hook.  As with parserSetupArg,
+ * it is caller's responsibility that the referenced data remains
+ * valid for as long as the CachedPlanSource exists.
+ */
+void
+SetPostRewriteHook(CachedPlanSource *plansource,
+				   PostRewriteHook postRewrite,
+				   void *postRewriteArg)
+{
+	Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
+	plansource->postRewrite = postRewrite;
+	plansource->postRewriteArg = postRewriteArg;
+}
+
 /*
  * SaveCachedPlan: save a cached plan permanently
  *
@@ -813,6 +840,10 @@ RevalidateCachedQuery(CachedPlanSource *plansource,
 		tlist = NIL;
 	}
 
+	/* Apply post-rewrite callback if there is one */
+	if (plansource->postRewrite != NULL)
+		plansource->postRewrite(tlist, plansource->postRewriteArg);
+
 	/* Release snapshot if we got one */
 	if (snapshot_set)
 		PopActiveSnapshot();
@@ -1800,6 +1831,8 @@ CopyCachedPlan(CachedPlanSource *plansource)
 	newsource->num_params = plansource->num_params;
 	newsource->parserSetup = plansource->parserSetup;
 	newsource->parserSetupArg = plansource->parserSetupArg;
+	newsource->postRewrite = plansource->postRewrite;
+	newsource->postRewriteArg = plansource->postRewriteArg;
 	newsource->cursor_options = plansource->cursor_options;
 	newsource->fixed_result = plansource->fixed_result;
 	if (plansource->resultDesc)
diff --git a/src/include/utils/plancache.h b/src/include/utils/plancache.h
index 5930fcb50f0..07ec5318db7 100644
--- a/src/include/utils/plancache.h
+++ b/src/include/utils/plancache.h
@@ -40,6 +40,9 @@ typedef enum
 /* GUC parameter */
 extern PGDLLIMPORT int plan_cache_mode;
 
+/* Optional callback to editorialize on rewritten parse trees */
+typedef void (*PostRewriteHook) (List *querytree_list, void *arg);
+
 #define CACHEDPLANSOURCE_MAGIC		195726186
 #define CACHEDPLAN_MAGIC			953717834
 #define CACHEDEXPR_MAGIC			838275847
@@ -112,6 +115,8 @@ typedef struct CachedPlanSource
 	int			num_params;		/* length of param_types array */
 	ParserSetupHook parserSetup;	/* alternative parameter spec method */
 	void	   *parserSetupArg;
+	PostRewriteHook postRewrite;	/* see SetPostRewriteHook */
+	void	   *postRewriteArg;
 	int			cursor_options; /* cursor options used for planning */
 	bool		fixed_result;	/* disallow change in result tupdesc? */
 	TupleDesc	resultDesc;		/* result type; NULL = doesn't return tuples */
@@ -223,6 +228,9 @@ extern void CompleteCachedPlan(CachedPlanSource *plansource,
 							   void *parserSetupArg,
 							   int cursor_options,
 							   bool fixed_result);
+extern void SetPostRewriteHook(CachedPlanSource *plansource,
+							   PostRewriteHook postRewrite,
+							   void *postRewriteArg);
 
 extern void SaveCachedPlan(CachedPlanSource *plansource);
 extern void DropCachedPlan(CachedPlanSource *plansource);
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index b66cecd8799..ff75a508876 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -2266,6 +2266,7 @@ PortalHashEnt
 PortalStatus
 PortalStrategy
 PostParseColumnRefHook
+PostRewriteHook
 PostgresPollingStatusType
 PostingItem
 PreParseColumnRefHook
-- 
2.43.5

