From 46d51755859746789aa073625fd5570f2d01be0c Mon Sep 17 00:00:00 2001
From: Jeff Davis <jeff@j-davis.com>
Date: Thu, 19 Oct 2023 15:19:05 -0700
Subject: [PATCH v7 4/6] Optimize check_search_path() by using SearchPathCache.

A hash lookup is faster than re-validating the string, particularly
because we use SplitIdentifierString() for validation.

Important when search_path changes frequently.

Discussion: https://postgr.es/m/04c8592dbd694e4114a3ed87139a7a04e4363030.camel%40j-davis.com
---
 src/backend/catalog/namespace.c | 50 +++++++++++++++++++++++++++++++--
 1 file changed, 47 insertions(+), 3 deletions(-)

diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index 258c97b590..68deb86554 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -234,6 +234,10 @@ static bool MatchNamedCall(HeapTuple proctup, int nargs, List *argnames,
  * when a function has search_path set in proconfig. Add a search path cache
  * that can be used by recomputeNamespacePath().
  *
+ * The cache is also used to remember already-validated strings in
+ * check_search_path() to avoid the need to call SplitIdentifierString()
+ * repeatedly. In this case, the caller uses roleid=InvalidOid.
+ *
  * The search path cache is based on a wrapper around a simplehash hash table
  * (nsphash, defined below). The spcache wrapper deals with OOM while trying
  * to initialize a key, and also offers a more convenient API.
@@ -314,6 +318,21 @@ spcache_members(void)
 	return SearchPathCache->members;
 }
 
+/*
+ * Look up entry in search path cache without inserting. Returns NULL if not
+ * present.
+ */
+static SearchPathCacheEntry *
+spcache_lookup(const char *searchPath, Oid roleid)
+{
+	SearchPathCacheKey		 cachekey = {
+		.searchPath					  = searchPath,
+		.roleid						  = roleid
+	};
+
+	return nsphash_lookup(SearchPathCache, cachekey);
+}
+
 /*
  * Look up or insert entry in search path cache.
  *
@@ -4633,9 +4652,10 @@ ResetTempTableNamespace(void)
 bool
 check_search_path(char **newval, void **extra, GucSource source)
 {
-	const char				*searchPath = *newval;
-	char					*rawname;
-	List					*namelist;
+	Oid			 roleid		= InvalidOid;
+	const char	*searchPath = *newval;
+	char		*rawname;
+	List		*namelist;
 
 	/*
 	 * We used to try to check that the named schemas exist, but there are
@@ -4645,6 +4665,24 @@ check_search_path(char **newval, void **extra, GucSource source)
 	 * requirement is syntactic validity of the identifier list.
 	 */
 
+	/*
+	 * Checking only the syntactic validity also allows us to use the search
+	 * path cache (if available) to avoid calling SplitIdentifierString() on
+	 * the same string repeatedly.
+	 */
+	if (SearchPathCacheContext != NULL)
+	{
+		spcache_init();
+
+		roleid = GetUserId();
+
+		if (spcache_lookup(searchPath, roleid) != NULL)
+			return true;
+
+		if (spcache_members() >= SPCACHE_RESET_THRESHOLD)
+			spcache_reset();
+	}
+
 	/*
 	 * Ensure validity check succeeds before creating cache entry.
 	 */
@@ -4664,6 +4702,12 @@ check_search_path(char **newval, void **extra, GucSource source)
 	pfree(rawname);
 	list_free(namelist);
 
+	/* create empty cache entry */
+	if (SearchPathCacheContext != NULL)
+	{
+		(void) spcache_insert(searchPath, roleid);
+	}
+
 	return true;
 }
 
-- 
2.34.1

