Hi,

On Fri, 2023-11-17 at 16:10 -0800, Andres Freund wrote:

> > The requested name is already case-folded in most contexts. We can
> > do a
> > lookup first, and if that fails, case-fold and try again. I'll hack
> > up
> > a patch -- I believe that would be measurable for the proconfigs.
> 
> I'd just always case fold before lookups. The expensive bit of the
> case
> folding imo is that you need to do awkward things during hash
> lookups.

Attached are a bunch of tiny patches and some perf numbers based on
simple test described here:

https://www.postgresql.org/message-id/04c8592dbd694e4114a3ed87139a7a04e4363030.camel%40j-davis.com

0001: Use simplehash (without SH_STORE_HASH)

0002: fold before lookups

0003: have gen->name_key alias gen->name in typical case. Saves
allocations in typical case where the name is already folded.

0004: second-chance lookup in hash table (avoids case-folding for
already-folded names)

0005: Use SH_STORE_HASH

(These are split out into tiny patches for perf measurement, some are
pretty obvious but I wanted to see the impact, if any.)

Numbers below are cumulative (i.e. 0003 includes 0002 and 0001):
  master: 7899ms
    0001: 7850
    0002: 7958
    0003: 7942
    0004: 7549
    0005: 7411

I'm inclined toward all of these patches. I'll also look at adding
SH_STORE_HASH for the search_path cache.

Looks like we're on track to bring the overhead of SET search_path down
to reasonable levels. Thank you!

Regards,
        Jeff Davis

From 712c42f106d6a362ad201b56881c707af4412dc7 Mon Sep 17 00:00:00 2001
From: Jeff Davis <j...@j-davis.com>
Date: Sun, 19 Nov 2023 14:24:31 -0800
Subject: [PATCH v3 5/5] Use SH_STORE_HASH for GUC hash table.

---
 src/backend/utils/misc/guc.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index bcadbface6..1102ea9954 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -205,6 +205,7 @@ typedef struct
 	struct config_generic *gucvar;	/* -> GUC's defining structure */
 
 	/* needed by simplehash */
+	uint32	hash;
 	char status;
 } GUCHashEntry;
 
@@ -276,6 +277,8 @@ static char * guc_name_key(int elevel, const char *name);
 #define SH_EQUAL(tb, a, b)		(strcmp(a, b) == 0)
 #define	SH_SCOPE		static inline
 #define SH_DECLARE
+#define SH_GET_HASH(tb, a)		a->hash
+#define SH_STORE_HASH
 #define SH_DEFINE
 #include "lib/simplehash.h"
 
-- 
2.34.1

From 5ba89f4bdfc6b1056d6e7b78a5577c711cebd0af Mon Sep 17 00:00:00 2001
From: Jeff Davis <j...@j-davis.com>
Date: Sun, 19 Nov 2023 14:00:23 -0800
Subject: [PATCH v3 4/5] GUC: optimize for already case-folded names.

---
 src/backend/utils/misc/guc.c | 23 +++++++++++++++--------
 1 file changed, 15 insertions(+), 8 deletions(-)

diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 5e3b95e3f2..bcadbface6 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -1310,17 +1310,24 @@ find_option(const char *name, bool create_placeholders, bool skip_errors,
 {
 	GUCHashEntry *hentry;
 	int			i;
-	char *name_key;
 
 	Assert(name);
 
-	/* Look it up using the hash table. */
-	name_key = guc_name_key(elevel, name);
-	if (name_key == NULL)
-		return NULL;
-	hentry = GUCHash_lookup(guc_hashtab, name_key);
-	guc_free(name_key);
-	name_key = NULL;
+	/*
+	 * Look it up using the hash table without case-folding first, as an
+	 * optimization.
+	 */
+	hentry = GUCHash_lookup(guc_hashtab, name);
+
+	/* Try again with case folding. */
+	if (!hentry)
+	{
+		char *name_key = guc_name_key(elevel, name);
+		if (name_key == NULL)
+			return NULL;
+		hentry = GUCHash_lookup(guc_hashtab, name_key);
+		guc_free(name_key);
+	}
 
 	if (hentry)
 		return hentry->gucvar;
-- 
2.34.1

From 485dd56a0a2a50f725fcc9af2d370bfa4a197c8b Mon Sep 17 00:00:00 2001
From: Jeff Davis <j...@j-davis.com>
Date: Sun, 19 Nov 2023 13:41:49 -0800
Subject: [PATCH v3 3/5] Avoid duplicating GUC name when it's already
 case-folded.

---
 src/backend/utils/misc/guc.c   | 70 ++++++++++++++++++++++++++++------
 src/include/utils/guc_tables.h |  3 +-
 2 files changed, 60 insertions(+), 13 deletions(-)

diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index b51d10dbc0..5e3b95e3f2 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -265,6 +265,7 @@ static bool call_string_check_hook(struct config_string *conf, char **newval,
 								   void **extra, GucSource source, int elevel);
 static bool call_enum_check_hook(struct config_enum *conf, int *newval,
 								 void **extra, GucSource source, int elevel);
+static bool guc_is_name_key(const char *name);
 static char * guc_name_key(int elevel, const char *name);
 
 #define SH_PREFIX		GUCHash
@@ -903,6 +904,22 @@ get_guc_variables(int *num_vars)
 }
 
 
+/*
+ * Check if name is already case-folded.
+ */
+static bool
+guc_is_name_key(const char *name)
+{
+	for(const char *p = name; *p; p++)
+	{
+		if (*p >= 'A' && *p <= 'Z')
+			return false;
+	}
+
+	return true;
+}
+
+
 /*
  * Convert to key by case folding.
  */
@@ -953,7 +970,11 @@ build_guc_variables(void)
 		/* Rather than requiring vartype to be filled in by hand, do this: */
 		conf->gen.vartype = PGC_BOOL;
 
-		conf->gen.name_key = guc_name_key(ERROR, conf->gen.name);
+		if (guc_is_name_key(conf->gen.name))
+			conf->gen.name_key = conf->gen.name;
+		else
+			conf->gen.name_key = guc_name_key(ERROR, conf->gen.name);
+
 		num_vars++;
 	}
 
