v1-0002-Optimize-check_search_path.patch

text/x-patch

Filename: v1-0002-Optimize-check_search_path.patch
Type: text/x-patch
Part: 1
Message: Faster "SET search_path"

Patch

Format: format-patch
Series: patch v1-0002
Subject: Optimize check_search_path().
File+
src/backend/catalog/namespace.c 2 11
src/backend/utils/adt/varlena.c 70 0
src/include/utils/varlena.h 1 0
From 8f7ff4cbf0697144dd6f73d964abc2e589fc5020 Mon Sep 17 00:00:00 2001
From: Jeff Davis <jeff@j-davis.com>
Date: Thu, 27 Jul 2023 15:23:51 -0700
Subject: [PATCH v1 2/3] Optimize check_search_path().

Introduce CheckIdentifierString(), which validates a string list of
identifiers without the work of actually storing them in a list.

This speeds up frequent search_path changes, such as when a function
has search_path set in proconfig. Based on profiling.
---
 src/backend/catalog/namespace.c | 13 +-----
 src/backend/utils/adt/varlena.c | 70 +++++++++++++++++++++++++++++++++
 src/include/utils/varlena.h     |  1 +
 3 files changed, 73 insertions(+), 11 deletions(-)

diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index 69ab1b8e4b..cb87f3385e 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -4322,19 +4322,13 @@ ResetTempTableNamespace(void)
 bool
 check_search_path(char **newval, void **extra, GucSource source)
 {
-	char	   *rawname;
-	List	   *namelist;
-
-	/* Need a modifiable copy of string */
-	rawname = pstrdup(*newval);
+	char *rawname = *newval;
 
 	/* Parse string into list of identifiers */
-	if (!SplitIdentifierString(rawname, ',', &namelist))
+	if (!CheckIdentifierString(rawname, ','))
 	{
 		/* syntax error in name list */
 		GUC_check_errdetail("List syntax is invalid.");
-		pfree(rawname);
-		list_free(namelist);
 		return false;
 	}
 
@@ -4346,9 +4340,6 @@ check_search_path(char **newval, void **extra, GucSource source)
 	 * requirement is syntactic validity of the identifier list.
 	 */
 
-	pfree(rawname);
-	list_free(namelist);
-
 	return true;
 }
 
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index b1ec5c32ce..c8d36fd868 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -3427,6 +3427,76 @@ textToQualifiedNameList(text *textval)
 	return result;
 }
 
+/*
+ * Check that an identifier list is syntactically valid; that is, that there
+ * are no mismatched quotes and that identifiers are separated with the given
+ * separator. Empty identifiers are considered acceptable if (and only if)
+ * quoted.
+ *
+ * Note that an empty string is considered okay here, though not in
+ * textToQualifiedNameList.
+ */
+bool
+CheckIdentifierString(const char *rawstring, char separator)
+{
+	const char *nextp = rawstring;
+	bool		done = false;
+
+	while (scanner_isspace(*nextp))
+		nextp++;				/* skip leading whitespace */
+
+	if (*nextp == '\0')
+		return true;			/* allow empty string */
+
+	/* At the top of the loop, we are at start of a new identifier. */
+	do
+	{
+		if (*nextp == '"')
+		{
+			for (;;)
+			{
+				nextp = strchr(nextp + 1, '"');
+				if (nextp == NULL)
+					return false;	/* mismatched quotes */
+				if (nextp[1] != '"')
+					break;		/* found end of quoted name */
+				nextp++;
+			}
+
+			nextp++;
+		}
+		else
+		{
+			/* Unquoted name --- extends to separator or whitespace */
+			const char *curname = nextp;
+			while (*nextp && *nextp != separator &&
+				   !scanner_isspace(*nextp))
+				nextp++;
+			if (curname == nextp)
+				return false;	/* empty unquoted name not allowed */
+		}
+
+		while (scanner_isspace(*nextp))
+			nextp++;			/* skip trailing whitespace */
+
+		if (*nextp == separator)
+		{
+			nextp++;
+			while (scanner_isspace(*nextp))
+				nextp++;		/* skip leading whitespace for next */
+			/* we expect another name, so done remains false */
+		}
+		else if (*nextp == '\0')
+			done = true;
+		else
+			return false;		/* invalid syntax */
+
+		/* Loop back if we didn't reach end of string */
+	} while (!done);
+
+	return true;
+}
+
 /*
  * SplitIdentifierString --- parse a string containing identifiers
  *
diff --git a/src/include/utils/varlena.h b/src/include/utils/varlena.h
index 77f5b24735..4ff0cecc91 100644
--- a/src/include/utils/varlena.h
+++ b/src/include/utils/varlena.h
@@ -27,6 +27,7 @@ extern int	varstr_levenshtein_less_equal(const char *source, int slen,
 										  int ins_c, int del_c, int sub_c,
 										  int max_d, bool trusted);
 extern List *textToQualifiedNameList(text *textval);
+extern bool CheckIdentifierString(const char *rawstring, char separator);
 extern bool SplitIdentifierString(char *rawstring, char separator,
 								  List **namelist);
 extern bool SplitDirectoriesString(char *rawstring, char separator,
-- 
2.34.1