diff --git a/contrib/auth_delay/auth_delay.c b/contrib/auth_delay/auth_delay.c index 199de9bb39130de2d0ae0862571a464515f107c3..ca388c4498414d86cb0fbd889ef1f851e9f93ddf 100644 --- a/contrib/auth_delay/auth_delay.c +++ b/contrib/auth_delay/auth_delay.c @@ -53,7 +53,7 @@ auth_delay_checks(Port *port, int status) void _PG_init(void) { - /* Define custome GUC variables */ + /* Define custom GUC variables */ DefineCustomIntVariable("auth_delay.milliseconds", "Milliseconds to delay before reporting authentication failure", NULL, @@ -63,6 +63,7 @@ _PG_init(void) PGC_SIGHUP, GUC_UNIT_MS, NULL, + NULL, NULL); /* Install Hooks */ original_client_auth_hook = ClientAuthentication_hook; diff --git a/contrib/auto_explain/auto_explain.c b/contrib/auto_explain/auto_explain.c index 78e604e7bc4f0f6203bacd4047d55541afd9b9e9..b3206987484e57a91850033fef01bc9c507172e9 100644 --- a/contrib/auto_explain/auto_explain.c +++ b/contrib/auto_explain/auto_explain.c @@ -74,6 +74,7 @@ _PG_init(void) PGC_SUSET, GUC_UNIT_MS, NULL, + NULL, NULL); DefineCustomBoolVariable("auto_explain.log_analyze", @@ -84,6 +85,7 @@ _PG_init(void) PGC_SUSET, 0, NULL, + NULL, NULL); DefineCustomBoolVariable("auto_explain.log_verbose", @@ -94,6 +96,7 @@ _PG_init(void) PGC_SUSET, 0, NULL, + NULL, NULL); DefineCustomBoolVariable("auto_explain.log_buffers", @@ -104,6 +107,7 @@ _PG_init(void) PGC_SUSET, 0, NULL, + NULL, NULL); DefineCustomEnumVariable("auto_explain.log_format", @@ -115,6 +119,7 @@ _PG_init(void) PGC_SUSET, 0, NULL, + NULL, NULL); DefineCustomBoolVariable("auto_explain.log_nested_statements", @@ -125,6 +130,7 @@ _PG_init(void) PGC_SUSET, 0, NULL, + NULL, NULL); EmitWarningsOnPlaceholders("auto_explain"); diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c index 0390ec4c8ed6d3e9d6c09cd85b3cadbc2dd3a6fb..87cf8c55cf491831bbf5a9db79ca1eb3e9b81cc9 100644 --- a/contrib/pg_stat_statements/pg_stat_statements.c +++ b/contrib/pg_stat_statements/pg_stat_statements.c @@ -219,6 +219,7 @@ _PG_init(void) PGC_POSTMASTER, 0, NULL, + NULL, NULL); DefineCustomEnumVariable("pg_stat_statements.track", @@ -230,6 +231,7 @@ _PG_init(void) PGC_SUSET, 0, NULL, + NULL, NULL); DefineCustomBoolVariable("pg_stat_statements.track_utility", @@ -240,6 +242,7 @@ _PG_init(void) PGC_SUSET, 0, NULL, + NULL, NULL); DefineCustomBoolVariable("pg_stat_statements.save", @@ -250,6 +253,7 @@ _PG_init(void) PGC_SIGHUP, 0, NULL, + NULL, NULL); EmitWarningsOnPlaceholders("pg_stat_statements"); diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c index 27e85d25b1d33222a4c935b13af9c8263a720bac..5dc8a3ecaa8b704a158409d6be46d8cd755253c5 100644 --- a/contrib/sepgsql/hooks.c +++ b/contrib/sepgsql/hooks.c @@ -394,6 +394,7 @@ _PG_init(void) PGC_SIGHUP, GUC_NOT_IN_SAMPLE, NULL, + NULL, NULL); /* @@ -412,6 +413,7 @@ _PG_init(void) PGC_USERSET, GUC_NOT_IN_SAMPLE, NULL, + NULL, NULL); /* diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index b22e498f8b50a8d4c212f0637862e81cc92cd7e2..b31c79ebbdcb03660c81e5f83c55e107ee2b6fb2 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -4887,36 +4887,60 @@ GetSystemIdentifier(void) /* * Auto-tune the number of XLOG buffers. * - * If the user-set value of wal_buffers is -1, we auto-tune to about 3% of - * shared_buffers, with a maximum of one XLOG segment and a minimum of 8 - * blocks (8 was the default value prior to PostgreSQL 9.1, when auto-tuning - * was added). We also clamp manually-set values to at least 4 blocks; prior - * to PostgreSQL 9.1, a minimum of 4 was enforced by guc.c, but since that - * is no longer possible, we just silently treat such values as a request for - * the minimum. + * The preferred setting for wal_buffers is about 3% of shared_buffers, with + * a maximum of one XLOG segment (there is little reason to think that more + * is helpful, at least so long as we force an fsync when switching log files) + * and a minimum of 8 blocks (which was the default value prior to PostgreSQL + * 9.1, when auto-tuning was added). + * + * This should not be called until NBuffers has received its final value. */ -static void -XLOGTuneNumBuffers(void) +static int +XLOGChooseNumBuffers(void) { - int xbuffers = XLOGbuffers; - char buf[32]; + int xbuffers; - if (xbuffers == -1) - { - xbuffers = NBuffers / 32; - if (xbuffers > XLOG_SEG_SIZE / XLOG_BLCKSZ) - xbuffers = XLOG_SEG_SIZE / XLOG_BLCKSZ; - if (xbuffers < 8) - xbuffers = 8; - } - else if (xbuffers < 4) - xbuffers = 4; + xbuffers = NBuffers / 32; + if (xbuffers > XLOG_SEG_SIZE / XLOG_BLCKSZ) + xbuffers = XLOG_SEG_SIZE / XLOG_BLCKSZ; + if (xbuffers < 8) + xbuffers = 8; + return xbuffers; +} - if (xbuffers != XLOGbuffers) +/* + * GUC check_hook for wal_buffers + */ +bool +check_wal_buffers(int *newval, void **extra, GucSource source) +{ + /* + * -1 indicates a request for auto-tune. + */ + if (*newval == -1) { - snprintf(buf, sizeof(buf), "%d", xbuffers); - SetConfigOption("wal_buffers", buf, PGC_POSTMASTER, PGC_S_OVERRIDE); + /* + * If we haven't yet changed the boot_val default of -1, just let it + * be. We'll fix it when XLOGShmemSize is called. + */ + if (XLOGbuffers == -1) + return true; + + /* Otherwise, substitute the auto-tune value */ + *newval = XLOGChooseNumBuffers(); } + + /* + * We clamp manually-set values to at least 4 blocks. Prior to PostgreSQL + * 9.1, a minimum of 4 was enforced by guc.c, but since that is no longer + * the case, we just silently treat such values as a request for the + * minimum. (We could throw an error instead, but that doesn't seem very + * helpful.) + */ + if (*newval < 4) + *newval = 4; + + return true; } /* @@ -4927,8 +4951,19 @@ XLOGShmemSize(void) { Size size; - /* Figure out how many XLOG buffers we need. */ - XLOGTuneNumBuffers(); + /* + * If the value of wal_buffers is -1, use the preferred auto-tune value. + * This isn't an amazingly clean place to do this, but we must wait till + * NBuffers has received its final value, and must do it before using + * the value of XLOGbuffers to do anything important. + */ + if (XLOGbuffers == -1) + { + char buf[32]; + + snprintf(buf, sizeof(buf), "%d", XLOGChooseNumBuffers()); + SetConfigOption("wal_buffers", buf, PGC_POSTMASTER, PGC_S_OVERRIDE); + } Assert(XLOGbuffers > 0); /* XLogCtl */ @@ -8653,12 +8688,9 @@ get_sync_bit(int method) /* * GUC support */ -bool -assign_xlog_sync_method(int new_sync_method, bool doit, GucSource source) +void +assign_xlog_sync_method(int new_sync_method, void *extra) { - if (!doit) - return true; - if (sync_method != new_sync_method) { /* @@ -8678,8 +8710,6 @@ assign_xlog_sync_method(int new_sync_method, bool doit, GucSource source) XLogFileClose(); } } - - return true; } diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c index 77c9805aed1c6d70214c01938b957d88377f969d..734581e48557dcd72e8e7796729f344d00afd9ce 100644 --- a/src/backend/catalog/namespace.c +++ b/src/backend/catalog/namespace.c @@ -3468,31 +3468,33 @@ ResetTempTableNamespace(void) * Routines for handling the GUC variable 'search_path'. */ -/* assign_hook: validate new search_path, do extra actions as needed */ -const char * -assign_search_path(const char *newval, bool doit, GucSource source) +/* check_hook: validate new search_path, if possible */ +bool +check_search_path(char **newval, void **extra, GucSource source) { + bool result = true; char *rawname; List *namelist; ListCell *l; /* Need a modifiable copy of string */ - rawname = pstrdup(newval); + rawname = pstrdup(*newval); /* Parse string into list of identifiers */ if (!SplitIdentifierString(rawname, ',', &namelist)) { /* syntax error in name list */ + GUC_check_errdetail("List syntax is invalid."); pfree(rawname); list_free(namelist); - return NULL; + return false; } /* * If we aren't inside a transaction, we cannot do database access so * cannot verify the individual names. Must accept the list on faith. */ - if (source >= PGC_S_INTERACTIVE && IsTransactionState()) + if (IsTransactionState()) { /* * Verify that all the names are either valid namespace names or @@ -3504,7 +3506,7 @@ assign_search_path(const char *newval, bool doit, GucSource source) * DATABASE SET or ALTER USER SET command. It could be that the * intended use of the search path is for some other database, so we * should not error out if it mentions schemas not present in the - * current database. We reduce the message to NOTICE instead. + * current database. We issue a NOTICE instead. */ foreach(l, namelist) { @@ -3516,24 +3518,37 @@ assign_search_path(const char *newval, bool doit, GucSource source) continue; if (!SearchSysCacheExists1(NAMESPACENAME, CStringGetDatum(curname))) - ereport((source == PGC_S_TEST) ? NOTICE : ERROR, - (errcode(ERRCODE_UNDEFINED_SCHEMA), - errmsg("schema \"%s\" does not exist", curname))); + { + if (source == PGC_S_TEST) + ereport(NOTICE, + (errcode(ERRCODE_UNDEFINED_SCHEMA), + errmsg("schema \"%s\" does not exist", curname))); + else + { + GUC_check_errdetail("schema \"%s\" does not exist", curname); + result = false; + break; + } + } } } pfree(rawname); list_free(namelist); + return result; +} + +/* assign_hook: do extra actions as needed */ +void +assign_search_path(const char *newval, void *extra) +{ /* * We mark the path as needing recomputation, but don't do anything until * it's needed. This avoids trying to do database access during GUC - * initialization. + * initialization, or outside a transaction. */ - if (doit) - baseSearchPathValid = false; - - return newval; + baseSearchPathValid = false; } /* diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c index b5a2d9d005e223f49913ca64068e5955ff3da403..42a704beb164f39316862f4bdcdcc6c6b6edd1ba 100644 --- a/src/backend/commands/tablespace.c +++ b/src/backend/commands/tablespace.c @@ -1023,9 +1023,9 @@ AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt) * Routines for handling the GUC variable 'default_tablespace'. */ -/* assign_hook: validate new default_tablespace, do extra actions as needed */ -const char * -assign_default_tablespace(const char *newval, bool doit, GucSource source) +/* check_hook: validate new default_tablespace */ +bool +check_default_tablespace(char **newval, void **extra, GucSource source) { /* * If we aren't inside a transaction, we cannot do database access so @@ -1033,18 +1033,16 @@ assign_default_tablespace(const char *newval, bool doit, GucSource source) */ if (IsTransactionState()) { - if (newval[0] != '\0' && - !OidIsValid(get_tablespace_oid(newval, true))) + if (**newval != '\0' && + !OidIsValid(get_tablespace_oid(*newval, true))) { - ereport(GUC_complaint_elevel(source), - (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("tablespace \"%s\" does not exist", - newval))); - return NULL; + GUC_check_errdetail("Tablespace \"%s\" does not exist.", + *newval); + return false; } } - return newval; + return true; } /* @@ -1100,23 +1098,30 @@ GetDefaultTablespace(char relpersistence) * Routines for handling the GUC variable 'temp_tablespaces'. */ -/* assign_hook: validate new temp_tablespaces, do extra actions as needed */ -const char * -assign_temp_tablespaces(const char *newval, bool doit, GucSource source) +typedef struct +{ + int numSpcs; + Oid tblSpcs[1]; /* VARIABLE LENGTH ARRAY */ +} temp_tablespaces_extra; + +/* check_hook: validate new temp_tablespaces */ +bool +check_temp_tablespaces(char **newval, void **extra, GucSource source) { char *rawname; List *namelist; /* Need a modifiable copy of string */ - rawname = pstrdup(newval); + rawname = pstrdup(*newval); /* Parse string into list of identifiers */ if (!SplitIdentifierString(rawname, ',', &namelist)) { /* syntax error in name list */ + GUC_check_errdetail("List syntax is invalid."); pfree(rawname); list_free(namelist); - return NULL; + return false; } /* @@ -1126,17 +1131,13 @@ assign_temp_tablespaces(const char *newval, bool doit, GucSource source) */ if (IsTransactionState()) { - /* - * If we error out below, or if we are called multiple times in one - * transaction, we'll leak a bit of TopTransactionContext memory. - * Doesn't seem worth worrying about. - */ + temp_tablespaces_extra *myextra; Oid *tblSpcs; int numSpcs; ListCell *l; - tblSpcs = (Oid *) MemoryContextAlloc(TopTransactionContext, - list_length(namelist) * sizeof(Oid)); + /* temporary workspace until we are done verifying the list */ + tblSpcs = (Oid *) palloc(list_length(namelist) * sizeof(Oid)); numSpcs = 0; foreach(l, namelist) { @@ -1169,7 +1170,7 @@ assign_temp_tablespaces(const char *newval, bool doit, GucSource source) continue; } - /* Check permissions similarly */ + /* Check permissions, similarly complaining only if interactive */ aclresult = pg_tablespace_aclcheck(curoid, GetUserId(), ACL_CREATE); if (aclresult != ACLCHECK_OK) @@ -1182,17 +1183,41 @@ assign_temp_tablespaces(const char *newval, bool doit, GucSource source) tblSpcs[numSpcs++] = curoid; } - /* If actively "doing it", give the new list to fd.c */ - if (doit) - SetTempTablespaces(tblSpcs, numSpcs); - else - pfree(tblSpcs); + /* Now prepare an "extra" struct for assign_temp_tablespaces */ + myextra = malloc(offsetof(temp_tablespaces_extra, tblSpcs) + + numSpcs * sizeof(Oid)); + if (!myextra) + return false; + myextra->numSpcs = numSpcs; + memcpy(myextra->tblSpcs, tblSpcs, numSpcs * sizeof(Oid)); + *extra = (void *) myextra; + + pfree(tblSpcs); } pfree(rawname); list_free(namelist); - return newval; + return true; +} + +/* assign_hook: do extra actions as needed */ +void +assign_temp_tablespaces(const char *newval, void *extra) +{ + temp_tablespaces_extra *myextra = (temp_tablespaces_extra *) extra; + + /* + * If check_temp_tablespaces was executed inside a transaction, then pass + * the list it made to fd.c. Otherwise, clear fd.c's list; we must be + * still outside a transaction, or else restoring during transaction exit, + * and in either case we can just let the next PrepareTempTablespaces call + * make things sane. + */ + if (myextra) + SetTempTablespaces(myextra->tblSpcs, myextra->numSpcs); + else + SetTempTablespaces(NULL, 0); } /* diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index 2a61ea3bc7cc970efffaede20d4da4d414faa121..2cec713089668825d49e6cf9e95c2dbc5ee6ea42 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -33,10 +33,10 @@ */ /* - * assign_datestyle: GUC assign_hook for datestyle + * check_datestyle: GUC check_hook for datestyle */ -const char * -assign_datestyle(const char *value, bool doit, GucSource source) +bool +check_datestyle(char **newval, void **extra, GucSource source) { int newDateStyle = DateStyle; int newDateOrder = DateOrder; @@ -44,23 +44,22 @@ assign_datestyle(const char *value, bool doit, GucSource source) bool have_order = false; bool ok = true; char *rawstring; + int *myextra; char *result; List *elemlist; ListCell *l; /* Need a modifiable copy of string */ - rawstring = pstrdup(value); + rawstring = pstrdup(*newval); /* Parse string into list of identifiers */ if (!SplitIdentifierString(rawstring, ',', &elemlist)) { /* syntax error in list */ + GUC_check_errdetail("List syntax is invalid."); pfree(rawstring); list_free(elemlist); - ereport(GUC_complaint_elevel(source), - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("invalid list syntax for parameter \"datestyle\""))); - return NULL; + return false; } foreach(l, elemlist) @@ -130,38 +129,38 @@ assign_datestyle(const char *value, bool doit, GucSource source) * Easiest way to get the current DEFAULT state is to fetch the * DEFAULT string from guc.c and recursively parse it. * - * We can't simply "return assign_datestyle(...)" because we need + * We can't simply "return check_datestyle(...)" because we need * to handle constructs like "DEFAULT, ISO". */ - int saveDateStyle = DateStyle; - int saveDateOrder = DateOrder; - const char *subval; + char *subval; + void *subextra = NULL; - subval = assign_datestyle(GetConfigOptionResetString("datestyle"), - true, source); - if (!have_style) - newDateStyle = DateStyle; - if (!have_order) - newDateOrder = DateOrder; - DateStyle = saveDateStyle; - DateOrder = saveDateOrder; + subval = strdup(GetConfigOptionResetString("datestyle")); if (!subval) { ok = false; break; } - /* Here we know that our own return value is always malloc'd */ - /* when doit is true */ - free((char *) subval); + if (!check_datestyle(&subval, &subextra, source)) + { + free(subval); + ok = false; + break; + } + myextra = (int *) subextra; + if (!have_style) + newDateStyle = myextra[0]; + if (!have_order) + newDateOrder = myextra[1]; + free(subval); + free(subextra); } else { - ereport(GUC_complaint_elevel(source), - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("unrecognized \"datestyle\" key word: \"%s\"", - tok))); - ok = false; - break; + GUC_check_errdetail("Unrecognized key word: \"%s\".", tok); + pfree(rawstring); + list_free(elemlist); + return false; } } @@ -170,24 +169,16 @@ assign_datestyle(const char *value, bool doit, GucSource source) if (!ok) { - ereport(GUC_complaint_elevel(source), - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("conflicting \"datestyle\" specifications"))); - return NULL; + GUC_check_errdetail("Conflicting \"datestyle\" specifications."); + return false; } - /* - * If we aren't going to do the assignment, just return OK indicator. - */ - if (!doit) - return value; - /* * Prepare the canonical string to return. GUC wants it malloc'd. */ result = (char *) malloc(32); if (!result) - return NULL; + return false; switch (newDateStyle) { @@ -217,14 +208,32 @@ assign_datestyle(const char *value, bool doit, GucSource source) break; } + free(*newval); + *newval = result; + /* - * Finally, it's safe to assign to the global variables; the assignment - * cannot fail now. + * Set up the "extra" struct actually used by assign_datestyle. */ - DateStyle = newDateStyle; - DateOrder = newDateOrder; + myextra = (int *) malloc(2 * sizeof(int)); + if (!myextra) + return false; + myextra[0] = newDateStyle; + myextra[1] = newDateOrder; + *extra = (void *) myextra; + + return true; +} + +/* + * assign_datestyle: GUC assign_hook for datestyle + */ +void +assign_datestyle(const char *newval, void *extra) +{ + int *myextra = (int *) extra; - return result; + DateStyle = myextra[0]; + DateOrder = myextra[1]; } @@ -232,22 +241,58 @@ assign_datestyle(const char *value, bool doit, GucSource source) * TIMEZONE */ +typedef struct +{ + pg_tz *session_timezone; + int CTimeZone; + bool HasCTZSet; +} timezone_extra; + /* - * assign_timezone: GUC assign_hook for timezone + * check_timezone: GUC check_hook for timezone */ -const char * -assign_timezone(const char *value, bool doit, GucSource source) +bool +check_timezone(char **newval, void **extra, GucSource source) { - char *result; + timezone_extra myextra; char *endptr; double hours; + if (*newval == NULL) + { + /* + * The boot_val given for TimeZone in guc.c is NULL. When we see this + * we just do nothing. If this isn't overridden from the config file + * then pg_timezone_initialize() will eventually select a default + * value from the environment. This hack has two purposes: to avoid + * wasting cycles loading values that might soon be overridden from + * the config file, and to avoid trying to read the timezone files + * during InitializeGUCOptions(). The latter doesn't work in an + * EXEC_BACKEND subprocess because my_exec_path hasn't been set yet + * and so we can't locate PGSHAREDIR. + */ + Assert(source == PGC_S_DEFAULT); + return true; + } + /* - * Check for INTERVAL 'foo' + * Initialize the "extra" struct that will be passed to assign_timezone. + * We don't want to change any of the three global variables except as + * specified by logic below. To avoid leaking memory during failure + * returns, we set up the struct contents in a local variable, and only + * copy it to *extra at the end. */ - if (pg_strncasecmp(value, "interval", 8) == 0) + myextra.session_timezone = session_timezone; + myextra.CTimeZone = CTimeZone; + myextra.HasCTZSet = HasCTZSet; + + if (pg_strncasecmp(*newval, "interval", 8) == 0) { - const char *valueptr = value; + /* + * Support INTERVAL 'foo'. This is for SQL spec compliance, not + * because it has any actual real-world usefulness. + */ + const char *valueptr = *newval; char *val; Interval *interval; @@ -255,14 +300,14 @@ assign_timezone(const char *value, bool doit, GucSource source) while (isspace((unsigned char) *valueptr)) valueptr++; if (*valueptr++ != '\'') - return NULL; + return false; val = pstrdup(valueptr); /* Check and remove trailing quote */ endptr = strchr(val, '\''); if (!endptr || endptr[1] != '\0') { pfree(val); - return NULL; + return false; } *endptr = '\0'; @@ -280,31 +325,25 @@ assign_timezone(const char *value, bool doit, GucSource source) pfree(val); if (interval->month != 0) { - ereport(GUC_complaint_elevel(source), - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("invalid interval value for time zone: month not allowed"))); + GUC_check_errdetail("Cannot specify months in time zone interval."); pfree(interval); - return NULL; + return false; } if (interval->day != 0) { - ereport(GUC_complaint_elevel(source), - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("invalid interval value for time zone: day not allowed"))); + GUC_check_errdetail("Cannot specify days in time zone interval."); pfree(interval); - return NULL; + return false; } - if (doit) - { - /* Here we change from SQL to Unix sign convention */ + + /* Here we change from SQL to Unix sign convention */ #ifdef HAVE_INT64_TIMESTAMP - CTimeZone = -(interval->time / USECS_PER_SEC); + myextra.CTimeZone = -(interval->time / USECS_PER_SEC); #else - CTimeZone = -interval->time; + myextra.CTimeZone = -interval->time; #endif + myextra.HasCTZSet = true; - HasCTZSet = true; - } pfree(interval); } else @@ -312,38 +351,12 @@ assign_timezone(const char *value, bool doit, GucSource source) /* * Try it as a numeric number of hours (possibly fractional). */ - hours = strtod(value, &endptr); - if (endptr != value && *endptr == '\0') - { - if (doit) - { - /* Here we change from SQL to Unix sign convention */ - CTimeZone = -hours * SECS_PER_HOUR; - HasCTZSet = true; - } - } - else if (pg_strcasecmp(value, "UNKNOWN") == 0) + hours = strtod(*newval, &endptr); + if (endptr != *newval && *endptr == '\0') { - /* - * UNKNOWN is the value shown as the "default" for TimeZone in - * guc.c. We interpret it as being a complete no-op; we don't - * change the timezone setting. Note that if there is a known - * timezone setting, we will return that name rather than UNKNOWN - * as the canonical spelling. - * - * During GUC initialization, since the timezone library isn't set - * up yet, pg_get_timezone_name will return NULL and we will leave - * the setting as UNKNOWN. If this isn't overridden from the - * config file then pg_timezone_initialize() will eventually - * select a default value from the environment. - */ - if (doit) - { - const char *curzone = pg_get_timezone_name(session_timezone); - - if (curzone) - value = curzone; - } + /* Here we change from SQL to Unix sign convention */ + myextra.CTimeZone = -hours * SECS_PER_HOUR; + myextra.HasCTZSet = true; } else { @@ -352,61 +365,83 @@ assign_timezone(const char *value, bool doit, GucSource source) */ pg_tz *new_tz; - new_tz = pg_tzset(value); + new_tz = pg_tzset(*newval); if (!new_tz) { - ereport(GUC_complaint_elevel(source), - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("unrecognized time zone name: \"%s\"", - value))); - return NULL; + /* Doesn't seem to be any great value in errdetail here */ + return false; } if (!tz_acceptable(new_tz)) { - ereport(GUC_complaint_elevel(source), - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("time zone \"%s\" appears to use leap seconds", - value), - errdetail("PostgreSQL does not support leap seconds."))); - return NULL; + GUC_check_errmsg("time zone \"%s\" appears to use leap seconds", + *newval); + GUC_check_errdetail("PostgreSQL does not support leap seconds."); + return false; } - if (doit) - { - /* Save the changed TZ */ - session_timezone = new_tz; - HasCTZSet = false; - } + myextra.session_timezone = new_tz; + myextra.HasCTZSet = false; } } - /* - * If we aren't going to do the assignment, just return OK indicator. - */ - if (!doit) - return value; - /* * Prepare the canonical string to return. GUC wants it malloc'd. + * + * Note: the result string should be something that we'd accept as input. + * We use the numeric format for interval cases, because it's simpler to + * reload. In the named-timezone case, *newval is already OK and need not + * be changed; it might not have the canonical casing, but that's taken + * care of by show_timezone. */ - if (HasCTZSet) + if (myextra.HasCTZSet) { - result = (char *) malloc(64); + char *result = (char *) malloc(64); + if (!result) - return NULL; + return false; snprintf(result, 64, "%.5f", - (double) (-CTimeZone) / (double) SECS_PER_HOUR); + (double) (-myextra.CTimeZone) / (double) SECS_PER_HOUR); + free(*newval); + *newval = result; } - else - result = strdup(value); - return result; + /* + * Pass back data for assign_timezone to use + */ + *extra = malloc(sizeof(timezone_extra)); + if (!*extra) + return false; + memcpy(*extra, &myextra, sizeof(timezone_extra)); + + return true; +} + +/* + * assign_timezone: GUC assign_hook for timezone + */ +void +assign_timezone(const char *newval, void *extra) +{ + timezone_extra *myextra = (timezone_extra *) extra; + + /* Do nothing for the boot_val default of NULL */ + if (!myextra) + return; + + session_timezone = myextra->session_timezone; + CTimeZone = myextra->CTimeZone; + HasCTZSet = myextra->HasCTZSet; } /* * show_timezone: GUC show_hook for timezone + * + * We wouldn't need this, except that historically interval values have been + * shown without an INTERVAL prefix, so the display format isn't what would + * be accepted as input. Otherwise we could have check_timezone return the + * preferred string to begin with. */ const char * show_timezone(void) @@ -447,83 +482,66 @@ show_timezone(void) */ /* - * assign_log_timezone: GUC assign_hook for log_timezone + * check_log_timezone: GUC check_hook for log_timezone */ -const char * -assign_log_timezone(const char *value, bool doit, GucSource source) +bool +check_log_timezone(char **newval, void **extra, GucSource source) { - char *result; + pg_tz *new_tz; - if (pg_strcasecmp(value, "UNKNOWN") == 0) + if (*newval == NULL) { /* - * UNKNOWN is the value shown as the "default" for log_timezone in - * guc.c. We interpret it as being a complete no-op; we don't change - * the timezone setting. Note that if there is a known timezone - * setting, we will return that name rather than UNKNOWN as the - * canonical spelling. - * - * During GUC initialization, since the timezone library isn't set up - * yet, pg_get_timezone_name will return NULL and we will leave the - * setting as UNKNOWN. If this isn't overridden from the config file - * then pg_timezone_initialize() will eventually select a default + * The boot_val given for log_timezone in guc.c is NULL. When we see + * this we just do nothing. If this isn't overridden from the config + * file then pg_timezone_initialize() will eventually select a default * value from the environment. */ - if (doit) - { - const char *curzone = pg_get_timezone_name(log_timezone); - - if (curzone) - value = curzone; - } + Assert(source == PGC_S_DEFAULT); + return true; } - else - { - /* - * Otherwise assume it is a timezone name, and try to load it. - */ - pg_tz *new_tz; - - new_tz = pg_tzset(value); - if (!new_tz) - { - ereport(GUC_complaint_elevel(source), - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("unrecognized time zone name: \"%s\"", - value))); - return NULL; - } + /* + * Otherwise assume it is a timezone name, and try to load it. + */ + new_tz = pg_tzset(*newval); - if (!tz_acceptable(new_tz)) - { - ereport(GUC_complaint_elevel(source), - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("time zone \"%s\" appears to use leap seconds", - value), - errdetail("PostgreSQL does not support leap seconds."))); - return NULL; - } + if (!new_tz) + { + /* Doesn't seem to be any great value in errdetail here */ + return false; + } - if (doit) - { - /* Save the changed TZ */ - log_timezone = new_tz; - } + if (!tz_acceptable(new_tz)) + { + GUC_check_errmsg("time zone \"%s\" appears to use leap seconds", + *newval); + GUC_check_errdetail("PostgreSQL does not support leap seconds."); + return false; } /* - * If we aren't going to do the assignment, just return OK indicator. + * Pass back data for assign_log_timezone to use */ - if (!doit) - return value; + *extra = malloc(sizeof(pg_tz *)); + if (!*extra) + return false; + memcpy(*extra, &new_tz, sizeof(pg_tz *)); - /* - * Prepare the canonical string to return. GUC wants it malloc'd. - */ - result = strdup(value); + return true; +} + +/* + * assign_log_timezone: GUC assign_hook for log_timezone + */ +void +assign_log_timezone(const char *newval, void *extra) +{ + /* Do nothing for the boot_val default of NULL */ + if (!extra) + return; - return result; + log_timezone = *((pg_tz **) extra); } /* @@ -548,38 +566,33 @@ show_log_timezone(void) * * We allow idempotent changes (r/w -> r/w and r/o -> r/o) at any time, and * we also always allow changes from read-write to read-only. However, - * read-only to read-write may be changed only when source == PGC_S_OVERRIDE - * (i.e. we're aborting a read only transaction and restoring the previous - * setting) or in a top-level transaction that has not yet taken an initial - * snapshot. + * read-only may be changed to read-write only when in a top-level transaction + * that has not yet taken an initial snapshot. Can't do it in a hot standby + * slave, either. */ bool -assign_transaction_read_only(bool newval, bool doit, GucSource source) +check_transaction_read_only(bool *newval, void **extra, GucSource source) { - if (source != PGC_S_OVERRIDE && newval == false && XactReadOnly) + if (*newval == false && XactReadOnly) { /* Can't go to r/w mode inside a r/o transaction */ if (IsSubTransaction()) { - ereport(GUC_complaint_elevel(source), - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("cannot set transaction read-write mode inside a read-only transaction"))); + GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION); + GUC_check_errmsg("cannot set transaction read-write mode inside a read-only transaction"); return false; } /* Top level transaction can't change to r/w after first snapshot. */ if (FirstSnapshotSet) { - ereport(GUC_complaint_elevel(source), - (errcode(ERRCODE_ACTIVE_SQL_TRANSACTION), - errmsg("transaction read-write mode must be set before any query"))); + GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION); + GUC_check_errmsg("transaction read-write mode must be set before any query"); return false; } /* Can't go to r/w mode while recovery is still active */ if (RecoveryInProgress()) { - ereport(GUC_complaint_elevel(source), - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("cannot set transaction read-write mode during recovery"))); + GUC_check_errmsg("cannot set transaction read-write mode during recovery"); return false; } } @@ -591,76 +604,78 @@ assign_transaction_read_only(bool newval, bool doit, GucSource source) * SET TRANSACTION ISOLATION LEVEL * * We allow idempotent changes at any time, but otherwise this can only be - * changed from a toplevel transaction that has not yet taken a snapshot, or - * when source == PGC_S_OVERRIDE (i.e. we're aborting a transaction and - * restoring the previously set value). + * changed in a toplevel transaction that has not yet taken a snapshot. */ -const char * -assign_XactIsoLevel(const char *value, bool doit, GucSource source) +bool +check_XactIsoLevel(char **newval, void **extra, GucSource source) { - /* source == PGC_S_OVERRIDE means do it anyway, eg at xact abort */ - if (source != PGC_S_OVERRIDE && strcmp(value, XactIsoLevel_string) != 0) + int newXactIsoLevel; + + if (strcmp(*newval, "serializable") == 0) + { + newXactIsoLevel = XACT_SERIALIZABLE; + } + else if (strcmp(*newval, "repeatable read") == 0) + { + newXactIsoLevel = XACT_REPEATABLE_READ; + } + else if (strcmp(*newval, "read committed") == 0) + { + newXactIsoLevel = XACT_READ_COMMITTED; + } + else if (strcmp(*newval, "read uncommitted") == 0) + { + newXactIsoLevel = XACT_READ_UNCOMMITTED; + } + else if (strcmp(*newval, "default") == 0) + { + newXactIsoLevel = DefaultXactIsoLevel; + } + else + return false; + + if (newXactIsoLevel != XactIsoLevel) { if (FirstSnapshotSet) { - ereport(GUC_complaint_elevel(source), - (errcode(ERRCODE_ACTIVE_SQL_TRANSACTION), - errmsg("SET TRANSACTION ISOLATION LEVEL must be called before any query"))); - return NULL; + GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION); + GUC_check_errmsg("SET TRANSACTION ISOLATION LEVEL must be called before any query"); + return false; } /* We ignore a subtransaction setting it to the existing value. */ if (IsSubTransaction()) { - ereport(GUC_complaint_elevel(source), - (errcode(ERRCODE_ACTIVE_SQL_TRANSACTION), - errmsg("SET TRANSACTION ISOLATION LEVEL must not be called in a subtransaction"))); - return NULL; + GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION); + GUC_check_errmsg("SET TRANSACTION ISOLATION LEVEL must not be called in a subtransaction"); + return false; } /* Can't go to serializable mode while recovery is still active */ - if (RecoveryInProgress() && strcmp(value, "serializable") == 0) + if (newXactIsoLevel == XACT_SERIALIZABLE && RecoveryInProgress()) { - ereport(GUC_complaint_elevel(source), - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("cannot use serializable mode in a hot standby"), - errhint("You can use REPEATABLE READ instead."))); + GUC_check_errmsg("cannot use serializable mode in a hot standby"); + GUC_check_errhint("You can use REPEATABLE READ instead."); return false; } } - if (strcmp(value, "serializable") == 0) - { - if (doit) - XactIsoLevel = XACT_SERIALIZABLE; - } - else if (strcmp(value, "repeatable read") == 0) - { - if (doit) - XactIsoLevel = XACT_REPEATABLE_READ; - } - else if (strcmp(value, "read committed") == 0) - { - if (doit) - XactIsoLevel = XACT_READ_COMMITTED; - } - else if (strcmp(value, "read uncommitted") == 0) - { - if (doit) - XactIsoLevel = XACT_READ_UNCOMMITTED; - } - else if (strcmp(value, "default") == 0) - { - if (doit) - XactIsoLevel = DefaultXactIsoLevel; - } - else - return NULL; + *extra = malloc(sizeof(int)); + if (!*extra) + return false; + *((int *) *extra) = newXactIsoLevel; + + return true; +} - return value; +void +assign_XactIsoLevel(const char *newval, void *extra) +{ + XactIsoLevel = *((int *) extra); } const char * show_XactIsoLevel(void) { + /* We need this because we don't want to show "default". */ switch (XactIsoLevel) { case XACT_READ_UNCOMMITTED: @@ -681,25 +696,18 @@ show_XactIsoLevel(void) */ bool -assign_transaction_deferrable(bool newval, bool doit, GucSource source) +check_transaction_deferrable(bool *newval, void **extra, GucSource source) { - /* source == PGC_S_OVERRIDE means do it anyway, eg at xact abort */ - if (source == PGC_S_OVERRIDE) - return true; - if (IsSubTransaction()) { - ereport(GUC_complaint_elevel(source), - (errcode(ERRCODE_ACTIVE_SQL_TRANSACTION), - errmsg("SET TRANSACTION [NOT] DEFERRABLE cannot be called within a subtransaction"))); + GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION); + GUC_check_errmsg("SET TRANSACTION [NOT] DEFERRABLE cannot be called within a subtransaction"); return false; } - if (FirstSnapshotSet) { - ereport(GUC_complaint_elevel(source), - (errcode(ERRCODE_ACTIVE_SQL_TRANSACTION), - errmsg("SET TRANSACTION [NOT] DEFERRABLE must be called before any query"))); + GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION); + GUC_check_errmsg("SET TRANSACTION [NOT] DEFERRABLE must be called before any query"); return false; } @@ -708,17 +716,34 @@ assign_transaction_deferrable(bool newval, bool doit, GucSource source) /* * Random number seed + * + * We can't roll back the random sequence on error, and we don't want + * config file reloads to affect it, so we only want interactive SET SEED + * commands to set it. We use the "extra" storage to ensure that rollbacks + * don't try to do the operation again. */ bool -assign_random_seed(double value, bool doit, GucSource source) +check_random_seed(double *newval, void **extra, GucSource source) { - /* Can't really roll back on error, so ignore non-interactive setting */ - if (doit && source >= PGC_S_INTERACTIVE) - DirectFunctionCall1(setseed, Float8GetDatum(value)); + *extra = malloc(sizeof(int)); + if (!*extra) + return false; + /* Arm the assign only if source of value is an interactive SET */ + *((int *) *extra) = (source >= PGC_S_INTERACTIVE); + return true; } +void +assign_random_seed(double newval, void *extra) +{ + /* We'll do this at most once for any setting of the GUC variable */ + if (*((int *) extra)) + DirectFunctionCall1(setseed, Float8GetDatum(newval)); + *((int *) extra) = 0; +} + const char * show_random_seed(void) { @@ -727,214 +752,189 @@ show_random_seed(void) /* - * encoding handling functions + * SET CLIENT_ENCODING */ -const char * -assign_client_encoding(const char *value, bool doit, GucSource source) +bool +check_client_encoding(char **newval, void **extra, GucSource source) { int encoding; - encoding = pg_valid_client_encoding(value); + /* Look up the encoding by name */ + encoding = pg_valid_client_encoding(*newval); if (encoding < 0) - return NULL; + return false; /* - * Note: if we are in startup phase then SetClientEncoding may not be able - * to really set the encoding. In this case we will assume that the - * encoding is okay, and InitializeClientEncoding() will fix things once - * initialization is complete. + * If we are not within a transaction then PrepareClientEncoding will not + * be able to look up the necessary conversion procs. If we are still + * starting up, it will return "OK" anyway, and InitializeClientEncoding + * will fix things once initialization is far enough along. After + * startup, we'll fail. This would only happen if someone tries to change + * client_encoding in postgresql.conf and then SIGHUP existing sessions. + * It seems like a bad idea for client_encoding to change that way anyhow, + * so we don't go out of our way to support it. + * + * Note: in the postmaster, or any other process that never calls + * InitializeClientEncoding, PrepareClientEncoding will always succeed, + * and so will SetClientEncoding; but they won't do anything, which is OK. */ - if (SetClientEncoding(encoding, doit) < 0) + if (PrepareClientEncoding(encoding) < 0) { - ereport(GUC_complaint_elevel(source), - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("conversion between %s and %s is not supported", - value, GetDatabaseEncodingName()))); - return NULL; + if (IsTransactionState()) + { + /* Must be a genuine no-such-conversion problem */ + GUC_check_errcode(ERRCODE_FEATURE_NOT_SUPPORTED); + GUC_check_errdetail("Conversion between %s and %s is not supported.", + pg_encoding_to_char(encoding), + GetDatabaseEncodingName()); + } + else + { + /* Provide a useful complaint */ + GUC_check_errdetail("Cannot change \"client_encoding\" now."); + } + return false; } - return value; + + /* + * Return the encoding's canonical name, and save its ID in *extra. + */ + free(*newval); + *newval = strdup(pg_encoding_to_char(encoding)); + if (!*newval) + return false; + + *extra = malloc(sizeof(int)); + if (!*extra) + return false; + *((int *) *extra) = encoding; + + return true; +} + +void +assign_client_encoding(const char *newval, void *extra) +{ + int encoding = *((int *) extra); + + /* We do not expect an error if PrepareClientEncoding succeeded */ + if (SetClientEncoding(encoding) < 0) + elog(LOG, "SetClientEncoding(%d) failed", encoding); } /* * SET SESSION AUTHORIZATION - * - * When resetting session auth after an error, we can't expect to do catalog - * lookups. Hence, the stored form of the value must provide a numeric oid - * that can be re-used directly. We store the string in the form of - * NAMEDATALEN 'x's, followed by T or F to indicate superuserness, followed - * by the numeric oid, followed by a comma, followed by the role name. - * This cannot be confused with a plain role name because of the NAMEDATALEN - * limit on names, so we can tell whether we're being passed an initial - * role name or a saved/restored value. (NOTE: we rely on guc.c to have - * properly truncated any incoming value, but not to truncate already-stored - * values. See GUC_IS_NAME processing.) */ -extern char *session_authorization_string; /* in guc.c */ -const char * -assign_session_authorization(const char *value, bool doit, GucSource source) +typedef struct { - Oid roleid = InvalidOid; - bool is_superuser = false; - const char *actual_rolename = NULL; - char *result; + /* This is the "extra" state for both SESSION AUTHORIZATION and ROLE */ + Oid roleid; + bool is_superuser; +} role_auth_extra; - if (strspn(value, "x") == NAMEDATALEN && - (value[NAMEDATALEN] == 'T' || value[NAMEDATALEN] == 'F')) - { - /* might be a saved userid string */ - Oid savedoid; - char *endptr; +bool +check_session_authorization(char **newval, void **extra, GucSource source) +{ + HeapTuple roleTup; + Oid roleid; + bool is_superuser; + role_auth_extra *myextra; - savedoid = (Oid) strtoul(value + NAMEDATALEN + 1, &endptr, 10); + /* Do nothing for the boot_val default of NULL */ + if (*newval == NULL) + return true; - if (endptr != value + NAMEDATALEN + 1 && *endptr == ',') - { - /* syntactically valid, so break out the data */ - roleid = savedoid; - is_superuser = (value[NAMEDATALEN] == 'T'); - actual_rolename = endptr + 1; - } + if (!IsTransactionState()) + { + /* + * Can't do catalog lookups, so fail. The result of this is that + * session_authorization cannot be set in postgresql.conf, which + * seems like a good thing anyway, so we don't work hard to avoid it. + */ + return false; } - if (roleid == InvalidOid) + /* Look up the username */ + roleTup = SearchSysCache1(AUTHNAME, PointerGetDatum(*newval)); + if (!HeapTupleIsValid(roleTup)) { - /* not a saved ID, so look it up */ - HeapTuple roleTup; - - if (!IsTransactionState()) - { - /* - * Can't do catalog lookups, so fail. The upshot of this is that - * session_authorization cannot be set in postgresql.conf, which - * seems like a good thing anyway. - */ - return NULL; - } - - roleTup = SearchSysCache1(AUTHNAME, PointerGetDatum(value)); - if (!HeapTupleIsValid(roleTup)) - { - ereport(GUC_complaint_elevel(source), - (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("role \"%s\" does not exist", value))); - return NULL; - } - - roleid = HeapTupleGetOid(roleTup); - is_superuser = ((Form_pg_authid) GETSTRUCT(roleTup))->rolsuper; - actual_rolename = value; - - ReleaseSysCache(roleTup); + GUC_check_errmsg("role \"%s\" does not exist", *newval); + return false; } - if (doit) - SetSessionAuthorization(roleid, is_superuser); + roleid = HeapTupleGetOid(roleTup); + is_superuser = ((Form_pg_authid) GETSTRUCT(roleTup))->rolsuper; - result = (char *) malloc(NAMEDATALEN + 32 + strlen(actual_rolename)); - if (!result) - return NULL; - - memset(result, 'x', NAMEDATALEN); + ReleaseSysCache(roleTup); - sprintf(result + NAMEDATALEN, "%c%u,%s", - is_superuser ? 'T' : 'F', - roleid, - actual_rolename); + /* Set up "extra" struct for assign_session_authorization to use */ + myextra = (role_auth_extra *) malloc(sizeof(role_auth_extra)); + if (!myextra) + return false; + myextra->roleid = roleid; + myextra->is_superuser = is_superuser; + *extra = (void *) myextra; - return result; + return true; } -const char * -show_session_authorization(void) +void +assign_session_authorization(const char *newval, void *extra) { - /* - * Extract the user name from the stored string; see - * assign_session_authorization - */ - const char *value = session_authorization_string; - Oid savedoid; - char *endptr; + role_auth_extra *myextra = (role_auth_extra *) extra; - /* If session_authorization hasn't been set in this process, return "" */ - if (value == NULL || value[0] == '\0') - return ""; + /* Do nothing for the boot_val default of NULL */ + if (!myextra) + return; - Assert(strspn(value, "x") == NAMEDATALEN && - (value[NAMEDATALEN] == 'T' || value[NAMEDATALEN] == 'F')); - - savedoid = (Oid) strtoul(value + NAMEDATALEN + 1, &endptr, 10); - - Assert(endptr != value + NAMEDATALEN + 1 && *endptr == ','); - - return endptr + 1; + SetSessionAuthorization(myextra->roleid, myextra->is_superuser); } /* * SET ROLE * - * When resetting session auth after an error, we can't expect to do catalog - * lookups. Hence, the stored form of the value must provide a numeric oid - * that can be re-used directly. We implement this exactly like SET - * SESSION AUTHORIZATION. - * * The SQL spec requires "SET ROLE NONE" to unset the role, so we hardwire - * a translation of "none" to InvalidOid. + * a translation of "none" to InvalidOid. Otherwise this is much like + * SET SESSION AUTHORIZATION. */ extern char *role_string; /* in guc.c */ -const char * -assign_role(const char *value, bool doit, GucSource source) +bool +check_role(char **newval, void **extra, GucSource source) { - Oid roleid = InvalidOid; - bool is_superuser = false; - const char *actual_rolename = value; - char *result; + HeapTuple roleTup; + Oid roleid; + bool is_superuser; + role_auth_extra *myextra; - if (strspn(value, "x") == NAMEDATALEN && - (value[NAMEDATALEN] == 'T' || value[NAMEDATALEN] == 'F')) + if (strcmp(*newval, "none") == 0) { - /* might be a saved userid string */ - Oid savedoid; - char *endptr; - - savedoid = (Oid) strtoul(value + NAMEDATALEN + 1, &endptr, 10); - - if (endptr != value + NAMEDATALEN + 1 && *endptr == ',') - { - /* syntactically valid, so break out the data */ - roleid = savedoid; - is_superuser = (value[NAMEDATALEN] == 'T'); - actual_rolename = endptr + 1; - } + /* hardwired translation */ + roleid = InvalidOid; + is_superuser = false; } - - if (roleid == InvalidOid && - strcmp(actual_rolename, "none") != 0) + else { - /* not a saved ID, so look it up */ - HeapTuple roleTup; - if (!IsTransactionState()) { /* - * Can't do catalog lookups, so fail. The upshot of this is that + * Can't do catalog lookups, so fail. The result of this is that * role cannot be set in postgresql.conf, which seems like a good - * thing anyway. + * thing anyway, so we don't work hard to avoid it. */ - return NULL; + return false; } - roleTup = SearchSysCache1(AUTHNAME, PointerGetDatum(value)); + /* Look up the username */ + roleTup = SearchSysCache1(AUTHNAME, PointerGetDatum(*newval)); if (!HeapTupleIsValid(roleTup)) { - ereport(GUC_complaint_elevel(source), - (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("role \"%s\" does not exist", value))); - return NULL; + GUC_check_errmsg("role \"%s\" does not exist", *newval); + return false; } roleid = HeapTupleGetOid(roleTup); @@ -947,61 +947,45 @@ assign_role(const char *value, bool doit, GucSource source) */ if (!is_member_of_role(GetSessionUserId(), roleid)) { - ereport(GUC_complaint_elevel(source), - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("permission denied to set role \"%s\"", - value))); - return NULL; + GUC_check_errcode(ERRCODE_INSUFFICIENT_PRIVILEGE); + GUC_check_errmsg("permission denied to set role \"%s\"", + *newval); + return false; } } - if (doit) - SetCurrentRoleId(roleid, is_superuser); - - result = (char *) malloc(NAMEDATALEN + 32 + strlen(actual_rolename)); - if (!result) - return NULL; + /* Set up "extra" struct for assign_role to use */ + myextra = (role_auth_extra *) malloc(sizeof(role_auth_extra)); + if (!myextra) + return false; + myextra->roleid = roleid; + myextra->is_superuser = is_superuser; + *extra = (void *) myextra; - memset(result, 'x', NAMEDATALEN); + return true; +} - sprintf(result + NAMEDATALEN, "%c%u,%s", - is_superuser ? 'T' : 'F', - roleid, - actual_rolename); +void +assign_role(const char *newval, void *extra) +{ + role_auth_extra *myextra = (role_auth_extra *) extra; - return result; + SetCurrentRoleId(myextra->roleid, myextra->is_superuser); } const char * show_role(void) { /* - * Extract the role name from the stored string; see assign_role - */ - const char *value = role_string; - Oid savedoid; - char *endptr; - - /* This special case only applies if no SET ROLE has been done */ - if (value == NULL || strcmp(value, "none") == 0) - return "none"; - - Assert(strspn(value, "x") == NAMEDATALEN && - (value[NAMEDATALEN] == 'T' || value[NAMEDATALEN] == 'F')); - - savedoid = (Oid) strtoul(value + NAMEDATALEN + 1, &endptr, 10); - - Assert(endptr != value + NAMEDATALEN + 1 && *endptr == ','); - - /* - * Check that the stored string still matches the effective setting, else - * return "none". This is a kluge to deal with the fact that SET SESSION - * AUTHORIZATION logically resets SET ROLE to NONE, but we cannot set the - * GUC role variable from assign_session_authorization (because we haven't - * got enough info to call set_config_option). + * Check whether SET ROLE is active; if not return "none". This is a + * kluge to deal with the fact that SET SESSION AUTHORIZATION logically + * resets SET ROLE to NONE, but we cannot set the GUC role variable from + * assign_session_authorization (because we haven't got enough info to + * call set_config_option). */ - if (savedoid != GetCurrentRoleId()) + if (!OidIsValid(GetCurrentRoleId())) return "none"; - return endptr + 1; + /* Otherwise we can just use the GUC string */ + return role_string ? role_string : "none"; } diff --git a/src/backend/nls.mk b/src/backend/nls.mk index eec8a5e2b0dd409945b284c9a2e296385494e890..cf09335a01cd358141b1e86d04f2f610d72ac9f6 100644 --- a/src/backend/nls.mk +++ b/src/backend/nls.mk @@ -2,7 +2,10 @@ CATALOG_NAME := postgres AVAIL_LANGUAGES := de es fr ja pt_BR tr GETTEXT_FILES := + gettext-files -GETTEXT_TRIGGERS:= _ errmsg errmsg_plural:1,2 errdetail errdetail_log errdetail_plural:1,2 errhint errcontext write_stderr yyerror parser_yyerror +GETTEXT_TRIGGERS:= _ errmsg errmsg_plural:1,2 errdetail errdetail_log \ + errdetail_plural:1,2 errhint errcontext \ + GUC_check_errmsg GUC_check_errdetail GUC_check_errhint \ + write_stderr yyerror parser_yyerror gettext-files: distprep find $(srcdir)/ $(srcdir)/../port/ -name '*.c' -print >$@ diff --git a/src/backend/replication/syncrep.c b/src/backend/replication/syncrep.c index 0d3381465108919d5c086e8ae01223c32afb2742..192aac9aea3fd6582710af5c3786dd3baf505889 100644 --- a/src/backend/replication/syncrep.c +++ b/src/backend/replication/syncrep.c @@ -639,25 +639,23 @@ SyncRepQueueIsOrderedByLSN(void) * =========================================================== */ -const char * -assign_synchronous_standby_names(const char *newval, bool doit, GucSource source) +bool +check_synchronous_standby_names(char **newval, void **extra, GucSource source) { char *rawstring; List *elemlist; /* Need a modifiable copy of string */ - rawstring = pstrdup(newval); + rawstring = pstrdup(*newval); /* Parse string into list of identifiers */ if (!SplitIdentifierString(rawstring, ',', &elemlist)) { /* syntax error in list */ + GUC_check_errdetail("List syntax is invalid."); pfree(rawstring); list_free(elemlist); - ereport(GUC_complaint_elevel(source), - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("invalid list syntax for parameter \"synchronous_standby_names\""))); - return NULL; + return false; } /* @@ -671,5 +669,5 @@ assign_synchronous_standby_names(const char *newval, bool doit, GucSource source pfree(rawstring); list_free(elemlist); - return newval; + return true; } diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index 79c0f68966ea33e0ad7f76625b1e582eda6da4d5..cc7945aeb669cc9d558854dcf51cf69e12c3f801 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -2804,7 +2804,8 @@ RecoveryConflictInterrupt(ProcSignalReason reason) break; default: - elog(FATAL, "Unknown conflict mode"); + elog(FATAL, "unrecognized conflict mode: %d", + (int) reason); } Assert(RecoveryConflictPending && (QueryCancelPending || ProcDiePending)); @@ -3062,27 +3063,32 @@ check_stack_depth(void) #endif /* IA64 */ } -/* GUC assign hook for max_stack_depth */ +/* GUC check hook for max_stack_depth */ bool -assign_max_stack_depth(int newval, bool doit, GucSource source) +check_max_stack_depth(int *newval, void **extra, GucSource source) { - long newval_bytes = newval * 1024L; + long newval_bytes = *newval * 1024L; long stack_rlimit = get_stack_depth_rlimit(); if (stack_rlimit > 0 && newval_bytes > stack_rlimit - STACK_DEPTH_SLOP) { - ereport(GUC_complaint_elevel(source), - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("\"max_stack_depth\" must not exceed %ldkB", - (stack_rlimit - STACK_DEPTH_SLOP) / 1024L), - errhint("Increase the platform's stack depth limit via \"ulimit -s\" or local equivalent."))); + GUC_check_errdetail("\"max_stack_depth\" must not exceed %ldkB.", + (stack_rlimit - STACK_DEPTH_SLOP) / 1024L); + GUC_check_errhint("Increase the platform's stack depth limit via \"ulimit -s\" or local equivalent."); return false; } - if (doit) - max_stack_depth_bytes = newval_bytes; return true; } +/* GUC assign hook for max_stack_depth */ +void +assign_max_stack_depth(int newval, void *extra) +{ + long newval_bytes = newval * 1024L; + + max_stack_depth_bytes = newval_bytes; +} + /* * set_debug_options --- apply "-d N" command line option diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c index f0fe2e31a283293570a343f2ba4b215c647b28c0..0410b8384e9d18dfb62f8bc6e59239f2da17aa86 100644 --- a/src/backend/utils/adt/datetime.c +++ b/src/backend/utils/adt/datetime.c @@ -4140,20 +4140,17 @@ CheckDateTokenTables(void) /* * This function gets called during timezone config file load or reload * to create the final array of timezone tokens. The argument array - * is already sorted in name order. This data is in a temporary memory - * context and must be copied to somewhere permanent. + * is already sorted in name order. The data is converted to datetkn + * format and installed in *tbl, which must be allocated by the caller. */ void -InstallTimeZoneAbbrevs(tzEntry *abbrevs, int n) +ConvertTimeZoneAbbrevs(TimeZoneAbbrevTable *tbl, + struct tzEntry *abbrevs, int n) { - datetkn *newtbl; + datetkn *newtbl = tbl->abbrevs; int i; - /* - * Copy the data into TopMemoryContext and convert to datetkn format. - */ - newtbl = (datetkn *) MemoryContextAlloc(TopMemoryContext, - n * sizeof(datetkn)); + tbl->numabbrevs = n; for (i = 0; i < n; i++) { strncpy(newtbl[i].token, abbrevs[i].abbrev, TOKMAXLEN); @@ -4163,12 +4160,20 @@ InstallTimeZoneAbbrevs(tzEntry *abbrevs, int n) /* Check the ordering, if testing */ Assert(CheckDateTokenTable("timezone offset", newtbl, n)); +} + +/* + * Install a TimeZoneAbbrevTable as the active table. + * + * Caller is responsible that the passed table doesn't go away while in use. + */ +void +InstallTimeZoneAbbrevs(TimeZoneAbbrevTable *tbl) +{ + int i; - /* Now safe to replace existing table (if any) */ - if (timezonetktbl) - pfree(timezonetktbl); - timezonetktbl = newtbl; - sztimezonetktbl = n; + timezonetktbl = tbl->abbrevs; + sztimezonetktbl = tbl->numabbrevs; /* clear date cache in case it contains any stale timezone names */ for (i = 0; i < MAXDATEFIELDS; i++) diff --git a/src/backend/utils/adt/pg_locale.c b/src/backend/utils/adt/pg_locale.c index 163856d5b18d24ce2a6e5667bd75a71d2c153a39..cbf74a07f2dcde385578562987d6ce486501505b 100644 --- a/src/backend/utils/adt/pg_locale.c +++ b/src/backend/utils/adt/pg_locale.c @@ -236,52 +236,53 @@ check_locale(int category, const char *value) return ret; } -/* GUC assign hooks */ /* - * This is common code for several locale categories. This doesn't - * actually set the locale permanently, it only tests if the locale is - * valid. (See explanation at the top of this file.) + * GUC check/assign hooks + * + * For most locale categories, the assign hook doesn't actually set the locale + * permanently, just reset flags so that the next use will cache the + * appropriate values. (See explanation at the top of this file.) * * Note: we accept value = "" as selecting the postmaster's environment * value, whatever it was (so long as the environment setting is legal). * This will have been locked down by an earlier call to pg_perm_setlocale. */ -static const char * -locale_xxx_assign(int category, const char *value, bool doit, GucSource source) +bool +check_locale_monetary(char **newval, void **extra, GucSource source) { - if (!check_locale(category, value)) - value = NULL; /* set failure return marker */ - - /* need to reload cache next time? */ - if (doit && value != NULL) - { - CurrentLocaleConvValid = false; - CurrentLCTimeValid = false; - } - - return value; + return check_locale(LC_MONETARY, *newval); } +void +assign_locale_monetary(const char *newval, void *extra) +{ + CurrentLocaleConvValid = false; +} -const char * -locale_monetary_assign(const char *value, bool doit, GucSource source) +bool +check_locale_numeric(char **newval, void **extra, GucSource source) { - return locale_xxx_assign(LC_MONETARY, value, doit, source); + return check_locale(LC_NUMERIC, *newval); } -const char * -locale_numeric_assign(const char *value, bool doit, GucSource source) +void +assign_locale_numeric(const char *newval, void *extra) { - return locale_xxx_assign(LC_NUMERIC, value, doit, source); + CurrentLocaleConvValid = false; } -const char * -locale_time_assign(const char *value, bool doit, GucSource source) +bool +check_locale_time(char **newval, void **extra, GucSource source) { - return locale_xxx_assign(LC_TIME, value, doit, source); + return check_locale(LC_TIME, *newval); } +void +assign_locale_time(const char *newval, void *extra) +{ + CurrentLCTimeValid = false; +} /* * We allow LC_MESSAGES to actually be set globally. @@ -293,31 +294,39 @@ locale_time_assign(const char *value, bool doit, GucSource source) * The idea there is just to accept the environment setting *if possible* * during startup, until we can read the proper value from postgresql.conf. */ -const char * -locale_messages_assign(const char *value, bool doit, GucSource source) +bool +check_locale_messages(char **newval, void **extra, GucSource source) { - if (*value == '\0' && source != PGC_S_DEFAULT) - return NULL; + if (**newval == '\0') + { + if (source == PGC_S_DEFAULT) + return true; + else + return false; + } /* * LC_MESSAGES category does not exist everywhere, but accept it anyway * - * On Windows, we can't even check the value, so the non-doit case is a - * no-op + * On Windows, we can't even check the value, so accept blindly + */ +#if defined(LC_MESSAGES) && !defined(WIN32) + return check_locale(LC_MESSAGES, *newval); +#else + return true; +#endif +} + +void +assign_locale_messages(const char *newval, void *extra) +{ + /* + * LC_MESSAGES category does not exist everywhere, but accept it anyway. + * We ignore failure, as per comment above. */ #ifdef LC_MESSAGES - if (doit) - { - if (!pg_perm_setlocale(LC_MESSAGES, value)) - if (source != PGC_S_DEFAULT) - return NULL; - } -#ifndef WIN32 - else - value = locale_xxx_assign(LC_MESSAGES, value, false, source); -#endif /* WIN32 */ -#endif /* LC_MESSAGES */ - return value; + (void) pg_perm_setlocale(LC_MESSAGES, newval); +#endif } diff --git a/src/backend/utils/cache/ts_cache.c b/src/backend/utils/cache/ts_cache.c index 6a99e78c6a42c7fac68671948850d7a851bb9ad6..fc93551d069d2de1bce3de51dacd8385fab6dea9 100644 --- a/src/backend/utils/cache/ts_cache.c +++ b/src/backend/utils/cache/ts_cache.c @@ -587,8 +587,9 @@ getTSCurrentConfig(bool emitError) return TSCurrentConfigCache; } -const char * -assignTSCurrentConfig(const char *newval, bool doit, GucSource source) +/* GUC check_hook for default_text_search_config */ +bool +check_TSCurrentConfig(char **newval, void **extra, GucSource source) { /* * If we aren't inside a transaction, we cannot do database access so @@ -601,10 +602,10 @@ assignTSCurrentConfig(const char *newval, bool doit, GucSource source) Form_pg_ts_config cfg; char *buf; - cfgId = get_ts_config_oid(stringToQualifiedNameList(newval), true); + cfgId = get_ts_config_oid(stringToQualifiedNameList(*newval), true); if (!OidIsValid(cfgId)) - return NULL; + return false; /* * Modify the actually stored value to be fully qualified, to ensure @@ -622,17 +623,20 @@ assignTSCurrentConfig(const char *newval, bool doit, GucSource source) ReleaseSysCache(tuple); /* GUC wants it malloc'd not palloc'd */ - newval = strdup(buf); + free(*newval); + *newval = strdup(buf); pfree(buf); - - if (doit && newval) - TSCurrentConfigCache = cfgId; - } - else - { - if (doit) - TSCurrentConfigCache = InvalidOid; + if (!newval) + return false; } - return newval; + return true; +} + +/* GUC assign_hook for default_text_search_config */ +void +assign_TSCurrentConfig(const char *newval, void *extra) +{ + /* Just reset the cache to force a lookup on first use */ + TSCurrentConfigCache = InvalidOid; } diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c index e7bc046d87007d40431d262a9a0e80aa6edac12f..9e58735aeec2fdc48e9838a5d1997e52db51afb9 100644 --- a/src/backend/utils/error/elog.c +++ b/src/backend/utils/error/elog.c @@ -1156,6 +1156,62 @@ elog_finish(int elevel, const char *fmt,...) errfinish(0); } + +/* + * Functions to allow construction of error message strings separately from + * the ereport() call itself. + * + * The expected calling convention is + * + * pre_format_elog_string(errno, domain), var = format_elog_string(format,...) + * + * which can be hidden behind a macro such as GUC_check_errdetail(). We + * assume that any functions called in the arguments of format_elog_string() + * cannot result in re-entrant use of these functions --- otherwise the wrong + * text domain might be used, or the wrong errno substituted for %m. This is + * okay for the current usage with GUC check hooks, but might need further + * effort someday. + * + * The result of format_elog_string() is stored in ErrorContext, and will + * therefore survive until FlushErrorState() is called. + */ +static int save_format_errnumber; +static const char *save_format_domain; + +void +pre_format_elog_string(int errnumber, const char *domain) +{ + /* Save errno before evaluation of argument functions can change it */ + save_format_errnumber = errnumber; + /* Save caller's text domain */ + save_format_domain = domain; +} + +char * +format_elog_string(const char *fmt, ...) +{ + ErrorData errdata; + ErrorData *edata; + MemoryContext oldcontext; + + /* Initialize a mostly-dummy error frame */ + edata = &errdata; + MemSet(edata, 0, sizeof(ErrorData)); + /* the default text domain is the backend's */ + edata->domain = save_format_domain ? save_format_domain : PG_TEXTDOMAIN("postgres"); + /* set the errno to be used to interpret %m */ + edata->saved_errno = save_format_errnumber; + + oldcontext = MemoryContextSwitchTo(ErrorContext); + + EVALUATE_MESSAGE(message, false, true); + + MemoryContextSwitchTo(oldcontext); + + return edata->message; +} + + /* * Actual output of the top-of-stack error message * diff --git a/src/backend/utils/mb/mbutils.c b/src/backend/utils/mb/mbutils.c index a60188df1099a944f6b0b6fec157052ce31f4381..b1281778036a398cf4f31fafcf8a728ac742affb 100644 --- a/src/backend/utils/mb/mbutils.c +++ b/src/backend/utils/mb/mbutils.c @@ -77,12 +77,16 @@ static int cliplen(const char *str, int len, int limit); /* - * Set the client encoding and save fmgrinfo for the conversion - * function if necessary. Returns 0 if okay, -1 if not (bad encoding - * or can't support conversion) + * Prepare for a future call to SetClientEncoding. Success should mean + * that SetClientEncoding is guaranteed to succeed for this encoding request. + * + * (But note that success before backend_startup_complete does not guarantee + * success after ...) + * + * Returns 0 if okay, -1 if not (bad encoding or can't support conversion) */ int -SetClientEncoding(int encoding, bool doit) +PrepareClientEncoding(int encoding) { int current_server_encoding; ListCell *lc; @@ -92,11 +96,7 @@ SetClientEncoding(int encoding, bool doit) /* Can't do anything during startup, per notes above */ if (!backend_startup_complete) - { - if (doit) - pending_client_encoding = encoding; return 0; - } current_server_encoding = GetDatabaseEncoding(); @@ -106,15 +106,7 @@ SetClientEncoding(int encoding, bool doit) if (current_server_encoding == encoding || current_server_encoding == PG_SQL_ASCII || encoding == PG_SQL_ASCII) - { - if (doit) - { - ClientEncoding = &pg_enc2name_tbl[encoding]; - ToServerConvProc = NULL; - ToClientConvProc = NULL; - } return 0; - } if (IsTransactionState()) { @@ -138,12 +130,6 @@ SetClientEncoding(int encoding, bool doit) if (!OidIsValid(to_client_proc)) return -1; - /* - * Done if not wanting to actually apply setting. - */ - if (!doit) - return 0; - /* * Load the fmgr info into TopMemoryContext (could still fail here) */ @@ -162,30 +148,9 @@ SetClientEncoding(int encoding, bool doit) MemoryContextSwitchTo(oldcontext); /* - * Everything is okay, so apply the setting. + * We cannot yet remove any older entry for the same encoding pair, + * since it could still be in use. SetClientEncoding will clean up. */ - ClientEncoding = &pg_enc2name_tbl[encoding]; - ToServerConvProc = &convinfo->to_server_info; - ToClientConvProc = &convinfo->to_client_info; - - /* - * Remove any older entry for the same encoding pair (this is just to - * avoid memory leakage). - */ - foreach(lc, ConvProcList) - { - ConvProcInfo *oldinfo = (ConvProcInfo *) lfirst(lc); - - if (oldinfo == convinfo) - continue; - if (oldinfo->s_encoding == convinfo->s_encoding && - oldinfo->c_encoding == convinfo->c_encoding) - { - ConvProcList = list_delete_ptr(ConvProcList, oldinfo); - pfree(oldinfo); - break; /* need not look further */ - } - } return 0; /* success */ } @@ -205,15 +170,7 @@ SetClientEncoding(int encoding, bool doit) if (oldinfo->s_encoding == current_server_encoding && oldinfo->c_encoding == encoding) - { - if (doit) - { - ClientEncoding = &pg_enc2name_tbl[encoding]; - ToServerConvProc = &oldinfo->to_server_info; - ToClientConvProc = &oldinfo->to_client_info; - } return 0; - } } return -1; /* it's not cached, so fail */ @@ -221,8 +178,83 @@ SetClientEncoding(int encoding, bool doit) } /* - * Initialize client encoding if necessary. - * called from InitPostgres() once during backend startup. + * Set the active client encoding and set up the conversion-function pointers. + * PrepareClientEncoding should have been called previously for this encoding. + * + * Returns 0 if okay, -1 if not (bad encoding or can't support conversion) + */ +int +SetClientEncoding(int encoding) +{ + int current_server_encoding; + bool found; + ListCell *lc; + + if (!PG_VALID_FE_ENCODING(encoding)) + return -1; + + /* Can't do anything during startup, per notes above */ + if (!backend_startup_complete) + { + pending_client_encoding = encoding; + return 0; + } + + current_server_encoding = GetDatabaseEncoding(); + + /* + * Check for cases that require no conversion function. + */ + if (current_server_encoding == encoding || + current_server_encoding == PG_SQL_ASCII || + encoding == PG_SQL_ASCII) + { + ClientEncoding = &pg_enc2name_tbl[encoding]; + ToServerConvProc = NULL; + ToClientConvProc = NULL; + return 0; + } + + /* + * Search the cache for the entry previously prepared by + * PrepareClientEncoding; if there isn't one, we lose. While at it, + * release any duplicate entries so that repeated Prepare/Set cycles + * don't leak memory. + */ + found = false; + foreach(lc, ConvProcList) + { + ConvProcInfo *convinfo = (ConvProcInfo *) lfirst(lc); + + if (convinfo->s_encoding == current_server_encoding && + convinfo->c_encoding == encoding) + { + if (!found) + { + /* Found newest entry, so set up */ + ClientEncoding = &pg_enc2name_tbl[encoding]; + ToServerConvProc = &convinfo->to_server_info; + ToClientConvProc = &convinfo->to_client_info; + found = true; + } + else + { + /* Duplicate entry, release it */ + ConvProcList = list_delete_ptr(ConvProcList, convinfo); + pfree(convinfo); + } + } + } + + if (found) + return 0; /* success */ + else + return -1; /* it's not cached, so fail */ +} + +/* + * Initialize client encoding conversions. + * Called from InitPostgres() once during backend startup. */ void InitializeClientEncoding(void) @@ -230,7 +262,8 @@ InitializeClientEncoding(void) Assert(!backend_startup_complete); backend_startup_complete = true; - if (SetClientEncoding(pending_client_encoding, true) < 0) + if (PrepareClientEncoding(pending_client_encoding) < 0 || + SetClientEncoding(pending_client_encoding) < 0) { /* * Oops, the requested conversion is not available. We couldn't fail diff --git a/src/backend/utils/misc/README b/src/backend/utils/misc/README index 881862a30b182a94cd71c6ed1a98dd6e6d5e51e0..70244ced18d869c5e46e8f1a6a455f9704c27ae0 100644 --- a/src/backend/utils/misc/README +++ b/src/backend/utils/misc/README @@ -1,10 +1,10 @@ src/backend/utils/misc/README -Guc Implementation Notes +GUC Implementation Notes ======================== The GUC (Grand Unified Configuration) module implements configuration -variables of multiple types (currently boolean, enum, int, float, and string). +variables of multiple types (currently boolean, enum, int, real, and string). Variable settings can come from various places, with a priority ordering determining which setting is used. @@ -12,65 +12,112 @@ determining which setting is used. Per-Variable Hooks ------------------ -Each variable known to GUC can optionally have an assign_hook and/or -a show_hook to provide customized behavior. Assign hooks are used to -perform validity checking on variable values (above and beyond what -GUC can do). They are also used to update any derived state that needs -to change when a GUC variable is set. Show hooks are used to modify -the default SHOW display for a variable. +Each variable known to GUC can optionally have a check_hook, an +assign_hook, and/or a show_hook to provide customized behavior. +Check hooks are used to perform validity checking on variable values +(above and beyond what GUC can do), to compute derived settings when +nontrivial work is needed to do that, and optionally to "canonicalize" +user-supplied values. Assign hooks are used to update any derived state +that needs to change when a GUC variable is set. Show hooks are used to +modify the default SHOW display for a variable. + + +If a check_hook is provided, it points to a function of the signature + bool check_hook(datatype *newvalue, void **extra, GucSource source) +The "newvalue" argument is of type bool *, int *, double *, or char ** +for bool, int/enum, real, or string variables respectively. The check +function should validate the proposed new value, and return true if it is +OK or false if not. The function can optionally do a few other things: + +* When rejecting a bad proposed value, it may be useful to append some +additional information to the generic "invalid value for parameter FOO" +complaint that guc.c will emit. To do that, call + void GUC_check_errdetail(const char *format, ...) +where the format string and additional arguments follow the rules for +errdetail() arguments. The resulting string will be emitted as the +DETAIL line of guc.c's error report, so it should follow the message style +guidelines for DETAIL messages. There is also + void GUC_check_errhint(const char *format, ...) +which can be used in the same way to append a HINT message. +Occasionally it may even be appropriate to override guc.c's generic primary +message or error code, which can be done with + void GUC_check_errcode(int sqlerrcode) + void GUC_check_errmsg(const char *format, ...) +In general, check_hooks should avoid throwing errors directly if possible, +though this may be impractical to avoid for some corner cases such as +out-of-memory. + +* Since the newvalue is pass-by-reference, the function can modify it. +This might be used for example to canonicalize the spelling of a string +value, round off a buffer size to the nearest supported value, or replace +a special value such as "-1" with a computed default value. If the +function wishes to replace a string value, it must malloc (not palloc) +the replacement value, and be sure to free() the previous value. + +* Derived information, such as the role OID represented by a user name, +can be stored for use by the assign hook. To do this, malloc (not palloc) +storage space for the information, and return its address at *extra. +guc.c will automatically free() this space when the associated GUC setting +is no longer of interest. *extra is initialized to NULL before call, so +it can be ignored if not needed. + +The "source" argument indicates the source of the proposed new value, +If it is >= PGC_S_INTERACTIVE, then we are performing an interactive +assignment (e.g., a SET command). But when source < PGC_S_INTERACTIVE, +we are reading a non-interactive option source, such as postgresql.conf. +This is sometimes needed to determine whether a setting should be +allowed. The check_hook might also look at the current actual value of +the variable to determine what is allowed. + +Note that check hooks are sometimes called just to validate a value, +without any intention of actually changing the setting. Therefore the +check hook must *not* take any action based on the assumption that an +assignment will occur. + If an assign_hook is provided, it points to a function of the signature - bool assign_hook(newvalue, bool doit, GucSource source) -where the type of "newvalue" matches the kind of variable. This function -is called immediately before actually setting the variable's value (so it -can look at the actual variable to determine the old value). If the -function returns "true" then the assignment is completed; if it returns -"false" then newvalue is considered invalid and the assignment is not -performed. If "doit" is false then the function should simply check -validity of newvalue and not change any derived state. The "source" parameter -indicates where the new value came from. If it is >= PGC_S_INTERACTIVE, -then we are performing an interactive assignment (e.g., a SET command), and -ereport(ERROR) is safe to do. But when source < PGC_S_INTERACTIVE, we are -reading a non-interactive option source, such as postgresql.conf. In this -case the assign_hook should *not* ereport but should just return false if it -doesn't like the newvalue. - -If an assign_hook returns false then guc.c will report a generic "invalid -value for option FOO" error message. If you feel the need to provide a more -specific error message, ereport() it using "GUC_complaint_elevel(source)" -as the error level. Note that this might return either ERROR or a lower level -such as LOG, so the ereport call might or might not return. If it does -return, return false out of the assign_hook. - -For string variables, the signature for assign hooks is a bit different: - const char *assign_hook(const char *newvalue, - bool doit, - GucSource source) -The meanings of the parameters are the same as for the other types of GUC -variables, but the return value is handled differently: - NULL --- assignment fails (like returning false for other datatypes) - newvalue --- assignment succeeds, assign the newvalue as-is - malloc'd (not palloc'd!!!) string --- assign that value instead -The third choice is allowed in case the assign_hook wants to return a -"canonical" version of the new value. For example, the assign_hook for -datestyle always returns a string that includes both output and input -datestyle options, although the input might have specified only one. - -Note that a string variable's assign_hook will NEVER be called with a NULL -value for newvalue, since there would be no way to distinguish success -and failure returns. If the boot_val or reset_val for a string variable -is NULL, it will just be assigned without calling the assign_hook. -Therefore, a NULL boot_val should never be used in combination with an -assign_hook that has side-effects, as the side-effects wouldn't happen -during a RESET that re-institutes the boot-time setting. + void assign_hook(datatype newvalue, void *extra) +where the type of "newvalue" matches the kind of variable, and "extra" +is the derived-information pointer returned by the check_hook (always +NULL if there is no check_hook). This function is called immediately +before actually setting the variable's value (so it can look at the actual +variable to determine the old value, for example to avoid doing work when +the value isn't really changing). + +Note that there is no provision for a failure result code. assign_hooks +should never fail except under the most dire circumstances, since a failure +may for example result in GUC settings not being rolled back properly during +transaction abort. In general, try to do anything that could conceivably +fail in a check_hook instead, and pass along the results in an "extra" +struct, so that the assign hook has little to do beyond copying the data to +someplace. This applies particularly to catalog lookups: any required +lookups must be done in the check_hook, since the assign_hook may be +executed during transaction rollback when lookups will be unsafe. + +Note that check_hooks are sometimes called outside any transaction, too. +This happens when processing the wired-in "bootstrap" value, values coming +from the postmaster command line or environment, or values coming from +postgresql.conf. Therefore, any catalog lookups done in a check_hook +should be guarded with an IsTransactionState() test, and there must be a +fallback path to allow derived values to be computed during the first +subsequent use of the GUC setting within a transaction. A typical +arrangement is for the catalog values computed by the check_hook and +installed by the assign_hook to be used only for the remainder of the +transaction in which the new setting is made. Each subsequent transaction +looks up the values afresh on first use. This arrangement is useful to +prevent use of stale catalog values, independently of the problem of +needing to check GUC values outside a transaction. + If a show_hook is provided, it points to a function of the signature const char *show_hook(void) This hook allows variable-specific computation of the value displayed -by SHOW. +by SHOW (and other SQL features for showing GUC variable values). +The return value can point to a static buffer, since show functions are +not used re-entrantly. -Saving/Restoring Guc Variable Values +Saving/Restoring GUC Variable Values ------------------------------------ Prior values of configuration variables must be remembered in order to deal @@ -200,27 +247,49 @@ these has a current source priority <= PGC_S_FILE. (It is thus possible for reset_val to track the config-file setting even if there is currently a different interactive value of the actual variable.) -The assign_hook and show_hook routines work only with the actual variable, -and are not directly aware of the additional values maintained by GUC. -This is not a problem for normal usage, since we can assign first to the -actual variable and then (if that succeeds) to the additional values as -needed. However, for SIGHUP rereads we may not want to assign to the -actual variable. Our procedure in that case is to call the assign_hook -with doit = false so that the value is validated, but no derived state is -changed. +The check_hook, assign_hook and show_hook routines work only with the +actual variable, and are not directly aware of the additional values +maintained by GUC. -String Memory Handling ----------------------- +GUC Memory Handling +------------------- -String option values are allocated with strdup, not with the -pstrdup/palloc mechanisms. We would need to keep them in a permanent -context anyway, and strdup gives us more control over handling +String variable values are allocated with malloc/strdup, not with the +palloc/pstrdup mechanisms. We would need to keep them in a permanent +context anyway, and malloc gives us more control over handling out-of-memory failures. We allow a string variable's actual value, reset_val, boot_val, and stacked values to point at the same storage. This makes it slightly harder to free space (we must test whether a value to be freed isn't equal to any of the other pointers in the GUC entry or associated stack items). The main -advantage is that we never need to strdup during transaction commit/abort, +advantage is that we never need to malloc during transaction commit/abort, so cannot cause an out-of-memory failure there. + +"Extra" structs returned by check_hook routines are managed in the same +way as string values. Note that we support "extra" structs for all types +of GUC variables, although they are mainly useful with strings. + + +GUC and Null String Variables +----------------------------- + +A GUC string variable can have a boot_val of NULL. guc.c handles this +unsurprisingly, assigning the NULL to the underlying C variable. Any code +using such a variable, as well as any hook functions for it, must then be +prepared to deal with a NULL value. + +However, it is not possible to assign a NULL value to a GUC string +variable in any other way: values coming from SET, postgresql.conf, etc, +might be empty strings, but they'll never be NULL. And SHOW displays +a NULL the same as an empty string. It is therefore not appropriate to +treat a NULL value as a distinct user-visible setting. A typical use +for a NULL boot_val is to denote that a value hasn't yet been set for +a variable that will receive a real value later in startup. + +If it's undesirable for code using the underlying C variable to have to +worry about NULL values ever, the variable can be given a non-null static +initializer as well as a non-null boot_val. guc.c will overwrite the +static initializer pointer with a copy of the boot_val during +InitializeGUCOptions, but the variable will never contain a NULL. diff --git a/src/backend/utils/misc/guc-file.l b/src/backend/utils/misc/guc-file.l index e660395898274a94ca182641dacdbdc8fefa8bce..10ef12eb24be0e0b4d463b27e111962dd4a1fcb0 100644 --- a/src/backend/utils/misc/guc-file.l +++ b/src/backend/utils/misc/guc-file.l @@ -141,7 +141,8 @@ ProcessConfigFile(GucContext context) */ cvc_struct = (struct config_string *) find_option("custom_variable_classes", false, elevel); - if (cvc_struct && cvc_struct->gen.reset_source > PGC_S_FILE) + Assert(cvc_struct); + if (cvc_struct->gen.reset_source > PGC_S_FILE) { cvc = guc_strdup(elevel, cvc_struct->reset_val); if (cvc == NULL) @@ -151,19 +152,18 @@ ProcessConfigFile(GucContext context) guc_name_compare(head->name, "custom_variable_classes") == 0) { /* - * Need to canonicalize the value via the assign hook. Casting away - * const is a bit ugly, but we know the result is malloc'd. + * Need to canonicalize the value by calling the check hook. */ - cvc = (char *) assign_custom_variable_classes(head->value, - false, PGC_S_FILE); + void *extra = NULL; + + cvc = guc_strdup(elevel, head->value); if (cvc == NULL) - { - ereport(elevel, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("invalid value for parameter \"%s\": \"%s\"", - head->name, head->value))); goto cleanup_list; - } + if (!call_string_check_hook(cvc_struct, &cvc, &extra, + PGC_S_FILE, elevel)) + goto cleanup_list; + if (extra) + free(extra); } /* diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 2151fde36185612f43c3299e8e5db4c1f54a2e57..5e4904aeb7fc8a34828358b68c41661365f573bd 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -104,7 +104,7 @@ * backend ID as a 3-byte signed integer. Even if that limitation were * removed, we still could not exceed INT_MAX/4 because some places compute * 4*MaxBackends without any overflow check. This is rechecked in - * assign_maxconnections, since MaxBackends is computed as MaxConnections + * check_maxconnections, since MaxBackends is computed as MaxConnections * plus autovacuum_max_workers plus one (for the autovacuum launcher). */ #define MAX_BACKENDS 0x7fffff @@ -143,11 +143,29 @@ extern bool trace_syncscan; extern bool optimize_bounded_sort; #endif +static int GUC_check_errcode_value; + +/* global variables for check hook support */ +char *GUC_check_errmsg_string; +char *GUC_check_errdetail_string; +char *GUC_check_errhint_string; + + static void set_config_sourcefile(const char *name, char *sourcefile, int sourceline); - -static const char *assign_log_destination(const char *value, - bool doit, GucSource source); +static bool call_bool_check_hook(struct config_bool *conf, bool *newval, + void **extra, GucSource source, int elevel); +static bool call_int_check_hook(struct config_int *conf, int *newval, + void **extra, GucSource source, int elevel); +static bool call_real_check_hook(struct config_real *conf, double *newval, + void **extra, GucSource source, int elevel); +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 check_log_destination(char **newval, void **extra, GucSource source); +static void assign_log_destination(const char *newval, void *extra); #ifdef HAVE_SYSLOG static int syslog_facility = LOG_LOCAL0; @@ -155,36 +173,36 @@ static int syslog_facility = LOG_LOCAL0; static int syslog_facility = 0; #endif -static bool assign_syslog_facility(int newval, - bool doit, GucSource source); -static const char *assign_syslog_ident(const char *ident, - bool doit, GucSource source); - -static bool assign_session_replication_role(int newval, bool doit, - GucSource source); -static const char *show_num_temp_buffers(void); -static bool assign_phony_autocommit(bool newval, bool doit, GucSource source); -static const char *assign_custom_variable_classes(const char *newval, bool doit, - GucSource source); -static bool assign_debug_assertions(bool newval, bool doit, GucSource source); -static bool assign_bonjour(bool newval, bool doit, GucSource source); -static bool assign_ssl(bool newval, bool doit, GucSource source); -static bool assign_stage_log_stats(bool newval, bool doit, GucSource source); -static bool assign_log_stats(bool newval, bool doit, GucSource source); -static const char *assign_canonical_path(const char *newval, bool doit, GucSource source); -static const char *assign_timezone_abbreviations(const char *newval, bool doit, GucSource source); +static void assign_syslog_facility(int newval, void *extra); +static void assign_syslog_ident(const char *newval, void *extra); +static void assign_session_replication_role(int newval, void *extra); +static bool check_temp_buffers(int *newval, void **extra, GucSource source); +static bool check_phony_autocommit(bool *newval, void **extra, GucSource source); +static bool check_custom_variable_classes(char **newval, void **extra, GucSource source); +static bool check_debug_assertions(bool *newval, void **extra, GucSource source); +static bool check_bonjour(bool *newval, void **extra, GucSource source); +static bool check_ssl(bool *newval, void **extra, GucSource source); +static bool check_stage_log_stats(bool *newval, void **extra, GucSource source); +static bool check_log_stats(bool *newval, void **extra, GucSource source); +static bool check_canonical_path(char **newval, void **extra, GucSource source); +static bool check_timezone_abbreviations(char **newval, void **extra, GucSource source); +static void assign_timezone_abbreviations(const char *newval, void *extra); static const char *show_archive_command(void); -static bool assign_tcp_keepalives_idle(int newval, bool doit, GucSource source); -static bool assign_tcp_keepalives_interval(int newval, bool doit, GucSource source); -static bool assign_tcp_keepalives_count(int newval, bool doit, GucSource source); +static void assign_tcp_keepalives_idle(int newval, void *extra); +static void assign_tcp_keepalives_interval(int newval, void *extra); +static void assign_tcp_keepalives_count(int newval, void *extra); static const char *show_tcp_keepalives_idle(void); static const char *show_tcp_keepalives_interval(void); static const char *show_tcp_keepalives_count(void); -static bool assign_maxconnections(int newval, bool doit, GucSource source); -static bool assign_autovacuum_max_workers(int newval, bool doit, GucSource source); -static bool assign_effective_io_concurrency(int newval, bool doit, GucSource source); -static const char *assign_pgstat_temp_directory(const char *newval, bool doit, GucSource source); -static const char *assign_application_name(const char *newval, bool doit, GucSource source); +static bool check_maxconnections(int *newval, void **extra, GucSource source); +static void assign_maxconnections(int newval, void *extra); +static bool check_autovacuum_max_workers(int *newval, void **extra, GucSource source); +static void assign_autovacuum_max_workers(int newval, void *extra); +static bool check_effective_io_concurrency(int *newval, void **extra, GucSource source); +static void assign_effective_io_concurrency(int newval, void *extra); +static void assign_pgstat_temp_directory(const char *newval, void *extra); +static bool check_application_name(char **newval, void **extra, GucSource source); +static void assign_application_name(const char *newval, void *extra); static const char *show_unix_socket_permissions(void); static const char *show_log_file_mode(void); @@ -407,7 +425,7 @@ int log_min_duration_statement = -1; int log_temp_files = -1; int trace_recovery_messages = LOG; -int num_temp_buffers = 1000; +int num_temp_buffers = 1024; char *data_directory; char *ConfigFileName; @@ -444,6 +462,8 @@ static int server_version_num; static char *timezone_string; static char *log_timezone_string; static char *timezone_abbreviations_string; +static char *XactIsoLevel_string; +static char *session_authorization_string; static char *custom_variable_classes; static int max_function_args; static int max_index_keys; @@ -455,10 +475,8 @@ static int wal_segment_size; static bool integer_datetimes; static int effective_io_concurrency; -/* should be static, but commands/variable.c needs to get at these */ +/* should be static, but commands/variable.c needs to get at this */ char *role_string; -char *session_authorization_string; -char *XactIsoLevel_string; /* @@ -643,7 +661,8 @@ static struct config_bool ConfigureNamesBool[] = NULL }, &enable_seqscan, - true, NULL, NULL + true, + NULL, NULL, NULL }, { {"enable_indexscan", PGC_USERSET, QUERY_TUNING_METHOD, @@ -651,7 +670,8 @@ static struct config_bool ConfigureNamesBool[] = NULL }, &enable_indexscan, - true, NULL, NULL + true, + NULL, NULL, NULL }, { {"enable_bitmapscan", PGC_USERSET, QUERY_TUNING_METHOD, @@ -659,7 +679,8 @@ static struct config_bool ConfigureNamesBool[] = NULL }, &enable_bitmapscan, - true, NULL, NULL + true, + NULL, NULL, NULL }, { {"enable_tidscan", PGC_USERSET, QUERY_TUNING_METHOD, @@ -667,7 +688,8 @@ static struct config_bool ConfigureNamesBool[] = NULL }, &enable_tidscan, - true, NULL, NULL + true, + NULL, NULL, NULL }, { {"enable_sort", PGC_USERSET, QUERY_TUNING_METHOD, @@ -675,7 +697,8 @@ static struct config_bool ConfigureNamesBool[] = NULL }, &enable_sort, - true, NULL, NULL + true, + NULL, NULL, NULL }, { {"enable_hashagg", PGC_USERSET, QUERY_TUNING_METHOD, @@ -683,7 +706,8 @@ static struct config_bool ConfigureNamesBool[] = NULL }, &enable_hashagg, - true, NULL, NULL + true, + NULL, NULL, NULL }, { {"enable_material", PGC_USERSET, QUERY_TUNING_METHOD, @@ -691,7 +715,8 @@ static struct config_bool ConfigureNamesBool[] = NULL }, &enable_material, - true, NULL, NULL + true, + NULL, NULL, NULL }, { {"enable_nestloop", PGC_USERSET, QUERY_TUNING_METHOD, @@ -699,7 +724,8 @@ static struct config_bool ConfigureNamesBool[] = NULL }, &enable_nestloop, - true, NULL, NULL + true, + NULL, NULL, NULL }, { {"enable_mergejoin", PGC_USERSET, QUERY_TUNING_METHOD, @@ -707,7 +733,8 @@ static struct config_bool ConfigureNamesBool[] = NULL }, &enable_mergejoin, - true, NULL, NULL + true, + NULL, NULL, NULL }, { {"enable_hashjoin", PGC_USERSET, QUERY_TUNING_METHOD, @@ -715,7 +742,8 @@ static struct config_bool ConfigureNamesBool[] = NULL }, &enable_hashjoin, - true, NULL, NULL + true, + NULL, NULL, NULL }, { {"geqo", PGC_USERSET, QUERY_TUNING_GEQO, @@ -724,7 +752,8 @@ static struct config_bool ConfigureNamesBool[] = "exhaustive searching.") }, &enable_geqo, - true, NULL, NULL + true, + NULL, NULL, NULL }, { /* Not for general use --- used by SET SESSION AUTHORIZATION */ @@ -734,7 +763,8 @@ static struct config_bool ConfigureNamesBool[] = GUC_REPORT | GUC_NO_SHOW_ALL | GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE }, &session_auth_is_superuser, - false, NULL, NULL + false, + NULL, NULL, NULL }, { {"bonjour", PGC_POSTMASTER, CONN_AUTH_SETTINGS, @@ -742,7 +772,8 @@ static struct config_bool ConfigureNamesBool[] = NULL }, &enable_bonjour, - false, assign_bonjour, NULL + false, + check_bonjour, NULL, NULL }, { {"ssl", PGC_POSTMASTER, CONN_AUTH_SECURITY, @@ -750,7 +781,8 @@ static struct config_bool ConfigureNamesBool[] = NULL }, &EnableSSL, - false, assign_ssl, NULL + false, + check_ssl, NULL, NULL }, { {"fsync", PGC_SIGHUP, WAL_SETTINGS, @@ -761,7 +793,8 @@ static struct config_bool ConfigureNamesBool[] = "an operating system or hardware crash.") }, &enableFsync, - true, NULL, NULL + true, + NULL, NULL, NULL }, { {"zero_damaged_pages", PGC_SUSET, DEVELOPER_OPTIONS, @@ -774,7 +807,8 @@ static struct config_bool ConfigureNamesBool[] = GUC_NOT_IN_SAMPLE }, &zero_damaged_pages, - false, NULL, NULL + false, + NULL, NULL, NULL }, { {"full_page_writes", PGC_SIGHUP, WAL_SETTINGS, @@ -786,7 +820,8 @@ static struct config_bool ConfigureNamesBool[] = "is possible.") }, &fullPageWrites, - true, NULL, NULL + true, + NULL, NULL, NULL }, { {"silent_mode", PGC_POSTMASTER, LOGGING_WHERE, @@ -795,7 +830,8 @@ static struct config_bool ConfigureNamesBool[] = "background and any controlling terminals are dissociated.") }, &SilentMode, - false, NULL, NULL + false, + NULL, NULL, NULL }, { {"log_checkpoints", PGC_SIGHUP, LOGGING_WHAT, @@ -803,7 +839,8 @@ static struct config_bool ConfigureNamesBool[] = NULL }, &log_checkpoints, - false, NULL, NULL + false, + NULL, NULL, NULL }, { {"log_connections", PGC_BACKEND, LOGGING_WHAT, @@ -811,7 +848,8 @@ static struct config_bool ConfigureNamesBool[] = NULL }, &Log_connections, - false, NULL, NULL + false, + NULL, NULL, NULL }, { {"log_disconnections", PGC_BACKEND, LOGGING_WHAT, @@ -819,7 +857,8 @@ static struct config_bool ConfigureNamesBool[] = NULL }, &Log_disconnections, - false, NULL, NULL + false, + NULL, NULL, NULL }, { {"debug_assertions", PGC_USERSET, DEVELOPER_OPTIONS, @@ -833,7 +872,7 @@ static struct config_bool ConfigureNamesBool[] = #else false, #endif - assign_debug_assertions, NULL + check_debug_assertions, NULL, NULL }, { @@ -842,7 +881,8 @@ static struct config_bool ConfigureNamesBool[] = NULL }, &ExitOnAnyError, - false, NULL, NULL + false, + NULL, NULL, NULL }, { {"restart_after_crash", PGC_SIGHUP, ERROR_HANDLING_OPTIONS, @@ -850,7 +890,8 @@ static struct config_bool ConfigureNamesBool[] = NULL }, &restart_after_crash, - true, NULL, NULL + true, + NULL, NULL, NULL }, { @@ -859,7 +900,8 @@ static struct config_bool ConfigureNamesBool[] = NULL }, &log_duration, - false, NULL, NULL + false, + NULL, NULL, NULL }, { {"debug_print_parse", PGC_USERSET, LOGGING_WHAT, @@ -867,7 +909,8 @@ static struct config_bool ConfigureNamesBool[] = NULL }, &Debug_print_parse, - false, NULL, NULL + false, + NULL, NULL, NULL }, { {"debug_print_rewritten", PGC_USERSET, LOGGING_WHAT, @@ -875,7 +918,8 @@ static struct config_bool ConfigureNamesBool[] = NULL }, &Debug_print_rewritten, - false, NULL, NULL + false, + NULL, NULL, NULL }, { {"debug_print_plan", PGC_USERSET, LOGGING_WHAT, @@ -883,7 +927,8 @@ static struct config_bool ConfigureNamesBool[] = NULL }, &Debug_print_plan, - false, NULL, NULL + false, + NULL, NULL, NULL }, { {"debug_pretty_print", PGC_USERSET, LOGGING_WHAT, @@ -891,7 +936,8 @@ static struct config_bool ConfigureNamesBool[] = NULL }, &Debug_pretty_print, - true, NULL, NULL + true, + NULL, NULL, NULL }, { {"log_parser_stats", PGC_SUSET, STATS_MONITORING, @@ -899,7 +945,8 @@ static struct config_bool ConfigureNamesBool[] = NULL }, &log_parser_stats, - false, assign_stage_log_stats, NULL + false, + check_stage_log_stats, NULL, NULL }, { {"log_planner_stats", PGC_SUSET, STATS_MONITORING, @@ -907,7 +954,8 @@ static struct config_bool ConfigureNamesBool[] = NULL }, &log_planner_stats, - false, assign_stage_log_stats, NULL + false, + check_stage_log_stats, NULL, NULL }, { {"log_executor_stats", PGC_SUSET, STATS_MONITORING, @@ -915,7 +963,8 @@ static struct config_bool ConfigureNamesBool[] = NULL }, &log_executor_stats, - false, assign_stage_log_stats, NULL + false, + check_stage_log_stats, NULL, NULL }, { {"log_statement_stats", PGC_SUSET, STATS_MONITORING, @@ -923,7 +972,8 @@ static struct config_bool ConfigureNamesBool[] = NULL }, &log_statement_stats, - false, assign_log_stats, NULL + false, + check_log_stats, NULL, NULL }, #ifdef BTREE_BUILD_STATS { @@ -933,7 +983,8 @@ static struct config_bool ConfigureNamesBool[] = GUC_NOT_IN_SAMPLE }, &log_btree_build_stats, - false, NULL, NULL + false, + NULL, NULL, NULL }, #endif @@ -945,7 +996,8 @@ static struct config_bool ConfigureNamesBool[] = "the time at which that command began execution.") }, &pgstat_track_activities, - true, NULL, NULL + true, + NULL, NULL, NULL }, { {"track_counts", PGC_SUSET, STATS_COLLECTOR, @@ -953,7 +1005,8 @@ static struct config_bool ConfigureNamesBool[] = NULL }, &pgstat_track_counts, - true, NULL, NULL + true, + NULL, NULL, NULL }, { @@ -962,7 +1015,8 @@ static struct config_bool ConfigureNamesBool[] = gettext_noop("Enables updating of the process title every time a new SQL command is received by the server.") }, &update_process_title, - true, NULL, NULL + true, + NULL, NULL, NULL }, { @@ -971,7 +1025,8 @@ static struct config_bool ConfigureNamesBool[] = NULL }, &autovacuum_start_daemon, - true, NULL, NULL + true, + NULL, NULL, NULL }, { @@ -981,7 +1036,8 @@ static struct config_bool ConfigureNamesBool[] = GUC_NOT_IN_SAMPLE }, &Trace_notify, - false, NULL, NULL + false, + NULL, NULL, NULL }, #ifdef LOCK_DEBUG @@ -992,7 +1048,8 @@ static struct config_bool ConfigureNamesBool[] = GUC_NOT_IN_SAMPLE }, &Trace_locks, - false, NULL, NULL + false, + NULL, NULL, NULL }, { {"trace_userlocks", PGC_SUSET, DEVELOPER_OPTIONS, @@ -1001,7 +1058,8 @@ static struct config_bool ConfigureNamesBool[] = GUC_NOT_IN_SAMPLE }, &Trace_userlocks, - false, NULL, NULL + false, + NULL, NULL, NULL }, { {"trace_lwlocks", PGC_SUSET, DEVELOPER_OPTIONS, @@ -1010,7 +1068,8 @@ static struct config_bool ConfigureNamesBool[] = GUC_NOT_IN_SAMPLE }, &Trace_lwlocks, - false, NULL, NULL + false, + NULL, NULL, NULL }, { {"debug_deadlocks", PGC_SUSET, DEVELOPER_OPTIONS, @@ -1019,7 +1078,8 @@ static struct config_bool ConfigureNamesBool[] = GUC_NOT_IN_SAMPLE }, &Debug_deadlocks, - false, NULL, NULL + false, + NULL, NULL, NULL }, #endif @@ -1029,7 +1089,8 @@ static struct config_bool ConfigureNamesBool[] = NULL }, &log_lock_waits, - false, NULL, NULL + false, + NULL, NULL, NULL }, { @@ -1041,7 +1102,8 @@ static struct config_bool ConfigureNamesBool[] = "setup it might impose a non-negligible performance penalty.") }, &log_hostname, - false, NULL, NULL + false, + NULL, NULL, NULL }, { {"sql_inheritance", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS, @@ -1049,7 +1111,8 @@ static struct config_bool ConfigureNamesBool[] = NULL }, &SQL_inheritance, - true, NULL, NULL + true, + NULL, NULL, NULL }, { {"password_encryption", PGC_USERSET, CONN_AUTH_SECURITY, @@ -1059,7 +1122,8 @@ static struct config_bool ConfigureNamesBool[] = "this parameter determines whether the password is to be encrypted.") }, &Password_encryption, - true, NULL, NULL + true, + NULL, NULL, NULL }, { {"transform_null_equals", PGC_USERSET, COMPAT_OPTIONS_CLIENT, @@ -1071,7 +1135,8 @@ static struct config_bool ConfigureNamesBool[] = "return null (unknown).") }, &Transform_null_equals, - false, NULL, NULL + false, + NULL, NULL, NULL }, { {"db_user_namespace", PGC_SIGHUP, CONN_AUTH_SECURITY, @@ -1079,7 +1144,8 @@ static struct config_bool ConfigureNamesBool[] = NULL }, &Db_user_namespace, - false, NULL, NULL + false, + NULL, NULL, NULL }, { /* only here for backwards compatibility */ @@ -1089,7 +1155,8 @@ static struct config_bool ConfigureNamesBool[] = GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE }, &phony_autocommit, - true, assign_phony_autocommit, NULL + true, + check_phony_autocommit, NULL, NULL }, { {"default_transaction_read_only", PGC_USERSET, CLIENT_CONN_STATEMENT, @@ -1097,7 +1164,8 @@ static struct config_bool ConfigureNamesBool[] = NULL }, &DefaultXactReadOnly, - false, NULL, NULL + false, + NULL, NULL, NULL }, { {"transaction_read_only", PGC_USERSET, CLIENT_CONN_STATEMENT, @@ -1106,7 +1174,8 @@ static struct config_bool ConfigureNamesBool[] = GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE }, &XactReadOnly, - false, assign_transaction_read_only, NULL + false, + check_transaction_read_only, NULL, NULL }, { {"default_transaction_deferrable", PGC_USERSET, CLIENT_CONN_STATEMENT, @@ -1114,7 +1183,8 @@ static struct config_bool ConfigureNamesBool[] = NULL }, &DefaultXactDeferrable, - false, NULL, NULL + false, + NULL, NULL, NULL }, { {"transaction_deferrable", PGC_USERSET, CLIENT_CONN_STATEMENT, @@ -1123,7 +1193,8 @@ static struct config_bool ConfigureNamesBool[] = GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE }, &XactDeferrable, - false, assign_transaction_deferrable, NULL + false, + check_transaction_deferrable, NULL, NULL }, { {"check_function_bodies", PGC_USERSET, CLIENT_CONN_STATEMENT, @@ -1131,7 +1202,8 @@ static struct config_bool ConfigureNamesBool[] = NULL }, &check_function_bodies, - true, NULL, NULL + true, + NULL, NULL, NULL }, { {"array_nulls", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS, @@ -1141,7 +1213,8 @@ static struct config_bool ConfigureNamesBool[] = "otherwise it is taken literally.") }, &Array_nulls, - true, NULL, NULL + true, + NULL, NULL, NULL }, { {"default_with_oids", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS, @@ -1149,7 +1222,8 @@ static struct config_bool ConfigureNamesBool[] = NULL }, &default_with_oids, - false, NULL, NULL + false, + NULL, NULL, NULL }, { {"logging_collector", PGC_POSTMASTER, LOGGING_WHERE, @@ -1157,7 +1231,8 @@ static struct config_bool ConfigureNamesBool[] = NULL }, &Logging_collector, - false, NULL, NULL + false, + NULL, NULL, NULL }, { {"log_truncate_on_rotation", PGC_SIGHUP, LOGGING_WHERE, @@ -1165,7 +1240,8 @@ static struct config_bool ConfigureNamesBool[] = NULL }, &Log_truncate_on_rotation, - false, NULL, NULL + false, + NULL, NULL, NULL }, #ifdef TRACE_SORT @@ -1176,7 +1252,8 @@ static struct config_bool ConfigureNamesBool[] = GUC_NOT_IN_SAMPLE }, &trace_sort, - false, NULL, NULL + false, + NULL, NULL, NULL }, #endif @@ -1189,7 +1266,8 @@ static struct config_bool ConfigureNamesBool[] = GUC_NOT_IN_SAMPLE }, &trace_syncscan, - false, NULL, NULL + false, + NULL, NULL, NULL }, #endif @@ -1203,7 +1281,8 @@ static struct config_bool ConfigureNamesBool[] = GUC_NOT_IN_SAMPLE }, &optimize_bounded_sort, - true, NULL, NULL + true, + NULL, NULL, NULL }, #endif @@ -1215,7 +1294,8 @@ static struct config_bool ConfigureNamesBool[] = GUC_NOT_IN_SAMPLE }, &XLOG_DEBUG, - false, NULL, NULL + false, + NULL, NULL, NULL }, #endif @@ -1227,10 +1307,11 @@ static struct config_bool ConfigureNamesBool[] = }, &integer_datetimes, #ifdef HAVE_INT64_TIMESTAMP - true, NULL, NULL + true, #else - false, NULL, NULL + false, #endif + NULL, NULL, NULL }, { @@ -1239,7 +1320,8 @@ static struct config_bool ConfigureNamesBool[] = NULL }, &pg_krb_caseins_users, - false, NULL, NULL + false, + NULL, NULL, NULL }, { @@ -1248,7 +1330,8 @@ static struct config_bool ConfigureNamesBool[] = NULL }, &escape_string_warning, - true, NULL, NULL + true, + NULL, NULL, NULL }, { @@ -1258,7 +1341,8 @@ static struct config_bool ConfigureNamesBool[] = GUC_REPORT }, &standard_conforming_strings, - true, NULL, NULL + true, + NULL, NULL, NULL }, { @@ -1267,7 +1351,8 @@ static struct config_bool ConfigureNamesBool[] = NULL }, &synchronize_seqscans, - true, NULL, NULL + true, + NULL, NULL, NULL }, { @@ -1276,7 +1361,8 @@ static struct config_bool ConfigureNamesBool[] = NULL }, &XLogArchiveMode, - false, NULL, NULL + false, + NULL, NULL, NULL }, { @@ -1285,7 +1371,8 @@ static struct config_bool ConfigureNamesBool[] = NULL }, &EnableHotStandby, - false, NULL, NULL + false, + NULL, NULL, NULL }, { @@ -1294,7 +1381,8 @@ static struct config_bool ConfigureNamesBool[] = NULL }, &hot_standby_feedback, - false, NULL, NULL + false, + NULL, NULL, NULL }, { @@ -1304,7 +1392,8 @@ static struct config_bool ConfigureNamesBool[] = GUC_NOT_IN_SAMPLE }, &allowSystemTableMods, - false, NULL, NULL + false, + NULL, NULL, NULL }, { @@ -1315,7 +1404,8 @@ static struct config_bool ConfigureNamesBool[] = GUC_NOT_IN_SAMPLE }, &IgnoreSystemIndexes, - false, NULL, NULL + false, + NULL, NULL, NULL }, { @@ -1325,7 +1415,8 @@ static struct config_bool ConfigureNamesBool[] = "for compatibility with PostgreSQL releases prior to 9.0.") }, &lo_compat_privileges, - false, NULL, NULL + false, + NULL, NULL, NULL }, { @@ -1334,12 +1425,13 @@ static struct config_bool ConfigureNamesBool[] = NULL, }, "e_all_identifiers, - false, NULL, NULL + false, + NULL, NULL, NULL }, /* End-of-list marker */ { - {NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL + {NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL, NULL } }; @@ -1354,7 +1446,8 @@ static struct config_int ConfigureNamesInt[] = GUC_UNIT_S }, &XLogArchiveTimeout, - 0, 0, INT_MAX, NULL, NULL + 0, 0, INT_MAX, + NULL, NULL, NULL }, { {"post_auth_delay", PGC_BACKEND, DEVELOPER_OPTIONS, @@ -1363,7 +1456,8 @@ static struct config_int ConfigureNamesInt[] = GUC_NOT_IN_SAMPLE | GUC_UNIT_S }, &PostAuthDelay, - 0, 0, INT_MAX, NULL, NULL + 0, 0, INT_MAX, + NULL, NULL, NULL }, { {"default_statistics_target", PGC_USERSET, QUERY_TUNING_OTHER, @@ -1372,7 +1466,8 @@ static struct config_int ConfigureNamesInt[] = "column-specific target set via ALTER TABLE SET STATISTICS.") }, &default_statistics_target, - 100, 1, 10000, NULL, NULL + 100, 1, 10000, + NULL, NULL, NULL }, { {"from_collapse_limit", PGC_USERSET, QUERY_TUNING_OTHER, @@ -1383,7 +1478,8 @@ static struct config_int ConfigureNamesInt[] = "this many items.") }, &from_collapse_limit, - 8, 1, INT_MAX, NULL, NULL + 8, 1, INT_MAX, + NULL, NULL, NULL }, { {"join_collapse_limit", PGC_USERSET, QUERY_TUNING_OTHER, @@ -1394,7 +1490,8 @@ static struct config_int ConfigureNamesInt[] = "list of no more than this many items would result.") }, &join_collapse_limit, - 8, 1, INT_MAX, NULL, NULL + 8, 1, INT_MAX, + NULL, NULL, NULL }, { {"geqo_threshold", PGC_USERSET, QUERY_TUNING_GEQO, @@ -1402,7 +1499,8 @@ static struct config_int ConfigureNamesInt[] = NULL }, &geqo_threshold, - 12, 2, INT_MAX, NULL, NULL + 12, 2, INT_MAX, + NULL, NULL, NULL }, { {"geqo_effort", PGC_USERSET, QUERY_TUNING_GEQO, @@ -1410,7 +1508,8 @@ static struct config_int ConfigureNamesInt[] = NULL }, &Geqo_effort, - DEFAULT_GEQO_EFFORT, MIN_GEQO_EFFORT, MAX_GEQO_EFFORT, NULL, NULL + DEFAULT_GEQO_EFFORT, MIN_GEQO_EFFORT, MAX_GEQO_EFFORT, + NULL, NULL, NULL }, { {"geqo_pool_size", PGC_USERSET, QUERY_TUNING_GEQO, @@ -1418,7 +1517,8 @@ static struct config_int ConfigureNamesInt[] = gettext_noop("Zero selects a suitable default value.") }, &Geqo_pool_size, - 0, 0, INT_MAX, NULL, NULL + 0, 0, INT_MAX, + NULL, NULL, NULL }, { {"geqo_generations", PGC_USERSET, QUERY_TUNING_GEQO, @@ -1426,7 +1526,8 @@ static struct config_int ConfigureNamesInt[] = gettext_noop("Zero selects a suitable default value.") }, &Geqo_generations, - 0, 0, INT_MAX, NULL, NULL + 0, 0, INT_MAX, + NULL, NULL, NULL }, { @@ -1437,7 +1538,8 @@ static struct config_int ConfigureNamesInt[] = GUC_UNIT_MS }, &DeadlockTimeout, - 1000, 1, INT_MAX, NULL, NULL + 1000, 1, INT_MAX, + NULL, NULL, NULL }, { @@ -1447,7 +1549,8 @@ static struct config_int ConfigureNamesInt[] = GUC_UNIT_MS }, &max_standby_archive_delay, - 30 * 1000, -1, INT_MAX, NULL, NULL + 30 * 1000, -1, INT_MAX, + NULL, NULL, NULL }, { @@ -1457,7 +1560,8 @@ static struct config_int ConfigureNamesInt[] = GUC_UNIT_MS }, &max_standby_streaming_delay, - 30 * 1000, -1, INT_MAX, NULL, NULL + 30 * 1000, -1, INT_MAX, + NULL, NULL, NULL }, { @@ -1467,7 +1571,8 @@ static struct config_int ConfigureNamesInt[] = GUC_UNIT_S }, &wal_receiver_status_interval, - 10, 0, INT_MAX/1000, NULL, NULL + 10, 0, INT_MAX/1000, + NULL, NULL, NULL }, { @@ -1476,7 +1581,8 @@ static struct config_int ConfigureNamesInt[] = NULL }, &MaxConnections, - 100, 1, MAX_BACKENDS, assign_maxconnections, NULL + 100, 1, MAX_BACKENDS, + check_maxconnections, assign_maxconnections, NULL }, { @@ -1485,7 +1591,8 @@ static struct config_int ConfigureNamesInt[] = NULL }, &ReservedBackends, - 3, 0, MAX_BACKENDS, NULL, NULL + 3, 0, MAX_BACKENDS, + NULL, NULL, NULL }, /* @@ -1499,7 +1606,8 @@ static struct config_int ConfigureNamesInt[] = GUC_UNIT_BLOCKS }, &NBuffers, - 1024, 16, INT_MAX / 2, NULL, NULL + 1024, 16, INT_MAX / 2, + NULL, NULL, NULL }, { @@ -1509,7 +1617,8 @@ static struct config_int ConfigureNamesInt[] = GUC_UNIT_BLOCKS }, &num_temp_buffers, - 1024, 100, INT_MAX / 2, NULL, show_num_temp_buffers + 1024, 100, INT_MAX / 2, + check_temp_buffers, NULL, NULL }, { @@ -1518,7 +1627,8 @@ static struct config_int ConfigureNamesInt[] = NULL }, &PostPortNumber, - DEF_PGPORT, 1, 65535, NULL, NULL + DEF_PGPORT, 1, 65535, + NULL, NULL, NULL }, { @@ -1532,7 +1642,8 @@ static struct config_int ConfigureNamesInt[] = "start with a 0 (zero).)") }, &Unix_socket_permissions, - 0777, 0000, 0777, NULL, show_unix_socket_permissions + 0777, 0000, 0777, + NULL, NULL, show_unix_socket_permissions }, { @@ -1545,7 +1656,8 @@ static struct config_int ConfigureNamesInt[] = "start with a 0 (zero).)") }, &Log_file_mode, - 0600, 0000, 0777, NULL, show_log_file_mode + 0600, 0000, 0777, + NULL, NULL, show_log_file_mode }, { @@ -1557,7 +1669,8 @@ static struct config_int ConfigureNamesInt[] = GUC_UNIT_KB }, &work_mem, - 1024, 64, MAX_KILOBYTES, NULL, NULL + 1024, 64, MAX_KILOBYTES, + NULL, NULL, NULL }, { @@ -1567,7 +1680,8 @@ static struct config_int ConfigureNamesInt[] = GUC_UNIT_KB }, &maintenance_work_mem, - 16384, 1024, MAX_KILOBYTES, NULL, NULL + 16384, 1024, MAX_KILOBYTES, + NULL, NULL, NULL }, /* @@ -1582,7 +1696,8 @@ static struct config_int ConfigureNamesInt[] = GUC_UNIT_KB }, &max_stack_depth, - 100, 100, MAX_KILOBYTES, assign_max_stack_depth, NULL + 100, 100, MAX_KILOBYTES, + check_max_stack_depth, assign_max_stack_depth, NULL }, { @@ -1591,7 +1706,8 @@ static struct config_int ConfigureNamesInt[] = NULL }, &VacuumCostPageHit, - 1, 0, 10000, NULL, NULL + 1, 0, 10000, + NULL, NULL, NULL }, { @@ -1600,7 +1716,8 @@ static struct config_int ConfigureNamesInt[] = NULL }, &VacuumCostPageMiss, - 10, 0, 10000, NULL, NULL + 10, 0, 10000, + NULL, NULL, NULL }, { @@ -1609,7 +1726,8 @@ static struct config_int ConfigureNamesInt[] = NULL }, &VacuumCostPageDirty, - 20, 0, 10000, NULL, NULL + 20, 0, 10000, + NULL, NULL, NULL }, { @@ -1618,7 +1736,8 @@ static struct config_int ConfigureNamesInt[] = NULL }, &VacuumCostLimit, - 200, 1, 10000, NULL, NULL + 200, 1, 10000, + NULL, NULL, NULL }, { @@ -1628,7 +1747,8 @@ static struct config_int ConfigureNamesInt[] = GUC_UNIT_MS }, &VacuumCostDelay, - 0, 0, 100, NULL, NULL + 0, 0, 100, + NULL, NULL, NULL }, { @@ -1638,7 +1758,8 @@ static struct config_int ConfigureNamesInt[] = GUC_UNIT_MS }, &autovacuum_vac_cost_delay, - 20, -1, 100, NULL, NULL + 20, -1, 100, + NULL, NULL, NULL }, { @@ -1647,7 +1768,8 @@ static struct config_int ConfigureNamesInt[] = NULL }, &autovacuum_vac_cost_limit, - -1, -1, 10000, NULL, NULL + -1, -1, 10000, + NULL, NULL, NULL }, { @@ -1656,7 +1778,8 @@ static struct config_int ConfigureNamesInt[] = NULL }, &max_files_per_process, - 1000, 25, INT_MAX, NULL, NULL + 1000, 25, INT_MAX, + NULL, NULL, NULL }, /* @@ -1668,7 +1791,8 @@ static struct config_int ConfigureNamesInt[] = NULL }, &max_prepared_xacts, - 0, 0, MAX_BACKENDS, NULL, NULL + 0, 0, MAX_BACKENDS, + NULL, NULL, NULL }, #ifdef LOCK_DEBUG @@ -1679,7 +1803,8 @@ static struct config_int ConfigureNamesInt[] = GUC_NOT_IN_SAMPLE }, &Trace_lock_oidmin, - FirstNormalObjectId, 0, INT_MAX, NULL, NULL + FirstNormalObjectId, 0, INT_MAX, + NULL, NULL, NULL }, { {"trace_lock_table", PGC_SUSET, DEVELOPER_OPTIONS, @@ -1688,7 +1813,8 @@ static struct config_int ConfigureNamesInt[] = GUC_NOT_IN_SAMPLE }, &Trace_lock_table, - 0, 0, INT_MAX, NULL, NULL + 0, 0, INT_MAX, + NULL, NULL, NULL }, #endif @@ -1699,7 +1825,8 @@ static struct config_int ConfigureNamesInt[] = GUC_UNIT_MS }, &StatementTimeout, - 0, 0, INT_MAX, NULL, NULL + 0, 0, INT_MAX, + NULL, NULL, NULL }, { @@ -1708,7 +1835,8 @@ static struct config_int ConfigureNamesInt[] = NULL }, &vacuum_freeze_min_age, - 50000000, 0, 1000000000, NULL, NULL + 50000000, 0, 1000000000, + NULL, NULL, NULL }, { @@ -1717,7 +1845,8 @@ static struct config_int ConfigureNamesInt[] = NULL }, &vacuum_freeze_table_age, - 150000000, 0, 2000000000, NULL, NULL + 150000000, 0, 2000000000, + NULL, NULL, NULL }, { @@ -1726,7 +1855,8 @@ static struct config_int ConfigureNamesInt[] = NULL }, &vacuum_defer_cleanup_age, - 0, 0, 1000000, NULL, NULL + 0, 0, 1000000, + NULL, NULL, NULL }, /* @@ -1740,7 +1870,8 @@ static struct config_int ConfigureNamesInt[] = "objects will need to be locked at any one time.") }, &max_locks_per_xact, - 64, 10, INT_MAX, NULL, NULL + 64, 10, INT_MAX, + NULL, NULL, NULL }, { @@ -1751,7 +1882,8 @@ static struct config_int ConfigureNamesInt[] = "objects will need to be locked at any one time.") }, &max_predicate_locks_per_xact, - 64, 10, INT_MAX, NULL, NULL + 64, 10, INT_MAX, + NULL, NULL, NULL }, { @@ -1761,7 +1893,8 @@ static struct config_int ConfigureNamesInt[] = GUC_UNIT_S }, &AuthenticationTimeout, - 60, 1, 600, NULL, NULL + 60, 1, 600, + NULL, NULL, NULL }, { @@ -1772,7 +1905,8 @@ static struct config_int ConfigureNamesInt[] = GUC_NOT_IN_SAMPLE | GUC_UNIT_S }, &PreAuthDelay, - 0, 0, 60, NULL, NULL + 0, 0, 60, + NULL, NULL, NULL }, { @@ -1781,7 +1915,8 @@ static struct config_int ConfigureNamesInt[] = NULL }, &wal_keep_segments, - 0, 0, INT_MAX, NULL, NULL + 0, 0, INT_MAX, + NULL, NULL, NULL }, { @@ -1790,7 +1925,8 @@ static struct config_int ConfigureNamesInt[] = NULL }, &CheckPointSegments, - 3, 1, INT_MAX, NULL, NULL + 3, 1, INT_MAX, + NULL, NULL, NULL }, { @@ -1800,7 +1936,8 @@ static struct config_int ConfigureNamesInt[] = GUC_UNIT_S }, &CheckPointTimeout, - 300, 30, 3600, NULL, NULL + 300, 30, 3600, + NULL, NULL, NULL }, { @@ -1813,7 +1950,8 @@ static struct config_int ConfigureNamesInt[] = GUC_UNIT_S }, &CheckPointWarning, - 30, 0, INT_MAX, NULL, NULL + 30, 0, INT_MAX, + NULL, NULL, NULL }, { @@ -1823,7 +1961,8 @@ static struct config_int ConfigureNamesInt[] = GUC_UNIT_XBLOCKS }, &XLOGbuffers, - -1, -1, INT_MAX, NULL, NULL + -1, -1, INT_MAX, + check_wal_buffers, NULL, NULL }, { @@ -1833,7 +1972,8 @@ static struct config_int ConfigureNamesInt[] = GUC_UNIT_MS }, &WalWriterDelay, - 200, 1, 10000, NULL, NULL + 200, 1, 10000, + NULL, NULL, NULL }, { @@ -1843,7 +1983,8 @@ static struct config_int ConfigureNamesInt[] = NULL }, &max_wal_senders, - 0, 0, MAX_BACKENDS, NULL, NULL + 0, 0, MAX_BACKENDS, + NULL, NULL, NULL }, { @@ -1853,7 +1994,8 @@ static struct config_int ConfigureNamesInt[] = GUC_UNIT_MS }, &WalSndDelay, - 1000, 1, 10000, NULL, NULL + 1000, 1, 10000, + NULL, NULL, NULL }, { @@ -1863,7 +2005,8 @@ static struct config_int ConfigureNamesInt[] = GUC_UNIT_MS }, &replication_timeout, - 60 * 1000, 0, INT_MAX, NULL, NULL + 60 * 1000, 0, INT_MAX, + NULL, NULL, NULL }, { @@ -1873,7 +2016,8 @@ static struct config_int ConfigureNamesInt[] = NULL }, &CommitDelay, - 0, 0, 100000, NULL, NULL + 0, 0, 100000, + NULL, NULL, NULL }, { @@ -1883,7 +2027,8 @@ static struct config_int ConfigureNamesInt[] = NULL }, &CommitSiblings, - 5, 0, 1000, NULL, NULL + 5, 0, 1000, + NULL, NULL, NULL }, { @@ -1894,7 +2039,8 @@ static struct config_int ConfigureNamesInt[] = "(FLT_DIG or DBL_DIG as appropriate).") }, &extra_float_digits, - 0, -15, 3, NULL, NULL + 0, -15, 3, + NULL, NULL, NULL }, { @@ -1905,7 +2051,8 @@ static struct config_int ConfigureNamesInt[] = GUC_UNIT_MS }, &log_min_duration_statement, - -1, -1, INT_MAX, NULL, NULL + -1, -1, INT_MAX, + NULL, NULL, NULL }, { @@ -1916,7 +2063,8 @@ static struct config_int ConfigureNamesInt[] = GUC_UNIT_MS }, &Log_autovacuum_min_duration, - -1, -1, INT_MAX, NULL, NULL + -1, -1, INT_MAX, + NULL, NULL, NULL }, { @@ -1926,7 +2074,8 @@ static struct config_int ConfigureNamesInt[] = GUC_UNIT_MS }, &BgWriterDelay, - 200, 10, 10000, NULL, NULL + 200, 10, 10000, + NULL, NULL, NULL }, { @@ -1935,7 +2084,8 @@ static struct config_int ConfigureNamesInt[] = NULL }, &bgwriter_lru_maxpages, - 100, 0, 1000, NULL, NULL + 100, 0, 1000, + NULL, NULL, NULL }, { @@ -1955,7 +2105,7 @@ static struct config_int ConfigureNamesInt[] = #else 0, 0, 0, #endif - assign_effective_io_concurrency, NULL + check_effective_io_concurrency, assign_effective_io_concurrency, NULL }, { @@ -1965,7 +2115,8 @@ static struct config_int ConfigureNamesInt[] = GUC_UNIT_MIN }, &Log_RotationAge, - HOURS_PER_DAY * MINS_PER_HOUR, 0, INT_MAX / MINS_PER_HOUR, NULL, NULL + HOURS_PER_DAY * MINS_PER_HOUR, 0, INT_MAX / MINS_PER_HOUR, + NULL, NULL, NULL }, { @@ -1975,7 +2126,8 @@ static struct config_int ConfigureNamesInt[] = GUC_UNIT_KB }, &Log_RotationSize, - 10 * 1024, 0, INT_MAX / 1024, NULL, NULL + 10 * 1024, 0, INT_MAX / 1024, + NULL, NULL, NULL }, { @@ -1985,7 +2137,8 @@ static struct config_int ConfigureNamesInt[] = GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE }, &max_function_args, - FUNC_MAX_ARGS, FUNC_MAX_ARGS, FUNC_MAX_ARGS, NULL, NULL + FUNC_MAX_ARGS, FUNC_MAX_ARGS, FUNC_MAX_ARGS, + NULL, NULL, NULL }, { @@ -1995,7 +2148,8 @@ static struct config_int ConfigureNamesInt[] = GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE }, &max_index_keys, - INDEX_MAX_KEYS, INDEX_MAX_KEYS, INDEX_MAX_KEYS, NULL, NULL + INDEX_MAX_KEYS, INDEX_MAX_KEYS, INDEX_MAX_KEYS, + NULL, NULL, NULL }, { @@ -2005,7 +2159,8 @@ static struct config_int ConfigureNamesInt[] = GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE }, &max_identifier_length, - NAMEDATALEN - 1, NAMEDATALEN - 1, NAMEDATALEN - 1, NULL, NULL + NAMEDATALEN - 1, NAMEDATALEN - 1, NAMEDATALEN - 1, + NULL, NULL, NULL }, { @@ -2015,7 +2170,8 @@ static struct config_int ConfigureNamesInt[] = GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE }, &block_size, - BLCKSZ, BLCKSZ, BLCKSZ, NULL, NULL + BLCKSZ, BLCKSZ, BLCKSZ, + NULL, NULL, NULL }, { @@ -2025,7 +2181,8 @@ static struct config_int ConfigureNamesInt[] = GUC_UNIT_BLOCKS | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE }, &segment_size, - RELSEG_SIZE, RELSEG_SIZE, RELSEG_SIZE, NULL, NULL + RELSEG_SIZE, RELSEG_SIZE, RELSEG_SIZE, + NULL, NULL, NULL }, { @@ -2035,7 +2192,8 @@ static struct config_int ConfigureNamesInt[] = GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE }, &wal_block_size, - XLOG_BLCKSZ, XLOG_BLCKSZ, XLOG_BLCKSZ, NULL, NULL + XLOG_BLCKSZ, XLOG_BLCKSZ, XLOG_BLCKSZ, + NULL, NULL, NULL }, { @@ -2048,7 +2206,7 @@ static struct config_int ConfigureNamesInt[] = (XLOG_SEG_SIZE / XLOG_BLCKSZ), (XLOG_SEG_SIZE / XLOG_BLCKSZ), (XLOG_SEG_SIZE / XLOG_BLCKSZ), - NULL, NULL + NULL, NULL, NULL }, { @@ -2058,7 +2216,8 @@ static struct config_int ConfigureNamesInt[] = GUC_UNIT_S }, &autovacuum_naptime, - 60, 1, INT_MAX / 1000, NULL, NULL + 60, 1, INT_MAX / 1000, + NULL, NULL, NULL }, { {"autovacuum_vacuum_threshold", PGC_SIGHUP, AUTOVACUUM, @@ -2066,7 +2225,8 @@ static struct config_int ConfigureNamesInt[] = NULL }, &autovacuum_vac_thresh, - 50, 0, INT_MAX, NULL, NULL + 50, 0, INT_MAX, + NULL, NULL, NULL }, { {"autovacuum_analyze_threshold", PGC_SIGHUP, AUTOVACUUM, @@ -2074,7 +2234,8 @@ static struct config_int ConfigureNamesInt[] = NULL }, &autovacuum_anl_thresh, - 50, 0, INT_MAX, NULL, NULL + 50, 0, INT_MAX, + NULL, NULL, NULL }, { /* see varsup.c for why this is PGC_POSTMASTER not PGC_SIGHUP */ @@ -2084,7 +2245,8 @@ static struct config_int ConfigureNamesInt[] = }, &autovacuum_freeze_max_age, /* see pg_resetxlog if you change the upper-limit value */ - 200000000, 100000000, 2000000000, NULL, NULL + 200000000, 100000000, 2000000000, + NULL, NULL, NULL }, { /* see max_connections */ @@ -2093,7 +2255,8 @@ static struct config_int ConfigureNamesInt[] = NULL }, &autovacuum_max_workers, - 3, 1, MAX_BACKENDS, assign_autovacuum_max_workers, NULL + 3, 1, MAX_BACKENDS, + check_autovacuum_max_workers, assign_autovacuum_max_workers, NULL }, { @@ -2103,7 +2266,8 @@ static struct config_int ConfigureNamesInt[] = GUC_UNIT_S }, &tcp_keepalives_idle, - 0, 0, INT_MAX, assign_tcp_keepalives_idle, show_tcp_keepalives_idle + 0, 0, INT_MAX, + NULL, assign_tcp_keepalives_idle, show_tcp_keepalives_idle }, { @@ -2113,7 +2277,8 @@ static struct config_int ConfigureNamesInt[] = GUC_UNIT_S }, &tcp_keepalives_interval, - 0, 0, INT_MAX, assign_tcp_keepalives_interval, show_tcp_keepalives_interval + 0, 0, INT_MAX, + NULL, assign_tcp_keepalives_interval, show_tcp_keepalives_interval }, { @@ -2123,7 +2288,8 @@ static struct config_int ConfigureNamesInt[] = GUC_UNIT_KB, }, &ssl_renegotiation_limit, - 512 * 1024, 0, MAX_KILOBYTES, NULL, NULL + 512 * 1024, 0, MAX_KILOBYTES, + NULL, NULL, NULL }, { @@ -2134,7 +2300,8 @@ static struct config_int ConfigureNamesInt[] = "system default."), }, &tcp_keepalives_count, - 0, 0, INT_MAX, assign_tcp_keepalives_count, show_tcp_keepalives_count + 0, 0, INT_MAX, + NULL, assign_tcp_keepalives_count, show_tcp_keepalives_count }, { @@ -2144,7 +2311,8 @@ static struct config_int ConfigureNamesInt[] = 0 }, &GinFuzzySearchLimit, - 0, 0, INT_MAX, NULL, NULL + 0, 0, INT_MAX, + NULL, NULL, NULL }, { @@ -2156,7 +2324,8 @@ static struct config_int ConfigureNamesInt[] = GUC_UNIT_BLOCKS, }, &effective_cache_size, - DEFAULT_EFFECTIVE_CACHE_SIZE, 1, INT_MAX, NULL, NULL + DEFAULT_EFFECTIVE_CACHE_SIZE, 1, INT_MAX, + NULL, NULL, NULL }, { @@ -2167,7 +2336,8 @@ static struct config_int ConfigureNamesInt[] = GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE }, &server_version_num, - PG_VERSION_NUM, PG_VERSION_NUM, PG_VERSION_NUM, NULL, NULL + PG_VERSION_NUM, PG_VERSION_NUM, PG_VERSION_NUM, + NULL, NULL, NULL }, { @@ -2177,7 +2347,8 @@ static struct config_int ConfigureNamesInt[] = GUC_UNIT_KB }, &log_temp_files, - -1, -1, INT_MAX, NULL, NULL + -1, -1, INT_MAX, + NULL, NULL, NULL }, { @@ -2186,12 +2357,13 @@ static struct config_int ConfigureNamesInt[] = NULL, }, &pgstat_track_activity_query_size, - 1024, 100, 102400, NULL, NULL + 1024, 100, 102400, + NULL, NULL, NULL }, /* End-of-list marker */ { - {NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL + {NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL } }; @@ -2205,7 +2377,8 @@ static struct config_real ConfigureNamesReal[] = NULL }, &seq_page_cost, - DEFAULT_SEQ_PAGE_COST, 0, DBL_MAX, NULL, NULL + DEFAULT_SEQ_PAGE_COST, 0, DBL_MAX, + NULL, NULL, NULL }, { {"random_page_cost", PGC_USERSET, QUERY_TUNING_COST, @@ -2214,7 +2387,8 @@ static struct config_real ConfigureNamesReal[] = NULL }, &random_page_cost, - DEFAULT_RANDOM_PAGE_COST, 0, DBL_MAX, NULL, NULL + DEFAULT_RANDOM_PAGE_COST, 0, DBL_MAX, + NULL, NULL, NULL }, { {"cpu_tuple_cost", PGC_USERSET, QUERY_TUNING_COST, @@ -2223,7 +2397,8 @@ static struct config_real ConfigureNamesReal[] = NULL }, &cpu_tuple_cost, - DEFAULT_CPU_TUPLE_COST, 0, DBL_MAX, NULL, NULL + DEFAULT_CPU_TUPLE_COST, 0, DBL_MAX, + NULL, NULL, NULL }, { {"cpu_index_tuple_cost", PGC_USERSET, QUERY_TUNING_COST, @@ -2232,7 +2407,8 @@ static struct config_real ConfigureNamesReal[] = NULL }, &cpu_index_tuple_cost, - DEFAULT_CPU_INDEX_TUPLE_COST, 0, DBL_MAX, NULL, NULL + DEFAULT_CPU_INDEX_TUPLE_COST, 0, DBL_MAX, + NULL, NULL, NULL }, { {"cpu_operator_cost", PGC_USERSET, QUERY_TUNING_COST, @@ -2241,7 +2417,8 @@ static struct config_real ConfigureNamesReal[] = NULL }, &cpu_operator_cost, - DEFAULT_CPU_OPERATOR_COST, 0, DBL_MAX, NULL, NULL + DEFAULT_CPU_OPERATOR_COST, 0, DBL_MAX, + NULL, NULL, NULL }, { @@ -2251,7 +2428,8 @@ static struct config_real ConfigureNamesReal[] = NULL }, &cursor_tuple_fraction, - DEFAULT_CURSOR_TUPLE_FRACTION, 0.0, 1.0, NULL, NULL + DEFAULT_CURSOR_TUPLE_FRACTION, 0.0, 1.0, + NULL, NULL, NULL }, { @@ -2260,8 +2438,9 @@ static struct config_real ConfigureNamesReal[] = NULL }, &Geqo_selection_bias, - DEFAULT_GEQO_SELECTION_BIAS, MIN_GEQO_SELECTION_BIAS, - MAX_GEQO_SELECTION_BIAS, NULL, NULL + DEFAULT_GEQO_SELECTION_BIAS, + MIN_GEQO_SELECTION_BIAS, MAX_GEQO_SELECTION_BIAS, + NULL, NULL, NULL }, { {"geqo_seed", PGC_USERSET, QUERY_TUNING_GEQO, @@ -2269,7 +2448,8 @@ static struct config_real ConfigureNamesReal[] = NULL }, &Geqo_seed, - 0.0, 0.0, 1.0, NULL, NULL + 0.0, 0.0, 1.0, + NULL, NULL, NULL }, { @@ -2278,7 +2458,8 @@ static struct config_real ConfigureNamesReal[] = NULL }, &bgwriter_lru_multiplier, - 2.0, 0.0, 10.0, NULL, NULL + 2.0, 0.0, 10.0, + NULL, NULL, NULL }, { @@ -2288,7 +2469,8 @@ static struct config_real ConfigureNamesReal[] = GUC_NO_SHOW_ALL | GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE }, &phony_random_seed, - 0.0, -1.0, 1.0, assign_random_seed, show_random_seed + 0.0, -1.0, 1.0, + check_random_seed, assign_random_seed, show_random_seed }, { @@ -2297,7 +2479,8 @@ static struct config_real ConfigureNamesReal[] = NULL }, &autovacuum_vac_scale, - 0.2, 0.0, 100.0, NULL, NULL + 0.2, 0.0, 100.0, + NULL, NULL, NULL }, { {"autovacuum_analyze_scale_factor", PGC_SIGHUP, AUTOVACUUM, @@ -2305,7 +2488,8 @@ static struct config_real ConfigureNamesReal[] = NULL }, &autovacuum_anl_scale, - 0.1, 0.0, 100.0, NULL, NULL + 0.1, 0.0, 100.0, + NULL, NULL, NULL }, { @@ -2314,12 +2498,13 @@ static struct config_real ConfigureNamesReal[] = NULL }, &CheckPointCompletionTarget, - 0.5, 0.0, 1.0, NULL, NULL + 0.5, 0.0, 1.0, + NULL, NULL, NULL }, /* End-of-list marker */ { - {NULL, 0, 0, NULL, NULL}, NULL, 0.0, 0.0, 0.0, NULL, NULL + {NULL, 0, 0, NULL, NULL}, NULL, 0.0, 0.0, 0.0, NULL, NULL, NULL } }; @@ -2332,7 +2517,8 @@ static struct config_string ConfigureNamesString[] = NULL }, &XLogArchiveCommand, - "", NULL, show_archive_command + "", + NULL, NULL, show_archive_command }, { @@ -2342,7 +2528,8 @@ static struct config_string ConfigureNamesString[] = GUC_IS_NAME | GUC_REPORT }, &client_encoding_string, - "SQL_ASCII", assign_client_encoding, NULL + "SQL_ASCII", + check_client_encoding, assign_client_encoding, NULL }, { @@ -2351,7 +2538,8 @@ static struct config_string ConfigureNamesString[] = gettext_noop("If blank, no prefix is used.") }, &Log_line_prefix, - "", NULL, NULL + "", + NULL, NULL, NULL }, { @@ -2360,7 +2548,8 @@ static struct config_string ConfigureNamesString[] = NULL }, &log_timezone_string, - "UNKNOWN", assign_log_timezone, show_log_timezone + NULL, + check_log_timezone, assign_log_timezone, show_log_timezone }, { @@ -2371,7 +2560,8 @@ static struct config_string ConfigureNamesString[] = GUC_LIST_INPUT | GUC_REPORT }, &datestyle_string, - "ISO, MDY", assign_datestyle, NULL + "ISO, MDY", + check_datestyle, assign_datestyle, NULL }, { @@ -2381,7 +2571,8 @@ static struct config_string ConfigureNamesString[] = GUC_IS_NAME }, &default_tablespace, - "", assign_default_tablespace, NULL + "", + check_default_tablespace, NULL, NULL }, { @@ -2391,7 +2582,8 @@ static struct config_string ConfigureNamesString[] = GUC_LIST_INPUT | GUC_LIST_QUOTE }, &temp_tablespaces, - "", assign_temp_tablespaces, NULL + "", + check_temp_tablespaces, assign_temp_tablespaces, NULL }, { @@ -2404,7 +2596,8 @@ static struct config_string ConfigureNamesString[] = GUC_SUPERUSER_ONLY }, &Dynamic_library_path, - "$libdir", NULL, NULL + "$libdir", + NULL, NULL, NULL }, { @@ -2414,7 +2607,8 @@ static struct config_string ConfigureNamesString[] = GUC_SUPERUSER_ONLY }, &pg_krb_server_keyfile, - PG_KRB_SRVTAB, NULL, NULL + PG_KRB_SRVTAB, + NULL, NULL, NULL }, { @@ -2423,7 +2617,8 @@ static struct config_string ConfigureNamesString[] = NULL }, &pg_krb_srvnam, - PG_KRB_SRVNAM, NULL, NULL + PG_KRB_SRVNAM, + NULL, NULL, NULL }, { @@ -2432,7 +2627,8 @@ static struct config_string ConfigureNamesString[] = NULL }, &bonjour_name, - "", NULL, NULL + "", + NULL, NULL, NULL }, /* See main.c about why defaults for LC_foo are not all alike */ @@ -2444,7 +2640,8 @@ static struct config_string ConfigureNamesString[] = GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE }, &locale_collate, - "C", NULL, NULL + "C", + NULL, NULL, NULL }, { @@ -2454,7 +2651,8 @@ static struct config_string ConfigureNamesString[] = GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE }, &locale_ctype, - "C", NULL, NULL + "C", + NULL, NULL, NULL }, { @@ -2463,7 +2661,8 @@ static struct config_string ConfigureNamesString[] = NULL }, &locale_messages, - "", locale_messages_assign, NULL + "", + check_locale_messages, assign_locale_messages, NULL }, { @@ -2472,7 +2671,8 @@ static struct config_string ConfigureNamesString[] = NULL }, &locale_monetary, - "C", locale_monetary_assign, NULL + "C", + check_locale_monetary, assign_locale_monetary, NULL }, { @@ -2481,7 +2681,8 @@ static struct config_string ConfigureNamesString[] = NULL }, &locale_numeric, - "C", locale_numeric_assign, NULL + "C", + check_locale_numeric, assign_locale_numeric, NULL }, { @@ -2490,7 +2691,8 @@ static struct config_string ConfigureNamesString[] = NULL }, &locale_time, - "C", locale_time_assign, NULL + "C", + check_locale_time, assign_locale_time, NULL }, { @@ -2500,7 +2702,8 @@ static struct config_string ConfigureNamesString[] = GUC_LIST_INPUT | GUC_LIST_QUOTE | GUC_SUPERUSER_ONLY }, &shared_preload_libraries_string, - "", NULL, NULL + "", + NULL, NULL, NULL }, { @@ -2510,7 +2713,8 @@ static struct config_string ConfigureNamesString[] = GUC_LIST_INPUT | GUC_LIST_QUOTE }, &local_preload_libraries_string, - "", NULL, NULL + "", + NULL, NULL, NULL }, { @@ -2520,7 +2724,8 @@ static struct config_string ConfigureNamesString[] = GUC_LIST_INPUT | GUC_LIST_QUOTE }, &namespace_search_path, - "\"$user\",public", assign_search_path, NULL + "\"$user\",public", + check_search_path, assign_search_path, NULL }, { @@ -2531,7 +2736,8 @@ static struct config_string ConfigureNamesString[] = GUC_IS_NAME | GUC_REPORT | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE }, &server_encoding_string, - "SQL_ASCII", NULL, NULL + "SQL_ASCII", + NULL, NULL, NULL }, { @@ -2542,7 +2748,8 @@ static struct config_string ConfigureNamesString[] = GUC_REPORT | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE }, &server_version_string, - PG_VERSION, NULL, NULL + PG_VERSION, + NULL, NULL, NULL }, { @@ -2553,7 +2760,8 @@ static struct config_string ConfigureNamesString[] = GUC_IS_NAME | GUC_NO_SHOW_ALL | GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE | GUC_NOT_WHILE_SEC_REST }, &role_string, - "none", assign_role, show_role + "none", + check_role, assign_role, show_role }, { @@ -2564,7 +2772,8 @@ static struct config_string ConfigureNamesString[] = GUC_IS_NAME | GUC_REPORT | GUC_NO_SHOW_ALL | GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE | GUC_NOT_WHILE_SEC_REST }, &session_authorization_string, - NULL, assign_session_authorization, show_session_authorization + NULL, + check_session_authorization, assign_session_authorization, NULL }, { @@ -2576,7 +2785,8 @@ static struct config_string ConfigureNamesString[] = GUC_LIST_INPUT }, &log_destination_string, - "stderr", assign_log_destination, NULL + "stderr", + check_log_destination, assign_log_destination, NULL }, { {"log_directory", PGC_SIGHUP, LOGGING_WHERE, @@ -2586,7 +2796,8 @@ static struct config_string ConfigureNamesString[] = GUC_SUPERUSER_ONLY }, &Log_directory, - "pg_log", assign_canonical_path, NULL + "pg_log", + check_canonical_path, NULL, NULL }, { {"log_filename", PGC_SIGHUP, LOGGING_WHERE, @@ -2595,7 +2806,8 @@ static struct config_string ConfigureNamesString[] = GUC_SUPERUSER_ONLY }, &Log_filename, - "postgresql-%Y-%m-%d_%H%M%S.log", NULL, NULL + "postgresql-%Y-%m-%d_%H%M%S.log", + NULL, NULL, NULL }, { @@ -2605,7 +2817,8 @@ static struct config_string ConfigureNamesString[] = NULL }, &syslog_ident_str, - "postgres", assign_syslog_ident, NULL + "postgres", + NULL, assign_syslog_ident, NULL }, { @@ -2615,7 +2828,8 @@ static struct config_string ConfigureNamesString[] = GUC_REPORT }, &timezone_string, - "UNKNOWN", assign_timezone, show_timezone + NULL, + check_timezone, assign_timezone, show_timezone }, { {"timezone_abbreviations", PGC_USERSET, CLIENT_CONN_LOCALE, @@ -2623,7 +2837,8 @@ static struct config_string ConfigureNamesString[] = NULL }, &timezone_abbreviations_string, - "UNKNOWN", assign_timezone_abbreviations, NULL + NULL, + check_timezone_abbreviations, assign_timezone_abbreviations, NULL }, { @@ -2633,7 +2848,8 @@ static struct config_string ConfigureNamesString[] = GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE }, &XactIsoLevel_string, - NULL, assign_XactIsoLevel, show_XactIsoLevel + "default", + check_XactIsoLevel, assign_XactIsoLevel, show_XactIsoLevel }, { @@ -2643,7 +2859,8 @@ static struct config_string ConfigureNamesString[] = "that starts the server.") }, &Unix_socket_group, - "", NULL, NULL + "", + NULL, NULL, NULL }, { @@ -2653,7 +2870,8 @@ static struct config_string ConfigureNamesString[] = GUC_SUPERUSER_ONLY }, &UnixSocketDir, - "", assign_canonical_path, NULL + "", + check_canonical_path, NULL, NULL }, { @@ -2663,7 +2881,8 @@ static struct config_string ConfigureNamesString[] = GUC_LIST_INPUT }, &ListenAddresses, - "localhost", NULL, NULL + "localhost", + NULL, NULL, NULL }, { @@ -2673,7 +2892,8 @@ static struct config_string ConfigureNamesString[] = GUC_LIST_INPUT | GUC_LIST_QUOTE }, &custom_variable_classes, - NULL, assign_custom_variable_classes, NULL + NULL, + check_custom_variable_classes, NULL, NULL }, { @@ -2683,6 +2903,7 @@ static struct config_string ConfigureNamesString[] = GUC_SUPERUSER_ONLY }, &data_directory, + NULL, NULL, NULL, NULL }, @@ -2693,6 +2914,7 @@ static struct config_string ConfigureNamesString[] = GUC_DISALLOW_IN_FILE | GUC_SUPERUSER_ONLY }, &ConfigFileName, + NULL, NULL, NULL, NULL }, @@ -2703,6 +2925,7 @@ static struct config_string ConfigureNamesString[] = GUC_SUPERUSER_ONLY }, &HbaFileName, + NULL, NULL, NULL, NULL }, @@ -2713,6 +2936,7 @@ static struct config_string ConfigureNamesString[] = GUC_SUPERUSER_ONLY }, &IdentFileName, + NULL, NULL, NULL, NULL }, @@ -2723,7 +2947,8 @@ static struct config_string ConfigureNamesString[] = GUC_SUPERUSER_ONLY }, &external_pid_file, - NULL, assign_canonical_path, NULL + NULL, + check_canonical_path, NULL, NULL }, { @@ -2733,7 +2958,8 @@ static struct config_string ConfigureNamesString[] = GUC_SUPERUSER_ONLY }, &pgstat_temp_directory, - "pg_stat_tmp", assign_pgstat_temp_directory, NULL + "pg_stat_tmp", + check_canonical_path, assign_pgstat_temp_directory, NULL }, { @@ -2743,7 +2969,8 @@ static struct config_string ConfigureNamesString[] = GUC_LIST_INPUT }, &SyncRepStandbyNames, - "", assign_synchronous_standby_names, NULL + "", + check_synchronous_standby_names, NULL, NULL }, { @@ -2752,7 +2979,8 @@ static struct config_string ConfigureNamesString[] = NULL }, &TSCurrentConfig, - "pg_catalog.simple", assignTSCurrentConfig, NULL + "pg_catalog.simple", + check_TSCurrentConfig, assign_TSCurrentConfig, NULL }, { @@ -2767,7 +2995,7 @@ static struct config_string ConfigureNamesString[] = #else "none", #endif - NULL, NULL + NULL, NULL, NULL }, { @@ -2777,12 +3005,13 @@ static struct config_string ConfigureNamesString[] = GUC_IS_NAME | GUC_REPORT | GUC_NOT_IN_SAMPLE }, &application_name, - "", assign_application_name, NULL + "", + check_application_name, assign_application_name, NULL }, /* End-of-list marker */ { - {NULL, 0, 0, NULL, NULL}, NULL, NULL, NULL, NULL + {NULL, 0, 0, NULL, NULL}, NULL, NULL, NULL, NULL, NULL } }; @@ -2795,7 +3024,8 @@ static struct config_enum ConfigureNamesEnum[] = NULL }, &backslash_quote, - BACKSLASH_QUOTE_SAFE_ENCODING, backslash_quote_options, NULL, NULL + BACKSLASH_QUOTE_SAFE_ENCODING, backslash_quote_options, + NULL, NULL, NULL }, { @@ -2804,7 +3034,8 @@ static struct config_enum ConfigureNamesEnum[] = NULL }, &bytea_output, - BYTEA_OUTPUT_HEX, bytea_output_options, NULL, NULL + BYTEA_OUTPUT_HEX, bytea_output_options, + NULL, NULL, NULL }, { @@ -2814,7 +3045,8 @@ static struct config_enum ConfigureNamesEnum[] = " the level, the fewer messages are sent.") }, &client_min_messages, - NOTICE, client_message_level_options, NULL, NULL + NOTICE, client_message_level_options, + NULL, NULL, NULL }, { @@ -2825,7 +3057,7 @@ static struct config_enum ConfigureNamesEnum[] = }, &constraint_exclusion, CONSTRAINT_EXCLUSION_PARTITION, constraint_exclusion_options, - NULL, NULL + NULL, NULL, NULL }, { @@ -2834,7 +3066,8 @@ static struct config_enum ConfigureNamesEnum[] = NULL }, &DefaultXactIsoLevel, - XACT_READ_COMMITTED, isolation_level_options, NULL, NULL + XACT_READ_COMMITTED, isolation_level_options, + NULL, NULL, NULL }, { @@ -2844,7 +3077,8 @@ static struct config_enum ConfigureNamesEnum[] = GUC_REPORT }, &IntervalStyle, - INTSTYLE_POSTGRES, intervalstyle_options, NULL, NULL + INTSTYLE_POSTGRES, intervalstyle_options, + NULL, NULL, NULL }, { @@ -2853,7 +3087,8 @@ static struct config_enum ConfigureNamesEnum[] = NULL }, &Log_error_verbosity, - PGERROR_DEFAULT, log_error_verbosity_options, NULL, NULL + PGERROR_DEFAULT, log_error_verbosity_options, + NULL, NULL, NULL }, { @@ -2863,7 +3098,8 @@ static struct config_enum ConfigureNamesEnum[] = " the level, the fewer messages are sent.") }, &log_min_messages, - WARNING, server_message_level_options, NULL, NULL + WARNING, server_message_level_options, + NULL, NULL, NULL }, { @@ -2873,7 +3109,8 @@ static struct config_enum ConfigureNamesEnum[] = " the level, the fewer messages are sent.") }, &log_min_error_statement, - ERROR, server_message_level_options, NULL, NULL + ERROR, server_message_level_options, + NULL, NULL, NULL }, { @@ -2882,7 +3119,8 @@ static struct config_enum ConfigureNamesEnum[] = NULL }, &log_statement, - LOGSTMT_NONE, log_statement_options, NULL, NULL + LOGSTMT_NONE, log_statement_options, + NULL, NULL, NULL }, { @@ -2896,7 +3134,8 @@ static struct config_enum ConfigureNamesEnum[] = #else 0, #endif - syslog_facility_options, assign_syslog_facility, NULL + syslog_facility_options, + NULL, assign_syslog_facility, NULL }, { @@ -2906,7 +3145,7 @@ static struct config_enum ConfigureNamesEnum[] = }, &SessionReplicationRole, SESSION_REPLICATION_ROLE_ORIGIN, session_replication_role_options, - assign_session_replication_role, NULL + NULL, assign_session_replication_role, NULL }, { @@ -2916,7 +3155,7 @@ static struct config_enum ConfigureNamesEnum[] = }, &synchronous_commit, SYNCHRONOUS_COMMIT_ON, synchronous_commit_options, - NULL, NULL + NULL, NULL, NULL }, { @@ -2930,7 +3169,8 @@ static struct config_enum ConfigureNamesEnum[] = * client_message_level_options allows too many values, really, * but it's not worth having a separate options array for this. */ - LOG, client_message_level_options, NULL, NULL + LOG, client_message_level_options, + NULL, NULL, NULL }, { @@ -2939,7 +3179,8 @@ static struct config_enum ConfigureNamesEnum[] = NULL }, &pgstat_track_functions, - TRACK_FUNC_OFF, track_function_options, NULL, NULL + TRACK_FUNC_OFF, track_function_options, + NULL, NULL, NULL }, { @@ -2948,7 +3189,8 @@ static struct config_enum ConfigureNamesEnum[] = NULL }, &wal_level, - WAL_LEVEL_MINIMAL, wal_level_options, NULL + WAL_LEVEL_MINIMAL, wal_level_options, + NULL, NULL, NULL }, { @@ -2958,7 +3200,7 @@ static struct config_enum ConfigureNamesEnum[] = }, &sync_method, DEFAULT_SYNC_METHOD, sync_method_options, - assign_xlog_sync_method, NULL + NULL, assign_xlog_sync_method, NULL }, { @@ -2967,7 +3209,8 @@ static struct config_enum ConfigureNamesEnum[] = NULL }, &xmlbinary, - XMLBINARY_BASE64, xmlbinary_options, NULL, NULL + XMLBINARY_BASE64, xmlbinary_options, + NULL, NULL, NULL }, { @@ -2977,13 +3220,14 @@ static struct config_enum ConfigureNamesEnum[] = NULL }, &xmloption, - XMLOPTION_CONTENT, xmloption_options, NULL, NULL + XMLOPTION_CONTENT, xmloption_options, + NULL, NULL, NULL }, /* End-of-list marker */ { - {NULL, 0, 0, NULL, NULL}, NULL, 0, NULL, NULL, NULL + {NULL, 0, 0, NULL, NULL}, NULL, 0, NULL, NULL, NULL, NULL } }; @@ -3030,7 +3274,6 @@ static void ReportGUCOption(struct config_generic * record); static void ShowGUCConfigOption(const char *name, DestReceiver *dest); static void ShowAllGUCConfig(DestReceiver *dest); static char *_ShowOption(struct config_generic * record, bool use_units); -static bool is_newvalue_equal(struct config_generic * record, const char *newvalue); static bool validate_option_array_item(const char *name, const char *value, bool skipIfNoPermissions); @@ -3078,6 +3321,27 @@ guc_strdup(int elevel, const char *src) } +/* + * Detect whether strval is referenced anywhere in a GUC string item + */ +static bool +string_field_used(struct config_string * conf, char *strval) +{ + GucStack *stack; + + if (strval == *(conf->variable) || + strval == conf->reset_val || + strval == conf->boot_val) + return true; + for (stack = conf->gen.stack; stack; stack = stack->prev) + { + if (strval == stack->prior.val.stringval || + strval == stack->masked.val.stringval) + return true; + } + return false; +} + /* * Support for assigning to a field of a string GUC item. Free the prior * value if it's not referenced anywhere else in the item (including stacked @@ -3087,87 +3351,119 @@ static void set_string_field(struct config_string * conf, char **field, char *newval) { char *oldval = *field; - GucStack *stack; /* Do the assignment */ *field = newval; - /* Exit if any duplicate references, or if old value was NULL anyway */ - if (oldval == NULL || - oldval == *(conf->variable) || - oldval == conf->reset_val || - oldval == conf->boot_val) - return; - for (stack = conf->gen.stack; stack; stack = stack->prev) - { - if (oldval == stack->prior.stringval || - oldval == stack->masked.stringval) - return; - } - - /* Not used anymore, so free it */ - free(oldval); + /* Free old value if it's not NULL and isn't referenced anymore */ + if (oldval && !string_field_used(conf, oldval)) + free(oldval); } /* - * Detect whether strval is referenced anywhere in a GUC string item + * Detect whether an "extra" struct is referenced anywhere in a GUC item */ static bool -string_field_used(struct config_string * conf, char *strval) +extra_field_used(struct config_generic * gconf, void *extra) { GucStack *stack; - if (strval == *(conf->variable) || - strval == conf->reset_val || - strval == conf->boot_val) + if (extra == gconf->extra) return true; - for (stack = conf->gen.stack; stack; stack = stack->prev) + switch (gconf->vartype) { - if (strval == stack->prior.stringval || - strval == stack->masked.stringval) + case PGC_BOOL: + if (extra == ((struct config_bool *) gconf)->reset_extra) + return true; + break; + case PGC_INT: + if (extra == ((struct config_int *) gconf)->reset_extra) + return true; + break; + case PGC_REAL: + if (extra == ((struct config_real *) gconf)->reset_extra) + return true; + break; + case PGC_STRING: + if (extra == ((struct config_string *) gconf)->reset_extra) + return true; + break; + case PGC_ENUM: + if (extra == ((struct config_enum *) gconf)->reset_extra) + return true; + break; + } + for (stack = gconf->stack; stack; stack = stack->prev) + { + if (extra == stack->prior.extra || + extra == stack->masked.extra) return true; } + return false; } /* - * Support for copying a variable's active value into a stack entry + * Support for assigning to an "extra" field of a GUC item. Free the prior + * value if it's not referenced anywhere else in the item (including stacked + * states). + */ +static void +set_extra_field(struct config_generic * gconf, void **field, void *newval) +{ + void *oldval = *field; + + /* Do the assignment */ + *field = newval; + + /* Free old value if it's not NULL and isn't referenced anymore */ + if (oldval && !extra_field_used(gconf, oldval)) + free(oldval); +} + +/* + * Support for copying a variable's active value into a stack entry. + * The "extra" field associated with the active value is copied, too. + * + * NB: be sure stringval and extra fields of a new stack entry are + * initialized to NULL before this is used, else we'll try to free() them. */ static void -set_stack_value(struct config_generic * gconf, union config_var_value * val) +set_stack_value(struct config_generic * gconf, config_var_value *val) { switch (gconf->vartype) { case PGC_BOOL: - val->boolval = + val->val.boolval = *((struct config_bool *) gconf)->variable; break; case PGC_INT: - val->intval = + val->val.intval = *((struct config_int *) gconf)->variable; break; case PGC_REAL: - val->realval = + val->val.realval = *((struct config_real *) gconf)->variable; break; case PGC_STRING: - /* we assume stringval is NULL if not valid */ set_string_field((struct config_string *) gconf, - &(val->stringval), + &(val->val.stringval), *((struct config_string *) gconf)->variable); break; case PGC_ENUM: - val->enumval = + val->val.enumval = *((struct config_enum *) gconf)->variable; break; } + set_extra_field(gconf, &(val->extra), gconf->extra); } /* - * Support for discarding a no-longer-needed value in a stack entry + * Support for discarding a no-longer-needed value in a stack entry. + * The "extra" field associated with the stack entry is cleared, too. */ static void -discard_stack_value(struct config_generic * gconf, union config_var_value * val) +discard_stack_value(struct config_generic * gconf, config_var_value *val) { switch (gconf->vartype) { @@ -3179,10 +3475,11 @@ discard_stack_value(struct config_generic * gconf, union config_var_value * val) break; case PGC_STRING: set_string_field((struct config_string *) gconf, - &(val->stringval), + &(val->val.stringval), NULL); break; } + set_extra_field(gconf, &(val->extra), NULL); } @@ -3594,6 +3891,9 @@ InitializeGUCOptions(void) /* * Initialize one GUC option variable to its compiled-in default. + * + * Note: the reason for calling check_hooks is not that we think the boot_val + * might fail, but that the hooks might wish to compute an "extra" struct. */ static void InitializeOneGUCOption(struct config_generic * gconf) @@ -3602,6 +3902,7 @@ InitializeOneGUCOption(struct config_generic * gconf) gconf->reset_source = PGC_S_DEFAULT; gconf->source = PGC_S_DEFAULT; gconf->stack = NULL; + gconf->extra = NULL; gconf->sourcefile = NULL; gconf->sourceline = 0; @@ -3610,96 +3911,91 @@ InitializeOneGUCOption(struct config_generic * gconf) case PGC_BOOL: { struct config_bool *conf = (struct config_bool *) gconf; + bool newval = conf->boot_val; + void *extra = NULL; + if (!call_bool_check_hook(conf, &newval, &extra, + PGC_S_DEFAULT, LOG)) + elog(FATAL, "failed to initialize %s to %d", + conf->gen.name, (int) newval); if (conf->assign_hook) - if (!(*conf->assign_hook) (conf->boot_val, true, - PGC_S_DEFAULT)) - elog(FATAL, "failed to initialize %s to %d", - conf->gen.name, (int) conf->boot_val); - *conf->variable = conf->reset_val = conf->boot_val; + (*conf->assign_hook) (newval, extra); + *conf->variable = conf->reset_val = newval; + conf->gen.extra = conf->reset_extra = extra; break; } case PGC_INT: { struct config_int *conf = (struct config_int *) gconf; - - Assert(conf->boot_val >= conf->min); - Assert(conf->boot_val <= conf->max); + int newval = conf->boot_val; + void *extra = NULL; + + Assert(newval >= conf->min); + Assert(newval <= conf->max); + if (!call_int_check_hook(conf, &newval, &extra, + PGC_S_DEFAULT, LOG)) + elog(FATAL, "failed to initialize %s to %d", + conf->gen.name, newval); if (conf->assign_hook) - if (!(*conf->assign_hook) (conf->boot_val, true, - PGC_S_DEFAULT)) - elog(FATAL, "failed to initialize %s to %d", - conf->gen.name, conf->boot_val); - *conf->variable = conf->reset_val = conf->boot_val; + (*conf->assign_hook) (newval, extra); + *conf->variable = conf->reset_val = newval; + conf->gen.extra = conf->reset_extra = extra; break; } case PGC_REAL: { struct config_real *conf = (struct config_real *) gconf; - - Assert(conf->boot_val >= conf->min); - Assert(conf->boot_val <= conf->max); + double newval = conf->boot_val; + void *extra = NULL; + + Assert(newval >= conf->min); + Assert(newval <= conf->max); + if (!call_real_check_hook(conf, &newval, &extra, + PGC_S_DEFAULT, LOG)) + elog(FATAL, "failed to initialize %s to %g", + conf->gen.name, newval); if (conf->assign_hook) - if (!(*conf->assign_hook) (conf->boot_val, true, - PGC_S_DEFAULT)) - elog(FATAL, "failed to initialize %s to %g", - conf->gen.name, conf->boot_val); - *conf->variable = conf->reset_val = conf->boot_val; + (*conf->assign_hook) (newval, extra); + *conf->variable = conf->reset_val = newval; + conf->gen.extra = conf->reset_extra = extra; break; } case PGC_STRING: { struct config_string *conf = (struct config_string *) gconf; - char *str; - - *conf->variable = NULL; - conf->reset_val = NULL; - - if (conf->boot_val == NULL) - { - /* leave the value NULL, do not call assign hook */ - break; - } + char *newval; + void *extra = NULL; - str = guc_strdup(FATAL, conf->boot_val); - conf->reset_val = str; + /* non-NULL boot_val must always get strdup'd */ + if (conf->boot_val != NULL) + newval = guc_strdup(FATAL, conf->boot_val); + else + newval = NULL; + if (!call_string_check_hook(conf, &newval, &extra, + PGC_S_DEFAULT, LOG)) + elog(FATAL, "failed to initialize %s to \"%s\"", + conf->gen.name, newval ? newval : ""); if (conf->assign_hook) - { - const char *newstr; - - newstr = (*conf->assign_hook) (str, true, - PGC_S_DEFAULT); - if (newstr == NULL) - { - elog(FATAL, "failed to initialize %s to \"%s\"", - conf->gen.name, str); - } - else if (newstr != str) - { - free(str); - - /* - * See notes in set_config_option about casting - */ - str = (char *) newstr; - conf->reset_val = str; - } - } - *conf->variable = str; + (*conf->assign_hook) (newval, extra); + *conf->variable = conf->reset_val = newval; + conf->gen.extra = conf->reset_extra = extra; break; } case PGC_ENUM: { struct config_enum *conf = (struct config_enum *) gconf; + int newval = conf->boot_val; + void *extra = NULL; + if (!call_enum_check_hook(conf, &newval, &extra, + PGC_S_DEFAULT, LOG)) + elog(FATAL, "failed to initialize %s to %d", + conf->gen.name, newval); if (conf->assign_hook) - if (!(*conf->assign_hook) (conf->boot_val, true, - PGC_S_DEFAULT)) - elog(FATAL, "failed to initialize %s to %s", - conf->gen.name, - config_enum_lookup_by_value(conf, conf->boot_val)); - *conf->variable = conf->reset_val = conf->boot_val; + (*conf->assign_hook) (newval, extra); + *conf->variable = conf->reset_val = newval; + conf->gen.extra = conf->reset_extra = extra; break; } } @@ -3888,11 +4184,11 @@ ResetAllOptions(void) struct config_bool *conf = (struct config_bool *) gconf; if (conf->assign_hook) - if (!(*conf->assign_hook) (conf->reset_val, true, - PGC_S_SESSION)) - elog(ERROR, "failed to reset %s to %d", - conf->gen.name, (int) conf->reset_val); + (*conf->assign_hook) (conf->reset_val, + conf->reset_extra); *conf->variable = conf->reset_val; + set_extra_field(&conf->gen, &conf->gen.extra, + conf->reset_extra); break; } case PGC_INT: @@ -3900,11 +4196,11 @@ ResetAllOptions(void) struct config_int *conf = (struct config_int *) gconf; if (conf->assign_hook) - if (!(*conf->assign_hook) (conf->reset_val, true, - PGC_S_SESSION)) - elog(ERROR, "failed to reset %s to %d", - conf->gen.name, conf->reset_val); + (*conf->assign_hook) (conf->reset_val, + conf->reset_extra); *conf->variable = conf->reset_val; + set_extra_field(&conf->gen, &conf->gen.extra, + conf->reset_extra); break; } case PGC_REAL: @@ -3912,40 +4208,23 @@ ResetAllOptions(void) struct config_real *conf = (struct config_real *) gconf; if (conf->assign_hook) - if (!(*conf->assign_hook) (conf->reset_val, true, - PGC_S_SESSION)) - elog(ERROR, "failed to reset %s to %g", - conf->gen.name, conf->reset_val); + (*conf->assign_hook) (conf->reset_val, + conf->reset_extra); *conf->variable = conf->reset_val; + set_extra_field(&conf->gen, &conf->gen.extra, + conf->reset_extra); break; } case PGC_STRING: { struct config_string *conf = (struct config_string *) gconf; - char *str; - - /* We need not strdup here */ - str = conf->reset_val; - - if (conf->assign_hook && str) - { - const char *newstr; - - newstr = (*conf->assign_hook) (str, true, - PGC_S_SESSION); - if (newstr == NULL) - elog(ERROR, "failed to reset %s to \"%s\"", - conf->gen.name, str); - else if (newstr != str) - { - /* - * See notes in set_config_option about casting - */ - str = (char *) newstr; - } - } - set_string_field(conf, conf->variable, str); + if (conf->assign_hook) + (*conf->assign_hook) (conf->reset_val, + conf->reset_extra); + set_string_field(conf, conf->variable, conf->reset_val); + set_extra_field(&conf->gen, &conf->gen.extra, + conf->reset_extra); break; } case PGC_ENUM: @@ -3953,12 +4232,11 @@ ResetAllOptions(void) struct config_enum *conf = (struct config_enum *) gconf; if (conf->assign_hook) - if (!(*conf->assign_hook) (conf->reset_val, true, - PGC_S_SESSION)) - elog(ERROR, "failed to reset %s to %s", - conf->gen.name, - config_enum_lookup_by_value(conf, conf->reset_val)); + (*conf->assign_hook) (conf->reset_val, + conf->reset_extra); *conf->variable = conf->reset_val; + set_extra_field(&conf->gen, &conf->gen.extra, + conf->reset_extra); break; } } @@ -4209,7 +4487,7 @@ AtEOXact_GUC(bool isCommit, int nestLevel) if (restorePrior || restoreMasked) { /* Perform appropriate restoration of the stacked value */ - union config_var_value newvalue; + config_var_value newvalue; GucSource newsource; if (restoreMasked) @@ -4228,16 +4506,17 @@ AtEOXact_GUC(bool isCommit, int nestLevel) case PGC_BOOL: { struct config_bool *conf = (struct config_bool *) gconf; - bool newval = newvalue.boolval; + bool newval = newvalue.val.boolval; + void *newextra = newvalue.extra; - if (*conf->variable != newval) + if (*conf->variable != newval || + conf->gen.extra != newextra) { if (conf->assign_hook) - if (!(*conf->assign_hook) (newval, - true, PGC_S_OVERRIDE)) - elog(LOG, "failed to commit %s as %d", - conf->gen.name, (int) newval); + (*conf->assign_hook) (newval, newextra); *conf->variable = newval; + set_extra_field(&conf->gen, &conf->gen.extra, + newextra); changed = true; } break; @@ -4245,16 +4524,17 @@ AtEOXact_GUC(bool isCommit, int nestLevel) case PGC_INT: { struct config_int *conf = (struct config_int *) gconf; - int newval = newvalue.intval; + int newval = newvalue.val.intval; + void *newextra = newvalue.extra; - if (*conf->variable != newval) + if (*conf->variable != newval || + conf->gen.extra != newextra) { if (conf->assign_hook) - if (!(*conf->assign_hook) (newval, - true, PGC_S_OVERRIDE)) - elog(LOG, "failed to commit %s as %d", - conf->gen.name, newval); + (*conf->assign_hook) (newval, newextra); *conf->variable = newval; + set_extra_field(&conf->gen, &conf->gen.extra, + newextra); changed = true; } break; @@ -4262,16 +4542,17 @@ AtEOXact_GUC(bool isCommit, int nestLevel) case PGC_REAL: { struct config_real *conf = (struct config_real *) gconf; - double newval = newvalue.realval; + double newval = newvalue.val.realval; + void *newextra = newvalue.extra; - if (*conf->variable != newval) + if (*conf->variable != newval || + conf->gen.extra != newextra) { if (conf->assign_hook) - if (!(*conf->assign_hook) (newval, - true, PGC_S_OVERRIDE)) - elog(LOG, "failed to commit %s as %g", - conf->gen.name, newval); + (*conf->assign_hook) (newval, newextra); *conf->variable = newval; + set_extra_field(&conf->gen, &conf->gen.extra, + newextra); changed = true; } break; @@ -4279,33 +4560,17 @@ AtEOXact_GUC(bool isCommit, int nestLevel) case PGC_STRING: { struct config_string *conf = (struct config_string *) gconf; - char *newval = newvalue.stringval; + char *newval = newvalue.val.stringval; + void *newextra = newvalue.extra; - if (*conf->variable != newval) + if (*conf->variable != newval || + conf->gen.extra != newextra) { - if (conf->assign_hook && newval) - { - const char *newstr; - - newstr = (*conf->assign_hook) (newval, true, - PGC_S_OVERRIDE); - if (newstr == NULL) - elog(LOG, "failed to commit %s as \"%s\"", - conf->gen.name, newval); - else if (newstr != newval) - { - /* - * If newval should now be freed, - * it'll be taken care of below. - * - * See notes in set_config_option - * about casting - */ - newval = (char *) newstr; - } - } - + if (conf->assign_hook) + (*conf->assign_hook) (newval, newextra); set_string_field(conf, conf->variable, newval); + set_extra_field(&conf->gen, &conf->gen.extra, + newextra); changed = true; } @@ -4315,30 +4580,36 @@ AtEOXact_GUC(bool isCommit, int nestLevel) * we have type-specific code anyway, might as * well inline it. */ - set_string_field(conf, &stack->prior.stringval, NULL); - set_string_field(conf, &stack->masked.stringval, NULL); + set_string_field(conf, &stack->prior.val.stringval, NULL); + set_string_field(conf, &stack->masked.val.stringval, NULL); break; } case PGC_ENUM: { struct config_enum *conf = (struct config_enum *) gconf; - int newval = newvalue.enumval; + int newval = newvalue.val.enumval; + void *newextra = newvalue.extra; - if (*conf->variable != newval) + if (*conf->variable != newval || + conf->gen.extra != newextra) { if (conf->assign_hook) - if (!(*conf->assign_hook) (newval, - true, PGC_S_OVERRIDE)) - elog(LOG, "failed to commit %s as %s", - conf->gen.name, - config_enum_lookup_by_value(conf, newval)); + (*conf->assign_hook) (newval, newextra); *conf->variable = newval; + set_extra_field(&conf->gen, &conf->gen.extra, + newextra); changed = true; } break; } } + /* + * Release stacked extra values if not used anymore. + */ + set_extra_field(gconf, &(stack->prior.extra), NULL); + set_extra_field(gconf, &(stack->masked.extra), NULL); + gconf->source = newsource; } @@ -4748,33 +5019,6 @@ config_enum_get_options(struct config_enum * record, const char *prefix, return retstr.data; } -/* - * Call a GucStringAssignHook function, being careful to free the - * "newval" string if the hook ereports. - * - * This is split out of set_config_option just to avoid the "volatile" - * qualifiers that would otherwise have to be plastered all over. - */ -static const char * -call_string_assign_hook(GucStringAssignHook assign_hook, - char *newval, bool doit, GucSource source) -{ - const char *result; - - PG_TRY(); - { - result = (*assign_hook) (newval, doit, source); - } - PG_CATCH(); - { - free(newval); - PG_RE_THROW(); - } - PG_END_TRY(); - - return result; -} - /* * Sets option `name' to given value. The value should be a string @@ -4810,6 +5054,7 @@ set_config_option(const char *name, const char *value, { struct config_generic *record; int elevel; + bool prohibitValueChange = false; bool makeDefault; if (context == PGC_SIGHUP || source == PGC_S_DEFAULT) @@ -4846,15 +5091,21 @@ set_config_option(const char *name, const char *value, /* * Check if the option can be set at this time. See guc.h for the precise - * rules. Note that we don't want to throw errors if we're in the SIGHUP - * context. In that case we just ignore the attempt and return true. + * rules. */ switch (record->context) { case PGC_INTERNAL: if (context == PGC_SIGHUP) + { + /* + * Historically we've just silently ignored attempts to set + * PGC_INTERNAL variables from the config file. Maybe it'd + * be better to use the prohibitValueChange logic for this? + */ return true; - if (context != PGC_INTERNAL) + } + else if (context != PGC_INTERNAL) { ereport(elevel, (errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM), @@ -4867,19 +5118,23 @@ set_config_option(const char *name, const char *value, if (context == PGC_SIGHUP) { /* - * We are reading a PGC_POSTMASTER var from postgresql.conf. - * We can't change the setting, so give a warning if the DBA - * tries to change it. (Throwing an error would be more - * consistent, but seems overly rigid.) + * We are re-reading a PGC_POSTMASTER variable from + * postgresql.conf. We can't change the setting, so we should + * give a warning if the DBA tries to change it. However, + * because of variant formats, canonicalization by check + * hooks, etc, we can't just compare the given string directly + * to what's stored. Set a flag to check below after we have + * the final storable value. + * + * During the "checking" pass we just do nothing, to avoid + * printing the warning twice. */ - if (changeVal && !is_newvalue_equal(record, value)) - ereport(elevel, - (errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM), - errmsg("parameter \"%s\" cannot be changed without restarting the server", - name))); - return true; + if (!changeVal) + return true; + + prohibitValueChange = true; } - if (context != PGC_POSTMASTER) + else if (context != PGC_POSTMASTER) { ereport(elevel, (errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM), @@ -5022,6 +5277,7 @@ set_config_option(const char *name, const char *value, { struct config_bool *conf = (struct config_bool *) record; bool newval; + void *newextra = NULL; if (value) { @@ -5033,32 +5289,45 @@ set_config_option(const char *name, const char *value, name))); return false; } + if (!call_bool_check_hook(conf, &newval, &newextra, + source, elevel)) + return false; } else if (source == PGC_S_DEFAULT) + { newval = conf->boot_val; + if (!call_bool_check_hook(conf, &newval, &newextra, + source, elevel)) + return false; + } else { newval = conf->reset_val; + newextra = conf->reset_extra; source = conf->gen.reset_source; } - /* Save old value to support transaction abort */ - if (changeVal && !makeDefault) - push_old_value(&conf->gen, action); - - if (conf->assign_hook) - if (!(*conf->assign_hook) (newval, changeVal, source)) - { + if (prohibitValueChange) + { + if (*conf->variable != newval) ereport(elevel, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("invalid value for parameter \"%s\": %d", - name, (int) newval))); - return false; - } + (errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM), + errmsg("parameter \"%s\" cannot be changed without restarting the server", + name))); + return false; + } if (changeVal) { + /* Save old value to support transaction abort */ + if (!makeDefault) + push_old_value(&conf->gen, action); + + if (conf->assign_hook) + (*conf->assign_hook) (newval, newextra); *conf->variable = newval; + set_extra_field(&conf->gen, &conf->gen.extra, + newextra); conf->gen.source = source; } if (makeDefault) @@ -5068,17 +5337,25 @@ set_config_option(const char *name, const char *value, if (conf->gen.reset_source <= source) { conf->reset_val = newval; + set_extra_field(&conf->gen, &conf->reset_extra, + newextra); conf->gen.reset_source = source; } for (stack = conf->gen.stack; stack; stack = stack->prev) { if (stack->source <= source) { - stack->prior.boolval = newval; + stack->prior.val.boolval = newval; + set_extra_field(&conf->gen, &stack->prior.extra, + newextra); stack->source = source; } } } + + /* Perhaps we didn't install newextra anywhere */ + if (newextra && !extra_field_used(&conf->gen, newextra)) + free(newextra); break; } @@ -5086,6 +5363,7 @@ set_config_option(const char *name, const char *value, { struct config_int *conf = (struct config_int *) record; int newval; + void *newextra = NULL; if (value) { @@ -5108,32 +5386,45 @@ set_config_option(const char *name, const char *value, newval, name, conf->min, conf->max))); return false; } + if (!call_int_check_hook(conf, &newval, &newextra, + source, elevel)) + return false; } else if (source == PGC_S_DEFAULT) + { newval = conf->boot_val; + if (!call_int_check_hook(conf, &newval, &newextra, + source, elevel)) + return false; + } else { newval = conf->reset_val; + newextra = conf->reset_extra; source = conf->gen.reset_source; } - /* Save old value to support transaction abort */ - if (changeVal && !makeDefault) - push_old_value(&conf->gen, action); - - if (conf->assign_hook) - if (!(*conf->assign_hook) (newval, changeVal, source)) - { + if (prohibitValueChange) + { + if (*conf->variable != newval) ereport(elevel, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("invalid value for parameter \"%s\": %d", - name, newval))); - return false; - } + (errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM), + errmsg("parameter \"%s\" cannot be changed without restarting the server", + name))); + return false; + } if (changeVal) { + /* Save old value to support transaction abort */ + if (!makeDefault) + push_old_value(&conf->gen, action); + + if (conf->assign_hook) + (*conf->assign_hook) (newval, newextra); *conf->variable = newval; + set_extra_field(&conf->gen, &conf->gen.extra, + newextra); conf->gen.source = source; } if (makeDefault) @@ -5143,17 +5434,25 @@ set_config_option(const char *name, const char *value, if (conf->gen.reset_source <= source) { conf->reset_val = newval; + set_extra_field(&conf->gen, &conf->reset_extra, + newextra); conf->gen.reset_source = source; } for (stack = conf->gen.stack; stack; stack = stack->prev) { if (stack->source <= source) { - stack->prior.intval = newval; + stack->prior.val.intval = newval; + set_extra_field(&conf->gen, &stack->prior.extra, + newextra); stack->source = source; } } } + + /* Perhaps we didn't install newextra anywhere */ + if (newextra && !extra_field_used(&conf->gen, newextra)) + free(newextra); break; } @@ -5161,6 +5460,7 @@ set_config_option(const char *name, const char *value, { struct config_real *conf = (struct config_real *) record; double newval; + void *newextra = NULL; if (value) { @@ -5180,32 +5480,45 @@ set_config_option(const char *name, const char *value, newval, name, conf->min, conf->max))); return false; } + if (!call_real_check_hook(conf, &newval, &newextra, + source, elevel)) + return false; } else if (source == PGC_S_DEFAULT) + { newval = conf->boot_val; + if (!call_real_check_hook(conf, &newval, &newextra, + source, elevel)) + return false; + } else { newval = conf->reset_val; + newextra = conf->reset_extra; source = conf->gen.reset_source; } - /* Save old value to support transaction abort */ - if (changeVal && !makeDefault) - push_old_value(&conf->gen, action); - - if (conf->assign_hook) - if (!(*conf->assign_hook) (newval, changeVal, source)) - { + if (prohibitValueChange) + { + if (*conf->variable != newval) ereport(elevel, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("invalid value for parameter \"%s\": %g", - name, newval))); - return false; - } + (errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM), + errmsg("parameter \"%s\" cannot be changed without restarting the server", + name))); + return false; + } if (changeVal) { + /* Save old value to support transaction abort */ + if (!makeDefault) + push_old_value(&conf->gen, action); + + if (conf->assign_hook) + (*conf->assign_hook) (newval, newextra); *conf->variable = newval; + set_extra_field(&conf->gen, &conf->gen.extra, + newextra); conf->gen.source = source; } if (makeDefault) @@ -5215,17 +5528,25 @@ set_config_option(const char *name, const char *value, if (conf->gen.reset_source <= source) { conf->reset_val = newval; + set_extra_field(&conf->gen, &conf->reset_extra, + newextra); conf->gen.reset_source = source; } for (stack = conf->gen.stack; stack; stack = stack->prev) { if (stack->source <= source) { - stack->prior.realval = newval; + stack->prior.val.realval = newval; + set_extra_field(&conf->gen, &stack->prior.extra, + newextra); stack->source = source; } } } + + /* Perhaps we didn't install newextra anywhere */ + if (newextra && !extra_field_used(&conf->gen, newextra)) + free(newextra); break; } @@ -5233,95 +5554,88 @@ set_config_option(const char *name, const char *value, { struct config_string *conf = (struct config_string *) record; char *newval; + void *newextra = NULL; if (value) { + /* + * The value passed by the caller could be transient, + * so we always strdup it. + */ newval = guc_strdup(elevel, value); if (newval == NULL) return false; /* - * The only sort of "parsing" check we need to do is apply + * The only built-in "parsing" check we have is to apply * truncation if GUC_IS_NAME. */ if (conf->gen.flags & GUC_IS_NAME) truncate_identifier(newval, strlen(newval), true); + + if (!call_string_check_hook(conf, &newval, &newextra, + source, elevel)) + { + free(newval); + return false; + } } else if (source == PGC_S_DEFAULT) { - if (conf->boot_val == NULL) - newval = NULL; - else + /* non-NULL boot_val must always get strdup'd */ + if (conf->boot_val != NULL) { newval = guc_strdup(elevel, conf->boot_val); if (newval == NULL) return false; } + else + newval = NULL; + + if (!call_string_check_hook(conf, &newval, &newextra, + source, elevel)) + { + free(newval); + return false; + } } else { /* - * We could possibly avoid strdup here, but easier to make - * this case work the same as the normal assignment case; - * note the possible free of newval below. + * strdup not needed, since reset_val is already under + * guc.c's control */ - if (conf->reset_val == NULL) - newval = NULL; - else - { - newval = guc_strdup(elevel, conf->reset_val); - if (newval == NULL) - return false; - } + newval = conf->reset_val; + newextra = conf->reset_extra; source = conf->gen.reset_source; } - /* Save old value to support transaction abort */ - if (changeVal && !makeDefault) - push_old_value(&conf->gen, action); - - if (conf->assign_hook && newval) + if (prohibitValueChange) { - const char *hookresult; - - /* - * If the hook ereports, we have to make sure we free - * newval, else it will be a permanent memory leak. - */ - hookresult = call_string_assign_hook(conf->assign_hook, - newval, - changeVal, - source); - if (hookresult == NULL) - { - free(newval); + /* newval shouldn't be NULL, so we're a bit sloppy here */ + if (*conf->variable == NULL || newval == NULL || + strcmp(*conf->variable, newval) != 0) ereport(elevel, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("invalid value for parameter \"%s\": \"%s\"", - name, value ? value : ""))); - return false; - } - else if (hookresult != newval) - { - free(newval); - - /* - * Having to cast away const here is annoying, but the - * alternative is to declare assign_hooks as returning - * char*, which would mean they'd have to cast away - * const, or as both taking and returning char*, which - * doesn't seem attractive either --- we don't want - * them to scribble on the passed str. - */ - newval = (char *) hookresult; - } + (errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM), + errmsg("parameter \"%s\" cannot be changed without restarting the server", + name))); + return false; } if (changeVal) { + /* Save old value to support transaction abort */ + if (!makeDefault) + push_old_value(&conf->gen, action); + + if (conf->assign_hook) + (*conf->assign_hook) (newval, newextra); set_string_field(conf, conf->variable, newval); + set_extra_field(&conf->gen, &conf->gen.extra, + newextra); conf->gen.source = source; } + if (makeDefault) { GucStack *stack; @@ -5329,27 +5643,37 @@ set_config_option(const char *name, const char *value, if (conf->gen.reset_source <= source) { set_string_field(conf, &conf->reset_val, newval); + set_extra_field(&conf->gen, &conf->reset_extra, + newextra); conf->gen.reset_source = source; } for (stack = conf->gen.stack; stack; stack = stack->prev) { if (stack->source <= source) { - set_string_field(conf, &stack->prior.stringval, + set_string_field(conf, &stack->prior.val.stringval, newval); + set_extra_field(&conf->gen, &stack->prior.extra, + newextra); stack->source = source; } } } + /* Perhaps we didn't install newval anywhere */ if (newval && !string_field_used(conf, newval)) free(newval); + /* Perhaps we didn't install newextra anywhere */ + if (newextra && !extra_field_used(&conf->gen, newextra)) + free(newextra); break; } + case PGC_ENUM: { struct config_enum *conf = (struct config_enum *) record; int newval; + void *newextra = NULL; if (value) { @@ -5371,33 +5695,45 @@ set_config_option(const char *name, const char *value, pfree(hintmsg); return false; } + if (!call_enum_check_hook(conf, &newval, &newextra, + source, elevel)) + return false; } else if (source == PGC_S_DEFAULT) + { newval = conf->boot_val; + if (!call_enum_check_hook(conf, &newval, &newextra, + source, elevel)) + return false; + } else { newval = conf->reset_val; + newextra = conf->reset_extra; source = conf->gen.reset_source; } - /* Save old value to support transaction abort */ - if (changeVal && !makeDefault) - push_old_value(&conf->gen, action); - - if (conf->assign_hook) - if (!(*conf->assign_hook) (newval, changeVal, source)) - { + if (prohibitValueChange) + { + if (*conf->variable != newval) ereport(elevel, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("invalid value for parameter \"%s\": \"%s\"", - name, - config_enum_lookup_by_value(conf, newval)))); - return false; - } + (errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM), + errmsg("parameter \"%s\" cannot be changed without restarting the server", + name))); + return false; + } if (changeVal) { + /* Save old value to support transaction abort */ + if (!makeDefault) + push_old_value(&conf->gen, action); + + if (conf->assign_hook) + (*conf->assign_hook) (newval, newextra); *conf->variable = newval; + set_extra_field(&conf->gen, &conf->gen.extra, + newextra); conf->gen.source = source; } if (makeDefault) @@ -5407,17 +5743,25 @@ set_config_option(const char *name, const char *value, if (conf->gen.reset_source <= source) { conf->reset_val = newval; + set_extra_field(&conf->gen, &conf->reset_extra, + newextra); conf->gen.reset_source = source; } for (stack = conf->gen.stack; stack; stack = stack->prev) { if (stack->source <= source) { - stack->prior.enumval = newval; + stack->prior.val.enumval = newval; + set_extra_field(&conf->gen, &stack->prior.extra, + newextra); stack->source = source; } } } + + /* Perhaps we didn't install newextra anywhere */ + if (newextra && !extra_field_used(&conf->gen, newextra)) + free(newextra); break; } } @@ -5577,51 +5921,6 @@ GetConfigOptionResetString(const char *name) } -/* - * GUC_complaint_elevel - * Get the ereport error level to use in an assign_hook's error report. - * - * This should be used by assign hooks that want to emit a custom error - * report (in addition to the generic "invalid value for option FOO" that - * guc.c will provide). Note that the result might be ERROR or a lower - * level, so the caller must be prepared for control to return from ereport, - * or not. If control does return, return false/NULL from the hook function. - * - * At some point it'd be nice to replace this with a mechanism that allows - * the custom message to become the DETAIL line of guc.c's generic message. - */ -int -GUC_complaint_elevel(GucSource source) -{ - int elevel; - - if (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_OVERRIDE) - { - /* - * If we're a postmaster child, this is probably "undo" during - * transaction abort, so we don't want to clutter the log. There's a - * small chance of a real problem with an OVERRIDE setting, though, so - * suppressing the message entirely wouldn't be desirable. - */ - elevel = IsUnderPostmaster ? DEBUG5 : LOG; - } - else if (source < PGC_S_INTERACTIVE) - elevel = LOG; - else - elevel = ERROR; - - return elevel; -} - - /* * flatten_set_variable_args * Given a parsenode List as emitted by the grammar for SET, @@ -6115,6 +6414,7 @@ DefineCustomBoolVariable(const char *name, bool bootValue, GucContext context, int flags, + GucBoolCheckHook check_hook, GucBoolAssignHook assign_hook, GucShowHook show_hook) { @@ -6126,6 +6426,7 @@ DefineCustomBoolVariable(const char *name, var->variable = valueAddr; var->boot_val = bootValue; var->reset_val = bootValue; + var->check_hook = check_hook; var->assign_hook = assign_hook; var->show_hook = show_hook; define_custom_variable(&var->gen); @@ -6141,6 +6442,7 @@ DefineCustomIntVariable(const char *name, int maxValue, GucContext context, int flags, + GucIntCheckHook check_hook, GucIntAssignHook assign_hook, GucShowHook show_hook) { @@ -6154,6 +6456,7 @@ DefineCustomIntVariable(const char *name, var->reset_val = bootValue; var->min = minValue; var->max = maxValue; + var->check_hook = check_hook; var->assign_hook = assign_hook; var->show_hook = show_hook; define_custom_variable(&var->gen); @@ -6169,6 +6472,7 @@ DefineCustomRealVariable(const char *name, double maxValue, GucContext context, int flags, + GucRealCheckHook check_hook, GucRealAssignHook assign_hook, GucShowHook show_hook) { @@ -6182,6 +6486,7 @@ DefineCustomRealVariable(const char *name, var->reset_val = bootValue; var->min = minValue; var->max = maxValue; + var->check_hook = check_hook; var->assign_hook = assign_hook; var->show_hook = show_hook; define_custom_variable(&var->gen); @@ -6195,6 +6500,7 @@ DefineCustomStringVariable(const char *name, const char *bootValue, GucContext context, int flags, + GucStringCheckHook check_hook, GucStringAssignHook assign_hook, GucShowHook show_hook) { @@ -6205,9 +6511,7 @@ DefineCustomStringVariable(const char *name, PGC_STRING, sizeof(struct config_string)); var->variable = valueAddr; var->boot_val = bootValue; - /* we could probably do without strdup, but keep it like normal case */ - if (var->boot_val) - var->reset_val = guc_strdup(ERROR, var->boot_val); + var->check_hook = check_hook; var->assign_hook = assign_hook; var->show_hook = show_hook; define_custom_variable(&var->gen); @@ -6222,6 +6526,7 @@ DefineCustomEnumVariable(const char *name, const struct config_enum_entry * options, GucContext context, int flags, + GucEnumCheckHook check_hook, GucEnumAssignHook assign_hook, GucShowHook show_hook) { @@ -6234,6 +6539,7 @@ DefineCustomEnumVariable(const char *name, var->boot_val = bootValue; var->reset_val = bootValue; var->options = options; + var->check_hook = check_hook; var->assign_hook = assign_hook; var->show_hook = show_hook; define_custom_variable(&var->gen); @@ -6995,67 +7301,6 @@ _ShowOption(struct config_generic * record, bool use_units) } -/* - * Attempt (badly) to detect if a proposed new GUC setting is the same - * as the current value. - * - * XXX this does not really work because it doesn't account for the - * effects of canonicalization of string values by assign_hooks. - */ -static bool -is_newvalue_equal(struct config_generic * record, const char *newvalue) -{ - /* newvalue == NULL isn't supported */ - Assert(newvalue != NULL); - - switch (record->vartype) - { - case PGC_BOOL: - { - struct config_bool *conf = (struct config_bool *) record; - bool newval; - - return parse_bool(newvalue, &newval) - && *conf->variable == newval; - } - case PGC_INT: - { - struct config_int *conf = (struct config_int *) record; - int newval; - - return parse_int(newvalue, &newval, record->flags, NULL) - && *conf->variable == newval; - } - case PGC_REAL: - { - struct config_real *conf = (struct config_real *) record; - double newval; - - return parse_real(newvalue, &newval) - && *conf->variable == newval; - } - case PGC_STRING: - { - struct config_string *conf = (struct config_string *) record; - - return *conf->variable != NULL && - strcmp(*conf->variable, newvalue) == 0; - } - - case PGC_ENUM: - { - struct config_enum *conf = (struct config_enum *) record; - int newval; - - return config_enum_lookup_by_name(conf, newvalue, &newval) && - *conf->variable == newval; - } - } - - return false; -} - - #ifdef EXEC_BACKEND /* @@ -7673,30 +7918,222 @@ validate_option_array_item(const char *name, const char *value, /* - * assign_hook and show_hook subroutines + * Called by check_hooks that want to override the normal + * ERRCODE_INVALID_PARAMETER_VALUE SQLSTATE for check hook failures. + * + * Note that GUC_check_errmsg() etc are just macros that result in a direct + * assignment to the associated variables. That is ugly, but forced by the + * limitations of C's macro mechanisms. */ +void +GUC_check_errcode(int sqlerrcode) +{ + GUC_check_errcode_value = sqlerrcode; +} -static const char * -assign_log_destination(const char *value, bool doit, GucSource source) + +/* + * Convenience functions to manage calling a variable's check_hook. + * These mostly take care of the protocol for letting check hooks supply + * portions of the error report on failure. + */ + +static bool +call_bool_check_hook(struct config_bool *conf, bool *newval, void **extra, + GucSource source, int elevel) +{ + /* Quick success if no hook */ + if (!conf->check_hook) + return true; + + /* Reset variables that might be set by hook */ + GUC_check_errcode_value = ERRCODE_INVALID_PARAMETER_VALUE; + GUC_check_errmsg_string = NULL; + GUC_check_errdetail_string = NULL; + GUC_check_errhint_string = NULL; + + if (!(*conf->check_hook) (newval, extra, source)) + { + ereport(elevel, + (errcode(GUC_check_errcode_value), + GUC_check_errmsg_string ? + errmsg("%s", GUC_check_errmsg_string) : + errmsg("invalid value for parameter \"%s\": %d", + conf->gen.name, (int) *newval), + GUC_check_errdetail_string ? + errdetail("%s", GUC_check_errdetail_string) : 0, + GUC_check_errhint_string ? + errhint("%s", GUC_check_errhint_string) : 0)); + /* Flush any strings created in ErrorContext */ + FlushErrorState(); + return false; + } + + return true; +} + +static bool +call_int_check_hook(struct config_int *conf, int *newval, void **extra, + GucSource source, int elevel) +{ + /* Quick success if no hook */ + if (!conf->check_hook) + return true; + + /* Reset variables that might be set by hook */ + GUC_check_errcode_value = ERRCODE_INVALID_PARAMETER_VALUE; + GUC_check_errmsg_string = NULL; + GUC_check_errdetail_string = NULL; + GUC_check_errhint_string = NULL; + + if (!(*conf->check_hook) (newval, extra, source)) + { + ereport(elevel, + (errcode(GUC_check_errcode_value), + GUC_check_errmsg_string ? + errmsg("%s", GUC_check_errmsg_string) : + errmsg("invalid value for parameter \"%s\": %d", + conf->gen.name, *newval), + GUC_check_errdetail_string ? + errdetail("%s", GUC_check_errdetail_string) : 0, + GUC_check_errhint_string ? + errhint("%s", GUC_check_errhint_string) : 0)); + /* Flush any strings created in ErrorContext */ + FlushErrorState(); + return false; + } + + return true; +} + +static bool +call_real_check_hook(struct config_real *conf, double *newval, void **extra, + GucSource source, int elevel) +{ + /* Quick success if no hook */ + if (!conf->check_hook) + return true; + + /* Reset variables that might be set by hook */ + GUC_check_errcode_value = ERRCODE_INVALID_PARAMETER_VALUE; + GUC_check_errmsg_string = NULL; + GUC_check_errdetail_string = NULL; + GUC_check_errhint_string = NULL; + + if (!(*conf->check_hook) (newval, extra, source)) + { + ereport(elevel, + (errcode(GUC_check_errcode_value), + GUC_check_errmsg_string ? + errmsg("%s", GUC_check_errmsg_string) : + errmsg("invalid value for parameter \"%s\": %g", + conf->gen.name, *newval), + GUC_check_errdetail_string ? + errdetail("%s", GUC_check_errdetail_string) : 0, + GUC_check_errhint_string ? + errhint("%s", GUC_check_errhint_string) : 0)); + /* Flush any strings created in ErrorContext */ + FlushErrorState(); + return false; + } + + return true; +} + +static bool +call_string_check_hook(struct config_string *conf, char **newval, void **extra, + GucSource source, int elevel) +{ + /* Quick success if no hook */ + if (!conf->check_hook) + return true; + + /* Reset variables that might be set by hook */ + GUC_check_errcode_value = ERRCODE_INVALID_PARAMETER_VALUE; + GUC_check_errmsg_string = NULL; + GUC_check_errdetail_string = NULL; + GUC_check_errhint_string = NULL; + + if (!(*conf->check_hook) (newval, extra, source)) + { + ereport(elevel, + (errcode(GUC_check_errcode_value), + GUC_check_errmsg_string ? + errmsg("%s", GUC_check_errmsg_string) : + errmsg("invalid value for parameter \"%s\": \"%s\"", + conf->gen.name, *newval ? *newval : ""), + GUC_check_errdetail_string ? + errdetail("%s", GUC_check_errdetail_string) : 0, + GUC_check_errhint_string ? + errhint("%s", GUC_check_errhint_string) : 0)); + /* Flush any strings created in ErrorContext */ + FlushErrorState(); + return false; + } + + return true; +} + +static bool +call_enum_check_hook(struct config_enum *conf, int *newval, void **extra, + GucSource source, int elevel) +{ + /* Quick success if no hook */ + if (!conf->check_hook) + return true; + + /* Reset variables that might be set by hook */ + GUC_check_errcode_value = ERRCODE_INVALID_PARAMETER_VALUE; + GUC_check_errmsg_string = NULL; + GUC_check_errdetail_string = NULL; + GUC_check_errhint_string = NULL; + + if (!(*conf->check_hook) (newval, extra, source)) + { + ereport(elevel, + (errcode(GUC_check_errcode_value), + GUC_check_errmsg_string ? + errmsg("%s", GUC_check_errmsg_string) : + errmsg("invalid value for parameter \"%s\": \"%s\"", + conf->gen.name, + config_enum_lookup_by_value(conf, *newval)), + GUC_check_errdetail_string ? + errdetail("%s", GUC_check_errdetail_string) : 0, + GUC_check_errhint_string ? + errhint("%s", GUC_check_errhint_string) : 0)); + /* Flush any strings created in ErrorContext */ + FlushErrorState(); + return false; + } + + return true; +} + + +/* + * check_hook, assign_hook and show_hook subroutines + */ + +static bool +check_log_destination(char **newval, void **extra, GucSource source) { char *rawstring; List *elemlist; ListCell *l; int newlogdest = 0; + int *myextra; /* Need a modifiable copy of string */ - rawstring = pstrdup(value); + rawstring = pstrdup(*newval); /* Parse string into list of identifiers */ if (!SplitIdentifierString(rawstring, ',', &elemlist)) { /* syntax error in list */ + GUC_check_errdetail("List syntax is invalid."); pfree(rawstring); list_free(elemlist); - ereport(GUC_complaint_elevel(source), - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("invalid list syntax for parameter \"log_destination\""))); - return NULL; + return false; } foreach(l, elemlist) @@ -7717,105 +8154,103 @@ assign_log_destination(const char *value, bool doit, GucSource source) #endif else { - ereport(GUC_complaint_elevel(source), - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("unrecognized \"log_destination\" key word: \"%s\"", - tok))); + GUC_check_errdetail("Unrecognized key word: \"%s\".", tok); pfree(rawstring); list_free(elemlist); - return NULL; + return false; } } - if (doit) - Log_destination = newlogdest; - pfree(rawstring); list_free(elemlist); - return value; + myextra = (int *) guc_malloc(ERROR, sizeof(int)); + *myextra = newlogdest; + *extra = (void *) myextra; + + return true; } -static bool -assign_syslog_facility(int newval, bool doit, GucSource source) +static void +assign_log_destination(const char *newval, void *extra) +{ + Log_destination = *((int *) extra); +} + +static void +assign_syslog_facility(int newval, void *extra) { #ifdef HAVE_SYSLOG - if (doit) - set_syslog_parameters(syslog_ident_str ? syslog_ident_str : "postgres", - newval); + set_syslog_parameters(syslog_ident_str ? syslog_ident_str : "postgres", + newval); #endif /* Without syslog support, just ignore it */ - - return true; } -static const char * -assign_syslog_ident(const char *ident, bool doit, GucSource source) +static void +assign_syslog_ident(const char *newval, void *extra) { #ifdef HAVE_SYSLOG - if (doit) - set_syslog_parameters(ident, syslog_facility); + set_syslog_parameters(newval, syslog_facility); #endif /* Without syslog support, it will always be set to "none", so ignore */ - - return ident; } -static bool -assign_session_replication_role(int newval, bool doit, GucSource source) +static void +assign_session_replication_role(int newval, void *extra) { /* * Must flush the plan cache when changing replication role; but don't * flush unnecessarily. */ - if (doit && SessionReplicationRole != newval) - { + if (SessionReplicationRole != newval) ResetPlanCache(); - } - - return true; } -static const char * -show_num_temp_buffers(void) +static bool +check_temp_buffers(int *newval, void **extra, GucSource source) { /* - * We show the GUC var until local buffers have been initialized, and - * NLocBuffer afterwards. + * Once local buffers have been initialized, it's too late to change this. */ - static char nbuf[32]; - - sprintf(nbuf, "%d", NLocBuffer ? NLocBuffer : num_temp_buffers); - return nbuf; + if (NLocBuffer && NLocBuffer != *newval) + { + GUC_check_errdetail("\"temp_buffers\" cannot be changed after any temp tables have been accessed in the session."); + return false; + } + return true; } static bool -assign_phony_autocommit(bool newval, bool doit, GucSource source) +check_phony_autocommit(bool *newval, void **extra, GucSource source) { - if (!newval) + if (!*newval) { - ereport(GUC_complaint_elevel(source), - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("SET AUTOCOMMIT TO OFF is no longer supported"))); + GUC_check_errcode(ERRCODE_FEATURE_NOT_SUPPORTED); + GUC_check_errmsg("SET AUTOCOMMIT TO OFF is no longer supported"); return false; } return true; } -static const char * -assign_custom_variable_classes(const char *newval, bool doit, GucSource source) +static bool +check_custom_variable_classes(char **newval, void **extra, GucSource source) { /* * Check syntax. newval must be a comma separated list of identifiers. * Whitespace is allowed but removed from the result. */ bool hasSpaceAfterToken = false; - const char *cp = newval; + const char *cp = *newval; int symLen = 0; char c; StringInfoData buf; + /* Default NULL is OK */ + if (cp == NULL) + return true; + initStringInfo(&buf); while ((c = *cp++) != '\0') { @@ -7844,7 +8279,7 @@ assign_custom_variable_classes(const char *newval, bool doit, GucSource source) * non-identifier character */ pfree(buf.data); - return NULL; + return false; } appendStringInfoChar(&buf, c); symLen++; @@ -7855,21 +8290,20 @@ assign_custom_variable_classes(const char *newval, bool doit, GucSource source) buf.data[--buf.len] = '\0'; /* GUC wants the result malloc'd */ - newval = guc_strdup(LOG, buf.data); + free(*newval); + *newval = guc_strdup(LOG, buf.data); pfree(buf.data); - return newval; + return true; } static bool -assign_debug_assertions(bool newval, bool doit, GucSource source) +check_debug_assertions(bool *newval, void **extra, GucSource source) { #ifndef USE_ASSERT_CHECKING - if (newval) + if (*newval) { - ereport(GUC_complaint_elevel(source), - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("assertion checking is not supported by this build"))); + GUC_check_errmsg("assertion checking is not supported by this build"); return false; } #endif @@ -7877,14 +8311,12 @@ assign_debug_assertions(bool newval, bool doit, GucSource source) } static bool -assign_bonjour(bool newval, bool doit, GucSource source) +check_bonjour(bool *newval, void **extra, GucSource source) { #ifndef USE_BONJOUR - if (newval) + if (*newval) { - ereport(GUC_complaint_elevel(source), - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("Bonjour is not supported by this build"))); + GUC_check_errmsg("Bonjour is not supported by this build"); return false; } #endif @@ -7892,14 +8324,12 @@ assign_bonjour(bool newval, bool doit, GucSource source) } static bool -assign_ssl(bool newval, bool doit, GucSource source) +check_ssl(bool *newval, void **extra, GucSource source) { #ifndef USE_SSL - if (newval) + if (*newval) { - ereport(GUC_complaint_elevel(source), - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("SSL is not supported by this build"))); + GUC_check_errmsg("SSL is not supported by this build"); return false; } #endif @@ -7907,57 +8337,48 @@ assign_ssl(bool newval, bool doit, GucSource source) } static bool -assign_stage_log_stats(bool newval, bool doit, GucSource source) +check_stage_log_stats(bool *newval, void **extra, GucSource source) { - if (newval && log_statement_stats) + if (*newval && log_statement_stats) { - ereport(GUC_complaint_elevel(source), - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("cannot enable parameter when \"log_statement_stats\" is true"))); - /* source == PGC_S_OVERRIDE means do it anyway, eg at xact abort */ - if (source != PGC_S_OVERRIDE) - return false; + GUC_check_errdetail("Cannot enable parameter when \"log_statement_stats\" is true."); + return false; } return true; } static bool -assign_log_stats(bool newval, bool doit, GucSource source) +check_log_stats(bool *newval, void **extra, GucSource source) { - if (newval && + if (*newval && (log_parser_stats || log_planner_stats || log_executor_stats)) { - ereport(GUC_complaint_elevel(source), - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("cannot enable \"log_statement_stats\" when " - "\"log_parser_stats\", \"log_planner_stats\", " - "or \"log_executor_stats\" is true"))); - /* source == PGC_S_OVERRIDE means do it anyway, eg at xact abort */ - if (source != PGC_S_OVERRIDE) - return false; + GUC_check_errdetail("Cannot enable \"log_statement_stats\" when " + "\"log_parser_stats\", \"log_planner_stats\", " + "or \"log_executor_stats\" is true."); + return false; } return true; } -static const char * -assign_canonical_path(const char *newval, bool doit, GucSource source) +static bool +check_canonical_path(char **newval, void **extra, GucSource source) { - if (doit) - { - char *canon_val = guc_strdup(ERROR, newval); - - canonicalize_path(canon_val); - return canon_val; - } - else - return newval; + /* + * Since canonicalize_path never enlarges the string, we can just + * modify newval in-place. But watch out for NULL, which is the + * default value for external_pid_file. + */ + if (*newval) + canonicalize_path(*newval); + return true; } -static const char * -assign_timezone_abbreviations(const char *newval, bool doit, GucSource source) +static bool +check_timezone_abbreviations(char **newval, void **extra, GucSource source) { /* - * The powerup value shown above for timezone_abbreviations is "UNKNOWN". + * The boot_val given above for timezone_abbreviations is NULL. * When we see this we just do nothing. If this value isn't overridden * from the config file then pg_timezone_abbrev_initialize() will * eventually replace it with "Default". This hack has two purposes: to @@ -7965,34 +8386,32 @@ assign_timezone_abbreviations(const char *newval, bool doit, GucSource source) * the config file, and to avoid trying to read the timezone abbrev files * during InitializeGUCOptions(). The latter doesn't work in an * EXEC_BACKEND subprocess because my_exec_path hasn't been set yet and so - * we can't locate PGSHAREDIR. (Essentially the same hack is used to - * delay initializing TimeZone ... if we have any more, we should try to - * clean up and centralize this mechanism ...) + * we can't locate PGSHAREDIR. */ - if (strcmp(newval, "UNKNOWN") == 0) + if (*newval == NULL) { - return newval; + Assert(source == PGC_S_DEFAULT); + return true; } - /* Loading abbrev file is expensive, so only do it when value changes */ - if (timezone_abbreviations_string == NULL || - strcmp(timezone_abbreviations_string, newval) != 0) - { - int elevel; + /* OK, load the file and produce a malloc'd TimeZoneAbbrevTable */ + *extra = load_tzoffsets(*newval); - /* - * If reading config file, only the postmaster should bleat loudly - * about problems. Otherwise, it's just this one process doing it, - * and we use WARNING message level. - */ - if (source == PGC_S_FILE) - elevel = IsUnderPostmaster ? DEBUG3 : LOG; - else - elevel = WARNING; - if (!load_tzoffsets(newval, doit, elevel)) - return NULL; - } - return newval; + /* tzparser.c returns NULL on failure, reporting via GUC_check_errmsg */ + if (!*extra) + return false; + + return true; +} + +static void +assign_timezone_abbreviations(const char *newval, void *extra) +{ + /* Do nothing for the boot_val default of NULL */ + if (!extra) + return; + + InstallTimeZoneAbbrevs((TimeZoneAbbrevTable *) extra); } /* @@ -8004,10 +8423,10 @@ assign_timezone_abbreviations(const char *newval, bool doit, GucSource source) void pg_timezone_abbrev_initialize(void) { - if (strcmp(timezone_abbreviations_string, "UNKNOWN") == 0) + if (timezone_abbreviations_string == NULL) { SetConfigOption("timezone_abbreviations", "Default", - PGC_POSTMASTER, PGC_S_ARGV); + PGC_POSTMASTER, PGC_S_DEFAULT); } } @@ -8020,54 +8439,61 @@ show_archive_command(void) return "(disabled)"; } -static bool -assign_tcp_keepalives_idle(int newval, bool doit, GucSource source) +static void +assign_tcp_keepalives_idle(int newval, void *extra) { - if (doit) - return (pq_setkeepalivesidle(newval, MyProcPort) == STATUS_OK); - - return true; + /* + * The kernel API provides no way to test a value without setting it; + * and once we set it we might fail to unset it. So there seems little + * point in fully implementing the check-then-assign GUC API for these + * variables. Instead we just do the assignment on demand. pqcomm.c + * reports any problems via elog(LOG). + * + * This approach means that the GUC value might have little to do with + * the actual kernel value, so we use a show_hook that retrieves the + * kernel value rather than trusting GUC's copy. + */ + (void) pq_setkeepalivesidle(newval, MyProcPort); } static const char * show_tcp_keepalives_idle(void) { + /* See comments in assign_tcp_keepalives_idle */ static char nbuf[16]; snprintf(nbuf, sizeof(nbuf), "%d", pq_getkeepalivesidle(MyProcPort)); return nbuf; } -static bool -assign_tcp_keepalives_interval(int newval, bool doit, GucSource source) +static void +assign_tcp_keepalives_interval(int newval, void *extra) { - if (doit) - return (pq_setkeepalivesinterval(newval, MyProcPort) == STATUS_OK); - - return true; + /* See comments in assign_tcp_keepalives_idle */ + (void) pq_setkeepalivesinterval(newval, MyProcPort); } static const char * show_tcp_keepalives_interval(void) { + /* See comments in assign_tcp_keepalives_idle */ static char nbuf[16]; snprintf(nbuf, sizeof(nbuf), "%d", pq_getkeepalivesinterval(MyProcPort)); return nbuf; } -static bool -assign_tcp_keepalives_count(int newval, bool doit, GucSource source) +static void +assign_tcp_keepalives_count(int newval, void *extra) { - if (doit) - return (pq_setkeepalivescount(newval, MyProcPort) == STATUS_OK); - - return true; + /* See comments in assign_tcp_keepalives_idle */ + (void) pq_setkeepalivescount(newval, MyProcPort); } static const char * show_tcp_keepalives_count(void) { + /* See comments in assign_tcp_keepalives_idle */ static char nbuf[16]; snprintf(nbuf, sizeof(nbuf), "%d", pq_getkeepalivescount(MyProcPort)); @@ -8075,31 +8501,35 @@ show_tcp_keepalives_count(void) } static bool -assign_maxconnections(int newval, bool doit, GucSource source) +check_maxconnections(int *newval, void **extra, GucSource source) { - if (newval + autovacuum_max_workers + 1 > MAX_BACKENDS) + if (*newval + autovacuum_max_workers + 1 > MAX_BACKENDS) return false; - - if (doit) - MaxBackends = newval + autovacuum_max_workers + 1; - return true; } +static void +assign_maxconnections(int newval, void *extra) +{ + MaxBackends = newval + autovacuum_max_workers + 1; +} + static bool -assign_autovacuum_max_workers(int newval, bool doit, GucSource source) +check_autovacuum_max_workers(int *newval, void **extra, GucSource source) { - if (MaxConnections + newval + 1 > MAX_BACKENDS) + if (MaxConnections + *newval + 1 > MAX_BACKENDS) return false; - - if (doit) - MaxBackends = MaxConnections + newval + 1; - return true; } +static void +assign_autovacuum_max_workers(int newval, void *extra) +{ + MaxBackends = MaxConnections + newval + 1; +} + static bool -assign_effective_io_concurrency(int newval, bool doit, GucSource source) +check_effective_io_concurrency(int *newval, void **extra, GucSource source) { #ifdef USE_PREFETCH double new_prefetch_pages = 0.0; @@ -8108,6 +8538,8 @@ assign_effective_io_concurrency(int newval, bool doit, GucSource source) /*---------- * The user-visible GUC parameter is the number of drives (spindles), * which we need to translate to a number-of-pages-to-prefetch target. + * The target value is stashed in *extra and then assigned to the actual + * variable by assign_effective_io_concurrency. * * The expected number of prefetch pages needed to keep N drives busy is: * @@ -8132,18 +8564,21 @@ assign_effective_io_concurrency(int newval, bool doit, GucSource source) * Experimental results show that both of these formulas aren't aggressive * enough, but we don't really have any better proposals. * - * Note that if newval = 0 (disabled), we must set target = 0. + * Note that if *newval = 0 (disabled), we must set target = 0. *---------- */ - for (i = 1; i <= newval; i++) - new_prefetch_pages += (double) newval / (double) i; + for (i = 1; i <= *newval; i++) + new_prefetch_pages += (double) *newval / (double) i; /* This range check shouldn't fail, but let's be paranoid */ if (new_prefetch_pages >= 0.0 && new_prefetch_pages < (double) INT_MAX) { - if (doit) - target_prefetch_pages = (int) rint(new_prefetch_pages); + int *myextra = (int *) guc_malloc(ERROR, sizeof(int)); + + *myextra = (int) rint(new_prefetch_pages); + *extra = (void *) myextra; + return true; } else @@ -8153,57 +8588,54 @@ assign_effective_io_concurrency(int newval, bool doit, GucSource source) #endif /* USE_PREFETCH */ } -static const char * -assign_pgstat_temp_directory(const char *newval, bool doit, GucSource source) +static void +assign_effective_io_concurrency(int newval, void *extra) { - if (doit) - { - char *canon_val = guc_strdup(ERROR, newval); - char *tname; - char *fname; - - canonicalize_path(canon_val); - - tname = guc_malloc(ERROR, strlen(canon_val) + 12); /* /pgstat.tmp */ - sprintf(tname, "%s/pgstat.tmp", canon_val); - fname = guc_malloc(ERROR, strlen(canon_val) + 13); /* /pgstat.stat */ - sprintf(fname, "%s/pgstat.stat", canon_val); +#ifdef USE_PREFETCH + target_prefetch_pages = *((int *) extra); +#endif /* USE_PREFETCH */ +} - if (pgstat_stat_tmpname) - free(pgstat_stat_tmpname); - pgstat_stat_tmpname = tname; - if (pgstat_stat_filename) - free(pgstat_stat_filename); - pgstat_stat_filename = fname; +static void +assign_pgstat_temp_directory(const char *newval, void *extra) +{ + /* check_canonical_path already canonicalized newval for us */ + char *tname; + char *fname; - return canon_val; - } - else - return newval; + tname = guc_malloc(ERROR, strlen(newval) + 12); /* /pgstat.tmp */ + sprintf(tname, "%s/pgstat.tmp", newval); + fname = guc_malloc(ERROR, strlen(newval) + 13); /* /pgstat.stat */ + sprintf(fname, "%s/pgstat.stat", newval); + + if (pgstat_stat_tmpname) + free(pgstat_stat_tmpname); + pgstat_stat_tmpname = tname; + if (pgstat_stat_filename) + free(pgstat_stat_filename); + pgstat_stat_filename = fname; } -static const char * -assign_application_name(const char *newval, bool doit, GucSource source) +static bool +check_application_name(char **newval, void **extra, GucSource source) { - if (doit) - { - /* Only allow clean ASCII chars in the application name */ - char *repval = guc_strdup(ERROR, newval); - char *p; + /* Only allow clean ASCII chars in the application name */ + char *p; - for (p = repval; *p; p++) - { - if (*p < 32 || *p > 126) - *p = '?'; - } + for (p = *newval; *p; p++) + { + if (*p < 32 || *p > 126) + *p = '?'; + } - /* Update the pg_stat_activity view */ - pgstat_report_appname(repval); + return true; +} - return repval; - } - else - return newval; +static void +assign_application_name(const char *newval, void *extra) +{ + /* Update the pg_stat_activity view */ + pgstat_report_appname(newval); } static const char * diff --git a/src/backend/utils/misc/tzparser.c b/src/backend/utils/misc/tzparser.c index e63ce58e69aecf2605c4c101ed93be01e49e42fa..b2f6dd3a2bbdaf189b61e40e93e14227cde01061 100644 --- a/src/backend/utils/misc/tzparser.c +++ b/src/backend/utils/misc/tzparser.c @@ -3,10 +3,12 @@ * tzparser.c * Functions for parsing timezone offset files * - * Note: we generally should not throw any errors in this file, but instead - * try to return an error code. This is not completely bulletproof at - * present --- in particular out-of-memory will throw an error. Could - * probably fix with PG_TRY if necessary. + * Note: this code is invoked from the check_hook for the GUC variable + * timezone_abbreviations. Therefore, it should report problems using + * GUC_check_errmsg() and related functions, and try to avoid throwing + * elog(ERROR). This is not completely bulletproof at present --- in + * particular out-of-memory will throw an error. Could probably fix with + * PG_TRY if necessary. * * * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group @@ -24,15 +26,13 @@ #include "miscadmin.h" #include "storage/fd.h" -#include "utils/datetime.h" +#include "utils/guc.h" #include "utils/memutils.h" #include "utils/tzparser.h" #define WHITESPACE " \t\n\r" -static int tz_elevel; /* to avoid passing this around a lot */ - static bool validateTzEntry(tzEntry *tzentry); static bool splitTzLine(const char *filename, int lineno, char *line, tzEntry *tzentry); @@ -58,20 +58,16 @@ validateTzEntry(tzEntry *tzentry) */ if (strlen(tzentry->abbrev) > TOKMAXLEN) { - ereport(tz_elevel, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("time zone abbreviation \"%s\" is too long (maximum %d characters) in time zone file \"%s\", line %d", - tzentry->abbrev, TOKMAXLEN, - tzentry->filename, tzentry->lineno))); + GUC_check_errmsg("time zone abbreviation \"%s\" is too long (maximum %d characters) in time zone file \"%s\", line %d", + tzentry->abbrev, TOKMAXLEN, + tzentry->filename, tzentry->lineno); return false; } if (tzentry->offset % 900 != 0) { - ereport(tz_elevel, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("time zone offset %d is not a multiple of 900 sec (15 min) in time zone file \"%s\", line %d", - tzentry->offset, - tzentry->filename, tzentry->lineno))); + GUC_check_errmsg("time zone offset %d is not a multiple of 900 sec (15 min) in time zone file \"%s\", line %d", + tzentry->offset, + tzentry->filename, tzentry->lineno); return false; } @@ -81,11 +77,9 @@ validateTzEntry(tzEntry *tzentry) if (tzentry->offset > 14 * 60 * 60 || tzentry->offset < -14 * 60 * 60) { - ereport(tz_elevel, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("time zone offset %d is out of range in time zone file \"%s\", line %d", - tzentry->offset, - tzentry->filename, tzentry->lineno))); + GUC_check_errmsg("time zone offset %d is out of range in time zone file \"%s\", line %d", + tzentry->offset, + tzentry->filename, tzentry->lineno); return false; } @@ -118,10 +112,8 @@ splitTzLine(const char *filename, int lineno, char *line, tzEntry *tzentry) abbrev = strtok(line, WHITESPACE); if (!abbrev) { - ereport(tz_elevel, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("missing time zone abbreviation in time zone file \"%s\", line %d", - filename, lineno))); + GUC_check_errmsg("missing time zone abbreviation in time zone file \"%s\", line %d", + filename, lineno); return false; } tzentry->abbrev = abbrev; @@ -129,19 +121,15 @@ splitTzLine(const char *filename, int lineno, char *line, tzEntry *tzentry) offset = strtok(NULL, WHITESPACE); if (!offset) { - ereport(tz_elevel, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("missing time zone offset in time zone file \"%s\", line %d", - filename, lineno))); + GUC_check_errmsg("missing time zone offset in time zone file \"%s\", line %d", + filename, lineno); return false; } tzentry->offset = strtol(offset, &offset_endptr, 10); if (offset_endptr == offset || *offset_endptr != '\0') { - ereport(tz_elevel, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("invalid number for time zone offset in time zone file \"%s\", line %d", - filename, lineno))); + GUC_check_errmsg("invalid number for time zone offset in time zone file \"%s\", line %d", + filename, lineno); return false; } @@ -163,10 +151,8 @@ splitTzLine(const char *filename, int lineno, char *line, tzEntry *tzentry) if (remain[0] != '#') /* must be a comment */ { - ereport(tz_elevel, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("invalid syntax in time zone file \"%s\", line %d", - filename, lineno))); + GUC_check_errmsg("invalid syntax in time zone file \"%s\", line %d", + filename, lineno); return false; } return true; @@ -229,13 +215,11 @@ addToArray(tzEntry **base, int *arraysize, int n, return n; } /* same abbrev but something is different, complain */ - ereport(tz_elevel, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("time zone abbreviation \"%s\" is multiply defined", - entry->abbrev), - errdetail("Entry in time zone file \"%s\", line %d, conflicts with entry in file \"%s\", line %d.", - midptr->filename, midptr->lineno, - entry->filename, entry->lineno))); + GUC_check_errmsg("time zone abbreviation \"%s\" is multiply defined", + entry->abbrev); + GUC_check_errdetail("Entry in time zone file \"%s\", line %d, conflicts with entry in file \"%s\", line %d.", + midptr->filename, midptr->lineno, + entry->filename, entry->lineno); return -1; } } @@ -296,12 +280,10 @@ ParseTzFile(const char *filename, int depth, { if (!isalpha((unsigned char) *p)) { - /* at level 0, we need no ereport since guc.c will say enough */ + /* at level 0, just use guc.c's regular "invalid value" message */ if (depth > 0) - ereport(tz_elevel, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("invalid time zone file name \"%s\"", - filename))); + GUC_check_errmsg("invalid time zone file name \"%s\"", + filename); return -1; } } @@ -313,10 +295,8 @@ ParseTzFile(const char *filename, int depth, */ if (depth > 3) { - ereport(tz_elevel, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("time zone file recursion limit exceeded in file \"%s\"", - filename))); + GUC_check_errmsg("time zone file recursion limit exceeded in file \"%s\"", + filename); return -1; } @@ -340,12 +320,10 @@ ParseTzFile(const char *filename, int depth, tzdir = AllocateDir(file_path); if (tzdir == NULL) { - ereport(tz_elevel, - (errcode_for_file_access(), - errmsg("could not open directory \"%s\": %m", - file_path), - errhint("This may indicate an incomplete PostgreSQL installation, or that the file \"%s\" has been moved away from its proper location.", - my_exec_path))); + GUC_check_errmsg("could not open directory \"%s\": %m", + file_path); + GUC_check_errhint("This may indicate an incomplete PostgreSQL installation, or that the file \"%s\" has been moved away from its proper location.", + my_exec_path); return -1; } FreeDir(tzdir); @@ -356,10 +334,8 @@ ParseTzFile(const char *filename, int depth, * complaint is enough */ if (errno != ENOENT || depth > 0) - ereport(tz_elevel, - (errcode_for_file_access(), - errmsg("could not read time zone file \"%s\": %m", - filename))); + GUC_check_errmsg("could not read time zone file \"%s\": %m", + filename); return -1; } @@ -371,10 +347,8 @@ ParseTzFile(const char *filename, int depth, { if (ferror(tzFile)) { - ereport(tz_elevel, - (errcode_for_file_access(), - errmsg("could not read time zone file \"%s\": %m", - filename))); + GUC_check_errmsg("could not read time zone file \"%s\": %m", + filename); return -1; } /* else we're at EOF after all */ @@ -383,10 +357,8 @@ ParseTzFile(const char *filename, int depth, if (strlen(tzbuf) == sizeof(tzbuf) - 1) { /* the line is too long for tzbuf */ - ereport(tz_elevel, - (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), - errmsg("line is too long in time zone file \"%s\", line %d", - filename, lineno))); + GUC_check_errmsg("line is too long in time zone file \"%s\", line %d", + filename, lineno); return -1; } @@ -408,10 +380,8 @@ ParseTzFile(const char *filename, int depth, includeFile = strtok(includeFile, WHITESPACE); if (!includeFile || !*includeFile) { - ereport(tz_elevel, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("@INCLUDE without file name in time zone file \"%s\", line %d", - filename, lineno))); + GUC_check_errmsg("@INCLUDE without file name in time zone file \"%s\", line %d", + filename, lineno); return -1; } n = ParseTzFile(includeFile, depth + 1, @@ -444,23 +414,20 @@ ParseTzFile(const char *filename, int depth, /* * load_tzoffsets --- read and parse the specified timezone offset file * - * filename: name specified by user - * doit: whether to actually apply the new values, or just check - * elevel: elog reporting level (will be less than ERROR) - * - * Returns TRUE if OK, FALSE if not; should avoid erroring out + * On success, return a filled-in TimeZoneAbbrevTable, which must have been + * malloc'd not palloc'd. On failure, return NULL, using GUC_check_errmsg + * and friends to give details of the problem. */ -bool -load_tzoffsets(const char *filename, bool doit, int elevel) +TimeZoneAbbrevTable * +load_tzoffsets(const char *filename) { + TimeZoneAbbrevTable *result = NULL; MemoryContext tmpContext; MemoryContext oldContext; tzEntry *array; int arraysize; int n; - tz_elevel = elevel; - /* * Create a temp memory context to work in. This makes it easy to clean * up afterwards. @@ -479,13 +446,20 @@ load_tzoffsets(const char *filename, bool doit, int elevel) /* Parse the file(s) */ n = ParseTzFile(filename, 0, &array, &arraysize, 0); - /* If no errors and we should apply the result, pass it to datetime.c */ - if (n >= 0 && doit) - InstallTimeZoneAbbrevs(array, n); + /* If no errors so far, allocate result and let datetime.c convert data */ + if (n >= 0) + { + result = malloc(offsetof(TimeZoneAbbrevTable, abbrevs) + + n * sizeof(datetkn)); + if (!result) + GUC_check_errmsg("out of memory"); + else + ConvertTimeZoneAbbrevs(result, array, n); + } /* Clean up */ MemoryContextSwitchTo(oldContext); MemoryContextDelete(tmpContext); - return (n >= 0); + return result; } diff --git a/src/include/commands/variable.h b/src/include/commands/variable.h index 39bccbd5bf616c1b273ee32fe04009a0f5a7853d..1d904ff984bf7e96e5b2e621dd65808489939429 100644 --- a/src/include/commands/variable.h +++ b/src/include/commands/variable.h @@ -13,31 +13,28 @@ #include "utils/guc.h" -extern const char *assign_datestyle(const char *value, - bool doit, GucSource source); -extern const char *assign_timezone(const char *value, - bool doit, GucSource source); +extern bool check_datestyle(char **newval, void **extra, GucSource source); +extern void assign_datestyle(const char *newval, void *extra); +extern bool check_timezone(char **newval, void **extra, GucSource source); +extern void assign_timezone(const char *newval, void *extra); extern const char *show_timezone(void); -extern const char *assign_log_timezone(const char *value, - bool doit, GucSource source); +extern bool check_log_timezone(char **newval, void **extra, GucSource source); +extern void assign_log_timezone(const char *newval, void *extra); extern const char *show_log_timezone(void); -extern bool assign_transaction_read_only(bool value, - bool doit, GucSource source); -extern const char *assign_XactIsoLevel(const char *value, - bool doit, GucSource source); +extern bool check_transaction_read_only(bool *newval, void **extra, GucSource source); +extern bool check_XactIsoLevel(char **newval, void **extra, GucSource source); +extern void assign_XactIsoLevel(const char *newval, void *extra); extern const char *show_XactIsoLevel(void); -extern bool assign_transaction_deferrable(bool newval, bool doit, - GucSource source); -extern bool assign_random_seed(double value, - bool doit, GucSource source); +extern bool check_transaction_deferrable(bool *newval, void **extra, GucSource source); +extern bool check_random_seed(double *newval, void **extra, GucSource source); +extern void assign_random_seed(double newval, void *extra); extern const char *show_random_seed(void); -extern const char *assign_client_encoding(const char *value, - bool doit, GucSource source); -extern const char *assign_role(const char *value, - bool doit, GucSource source); +extern bool check_client_encoding(char **newval, void **extra, GucSource source); +extern void assign_client_encoding(const char *newval, void *extra); +extern bool check_session_authorization(char **newval, void **extra, GucSource source); +extern void assign_session_authorization(const char *newval, void *extra); +extern bool check_role(char **newval, void **extra, GucSource source); +extern void assign_role(const char *newval, void *extra); extern const char *show_role(void); -extern const char *assign_session_authorization(const char *value, - bool doit, GucSource source); -extern const char *show_session_authorization(void); #endif /* VARIABLE_H */ diff --git a/src/include/mb/pg_wchar.h b/src/include/mb/pg_wchar.h index 85a7b2f87dd257ec5bd4e553299017b630811a55..8efc6d30464bfc4c71bed1b1bc6dbc03376f7b0f 100644 --- a/src/include/mb/pg_wchar.h +++ b/src/include/mb/pg_wchar.h @@ -397,7 +397,8 @@ extern size_t wchar2char(char *to, const wchar_t *from, size_t tolen, Oid collat extern size_t char2wchar(wchar_t *to, size_t tolen, const char *from, size_t fromlen, Oid collation); #endif -extern int SetClientEncoding(int encoding, bool doit); +extern int PrepareClientEncoding(int encoding); +extern int SetClientEncoding(int encoding); extern void InitializeClientEncoding(void); extern int pg_get_client_encoding(void); extern const char *pg_get_client_encoding_name(void); diff --git a/src/include/replication/syncrep.h b/src/include/replication/syncrep.h index 9022b96585536410f86d1eb657b5128974929a0a..728e2c8f2d2772b6d5704228fdc3abbe14483f0c 100644 --- a/src/include/replication/syncrep.h +++ b/src/include/replication/syncrep.h @@ -45,6 +45,6 @@ extern void SyncRepUpdateSyncStandbysDefined(void); /* called by various procs */ extern int SyncRepWakeQueue(bool all); -extern const char *assign_synchronous_standby_names(const char *newval, bool doit, GucSource source); +extern bool check_synchronous_standby_names(char **newval, void **extra, GucSource source); #endif /* _SYNCREP_H */ diff --git a/src/include/tcop/tcopprot.h b/src/include/tcop/tcopprot.h index bf21cd9d06f110b9b6f56597c3f8f9bd0c7d73dd..d5192d98558de6f79a4e1015e3d79cbf68de25b4 100644 --- a/src/include/tcop/tcopprot.h +++ b/src/include/tcop/tcopprot.h @@ -57,7 +57,8 @@ extern PlannedStmt *pg_plan_query(Query *querytree, int cursorOptions, extern List *pg_plan_queries(List *querytrees, int cursorOptions, ParamListInfo boundParams); -extern bool assign_max_stack_depth(int newval, bool doit, GucSource source); +extern bool check_max_stack_depth(int *newval, void **extra, GucSource source); +extern void assign_max_stack_depth(int newval, void *extra); extern void die(SIGNAL_ARGS); extern void quickdie(SIGNAL_ARGS); diff --git a/src/include/tsearch/ts_cache.h b/src/include/tsearch/ts_cache.h index 5ae38b2a43ff4b91ff0fda4fe1b2c1a8b5aad7b5..01550eddfe3c412d9fc9076d3659193a0edd386c 100644 --- a/src/include/tsearch/ts_cache.h +++ b/src/include/tsearch/ts_cache.h @@ -93,6 +93,7 @@ extern TSDictionaryCacheEntry *lookup_ts_dictionary_cache(Oid dictId); extern TSConfigCacheEntry *lookup_ts_config_cache(Oid cfgId); extern Oid getTSCurrentConfig(bool emitError); -extern const char *assignTSCurrentConfig(const char *newval, bool doit, GucSource source); +extern bool check_TSCurrentConfig(char **newval, void **extra, GucSource source); +extern void assign_TSCurrentConfig(const char *newval, void *extra); #endif /* TS_CACHE_H */ diff --git a/src/include/utils/datetime.h b/src/include/utils/datetime.h index 11672458f4c2da498c4a2cff744b5681e36090fd..9911d2079364f98fdf6b2e077d59def8c3498e69 100644 --- a/src/include/utils/datetime.h +++ b/src/include/utils/datetime.h @@ -20,7 +20,9 @@ #include <math.h> #include "utils/timestamp.h" -#include "utils/tzparser.h" + +/* this struct is declared in utils/tzparser.h: */ +struct tzEntry; /* ---------------------------------------------------------------- @@ -203,6 +205,13 @@ typedef struct char value; /* this may be unsigned, alas */ } datetkn; +/* one of its uses is in tables of time zone abbreviations */ +typedef struct TimeZoneAbbrevTable +{ + int numabbrevs; + datetkn abbrevs[1]; /* VARIABLE LENGTH ARRAY */ +} TimeZoneAbbrevTable; + /* FMODULO() * Macro to replace modf(), which is broken on some platforms. @@ -317,7 +326,10 @@ extern int DecodeUnits(int field, char *lowtoken, int *val); extern int j2day(int jd); extern bool CheckDateTokenTables(void); -extern void InstallTimeZoneAbbrevs(tzEntry *abbrevs, int n); + +extern void ConvertTimeZoneAbbrevs(TimeZoneAbbrevTable *tbl, + struct tzEntry *abbrevs, int n); +extern void InstallTimeZoneAbbrevs(TimeZoneAbbrevTable *tbl); extern Datum pg_timezone_abbrevs(PG_FUNCTION_ARGS); extern Datum pg_timezone_names(PG_FUNCTION_ARGS); diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h index 71a1cdbd782fe5006a979862c2a5ef69f6d890ab..1f2a9f53c14934e901d3ea6fbc23b7b8de095801 100644 --- a/src/include/utils/elog.h +++ b/src/include/utils/elog.h @@ -200,6 +200,15 @@ elog_finish(int elevel, const char *fmt,...) __attribute__((format(printf, 2, 3))); +/* Support for constructing error strings separately from ereport() calls */ + +extern void pre_format_elog_string(int errnumber, const char *domain); +extern char *format_elog_string(const char *fmt,...) +/* This extension allows gcc to check the format string for consistency with + the supplied arguments. */ +__attribute__((format(printf, 1, 2))); + + /* Support for attaching context information to error reports */ typedef struct ErrorContextCallback diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h index c07f263ea073b0dab52c0f3960904e0767cceb45..452310f2bdfe8d6d0f0b6e17470bbb99a4a95b7a 100644 --- a/src/include/utils/guc.h +++ b/src/include/utils/guc.h @@ -97,7 +97,8 @@ typedef enum } GucSource; /* - * Parsing the configuation file will return a list of name-value pairs + * Parsing the configuration file will return a list of name-value pairs + * with source location info. */ typedef struct ConfigVariable { @@ -117,7 +118,9 @@ extern bool ParseConfigFp(FILE *fp, const char *config_file, extern void FreeConfigVariables(ConfigVariable *list); /* - * Enum values are made up of an array of name-value pairs + * The possible values of an enum variable are specified by an array of + * name-value pairs. The "hidden" flag means the value is accepted but + * won't be displayed when guc.c is asked for a list of acceptable values. */ struct config_enum_entry { @@ -126,15 +129,26 @@ struct config_enum_entry bool hidden; }; - -typedef const char *(*GucStringAssignHook) (const char *newval, bool doit, GucSource source); -typedef bool (*GucBoolAssignHook) (bool newval, bool doit, GucSource source); -typedef bool (*GucIntAssignHook) (int newval, bool doit, GucSource source); -typedef bool (*GucRealAssignHook) (double newval, bool doit, GucSource source); -typedef bool (*GucEnumAssignHook) (int newval, bool doit, GucSource source); +/* + * Signatures for per-variable check/assign/show hook functions + */ +typedef bool (*GucBoolCheckHook) (bool *newval, void **extra, GucSource source); +typedef bool (*GucIntCheckHook) (int *newval, void **extra, GucSource source); +typedef bool (*GucRealCheckHook) (double *newval, void **extra, GucSource source); +typedef bool (*GucStringCheckHook) (char **newval, void **extra, GucSource source); +typedef bool (*GucEnumCheckHook) (int *newval, void **extra, GucSource source); + +typedef void (*GucBoolAssignHook) (bool newval, void *extra); +typedef void (*GucIntAssignHook) (int newval, void *extra); +typedef void (*GucRealAssignHook) (double newval, void *extra); +typedef void (*GucStringAssignHook) (const char *newval, void *extra); +typedef void (*GucEnumAssignHook) (int newval, void *extra); typedef const char *(*GucShowHook) (void); +/* + * Miscellaneous + */ typedef enum { /* Types of set_config_option actions */ @@ -201,7 +215,6 @@ extern char *ConfigFileName; extern char *HbaFileName; extern char *IdentFileName; extern char *external_pid_file; -extern char *XactIsoLevel_string; extern char *application_name; @@ -209,6 +222,9 @@ extern int tcp_keepalives_idle; extern int tcp_keepalives_interval; extern int tcp_keepalives_count; +/* + * Functions exported by guc.c + */ extern void SetConfigOption(const char *name, const char *value, GucContext context, GucSource source); @@ -220,6 +236,7 @@ extern void DefineCustomBoolVariable( bool bootValue, GucContext context, int flags, + GucBoolCheckHook check_hook, GucBoolAssignHook assign_hook, GucShowHook show_hook); @@ -233,6 +250,7 @@ extern void DefineCustomIntVariable( int maxValue, GucContext context, int flags, + GucIntCheckHook check_hook, GucIntAssignHook assign_hook, GucShowHook show_hook); @@ -246,6 +264,7 @@ extern void DefineCustomRealVariable( double maxValue, GucContext context, int flags, + GucRealCheckHook check_hook, GucRealAssignHook assign_hook, GucShowHook show_hook); @@ -257,6 +276,7 @@ extern void DefineCustomStringVariable( const char *bootValue, GucContext context, int flags, + GucStringCheckHook check_hook, GucStringAssignHook assign_hook, GucShowHook show_hook); @@ -269,6 +289,7 @@ extern void DefineCustomEnumVariable( const struct config_enum_entry * options, GucContext context, int flags, + GucEnumCheckHook check_hook, GucEnumAssignHook assign_hook, GucShowHook show_hook); @@ -308,8 +329,6 @@ extern ArrayType *GUCArrayAdd(ArrayType *array, const char *name, const char *va extern ArrayType *GUCArrayDelete(ArrayType *array, const char *name); extern ArrayType *GUCArrayReset(ArrayType *array); -extern int GUC_complaint_elevel(GucSource source); - extern void pg_timezone_abbrev_initialize(void); #ifdef EXEC_BACKEND @@ -317,6 +336,27 @@ extern void write_nondefault_variables(GucContext context); extern void read_nondefault_variables(void); #endif +/* Support for messages reported from GUC check hooks */ + +extern PGDLLIMPORT char *GUC_check_errmsg_string; +extern PGDLLIMPORT char *GUC_check_errdetail_string; +extern PGDLLIMPORT char *GUC_check_errhint_string; + +extern void GUC_check_errcode(int sqlerrcode); + +#define GUC_check_errmsg \ + pre_format_elog_string(errno, TEXTDOMAIN), \ + GUC_check_errmsg_string = format_elog_string + +#define GUC_check_errdetail \ + pre_format_elog_string(errno, TEXTDOMAIN), \ + GUC_check_errdetail_string = format_elog_string + +#define GUC_check_errhint \ + pre_format_elog_string(errno, TEXTDOMAIN), \ + GUC_check_errhint_string = format_elog_string + + /* * The following functions are not in guc.c, but are declared here to avoid * having to include guc.h in some widely used headers that it really doesn't @@ -324,17 +364,16 @@ extern void read_nondefault_variables(void); */ /* in commands/tablespace.c */ -extern const char *assign_default_tablespace(const char *newval, - bool doit, GucSource source); -extern const char *assign_temp_tablespaces(const char *newval, - bool doit, GucSource source); +extern bool check_default_tablespace(char **newval, void **extra, GucSource source); +extern bool check_temp_tablespaces(char **newval, void **extra, GucSource source); +extern void assign_temp_tablespaces(const char *newval, void *extra); /* in catalog/namespace.c */ -extern const char *assign_search_path(const char *newval, - bool doit, GucSource source); +extern bool check_search_path(char **newval, void **extra, GucSource source); +extern void assign_search_path(const char *newval, void *extra); /* in access/transam/xlog.c */ -extern bool assign_xlog_sync_method(int newval, - bool doit, GucSource source); +extern bool check_wal_buffers(int *newval, void **extra, GucSource source); +extern void assign_xlog_sync_method(int new_sync_method, void *extra); #endif /* GUC_H */ diff --git a/src/include/utils/guc_tables.h b/src/include/utils/guc_tables.h index 073b77d28ce60c11d45c4d0a8651e8a506997245..a1ca012ef9a7b8aade5bf50106f3d6d6bf6c95b1 100644 --- a/src/include/utils/guc_tables.h +++ b/src/include/utils/guc_tables.h @@ -28,7 +28,7 @@ enum config_type PGC_ENUM }; -union config_var_value +union config_var_val { bool boolval; int intval; @@ -37,6 +37,16 @@ union config_var_value int enumval; }; +/* + * The actual value of a GUC variable can include a malloc'd opaque struct + * "extra", which is created by its check_hook and used by its assign_hook. + */ +typedef struct config_var_value +{ + union config_var_val val; + void *extra; +} config_var_value; + /* * Groupings to help organize all the run-time options for display */ @@ -105,8 +115,8 @@ typedef struct guc_stack int nest_level; /* nesting depth at which we made entry */ GucStackState state; /* see enum above */ GucSource source; /* source of the prior value */ - union config_var_value prior; /* previous value of variable */ - union config_var_value masked; /* SET value in a GUC_SET_LOCAL entry */ + config_var_value prior; /* previous value of variable */ + config_var_value masked; /* SET value in a GUC_SET_LOCAL entry */ /* masked value's source must be PGC_S_SESSION, so no need to store it */ } GucStack; @@ -116,6 +126,11 @@ typedef struct guc_stack * The short description should be less than 80 chars in length. Some * applications may use the long description as well, and will append * it to the short description. (separated by a newline or '. ') + * + * Note that sourcefile/sourceline are kept here, and not pushed into stacked + * values, although in principle they belong with some stacked value if the + * active value is session- or transaction-local. This is to avoid bloating + * stack entries. We know they are only relevant when source == PGC_S_FILE. */ struct config_generic { @@ -132,7 +147,8 @@ struct config_generic GucSource reset_source; /* source of the reset_value */ GucSource source; /* source of the current actual value */ GucStack *stack; /* stacked prior values */ - char *sourcefile; /* file this settings is from (NULL if not + void *extra; /* "extra" pointer for current actual value */ + char *sourcefile; /* file current setting is from (NULL if not * file) */ int sourceline; /* line in source file */ }; @@ -155,10 +171,12 @@ struct config_bool /* constant fields, must be set correctly in initial value: */ bool *variable; bool boot_val; + GucBoolCheckHook check_hook; GucBoolAssignHook assign_hook; GucShowHook show_hook; /* variable fields, initialized at runtime: */ bool reset_val; + void *reset_extra; }; struct config_int @@ -169,10 +187,12 @@ struct config_int int boot_val; int min; int max; + GucIntCheckHook check_hook; GucIntAssignHook assign_hook; GucShowHook show_hook; /* variable fields, initialized at runtime: */ int reset_val; + void *reset_extra; }; struct config_real @@ -183,10 +203,12 @@ struct config_real double boot_val; double min; double max; + GucRealCheckHook check_hook; GucRealAssignHook assign_hook; GucShowHook show_hook; /* variable fields, initialized at runtime: */ double reset_val; + void *reset_extra; }; struct config_string @@ -195,10 +217,12 @@ struct config_string /* constant fields, must be set correctly in initial value: */ char **variable; const char *boot_val; + GucStringCheckHook check_hook; GucStringAssignHook assign_hook; GucShowHook show_hook; /* variable fields, initialized at runtime: */ char *reset_val; + void *reset_extra; }; struct config_enum @@ -208,10 +232,12 @@ struct config_enum int *variable; int boot_val; const struct config_enum_entry *options; + GucEnumCheckHook check_hook; GucEnumAssignHook assign_hook; GucShowHook show_hook; /* variable fields, initialized at runtime: */ int reset_val; + void *reset_extra; }; /* constant tables corresponding to enums above and in guc.h */ diff --git a/src/include/utils/pg_locale.h b/src/include/utils/pg_locale.h index 4c72fd0dc6c3137757175713fc5169c22e1aeed6..25b9d5091505c6b17e8cba2b1b0a8d4bf572aa33 100644 --- a/src/include/utils/pg_locale.h +++ b/src/include/utils/pg_locale.h @@ -33,14 +33,14 @@ extern char *localized_abbrev_months[]; extern char *localized_full_months[]; -extern const char *locale_messages_assign(const char *value, - bool doit, GucSource source); -extern const char *locale_monetary_assign(const char *value, - bool doit, GucSource source); -extern const char *locale_numeric_assign(const char *value, - bool doit, GucSource source); -extern const char *locale_time_assign(const char *value, - bool doit, GucSource source); +extern bool check_locale_messages(char **newval, void **extra, GucSource source); +extern void assign_locale_messages(const char *newval, void *extra); +extern bool check_locale_monetary(char **newval, void **extra, GucSource source); +extern void assign_locale_monetary(const char *newval, void *extra); +extern bool check_locale_numeric(char **newval, void **extra, GucSource source); +extern void assign_locale_numeric(const char *newval, void *extra); +extern bool check_locale_time(char **newval, void **extra, GucSource source); +extern void assign_locale_time(const char *newval, void *extra); extern bool check_locale(int category, const char *locale); extern char *pg_perm_setlocale(int category, const char *locale); diff --git a/src/include/utils/tzparser.h b/src/include/utils/tzparser.h index ba18819ef5928a4101843fb300c464682ebdb22f..2065078116a3e11bdc2b94f5cba5b58a6867cb20 100644 --- a/src/include/utils/tzparser.h +++ b/src/include/utils/tzparser.h @@ -13,6 +13,8 @@ #ifndef TZPARSER_H #define TZPARSER_H +#include "utils/datetime.h" + /* * The result of parsing a timezone configuration file is an array of * these structs, in order by abbrev. We export this because datetime.c @@ -30,6 +32,6 @@ typedef struct tzEntry } tzEntry; -extern bool load_tzoffsets(const char *filename, bool doit, int elevel); +extern TimeZoneAbbrevTable *load_tzoffsets(const char *filename); #endif /* TZPARSER_H */ diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c index 9a94b3f085236d4333eb1a80a8e1d19dcb891681..35bf72ca30ffd0388a3df8ffec61aa786dcc2c9e 100644 --- a/src/pl/plperl/plperl.c +++ b/src/pl/plperl/plperl.c @@ -363,7 +363,7 @@ _PG_init(void) &plperl_use_strict, false, PGC_USERSET, 0, - NULL, NULL); + NULL, NULL, NULL); /* * plperl.on_init is marked PGC_SIGHUP to support the idea that it might @@ -377,7 +377,7 @@ _PG_init(void) &plperl_on_init, NULL, PGC_SIGHUP, 0, - NULL, NULL); + NULL, NULL, NULL); /* * plperl.on_plperl_init is marked PGC_SUSET to avoid issues whereby a @@ -399,7 +399,7 @@ _PG_init(void) &plperl_on_plperl_init, NULL, PGC_SUSET, 0, - NULL, NULL); + NULL, NULL, NULL); DefineCustomStringVariable("plperl.on_plperlu_init", gettext_noop("Perl initialization code to execute once when plperlu is first used."), @@ -407,7 +407,7 @@ _PG_init(void) &plperl_on_plperlu_init, NULL, PGC_SUSET, 0, - NULL, NULL); + NULL, NULL, NULL); EmitWarningsOnPlaceholders("plperl"); diff --git a/src/pl/plpgsql/src/pl_handler.c b/src/pl/plpgsql/src/pl_handler.c index 2389849a223195fb3e78e884d07e17b85f08fb00..e6e71432ff9ae72e43c402a4873446234dc7d7da 100644 --- a/src/pl/plpgsql/src/pl_handler.c +++ b/src/pl/plpgsql/src/pl_handler.c @@ -63,7 +63,7 @@ _PG_init(void) PLPGSQL_RESOLVE_ERROR, variable_conflict_options, PGC_SUSET, 0, - NULL, NULL); + NULL, NULL, NULL); EmitWarningsOnPlaceholders("plpgsql"); diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index a0e24e84cbf68ced1a3aca0ecb4e824883215790..98671a2bcdf892cd0573fd1e8ed7e2d0bcf2d3af 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -622,7 +622,8 @@ alter function report_guc(text) set search_path = no_such_schema; NOTICE: schema "no_such_schema" does not exist -- with error occurring here select report_guc('work_mem'), current_setting('work_mem'); -ERROR: schema "no_such_schema" does not exist +ERROR: invalid value for parameter "search_path": "no_such_schema" +DETAIL: schema "no_such_schema" does not exist alter function report_guc(text) reset search_path set work_mem = '2MB'; select report_guc('work_mem'), current_setting('work_mem'); report_guc | current_setting diff --git a/src/timezone/pgtz.c b/src/timezone/pgtz.c index d09d4ad736f54f04ed82b67a12ef4f27321d8f57..382a4e04f506b4e2b1cfb70fbb561292df214ab0 100644 --- a/src/timezone/pgtz.c +++ b/src/timezone/pgtz.c @@ -1444,27 +1444,39 @@ pg_timezone_initialize(void) { pg_tz *def_tz = NULL; - /* Do we need to try to figure the session timezone? */ - if (pg_strcasecmp(GetConfigOption("timezone", false), "UNKNOWN") == 0) + /* + * Make sure that session_timezone and log_timezone are set. + * (session_timezone could still be NULL even if a timezone value was set + * in postgresql.conf, if that setting was interval-based rather than + * timezone-based.) + */ + if (!session_timezone) { - /* Select setting */ def_tz = select_default_timezone(); session_timezone = def_tz; - /* Tell GUC about the value. Will redundantly call pg_tzset() */ - SetConfigOption("timezone", pg_get_timezone_name(def_tz), - PGC_POSTMASTER, PGC_S_ARGV); } - - /* What about the log timezone? */ - if (pg_strcasecmp(GetConfigOption("log_timezone", false), "UNKNOWN") == 0) + if (!log_timezone) { - /* Select setting, but don't duplicate work */ + /* Don't duplicate work */ if (!def_tz) def_tz = select_default_timezone(); log_timezone = def_tz; + } + + /* Now, set the timezone GUC if it's not already set */ + if (GetConfigOption("timezone", false) == NULL) + { + /* Tell GUC about the value. Will redundantly call pg_tzset() */ + SetConfigOption("timezone", pg_get_timezone_name(session_timezone), + PGC_POSTMASTER, PGC_S_ENV_VAR); + } + + /* Likewise for log timezone */ + if (GetConfigOption("log_timezone", false) == NULL) + { /* Tell GUC about the value. Will redundantly call pg_tzset() */ - SetConfigOption("log_timezone", pg_get_timezone_name(def_tz), - PGC_POSTMASTER, PGC_S_ARGV); + SetConfigOption("log_timezone", pg_get_timezone_name(log_timezone), + PGC_POSTMASTER, PGC_S_ENV_VAR); } }