@@ -963,7 +984,11 @@ build_guc_variables(void)
 
 		conf->gen.vartype = PGC_INT;
 
-		conf->gen.name_key = guc_name_key(ERROR, conf->gen.name);
+		if (guc_is_name_key(conf->gen.name))
+			conf->gen.name_key = conf->gen.name;
+		else
+			conf->gen.name_key = guc_name_key(ERROR, conf->gen.name);
+
 		num_vars++;
 	}
 
@@ -973,7 +998,11 @@ build_guc_variables(void)
 
 		conf->gen.vartype = PGC_REAL;
 
-		conf->gen.name_key = guc_name_key(ERROR, conf->gen.name);
+		if (guc_is_name_key(conf->gen.name))
+			conf->gen.name_key = conf->gen.name;
+		else
+			conf->gen.name_key = guc_name_key(ERROR, conf->gen.name);
+
 		num_vars++;
 	}
 
@@ -983,7 +1012,11 @@ build_guc_variables(void)
 
 		conf->gen.vartype = PGC_STRING;
 
-		conf->gen.name_key = guc_name_key(ERROR, conf->gen.name);
+		if (guc_is_name_key(conf->gen.name))
+			conf->gen.name_key = conf->gen.name;
+		else
+			conf->gen.name_key = guc_name_key(ERROR, conf->gen.name);
+
 		num_vars++;
 	}
 
@@ -993,7 +1026,11 @@ build_guc_variables(void)
 
 		conf->gen.vartype = PGC_ENUM;
 
-		conf->gen.name_key = guc_name_key(ERROR, conf->gen.name);
+		if (guc_is_name_key(conf->gen.name))
+			conf->gen.name_key = conf->gen.name;
+		else
+			conf->gen.name_key = guc_name_key(ERROR, conf->gen.name);
+
 		num_vars++;
 	}
 
@@ -1212,13 +1249,18 @@ add_placeholder_variable(const char *constname, int elevel)
 		return NULL;
 	}
 
-	name_key = guc_name_key(elevel, name);
-	if (name_key == NULL)
+	if (!guc_is_name_key(name))
 	{
-		guc_free(name);
-		guc_free(var);
-		return NULL;
+		name_key = guc_name_key(elevel, name);
+		if (name_key == NULL)
+		{
+			guc_free(name);
+			guc_free(var);
+			return NULL;
+		}
 	}
+	else
+		name_key = name;
 
 	gen->name = name;
 	gen->context = PGC_USERSET;
