static HTAB *guc_hashtab; /* entries are GUCHashEntrys */
-static bool guc_dirty; /* true if need to do commit/abort work */
+/*
+ * In addition to the hash table, variables having certain properties are
+ * linked into these lists, so that we can find them without scanning the
+ * whole hash table. In most applications, only a small fraction of the
+ * GUCs appear in these lists at any given time. The usage of the stack
+ * and report lists is stylized enough that they can be slists, but the
+ * nondef list has to be a dlist to avoid O(N) deletes in common cases.
+ */
+static dlist_head guc_nondef_list; /* list of variables that have source
+ * different from PGC_S_DEFAULT */
+static slist_head guc_stack_list; /* list of variables that have non-NULL
+ * stack */
+static slist_head guc_report_list; /* list of variables that have the
+ * GUC_NEEDS_REPORT bit set in status */
static bool reporting_enabled; /* true to enable GUC_REPORT */
-static bool report_needed; /* true if any GUC_REPORT reports are needed */
-
static int GUCNestLevel = 0; /* 1 when in main transaction */
static int guc_name_match(const void *key1, const void *key2, Size keysize);
static void InitializeGUCOptionsFromEnvironment(void);
static void InitializeOneGUCOption(struct config_generic *gconf);
+static void RemoveGUCFromLists(struct config_generic *gconf);
+static void set_guc_source(struct config_generic *gconf, GucSource newsource);
static void pg_timezone_abbrev_initialize(void);
static void push_old_value(struct config_generic *gconf, GucAction action);
static void ReportGUCOption(struct config_generic *record);
if (gconf->reset_source == PGC_S_FILE)
gconf->reset_source = PGC_S_DEFAULT;
if (gconf->source == PGC_S_FILE)
- gconf->source = PGC_S_DEFAULT;
+ set_guc_source(gconf, PGC_S_DEFAULT);
for (stack = gconf->stack; stack; stack = stack->prev)
{
if (stack->source == PGC_S_FILE)
InitializeOneGUCOption(hentry->gucvar);
}
- guc_dirty = false;
-
reporting_enabled = false;
/*
}
}
+/*
+ * Summarily remove a GUC variable from any linked lists it's in.
+ *
+ * We use this in cases where the variable is about to be deleted or reset.
+ * These aren't common operations, so it's okay if this is a bit slow.
+ */
+static void
+RemoveGUCFromLists(struct config_generic *gconf)
+{
+ if (gconf->source != PGC_S_DEFAULT)
+ dlist_delete(&gconf->nondef_link);
+ if (gconf->stack != NULL)
+ slist_delete(&guc_stack_list, &gconf->stack_link);
+ if (gconf->status & GUC_NEEDS_REPORT)
+ slist_delete(&guc_report_list, &gconf->report_link);
+}
+
/*
* Select the configuration files and data directory to be used, and
void
ResetAllOptions(void)
{
- HASH_SEQ_STATUS status;
- GUCHashEntry *hentry;
+ dlist_mutable_iter iter;
- hash_seq_init(&status, guc_hashtab);
- while ((hentry = (GUCHashEntry *) hash_seq_search(&status)) != NULL)
+ /* We need only consider GUCs not already at PGC_S_DEFAULT */
+ dlist_foreach_modify(iter, &guc_nondef_list)
{
- struct config_generic *gconf = hentry->gucvar;
+ struct config_generic *gconf = dlist_container(struct config_generic,
+ nondef_link, iter.cur);
/* Don't reset non-SET-able values */
if (gconf->context != PGC_SUSET &&
}
}
- gconf->source = gconf->reset_source;
+ set_guc_source(gconf, gconf->reset_source);
gconf->scontext = gconf->reset_scontext;
gconf->srole = gconf->reset_srole;
- if (gconf->flags & GUC_REPORT)
+ if ((gconf->flags & GUC_REPORT) && !(gconf->status & GUC_NEEDS_REPORT))
{
gconf->status |= GUC_NEEDS_REPORT;
- report_needed = true;
+ slist_push_head(&guc_report_list, &gconf->report_link);
}
}
}
+/*
+ * Apply a change to a GUC variable's "source" field.
+ *
+ * Use this rather than just assigning, to ensure that the variable's
+ * membership in guc_nondef_list is updated correctly.
+ */
+static void
+set_guc_source(struct config_generic *gconf, GucSource newsource)
+{
+ /* Adjust nondef list membership if appropriate for change */
+ if (gconf->source == PGC_S_DEFAULT)
+ {
+ if (newsource != PGC_S_DEFAULT)
+ dlist_push_tail(&guc_nondef_list, &gconf->nondef_link);
+ }
+ else
+ {
+ if (newsource == PGC_S_DEFAULT)
+ dlist_delete(&gconf->nondef_link);
+ }
+ /* Now update the source field */
+ gconf->source = newsource;
+}
+
+
/*
* push_old_value
* Push previous state during transactional assignment to a GUC variable.
Assert(stack->state == GUC_SAVE);
break;
}
- Assert(guc_dirty); /* must be set already */
return;
}
stack->srole = gconf->srole;
set_stack_value(gconf, &stack->prior);
+ if (gconf->stack == NULL)
+ slist_push_head(&guc_stack_list, &gconf->stack_link);
gconf->stack = stack;
-
- /* Ensure we remember to pop at end of xact */
- guc_dirty = true;
}
void
AtEOXact_GUC(bool isCommit, int nestLevel)
{
- bool still_dirty;
- HASH_SEQ_STATUS status;
- GUCHashEntry *hentry;
+ slist_mutable_iter iter;
/*
* Note: it's possible to get here with GUCNestLevel == nestLevel-1 during
(nestLevel <= GUCNestLevel ||
(nestLevel == GUCNestLevel + 1 && !isCommit)));
- /* Quick exit if nothing's changed in this transaction */
- if (!guc_dirty)
+ /* We need only process GUCs having nonempty stacks */
+ slist_foreach_modify(iter, &guc_stack_list)
{
- GUCNestLevel = nestLevel - 1;
- return;
- }
-
- still_dirty = false;
- hash_seq_init(&status, guc_hashtab);
- while ((hentry = (GUCHashEntry *) hash_seq_search(&status)) != NULL)
- {
- struct config_generic *gconf = hentry->gucvar;
+ struct config_generic *gconf = slist_container(struct config_generic,
+ stack_link, iter.cur);
GucStack *stack;
/*
set_extra_field(gconf, &(stack->masked.extra), NULL);
/* And restore source information */
- gconf->source = newsource;
+ set_guc_source(gconf, newsource);
gconf->scontext = newscontext;
gconf->srole = newsrole;
}
- /* Finish popping the state stack */
+ /*
+ * Pop the GUC's state stack; if it's now empty, remove the GUC
+ * from guc_stack_list.
+ */
gconf->stack = prev;
+ if (prev == NULL)
+ slist_delete_current(&iter);
pfree(stack);
/* Report new value if we changed it */
- if (changed && (gconf->flags & GUC_REPORT))
+ if (changed && (gconf->flags & GUC_REPORT) &&
+ !(gconf->status & GUC_NEEDS_REPORT))
{
gconf->status |= GUC_NEEDS_REPORT;
- report_needed = true;
+ slist_push_head(&guc_report_list, &gconf->report_link);
}
} /* end of stack-popping loop */
-
- if (stack != NULL)
- still_dirty = true;
}
- /* If there are no remaining stack entries, we can reset guc_dirty */
- guc_dirty = still_dirty;
-
/* Update nesting level */
GUCNestLevel = nestLevel - 1;
}
if (conf->flags & GUC_REPORT)
ReportGUCOption(conf);
}
-
- report_needed = false;
}
/*
void
ReportChangedGUCOptions(void)
{
- HASH_SEQ_STATUS status;
- GUCHashEntry *hentry;
+ slist_mutable_iter iter;
/* Quick exit if not (yet) enabled */
if (!reporting_enabled)
SetConfigOption("in_hot_standby", "false",
PGC_INTERNAL, PGC_S_OVERRIDE);
- /* Quick exit if no values have been changed */
- if (!report_needed)
- return;
-
/* Transmit new values of interesting variables */
- hash_seq_init(&status, guc_hashtab);
- while ((hentry = (GUCHashEntry *) hash_seq_search(&status)) != NULL)
+ slist_foreach_modify(iter, &guc_report_list)
{
- struct config_generic *conf = hentry->gucvar;
+ struct config_generic *conf = slist_container(struct config_generic,
+ report_link, iter.cur);
- if ((conf->flags & GUC_REPORT) && (conf->status & GUC_NEEDS_REPORT))
- ReportGUCOption(conf);
+ Assert((conf->flags & GUC_REPORT) && (conf->status & GUC_NEEDS_REPORT));
+ ReportGUCOption(conf);
+ conf->status &= ~GUC_NEEDS_REPORT;
+ slist_delete_current(&iter);
}
-
- report_needed = false;
}
/*
* ReportGUCOption: if appropriate, transmit option value to frontend
*
* We need not transmit the value if it's the same as what we last
- * transmitted. However, clear the NEEDS_REPORT flag in any case.
+ * transmitted.
*/
static void
ReportGUCOption(struct config_generic *record)
}
pfree(val);
-
- record->status &= ~GUC_NEEDS_REPORT;
}
/*
*conf->variable = newval;
set_extra_field(&conf->gen, &conf->gen.extra,
newextra);
- conf->gen.source = source;
+ set_guc_source(&conf->gen, source);
conf->gen.scontext = context;
conf->gen.srole = srole;
}
*conf->variable = newval;
set_extra_field(&conf->gen, &conf->gen.extra,
newextra);
- conf->gen.source = source;
+ set_guc_source(&conf->gen, source);
conf->gen.scontext = context;
conf->gen.srole = srole;
}
*conf->variable = newval;
set_extra_field(&conf->gen, &conf->gen.extra,
newextra);
- conf->gen.source = source;
+ set_guc_source(&conf->gen, source);
conf->gen.scontext = context;
conf->gen.srole = srole;
}
set_string_field(conf, conf->variable, newval);
set_extra_field(&conf->gen, &conf->gen.extra,
newextra);
- conf->gen.source = source;
+ set_guc_source(&conf->gen, source);
conf->gen.scontext = context;
conf->gen.srole = srole;
}
*conf->variable = newval;
set_extra_field(&conf->gen, &conf->gen.extra,
newextra);
- conf->gen.source = source;
+ set_guc_source(&conf->gen, source);
conf->gen.scontext = context;
conf->gen.srole = srole;
}
}
}
- if (changeVal && (record->flags & GUC_REPORT))
+ if (changeVal && (record->flags & GUC_REPORT) &&
+ !(record->status & GUC_NEEDS_REPORT))
{
record->status |= GUC_NEEDS_REPORT;
- report_needed = true;
+ slist_push_head(&guc_report_list, &record->report_link);
}
return changeVal ? 1 : -1;
hentry->gucname = name;
hentry->gucvar = variable;
+ /*
+ * Remove the placeholder from any lists it's in, too.
+ */
+ RemoveGUCFromLists(&pHolder->gen);
+
/*
* Assign the string value(s) stored in the placeholder to the real
* variable. Essentially, we need to duplicate all the active and stacked
(void) set_config_option_ext(name, curvalue,
curscontext, cursource, cursrole,
GUC_ACTION_SET, true, WARNING, false);
- variable->stack = NULL;
+ if (variable->stack != NULL)
+ {
+ slist_delete(&guc_stack_list, &variable->stack_link);
+ variable->stack = NULL;
+ }
}
}
}
var->name),
errdetail("\"%s\" is now a reserved prefix.",
className)));
+ /* Remove it from the hash table */
hash_search(guc_hashtab,
&var->name,
HASH_REMOVE,
NULL);
+ /* Remove it from any lists it's in, too */
+ RemoveGUCFromLists(var);
}
}
get_explain_guc_options(int *num)
{
struct config_generic **result;
- HASH_SEQ_STATUS status;
- GUCHashEntry *hentry;
+ dlist_iter iter;
*num = 0;
*/
result = palloc(sizeof(struct config_generic *) * hash_get_num_entries(guc_hashtab));
- hash_seq_init(&status, guc_hashtab);
- while ((hentry = (GUCHashEntry *) hash_seq_search(&status)) != NULL)
+ /* We need only consider GUCs with source not PGC_S_DEFAULT */
+ dlist_foreach(iter, &guc_nondef_list)
{
- struct config_generic *conf = hentry->gucvar;
+ struct config_generic *conf = dlist_container(struct config_generic,
+ nondef_link, iter.cur);
bool modified;
/* return only parameters marked for inclusion in explain */
static void
write_one_nondefault_variable(FILE *fp, struct config_generic *gconf)
{
- if (gconf->source == PGC_S_DEFAULT)
- return;
+ Assert(gconf->source != PGC_S_DEFAULT);
fprintf(fp, "%s", gconf->name);
fputc(0, fp);
{
int elevel;
FILE *fp;
- HASH_SEQ_STATUS status;
- GUCHashEntry *hentry;
+ dlist_iter iter;
Assert(context == PGC_POSTMASTER || context == PGC_SIGHUP);
return;
}
- hash_seq_init(&status, guc_hashtab);
- while ((hentry = (GUCHashEntry *) hash_seq_search(&status)) != NULL)
+ /* We need only consider GUCs with source not PGC_S_DEFAULT */
+ dlist_foreach(iter, &guc_nondef_list)
{
- write_one_nondefault_variable(fp, hentry->gucvar);
+ struct config_generic *gconf = dlist_container(struct config_generic,
+ nondef_link, iter.cur);
+
+ write_one_nondefault_variable(fp, gconf);
}
if (FreeFile(fp))
EstimateGUCStateSpace(void)
{
Size size;
- HASH_SEQ_STATUS status;
- GUCHashEntry *hentry;
+ dlist_iter iter;
/* Add space reqd for saving the data size of the guc state */
size = sizeof(Size);
- /* Add up the space needed for each GUC variable */
- hash_seq_init(&status, guc_hashtab);
- while ((hentry = (GUCHashEntry *) hash_seq_search(&status)) != NULL)
- size = add_size(size,
- estimate_variable_size(hentry->gucvar));
+ /*
+ * Add up the space needed for each GUC variable.
+ *
+ * We need only process non-default GUCs.
+ */
+ dlist_foreach(iter, &guc_nondef_list)
+ {
+ struct config_generic *gconf = dlist_container(struct config_generic,
+ nondef_link, iter.cur);
+
+ size = add_size(size, estimate_variable_size(gconf));
+ }
return size;
}
char *curptr;
Size actual_size;
Size bytes_left;
- HASH_SEQ_STATUS status;
- GUCHashEntry *hentry;
+ dlist_iter iter;
/* Reserve space for saving the actual size of the guc state */
Assert(maxsize > sizeof(actual_size));
curptr = start_address + sizeof(actual_size);
bytes_left = maxsize - sizeof(actual_size);
- hash_seq_init(&status, guc_hashtab);
- while ((hentry = (GUCHashEntry *) hash_seq_search(&status)) != NULL)
- serialize_variable(&curptr, &bytes_left, hentry->gucvar);
+ /* We need only consider GUCs with source not PGC_S_DEFAULT */
+ dlist_foreach(iter, &guc_nondef_list)
+ {
+ struct config_generic *gconf = dlist_container(struct config_generic,
+ nondef_link, iter.cur);
+
+ serialize_variable(&curptr, &bytes_left, gconf);
+ }
/* Store actual size without assuming alignment of start_address. */
actual_size = maxsize - bytes_left - sizeof(actual_size);
char *srcptr = (char *) gucstate;
char *srcend;
Size len;
- HASH_SEQ_STATUS status;
- GUCHashEntry *hentry;
+ dlist_mutable_iter iter;
ErrorContextCallback error_context_callback;
/*
* also ensures that set_config_option won't refuse to set them because of
* source-priority comparisons.
*/
- hash_seq_init(&status, guc_hashtab);
- while ((hentry = (GUCHashEntry *) hash_seq_search(&status)) != NULL)
+ dlist_foreach_modify(iter, &guc_nondef_list)
{
- struct config_generic *gconf = hentry->gucvar;
+ struct config_generic *gconf = dlist_container(struct config_generic,
+ nondef_link, iter.cur);
/* Do nothing if non-shippable or if already at PGC_S_DEFAULT. */
if (can_skip_gucvar(gconf))
* first we must free any existing subsidiary data to avoid leaking
* memory. The stack must be empty, but we have to clean up all other
* fields. Beware that there might be duplicate value or "extra"
- * pointers.
+ * pointers. We also have to be sure to take it out of any lists it's
+ * in.
*/
Assert(gconf->stack == NULL);
guc_free(gconf->extra);
break;
}
}
+ /* Remove it from any lists it's in. */
+ RemoveGUCFromLists(gconf);
/* Now we can reset the struct to PGS_S_DEFAULT state. */
InitializeOneGUCOption(gconf);
}