Optimize check_search_path() by using SearchPathCache.
authorJeff Davis <jdavis@postgresql.org>
Mon, 20 Nov 2023 23:53:42 +0000 (15:53 -0800)
committerJeff Davis <jdavis@postgresql.org>
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;
 }