#define S_PER_D (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 */
extern bool Log_disconnections;
extern int CommitDelay;
char *GUC_check_errdetail_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,
int sourceline);
int
set_config_option(const char *name, const char *value,
GucContext context, GucSource source,
- GucAction action, bool changeVal, int elevel)
+ GucAction action, bool changeVal, int elevel,
+ bool is_reload)
{
struct config_generic *record;
bool prohibitValueChange = false;
* nondefault settings from the CONFIG_EXEC_PARAMS file during
* backend start. In that case we must accept PGC_SIGHUP
* settings, so as to have the same value as if we'd forked
- * from the postmaster. We detect this situation by checking
- * IsInitProcessingMode, which is a bit ugly, but it doesn't
- * seem worth passing down an explicit flag saying we're doing
- * read_nondefault_variables().
+ * from the postmaster. This can also happen when using
+ * RestoreGUCState() within a background worker that needs to
+ * have the same settings as the user backend that started it.
+ * is_reload will be true when either situation applies.
*/
-#ifdef EXEC_BACKEND
- if (IsUnderPostmaster && !IsInitProcessingMode())
- return -1;
-#else
- if (IsUnderPostmaster)
+ if (IsUnderPostmaster && !is_reload)
return -1;
-#endif
}
else if (context != PGC_POSTMASTER &&
context != PGC_BACKEND &&
GucContext context, GucSource source)
{
(void) set_config_option(name, value, context, source,
- GUC_ACTION_SET, true, 0);
+ GUC_ACTION_SET, true, 0, false);
}
ExtractSetVariableArgs(stmt),
(superuser() ? PGC_SUSET : PGC_USERSET),
PGC_S_SESSION,
- action,
- true,
- 0);
+ action, true, 0, false);
break;
case VAR_SET_MULTI:
NULL,
(superuser() ? PGC_SUSET : PGC_USERSET),
PGC_S_SESSION,
- action,
- true,
- 0);
+ action, true, 0, false);
break;
case VAR_RESET_ALL:
ResetAllOptions();
(superuser() ? PGC_SUSET : PGC_USERSET),
PGC_S_SESSION,
is_local ? GUC_ACTION_LOCAL : GUC_ACTION_SET,
- true,
- 0);
+ true, 0, false);
}
/*
(superuser() ? PGC_SUSET : PGC_USERSET),
PGC_S_SESSION,
is_local ? GUC_ACTION_LOCAL : GUC_ACTION_SET,
- true,
- 0);
+ true, 0, false);
/* get the new current value */
new_value = GetConfigOptionByName(name, NULL);
(void) set_config_option(name, pHolder->reset_val,
pHolder->gen.reset_scontext,
pHolder->gen.reset_source,
- GUC_ACTION_SET, true, WARNING);
+ GUC_ACTION_SET, true, WARNING, false);
/* That should not have resulted in stacking anything */
Assert(variable->stack == NULL);
case GUC_SAVE:
(void) set_config_option(name, curvalue,
curscontext, cursource,
- GUC_ACTION_SAVE, true, WARNING);
+ GUC_ACTION_SAVE, true,
+ WARNING, false);
break;
case GUC_SET:
(void) set_config_option(name, curvalue,
curscontext, cursource,
- GUC_ACTION_SET, true, WARNING);
+ GUC_ACTION_SET, true,
+ WARNING, false);
break;
case GUC_LOCAL:
(void) set_config_option(name, curvalue,
curscontext, cursource,
- GUC_ACTION_LOCAL, true, WARNING);
+ GUC_ACTION_LOCAL, true,
+ WARNING, false);
break;
case GUC_SET_LOCAL:
/* first, apply the masked value as SET */
(void) set_config_option(name, stack->masked.val.stringval,
stack->masked_scontext, PGC_S_SESSION,
- GUC_ACTION_SET, true, WARNING);
+ GUC_ACTION_SET, true,
+ WARNING, false);
/* then apply the current value as LOCAL */
(void) set_config_option(name, curvalue,
curscontext, cursource,
- GUC_ACTION_LOCAL, true, WARNING);
+ GUC_ACTION_LOCAL, true,
+ WARNING, false);
break;
}
{
(void) set_config_option(name, curvalue,
curscontext, cursource,
- GUC_ACTION_SET, true, WARNING);
+ GUC_ACTION_SET, true, WARNING, false);
variable->stack = NULL;
}
}
(void) set_config_option(varname, varvalue,
varscontext, varsource,
- GUC_ACTION_SET, true, 0);
+ GUC_ACTION_SET, true, 0, true);
if (varsourcefile[0])
set_config_sourcefile(varname, varsourcefile, varsourceline);
}
#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
(void) set_config_option(name, value,
context, source,
- action, true, 0);
+ action, true, 0, false);
free(name);
if (value)
/* test for permissions and valid option value */
(void) set_config_option(name, value,
superuser() ? PGC_SUSET : PGC_USERSET,
- PGC_S_TEST, GUC_ACTION_SET, false, 0);
+ PGC_S_TEST, GUC_ACTION_SET, false, 0, false);
return true;
}