Commit 2594cf0e authored by Tom Lane's avatar Tom Lane

Revise the API for GUC variable assign hooks.

The previous functions of assign hooks are now split between check hooks
and assign hooks, where the former can fail but the latter shouldn't.
Aside from being conceptually clearer, this approach exposes the
"canonicalized" form of the variable value to guc.c without having to do
an actual assignment.  And that lets us fix the problem recently noted by
Bernd Helmle that the auto-tune patch for wal_buffers resulted in bogus
log messages about "parameter "wal_buffers" cannot be changed without
restarting the server".  There may be some speed advantage too, because
this design lets hook functions avoid re-parsing variable values when
restoring a previous state after a rollback (they can store a pre-parsed
representation of the value instead).  This patch also resolves a
longstanding annoyance about custom error messages from variable assign
hooks: they should modify, not appear separately from, guc.c's own message
about "invalid parameter value".
parent 5d0e4623
......@@ -53,7 +53,7 @@ auth_delay_checks(Port *port, int status)
void
_PG_init(void)
{
/* Define custome GUC variables */
/* Define custom GUC variables */
DefineCustomIntVariable("auth_delay.milliseconds",
"Milliseconds to delay before reporting authentication failure",
NULL,
......@@ -63,6 +63,7 @@ _PG_init(void)
PGC_SIGHUP,
GUC_UNIT_MS,
NULL,
NULL,
NULL);
/* Install Hooks */
original_client_auth_hook = ClientAuthentication_hook;
......
......@@ -74,6 +74,7 @@ _PG_init(void)
PGC_SUSET,
GUC_UNIT_MS,
NULL,
NULL,
NULL);
DefineCustomBoolVariable("auto_explain.log_analyze",
......@@ -84,6 +85,7 @@ _PG_init(void)
PGC_SUSET,
0,
NULL,
NULL,
NULL);
DefineCustomBoolVariable("auto_explain.log_verbose",
......@@ -94,6 +96,7 @@ _PG_init(void)
PGC_SUSET,
0,
NULL,
NULL,
NULL);
DefineCustomBoolVariable("auto_explain.log_buffers",
......@@ -104,6 +107,7 @@ _PG_init(void)
PGC_SUSET,
0,
NULL,
NULL,
NULL);
DefineCustomEnumVariable("auto_explain.log_format",
......@@ -115,6 +119,7 @@ _PG_init(void)
PGC_SUSET,
0,
NULL,
NULL,
NULL);
DefineCustomBoolVariable("auto_explain.log_nested_statements",
......@@ -125,6 +130,7 @@ _PG_init(void)
PGC_SUSET,
0,
NULL,
NULL,
NULL);
EmitWarningsOnPlaceholders("auto_explain");
......
......@@ -219,6 +219,7 @@ _PG_init(void)
PGC_POSTMASTER,
0,
NULL,
NULL,
NULL);
DefineCustomEnumVariable("pg_stat_statements.track",
......@@ -230,6 +231,7 @@ _PG_init(void)
PGC_SUSET,
0,
NULL,
NULL,
NULL);
DefineCustomBoolVariable("pg_stat_statements.track_utility",
......@@ -240,6 +242,7 @@ _PG_init(void)
PGC_SUSET,
0,
NULL,
NULL,
NULL);
DefineCustomBoolVariable("pg_stat_statements.save",
......@@ -250,6 +253,7 @@ _PG_init(void)
PGC_SIGHUP,
0,
NULL,
NULL,
NULL);
EmitWarningsOnPlaceholders("pg_stat_statements");
......
......@@ -394,6 +394,7 @@ _PG_init(void)
PGC_SIGHUP,
GUC_NOT_IN_SAMPLE,
NULL,
NULL,
NULL);
/*
......@@ -412,6 +413,7 @@ _PG_init(void)
PGC_USERSET,
GUC_NOT_IN_SAMPLE,
NULL,
NULL,
NULL);
/*
......
......@@ -4887,36 +4887,60 @@ GetSystemIdentifier(void)
/*
* Auto-tune the number of XLOG buffers.
*
* If the user-set value of wal_buffers is -1, we auto-tune to about 3% of
* shared_buffers, with a maximum of one XLOG segment and a minimum of 8
* blocks (8 was the default value prior to PostgreSQL 9.1, when auto-tuning
* was added). We also clamp manually-set values to at least 4 blocks; prior
* to PostgreSQL 9.1, a minimum of 4 was enforced by guc.c, but since that
* is no longer possible, we just silently treat such values as a request for
* the minimum.
* The preferred setting for wal_buffers is about 3% of shared_buffers, with
* a maximum of one XLOG segment (there is little reason to think that more
* is helpful, at least so long as we force an fsync when switching log files)
* and a minimum of 8 blocks (which was the default value prior to PostgreSQL
* 9.1, when auto-tuning was added).
*
* This should not be called until NBuffers has received its final value.
*/
static void
XLOGTuneNumBuffers(void)
static int
XLOGChooseNumBuffers(void)
{
int xbuffers = XLOGbuffers;
char buf[32];
int xbuffers;
if (xbuffers == -1)
{
xbuffers = NBuffers / 32;
if (xbuffers > XLOG_SEG_SIZE / XLOG_BLCKSZ)
xbuffers = XLOG_SEG_SIZE / XLOG_BLCKSZ;
if (xbuffers < 8)
xbuffers = 8;
}
else if (xbuffers < 4)
xbuffers = 4;
return xbuffers;
}
if (xbuffers != XLOGbuffers)
/*
* GUC check_hook for wal_buffers
*/
bool
check_wal_buffers(int *newval, void **extra, GucSource source)
{
/*
* -1 indicates a request for auto-tune.
*/
if (*newval == -1)
{
snprintf(buf, sizeof(buf), "%d", xbuffers);
SetConfigOption("wal_buffers", buf, PGC_POSTMASTER, PGC_S_OVERRIDE);
/*
* If we haven't yet changed the boot_val default of -1, just let it
* be. We'll fix it when XLOGShmemSize is called.
*/
if (XLOGbuffers == -1)
return true;
/* Otherwise, substitute the auto-tune value */
*newval = XLOGChooseNumBuffers();
}
/*
* We clamp manually-set values to at least 4 blocks. Prior to PostgreSQL
* 9.1, a minimum of 4 was enforced by guc.c, but since that is no longer
* the case, we just silently treat such values as a request for the
* minimum. (We could throw an error instead, but that doesn't seem very
* helpful.)
*/
if (*newval < 4)
*newval = 4;
return true;
}
/*
......@@ -4927,8 +4951,19 @@ XLOGShmemSize(void)
{
Size size;
/* Figure out how many XLOG buffers we need. */
XLOGTuneNumBuffers();
/*
* If the value of wal_buffers is -1, use the preferred auto-tune value.
* This isn't an amazingly clean place to do this, but we must wait till
* NBuffers has received its final value, and must do it before using
* the value of XLOGbuffers to do anything important.
*/
if (XLOGbuffers == -1)
{
char buf[32];
snprintf(buf, sizeof(buf), "%d", XLOGChooseNumBuffers());
SetConfigOption("wal_buffers", buf, PGC_POSTMASTER, PGC_S_OVERRIDE);
}
Assert(XLOGbuffers > 0);
/* XLogCtl */
......@@ -8653,12 +8688,9 @@ get_sync_bit(int method)
/*
* GUC support
*/
bool
assign_xlog_sync_method(int new_sync_method, bool doit, GucSource source)
void
assign_xlog_sync_method(int new_sync_method, void *extra)
{
if (!doit)
return true;
if (sync_method != new_sync_method)
{
/*
......@@ -8678,8 +8710,6 @@ assign_xlog_sync_method(int new_sync_method, bool doit, GucSource source)
XLogFileClose();
}
}
return true;
}
......
......@@ -3468,31 +3468,33 @@ ResetTempTableNamespace(void)
* Routines for handling the GUC variable 'search_path'.
*/
/* assign_hook: validate new search_path, do extra actions as needed */
const char *
assign_search_path(const char *newval, bool doit, GucSource source)
/* check_hook: validate new search_path, if possible */
bool
check_search_path(char **newval, void **extra, GucSource source)
{
bool result = true;
char *rawname;
List *namelist;
ListCell *l;
/* Need a modifiable copy of string */
rawname = pstrdup(newval);
rawname = pstrdup(*newval);
/* Parse string into list of identifiers */
if (!SplitIdentifierString(rawname, ',', &namelist))
{
/* syntax error in name list */
GUC_check_errdetail("List syntax is invalid.");
pfree(rawname);
list_free(namelist);
return NULL;
return false;
}
/*
* If we aren't inside a transaction, we cannot do database access so
* cannot verify the individual names. Must accept the list on faith.
*/
if (source >= PGC_S_INTERACTIVE && IsTransactionState())
if (IsTransactionState())
{
/*
* Verify that all the names are either valid namespace names or
......@@ -3504,7 +3506,7 @@ assign_search_path(const char *newval, bool doit, GucSource source)
* DATABASE SET or ALTER USER SET command. It could be that the
* intended use of the search path is for some other database, so we
* should not error out if it mentions schemas not present in the
* current database. We reduce the message to NOTICE instead.
* current database. We issue a NOTICE instead.
*/
foreach(l, namelist)
{
......@@ -3516,24 +3518,37 @@ assign_search_path(const char *newval, bool doit, GucSource source)
continue;
if (!SearchSysCacheExists1(NAMESPACENAME,
CStringGetDatum(curname)))
ereport((source == PGC_S_TEST) ? NOTICE : ERROR,
{
if (source == PGC_S_TEST)
ereport(NOTICE,
(errcode(ERRCODE_UNDEFINED_SCHEMA),
errmsg("schema \"%s\" does not exist", curname)));
else
{
GUC_check_errdetail("schema \"%s\" does not exist", curname);
result = false;
break;
}
}
}
}
pfree(rawname);
list_free(namelist);
return result;
}
/* assign_hook: do extra actions as needed */
void
assign_search_path(const char *newval, void *extra)
{
/*
* We mark the path as needing recomputation, but don't do anything until
* it's needed. This avoids trying to do database access during GUC
* initialization.
* initialization, or outside a transaction.
*/
if (doit)
baseSearchPathValid = false;
return newval;
}
/*
......
......@@ -1023,9 +1023,9 @@ AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt)
* Routines for handling the GUC variable 'default_tablespace'.
*/
/* assign_hook: validate new default_tablespace, do extra actions as needed */
const char *
assign_default_tablespace(const char *newval, bool doit, GucSource source)
/* check_hook: validate new default_tablespace */
bool
check_default_tablespace(char **newval, void **extra, GucSource source)
{
/*
* If we aren't inside a transaction, we cannot do database access so
......@@ -1033,18 +1033,16 @@ assign_default_tablespace(const char *newval, bool doit, GucSource source)
*/
if (IsTransactionState())
{
if (newval[0] != '\0' &&
!OidIsValid(get_tablespace_oid(newval, true)))
if (**newval != '\0' &&
!OidIsValid(get_tablespace_oid(*newval, true)))
{
ereport(GUC_complaint_elevel(source),
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("tablespace \"%s\" does not exist",
newval)));
return NULL;
GUC_check_errdetail("Tablespace \"%s\" does not exist.",
*newval);
return false;
}
}
return newval;
return true;
}
/*
......@@ -1100,23 +1098,30 @@ GetDefaultTablespace(char relpersistence)
* Routines for handling the GUC variable 'temp_tablespaces'.
*/
/* assign_hook: validate new temp_tablespaces, do extra actions as needed */
const char *
assign_temp_tablespaces(const char *newval, bool doit, GucSource source)
typedef struct
{
int numSpcs;
Oid tblSpcs[1]; /* VARIABLE LENGTH ARRAY */
} temp_tablespaces_extra;
/* check_hook: validate new temp_tablespaces */
bool
check_temp_tablespaces(char **newval, void **extra, GucSource source)
{
char *rawname;
List *namelist;
/* Need a modifiable copy of string */
rawname = pstrdup(newval);
rawname = pstrdup(*newval);
/* Parse string into list of identifiers */
if (!SplitIdentifierString(rawname, ',', &namelist))
{
/* syntax error in name list */
GUC_check_errdetail("List syntax is invalid.");
pfree(rawname);
list_free(namelist);
return NULL;
return false;
}
/*
......@@ -1126,17 +1131,13 @@ assign_temp_tablespaces(const char *newval, bool doit, GucSource source)
*/
if (IsTransactionState())
{
/*
* If we error out below, or if we are called multiple times in one
* transaction, we'll leak a bit of TopTransactionContext memory.
* Doesn't seem worth worrying about.
*/
temp_tablespaces_extra *myextra;
Oid *tblSpcs;
int numSpcs;
ListCell *l;
tblSpcs = (Oid *) MemoryContextAlloc(TopTransactionContext,
list_length(namelist) * sizeof(Oid));
/* temporary workspace until we are done verifying the list */
tblSpcs = (Oid *) palloc(list_length(namelist) * sizeof(Oid));
numSpcs = 0;
foreach(l, namelist)
{
......@@ -1169,7 +1170,7 @@ assign_temp_tablespaces(const char *newval, bool doit, GucSource source)
continue;
}
/* Check permissions similarly */
/* Check permissions, similarly complaining only if interactive */
aclresult = pg_tablespace_aclcheck(curoid, GetUserId(),
ACL_CREATE);
if (aclresult != ACLCHECK_OK)
......@@ -1182,17 +1183,41 @@ assign_temp_tablespaces(const char *newval, bool doit, GucSource source)
tblSpcs[numSpcs++] = curoid;
}
/* If actively "doing it", give the new list to fd.c */
if (doit)
SetTempTablespaces(tblSpcs, numSpcs);
else
/* Now prepare an "extra" struct for assign_temp_tablespaces */
myextra = malloc(offsetof(temp_tablespaces_extra, tblSpcs) +
numSpcs * sizeof(Oid));
if (!myextra)
return false;
myextra->numSpcs = numSpcs;
memcpy(myextra->tblSpcs, tblSpcs, numSpcs * sizeof(Oid));
*extra = (void *) myextra;
pfree(tblSpcs);
}
pfree(rawname);
list_free(namelist);
return newval;
return true;
}
/* assign_hook: do extra actions as needed */
void
assign_temp_tablespaces(const char *newval, void *extra)
{
temp_tablespaces_extra *myextra = (temp_tablespaces_extra *) extra;
/*
* If check_temp_tablespaces was executed inside a transaction, then pass
* the list it made to fd.c. Otherwise, clear fd.c's list; we must be
* still outside a transaction, or else restoring during transaction exit,
* and in either case we can just let the next PrepareTempTablespaces call
* make things sane.
*/
if (myextra)
SetTempTablespaces(myextra->tblSpcs, myextra->numSpcs);
else
SetTempTablespaces(NULL, 0);
}
/*
......
This diff is collapsed.
......@@ -2,7 +2,10 @@
CATALOG_NAME := postgres
AVAIL_LANGUAGES := de es fr ja pt_BR tr
GETTEXT_FILES := + gettext-files
GETTEXT_TRIGGERS:= _ errmsg errmsg_plural:1,2 errdetail errdetail_log errdetail_plural:1,2 errhint errcontext write_stderr yyerror parser_yyerror
GETTEXT_TRIGGERS:= _ errmsg errmsg_plural:1,2 errdetail errdetail_log \
errdetail_plural:1,2 errhint errcontext \
GUC_check_errmsg GUC_check_errdetail GUC_check_errhint \
write_stderr yyerror parser_yyerror
gettext-files: distprep
find $(srcdir)/ $(srcdir)/../port/ -name '*.c' -print >$@
......
......@@ -639,25 +639,23 @@ SyncRepQueueIsOrderedByLSN(void)
* ===========================================================
*/
const char *
assign_synchronous_standby_names(const char *newval, bool doit, GucSource source)
bool
check_synchronous_standby_names(char **newval, void **extra, GucSource source)
{
char *rawstring;
List *elemlist;
/* Need a modifiable copy of string */
rawstring = pstrdup(newval);
rawstring = pstrdup(*newval);
/* Parse string into list of identifiers */
if (!SplitIdentifierString(rawstring, ',', &elemlist))
{
/* syntax error in list */
GUC_check_errdetail("List syntax is invalid.");
pfree(rawstring);
list_free(elemlist);
ereport(GUC_complaint_elevel(source),
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid list syntax for parameter \"synchronous_standby_names\"")));
return NULL;
return false;
}
/*
......@@ -671,5 +669,5 @@ assign_synchronous_standby_names(const char *newval, bool doit, GucSource source
pfree(rawstring);
list_free(elemlist);
return newval;
return true;
}
......@@ -2804,7 +2804,8 @@ RecoveryConflictInterrupt(ProcSignalReason reason)
break;
default:
elog(FATAL, "Unknown conflict mode");
elog(FATAL, "unrecognized conflict mode: %d",
(int) reason);
}
Assert(RecoveryConflictPending && (QueryCancelPending || ProcDiePending));
......@@ -3062,27 +3063,32 @@ check_stack_depth(void)
#endif /* IA64 */
}
/* GUC assign hook for max_stack_depth */
/* GUC check hook for max_stack_depth */
bool
assign_max_stack_depth(int newval, bool doit, GucSource source)
check_max_stack_depth(int *newval, void **extra, GucSource source)
{
long newval_bytes = newval * 1024L;
long newval_bytes = *newval * 1024L;
long stack_rlimit = get_stack_depth_rlimit();
if (stack_rlimit > 0 && newval_bytes > stack_rlimit - STACK_DEPTH_SLOP)
{
ereport(GUC_complaint_elevel(source),
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("\"max_stack_depth\" must not exceed %ldkB",
(stack_rlimit - STACK_DEPTH_SLOP) / 1024L),
errhint("Increase the platform's stack depth limit via \"ulimit -s\" or local equivalent.")));
GUC_check_errdetail("\"max_stack_depth\" must not exceed %ldkB.",
(stack_rlimit - STACK_DEPTH_SLOP) / 1024L);
GUC_check_errhint("Increase the platform's stack depth limit via \"ulimit -s\" or local equivalent.");
return false;
}
if (doit)
max_stack_depth_bytes = newval_bytes;
return true;
}
/* GUC assign hook for max_stack_depth */
void
assign_max_stack_depth(int newval, void *extra)
{
long newval_bytes = newval * 1024L;
max_stack_depth_bytes = newval_bytes;
}
/*
* set_debug_options --- apply "-d N" command line option
......
......@@ -4140,20 +4140,17 @@ CheckDateTokenTables(void)
/*
* This function gets called during timezone config file load or reload
* to create the final array of timezone tokens. The argument array
* is already sorted in name order. This data is in a temporary memory
* context and must be copied to somewhere permanent.
* is already sorted in name order. The data is converted to datetkn
* format and installed in *tbl, which must be allocated by the caller.
*/
void
InstallTimeZoneAbbrevs(tzEntry *abbrevs, int n)
ConvertTimeZoneAbbrevs(TimeZoneAbbrevTable *tbl,
struct tzEntry *abbrevs, int n)
{
datetkn *newtbl;
datetkn *newtbl = tbl->abbrevs;
int i;
/*
* Copy the data into TopMemoryContext and convert to datetkn format.
*/
newtbl = (datetkn *) MemoryContextAlloc(TopMemoryContext,
n * sizeof(datetkn));
tbl->numabbrevs = n;
for (i = 0; i < n; i++)
{
strncpy(newtbl[i].token, abbrevs[i].abbrev, TOKMAXLEN);
......@@ -4163,12 +4160,20 @@ InstallTimeZoneAbbrevs(tzEntry *abbrevs, int n)
/* Check the ordering, if testing */
Assert(CheckDateTokenTable("timezone offset", newtbl, n));
}
/*
* Install a TimeZoneAbbrevTable as the active table.
*
* Caller is responsible that the passed table doesn't go away while in use.
*/
void
InstallTimeZoneAbbrevs(TimeZoneAbbrevTable *tbl)
{
int i;
/* Now safe to replace existing table (if any) */
if (timezonetktbl)
pfree(timezonetktbl);
timezonetktbl = newtbl;
sztimezonetktbl = n;
timezonetktbl = tbl->abbrevs;
sztimezonetktbl = tbl->numabbrevs;
/* clear date cache in case it contains any stale timezone names */
for (i = 0; i < MAXDATEFIELDS; i++)
......
......@@ -236,52 +236,53 @@ check_locale(int category, const char *value)
return ret;
}
/* GUC assign hooks */
/*
* This is common code for several locale categories. This doesn't
* actually set the locale permanently, it only tests if the locale is
* valid. (See explanation at the top of this file.)
* GUC check/assign hooks
*
* For most locale categories, the assign hook doesn't actually set the locale
* permanently, just reset flags so that the next use will cache the
* appropriate values. (See explanation at the top of this file.)
*
* Note: we accept value = "" as selecting the postmaster's environment
* value, whatever it was (so long as the environment setting is legal).
* This will have been locked down by an earlier call to pg_perm_setlocale.
*/
static const char *
locale_xxx_assign(int category, const char *value, bool doit, GucSource source)
bool
check_locale_monetary(char **newval, void **extra, GucSource source)
{
if (!check_locale(category, value))
value = NULL; /* set failure return marker */
return check_locale(LC_MONETARY, *newval);
}
/* need to reload cache next time? */
if (doit && value != NULL)
{
void
assign_locale_monetary(const char *newval, void *extra)
{
CurrentLocaleConvValid = false;
CurrentLCTimeValid = false;
}
return value;
}
const char *
locale_monetary_assign(const char *value, bool doit, GucSource source)
bool
check_locale_numeric(char **newval, void **extra, GucSource source)
{
return locale_xxx_assign(LC_MONETARY, value, doit, source);
return check_locale(LC_NUMERIC, *newval);
}
const char *
locale_numeric_assign(const char *value, bool doit, GucSource source)
void
assign_locale_numeric(const char *newval, void *extra)
{
return locale_xxx_assign(LC_NUMERIC, value, doit, source);
CurrentLocaleConvValid = false;
}
const char *
locale_time_assign(const char *value, bool doit, GucSource source)
bool
check_locale_time(char **newval, void **extra, GucSource source)
{
return locale_xxx_assign(LC_TIME, value, doit, source);
return check_locale(LC_TIME, *newval);
}
void
assign_locale_time(const char *newval, void *extra)
{
CurrentLCTimeValid = false;
}
/*
* We allow LC_MESSAGES to actually be set globally.
......@@ -293,31 +294,39 @@ locale_time_assign(const char *value, bool doit, GucSource source)
* The idea there is just to accept the environment setting *if possible*
* during startup, until we can read the proper value from postgresql.conf.
*/
const char *
locale_messages_assign(const char *value, bool doit, GucSource source)
bool
check_locale_messages(char **newval, void **extra, GucSource source)
{
if (*value == '\0' && source != PGC_S_DEFAULT)
return NULL;
if (**newval == '\0')
{
if (source == PGC_S_DEFAULT)
return true;
else
return false;
}
/*
* LC_MESSAGES category does not exist everywhere, but accept it anyway
*
* On Windows, we can't even check the value, so the non-doit case is a
* no-op
* On Windows, we can't even check the value, so accept blindly
*/
#if defined(LC_MESSAGES) && !defined(WIN32)
return check_locale(LC_MESSAGES, *newval);
#else
return true;
#endif
}
void
assign_locale_messages(const char *newval, void *extra)
{
/*
* LC_MESSAGES category does not exist everywhere, but accept it anyway.
* We ignore failure, as per comment above.
*/
#ifdef LC_MESSAGES
if (doit)
{
if (!pg_perm_setlocale(LC_MESSAGES, value))
if (source != PGC_S_DEFAULT)
return NULL;
}
#ifndef WIN32
else
value = locale_xxx_assign(LC_MESSAGES, value, false, source);
#endif /* WIN32 */
#endif /* LC_MESSAGES */
return value;
(void) pg_perm_setlocale(LC_MESSAGES, newval);
#endif
}
......
......@@ -587,8 +587,9 @@ getTSCurrentConfig(bool emitError)
return TSCurrentConfigCache;
}
const char *
assignTSCurrentConfig(const char *newval, bool doit, GucSource source)
/* GUC check_hook for default_text_search_config */
bool
check_TSCurrentConfig(char **newval, void **extra, GucSource source)
{
/*
* If we aren't inside a transaction, we cannot do database access so
......@@ -601,10 +602,10 @@ assignTSCurrentConfig(const char *newval, bool doit, GucSource source)
Form_pg_ts_config cfg;
char *buf;
cfgId = get_ts_config_oid(stringToQualifiedNameList(newval), true);
cfgId = get_ts_config_oid(stringToQualifiedNameList(*newval), true);
if (!OidIsValid(cfgId))
return NULL;
return false;
/*
* Modify the actually stored value to be fully qualified, to ensure
......@@ -622,17 +623,20 @@ assignTSCurrentConfig(const char *newval, bool doit, GucSource source)
ReleaseSysCache(tuple);
/* GUC wants it malloc'd not palloc'd */
newval = strdup(buf);
free(*newval);
*newval = strdup(buf);
pfree(buf);
if (doit && newval)
TSCurrentConfigCache = cfgId;
}
else
{
if (doit)
TSCurrentConfigCache = InvalidOid;
if (!newval)
return false;
}
return newval;
return true;
}
/* GUC assign_hook for default_text_search_config */
void
assign_TSCurrentConfig(const char *newval, void *extra)
{
/* Just reset the cache to force a lookup on first use */
TSCurrentConfigCache = InvalidOid;
}
......@@ -1156,6 +1156,62 @@ elog_finish(int elevel, const char *fmt,...)
errfinish(0);
}
/*
* Functions to allow construction of error message strings separately from
* the ereport() call itself.
*
* The expected calling convention is
*
* pre_format_elog_string(errno, domain), var = format_elog_string(format,...)
*
* which can be hidden behind a macro such as GUC_check_errdetail(). We
* assume that any functions called in the arguments of format_elog_string()
* cannot result in re-entrant use of these functions --- otherwise the wrong
* text domain might be used, or the wrong errno substituted for %m. This is
* okay for the current usage with GUC check hooks, but might need further
* effort someday.
*
* The result of format_elog_string() is stored in ErrorContext, and will
* therefore survive until FlushErrorState() is called.
*/
static int save_format_errnumber;
static const char *save_format_domain;
void
pre_format_elog_string(int errnumber, const char *domain)
{
/* Save errno before evaluation of argument functions can change it */
save_format_errnumber = errnumber;
/* Save caller's text domain */
save_format_domain = domain;
}
char *
format_elog_string(const char *fmt, ...)
{
ErrorData errdata;
ErrorData *edata;
MemoryContext oldcontext;
/* Initialize a mostly-dummy error frame */
edata = &errdata;
MemSet(edata, 0, sizeof(ErrorData));
/* the default text domain is the backend's */
edata->domain = save_format_domain ? save_format_domain : PG_TEXTDOMAIN("postgres");
/* set the errno to be used to interpret %m */
edata->saved_errno = save_format_errnumber;
oldcontext = MemoryContextSwitchTo(ErrorContext);
EVALUATE_MESSAGE(message, false, true);
MemoryContextSwitchTo(oldcontext);
return edata->message;
}
/*
* Actual output of the top-of-stack error message
*
......
......@@ -77,12 +77,16 @@ static int cliplen(const char *str, int len, int limit);
/*
* Set the client encoding and save fmgrinfo for the conversion
* function if necessary. Returns 0 if okay, -1 if not (bad encoding
* or can't support conversion)
* Prepare for a future call to SetClientEncoding. Success should mean
* that SetClientEncoding is guaranteed to succeed for this encoding request.
*
* (But note that success before backend_startup_complete does not guarantee
* success after ...)
*
* Returns 0 if okay, -1 if not (bad encoding or can't support conversion)
*/
int
SetClientEncoding(int encoding, bool doit)
PrepareClientEncoding(int encoding)
{
int current_server_encoding;
ListCell *lc;
......@@ -92,11 +96,7 @@ SetClientEncoding(int encoding, bool doit)
/* Can't do anything during startup, per notes above */
if (!backend_startup_complete)
{
if (doit)
pending_client_encoding = encoding;
return 0;
}
current_server_encoding = GetDatabaseEncoding();
......@@ -106,15 +106,7 @@ SetClientEncoding(int encoding, bool doit)
if (current_server_encoding == encoding ||
current_server_encoding == PG_SQL_ASCII ||
encoding == PG_SQL_ASCII)
{
if (doit)
{
ClientEncoding = &pg_enc2name_tbl[encoding];
ToServerConvProc = NULL;
ToClientConvProc = NULL;
}
return 0;
}
if (IsTransactionState())
{
......@@ -138,12 +130,6 @@ SetClientEncoding(int encoding, bool doit)
if (!OidIsValid(to_client_proc))
return -1;
/*
* Done if not wanting to actually apply setting.
*/
if (!doit)
return 0;
/*
* Load the fmgr info into TopMemoryContext (could still fail here)
*/
......@@ -162,30 +148,9 @@ SetClientEncoding(int encoding, bool doit)
MemoryContextSwitchTo(oldcontext);
/*
* Everything is okay, so apply the setting.
*/
ClientEncoding = &pg_enc2name_tbl[encoding];
ToServerConvProc = &convinfo->to_server_info;
ToClientConvProc = &convinfo->to_client_info;
/*
* Remove any older entry for the same encoding pair (this is just to
* avoid memory leakage).
* We cannot yet remove any older entry for the same encoding pair,
* since it could still be in use. SetClientEncoding will clean up.
*/
foreach(lc, ConvProcList)
{
ConvProcInfo *oldinfo = (ConvProcInfo *) lfirst(lc);
if (oldinfo == convinfo)
continue;
if (oldinfo->s_encoding == convinfo->s_encoding &&
oldinfo->c_encoding == convinfo->c_encoding)
{
ConvProcList = list_delete_ptr(ConvProcList, oldinfo);
pfree(oldinfo);
break; /* need not look further */
}
}
return 0; /* success */
}
......@@ -205,24 +170,91 @@ SetClientEncoding(int encoding, bool doit)
if (oldinfo->s_encoding == current_server_encoding &&
oldinfo->c_encoding == encoding)
return 0;
}
return -1; /* it's not cached, so fail */
}
}
/*
* Set the active client encoding and set up the conversion-function pointers.
* PrepareClientEncoding should have been called previously for this encoding.
*
* Returns 0 if okay, -1 if not (bad encoding or can't support conversion)
*/
int
SetClientEncoding(int encoding)
{
int current_server_encoding;
bool found;
ListCell *lc;
if (!PG_VALID_FE_ENCODING(encoding))
return -1;
/* Can't do anything during startup, per notes above */
if (!backend_startup_complete)
{
if (doit)
pending_client_encoding = encoding;
return 0;
}
current_server_encoding = GetDatabaseEncoding();
/*
* Check for cases that require no conversion function.
*/
if (current_server_encoding == encoding ||
current_server_encoding == PG_SQL_ASCII ||
encoding == PG_SQL_ASCII)
{
ClientEncoding = &pg_enc2name_tbl[encoding];
ToServerConvProc = &oldinfo->to_server_info;
ToClientConvProc = &oldinfo->to_client_info;
}
ToServerConvProc = NULL;
ToClientConvProc = NULL;
return 0;
}
/*
* Search the cache for the entry previously prepared by
* PrepareClientEncoding; if there isn't one, we lose. While at it,
* release any duplicate entries so that repeated Prepare/Set cycles
* don't leak memory.
*/
found = false;
foreach(lc, ConvProcList)
{
ConvProcInfo *convinfo = (ConvProcInfo *) lfirst(lc);
if (convinfo->s_encoding == current_server_encoding &&
convinfo->c_encoding == encoding)
{
if (!found)
{
/* Found newest entry, so set up */
ClientEncoding = &pg_enc2name_tbl[encoding];
ToServerConvProc = &convinfo->to_server_info;
ToClientConvProc = &convinfo->to_client_info;
found = true;
}
else
{
/* Duplicate entry, release it */
ConvProcList = list_delete_ptr(ConvProcList, convinfo);
pfree(convinfo);
}
}
}
if (found)
return 0; /* success */
else
return -1; /* it's not cached, so fail */
}
}
/*
* Initialize client encoding if necessary.
* called from InitPostgres() once during backend startup.
* Initialize client encoding conversions.
* Called from InitPostgres() once during backend startup.
*/
void
InitializeClientEncoding(void)
......@@ -230,7 +262,8 @@ InitializeClientEncoding(void)
Assert(!backend_startup_complete);
backend_startup_complete = true;
if (SetClientEncoding(pending_client_encoding, true) < 0)
if (PrepareClientEncoding(pending_client_encoding) < 0 ||
SetClientEncoding(pending_client_encoding) < 0)
{
/*
* Oops, the requested conversion is not available. We couldn't fail
......
This diff is collapsed.
......@@ -141,7 +141,8 @@ ProcessConfigFile(GucContext context)
*/
cvc_struct = (struct config_string *)
find_option("custom_variable_classes", false, elevel);
if (cvc_struct && cvc_struct->gen.reset_source > PGC_S_FILE)
Assert(cvc_struct);
if (cvc_struct->gen.reset_source > PGC_S_FILE)
{
cvc = guc_strdup(elevel, cvc_struct->reset_val);
if (cvc == NULL)
......@@ -151,19 +152,18 @@ ProcessConfigFile(GucContext context)
guc_name_compare(head->name, "custom_variable_classes") == 0)
{
/*
* Need to canonicalize the value via the assign hook. Casting away
* const is a bit ugly, but we know the result is malloc'd.
* Need to canonicalize the value by calling the check hook.
*/
cvc = (char *) assign_custom_variable_classes(head->value,
false, PGC_S_FILE);
void *extra = NULL;
cvc = guc_strdup(elevel, head->value);
if (cvc == NULL)
{
ereport(elevel,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid value for parameter \"%s\": \"%s\"",
head->name, head->value)));
goto cleanup_list;
}
if (!call_string_check_hook(cvc_struct, &cvc, &extra,
PGC_S_FILE, elevel))
goto cleanup_list;
if (extra)
free(extra);
}
/*
......
This diff is collapsed.
This diff is collapsed.
......@@ -13,31 +13,28 @@
#include "utils/guc.h"
extern const char *assign_datestyle(const char *value,
bool doit, GucSource source);
extern const char *assign_timezone(const char *value,
bool doit, GucSource source);
extern bool check_datestyle(char **newval, void **extra, GucSource source);
extern void assign_datestyle(const char *newval, void *extra);
extern bool check_timezone(char **newval, void **extra, GucSource source);
extern void assign_timezone(const char *newval, void *extra);
extern const char *show_timezone(void);
extern const char *assign_log_timezone(const char *value,
bool doit, GucSource source);
extern bool check_log_timezone(char **newval, void **extra, GucSource source);
extern void assign_log_timezone(const char *newval, void *extra);
extern const char *show_log_timezone(void);
extern bool assign_transaction_read_only(bool value,
bool doit, GucSource source);
extern const char *assign_XactIsoLevel(const char *value,
bool doit, GucSource source);
extern bool check_transaction_read_only(bool *newval, void **extra, GucSource source);
extern bool check_XactIsoLevel(char **newval, void **extra, GucSource source);
extern void assign_XactIsoLevel(const char *newval, void *extra);
extern const char *show_XactIsoLevel(void);
extern bool assign_transaction_deferrable(bool newval, bool doit,
GucSource source);
extern bool assign_random_seed(double value,
bool doit, GucSource source);
extern bool check_transaction_deferrable(bool *newval, void **extra, GucSource source);
extern bool check_random_seed(double *newval, void **extra, GucSource source);
extern void assign_random_seed(double newval, void *extra);
extern const char *show_random_seed(void);
extern const char *assign_client_encoding(const char *value,
bool doit, GucSource source);
extern const char *assign_role(const char *value,
bool doit, GucSource source);
extern bool check_client_encoding(char **newval, void **extra, GucSource source);
extern void assign_client_encoding(const char *newval, void *extra);
extern bool check_session_authorization(char **newval, void **extra, GucSource source);
extern void assign_session_authorization(const char *newval, void *extra);
extern bool check_role(char **newval, void **extra, GucSource source);
extern void assign_role(const char *newval, void *extra);
extern const char *show_role(void);
extern const char *assign_session_authorization(const char *value,
bool doit, GucSource source);
extern const char *show_session_authorization(void);
#endif /* VARIABLE_H */
......@@ -397,7 +397,8 @@ extern size_t wchar2char(char *to, const wchar_t *from, size_t tolen, Oid collat
extern size_t char2wchar(wchar_t *to, size_t tolen, const char *from, size_t fromlen, Oid collation);
#endif
extern int SetClientEncoding(int encoding, bool doit);
extern int PrepareClientEncoding(int encoding);
extern int SetClientEncoding(int encoding);
extern void InitializeClientEncoding(void);
extern int pg_get_client_encoding(void);
extern const char *pg_get_client_encoding_name(void);
......
......@@ -45,6 +45,6 @@ extern void SyncRepUpdateSyncStandbysDefined(void);
/* called by various procs */
extern int SyncRepWakeQueue(bool all);
extern const char *assign_synchronous_standby_names(const char *newval, bool doit, GucSource source);
extern bool check_synchronous_standby_names(char **newval, void **extra, GucSource source);
#endif /* _SYNCREP_H */
......@@ -57,7 +57,8 @@ extern PlannedStmt *pg_plan_query(Query *querytree, int cursorOptions,
extern List *pg_plan_queries(List *querytrees, int cursorOptions,
ParamListInfo boundParams);
extern bool assign_max_stack_depth(int newval, bool doit, GucSource source);
extern bool check_max_stack_depth(int *newval, void **extra, GucSource source);
extern void assign_max_stack_depth(int newval, void *extra);
extern void die(SIGNAL_ARGS);
extern void quickdie(SIGNAL_ARGS);
......
......@@ -93,6 +93,7 @@ extern TSDictionaryCacheEntry *lookup_ts_dictionary_cache(Oid dictId);
extern TSConfigCacheEntry *lookup_ts_config_cache(Oid cfgId);
extern Oid getTSCurrentConfig(bool emitError);
extern const char *assignTSCurrentConfig(const char *newval, bool doit, GucSource source);
extern bool check_TSCurrentConfig(char **newval, void **extra, GucSource source);
extern void assign_TSCurrentConfig(const char *newval, void *extra);
#endif /* TS_CACHE_H */
......@@ -20,7 +20,9 @@
#include <math.h>
#include "utils/timestamp.h"
#include "utils/tzparser.h"
/* this struct is declared in utils/tzparser.h: */
struct tzEntry;
/* ----------------------------------------------------------------
......@@ -203,6 +205,13 @@ typedef struct
char value; /* this may be unsigned, alas */
} datetkn;
/* one of its uses is in tables of time zone abbreviations */
typedef struct TimeZoneAbbrevTable
{
int numabbrevs;
datetkn abbrevs[1]; /* VARIABLE LENGTH ARRAY */
} TimeZoneAbbrevTable;
/* FMODULO()
* Macro to replace modf(), which is broken on some platforms.
......@@ -317,7 +326,10 @@ extern int DecodeUnits(int field, char *lowtoken, int *val);
extern int j2day(int jd);
extern bool CheckDateTokenTables(void);
extern void InstallTimeZoneAbbrevs(tzEntry *abbrevs, int n);
extern void ConvertTimeZoneAbbrevs(TimeZoneAbbrevTable *tbl,
struct tzEntry *abbrevs, int n);
extern void InstallTimeZoneAbbrevs(TimeZoneAbbrevTable *tbl);
extern Datum pg_timezone_abbrevs(PG_FUNCTION_ARGS);
extern Datum pg_timezone_names(PG_FUNCTION_ARGS);
......
......@@ -200,6 +200,15 @@ elog_finish(int elevel, const char *fmt,...)
__attribute__((format(printf, 2, 3)));
/* Support for constructing error strings separately from ereport() calls */
extern void pre_format_elog_string(int errnumber, const char *domain);
extern char *format_elog_string(const char *fmt,...)
/* This extension allows gcc to check the format string for consistency with
the supplied arguments. */
__attribute__((format(printf, 1, 2)));
/* Support for attaching context information to error reports */
typedef struct ErrorContextCallback
......
......@@ -97,7 +97,8 @@ typedef enum
} GucSource;
/*
* Parsing the configuation file will return a list of name-value pairs
* Parsing the configuration file will return a list of name-value pairs
* with source location info.
*/
typedef struct ConfigVariable
{
......@@ -117,7 +118,9 @@ extern bool ParseConfigFp(FILE *fp, const char *config_file,
extern void FreeConfigVariables(ConfigVariable *list);
/*
* Enum values are made up of an array of name-value pairs
* The possible values of an enum variable are specified by an array of
* name-value pairs. The "hidden" flag means the value is accepted but
* won't be displayed when guc.c is asked for a list of acceptable values.
*/
struct config_enum_entry
{
......@@ -126,15 +129,26 @@ struct config_enum_entry
bool hidden;
};
typedef const char *(*GucStringAssignHook) (const char *newval, bool doit, GucSource source);
typedef bool (*GucBoolAssignHook) (bool newval, bool doit, GucSource source);
typedef bool (*GucIntAssignHook) (int newval, bool doit, GucSource source);
typedef bool (*GucRealAssignHook) (double newval, bool doit, GucSource source);
typedef bool (*GucEnumAssignHook) (int newval, bool doit, GucSource source);
/*
* Signatures for per-variable check/assign/show hook functions
*/
typedef bool (*GucBoolCheckHook) (bool *newval, void **extra, GucSource source);
typedef bool (*GucIntCheckHook) (int *newval, void **extra, GucSource source);
typedef bool (*GucRealCheckHook) (double *newval, void **extra, GucSource source);
typedef bool (*GucStringCheckHook) (char **newval, void **extra, GucSource source);
typedef bool (*GucEnumCheckHook) (int *newval, void **extra, GucSource source);
typedef void (*GucBoolAssignHook) (bool newval, void *extra);
typedef void (*GucIntAssignHook) (int newval, void *extra);
typedef void (*GucRealAssignHook) (double newval, void *extra);
typedef void (*GucStringAssignHook) (const char *newval, void *extra);
typedef void (*GucEnumAssignHook) (int newval, void *extra);
typedef const char *(*GucShowHook) (void);
/*
* Miscellaneous
*/
typedef enum
{
/* Types of set_config_option actions */
......@@ -201,7 +215,6 @@ extern char *ConfigFileName;
extern char *HbaFileName;
extern char *IdentFileName;
extern char *external_pid_file;
extern char *XactIsoLevel_string;
extern char *application_name;
......@@ -209,6 +222,9 @@ extern int tcp_keepalives_idle;
extern int tcp_keepalives_interval;
extern int tcp_keepalives_count;
/*
* Functions exported by guc.c
*/
extern void SetConfigOption(const char *name, const char *value,
GucContext context, GucSource source);
......@@ -220,6 +236,7 @@ extern void DefineCustomBoolVariable(
bool bootValue,
GucContext context,
int flags,
GucBoolCheckHook check_hook,
GucBoolAssignHook assign_hook,
GucShowHook show_hook);
......@@ -233,6 +250,7 @@ extern void DefineCustomIntVariable(
int maxValue,
GucContext context,
int flags,
GucIntCheckHook check_hook,
GucIntAssignHook assign_hook,
GucShowHook show_hook);
......@@ -246,6 +264,7 @@ extern void DefineCustomRealVariable(
double maxValue,
GucContext context,
int flags,
GucRealCheckHook check_hook,
GucRealAssignHook assign_hook,
GucShowHook show_hook);
......@@ -257,6 +276,7 @@ extern void DefineCustomStringVariable(
const char *bootValue,
GucContext context,
int flags,
GucStringCheckHook check_hook,
GucStringAssignHook assign_hook,
GucShowHook show_hook);
......@@ -269,6 +289,7 @@ extern void DefineCustomEnumVariable(
const struct config_enum_entry * options,
GucContext context,
int flags,
GucEnumCheckHook check_hook,
GucEnumAssignHook assign_hook,
GucShowHook show_hook);
......@@ -308,8 +329,6 @@ extern ArrayType *GUCArrayAdd(ArrayType *array, const char *name, const char *va
extern ArrayType *GUCArrayDelete(ArrayType *array, const char *name);
extern ArrayType *GUCArrayReset(ArrayType *array);
extern int GUC_complaint_elevel(GucSource source);
extern void pg_timezone_abbrev_initialize(void);
#ifdef EXEC_BACKEND
......@@ -317,6 +336,27 @@ extern void write_nondefault_variables(GucContext context);
extern void read_nondefault_variables(void);
#endif
/* Support for messages reported from GUC check hooks */
extern PGDLLIMPORT char *GUC_check_errmsg_string;
extern PGDLLIMPORT char *GUC_check_errdetail_string;
extern PGDLLIMPORT char *GUC_check_errhint_string;
extern void GUC_check_errcode(int sqlerrcode);
#define GUC_check_errmsg \
pre_format_elog_string(errno, TEXTDOMAIN), \
GUC_check_errmsg_string = format_elog_string
#define GUC_check_errdetail \
pre_format_elog_string(errno, TEXTDOMAIN), \
GUC_check_errdetail_string = format_elog_string
#define GUC_check_errhint \
pre_format_elog_string(errno, TEXTDOMAIN), \
GUC_check_errhint_string = format_elog_string
/*
* The following functions are not in guc.c, but are declared here to avoid
* having to include guc.h in some widely used headers that it really doesn't
......@@ -324,17 +364,16 @@ extern void read_nondefault_variables(void);
*/
/* in commands/tablespace.c */
extern const char *assign_default_tablespace(const char *newval,
bool doit, GucSource source);
extern const char *assign_temp_tablespaces(const char *newval,
bool doit, GucSource source);
extern bool check_default_tablespace(char **newval, void **extra, GucSource source);
extern bool check_temp_tablespaces(char **newval, void **extra, GucSource source);
extern void assign_temp_tablespaces(const char *newval, void *extra);
/* in catalog/namespace.c */
extern const char *assign_search_path(const char *newval,
bool doit, GucSource source);
extern bool check_search_path(char **newval, void **extra, GucSource source);
extern void assign_search_path(const char *newval, void *extra);
/* in access/transam/xlog.c */
extern bool assign_xlog_sync_method(int newval,
bool doit, GucSource source);
extern bool check_wal_buffers(int *newval, void **extra, GucSource source);
extern void assign_xlog_sync_method(int new_sync_method, void *extra);
#endif /* GUC_H */
......@@ -28,7 +28,7 @@ enum config_type
PGC_ENUM
};
union config_var_value
union config_var_val
{
bool boolval;
int intval;
......@@ -37,6 +37,16 @@ union config_var_value
int enumval;
};
/*
* The actual value of a GUC variable can include a malloc'd opaque struct
* "extra", which is created by its check_hook and used by its assign_hook.
*/
typedef struct config_var_value
{
union config_var_val val;
void *extra;
} config_var_value;
/*
* Groupings to help organize all the run-time options for display
*/
......@@ -105,8 +115,8 @@ typedef struct guc_stack
int nest_level; /* nesting depth at which we made entry */
GucStackState state; /* see enum above */
GucSource source; /* source of the prior value */
union config_var_value prior; /* previous value of variable */
union config_var_value masked; /* SET value in a GUC_SET_LOCAL entry */
config_var_value prior; /* previous value of variable */
config_var_value masked; /* SET value in a GUC_SET_LOCAL entry */
/* masked value's source must be PGC_S_SESSION, so no need to store it */
} GucStack;
......@@ -116,6 +126,11 @@ typedef struct guc_stack
* The short description should be less than 80 chars in length. Some
* applications may use the long description as well, and will append
* it to the short description. (separated by a newline or '. ')
*
* Note that sourcefile/sourceline are kept here, and not pushed into stacked
* values, although in principle they belong with some stacked value if the
* active value is session- or transaction-local. This is to avoid bloating
* stack entries. We know they are only relevant when source == PGC_S_FILE.
*/
struct config_generic
{
......@@ -132,7 +147,8 @@ struct config_generic
GucSource reset_source; /* source of the reset_value */
GucSource source; /* source of the current actual value */
GucStack *stack; /* stacked prior values */
char *sourcefile; /* file this settings is from (NULL if not
void *extra; /* "extra" pointer for current actual value */
char *sourcefile; /* file current setting is from (NULL if not
* file) */
int sourceline; /* line in source file */
};
......@@ -155,10 +171,12 @@ struct config_bool
/* constant fields, must be set correctly in initial value: */
bool *variable;
bool boot_val;
GucBoolCheckHook check_hook;
GucBoolAssignHook assign_hook;
GucShowHook show_hook;
/* variable fields, initialized at runtime: */
bool reset_val;
void *reset_extra;
};
struct config_int
......@@ -169,10 +187,12 @@ struct config_int
int boot_val;
int min;
int max;
GucIntCheckHook check_hook;
GucIntAssignHook assign_hook;
GucShowHook show_hook;
/* variable fields, initialized at runtime: */
int reset_val;
void *reset_extra;
};
struct config_real
......@@ -183,10 +203,12 @@ struct config_real
double boot_val;
double min;
double max;
GucRealCheckHook check_hook;
GucRealAssignHook assign_hook;
GucShowHook show_hook;
/* variable fields, initialized at runtime: */
double reset_val;
void *reset_extra;
};
struct config_string
......@@ -195,10 +217,12 @@ struct config_string
/* constant fields, must be set correctly in initial value: */
char **variable;
const char *boot_val;
GucStringCheckHook check_hook;
GucStringAssignHook assign_hook;
GucShowHook show_hook;
/* variable fields, initialized at runtime: */
char *reset_val;
void *reset_extra;
};
struct config_enum
......@@ -208,10 +232,12 @@ struct config_enum
int *variable;
int boot_val;
const struct config_enum_entry *options;
GucEnumCheckHook check_hook;
GucEnumAssignHook assign_hook;
GucShowHook show_hook;
/* variable fields, initialized at runtime: */
int reset_val;
void *reset_extra;
};
/* constant tables corresponding to enums above and in guc.h */
......
......@@ -33,14 +33,14 @@ extern char *localized_abbrev_months[];
extern char *localized_full_months[];
extern const char *locale_messages_assign(const char *value,
bool doit, GucSource source);
extern const char *locale_monetary_assign(const char *value,
bool doit, GucSource source);
extern const char *locale_numeric_assign(const char *value,
bool doit, GucSource source);
extern const char *locale_time_assign(const char *value,
bool doit, GucSource source);
extern bool check_locale_messages(char **newval, void **extra, GucSource source);
extern void assign_locale_messages(const char *newval, void *extra);
extern bool check_locale_monetary(char **newval, void **extra, GucSource source);
extern void assign_locale_monetary(const char *newval, void *extra);
extern bool check_locale_numeric(char **newval, void **extra, GucSource source);
extern void assign_locale_numeric(const char *newval, void *extra);
extern bool check_locale_time(char **newval, void **extra, GucSource source);
extern void assign_locale_time(const char *newval, void *extra);
extern bool check_locale(int category, const char *locale);
extern char *pg_perm_setlocale(int category, const char *locale);
......
......@@ -13,6 +13,8 @@
#ifndef TZPARSER_H
#define TZPARSER_H
#include "utils/datetime.h"
/*
* The result of parsing a timezone configuration file is an array of
* these structs, in order by abbrev. We export this because datetime.c
......@@ -30,6 +32,6 @@ typedef struct tzEntry
} tzEntry;
extern bool load_tzoffsets(const char *filename, bool doit, int elevel);
extern TimeZoneAbbrevTable *load_tzoffsets(const char *filename);
#endif /* TZPARSER_H */
......@@ -363,7 +363,7 @@ _PG_init(void)
&plperl_use_strict,
false,
PGC_USERSET, 0,
NULL, NULL);
NULL, NULL, NULL);
/*
* plperl.on_init is marked PGC_SIGHUP to support the idea that it might
......@@ -377,7 +377,7 @@ _PG_init(void)
&plperl_on_init,
NULL,
PGC_SIGHUP, 0,
NULL, NULL);
NULL, NULL, NULL);
/*
* plperl.on_plperl_init is marked PGC_SUSET to avoid issues whereby a
......@@ -399,7 +399,7 @@ _PG_init(void)
&plperl_on_plperl_init,
NULL,
PGC_SUSET, 0,
NULL, NULL);
NULL, NULL, NULL);
DefineCustomStringVariable("plperl.on_plperlu_init",
gettext_noop("Perl initialization code to execute once when plperlu is first used."),
......@@ -407,7 +407,7 @@ _PG_init(void)
&plperl_on_plperlu_init,
NULL,
PGC_SUSET, 0,
NULL, NULL);
NULL, NULL, NULL);
EmitWarningsOnPlaceholders("plperl");
......
......@@ -63,7 +63,7 @@ _PG_init(void)
PLPGSQL_RESOLVE_ERROR,
variable_conflict_options,
PGC_SUSET, 0,
NULL, NULL);
NULL, NULL, NULL);
EmitWarningsOnPlaceholders("plpgsql");
......
......@@ -622,7 +622,8 @@ alter function report_guc(text) set search_path = no_such_schema;
NOTICE: schema "no_such_schema" does not exist
-- with error occurring here
select report_guc('work_mem'), current_setting('work_mem');
ERROR: schema "no_such_schema" does not exist
ERROR: invalid value for parameter "search_path": "no_such_schema"
DETAIL: schema "no_such_schema" does not exist
alter function report_guc(text) reset search_path set work_mem = '2MB';
select report_guc('work_mem'), current_setting('work_mem');
report_guc | current_setting
......
......@@ -1444,27 +1444,39 @@ pg_timezone_initialize(void)
{
pg_tz *def_tz = NULL;
/* Do we need to try to figure the session timezone? */
if (pg_strcasecmp(GetConfigOption("timezone", false), "UNKNOWN") == 0)
/*
* Make sure that session_timezone and log_timezone are set.
* (session_timezone could still be NULL even if a timezone value was set
* in postgresql.conf, if that setting was interval-based rather than
* timezone-based.)
*/
if (!session_timezone)
{
/* Select setting */
def_tz = select_default_timezone();
session_timezone = def_tz;
/* Tell GUC about the value. Will redundantly call pg_tzset() */
SetConfigOption("timezone", pg_get_timezone_name(def_tz),
PGC_POSTMASTER, PGC_S_ARGV);
}
/* What about the log timezone? */
if (pg_strcasecmp(GetConfigOption("log_timezone", false), "UNKNOWN") == 0)
if (!log_timezone)
{
/* Select setting, but don't duplicate work */
/* Don't duplicate work */
if (!def_tz)
def_tz = select_default_timezone();
log_timezone = def_tz;
}
/* Now, set the timezone GUC if it's not already set */
if (GetConfigOption("timezone", false) == NULL)
{
/* Tell GUC about the value. Will redundantly call pg_tzset() */
SetConfigOption("timezone", pg_get_timezone_name(session_timezone),
PGC_POSTMASTER, PGC_S_ENV_VAR);
}
/* Likewise for log timezone */
if (GetConfigOption("log_timezone", false) == NULL)
{
/* Tell GUC about the value. Will redundantly call pg_tzset() */
SetConfigOption("log_timezone", pg_get_timezone_name(def_tz),
PGC_POSTMASTER, PGC_S_ARGV);
SetConfigOption("log_timezone", pg_get_timezone_name(log_timezone),
PGC_POSTMASTER, PGC_S_ENV_VAR);
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment