*/
/*
- * 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;
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)
* 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;
}
}
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)
{
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];
}
* 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;
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';
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
/*
* 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
{
*/
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)
*/
/*
- * 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);
}
/*
*
* 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;
}
}
* 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:
*/
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;
}
/*
* 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)
{
/*
- * 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);
*/
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";
}
* 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
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;
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);
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;
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;
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;
/*
NULL
},
&enable_seqscan,
- true, NULL, NULL
+ true,
+ NULL, NULL, NULL
},
{
{"enable_indexscan", PGC_USERSET, QUERY_TUNING_METHOD,
NULL
},
&enable_indexscan,
- true, NULL, NULL
+ true,
+ NULL, NULL, NULL
},
{
{"enable_bitmapscan", PGC_USERSET, QUERY_TUNING_METHOD,
NULL
},
&enable_bitmapscan,
- true, NULL, NULL
+ true,
+ NULL, NULL, NULL
},
{
{"enable_tidscan", PGC_USERSET, QUERY_TUNING_METHOD,
NULL
},
&enable_tidscan,
- true, NULL, NULL
+ true,
+ NULL, NULL, NULL
},
{
{"enable_sort", PGC_USERSET, QUERY_TUNING_METHOD,
NULL
},
&enable_sort,
- true, NULL, NULL
+ true,
+ NULL, NULL, NULL
},
{
{"enable_hashagg", PGC_USERSET, QUERY_TUNING_METHOD,
NULL
},
&enable_hashagg,
- true, NULL, NULL
+ true,
+ NULL, NULL, NULL
},
{
{"enable_material", PGC_USERSET, QUERY_TUNING_METHOD,
NULL
},
&enable_material,
- true, NULL, NULL
+ true,
+ NULL, NULL, NULL
},
{
{"enable_nestloop", PGC_USERSET, QUERY_TUNING_METHOD,
NULL
},
&enable_nestloop,
- true, NULL, NULL
+ true,
+ NULL, NULL, NULL
},
{
{"enable_mergejoin", PGC_USERSET, QUERY_TUNING_METHOD,
NULL
},
&enable_mergejoin,
- true, NULL, NULL
+ true,
+ NULL, NULL, NULL
},
{
{"enable_hashjoin", PGC_USERSET, QUERY_TUNING_METHOD,
NULL
},
&enable_hashjoin,
- true, NULL, NULL
+ true,
+ NULL, NULL, NULL
},
{
{"geqo", PGC_USERSET, QUERY_TUNING_GEQO,
"exhaustive searching.")
},
&enable_geqo,
- true, NULL, NULL
+ true,
+ NULL, NULL, NULL
},
{
/* Not for general use --- used by SET SESSION AUTHORIZATION */
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,
NULL
},
&enable_bonjour,
- false, assign_bonjour, NULL
+ false,
+ check_bonjour, NULL, NULL
},
{
{"ssl", PGC_POSTMASTER, CONN_AUTH_SECURITY,
NULL
},
&EnableSSL,
- false, assign_ssl, NULL
+ false,
+ check_ssl, NULL, NULL
},
{
{"fsync", PGC_SIGHUP, WAL_SETTINGS,
"an operating system or hardware crash.")
},
&enableFsync,
- true, NULL, NULL
+ true,
+ NULL, NULL, NULL
},
{
{"zero_damaged_pages", PGC_SUSET, DEVELOPER_OPTIONS,
GUC_NOT_IN_SAMPLE
},
&zero_damaged_pages,
- false, NULL, NULL
+ false,
+ NULL, NULL, NULL
},
{
{"full_page_writes", PGC_SIGHUP, WAL_SETTINGS,
"is possible.")
},
&fullPageWrites,
- true, NULL, NULL
+ true,
+ NULL, NULL, NULL
},
{
{"silent_mode", PGC_POSTMASTER, LOGGING_WHERE,
"background and any controlling terminals are dissociated.")
},
&SilentMode,
- false, NULL, NULL
+ false,
+ NULL, NULL, NULL
},
{
{"log_checkpoints", PGC_SIGHUP, LOGGING_WHAT,
NULL
},
&log_checkpoints,
- false, NULL, NULL
+ false,
+ NULL, NULL, NULL
},
{
{"log_connections", PGC_BACKEND, LOGGING_WHAT,
NULL
},
&Log_connections,
- false, NULL, NULL
+ false,
+ NULL, NULL, NULL
},
{
{"log_disconnections", PGC_BACKEND, LOGGING_WHAT,
NULL
},
&Log_disconnections,
- false, NULL, NULL
+ false,
+ NULL, NULL, NULL
},
{
{"debug_assertions", PGC_USERSET, DEVELOPER_OPTIONS,
#else
false,
#endif
- assign_debug_assertions, NULL
+ check_debug_assertions, NULL, NULL
},
{
NULL
},
&ExitOnAnyError,
- false, NULL, NULL
+ false,
+ NULL, NULL, NULL
},
{
{"restart_after_crash", PGC_SIGHUP, ERROR_HANDLING_OPTIONS,
NULL
},
&restart_after_crash,
- true, NULL, NULL
+ true,
+ NULL, NULL, NULL
},
{
NULL
},
&log_duration,
- false, NULL, NULL
+ false,
+ NULL, NULL, NULL
},
{
{"debug_print_parse", PGC_USERSET, LOGGING_WHAT,
NULL
},
&Debug_print_parse,
- false, NULL, NULL
+ false,
+ NULL, NULL, NULL
},
{
{"debug_print_rewritten", PGC_USERSET, LOGGING_WHAT,
NULL
},
&Debug_print_rewritten,
- false, NULL, NULL
+ false,
+ NULL, NULL, NULL
},
{
{"debug_print_plan", PGC_USERSET, LOGGING_WHAT,
NULL
},
&Debug_print_plan,
- false, NULL, NULL
+ false,
+ NULL, NULL, NULL
},
{
{"debug_pretty_print", PGC_USERSET, LOGGING_WHAT,
NULL
},
&Debug_pretty_print,
- true, NULL, NULL
+ true,
+ NULL, NULL, NULL
},
{
{"log_parser_stats", PGC_SUSET, STATS_MONITORING,
NULL
},
&log_parser_stats,
- false, assign_stage_log_stats, NULL
+ false,
+ check_stage_log_stats, NULL, NULL
},
{
{"log_planner_stats", PGC_SUSET, STATS_MONITORING,
NULL
},
&log_planner_stats,
- false, assign_stage_log_stats, NULL
+ false,
+ check_stage_log_stats, NULL, NULL
},
{
{"log_executor_stats", PGC_SUSET, STATS_MONITORING,
NULL
},
&log_executor_stats,
- false, assign_stage_log_stats, NULL
+ false,
+ check_stage_log_stats, NULL, NULL
},
{
{"log_statement_stats", PGC_SUSET, STATS_MONITORING,
NULL
},
&log_statement_stats,
- false, assign_log_stats, NULL
+ false,
+ check_log_stats, NULL, NULL
},
#ifdef BTREE_BUILD_STATS
{
GUC_NOT_IN_SAMPLE
},
&log_btree_build_stats,
- false, NULL, NULL
+ false,
+ NULL, NULL, NULL
},
#endif
"the time at which that command began execution.")
},
&pgstat_track_activities,
- true, NULL, NULL
+ true,
+ NULL, NULL, NULL
},
{
{"track_counts", PGC_SUSET, STATS_COLLECTOR,
NULL
},
&pgstat_track_counts,
- true, NULL, NULL
+ true,
+ NULL, NULL, NULL
},
{
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
},
{
NULL
},
&autovacuum_start_daemon,
- true, NULL, NULL
+ true,
+ NULL, NULL, NULL
},
{
GUC_NOT_IN_SAMPLE
},
&Trace_notify,
- false, NULL, NULL
+ false,
+ NULL, NULL, NULL
},
#ifdef LOCK_DEBUG
GUC_NOT_IN_SAMPLE
},
&Trace_locks,
- false, NULL, NULL
+ false,
+ NULL, NULL, NULL
},
{
{"trace_userlocks", PGC_SUSET, DEVELOPER_OPTIONS,
GUC_NOT_IN_SAMPLE
},
&Trace_userlocks,
- false, NULL, NULL
+ false,
+ NULL, NULL, NULL
},
{
{"trace_lwlocks", PGC_SUSET, DEVELOPER_OPTIONS,
GUC_NOT_IN_SAMPLE
},
&Trace_lwlocks,
- false, NULL, NULL
+ false,
+ NULL, NULL, NULL
},
{
{"debug_deadlocks", PGC_SUSET, DEVELOPER_OPTIONS,
GUC_NOT_IN_SAMPLE
},
&Debug_deadlocks,
- false, NULL, NULL
+ false,
+ NULL, NULL, NULL
},
#endif
NULL
},
&log_lock_waits,
- false, NULL, NULL
+ false,
+ NULL, NULL, NULL
},
{
"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,
NULL
},
&SQL_inheritance,
- true, NULL, NULL
+ true,
+ NULL, NULL, NULL
},
{
{"password_encryption", PGC_USERSET, CONN_AUTH_SECURITY,
"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,
"return null (unknown).")
},
&Transform_null_equals,
- false, NULL, NULL
+ false,
+ NULL, NULL, NULL
},
{
{"db_user_namespace", PGC_SIGHUP, CONN_AUTH_SECURITY,
NULL
},
&Db_user_namespace,
- false, NULL, NULL
+ false,
+ NULL, NULL, NULL
},
{
/* only here for backwards compatibility */
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,
NULL
},
&DefaultXactReadOnly,
- false, NULL, NULL
+ false,
+ NULL, NULL, NULL
},
{
{"transaction_read_only", PGC_USERSET, CLIENT_CONN_STATEMENT,
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,
NULL
},
&DefaultXactDeferrable,
- false, NULL, NULL
+ false,
+ NULL, NULL, NULL
},
{
{"transaction_deferrable", PGC_USERSET, CLIENT_CONN_STATEMENT,
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,
NULL
},
&check_function_bodies,
- true, NULL, NULL
+ true,
+ NULL, NULL, NULL
},
{
{"array_nulls", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
"otherwise it is taken literally.")
},
&Array_nulls,
- true, NULL, NULL
+ true,
+ NULL, NULL, NULL
},
{
{"default_with_oids", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
NULL
},
&default_with_oids,
- false, NULL, NULL
+ false,
+ NULL, NULL, NULL
},
{
{"logging_collector", PGC_POSTMASTER, LOGGING_WHERE,
NULL
},
&Logging_collector,
- false, NULL, NULL
+ false,
+ NULL, NULL, NULL
},
{
{"log_truncate_on_rotation", PGC_SIGHUP, LOGGING_WHERE,
NULL
},
&Log_truncate_on_rotation,
- false, NULL, NULL
+ false,
+ NULL, NULL, NULL
},
#ifdef TRACE_SORT
GUC_NOT_IN_SAMPLE
},
&trace_sort,
- false, NULL, NULL
+ false,
+ NULL, NULL, NULL
},
#endif
GUC_NOT_IN_SAMPLE
},
&trace_syncscan,
- false, NULL, NULL
+ false,
+ NULL, NULL, NULL
},
#endif
GUC_NOT_IN_SAMPLE
},
&optimize_bounded_sort,
- true, NULL, NULL
+ true,
+ NULL, NULL, NULL
},
#endif
GUC_NOT_IN_SAMPLE
},
&XLOG_DEBUG,
- false, NULL, NULL
+ false,
+ NULL, NULL, NULL
},
#endif
},
&integer_datetimes,
#ifdef HAVE_INT64_TIMESTAMP
- true, NULL, NULL
+ true,
#else
- false, NULL, NULL
+ false,
#endif
+ NULL, NULL, NULL
},
{
NULL
},
&pg_krb_caseins_users,
- false, NULL, NULL
+ false,
+ NULL, NULL, NULL
},
{
NULL
},
&escape_string_warning,
- true, NULL, NULL
+ true,
+ NULL, NULL, NULL
},
{
GUC_REPORT
},
&standard_conforming_strings,
- true, NULL, NULL
+ true,
+ NULL, NULL, NULL
},
{
NULL
},
&synchronize_seqscans,
- true, NULL, NULL
+ true,
+ NULL, NULL, NULL
},
{
NULL
},
&XLogArchiveMode,
- false, NULL, NULL
+ false,
+ NULL, NULL, NULL
},
{
NULL
},
&EnableHotStandby,
- false, NULL, NULL
+ false,
+ NULL, NULL, NULL
},
{
NULL
},
&hot_standby_feedback,
- false, NULL, NULL
+ false,
+ NULL, NULL, NULL
},
{
GUC_NOT_IN_SAMPLE
},
&allowSystemTableMods,
- false, NULL, NULL
+ false,
+ NULL, NULL, NULL
},
{
GUC_NOT_IN_SAMPLE
},
&IgnoreSystemIndexes,
- false, NULL, NULL
+ false,
+ NULL, NULL, NULL
},
{
"for compatibility with PostgreSQL releases prior to 9.0.")
},
&lo_compat_privileges,
- false, NULL, NULL
+ false,
+ NULL, NULL, NULL
},
{
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
}
};
GUC_UNIT_S
},
&XLogArchiveTimeout,
- 0, 0, INT_MAX, NULL, NULL
+ 0, 0, INT_MAX,
+ NULL, NULL, NULL
},
{
{"post_auth_delay", PGC_BACKEND, DEVELOPER_OPTIONS,
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,
"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,
"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,
"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,
NULL
},
&geqo_threshold,
- 12, 2, INT_MAX, NULL, NULL
+ 12, 2, INT_MAX,
+ NULL, NULL, NULL
},
{
{"geqo_effort", PGC_USERSET, QUERY_TUNING_GEQO,
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,
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,
gettext_noop("Zero selects a suitable default value.")
},
&Geqo_generations,
- 0, 0, INT_MAX, NULL, NULL
+ 0, 0, INT_MAX,
+ NULL, NULL, NULL
},
{
GUC_UNIT_MS
},
&DeadlockTimeout,
- 1000, 1, INT_MAX, NULL, NULL
+ 1000, 1, INT_MAX,
+ NULL, NULL, NULL
},
{
GUC_UNIT_MS
},
&max_standby_archive_delay,
- 30 * 1000, -1, INT_MAX, NULL, NULL
+ 30 * 1000, -1, INT_MAX,
+ NULL, NULL, NULL
},
{
GUC_UNIT_MS
},
&max_standby_streaming_delay,
- 30 * 1000, -1, INT_MAX, NULL, NULL
+ 30 * 1000, -1, INT_MAX,
+ NULL, NULL, NULL
},
{
GUC_UNIT_S
},
&wal_receiver_status_interval,
- 10, 0, INT_MAX/1000, NULL, NULL
+ 10, 0, INT_MAX/1000,
+ NULL, NULL, NULL
},
{
NULL
},
&MaxConnections,
- 100, 1, MAX_BACKENDS, assign_maxconnections, NULL
+ 100, 1, MAX_BACKENDS,
+ check_maxconnections, assign_maxconnections, NULL
},
{
NULL
},
&ReservedBackends,
- 3, 0, MAX_BACKENDS, NULL, NULL
+ 3, 0, MAX_BACKENDS,
+ NULL, NULL, NULL
},
/*
GUC_UNIT_BLOCKS
},
&NBuffers,
- 1024, 16, INT_MAX / 2, NULL, NULL
+ 1024, 16, INT_MAX / 2,
+ NULL, NULL, NULL
},
{
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
},
{
NULL
},
&PostPortNumber,
- DEF_PGPORT, 1, 65535, NULL, NULL
+ DEF_PGPORT, 1, 65535,
+ NULL, NULL, NULL
},
{
"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
},
{
"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
},
{
GUC_UNIT_KB
},
&work_mem,
- 1024, 64, MAX_KILOBYTES, NULL, NULL
+ 1024, 64, MAX_KILOBYTES,
+ NULL, NULL, NULL
},
{
GUC_UNIT_KB
},
&maintenance_work_mem,
- 16384, 1024, MAX_KILOBYTES, NULL, NULL
+ 16384, 1024, MAX_KILOBYTES,
+ NULL, NULL, NULL
},
/*
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
},
{
NULL
},
&VacuumCostPageHit,
- 1, 0, 10000, NULL, NULL
+ 1, 0, 10000,
+ NULL, NULL, NULL
},
{
NULL
},
&VacuumCostPageMiss,
- 10, 0, 10000, NULL, NULL
+ 10, 0, 10000,
+ NULL, NULL, NULL
},
{
NULL
},
&VacuumCostPageDirty,
- 20, 0, 10000, NULL, NULL
+ 20, 0, 10000,
+ NULL, NULL, NULL
},
{
NULL
},
&VacuumCostLimit,
- 200, 1, 10000, NULL, NULL
+ 200, 1, 10000,
+ NULL, NULL, NULL
},
{
GUC_UNIT_MS
},
&VacuumCostDelay,
- 0, 0, 100, NULL, NULL
+ 0, 0, 100,
+ NULL, NULL, NULL
},
{
GUC_UNIT_MS
},
&autovacuum_vac_cost_delay,
- 20, -1, 100, NULL, NULL
+ 20, -1, 100,
+ NULL, NULL, NULL
},
{
NULL
},
&autovacuum_vac_cost_limit,
- -1, -1, 10000, NULL, NULL
+ -1, -1, 10000,
+ NULL, NULL, NULL
},
{
NULL
},
&max_files_per_process,
- 1000, 25, INT_MAX, NULL, NULL
+ 1000, 25, INT_MAX,
+ NULL, NULL, NULL
},
/*
NULL
},
&max_prepared_xacts,
- 0, 0, MAX_BACKENDS, NULL, NULL
+ 0, 0, MAX_BACKENDS,
+ NULL, NULL, NULL
},
#ifdef LOCK_DEBUG
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,
GUC_NOT_IN_SAMPLE
},
&Trace_lock_table,
- 0, 0, INT_MAX, NULL, NULL
+ 0, 0, INT_MAX,
+ NULL, NULL, NULL
},
#endif
GUC_UNIT_MS
},
&StatementTimeout,
- 0, 0, INT_MAX, NULL, NULL
+ 0, 0, INT_MAX,
+ NULL, NULL, NULL
},
{
NULL
},
&vacuum_freeze_min_age,
- 50000000, 0, 1000000000, NULL, NULL
+ 50000000, 0, 1000000000,
+ NULL, NULL, NULL
},
{
NULL
},
&vacuum_freeze_table_age,
- 150000000, 0, 2000000000, NULL, NULL
+ 150000000, 0, 2000000000,
+ NULL, NULL, NULL
},
{
NULL
},
&vacuum_defer_cleanup_age,
- 0, 0, 1000000, NULL, NULL
+ 0, 0, 1000000,
+ NULL, NULL, NULL
},
/*
"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
},
{
"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
},
{
GUC_UNIT_S
},
&AuthenticationTimeout,
- 60, 1, 600, NULL, NULL
+ 60, 1, 600,
+ NULL, NULL, NULL
},
{
GUC_NOT_IN_SAMPLE | GUC_UNIT_S
},
&PreAuthDelay,
- 0, 0, 60, NULL, NULL
+ 0, 0, 60,
+ NULL, NULL, NULL
},
{
NULL
},
&wal_keep_segments,
- 0, 0, INT_MAX, NULL, NULL
+ 0, 0, INT_MAX,
+ NULL, NULL, NULL
},
{
NULL
},
&CheckPointSegments,
- 3, 1, INT_MAX, NULL, NULL
+ 3, 1, INT_MAX,
+ NULL, NULL, NULL
},
{
GUC_UNIT_S
},
&CheckPointTimeout,
- 300, 30, 3600, NULL, NULL
+ 300, 30, 3600,
+ NULL, NULL, NULL
},
{
GUC_UNIT_S
},
&CheckPointWarning,
- 30, 0, INT_MAX, NULL, NULL
+ 30, 0, INT_MAX,
+ NULL, NULL, NULL
},
{
GUC_UNIT_XBLOCKS
},
&XLOGbuffers,
- -1, -1, INT_MAX, NULL, NULL
+ -1, -1, INT_MAX,
+ check_wal_buffers, NULL, NULL
},
{
GUC_UNIT_MS
},
&WalWriterDelay,
- 200, 1, 10000, NULL, NULL
+ 200, 1, 10000,
+ NULL, NULL, NULL
},
{
NULL
},
&max_wal_senders,
- 0, 0, MAX_BACKENDS, NULL, NULL
+ 0, 0, MAX_BACKENDS,
+ NULL, NULL, NULL
},
{
GUC_UNIT_MS
},
&WalSndDelay,
- 1000, 1, 10000, NULL, NULL
+ 1000, 1, 10000,
+ NULL, NULL, NULL
},
{
GUC_UNIT_MS
},
&replication_timeout,
- 60 * 1000, 0, INT_MAX, NULL, NULL
+ 60 * 1000, 0, INT_MAX,
+ NULL, NULL, NULL
},
{
NULL
},
&CommitDelay,
- 0, 0, 100000, NULL, NULL
+ 0, 0, 100000,
+ NULL, NULL, NULL
},
{
NULL
},
&CommitSiblings,
- 5, 0, 1000, NULL, NULL
+ 5, 0, 1000,
+ NULL, NULL, NULL
},
{
"(FLT_DIG or DBL_DIG as appropriate).")
},
&extra_float_digits,
- 0, -15, 3, NULL, NULL
+ 0, -15, 3,
+ NULL, NULL, NULL
},
{
GUC_UNIT_MS
},
&log_min_duration_statement,
- -1, -1, INT_MAX, NULL, NULL
+ -1, -1, INT_MAX,
+ NULL, NULL, NULL
},
{
GUC_UNIT_MS
},
&Log_autovacuum_min_duration,
- -1, -1, INT_MAX, NULL, NULL
+ -1, -1, INT_MAX,
+ NULL, NULL, NULL
},
{
GUC_UNIT_MS
},
&BgWriterDelay,
- 200, 10, 10000, NULL, NULL
+ 200, 10, 10000,
+ NULL, NULL, NULL
},
{
NULL
},
&bgwriter_lru_maxpages,
- 100, 0, 1000, NULL, NULL
+ 100, 0, 1000,
+ NULL, NULL, NULL
},
{
#else
0, 0, 0,
#endif
- assign_effective_io_concurrency, NULL
+ check_effective_io_concurrency, assign_effective_io_concurrency, NULL
},
{
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
},
{
GUC_UNIT_KB
},
&Log_RotationSize,
- 10 * 1024, 0, INT_MAX / 1024, NULL, NULL
+ 10 * 1024, 0, INT_MAX / 1024,
+ NULL, NULL, NULL
},
{
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
},
{
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
},
{
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
},
{
GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
},
&block_size,
- BLCKSZ, BLCKSZ, BLCKSZ, NULL, NULL
+ BLCKSZ, BLCKSZ, BLCKSZ,
+ NULL, NULL, NULL
},
{
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
},
{
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
},
{
(XLOG_SEG_SIZE / XLOG_BLCKSZ),
(XLOG_SEG_SIZE / XLOG_BLCKSZ),
(XLOG_SEG_SIZE / XLOG_BLCKSZ),
- NULL, NULL
+ NULL, NULL, NULL
},
{
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,
NULL
},
&autovacuum_vac_thresh,
- 50, 0, INT_MAX, NULL, NULL
+ 50, 0, INT_MAX,
+ NULL, NULL, NULL
},
{
{"autovacuum_analyze_threshold", PGC_SIGHUP, AUTOVACUUM,
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 */
},
&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 */
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
},
{
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
},
{
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
},
{
GUC_UNIT_KB,
},
&ssl_renegotiation_limit,
- 512 * 1024, 0, MAX_KILOBYTES, NULL, NULL
+ 512 * 1024, 0, MAX_KILOBYTES,
+ NULL, NULL, NULL
},
{
"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
},
{
0
},
&GinFuzzySearchLimit,
- 0, 0, INT_MAX, NULL, NULL
+ 0, 0, INT_MAX,
+ NULL, NULL, NULL
},
{
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
},
{
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
},
{
GUC_UNIT_KB
},
&log_temp_files,
- -1, -1, INT_MAX, NULL, NULL
+ -1, -1, INT_MAX,
+ NULL, NULL, NULL
},
{
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
}
};
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,
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,
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,
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,
NULL
},
&cpu_operator_cost,
- DEFAULT_CPU_OPERATOR_COST, 0, DBL_MAX, NULL, NULL
+ DEFAULT_CPU_OPERATOR_COST, 0, DBL_MAX,
+ NULL, NULL, NULL
},
{
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
},
{
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,
NULL
},
&Geqo_seed,
- 0.0, 0.0, 1.0, NULL, NULL
+ 0.0, 0.0, 1.0,
+ NULL, NULL, NULL
},
{
NULL
},
&bgwriter_lru_multiplier,
- 2.0, 0.0, 10.0, NULL, NULL
+ 2.0, 0.0, 10.0,
+ NULL, NULL, NULL
},
{
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
},
{
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,
NULL
},
&autovacuum_anl_scale,
- 0.1, 0.0, 100.0, NULL, NULL
+ 0.1, 0.0, 100.0,
+ NULL, NULL, NULL
},
{
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
}
};
NULL
},
&XLogArchiveCommand,
- "", NULL, show_archive_command
+ "",
+ NULL, NULL, show_archive_command
},
{
GUC_IS_NAME | GUC_REPORT
},
&client_encoding_string,
- "SQL_ASCII", assign_client_encoding, NULL
+ "SQL_ASCII",
+ check_client_encoding, assign_client_encoding, NULL
},
{
gettext_noop("If blank, no prefix is used.")
},
&Log_line_prefix,
- "", NULL, NULL
+ "",
+ NULL, NULL, NULL
},
{
NULL
},
&log_timezone_string,
- "UNKNOWN", assign_log_timezone, show_log_timezone
+ NULL,
+ check_log_timezone, assign_log_timezone, show_log_timezone
},
{
GUC_LIST_INPUT | GUC_REPORT
},
&datestyle_string,
- "ISO, MDY", assign_datestyle, NULL
+ "ISO, MDY",
+ check_datestyle, assign_datestyle, NULL
},
{
GUC_IS_NAME
},
&default_tablespace,
- "", assign_default_tablespace, NULL
+ "",
+ check_default_tablespace, NULL, NULL
},
{
GUC_LIST_INPUT | GUC_LIST_QUOTE
},
&temp_tablespaces,
- "", assign_temp_tablespaces, NULL
+ "",
+ check_temp_tablespaces, assign_temp_tablespaces, NULL
},
{
GUC_SUPERUSER_ONLY
},
&Dynamic_library_path,
- "$libdir", NULL, NULL
+ "$libdir",
+ NULL, NULL, NULL
},
{
GUC_SUPERUSER_ONLY
},
&pg_krb_server_keyfile,
- PG_KRB_SRVTAB, NULL, NULL
+ PG_KRB_SRVTAB,
+ NULL, NULL, NULL
},
{
NULL
},
&pg_krb_srvnam,
- PG_KRB_SRVNAM, NULL, NULL
+ PG_KRB_SRVNAM,
+ NULL, NULL, NULL
},
{
NULL
},
&bonjour_name,
- "", NULL, NULL
+ "",
+ NULL, NULL, NULL
},
/* See main.c about why defaults for LC_foo are not all alike */
GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
},
&locale_collate,
- "C", NULL, NULL
+ "C",
+ NULL, NULL, NULL
},
{
GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
},
&locale_ctype,
- "C", NULL, NULL
+ "C",
+ NULL, NULL, NULL
},
{
NULL
},
&locale_messages,
- "", locale_messages_assign, NULL
+ "",
+ check_locale_messages, assign_locale_messages, NULL
},
{
NULL
},
&locale_monetary,
- "C", locale_monetary_assign, NULL
+ "C",
+ check_locale_monetary, assign_locale_monetary, NULL
},
{
NULL
},
&locale_numeric,
- "C", locale_numeric_assign, NULL
+ "C",
+ check_locale_numeric, assign_locale_numeric, NULL
},
{
NULL
},
&locale_time,
- "C", locale_time_assign, NULL
+ "C",
+ check_locale_time, assign_locale_time, NULL
},
{
GUC_LIST_INPUT | GUC_LIST_QUOTE | GUC_SUPERUSER_ONLY
},
&shared_preload_libraries_string,
- "", NULL, NULL
+ "",
+ NULL, NULL, NULL
},
{
GUC_LIST_INPUT | GUC_LIST_QUOTE
},
&local_preload_libraries_string,
- "", NULL, NULL
+ "",
+ NULL, NULL, NULL
},
{
GUC_LIST_INPUT | GUC_LIST_QUOTE
},
&namespace_search_path,
- "\"$user\",public", assign_search_path, NULL
+ "\"$user\",public",
+ check_search_path, assign_search_path, NULL
},
{
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
},
{
GUC_REPORT | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
},
&server_version_string,
- PG_VERSION, NULL, NULL
+ PG_VERSION,
+ NULL, NULL, NULL
},
{
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
},
{
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
},
{
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,
GUC_SUPERUSER_ONLY
},
&Log_directory,
- "pg_log", assign_canonical_path, NULL
+ "pg_log",
+ check_canonical_path, NULL, NULL
},
{
{"log_filename", PGC_SIGHUP, LOGGING_WHERE,
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
},
{
NULL
},
&syslog_ident_str,
- "postgres", assign_syslog_ident, NULL
+ "postgres",
+ NULL, assign_syslog_ident, NULL
},
{
GUC_REPORT
},
&timezone_string,
- "UNKNOWN", assign_timezone, show_timezone
+ NULL,
+ check_timezone, assign_timezone, show_timezone
},
{
{"timezone_abbreviations", PGC_USERSET, CLIENT_CONN_LOCALE,
NULL
},
&timezone_abbreviations_string,
- "UNKNOWN", assign_timezone_abbreviations, NULL
+ NULL,
+ check_timezone_abbreviations, assign_timezone_abbreviations, NULL
},
{
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
},
{
"that starts the server.")
},
&Unix_socket_group,
- "", NULL, NULL
+ "",
+ NULL, NULL, NULL
},
{
GUC_SUPERUSER_ONLY
},
&UnixSocketDir,
- "", assign_canonical_path, NULL
+ "",
+ check_canonical_path, NULL, NULL
},
{
GUC_LIST_INPUT
},
&ListenAddresses,
- "localhost", NULL, NULL
+ "localhost",
+ NULL, NULL, NULL
},
{
GUC_LIST_INPUT | GUC_LIST_QUOTE
},
&custom_variable_classes,
- NULL, assign_custom_variable_classes, NULL
+ NULL,
+ check_custom_variable_classes, NULL, NULL
},
{
GUC_SUPERUSER_ONLY
},
&data_directory,
+ NULL,
NULL, NULL, NULL
},
GUC_DISALLOW_IN_FILE | GUC_SUPERUSER_ONLY
},
&ConfigFileName,
+ NULL,
NULL, NULL, NULL
},
GUC_SUPERUSER_ONLY
},
&HbaFileName,
+ NULL,
NULL, NULL, NULL
},
GUC_SUPERUSER_ONLY
},
&IdentFileName,
+ NULL,
NULL, NULL, NULL
},
GUC_SUPERUSER_ONLY
},
&external_pid_file,
- NULL, assign_canonical_path, NULL
+ NULL,
+ check_canonical_path, NULL, NULL
},
{
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
},
{
GUC_LIST_INPUT
},
&SyncRepStandbyNames,
- "", assign_synchronous_standby_names, NULL
+ "",
+ check_synchronous_standby_names, NULL, NULL
},
{
NULL
},
&TSCurrentConfig,
- "pg_catalog.simple", assignTSCurrentConfig, NULL
+ "pg_catalog.simple",
+ check_TSCurrentConfig, assign_TSCurrentConfig, NULL
},
{
#else
"none",
#endif
- NULL, NULL
+ NULL, NULL, NULL
},
{
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
}
};
NULL
},
&backslash_quote,
- BACKSLASH_QUOTE_SAFE_ENCODING, backslash_quote_options, NULL, NULL
+ BACKSLASH_QUOTE_SAFE_ENCODING, backslash_quote_options,
+ NULL, NULL, NULL
},
{
NULL
},
&bytea_output,
- BYTEA_OUTPUT_HEX, bytea_output_options, NULL, NULL
+ BYTEA_OUTPUT_HEX, bytea_output_options,
+ NULL, NULL, NULL
},
{
" 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
},
{
},
&constraint_exclusion,
CONSTRAINT_EXCLUSION_PARTITION, constraint_exclusion_options,
- NULL, NULL
+ NULL, NULL, NULL
},
{
NULL
},
&DefaultXactIsoLevel,
- XACT_READ_COMMITTED, isolation_level_options, NULL, NULL
+ XACT_READ_COMMITTED, isolation_level_options,
+ NULL, NULL, NULL
},
{
GUC_REPORT
},
&IntervalStyle,
- INTSTYLE_POSTGRES, intervalstyle_options, NULL, NULL
+ INTSTYLE_POSTGRES, intervalstyle_options,
+ NULL, NULL, NULL
},
{
NULL
},
&Log_error_verbosity,
- PGERROR_DEFAULT, log_error_verbosity_options, NULL, NULL
+ PGERROR_DEFAULT, log_error_verbosity_options,
+ NULL, NULL, NULL
},
{
" 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
},
{
" 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
},
{
NULL
},
&log_statement,
- LOGSTMT_NONE, log_statement_options, NULL, NULL
+ LOGSTMT_NONE, log_statement_options,
+ NULL, NULL, NULL
},
{
#else
0,
#endif
- syslog_facility_options, assign_syslog_facility, NULL
+ syslog_facility_options,
+ NULL, assign_syslog_facility, NULL
},
{
},
&SessionReplicationRole,
SESSION_REPLICATION_ROLE_ORIGIN, session_replication_role_options,
- assign_session_replication_role, NULL
+ NULL, assign_session_replication_role, NULL
},
{
},
&synchronous_commit,
SYNCHRONOUS_COMMIT_ON, synchronous_commit_options,
- NULL, NULL
+ NULL, NULL, NULL
},
{
* 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
},
{
NULL
},
&pgstat_track_functions,
- TRACK_FUNC_OFF, track_function_options, NULL, NULL
+ TRACK_FUNC_OFF, track_function_options,
+ NULL, NULL, NULL
},
{
NULL
},
&wal_level,
- WAL_LEVEL_MINIMAL, wal_level_options, NULL
+ WAL_LEVEL_MINIMAL, wal_level_options,
+ NULL, NULL, NULL
},
{
},
&sync_method,
DEFAULT_SYNC_METHOD, sync_method_options,
- assign_xlog_sync_method, NULL
+ NULL, assign_xlog_sync_method, NULL
},
{
NULL
},
&xmlbinary,
- XMLBINARY_BASE64, xmlbinary_options, NULL, NULL
+ XMLBINARY_BASE64, xmlbinary_options,
+ NULL, NULL, NULL
},
{
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
}
};
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);
}
+/*
+ * 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
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)
{
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);
}
/*
* 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)
gconf->reset_source = PGC_S_DEFAULT;
gconf->source = PGC_S_DEFAULT;
gconf->stack = NULL;
+ gconf->extra = NULL;
gconf->sourcefile = NULL;
gconf->sourceline = 0;
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;
}
}
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:
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:
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:
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;
}
}
if (restorePrior || restoreMasked)
{
/* Perform appropriate restoration of the stacked value */
- union config_var_value newvalue;
+ config_var_value newvalue;
GucSource newsource;
if (restoreMasked)
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;
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;
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;
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;
}
* 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;
}
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
{
struct config_generic *record;
int elevel;
+ bool prohibitValueChange = false;
bool makeDefault;
if (context == PGC_SIGHUP || source == PGC_S_DEFAULT)
/*
* 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),
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),
{
struct config_bool *conf = (struct config_bool *) record;
bool newval;
+ void *newextra = NULL;
if (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)
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;
}
{
struct config_int *conf = (struct config_int *) record;
int newval;
+ void *newextra = NULL;
if (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)
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;
}
{
struct config_real *conf = (struct config_real *) record;
double newval;
+ void *newextra = NULL;
if (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)
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;
}
{
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;
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)
{
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)
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;
}
}
}
-/*
- * 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,
bool bootValue,
GucContext context,
int flags,
+ GucBoolCheckHook check_hook,
GucBoolAssignHook assign_hook,
GucShowHook show_hook)
{
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);
int maxValue,
GucContext context,
int flags,
+ GucIntCheckHook check_hook,
GucIntAssignHook assign_hook,
GucShowHook show_hook)
{
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);
double maxValue,
GucContext context,
int flags,
+ GucRealCheckHook check_hook,
GucRealAssignHook assign_hook,
GucShowHook show_hook)
{
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);
const char *bootValue,
GucContext context,
int flags,
+ GucStringCheckHook check_hook,
GucStringAssignHook assign_hook,
GucShowHook show_hook)
{
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);
const struct config_enum_entry * options,
GucContext context,
int flags,
+ GucEnumCheckHook check_hook,
GucEnumAssignHook assign_hook,
GucShowHook show_hook)
{
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);
}
-/*
- * 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
/*
/*
- * 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)
#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')
{
* non-identifier character
*/
pfree(buf.data);
- return NULL;
+ return false;
}
appendStringInfoChar(&buf, c);
symLen++;
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
}
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
}
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
}
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
* 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);
}
/*
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);
}
}
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));
}
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;
/*----------
* 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:
*
* 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
#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 *