From e9b1d4429272857d3a05c096a7e8d75cd3e85f69 Mon Sep 17 00:00:00 2001
From: Jelte Fennema-Nio <jelte.fennema@microsoft.com>
Date: Wed, 10 Apr 2024 17:42:43 +0200
Subject: [PATCH v1] psql: Greatly speed up "\d tablename" when not using
 regexes

Running "\d tablename" from psql could take multiple seconds when
running on a system with 100k+ tables. The reason for this was that
a sequence scan on pg_class takes place, due to regex matching being
used.

Regex matching is obviously unnecessary when we're looking for an exact
match. This checks for this (common) case and starts using plain
equality in that case.
---
 src/fe_utils/string_utils.c | 65 +++++++++++++++++++++++++++++++++++--
 1 file changed, 63 insertions(+), 2 deletions(-)

diff --git a/src/fe_utils/string_utils.c b/src/fe_utils/string_utils.c
index 09fd33907dd..114d5c8e830 100644
--- a/src/fe_utils/string_utils.c
+++ b/src/fe_utils/string_utils.c
@@ -859,6 +859,31 @@ appendReloptionsArray(PQExpBuffer buffer, const char *reloptions,
 }
 
 
+/*
+ * Check if the given regex is a regex that looks for an exact character match.
+ *
+ * Assumes the string starts with "^(" and ends with ")$".
+ */
+static bool
+isExactMatchRegex(char *pattern)
+{
+	char	   *regexChars = "|*+?()[]{}.^$\\";
+	int			length = strlen(pattern);
+
+	Assert(pattern[0] == '^');
+	Assert(pattern[1] == '(');
+	Assert(pattern[length - 2] == ')');
+	Assert(pattern[length - 1] == '$');
+	for (char *c = regexChars; *c; c++)
+	{
+		char	   *match = strchr(&pattern[2], *c);
+
+		if (match != NULL && match < pattern + length - 2)
+			return false;
+	}
+	return true;
+}
+
 /*
  * processSQLNamePattern
  *
@@ -949,8 +974,32 @@ processSQLNamePattern(PGconn *conn, PQExpBuffer buf, const char *pattern,
 	{
 		/* We have a name pattern, so constrain the namevar(s) */
 
+		if (isExactMatchRegex(namebuf.data))
+		{
+			/*
+			 * Looking for an exact match, so we can use plain equality as
+			 * long as we strip the leading "^(" and trailing ")$".
+			 */
+			namebuf.data[namebuf.len - 2] = '\0';
+			WHEREAND();
+			if (altnamevar)
+			{
+				appendPQExpBuffer(buf, "(%s OPERATOR(pg_catalog.=) ", namevar);
+				appendStringLiteralConn(buf, &namebuf.data[2], conn);
+				appendPQExpBuffer(buf, "\n        OR %s OPERATOR(pg_catalog.=) ",
+								  altnamevar);
+				appendStringLiteralConn(buf, &namebuf.data[2], conn);
+				appendPQExpBufferStr(buf, ")\n");
+			}
+			else
+			{
+				appendPQExpBuffer(buf, "%s OPERATOR(pg_catalog.=) ", namevar);
+				appendStringLiteralConn(buf, &namebuf.data[2], conn);
+				appendPQExpBufferChar(buf, '\n');
+			}
+		}
 		/* Optimize away a "*" pattern */
-		if (strcmp(namebuf.data, "^(.*)$") != 0)
+		else if (strcmp(namebuf.data, "^(.*)$") != 0)
 		{
 			WHEREAND();
 			if (altnamevar)
@@ -983,8 +1032,20 @@ processSQLNamePattern(PGconn *conn, PQExpBuffer buf, const char *pattern,
 	{
 		/* We have a schema pattern, so constrain the schemavar */
 
+		if (isExactMatchRegex(schemabuf.data))
+		{
+			/*
+			 * Looking for an exact match, so we can use plain equality as
+			 * long as we strip the leading "^(" and trailing ")$".
+			 */
+			schemabuf.data[schemabuf.len - 2] = '\0';
+			WHEREAND();
+			appendPQExpBuffer(buf, "%s OPERATOR(pg_catalog.=) ", schemavar);
+			appendStringLiteralConn(buf, &schemabuf.data[2], conn);
+			appendPQExpBufferChar(buf, '\n');
+		}
 		/* Optimize away a "*" pattern */
-		if (strcmp(schemabuf.data, "^(.*)$") != 0 && schemavar)
+		else if (strcmp(schemabuf.data, "^(.*)$") != 0 && schemavar)
 		{
 			WHEREAND();
 			appendPQExpBuffer(buf, "%s OPERATOR(pg_catalog.~) ", schemavar);

base-commit: 52b49b796cc7fd976f4da6aa49c9679ecdae8bd5
-- 
2.34.1

