diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c index 5da5981726..3af15dd38b 100644 --- a/src/backend/commands/extension.c +++ b/src/backend/commands/extension.c @@ -774,9 +774,7 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control, const char *schemaName, Oid schemaOid) { char *filename; - char *save_client_min_messages, - *save_log_min_messages, - *save_search_path; + int save_nestlevel; StringInfoData pathbuf; ListCell *lc; @@ -808,22 +806,20 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control, * so that we won't spam the user with useless NOTICE messages from common * script actions like creating shell types. * - * We use the equivalent of SET LOCAL to ensure the setting is undone upon - * error. + * We use the equivalent of a function SET option to allow the setting to + * persist for exactly the duration of the script execution. guc.c also + * takes care of undoing the setting on error. */ - save_client_min_messages = - pstrdup(GetConfigOption("client_min_messages", false, false)); + save_nestlevel = NewGUCNestLevel(); + if (client_min_messages < WARNING) (void) set_config_option("client_min_messages", "warning", PGC_USERSET, PGC_S_SESSION, - GUC_ACTION_LOCAL, true, 0); - - save_log_min_messages = - pstrdup(GetConfigOption("log_min_messages", false, false)); + GUC_ACTION_SAVE, true, 0); if (log_min_messages < WARNING) (void) set_config_option("log_min_messages", "warning", PGC_SUSET, PGC_S_SESSION, - GUC_ACTION_LOCAL, true, 0); + GUC_ACTION_SAVE, true, 0); /* * Set up the search path to contain the target schema, then the schemas @@ -832,10 +828,9 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control, * * Note: it might look tempting to use PushOverrideSearchPath for this, * but we cannot do that. We have to actually set the search_path GUC in - * case the extension script examines or changes it. + * case the extension script examines or changes it. In any case, the + * GUC_ACTION_SAVE method is just as convenient. */ - save_search_path = pstrdup(GetConfigOption("search_path", false, false)); - initStringInfo(&pathbuf); appendStringInfoString(&pathbuf, quote_identifier(schemaName)); foreach(lc, requiredSchemas) @@ -849,7 +844,7 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control, (void) set_config_option("search_path", pathbuf.data, PGC_USERSET, PGC_S_SESSION, - GUC_ACTION_LOCAL, true, 0); + GUC_ACTION_SAVE, true, 0); /* * Set creating_extension and related variables so that @@ -910,18 +905,9 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control, CurrentExtensionObject = InvalidOid; /* - * Restore GUC variables for the remainder of the current transaction. - * Again use SET LOCAL, so we won't affect the session value. + * Restore the GUC variables we set above. */ - (void) set_config_option("search_path", save_search_path, - PGC_USERSET, PGC_S_SESSION, - GUC_ACTION_LOCAL, true, 0); - (void) set_config_option("client_min_messages", save_client_min_messages, - PGC_USERSET, PGC_S_SESSION, - GUC_ACTION_LOCAL, true, 0); - (void) set_config_option("log_min_messages", save_log_min_messages, - PGC_SUSET, PGC_S_SESSION, - GUC_ACTION_LOCAL, true, 0); + AtEOXact_GUC(true, save_nestlevel); } /* diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index b913e85b31..522a540c0c 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -2633,7 +2633,7 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) RangeTblEntry *fkrte; const char *sep; int i; - int old_work_mem; + int save_nestlevel; char workmembuf[32]; int spi_result; SPIPlanPtr qplan; @@ -2772,14 +2772,16 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) * this seems to meet the criteria for being considered a "maintenance" * operation, and accordingly we use maintenance_work_mem. * - * We do the equivalent of "SET LOCAL work_mem" so that transaction abort - * will restore the old value if we lose control due to an error. + * We use the equivalent of a function SET option to allow the setting to + * persist for exactly the duration of the check query. guc.c also takes + * care of undoing the setting on error. */ - old_work_mem = work_mem; + save_nestlevel = NewGUCNestLevel(); + snprintf(workmembuf, sizeof(workmembuf), "%d", maintenance_work_mem); (void) set_config_option("work_mem", workmembuf, PGC_USERSET, PGC_S_SESSION, - GUC_ACTION_LOCAL, true, 0); + GUC_ACTION_SAVE, true, 0); if (SPI_connect() != SPI_OK_CONNECT) elog(ERROR, "SPI_connect failed"); @@ -2862,13 +2864,9 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) elog(ERROR, "SPI_finish failed"); /* - * Restore work_mem for the remainder of the current transaction. This is - * another SET LOCAL, so it won't affect the session value. + * Restore work_mem. */ - snprintf(workmembuf, sizeof(workmembuf), "%d", old_work_mem); - (void) set_config_option("work_mem", workmembuf, - PGC_USERSET, PGC_S_SESSION, - GUC_ACTION_LOCAL, true, 0); + AtEOXact_GUC(true, save_nestlevel); return true; } diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 151cb190c3..85fdad8996 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -4329,8 +4329,9 @@ AtStart_GUC(void) /* * Enter a new nesting level for GUC values. This is called at subtransaction - * start and when entering a function that has proconfig settings. NOTE that - * we must not risk error here, else subtransaction start will be unhappy. + * start, and when entering a function that has proconfig settings, and in + * some other places where we want to set GUC variables transiently. + * NOTE we must not risk error here, else subtransaction start will be unhappy. */ int NewGUCNestLevel(void) @@ -4340,8 +4341,9 @@ NewGUCNestLevel(void) /* * Do GUC processing at transaction or subtransaction commit or abort, or - * when exiting a function that has proconfig settings. (The name is thus - * a bit of a misnomer; perhaps it should be ExitGUCNestLevel or some such.) + * when exiting a function that has proconfig settings, or when undoing a + * transient assignment to some GUC variables. (The name is thus a bit of + * a misnomer; perhaps it should be ExitGUCNestLevel or some such.) * During abort, we discard all GUC settings that were applied at nesting * levels >= nestLevel. nestLevel == 1 corresponds to the main transaction. */ @@ -4374,11 +4376,11 @@ AtEOXact_GUC(bool isCommit, int nestLevel) GucStack *stack; /* - * Process and pop each stack entry within the nest level. To - * simplify fmgr_security_definer(), we allow failure exit from a - * function-with-SET-options to be recovered at the surrounding - * transaction or subtransaction abort; so there could be more than - * one stack entry to pop. + * Process and pop each stack entry within the nest level. To simplify + * fmgr_security_definer() and other places that use GUC_ACTION_SAVE, + * we allow failure exit from code that uses a local nest level to be + * recovered at the surrounding transaction or subtransaction abort; + * so there could be more than one stack entry to pop. */ while ((stack = gconf->stack) != NULL && stack->nest_level >= nestLevel) diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h index d88a4b6cfd..8e3057a014 100644 --- a/src/include/utils/guc.h +++ b/src/include/utils/guc.h @@ -155,7 +155,7 @@ typedef enum /* Types of set_config_option actions */ GUC_ACTION_SET, /* regular SET command */ GUC_ACTION_LOCAL, /* SET LOCAL command */ - GUC_ACTION_SAVE /* function SET option */ + GUC_ACTION_SAVE /* function SET option, or temp assignment */ } GucAction; #define GUC_QUALIFIER_SEPARATOR '.'