On Tue, 2023-11-21 at 09:24 -0500, Robert Haas wrote: > As to the second, could we somehow come > up with an API for guc.c where you can ask for an opaque handle that > will later allow you to do a fast-SET of a GUC?
Yes, attached. That provides a significant speedup: my test goes from around ~7300ms to ~6800ms. This doesn't seem very controversial or complex, so I'll probably commit this soon unless someone else has a comment. -- Jeff Davis PostgreSQL Contributor Team - AWS
From 906cb1cdf42f92090d4a9acf296098ec3bfa53e0 Mon Sep 17 00:00:00 2001 From: Jeff Davis <j...@j-davis.com> Date: Mon, 4 Dec 2023 16:20:05 -0800 Subject: [PATCH v1] Cache opaque handle for GUC option to avoid repeasted lookups. When setting GUCs from proconfig, performance is important, and hash lookups in the GUC table are significant. Per suggestion from Robert Haas. Discussion: https://postgr.es/m/ca+tgmoypkxhr3hod9syk2xwcauvpa0+ba0xpnwwbcyxtklk...@mail.gmail.com --- src/backend/utils/fmgr/fmgr.c | 32 ++++++++++++----- src/backend/utils/misc/guc.c | 67 ++++++++++++++++++++++++++++++++--- src/include/utils/guc.h | 8 +++++ 3 files changed, 94 insertions(+), 13 deletions(-) diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c index 9dfdf890c5..6aac698e4b 100644 --- a/src/backend/utils/fmgr/fmgr.c +++ b/src/backend/utils/fmgr/fmgr.c @@ -612,7 +612,7 @@ struct fmgr_security_definer_cache { FmgrInfo flinfo; /* lookup info for target function */ Oid userid; /* userid to set, or InvalidOid */ - List *configNames; /* GUC names to set, or NIL */ + List *configHandles; /* GUC handles to set, or NIL */ List *configValues; /* GUC values to set, or NIL */ Datum arg; /* passthrough argument for plugin modules */ }; @@ -670,11 +670,25 @@ fmgr_security_definer(PG_FUNCTION_ARGS) if (!isnull) { ArrayType *array; + ListCell *lc; + List *names; oldcxt = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt); array = DatumGetArrayTypeP(datum); - TransformGUCArray(array, &fcache->configNames, + TransformGUCArray(array, &names, &fcache->configValues); + + /* transform names to config handles to avoid lookup cost */ + fcache->configHandles = NIL; + foreach(lc, names) + { + char *name = (char *) lfirst(lc); + + fcache->configHandles = lappend(fcache->configHandles, + get_config_handle(name)); + } + list_free_deep(names); + MemoryContextSwitchTo(oldcxt); } @@ -687,7 +701,7 @@ fmgr_security_definer(PG_FUNCTION_ARGS) /* GetUserIdAndSecContext is cheap enough that no harm in a wasted call */ GetUserIdAndSecContext(&save_userid, &save_sec_context); - if (fcache->configNames != NIL) /* Need a new GUC nesting level */ + if (fcache->configHandles != NIL) /* Need a new GUC nesting level */ save_nestlevel = NewGUCNestLevel(); else save_nestlevel = 0; /* keep compiler quiet */ @@ -696,17 +710,17 @@ fmgr_security_definer(PG_FUNCTION_ARGS) SetUserIdAndSecContext(fcache->userid, save_sec_context | SECURITY_LOCAL_USERID_CHANGE); - forboth(lc1, fcache->configNames, lc2, fcache->configValues) + forboth(lc1, fcache->configHandles, lc2, fcache->configValues) { GucContext context = superuser() ? PGC_SUSET : PGC_USERSET; GucSource source = PGC_S_SESSION; GucAction action = GUC_ACTION_SAVE; - char *name = lfirst(lc1); + config_handle *handle = lfirst(lc1); char *value = lfirst(lc2); - (void) set_config_option(name, value, - context, source, - action, true, 0, false); + (void) set_config_with_handle(handle, value, + context, source, GetUserId(), + action, true, 0, false); } /* function manager hook */ @@ -749,7 +763,7 @@ fmgr_security_definer(PG_FUNCTION_ARGS) fcinfo->flinfo = save_flinfo; - if (fcache->configNames != NIL) + if (fcache->configHandles != NIL) AtEOXact_GUC(true, save_nestlevel); if (OidIsValid(fcache->userid)) SetUserIdAndSecContext(save_userid, save_sec_context); diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index e76c083003..641dc264df 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -3357,6 +3357,52 @@ set_config_option_ext(const char *name, const char *value, bool is_reload) { struct config_generic *record; + + if (elevel == 0) + { + if (source == PGC_S_DEFAULT || source == PGC_S_FILE) + { + /* + * To avoid cluttering the log, only the postmaster bleats loudly + * about problems with the config file. + */ + elevel = IsUnderPostmaster ? DEBUG3 : LOG; + } + else if (source == PGC_S_GLOBAL || + source == PGC_S_DATABASE || + source == PGC_S_USER || + source == PGC_S_DATABASE_USER) + elevel = WARNING; + else + elevel = ERROR; + } + + record = find_option(name, true, false, elevel); + if (record == NULL) + return 0; + + return set_config_with_handle((config_handle *) record, value, + context, source, srole, + action, changeVal, elevel, + is_reload); +} + + +/* + * set_config_option_ext: sets option with the given handle to the given + * value. + * + * This should be used by callers that repeatedly set the same config + * option(s), and want to avoid the overhead of a hash lookup each time. + */ +int +set_config_with_handle(config_handle * handle, const char *value, + GucContext context, GucSource source, Oid srole, + GucAction action, bool changeVal, int elevel, + bool is_reload) +{ + struct config_generic *record = (struct config_generic *) handle; + const char *name = record->name; union config_var_val newval_union; void *newextra = NULL; bool prohibitValueChange = false; @@ -3395,10 +3441,6 @@ set_config_option_ext(const char *name, const char *value, (errcode(ERRCODE_INVALID_TRANSACTION_STATE), errmsg("cannot set parameters during a parallel operation"))); - record = find_option(name, true, false, elevel); - if (record == NULL) - return 0; - /* * Check if the option can be set at this time. See guc.h for the precise * rules. @@ -4166,6 +4208,23 @@ set_config_option_ext(const char *name, const char *value, } +/* + * Retrieve a config_handle for the given name, suitable for calling + * set_config_with_handle(). + */ +config_handle * +get_config_handle(const char *name) +{ + struct config_generic *record; + + record = find_option(name, false, false, 0); + if (record == NULL) + return 0; + + return (config_handle *) record; +} + + /* * Set the fields for source file and line number the setting came from. */ diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h index 20fe13702b..5847776c39 100644 --- a/src/include/utils/guc.h +++ b/src/include/utils/guc.h @@ -144,6 +144,8 @@ typedef struct ConfigVariable struct ConfigVariable *next; } ConfigVariable; +typedef struct config_handle config_handle; + extern bool ParseConfigFile(const char *config_file, bool strict, const char *calling_file, int calling_lineno, int depth, int elevel, @@ -387,6 +389,12 @@ extern int set_config_option_ext(const char *name, const char *value, Oid srole, GucAction action, bool changeVal, int elevel, bool is_reload); +extern int set_config_with_handle(config_handle * handle, const char *value, + GucContext context, GucSource source, + Oid srole, + GucAction action, bool changeVal, + int elevel, bool is_reload); +extern config_handle * get_config_handle(const char *name); extern void AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt); extern char *GetConfigOptionByName(const char *name, const char **varname, bool missing_ok); -- 2.34.1