@@ -1237,7 +1279,8 @@ add_placeholder_variable(const char *constname, int elevel)
 
 	if (!add_guc_variable((struct config_generic *) var, elevel))
 	{
-		guc_free(name_key);
+		if (name_key != name)
+			guc_free(name_key);
 		guc_free(name);
 		guc_free(var);
 		return NULL;
@@ -4796,7 +4839,10 @@ init_custom_variable(const char *name,
 	memset(gen, 0, sz);
 
 	gen->name = guc_strdup(ERROR, name);
-	gen->name_key = guc_name_key(ERROR, name);
+	if (guc_is_name_key(name))
+		gen->name_key = name;
+	else
+		gen->name_key = guc_name_key(ERROR, name);
 	gen->context = context;
 	gen->group = CUSTOM_OPTIONS;
 	gen->short_desc = short_desc;
diff --git a/src/include/utils/guc_tables.h b/src/include/utils/guc_tables.h
index 41b0316c0b..26bf7e0978 100644
--- a/src/include/utils/guc_tables.h
+++ b/src/include/utils/guc_tables.h
@@ -160,7 +160,8 @@ struct config_generic
 	int			flags;			/* flag bits, see guc.h */
 	/* variable fields, initialized at runtime: */
 	enum config_type vartype;	/* type of variable (set only at startup) */
-	const char *name_key;		/* name folded to lower case */
+	const char *name_key;		/* name folded to lower case; alias of name if
+								 * equal */
 	int			status;			/* status bits, see below */
 	GucSource	source;			/* source of the current actual value */
 	GucSource	reset_source;	/* source of the reset_value */
-- 
2.34.1

From e896e326688bef27f1f612ec64c5a0ce8fc8d6b3 Mon Sep 17 00:00:00 2001
From: Jeff Davis <j...@j-davis.com>
Date: Thu, 19 Oct 2023 23:42:58 -0700
Subject: [PATCH v3 2/5] Case fold earlier.

---
 src/backend/utils/misc/guc.c   | 102 +++++++++++++++++++++++----------
 src/include/utils/guc_tables.h |   1 +
 2 files changed, 72 insertions(+), 31 deletions(-)

diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 7295b0f00e..b51d10dbc0 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -33,6 +33,7 @@
 #include "catalog/objectaccess.h"
 #include "catalog/pg_authid.h"
 #include "catalog/pg_parameter_acl.h"
+#include "common/hashfn.h"
 #include "guc_internal.h"
 #include "libpq/pqformat.h"
 #include "parser/scansup.h"
@@ -200,7 +201,7 @@ static MemoryContext GUCMemoryContext;
  */
 typedef struct
 {
-	const char *gucname;		/* hash key */
+	const char *guc_hashkey;		/* case-folded GUC name */
 	struct config_generic *gucvar;	/* -> GUC's defining structure */
 
 	/* needed by simplehash */
@@ -264,13 +265,14 @@ static bool call_string_check_hook(struct config_string *conf, char **newval,
 								   void **extra, GucSource source, int elevel);
 static bool call_enum_check_hook(struct config_enum *conf, int *newval,
 								 void **extra, GucSource source, int elevel);
+static char * guc_name_key(int elevel, const char *name);
 
 #define SH_PREFIX		GUCHash
 #define SH_ELEMENT_TYPE	GUCHashEntry
 #define SH_KEY_TYPE		const char *
-#define	SH_KEY			gucname
+#define	SH_KEY			guc_hashkey
 #define SH_HASH_KEY(tb, key)   	guc_name_hash(key)
-#define SH_EQUAL(tb, a, b)		(guc_name_compare(a, b) == 0)
+#define SH_EQUAL(tb, a, b)		(strcmp(a, b) == 0)
 #define	SH_SCOPE		static inline
 #define SH_DECLARE
 #define SH_DEFINE
@@ -901,6 +903,24 @@ get_guc_variables(int *num_vars)
 }
 
 
+/*
+ * Convert to key by case folding.
+ */
+static char *
+guc_name_key(int elevel, const char *name)
+{
+	char *newstr = guc_strdup(elevel, name);
+
+	for(char *p = newstr; *p; p++)
+	{
+		if (*p >= 'A' && *p <= 'Z')
+			*p += 'a' - 'A';
+	}
+
+	return newstr;
+}
+
+
 /*
  * Build the GUC hash table.  This is split out so that help_config.c can
  * extract all the variables without running all of InitializeGUCOptions.
@@ -932,6 +952,8 @@ build_guc_variables(void)
 
 		/* Rather than requiring vartype to be filled in by hand, do this: */
 		conf->gen.vartype = PGC_BOOL;
+
+		conf->gen.name_key = guc_name_key(ERROR, conf->gen.name);
 		num_vars++;
 	}
 
@@ -940,6 +962,8 @@ build_guc_variables(void)
 		struct config_int *conf = &ConfigureNamesInt[i];
 
 		conf->gen.vartype = PGC_INT;
+
+		conf->gen.name_key = guc_name_key(ERROR, conf->gen.name);
 		num_vars++;
 	}
 
@@ -948,6 +972,8 @@ build_guc_variables(void)
 		struct config_real *conf = &ConfigureNamesReal[i];
 
 		conf->gen.vartype = PGC_REAL;
+
+		conf->gen.name_key = guc_name_key(ERROR, conf->gen.name);
 		num_vars++;
 	}
 
