mirror of
https://github.com/postgres/postgres.git
synced 2025-04-20 00:42:27 +03:00
Add infrastructure to save and restore GUC values.
This is further infrastructure for parallelism. Amit Khandekar, Noah Misch, Robert Haas
This commit is contained in:
parent
49b86fb1c9
commit
f5d9698a84
@ -2980,7 +2980,7 @@ applyRemoteGucs(PGconn *conn)
|
|||||||
/* Apply the option (this will throw error on failure) */
|
/* Apply the option (this will throw error on failure) */
|
||||||
(void) set_config_option(gucName, remoteVal,
|
(void) set_config_option(gucName, remoteVal,
|
||||||
PGC_USERSET, PGC_S_SESSION,
|
PGC_USERSET, PGC_S_SESSION,
|
||||||
GUC_ACTION_SAVE, true, 0);
|
GUC_ACTION_SAVE, true, 0, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return nestlevel;
|
return nestlevel;
|
||||||
|
@ -2104,15 +2104,15 @@ set_transmission_modes(void)
|
|||||||
if (DateStyle != USE_ISO_DATES)
|
if (DateStyle != USE_ISO_DATES)
|
||||||
(void) set_config_option("datestyle", "ISO",
|
(void) set_config_option("datestyle", "ISO",
|
||||||
PGC_USERSET, PGC_S_SESSION,
|
PGC_USERSET, PGC_S_SESSION,
|
||||||
GUC_ACTION_SAVE, true, 0);
|
GUC_ACTION_SAVE, true, 0, false);
|
||||||
if (IntervalStyle != INTSTYLE_POSTGRES)
|
if (IntervalStyle != INTSTYLE_POSTGRES)
|
||||||
(void) set_config_option("intervalstyle", "postgres",
|
(void) set_config_option("intervalstyle", "postgres",
|
||||||
PGC_USERSET, PGC_S_SESSION,
|
PGC_USERSET, PGC_S_SESSION,
|
||||||
GUC_ACTION_SAVE, true, 0);
|
GUC_ACTION_SAVE, true, 0, false);
|
||||||
if (extra_float_digits < 3)
|
if (extra_float_digits < 3)
|
||||||
(void) set_config_option("extra_float_digits", "3",
|
(void) set_config_option("extra_float_digits", "3",
|
||||||
PGC_USERSET, PGC_S_SESSION,
|
PGC_USERSET, PGC_S_SESSION,
|
||||||
GUC_ACTION_SAVE, true, 0);
|
GUC_ACTION_SAVE, true, 0, false);
|
||||||
|
|
||||||
return nestlevel;
|
return nestlevel;
|
||||||
}
|
}
|
||||||
|
@ -814,11 +814,11 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control,
|
|||||||
if (client_min_messages < WARNING)
|
if (client_min_messages < WARNING)
|
||||||
(void) set_config_option("client_min_messages", "warning",
|
(void) set_config_option("client_min_messages", "warning",
|
||||||
PGC_USERSET, PGC_S_SESSION,
|
PGC_USERSET, PGC_S_SESSION,
|
||||||
GUC_ACTION_SAVE, true, 0);
|
GUC_ACTION_SAVE, true, 0, false);
|
||||||
if (log_min_messages < WARNING)
|
if (log_min_messages < WARNING)
|
||||||
(void) set_config_option("log_min_messages", "warning",
|
(void) set_config_option("log_min_messages", "warning",
|
||||||
PGC_SUSET, PGC_S_SESSION,
|
PGC_SUSET, PGC_S_SESSION,
|
||||||
GUC_ACTION_SAVE, true, 0);
|
GUC_ACTION_SAVE, true, 0, false);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set up the search path to contain the target schema, then the schemas
|
* Set up the search path to contain the target schema, then the schemas
|
||||||
@ -843,7 +843,7 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control,
|
|||||||
|
|
||||||
(void) set_config_option("search_path", pathbuf.data,
|
(void) set_config_option("search_path", pathbuf.data,
|
||||||
PGC_USERSET, PGC_S_SESSION,
|
PGC_USERSET, PGC_S_SESSION,
|
||||||
GUC_ACTION_SAVE, true, 0);
|
GUC_ACTION_SAVE, true, 0, false);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set creating_extension and related variables so that
|
* Set creating_extension and related variables so that
|
||||||
|
@ -2422,7 +2422,7 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
|
|||||||
snprintf(workmembuf, sizeof(workmembuf), "%d", maintenance_work_mem);
|
snprintf(workmembuf, sizeof(workmembuf), "%d", maintenance_work_mem);
|
||||||
(void) set_config_option("work_mem", workmembuf,
|
(void) set_config_option("work_mem", workmembuf,
|
||||||
PGC_USERSET, PGC_S_SESSION,
|
PGC_USERSET, PGC_S_SESSION,
|
||||||
GUC_ACTION_SAVE, true, 0);
|
GUC_ACTION_SAVE, true, 0, false);
|
||||||
|
|
||||||
if (SPI_connect() != SPI_OK_CONNECT)
|
if (SPI_connect() != SPI_OK_CONNECT)
|
||||||
elog(ERROR, "SPI_connect failed");
|
elog(ERROR, "SPI_connect failed");
|
||||||
|
@ -318,7 +318,7 @@ ProcessConfigFile(GucContext context)
|
|||||||
/* Now we can re-apply the wired-in default (i.e., the boot_val) */
|
/* Now we can re-apply the wired-in default (i.e., the boot_val) */
|
||||||
if (set_config_option(gconf->name, NULL,
|
if (set_config_option(gconf->name, NULL,
|
||||||
context, PGC_S_DEFAULT,
|
context, PGC_S_DEFAULT,
|
||||||
GUC_ACTION_SET, true, 0) > 0)
|
GUC_ACTION_SET, true, 0, false) > 0)
|
||||||
{
|
{
|
||||||
/* Log the change if appropriate */
|
/* Log the change if appropriate */
|
||||||
if (context == PGC_SIGHUP)
|
if (context == PGC_SIGHUP)
|
||||||
@ -373,7 +373,7 @@ ProcessConfigFile(GucContext context)
|
|||||||
|
|
||||||
scres = set_config_option(item->name, item->value,
|
scres = set_config_option(item->name, item->value,
|
||||||
context, PGC_S_FILE,
|
context, PGC_S_FILE,
|
||||||
GUC_ACTION_SET, true, 0);
|
GUC_ACTION_SET, true, 0, false);
|
||||||
if (scres > 0)
|
if (scres > 0)
|
||||||
{
|
{
|
||||||
/* variable was updated, so log the change if appropriate */
|
/* variable was updated, so log the change if appropriate */
|
||||||
|
@ -110,6 +110,12 @@
|
|||||||
#define S_PER_D (60 * 60 * 24)
|
#define S_PER_D (60 * 60 * 24)
|
||||||
#define MS_PER_D (1000 * 60 * 60 * 24)
|
#define MS_PER_D (1000 * 60 * 60 * 24)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Precision with which REAL type guc values are to be printed for GUC
|
||||||
|
* serialization.
|
||||||
|
*/
|
||||||
|
#define REALTYPE_PRECISION 17
|
||||||
|
|
||||||
/* XXX these should appear in other modules' header files */
|
/* XXX these should appear in other modules' header files */
|
||||||
extern bool Log_disconnections;
|
extern bool Log_disconnections;
|
||||||
extern int CommitDelay;
|
extern int CommitDelay;
|
||||||
@ -136,6 +142,10 @@ char *GUC_check_errmsg_string;
|
|||||||
char *GUC_check_errdetail_string;
|
char *GUC_check_errdetail_string;
|
||||||
char *GUC_check_errhint_string;
|
char *GUC_check_errhint_string;
|
||||||
|
|
||||||
|
static void
|
||||||
|
do_serialize(char **destptr, Size *maxbytes, const char *fmt,...)
|
||||||
|
/* This lets gcc check the format string for consistency. */
|
||||||
|
__attribute__((format(PG_PRINTF_ATTRIBUTE, 3, 4)));
|
||||||
|
|
||||||
static void set_config_sourcefile(const char *name, char *sourcefile,
|
static void set_config_sourcefile(const char *name, char *sourcefile,
|
||||||
int sourceline);
|
int sourceline);
|
||||||
@ -5612,7 +5622,8 @@ validate_conf_option(struct config_generic * record, const char *name,
|
|||||||
int
|
int
|
||||||
set_config_option(const char *name, const char *value,
|
set_config_option(const char *name, const char *value,
|
||||||
GucContext context, GucSource source,
|
GucContext context, GucSource source,
|
||||||
GucAction action, bool changeVal, int elevel)
|
GucAction action, bool changeVal, int elevel,
|
||||||
|
bool is_reload)
|
||||||
{
|
{
|
||||||
struct config_generic *record;
|
struct config_generic *record;
|
||||||
bool prohibitValueChange = false;
|
bool prohibitValueChange = false;
|
||||||
@ -5726,18 +5737,13 @@ set_config_option(const char *name, const char *value,
|
|||||||
* nondefault settings from the CONFIG_EXEC_PARAMS file during
|
* nondefault settings from the CONFIG_EXEC_PARAMS file during
|
||||||
* backend start. In that case we must accept PGC_SIGHUP
|
* backend start. In that case we must accept PGC_SIGHUP
|
||||||
* settings, so as to have the same value as if we'd forked
|
* settings, so as to have the same value as if we'd forked
|
||||||
* from the postmaster. We detect this situation by checking
|
* from the postmaster. This can also happen when using
|
||||||
* IsInitProcessingMode, which is a bit ugly, but it doesn't
|
* RestoreGUCState() within a background worker that needs to
|
||||||
* seem worth passing down an explicit flag saying we're doing
|
* have the same settings as the user backend that started it.
|
||||||
* read_nondefault_variables().
|
* is_reload will be true when either situation applies.
|
||||||
*/
|
*/
|
||||||
#ifdef EXEC_BACKEND
|
if (IsUnderPostmaster && !is_reload)
|
||||||
if (IsUnderPostmaster && !IsInitProcessingMode())
|
|
||||||
return -1;
|
return -1;
|
||||||
#else
|
|
||||||
if (IsUnderPostmaster)
|
|
||||||
return -1;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
else if (context != PGC_POSTMASTER &&
|
else if (context != PGC_POSTMASTER &&
|
||||||
context != PGC_BACKEND &&
|
context != PGC_BACKEND &&
|
||||||
@ -6343,7 +6349,7 @@ SetConfigOption(const char *name, const char *value,
|
|||||||
GucContext context, GucSource source)
|
GucContext context, GucSource source)
|
||||||
{
|
{
|
||||||
(void) set_config_option(name, value, context, source,
|
(void) set_config_option(name, value, context, source,
|
||||||
GUC_ACTION_SET, true, 0);
|
GUC_ACTION_SET, true, 0, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -6923,9 +6929,7 @@ ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel)
|
|||||||
ExtractSetVariableArgs(stmt),
|
ExtractSetVariableArgs(stmt),
|
||||||
(superuser() ? PGC_SUSET : PGC_USERSET),
|
(superuser() ? PGC_SUSET : PGC_USERSET),
|
||||||
PGC_S_SESSION,
|
PGC_S_SESSION,
|
||||||
action,
|
action, true, 0, false);
|
||||||
true,
|
|
||||||
0);
|
|
||||||
break;
|
break;
|
||||||
case VAR_SET_MULTI:
|
case VAR_SET_MULTI:
|
||||||
|
|
||||||
@ -7012,9 +7016,7 @@ ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel)
|
|||||||
NULL,
|
NULL,
|
||||||
(superuser() ? PGC_SUSET : PGC_USERSET),
|
(superuser() ? PGC_SUSET : PGC_USERSET),
|
||||||
PGC_S_SESSION,
|
PGC_S_SESSION,
|
||||||
action,
|
action, true, 0, false);
|
||||||
true,
|
|
||||||
0);
|
|
||||||
break;
|
break;
|
||||||
case VAR_RESET_ALL:
|
case VAR_RESET_ALL:
|
||||||
ResetAllOptions();
|
ResetAllOptions();
|
||||||
@ -7059,8 +7061,7 @@ SetPGVariable(const char *name, List *args, bool is_local)
|
|||||||
(superuser() ? PGC_SUSET : PGC_USERSET),
|
(superuser() ? PGC_SUSET : PGC_USERSET),
|
||||||
PGC_S_SESSION,
|
PGC_S_SESSION,
|
||||||
is_local ? GUC_ACTION_LOCAL : GUC_ACTION_SET,
|
is_local ? GUC_ACTION_LOCAL : GUC_ACTION_SET,
|
||||||
true,
|
true, 0, false);
|
||||||
0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -7103,8 +7104,7 @@ set_config_by_name(PG_FUNCTION_ARGS)
|
|||||||
(superuser() ? PGC_SUSET : PGC_USERSET),
|
(superuser() ? PGC_SUSET : PGC_USERSET),
|
||||||
PGC_S_SESSION,
|
PGC_S_SESSION,
|
||||||
is_local ? GUC_ACTION_LOCAL : GUC_ACTION_SET,
|
is_local ? GUC_ACTION_LOCAL : GUC_ACTION_SET,
|
||||||
true,
|
true, 0, false);
|
||||||
0);
|
|
||||||
|
|
||||||
/* get the new current value */
|
/* get the new current value */
|
||||||
new_value = GetConfigOptionByName(name, NULL);
|
new_value = GetConfigOptionByName(name, NULL);
|
||||||
@ -7225,7 +7225,7 @@ define_custom_variable(struct config_generic * variable)
|
|||||||
(void) set_config_option(name, pHolder->reset_val,
|
(void) set_config_option(name, pHolder->reset_val,
|
||||||
pHolder->gen.reset_scontext,
|
pHolder->gen.reset_scontext,
|
||||||
pHolder->gen.reset_source,
|
pHolder->gen.reset_source,
|
||||||
GUC_ACTION_SET, true, WARNING);
|
GUC_ACTION_SET, true, WARNING, false);
|
||||||
/* That should not have resulted in stacking anything */
|
/* That should not have resulted in stacking anything */
|
||||||
Assert(variable->stack == NULL);
|
Assert(variable->stack == NULL);
|
||||||
|
|
||||||
@ -7281,30 +7281,35 @@ reapply_stacked_values(struct config_generic * variable,
|
|||||||
case GUC_SAVE:
|
case GUC_SAVE:
|
||||||
(void) set_config_option(name, curvalue,
|
(void) set_config_option(name, curvalue,
|
||||||
curscontext, cursource,
|
curscontext, cursource,
|
||||||
GUC_ACTION_SAVE, true, WARNING);
|
GUC_ACTION_SAVE, true,
|
||||||
|
WARNING, false);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GUC_SET:
|
case GUC_SET:
|
||||||
(void) set_config_option(name, curvalue,
|
(void) set_config_option(name, curvalue,
|
||||||
curscontext, cursource,
|
curscontext, cursource,
|
||||||
GUC_ACTION_SET, true, WARNING);
|
GUC_ACTION_SET, true,
|
||||||
|
WARNING, false);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GUC_LOCAL:
|
case GUC_LOCAL:
|
||||||
(void) set_config_option(name, curvalue,
|
(void) set_config_option(name, curvalue,
|
||||||
curscontext, cursource,
|
curscontext, cursource,
|
||||||
GUC_ACTION_LOCAL, true, WARNING);
|
GUC_ACTION_LOCAL, true,
|
||||||
|
WARNING, false);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GUC_SET_LOCAL:
|
case GUC_SET_LOCAL:
|
||||||
/* first, apply the masked value as SET */
|
/* first, apply the masked value as SET */
|
||||||
(void) set_config_option(name, stack->masked.val.stringval,
|
(void) set_config_option(name, stack->masked.val.stringval,
|
||||||
stack->masked_scontext, PGC_S_SESSION,
|
stack->masked_scontext, PGC_S_SESSION,
|
||||||
GUC_ACTION_SET, true, WARNING);
|
GUC_ACTION_SET, true,
|
||||||
|
WARNING, false);
|
||||||
/* then apply the current value as LOCAL */
|
/* then apply the current value as LOCAL */
|
||||||
(void) set_config_option(name, curvalue,
|
(void) set_config_option(name, curvalue,
|
||||||
curscontext, cursource,
|
curscontext, cursource,
|
||||||
GUC_ACTION_LOCAL, true, WARNING);
|
GUC_ACTION_LOCAL, true,
|
||||||
|
WARNING, false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -7328,7 +7333,7 @@ reapply_stacked_values(struct config_generic * variable,
|
|||||||
{
|
{
|
||||||
(void) set_config_option(name, curvalue,
|
(void) set_config_option(name, curvalue,
|
||||||
curscontext, cursource,
|
curscontext, cursource,
|
||||||
GUC_ACTION_SET, true, WARNING);
|
GUC_ACTION_SET, true, WARNING, false);
|
||||||
variable->stack = NULL;
|
variable->stack = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -8452,7 +8457,7 @@ read_nondefault_variables(void)
|
|||||||
|
|
||||||
(void) set_config_option(varname, varvalue,
|
(void) set_config_option(varname, varvalue,
|
||||||
varscontext, varsource,
|
varscontext, varsource,
|
||||||
GUC_ACTION_SET, true, 0);
|
GUC_ACTION_SET, true, 0, true);
|
||||||
if (varsourcefile[0])
|
if (varsourcefile[0])
|
||||||
set_config_sourcefile(varname, varsourcefile, varsourceline);
|
set_config_sourcefile(varname, varsourcefile, varsourceline);
|
||||||
|
|
||||||
@ -8465,6 +8470,398 @@ read_nondefault_variables(void)
|
|||||||
}
|
}
|
||||||
#endif /* EXEC_BACKEND */
|
#endif /* EXEC_BACKEND */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* can_skip_gucvar:
|
||||||
|
* When serializing, determine whether to skip this GUC. When restoring, the
|
||||||
|
* negation of this test determines whether to restore the compiled-in default
|
||||||
|
* value before processing serialized values.
|
||||||
|
*
|
||||||
|
* A PGC_S_DEFAULT setting on the serialize side will typically match new
|
||||||
|
* postmaster children, but that can be false when got_SIGHUP == true and the
|
||||||
|
* pending configuration change modifies this setting. Nonetheless, we omit
|
||||||
|
* PGC_S_DEFAULT settings from serialization and make up for that by restoring
|
||||||
|
* defaults before applying serialized values.
|
||||||
|
*
|
||||||
|
* PGC_POSTMASTER variables always have the same value in every child of a
|
||||||
|
* particular postmaster. Most PGC_INTERNAL variables are compile-time
|
||||||
|
* constants; a few, like server_encoding and lc_ctype, are handled specially
|
||||||
|
* outside the serialize/restore procedure. Therefore, SerializeGUCState()
|
||||||
|
* never sends these, and and RestoreGUCState() never changes them.
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
can_skip_gucvar(struct config_generic * gconf)
|
||||||
|
{
|
||||||
|
return gconf->context == PGC_POSTMASTER ||
|
||||||
|
gconf->context == PGC_INTERNAL || gconf->source == PGC_S_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* estimate_variable_size:
|
||||||
|
* Estimate max size for dumping the given GUC variable.
|
||||||
|
*/
|
||||||
|
static Size
|
||||||
|
estimate_variable_size(struct config_generic * gconf)
|
||||||
|
{
|
||||||
|
Size size;
|
||||||
|
Size valsize;
|
||||||
|
|
||||||
|
if (can_skip_gucvar(gconf))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
size = 0;
|
||||||
|
|
||||||
|
size = add_size(size, strlen(gconf->name) + 1);
|
||||||
|
|
||||||
|
/* Get the maximum display length of the GUC value. */
|
||||||
|
switch (gconf->vartype)
|
||||||
|
{
|
||||||
|
case PGC_BOOL:
|
||||||
|
{
|
||||||
|
valsize = 5; /* max(strlen('true'), strlen('false')) */
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PGC_INT:
|
||||||
|
{
|
||||||
|
struct config_int *conf = (struct config_int *) gconf;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Instead of getting the exact display length, use max
|
||||||
|
* length. Also reduce the max length for typical ranges of
|
||||||
|
* small values. Maximum value is 2147483647, i.e. 10 chars.
|
||||||
|
* Include one byte for sign.
|
||||||
|
*/
|
||||||
|
if (abs(*conf->variable) < 1000)
|
||||||
|
valsize = 3 + 1;
|
||||||
|
else
|
||||||
|
valsize = 10 + 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PGC_REAL:
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* We are going to print it with %.17g. Account for sign,
|
||||||
|
* decimal point, and e+nnn notation. E.g.
|
||||||
|
* -3.9932904234000002e+110
|
||||||
|
*/
|
||||||
|
valsize = REALTYPE_PRECISION + 1 + 1 + 5;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PGC_STRING:
|
||||||
|
{
|
||||||
|
struct config_string *conf = (struct config_string *) gconf;
|
||||||
|
|
||||||
|
valsize = strlen(*conf->variable);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PGC_ENUM:
|
||||||
|
{
|
||||||
|
struct config_enum *conf = (struct config_enum *) gconf;
|
||||||
|
|
||||||
|
valsize = strlen(config_enum_lookup_by_value(conf, *conf->variable));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Allow space for terminating zero-byte */
|
||||||
|
size = add_size(size, valsize + 1);
|
||||||
|
|
||||||
|
if (gconf->sourcefile)
|
||||||
|
size = add_size(size, strlen(gconf->sourcefile));
|
||||||
|
|
||||||
|
/* Allow space for terminating zero-byte */
|
||||||
|
size = add_size(size, 1);
|
||||||
|
|
||||||
|
/* Include line whenever we include file. */
|
||||||
|
if (gconf->sourcefile)
|
||||||
|
size = add_size(size, sizeof(gconf->sourceline));
|
||||||
|
|
||||||
|
size = add_size(size, sizeof(gconf->source));
|
||||||
|
size = add_size(size, sizeof(gconf->scontext));
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* EstimateGUCStateSpace:
|
||||||
|
* Returns the size needed to store the GUC state for the current process
|
||||||
|
*/
|
||||||
|
Size
|
||||||
|
EstimateGUCStateSpace(void)
|
||||||
|
{
|
||||||
|
Size size;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* Add space reqd for saving the data size of the guc state */
|
||||||
|
size = sizeof(Size);
|
||||||
|
|
||||||
|
/* Add up the space needed for each GUC variable */
|
||||||
|
for (i = 0; i < num_guc_variables; i++)
|
||||||
|
size = add_size(size,
|
||||||
|
estimate_variable_size(guc_variables[i]));
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* do_serialize:
|
||||||
|
* Copies the formatted string into the destination. Moves ahead the
|
||||||
|
* destination pointer, and decrements the maxbytes by that many bytes. If
|
||||||
|
* maxbytes is not sufficient to copy the string, error out.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
do_serialize(char **destptr, Size *maxbytes, const char *fmt,...)
|
||||||
|
{
|
||||||
|
va_list vargs;
|
||||||
|
int n;
|
||||||
|
|
||||||
|
if (*maxbytes <= 0)
|
||||||
|
elog(ERROR, "not enough space to serialize GUC state");
|
||||||
|
|
||||||
|
va_start(vargs, fmt);
|
||||||
|
n = vsnprintf(*destptr, *maxbytes, fmt, vargs);
|
||||||
|
va_end(vargs);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Cater to portability hazards in the vsnprintf() return value just like
|
||||||
|
* appendPQExpBufferVA() does. Note that this requires an extra byte of
|
||||||
|
* slack at the end of the buffer. Since serialize_variable() ends with a
|
||||||
|
* do_serialize_binary() rather than a do_serialize(), we'll always have
|
||||||
|
* that slack; estimate_variable_size() need not add a byte for it.
|
||||||
|
*/
|
||||||
|
if (n < 0 || n >= *maxbytes - 1)
|
||||||
|
{
|
||||||
|
if (n < 0 && errno != 0 && errno != ENOMEM)
|
||||||
|
/* Shouldn't happen. Better show errno description. */
|
||||||
|
elog(ERROR, "vsnprintf failed: %m");
|
||||||
|
else
|
||||||
|
elog(ERROR, "not enough space to serialize GUC state");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Shift the destptr ahead of the null terminator */
|
||||||
|
*destptr += n + 1;
|
||||||
|
*maxbytes -= n + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Binary copy version of do_serialize() */
|
||||||
|
static void
|
||||||
|
do_serialize_binary(char **destptr, Size *maxbytes, void *val, Size valsize)
|
||||||
|
{
|
||||||
|
if (valsize > *maxbytes)
|
||||||
|
elog(ERROR, "not enough space to serialize GUC state");
|
||||||
|
|
||||||
|
memcpy(*destptr, val, valsize);
|
||||||
|
*destptr += valsize;
|
||||||
|
*maxbytes -= valsize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* serialize_variable:
|
||||||
|
* Dumps name, value and other information of a GUC variable into destptr.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
serialize_variable(char **destptr, Size *maxbytes,
|
||||||
|
struct config_generic * gconf)
|
||||||
|
{
|
||||||
|
if (can_skip_gucvar(gconf))
|
||||||
|
return;
|
||||||
|
|
||||||
|
do_serialize(destptr, maxbytes, "%s", gconf->name);
|
||||||
|
|
||||||
|
switch (gconf->vartype)
|
||||||
|
{
|
||||||
|
case PGC_BOOL:
|
||||||
|
{
|
||||||
|
struct config_bool *conf = (struct config_bool *) gconf;
|
||||||
|
|
||||||
|
do_serialize(destptr, maxbytes,
|
||||||
|
(*conf->variable ? "true" : "false"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PGC_INT:
|
||||||
|
{
|
||||||
|
struct config_int *conf = (struct config_int *) gconf;
|
||||||
|
|
||||||
|
do_serialize(destptr, maxbytes, "%d", *conf->variable);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PGC_REAL:
|
||||||
|
{
|
||||||
|
struct config_real *conf = (struct config_real *) gconf;
|
||||||
|
|
||||||
|
do_serialize(destptr, maxbytes, "%.*g",
|
||||||
|
REALTYPE_PRECISION, *conf->variable);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PGC_STRING:
|
||||||
|
{
|
||||||
|
struct config_string *conf = (struct config_string *) gconf;
|
||||||
|
|
||||||
|
do_serialize(destptr, maxbytes, "%s", *conf->variable);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PGC_ENUM:
|
||||||
|
{
|
||||||
|
struct config_enum *conf = (struct config_enum *) gconf;
|
||||||
|
|
||||||
|
do_serialize(destptr, maxbytes, "%s",
|
||||||
|
config_enum_lookup_by_value(conf, *conf->variable));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
do_serialize(destptr, maxbytes, "%s",
|
||||||
|
(gconf->sourcefile ? gconf->sourcefile : ""));
|
||||||
|
|
||||||
|
if (gconf->sourcefile)
|
||||||
|
do_serialize_binary(destptr, maxbytes, &gconf->sourceline,
|
||||||
|
sizeof(gconf->sourceline));
|
||||||
|
|
||||||
|
do_serialize_binary(destptr, maxbytes, &gconf->source,
|
||||||
|
sizeof(gconf->source));
|
||||||
|
do_serialize_binary(destptr, maxbytes, &gconf->scontext,
|
||||||
|
sizeof(gconf->scontext));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SerializeGUCState:
|
||||||
|
* Dumps the complete GUC state onto the memory location at start_address.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
SerializeGUCState(Size maxsize, char *start_address)
|
||||||
|
{
|
||||||
|
char *curptr;
|
||||||
|
Size actual_size;
|
||||||
|
Size bytes_left;
|
||||||
|
int i;
|
||||||
|
int i_role;
|
||||||
|
|
||||||
|
/* Reserve space for saving the actual size of the guc state */
|
||||||
|
curptr = start_address + sizeof(actual_size);
|
||||||
|
bytes_left = maxsize - sizeof(actual_size);
|
||||||
|
|
||||||
|
for (i = 0; i < num_guc_variables; i++)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* It's pretty ugly, but we've got to force "role" to be initialized
|
||||||
|
* after "session_authorization"; otherwise, the latter will override
|
||||||
|
* the former.
|
||||||
|
*/
|
||||||
|
if (strcmp(guc_variables[i]->name, "role") == 0)
|
||||||
|
i_role = i;
|
||||||
|
else
|
||||||
|
serialize_variable(&curptr, &bytes_left, guc_variables[i]);
|
||||||
|
}
|
||||||
|
serialize_variable(&curptr, &bytes_left, guc_variables[i_role]);
|
||||||
|
|
||||||
|
/* Store actual size without assuming alignment of start_address. */
|
||||||
|
actual_size = maxsize - bytes_left - sizeof(actual_size);
|
||||||
|
memcpy(start_address, &actual_size, sizeof(actual_size));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* read_gucstate:
|
||||||
|
* Actually it does not read anything, just returns the srcptr. But it does
|
||||||
|
* move the srcptr past the terminating zero byte, so that the caller is ready
|
||||||
|
* to read the next string.
|
||||||
|
*/
|
||||||
|
static char *
|
||||||
|
read_gucstate(char **srcptr, char *srcend)
|
||||||
|
{
|
||||||
|
char *retptr = *srcptr;
|
||||||
|
char *ptr;
|
||||||
|
|
||||||
|
if (*srcptr >= srcend)
|
||||||
|
elog(ERROR, "incomplete GUC state");
|
||||||
|
|
||||||
|
/* The string variables are all null terminated */
|
||||||
|
for (ptr = *srcptr; ptr < srcend && *ptr != '\0'; ptr++)
|
||||||
|
;
|
||||||
|
|
||||||
|
if (ptr > srcend)
|
||||||
|
elog(ERROR, "could not find null terminator in GUC state");
|
||||||
|
|
||||||
|
/* Set the new position to the byte following the terminating NUL */
|
||||||
|
*srcptr = ptr + 1;
|
||||||
|
|
||||||
|
return retptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Binary read version of read_gucstate(). Copies into dest */
|
||||||
|
static void
|
||||||
|
read_gucstate_binary(char **srcptr, char *srcend, void *dest, Size size)
|
||||||
|
{
|
||||||
|
if (*srcptr + size > srcend)
|
||||||
|
elog(ERROR, "incomplete GUC state");
|
||||||
|
|
||||||
|
memcpy(dest, *srcptr, size);
|
||||||
|
*srcptr += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* RestoreGUCState:
|
||||||
|
* Reads the GUC state at the specified address and updates the GUCs with the
|
||||||
|
* values read from the GUC state.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
RestoreGUCState(void *gucstate)
|
||||||
|
{
|
||||||
|
char *varname,
|
||||||
|
*varvalue,
|
||||||
|
*varsourcefile;
|
||||||
|
int varsourceline;
|
||||||
|
GucSource varsource;
|
||||||
|
GucContext varscontext;
|
||||||
|
char *srcptr = (char *) gucstate;
|
||||||
|
char *srcend;
|
||||||
|
Size len;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* See comment at can_skip_gucvar(). */
|
||||||
|
for (i = 0; i < num_guc_variables; i++)
|
||||||
|
if (!can_skip_gucvar(guc_variables[i]))
|
||||||
|
InitializeOneGUCOption(guc_variables[i]);
|
||||||
|
|
||||||
|
/* First item is the length of the subsequent data */
|
||||||
|
memcpy(&len, gucstate, sizeof(len));
|
||||||
|
|
||||||
|
srcptr += sizeof(len);
|
||||||
|
srcend = srcptr + len;
|
||||||
|
|
||||||
|
while (srcptr < srcend)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
|
||||||
|
if ((varname = read_gucstate(&srcptr, srcend)) == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
varvalue = read_gucstate(&srcptr, srcend);
|
||||||
|
varsourcefile = read_gucstate(&srcptr, srcend);
|
||||||
|
if (varsourcefile[0])
|
||||||
|
read_gucstate_binary(&srcptr, srcend,
|
||||||
|
&varsourceline, sizeof(varsourceline));
|
||||||
|
read_gucstate_binary(&srcptr, srcend,
|
||||||
|
&varsource, sizeof(varsource));
|
||||||
|
read_gucstate_binary(&srcptr, srcend,
|
||||||
|
&varscontext, sizeof(varscontext));
|
||||||
|
|
||||||
|
result = set_config_option(varname, varvalue, varscontext, varsource,
|
||||||
|
GUC_ACTION_SET, true, ERROR, true);
|
||||||
|
if (result <= 0)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INTERNAL_ERROR),
|
||||||
|
errmsg("parameter \"%s\" could not be set", varname)));
|
||||||
|
if (varsourcefile[0])
|
||||||
|
set_config_sourcefile(varname, varsourcefile, varsourceline);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A little "long argument" simulation, although not quite GNU
|
* A little "long argument" simulation, although not quite GNU
|
||||||
@ -8555,7 +8952,7 @@ ProcessGUCArray(ArrayType *array,
|
|||||||
|
|
||||||
(void) set_config_option(name, value,
|
(void) set_config_option(name, value,
|
||||||
context, source,
|
context, source,
|
||||||
action, true, 0);
|
action, true, 0, false);
|
||||||
|
|
||||||
free(name);
|
free(name);
|
||||||
if (value)
|
if (value)
|
||||||
@ -8858,7 +9255,7 @@ validate_option_array_item(const char *name, const char *value,
|
|||||||
/* test for permissions and valid option value */
|
/* test for permissions and valid option value */
|
||||||
(void) set_config_option(name, value,
|
(void) set_config_option(name, value,
|
||||||
superuser() ? PGC_SUSET : PGC_USERSET,
|
superuser() ? PGC_SUSET : PGC_USERSET,
|
||||||
PGC_S_TEST, GUC_ACTION_SET, false, 0);
|
PGC_S_TEST, GUC_ACTION_SET, false, 0, false);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -339,7 +339,8 @@ extern bool parse_int(const char *value, int *result, int flags,
|
|||||||
extern bool parse_real(const char *value, double *result);
|
extern bool parse_real(const char *value, double *result);
|
||||||
extern int set_config_option(const char *name, const char *value,
|
extern int set_config_option(const char *name, const char *value,
|
||||||
GucContext context, GucSource source,
|
GucContext context, GucSource source,
|
||||||
GucAction action, bool changeVal, int elevel);
|
GucAction action, bool changeVal, int elevel,
|
||||||
|
bool is_reload);
|
||||||
extern void AlterSystemSetConfigFile(AlterSystemStmt *setstmt);
|
extern void AlterSystemSetConfigFile(AlterSystemStmt *setstmt);
|
||||||
extern char *GetConfigOptionByName(const char *name, const char **varname);
|
extern char *GetConfigOptionByName(const char *name, const char **varname);
|
||||||
extern void GetConfigOptionByNum(int varnum, const char **values, bool *noshow);
|
extern void GetConfigOptionByNum(int varnum, const char **values, bool *noshow);
|
||||||
@ -363,6 +364,11 @@ extern void write_nondefault_variables(GucContext context);
|
|||||||
extern void read_nondefault_variables(void);
|
extern void read_nondefault_variables(void);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* GUC serialization */
|
||||||
|
extern Size EstimateGUCStateSpace(void);
|
||||||
|
extern void SerializeGUCState(Size maxsize, char *start_address);
|
||||||
|
extern void RestoreGUCState(void *gucstate);
|
||||||
|
|
||||||
/* Support for messages reported from GUC check hooks */
|
/* Support for messages reported from GUC check hooks */
|
||||||
|
|
||||||
extern PGDLLIMPORT char *GUC_check_errmsg_string;
|
extern PGDLLIMPORT char *GUC_check_errmsg_string;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user