Improve and simplify CREATE EXTENSION's management of GUC variables.
CREATE EXTENSION needs to transiently set search_path, as well as client_min_messages and log_min_messages. We were doing this by the expedient of saving the current string value of each variable, doing a SET LOCAL, and then doing another SET LOCAL with the previous value at the end of the command. This is a bit expensive though, and it also fails badly if there is anything funny about the existing search_path value, as seen in a recent report from Roger Niederland. Fortunately, there's a much better way, which is to piggyback on the GUC infrastructure previously developed for functions with SET options. We just open a new GUC nesting level, do our assignments with GUC_ACTION_SAVE, and then close the nesting level when done. This automatically restores the prior settings without a re-parsing pass, so (in principle anyway) there can't be an error. And guc.c still takes care of cleanup in event of an error abort. The CREATE EXTENSION code for this was modeled on some much older code in ri_triggers.c, which I also changed to use the better method, even though there wasn't really much risk of failure there. Also improve the comments in guc.c to reflect this additional usage.
This commit is contained in:
parent
3919ad864d
commit
ba6f629326
@ -774,9 +774,7 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control,
|
|||||||
const char *schemaName, Oid schemaOid)
|
const char *schemaName, Oid schemaOid)
|
||||||
{
|
{
|
||||||
char *filename;
|
char *filename;
|
||||||
char *save_client_min_messages,
|
int save_nestlevel;
|
||||||
*save_log_min_messages,
|
|
||||||
*save_search_path;
|
|
||||||
StringInfoData pathbuf;
|
StringInfoData pathbuf;
|
||||||
ListCell *lc;
|
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
|
* so that we won't spam the user with useless NOTICE messages from common
|
||||||
* script actions like creating shell types.
|
* script actions like creating shell types.
|
||||||
*
|
*
|
||||||
* We use the equivalent of SET LOCAL to ensure the setting is undone upon
|
* We use the equivalent of a function SET option to allow the setting to
|
||||||
* error.
|
* persist for exactly the duration of the script execution. guc.c also
|
||||||
|
* takes care of undoing the setting on error.
|
||||||
*/
|
*/
|
||||||
save_client_min_messages =
|
save_nestlevel = NewGUCNestLevel();
|
||||||
pstrdup(GetConfigOption("client_min_messages", false, false));
|
|
||||||
if (client_min_messages < WARNING)
|
if (client_min_messages < WARNING)
|
||||||
(void) set_config_option("client_min_messages", "warning",
|
(void) set_config_option("client_min_messages", "warning",
|
||||||
PGC_USERSET, PGC_S_SESSION,
|
PGC_USERSET, PGC_S_SESSION,
|
||||||
GUC_ACTION_LOCAL, true, 0);
|
GUC_ACTION_SAVE, true, 0);
|
||||||
|
|
||||||
save_log_min_messages =
|
|
||||||
pstrdup(GetConfigOption("log_min_messages", false, false));
|
|
||||||
if (log_min_messages < WARNING)
|
if (log_min_messages < WARNING)
|
||||||
(void) set_config_option("log_min_messages", "warning",
|
(void) set_config_option("log_min_messages", "warning",
|
||||||
PGC_SUSET, PGC_S_SESSION,
|
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
|
* 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,
|
* 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
|
* 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);
|
initStringInfo(&pathbuf);
|
||||||
appendStringInfoString(&pathbuf, quote_identifier(schemaName));
|
appendStringInfoString(&pathbuf, quote_identifier(schemaName));
|
||||||
foreach(lc, requiredSchemas)
|
foreach(lc, requiredSchemas)
|
||||||
@ -849,7 +844,7 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control,
|
|||||||
|
|
||||||
(void) set_config_option("search_path", pathbuf.data,
|
(void) set_config_option("search_path", pathbuf.data,
|
||||||
PGC_USERSET, PGC_S_SESSION,
|
PGC_USERSET, PGC_S_SESSION,
|
||||||
GUC_ACTION_LOCAL, true, 0);
|
GUC_ACTION_SAVE, true, 0);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set creating_extension and related variables so that
|
* Set creating_extension and related variables so that
|
||||||
@ -910,18 +905,9 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control,
|
|||||||
CurrentExtensionObject = InvalidOid;
|
CurrentExtensionObject = InvalidOid;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Restore GUC variables for the remainder of the current transaction.
|
* Restore the GUC variables we set above.
|
||||||
* Again use SET LOCAL, so we won't affect the session value.
|
|
||||||
*/
|
*/
|
||||||
(void) set_config_option("search_path", save_search_path,
|
AtEOXact_GUC(true, save_nestlevel);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -2633,7 +2633,7 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
|
|||||||
RangeTblEntry *fkrte;
|
RangeTblEntry *fkrte;
|
||||||
const char *sep;
|
const char *sep;
|
||||||
int i;
|
int i;
|
||||||
int old_work_mem;
|
int save_nestlevel;
|
||||||
char workmembuf[32];
|
char workmembuf[32];
|
||||||
int spi_result;
|
int spi_result;
|
||||||
SPIPlanPtr qplan;
|
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"
|
* this seems to meet the criteria for being considered a "maintenance"
|
||||||
* operation, and accordingly we use maintenance_work_mem.
|
* operation, and accordingly we use maintenance_work_mem.
|
||||||
*
|
*
|
||||||
* We do the equivalent of "SET LOCAL work_mem" so that transaction abort
|
* We use the equivalent of a function SET option to allow the setting to
|
||||||
* will restore the old value if we lose control due to an error.
|
* 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);
|
snprintf(workmembuf, sizeof(workmembuf), "%d", maintenance_work_mem);
|
||||||
(void) set_config_option("work_mem", workmembuf,
|
(void) set_config_option("work_mem", workmembuf,
|
||||||
PGC_USERSET, PGC_S_SESSION,
|
PGC_USERSET, PGC_S_SESSION,
|
||||||
GUC_ACTION_LOCAL, true, 0);
|
GUC_ACTION_SAVE, true, 0);
|
||||||
|
|
||||||
if (SPI_connect() != SPI_OK_CONNECT)
|
if (SPI_connect() != SPI_OK_CONNECT)
|
||||||
elog(ERROR, "SPI_connect failed");
|
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");
|
elog(ERROR, "SPI_finish failed");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Restore work_mem for the remainder of the current transaction. This is
|
* Restore work_mem.
|
||||||
* another SET LOCAL, so it won't affect the session value.
|
|
||||||
*/
|
*/
|
||||||
snprintf(workmembuf, sizeof(workmembuf), "%d", old_work_mem);
|
AtEOXact_GUC(true, save_nestlevel);
|
||||||
(void) set_config_option("work_mem", workmembuf,
|
|
||||||
PGC_USERSET, PGC_S_SESSION,
|
|
||||||
GUC_ACTION_LOCAL, true, 0);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -4329,8 +4329,9 @@ AtStart_GUC(void)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Enter a new nesting level for GUC values. This is called at subtransaction
|
* 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
|
* start, and when entering a function that has proconfig settings, and in
|
||||||
* we must not risk error here, else subtransaction start will be unhappy.
|
* 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
|
int
|
||||||
NewGUCNestLevel(void)
|
NewGUCNestLevel(void)
|
||||||
@ -4340,8 +4341,9 @@ NewGUCNestLevel(void)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Do GUC processing at transaction or subtransaction commit or abort, or
|
* Do GUC processing at transaction or subtransaction commit or abort, or
|
||||||
* when exiting a function that has proconfig settings. (The name is thus
|
* when exiting a function that has proconfig settings, or when undoing a
|
||||||
* a bit of a misnomer; perhaps it should be ExitGUCNestLevel or some such.)
|
* 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
|
* During abort, we discard all GUC settings that were applied at nesting
|
||||||
* levels >= nestLevel. nestLevel == 1 corresponds to the main transaction.
|
* levels >= nestLevel. nestLevel == 1 corresponds to the main transaction.
|
||||||
*/
|
*/
|
||||||
@ -4374,11 +4376,11 @@ AtEOXact_GUC(bool isCommit, int nestLevel)
|
|||||||
GucStack *stack;
|
GucStack *stack;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Process and pop each stack entry within the nest level. To
|
* Process and pop each stack entry within the nest level. To simplify
|
||||||
* simplify fmgr_security_definer(), we allow failure exit from a
|
* fmgr_security_definer() and other places that use GUC_ACTION_SAVE,
|
||||||
* function-with-SET-options to be recovered at the surrounding
|
* we allow failure exit from code that uses a local nest level to be
|
||||||
* transaction or subtransaction abort; so there could be more than
|
* recovered at the surrounding transaction or subtransaction abort;
|
||||||
* one stack entry to pop.
|
* so there could be more than one stack entry to pop.
|
||||||
*/
|
*/
|
||||||
while ((stack = gconf->stack) != NULL &&
|
while ((stack = gconf->stack) != NULL &&
|
||||||
stack->nest_level >= nestLevel)
|
stack->nest_level >= nestLevel)
|
||||||
|
@ -155,7 +155,7 @@ typedef enum
|
|||||||
/* Types of set_config_option actions */
|
/* Types of set_config_option actions */
|
||||||
GUC_ACTION_SET, /* regular SET command */
|
GUC_ACTION_SET, /* regular SET command */
|
||||||
GUC_ACTION_LOCAL, /* SET LOCAL command */
|
GUC_ACTION_LOCAL, /* SET LOCAL command */
|
||||||
GUC_ACTION_SAVE /* function SET option */
|
GUC_ACTION_SAVE /* function SET option, or temp assignment */
|
||||||
} GucAction;
|
} GucAction;
|
||||||
|
|
||||||
#define GUC_QUALIFIER_SEPARATOR '.'
|
#define GUC_QUALIFIER_SEPARATOR '.'
|
||||||
|
Loading…
x
Reference in New Issue
Block a user