@@ -956,6 +982,8 @@ build_guc_variables(void)
 		struct config_string *conf = &ConfigureNamesString[i];
 
 		conf->gen.vartype = PGC_STRING;
+
+		conf->gen.name_key = guc_name_key(ERROR, conf->gen.name);
 		num_vars++;
 	}
 
@@ -964,6 +992,8 @@ build_guc_variables(void)
 		struct config_enum *conf = &ConfigureNamesEnum[i];
 
 		conf->gen.vartype = PGC_ENUM;
+
+		conf->gen.name_key = guc_name_key(ERROR, conf->gen.name);
 		num_vars++;
 	}
 
@@ -978,7 +1008,7 @@ build_guc_variables(void)
 	{
 		struct config_generic *gucvar = &ConfigureNamesBool[i].gen;
 
-		hentry = GUCHash_insert(guc_hashtab, gucvar->name, &found);
+		hentry = GUCHash_insert(guc_hashtab, gucvar->name_key, &found);
 
 		Assert(!found);
 		hentry->gucvar = gucvar;
@@ -988,7 +1018,7 @@ build_guc_variables(void)
 	{
 		struct config_generic *gucvar = &ConfigureNamesInt[i].gen;
 
-		hentry = GUCHash_insert(guc_hashtab, gucvar->name, &found);
+		hentry = GUCHash_insert(guc_hashtab, gucvar->name_key, &found);
 
 		Assert(!found);
 		hentry->gucvar = gucvar;
@@ -998,7 +1028,7 @@ build_guc_variables(void)
 	{
 		struct config_generic *gucvar = &ConfigureNamesReal[i].gen;
 
-		hentry = GUCHash_insert(guc_hashtab, gucvar->name, &found);
+		hentry = GUCHash_insert(guc_hashtab, gucvar->name_key, &found);
 
 		Assert(!found);
 		hentry->gucvar = gucvar;
@@ -1008,7 +1038,7 @@ build_guc_variables(void)
 	{
 		struct config_generic *gucvar = &ConfigureNamesString[i].gen;
 
-		hentry = GUCHash_insert(guc_hashtab, gucvar->name, &found);
+		hentry = GUCHash_insert(guc_hashtab, gucvar->name_key, &found);
 
 		Assert(!found);
 		hentry->gucvar = gucvar;
@@ -1018,7 +1048,7 @@ build_guc_variables(void)
 	{
 		struct config_generic *gucvar = &ConfigureNamesEnum[i].gen;
 
-		hentry = GUCHash_insert(guc_hashtab, gucvar->name, &found);
+		hentry = GUCHash_insert(guc_hashtab, gucvar->name_key, &found);
 
 		Assert(!found);
 		hentry->gucvar = gucvar;
@@ -1037,7 +1067,8 @@ add_guc_variable(struct config_generic *var, int elevel)
 	GUCHashEntry *hentry;
 	bool		found;
 
-	hentry = GUCHash_insert(guc_hashtab, var->name, &found);
+	Assert(var->name_key);
+	hentry = GUCHash_insert(guc_hashtab, var->name_key, &found);
 
 	if (unlikely(hentry == NULL))
 	{
@@ -1160,11 +1191,13 @@ assignable_custom_variable_name(const char *name, bool skip_errors, int elevel)
  * Create and add a placeholder variable for a custom variable name.
  */
 static struct config_generic *
-add_placeholder_variable(const char *name, int elevel)
+add_placeholder_variable(const char *constname, int elevel)
 {
 	size_t		sz = sizeof(struct config_string) + sizeof(char *);
 	struct config_string *var;
 	struct config_generic *gen;
+	char *name;
+	char *name_key;
 
 	var = (struct config_string *) guc_malloc(elevel, sz);
 	if (var == NULL)
@@ -1172,18 +1205,28 @@ add_placeholder_variable(const char *name, int elevel)
 	memset(var, 0, sz);
 	gen = &var->gen;
 
-	gen->name = guc_strdup(elevel, name);
-	if (gen->name == NULL)
+	name = guc_strdup(elevel, constname);
+	if (name == NULL)
 	{
 		guc_free(var);
 		return NULL;
 	}
 
+	name_key = guc_name_key(elevel, name);
+	if (name_key == NULL)
+	{
+		guc_free(name);
+		guc_free(var);
+		return NULL;
+	}
+
+	gen->name = name;
 	gen->context = PGC_USERSET;
 	gen->group = CUSTOM_OPTIONS;
 	gen->short_desc = "GUC placeholder variable";
 	gen->flags = GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE | GUC_CUSTOM_PLACEHOLDER;
 	gen->vartype = PGC_STRING;
+	gen->name_key = name_key;
 
 	/*
 	 * The char* is allocated at the end of the struct since we have no
@@ -1194,7 +1237,8 @@ add_placeholder_variable(const char *name, int elevel)
 
 	if (!add_guc_variable((struct config_generic *) var, elevel))
 	{
-		guc_free(unconstify(char *, gen->name));
+		guc_free(name_key);
+		guc_free(name);
 		guc_free(var);
 		return NULL;
 	}
@@ -1223,11 +1267,17 @@ find_option(const char *name, bool create_placeholders, bool skip_errors,
 {
 	GUCHashEntry *hentry;
 	int			i;
+	char *name_key;
 
 	Assert(name);
 
 	/* Look it up using the hash table. */
-	hentry = GUCHash_lookup(guc_hashtab, name);
+	name_key = guc_name_key(elevel, name);
+	if (name_key == NULL)
+		return NULL;
+	hentry = GUCHash_lookup(guc_hashtab, name_key);
+	guc_free(name_key);
+	name_key = NULL;
 
 	if (hentry)
 		return hentry->gucvar;
@@ -1313,21 +1363,10 @@ guc_name_compare(const char *namea, const char *nameb)
 static uint32
 guc_name_hash(const char *name)
 {
-	uint32		result = 0;
+	const unsigned char *bytes = (const unsigned char *)name;
+	int                  blen  = strlen(name);
 
-	while (*name)
-	{
-		char		ch = *name++;
-
-		/* Case-fold in the same way as guc_name_compare */
-		if (ch >= 'A' && ch <= 'Z')
-			ch += 'a' - 'A';
-
-		/* Merge into hash ... not very bright, but it needn't be */
-		result = pg_rotate_left32(result, 5);
-		result ^= (uint32) ch;
-	}
-	return result;
+	return hash_bytes(bytes, blen);
 }
 
 /*
@@ -4757,6 +4796,7 @@ init_custom_variable(const char *name,
 	memset(gen, 0, sz);
 
 	gen->name = guc_strdup(ERROR, name);
+	gen->name_key = guc_name_key(ERROR, name);
 	gen->context = context;
 	gen->group = CUSTOM_OPTIONS;
 	gen->short_desc = short_desc;
@@ -4784,7 +4824,7 @@ define_custom_variable(struct config_generic *variable)
 	/*
 	 * See if there's a placeholder by the same name.
 	 */
-	hentry = GUCHash_lookup(guc_hashtab, name);
+	hentry = GUCHash_lookup(guc_hashtab, variable->name_key);
 
 	if (hentry == NULL)
 	{
@@ -4819,7 +4859,7 @@ define_custom_variable(struct config_generic *variable)
 	 * Replace the placeholder in the hash table.  We aren't changing the name
 	 * (at least up to case-folding), so the hash value is unchanged.
 	 */
-	hentry->gucname = name;
+	hentry->guc_hashkey = variable->name_key;
 	hentry->gucvar = variable;
 
 	/*
@@ -5147,7 +5187,7 @@ MarkGUCPrefixReserved(const char *className)
 					 errdetail("\"%s\" is now a reserved prefix.",
 							   className)));
 			/* Remove it from the hash table */
-			GUCHash_delete(guc_hashtab, var->name);
+			GUCHash_delete(guc_hashtab, var->name_key);
 
 			/* Remove it from any lists it's in, too */
 			RemoveGUCFromLists(var);
diff --git a/src/include/utils/guc_tables.h b/src/include/utils/guc_tables.h
index 0c38255961..41b0316c0b 100644
--- a/src/include/utils/guc_tables.h
+++ b/src/include/utils/guc_tables.h
@@ -160,6 +160,7 @@ struct config_generic
 	int			flags;			/* flag bits, see guc.h */
 	/* variable fields, initialized at runtime: */
 	enum config_type vartype;	/* type of variable (set only at startup) */
+	const char *name_key;		/* name folded to lower case */
 	int			status;			/* status bits, see below */
 	GucSource	source;			/* source of the current actual value */
 	GucSource	reset_source;	/* source of the reset_value */
-- 
2.34.1

From b1dc4bd1a732fb6572a0e4d5a966709ca30ddf79 Mon Sep 17 00:00:00 2001
From: Jeff Davis <j...@j-davis.com>
Date: Wed, 2 Aug 2023 23:04:06 -0700
Subject: [PATCH v3 1/5] Convert GUC hashtable to use simplehash.

---
 src/backend/utils/misc/guc.c | 141 ++++++++++++++---------------------
 1 file changed, 56 insertions(+), 85 deletions(-)

diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 82d8efbc96..7295b0f00e 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -202,9 +202,10 @@ typedef struct
 {
 	const char *gucname;		/* hash key */
 	struct config_generic *gucvar;	/* -> GUC's defining structure */
-} GUCHashEntry;
 
-static HTAB *guc_hashtab;		/* entries are GUCHashEntrys */
+	/* needed by simplehash */
+	char status;
+} GUCHashEntry;
 
 /*
  * In addition to the hash table, variables having certain properties are
@@ -227,8 +228,7 @@ static int	GUCNestLevel = 0;	/* 1 when in main transaction */
 
 
 static int	guc_var_compare(const void *a, const void *b);
-static uint32 guc_name_hash(const void *key, Size keysize);
-static int	guc_name_match(const void *key1, const void *key2, Size keysize);
+static uint32 guc_name_hash(const char *name);
 static void InitializeGUCOptionsFromEnvironment(void);
 static void InitializeOneGUCOption(struct config_generic *gconf);
 static void RemoveGUCFromLists(struct config_generic *gconf);
@@ -265,6 +265,18 @@ static bool call_string_check_hook(struct config_string *conf, char **newval,
 static bool call_enum_check_hook(struct config_enum *conf, int *newval,
 								 void **extra, GucSource source, int elevel);
 
+#define SH_PREFIX		GUCHash
+#define SH_ELEMENT_TYPE	GUCHashEntry
+#define SH_KEY_TYPE		const char *
+#define	SH_KEY			gucname
+#define SH_HASH_KEY(tb, key)   	guc_name_hash(key)
+#define SH_EQUAL(tb, a, b)		(guc_name_compare(a, b) == 0)
+#define	SH_SCOPE		static inline
+#define SH_DECLARE
+#define SH_DEFINE
+#include "lib/simplehash.h"
+
+static GUCHash_hash *guc_hashtab = NULL;	/* entries are GUCHashEntrys */
 
 /*
  * This function handles both actual config file (re)loads and execution of
@@ -282,7 +294,7 @@ ProcessConfigFileInternal(GucContext context, bool applySettings, int elevel)
 	ConfigVariable *item,
 			   *head,
 			   *tail;
-	HASH_SEQ_STATUS status;
+	GUCHash_iterator iter;
 	GUCHashEntry *hentry;
 
 	/* Parse the main config file into a list of option names and values */
@@ -358,8 +370,8 @@ ProcessConfigFileInternal(GucContext context, bool applySettings, int elevel)
 	 * need this so that we can tell below which ones have been removed from
 	 * the file since we last processed it.
 	 */
-	hash_seq_init(&status, guc_hashtab);
-	while ((hentry = (GUCHashEntry *) hash_seq_search(&status)) != NULL)
+	GUCHash_start_iterate(guc_hashtab, &iter);
+	while ((hentry = GUCHash_iterate(guc_hashtab, &iter)) != NULL)
 	{
 		struct config_generic *gconf = hentry->gucvar;
 
@@ -445,8 +457,8 @@ ProcessConfigFileInternal(GucContext context, bool applySettings, int elevel)
 	 * boot-time defaults.  If such a variable can't be changed after startup,
 	 * report that and continue.
 	 */
-	hash_seq_init(&status, guc_hashtab);
-	while ((hentry = (GUCHashEntry *) hash_seq_search(&status)) != NULL)
+	GUCHash_start_iterate(guc_hashtab, &iter);
+	while ((hentry = GUCHash_iterate(guc_hashtab, &iter)) != NULL)
 	{
 		struct config_generic *gconf = hentry->gucvar;
 		GucStack   *stack;
@@ -867,17 +879,17 @@ struct config_generic **
 get_guc_variables(int *num_vars)
 {
 	struct config_generic **result;
-	HASH_SEQ_STATUS status;
+	GUCHash_iterator iter;
 	GUCHashEntry *hentry;
 	int			i;
 
-	*num_vars = hash_get_num_entries(guc_hashtab);
+	*num_vars = guc_hashtab->members;
 	result = palloc(sizeof(struct config_generic *) * *num_vars);
 
 	/* Extract pointers from the hash table */
 	i = 0;
-	hash_seq_init(&status, guc_hashtab);
-	while ((hentry = (GUCHashEntry *) hash_seq_search(&status)) != NULL)
+	GUCHash_start_iterate(guc_hashtab, &iter);
+	while ((hentry = GUCHash_iterate(guc_hashtab, &iter)) != NULL)
 		result[i++] = hentry->gucvar;
 	Assert(i == *num_vars);
 
@@ -899,7 +911,6 @@ build_guc_variables(void)
 {
 	int			size_vars;
 	int			num_vars = 0;
-	HASHCTL		hash_ctl;
 	GUCHashEntry *hentry;
 	bool		found;
 	int			i;
@@ -961,24 +972,14 @@ build_guc_variables(void)
 	 */
 	size_vars = num_vars + num_vars / 4;
 
-	hash_ctl.keysize = sizeof(char *);
-	hash_ctl.entrysize = sizeof(GUCHashEntry);
-	hash_ctl.hash = guc_name_hash;
-	hash_ctl.match = guc_name_match;
-	hash_ctl.hcxt = GUCMemoryContext;
-	guc_hashtab = hash_create("GUC hash table",
-							  size_vars,
-							  &hash_ctl,
-							  HASH_ELEM | HASH_FUNCTION | HASH_COMPARE | HASH_CONTEXT);
+	guc_hashtab = GUCHash_create(GUCMemoryContext, size_vars, NULL);
 
 	for (i = 0; ConfigureNamesBool[i].gen.name; i++)
 	{
 		struct config_generic *gucvar = &ConfigureNamesBool[i].gen;
 
-		hentry = (GUCHashEntry *) hash_search(guc_hashtab,
-											  &gucvar->name,
-											  HASH_ENTER,
-											  &found);
+		hentry = GUCHash_insert(guc_hashtab, gucvar->name, &found);
+
 		Assert(!found);
 		hentry->gucvar = gucvar;
 	}
@@ -987,10 +988,8 @@ build_guc_variables(void)
 	{
 		struct config_generic *gucvar = &ConfigureNamesInt[i].gen;
 
-		hentry = (GUCHashEntry *) hash_search(guc_hashtab,
-											  &gucvar->name,
-											  HASH_ENTER,
-											  &found);
+		hentry = GUCHash_insert(guc_hashtab, gucvar->name, &found);
+
 		Assert(!found);
 		hentry->gucvar = gucvar;
 	}
@@ -999,10 +998,8 @@ build_guc_variables(void)
 	{
 		struct config_generic *gucvar = &ConfigureNamesReal[i].gen;
 
-		hentry = (GUCHashEntry *) hash_search(guc_hashtab,
-											  &gucvar->name,
-											  HASH_ENTER,
-											  &found);
+		hentry = GUCHash_insert(guc_hashtab, gucvar->name, &found);
+
 		Assert(!found);
 		hentry->gucvar = gucvar;
 	}
@@ -1011,10 +1008,8 @@ build_guc_variables(void)
 	{
 		struct config_generic *gucvar = &ConfigureNamesString[i].gen;
 
-		hentry = (GUCHashEntry *) hash_search(guc_hashtab,
-											  &gucvar->name,
-											  HASH_ENTER,
-											  &found);
+		hentry = GUCHash_insert(guc_hashtab, gucvar->name, &found);
+
 		Assert(!found);
 		hentry->gucvar = gucvar;
 	}
@@ -1023,15 +1018,13 @@ build_guc_variables(void)
 	{
 		struct config_generic *gucvar = &ConfigureNamesEnum[i].gen;
 
-		hentry = (GUCHashEntry *) hash_search(guc_hashtab,
-											  &gucvar->name,
-											  HASH_ENTER,
-											  &found);
+		hentry = GUCHash_insert(guc_hashtab, gucvar->name, &found);
+
 		Assert(!found);
 		hentry->gucvar = gucvar;
 	}
 
-	Assert(num_vars == hash_get_num_entries(guc_hashtab));
+	Assert(num_vars == guc_hashtab->members);
 }
 
 /*
@@ -1044,10 +1037,8 @@ add_guc_variable(struct config_generic *var, int elevel)
 	GUCHashEntry *hentry;
 	bool		found;
 
-	hentry = (GUCHashEntry *) hash_search(guc_hashtab,
-										  &var->name,
-										  HASH_ENTER_NULL,
-										  &found);
+	hentry = GUCHash_insert(guc_hashtab, var->name, &found);
+
 	if (unlikely(hentry == NULL))
 	{
 		ereport(elevel,
@@ -1236,10 +1227,8 @@ find_option(const char *name, bool create_placeholders, bool skip_errors,
 	Assert(name);
 
 	/* Look it up using the hash table. */
-	hentry = (GUCHashEntry *) hash_search(guc_hashtab,
-										  &name,
-										  HASH_FIND,
-										  NULL);
+	hentry = GUCHash_lookup(guc_hashtab, name);
+
 	if (hentry)
 		return hentry->gucvar;
 
@@ -1322,10 +1311,9 @@ guc_name_compare(const char *namea, const char *nameb)
  * Hash function that's compatible with guc_name_compare
  */
 static uint32
-guc_name_hash(const void *key, Size keysize)
+guc_name_hash(const char *name)
 {
 	uint32		result = 0;
-	const char *name = *(const char *const *) key;
 
 	while (*name)
 	{
@@ -1342,19 +1330,6 @@ guc_name_hash(const void *key, Size keysize)
 	return result;
 }
 
-/*
- * Dynahash match function to use in guc_hashtab
- */
-static int
-guc_name_match(const void *key1, const void *key2, Size keysize)
-{
-	const char *name1 = *(const char *const *) key1;
-	const char *name2 = *(const char *const *) key2;
-
-	return guc_name_compare(name1, name2);
-}
-
-
 /*
  * Convert a GUC name to the form that should be used in pg_parameter_acl.
  *
@@ -1524,7 +1499,7 @@ check_GUC_init(struct config_generic *gconf)
 void
 InitializeGUCOptions(void)
 {
-	HASH_SEQ_STATUS status;
+	GUCHash_iterator iter;
 	GUCHashEntry *hentry;
 
 	/*
@@ -1542,8 +1517,8 @@ InitializeGUCOptions(void)
 	 * Load all variables with their compiled-in defaults, and initialize
 	 * status fields as needed.
 	 */
-	hash_seq_init(&status, guc_hashtab);
-	while ((hentry = (GUCHashEntry *) hash_seq_search(&status)) != NULL)
+	GUCHash_start_iterate(guc_hashtab, &iter);
+	while ((hentry = GUCHash_iterate(guc_hashtab, &iter)) != NULL)
 	{
 		/* Check mapping between initial and default value */
 		Assert(check_GUC_init(hentry->gucvar));
@@ -2528,7 +2503,7 @@ AtEOXact_GUC(bool isCommit, int nestLevel)
 void
 BeginReportingGUCOptions(void)
 {
-	HASH_SEQ_STATUS status;
+	GUCHash_iterator iter;
 	GUCHashEntry *hentry;
 
 	/*
@@ -2552,8 +2527,8 @@ BeginReportingGUCOptions(void)
 						PGC_INTERNAL, PGC_S_OVERRIDE);
 
 	/* Transmit initial values of interesting variables */
-	hash_seq_init(&status, guc_hashtab);
-	while ((hentry = (GUCHashEntry *) hash_seq_search(&status)) != NULL)
+	GUCHash_start_iterate(guc_hashtab, &iter);
+	while ((hentry = GUCHash_iterate(guc_hashtab, &iter)) != NULL)
 	{
 		struct config_generic *conf = hentry->gucvar;
 
@@ -4809,10 +4784,8 @@ define_custom_variable(struct config_generic *variable)
 	/*
 	 * See if there's a placeholder by the same name.
 	 */
-	hentry = (GUCHashEntry *) hash_search(guc_hashtab,
-										  &name,
-										  HASH_FIND,
-										  NULL);
+	hentry = GUCHash_lookup(guc_hashtab, name);
+
 	if (hentry == NULL)
 	{
 		/*
@@ -5148,7 +5121,7 @@ void
 MarkGUCPrefixReserved(const char *className)
 {
 	int			classLen = strlen(className);
-	HASH_SEQ_STATUS status;
+	GUCHash_iterator iter;
 	GUCHashEntry *hentry;
 	MemoryContext oldcontext;
 
@@ -5158,8 +5131,8 @@ MarkGUCPrefixReserved(const char *className)
 	 * don't bother trying to free associated memory, since this shouldn't
 	 * happen often.)
 	 */
-	hash_seq_init(&status, guc_hashtab);
-	while ((hentry = (GUCHashEntry *) hash_seq_search(&status)) != NULL)
+	GUCHash_start_iterate(guc_hashtab, &iter);
+	while ((hentry = GUCHash_iterate(guc_hashtab, &iter)) != NULL)
 	{
 		struct config_generic *var = hentry->gucvar;
 
@@ -5174,10 +5147,8 @@ MarkGUCPrefixReserved(const char *className)
 					 errdetail("\"%s\" is now a reserved prefix.",
 							   className)));
 			/* Remove it from the hash table */
-			hash_search(guc_hashtab,
-						&var->name,
-						HASH_REMOVE,
-						NULL);
+			GUCHash_delete(guc_hashtab, var->name);
+
 			/* Remove it from any lists it's in, too */
 			RemoveGUCFromLists(var);
 		}
@@ -5208,7 +5179,7 @@ get_explain_guc_options(int *num)
 	 * While only a fraction of all the GUC variables are marked GUC_EXPLAIN,
 	 * it doesn't seem worth dynamically resizing this array.
 	 */
-	result = palloc(sizeof(struct config_generic *) * hash_get_num_entries(guc_hashtab));
+	result = palloc(sizeof(struct config_generic *) * guc_hashtab->members);
 
 	/* We need only consider GUCs with source not PGC_S_DEFAULT */
 	dlist_foreach(iter, &guc_nondef_list)
-- 
2.34.1

Reply via email to