Optimize check_search_path() by using SearchPathCache.
authorJeff Davis <[email protected]>
Mon, 20 Nov 2023 23:53:42 +0000 (15:53 -0800)
committerJeff Davis <[email protected]>
Mon, 20 Nov 2023 23:53:42 +0000 (15:53 -0800)
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

index d74f6181a9cef8f634bf28941160b8160721cdf4..5027efc91d6e4371749571a7eaab625ce12dbe69 100644 (file)
@@ -235,6 +235,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.
+ *
  * 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.
@@ -296,6 +300,21 @@ spcache_init(void)
    searchPathCacheValid = true;
 }
 
+/*
+ * 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.
  *
@@ -4578,11 +4597,40 @@ ResetTempTableNamespace(void)
 bool
 check_search_path(char **newval, void **extra, GucSource source)
 {
+   Oid         roleid = InvalidOid;
+   const char *searchPath = *newval;
    char       *rawname;
    List       *namelist;
+   bool        use_cache = (SearchPathCacheContext != NULL);
 
-   /* Need a modifiable copy of string */
-   rawname = pstrdup(*newval);
+   /*
+    * We used to try to check that the named schemas exist, but there are
+    * many valid use-cases for having search_path settings that include
+    * schemas that don't exist; and often, we are not inside a transaction
+    * here and so can't consult the system catalogs anyway.  So now, the only
+    * 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 (use_cache)
+   {
+       spcache_init();
+
+       roleid = GetUserId();
+
+       if (spcache_lookup(searchPath, roleid) != NULL)
+           return true;
+   }
+
+   /*
+    * Ensure validity check succeeds before creating cache entry.
+    */
+
+   rawname = pstrdup(searchPath);  /* need a modifiable copy */
 
    /* Parse string into list of identifiers */
    if (!SplitIdentifierString(rawname, ',', &namelist))
@@ -4605,6 +4653,10 @@ check_search_path(char **newval, void **extra, GucSource source)
    pfree(rawname);
    list_free(namelist);
 
+   /* create empty cache entry */
+   if (use_cache)
+       (void) spcache_insert(searchPath, roleid);
+
    return true;
 }