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