summaryrefslogtreecommitdiff
path: root/src/backend/utils/misc/guc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/utils/misc/guc.c')
-rw-r--r--src/backend/utils/misc/guc.c532
1 files changed, 383 insertions, 149 deletions
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index f050e201e2a..f5c16de83ba 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -10,17 +10,16 @@
* Written by Peter Eisentraut <peter_e@gmx.net>.
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.211 2004/06/11 03:54:54 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.212 2004/07/01 00:51:24 tgl Exp $
*
*--------------------------------------------------------------------
*/
#include "postgres.h"
-#include <errno.h>
+#include <ctype.h>
#include <float.h>
#include <limits.h>
#include <unistd.h>
-#include <ctype.h>
#include "utils/guc.h"
#include "utils/guc_tables.h"
@@ -54,6 +53,7 @@
#include "tcop/tcopprot.h"
#include "utils/array.h"
#include "utils/builtins.h"
+#include "utils/memutils.h"
#include "utils/pg_locale.h"
#include "pgstat.h"
@@ -105,6 +105,7 @@ static const char *assign_custom_variable_classes(const char *newval, bool doit,
GucSource source);
static bool assign_stage_log_stats(bool newval, bool doit, GucSource source);
static bool assign_log_stats(bool newval, bool doit, GucSource source);
+static bool assign_transaction_read_only(bool newval, bool doit, GucSource source);
/*
@@ -174,45 +175,6 @@ static int max_identifier_length;
static int block_size;
static bool integer_datetimes;
-/* Macros for freeing malloc'd pointers only if appropriate to do so */
-/* Some of these tests are probably redundant, but be safe ... */
-#define SET_STRING_VARIABLE(rec, newval) \
- do { \
- if (*(rec)->variable && \
- *(rec)->variable != (rec)->reset_val && \
- *(rec)->variable != (rec)->session_val && \
- *(rec)->variable != (rec)->tentative_val) \
- free(*(rec)->variable); \
- *(rec)->variable = (newval); \
- } while (0)
-#define SET_STRING_RESET_VAL(rec, newval) \
- do { \
- if ((rec)->reset_val && \
- (rec)->reset_val != *(rec)->variable && \
- (rec)->reset_val != (rec)->session_val && \
- (rec)->reset_val != (rec)->tentative_val) \
- free((rec)->reset_val); \
- (rec)->reset_val = (newval); \
- } while (0)
-#define SET_STRING_SESSION_VAL(rec, newval) \
- do { \
- if ((rec)->session_val && \
- (rec)->session_val != *(rec)->variable && \
- (rec)->session_val != (rec)->reset_val && \
- (rec)->session_val != (rec)->tentative_val) \
- free((rec)->session_val); \
- (rec)->session_val = (newval); \
- } while (0)
-#define SET_STRING_TENTATIVE_VAL(rec, newval) \
- do { \
- if ((rec)->tentative_val && \
- (rec)->tentative_val != *(rec)->variable && \
- (rec)->tentative_val != (rec)->reset_val && \
- (rec)->tentative_val != (rec)->session_val) \
- free((rec)->tentative_val); \
- (rec)->tentative_val = (newval); \
- } while (0)
-
/*
* Displayable names for context types (enum GucContext)
@@ -801,7 +763,7 @@ static struct config_bool ConfigureNamesBool[] =
GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
},
&XactReadOnly,
- false, NULL, NULL
+ false, assign_transaction_read_only, NULL
},
{
{"add_missing_from", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
@@ -1766,14 +1728,13 @@ static const char * const map_old_guc_names[] = {
*/
static struct config_generic **guc_variables;
-/* Current number of variables contained in the vector
- */
+/* Current number of variables contained in the vector */
static int num_guc_variables;
-/* Vector capacity
- */
+/* Vector capacity */
static int size_guc_variables;
+
static bool guc_dirty; /* TRUE if need to do commit/abort work */
static bool reporting_enabled; /* TRUE to enable GUC_REPORT */
@@ -1783,14 +1744,71 @@ static char *guc_string_workspace; /* for avoiding memory leaks */
static int guc_var_compare(const void *a, const void *b);
static int guc_name_compare(const char *namea, const char *nameb);
+static void push_old_value(struct config_generic *gconf);
static void ReportGUCOption(struct config_generic * record);
static char *_ShowOption(struct config_generic * record);
-struct config_generic** get_guc_variables()
+
+/*
+ * Support for assigning to a field of a string GUC item. Free the prior
+ * value if it's not referenced anywhere else in the item (including stacked
+ * states).
+ */
+static void
+set_string_field(struct config_string *conf, char **field, char *newval)
+{
+ char *oldval = *field;
+ GucStack *stack;
+
+ /* Do the assignment */
+ *field = newval;
+
+ /* Exit if any duplicate references, or if old value was NULL anyway */
+ if (oldval == NULL ||
+ oldval == *(conf->variable) ||
+ oldval == conf->reset_val ||
+ oldval == conf->tentative_val)
+ return;
+ for (stack = conf->gen.stack; stack; stack = stack->prev)
+ {
+ if (oldval == stack->tentative_val.stringval ||
+ oldval == stack->value.stringval)
+ return;
+ }
+
+ /* Not used anymore, so free it */
+ free(oldval);
+}
+
+/*
+ * Detect whether strval is referenced anywhere in a GUC string item
+ */
+static bool
+string_field_used(struct config_string *conf, char *strval)
+{
+ GucStack *stack;
+
+ if (strval == *(conf->variable) ||
+ strval == conf->reset_val ||
+ strval == conf->tentative_val)
+ return true;
+ for (stack = conf->gen.stack; stack; stack = stack->prev)
+ {
+ if (strval == stack->tentative_val.stringval ||
+ strval == stack->value.stringval)
+ return true;
+ }
+ return false;
+}
+
+
+struct config_generic **
+get_guc_variables(void)
{
return guc_variables;
}
+
/*
* Build the sorted array. This is split out so that it could be
* re-executed after startup (eg, we could allow loadable modules to
@@ -2001,14 +2019,13 @@ find_option(const char *name)
return find_option(map_old_guc_names[i+1]);
}
- /* Check if the name is qualified, and if so, check if the qualifier
+ /*
+ * Check if the name is qualified, and if so, check if the qualifier
* maps to a custom variable class.
*/
dot = strchr(name, GUC_QUALIFIER_SEPARATOR);
if(dot != NULL && is_custom_class(name, dot - name))
- /*
- * Add a placeholder variable for this name
- */
+ /* Add a placeholder variable for this name */
return (struct config_generic*)add_placeholder_variable(name);
/* Unknown name */
@@ -2081,9 +2098,9 @@ InitializeGUCOptions(void)
gconf->status = 0;
gconf->reset_source = PGC_S_DEFAULT;
- gconf->session_source = PGC_S_DEFAULT;
gconf->tentative_source = PGC_S_DEFAULT;
gconf->source = PGC_S_DEFAULT;
+ gconf->stack = NULL;
switch (gconf->vartype)
{
@@ -2097,7 +2114,6 @@ InitializeGUCOptions(void)
elog(FATAL, "failed to initialize %s to %d",
conf->gen.name, (int) conf->reset_val);
*conf->variable = conf->reset_val;
- conf->session_val = conf->reset_val;
break;
}
case PGC_INT:
@@ -2119,7 +2135,6 @@ InitializeGUCOptions(void)
elog(FATAL, "failed to initialize %s to %d",
conf->gen.name, conf->reset_val);
*conf->variable = conf->reset_val;
- conf->session_val = conf->reset_val;
break;
}
case PGC_REAL:
@@ -2135,7 +2150,6 @@ InitializeGUCOptions(void)
elog(FATAL, "failed to initialize %s to %g",
conf->gen.name, conf->reset_val);
*conf->variable = conf->reset_val;
- conf->session_val = conf->reset_val;
break;
}
case PGC_STRING:
@@ -2150,7 +2164,6 @@ InitializeGUCOptions(void)
conf->assign_hook == assign_log_statement);
*conf->variable = NULL;
conf->reset_val = NULL;
- conf->session_val = NULL;
conf->tentative_val = NULL;
if (conf->boot_val == NULL)
@@ -2190,7 +2203,6 @@ InitializeGUCOptions(void)
}
}
*conf->variable = str;
- conf->session_val = str;
break;
}
}
@@ -2254,6 +2266,9 @@ ResetAllOptions(void)
if (gconf->source <= PGC_S_OVERRIDE)
continue;
+ /* Save old value to support transaction abort */
+ push_old_value(gconf);
+
switch (gconf->vartype)
{
case PGC_BOOL:
@@ -2336,8 +2351,8 @@ ResetAllOptions(void)
}
}
- SET_STRING_VARIABLE(conf, str);
- SET_STRING_TENTATIVE_VAL(conf, str);
+ set_string_field(conf, conf->variable, str);
+ set_string_field(conf, &conf->tentative_val, str);
conf->gen.source = conf->gen.reset_source;
conf->gen.tentative_source = conf->gen.reset_source;
conf->gen.status |= GUC_HAVE_TENTATIVE;
@@ -2353,11 +2368,93 @@ ResetAllOptions(void)
/*
- * Do GUC processing at transaction commit or abort.
+ * push_old_value
+ * Push previous state during first assignment to a GUC variable
+ * within a particular transaction.
+ *
+ * We have to be willing to "back-fill" the state stack if the first
+ * assignment occurs within a subtransaction nested several levels deep.
+ * This ensures that if an intermediate transaction aborts, it will have
+ * the proper value available to restore the setting to.
+ */
+static void
+push_old_value(struct config_generic *gconf)
+{
+ int my_level = GetCurrentTransactionNestLevel();
+ GucStack *stack;
+
+ /* If we're not inside a transaction, do nothing */
+ if (my_level == 0)
+ return;
+
+ for (;;)
+ {
+ /* Done if we already pushed it at this nesting depth */
+ if (gconf->stack && gconf->stack->nest_level >= my_level)
+ return;
+
+ /*
+ * We keep all the stack entries in TopTransactionContext so as to
+ * avoid allocation problems when a subtransaction back-fills stack
+ * entries for upper transaction levels.
+ */
+ stack = (GucStack *) MemoryContextAlloc(TopTransactionContext,
+ sizeof(GucStack));
+
+ stack->prev = gconf->stack;
+ stack->nest_level = stack->prev ? stack->prev->nest_level + 1 : 1;
+ stack->status = gconf->status;
+ stack->tentative_source = gconf->tentative_source;
+ stack->source = gconf->source;
+
+ switch (gconf->vartype)
+ {
+ case PGC_BOOL:
+ stack->tentative_val.boolval =
+ ((struct config_bool *) gconf)->tentative_val;
+ stack->value.boolval =
+ *((struct config_bool *) gconf)->variable;
+ break;
+
+ case PGC_INT:
+ stack->tentative_val.intval =
+ ((struct config_int *) gconf)->tentative_val;
+ stack->value.intval =
+ *((struct config_int *) gconf)->variable;
+ break;
+
+ case PGC_REAL:
+ stack->tentative_val.realval =
+ ((struct config_real *) gconf)->tentative_val;
+ stack->value.realval =
+ *((struct config_real *) gconf)->variable;
+ break;
+
+ case PGC_STRING:
+ stack->tentative_val.stringval =
+ ((struct config_string *) gconf)->tentative_val;
+ stack->value.stringval =
+ *((struct config_string *) gconf)->variable;
+ break;
+ }
+
+ gconf->stack = stack;
+
+ /* Set state to indicate nothing happened yet within this level */
+ gconf->status = GUC_HAVE_STACK;
+
+ /* Ensure we remember to pop at end of xact */
+ guc_dirty = true;
+ }
+}
+
+/*
+ * Do GUC processing at transaction or subtransaction commit or abort.
*/
void
-AtEOXact_GUC(bool isCommit)
+AtEOXact_GUC(bool isCommit, bool isSubXact)
{
+ int my_level;
int i;
/* Quick exit if nothing's changed in this transaction */
@@ -2371,15 +2468,56 @@ AtEOXact_GUC(bool isCommit)
guc_string_workspace = NULL;
}
+ my_level = GetCurrentTransactionNestLevel();
+ Assert(isSubXact ? (my_level > 1) : (my_level == 1));
+
for (i = 0; i < num_guc_variables; i++)
{
struct config_generic *gconf = guc_variables[i];
+ int my_status = gconf->status;
+ GucStack *stack = gconf->stack;
+ bool useTentative;
bool changed;
- /* Skip if nothing's happened to this var in this transaction */
- if (gconf->status == 0)
+ /*
+ * Skip if nothing's happened to this var in this transaction
+ */
+ if (my_status == 0)
+ {
+ Assert(stack == NULL);
+ continue;
+ }
+ /* Assert that we stacked old value before changing it */
+ Assert(stack != NULL && (my_status & GUC_HAVE_STACK));
+ /* However, the last change may have been at an outer xact level */
+ if (stack->nest_level < my_level)
continue;
+ Assert(stack->nest_level == my_level);
+
+ /*
+ * We will pop the stack entry. Start by restoring outer xact status
+ * (since we may want to modify it below). Be careful to use
+ * my_status to reference the inner xact status below this point...
+ */
+ gconf->status = stack->status;
+
+ /*
+ * We have two cases:
+ *
+ * If commit and HAVE_TENTATIVE, set actual value to tentative
+ * (this is to override a SET LOCAL if one occurred later than SET).
+ * We keep the tentative value and propagate HAVE_TENTATIVE to
+ * the parent status, allowing the SET's effect to percolate up.
+ * (But if we're exiting the outermost transaction, we'll drop the
+ * HAVE_TENTATIVE bit below.)
+ *
+ * Otherwise, we have a transaction that aborted or executed only
+ * SET LOCAL (or no SET at all). In either case it should have no
+ * further effect, so restore both tentative and actual values from
+ * the stack entry.
+ */
+ useTentative = isCommit && (my_status & GUC_HAVE_TENTATIVE) != 0;
changed = false;
switch (gconf->vartype)
@@ -2387,126 +2525,190 @@ AtEOXact_GUC(bool isCommit)
case PGC_BOOL:
{
struct config_bool *conf = (struct config_bool *) gconf;
+ bool newval;
+ GucSource newsource;
- if (isCommit && (conf->gen.status & GUC_HAVE_TENTATIVE))
+ if (useTentative)
+ {
+ newval = conf->tentative_val;
+ newsource = conf->gen.tentative_source;
+ conf->gen.status |= GUC_HAVE_TENTATIVE;
+ }
+ else
{
- conf->session_val = conf->tentative_val;
- conf->gen.session_source = conf->gen.tentative_source;
+ newval = stack->value.boolval;
+ newsource = stack->source;
+ conf->tentative_val = stack->tentative_val.boolval;
+ conf->gen.tentative_source = stack->tentative_source;
}
- if (*conf->variable != conf->session_val)
+ if (*conf->variable != newval)
{
if (conf->assign_hook)
- if (!(*conf->assign_hook) (conf->session_val,
+ if (!(*conf->assign_hook) (newval,
true, PGC_S_OVERRIDE))
elog(LOG, "failed to commit %s",
conf->gen.name);
- *conf->variable = conf->session_val;
+ *conf->variable = newval;
changed = true;
}
- conf->gen.source = conf->gen.session_source;
- conf->gen.status = 0;
+ conf->gen.source = newsource;
break;
}
case PGC_INT:
{
struct config_int *conf = (struct config_int *) gconf;
+ int newval;
+ GucSource newsource;
- if (isCommit && (conf->gen.status & GUC_HAVE_TENTATIVE))
+ if (useTentative)
+ {
+ newval = conf->tentative_val;
+ newsource = conf->gen.tentative_source;
+ conf->gen.status |= GUC_HAVE_TENTATIVE;
+ }
+ else
{
- conf->session_val = conf->tentative_val;
- conf->gen.session_source = conf->gen.tentative_source;
+ newval = stack->value.intval;
+ newsource = stack->source;
+ conf->tentative_val = stack->tentative_val.intval;
+ conf->gen.tentative_source = stack->tentative_source;
}
- if (*conf->variable != conf->session_val)
+ if (*conf->variable != newval)
{
if (conf->assign_hook)
- if (!(*conf->assign_hook) (conf->session_val,
+ if (!(*conf->assign_hook) (newval,
true, PGC_S_OVERRIDE))
elog(LOG, "failed to commit %s",
conf->gen.name);
- *conf->variable = conf->session_val;
+ *conf->variable = newval;
changed = true;
}
- conf->gen.source = conf->gen.session_source;
- conf->gen.status = 0;
+ conf->gen.source = newsource;
break;
}
case PGC_REAL:
{
struct config_real *conf = (struct config_real *) gconf;
+ double newval;
+ GucSource newsource;
- if (isCommit && (conf->gen.status & GUC_HAVE_TENTATIVE))
+ if (useTentative)
{
- conf->session_val = conf->tentative_val;
- conf->gen.session_source = conf->gen.tentative_source;
+ newval = conf->tentative_val;
+ newsource = conf->gen.tentative_source;
+ conf->gen.status |= GUC_HAVE_TENTATIVE;
+ }
+ else
+ {
+ newval = stack->value.realval;
+ newsource = stack->source;
+ conf->tentative_val = stack->tentative_val.realval;
+ conf->gen.tentative_source = stack->tentative_source;
}
- if (*conf->variable != conf->session_val)
+ if (*conf->variable != newval)
{
if (conf->assign_hook)
- if (!(*conf->assign_hook) (conf->session_val,
+ if (!(*conf->assign_hook) (newval,
true, PGC_S_OVERRIDE))
elog(LOG, "failed to commit %s",
conf->gen.name);
- *conf->variable = conf->session_val;
+ *conf->variable = newval;
changed = true;
}
- conf->gen.source = conf->gen.session_source;
- conf->gen.status = 0;
+ conf->gen.source = newsource;
break;
}
case PGC_STRING:
{
struct config_string *conf = (struct config_string *) gconf;
+ char *newval;
+ GucSource newsource;
- if (isCommit && (conf->gen.status & GUC_HAVE_TENTATIVE))
+ if (useTentative)
{
- SET_STRING_SESSION_VAL(conf, conf->tentative_val);
- conf->gen.session_source = conf->gen.tentative_source;
- conf->tentative_val = NULL; /* transfer ownership */
+ newval = conf->tentative_val;
+ newsource = conf->gen.tentative_source;
+ conf->gen.status |= GUC_HAVE_TENTATIVE;
}
else
- SET_STRING_TENTATIVE_VAL(conf, NULL);
-
- if (*conf->variable != conf->session_val)
{
- char *str = conf->session_val;
+ newval = stack->value.stringval;
+ newsource = stack->source;
+ set_string_field(conf, &conf->tentative_val,
+ stack->tentative_val.stringval);
+ conf->gen.tentative_source = stack->tentative_source;
+ }
+ if (*conf->variable != newval)
+ {
if (conf->assign_hook)
{
const char *newstr;
- newstr = (*conf->assign_hook) (str, true,
+ newstr = (*conf->assign_hook) (newval, true,
PGC_S_OVERRIDE);
if (newstr == NULL)
elog(LOG, "failed to commit %s",
conf->gen.name);
- else if (newstr != str)
+ else if (newstr != newval)
{
/*
+ * If newval should now be freed, it'll be
+ * taken care of below.
+ *
* See notes in set_config_option about
* casting
*/
- str = (char *) newstr;
- SET_STRING_SESSION_VAL(conf, str);
+ newval = (char *) newstr;
}
}
- SET_STRING_VARIABLE(conf, str);
+ set_string_field(conf, conf->variable, newval);
changed = true;
}
- conf->gen.source = conf->gen.session_source;
- conf->gen.status = 0;
+ conf->gen.source = newsource;
+ /* Release stacked values if not used anymore */
+ set_string_field(conf, &stack->value.stringval,
+ NULL);
+ set_string_field(conf, &stack->tentative_val.stringval,
+ NULL);
+ /* Don't store tentative value separately after commit */
+ if (!isSubXact)
+ set_string_field(conf, &conf->tentative_val, NULL);
break;
}
}
+ /* Finish popping the state stack */
+ gconf->stack = stack->prev;
+ pfree(stack);
+
+ /*
+ * If we're now out of all xact levels, forget TENTATIVE status bit;
+ * there's nothing tentative about the value anymore.
+ */
+ if (!isSubXact)
+ {
+ Assert(gconf->stack == NULL);
+ gconf->status = 0;
+ }
+
+ /* Report new value if we changed it */
if (changed && (gconf->flags & GUC_REPORT))
ReportGUCOption(gconf);
}
- guc_dirty = false;
+ /*
+ * If we're now out of all xact levels, we can clear guc_dirty.
+ * (Note: we cannot reset guc_dirty when exiting a subtransaction,
+ * because we know that all outer transaction levels will have stacked
+ * values to deal with.)
+ */
+ if (!isSubXact)
+ guc_dirty = false;
}
@@ -2810,7 +3012,7 @@ set_config_option(const char *name, const char *value,
}
/*
- * Should we set reset/session values? (If so, the behavior is not
+ * Should we set reset/stacked values? (If so, the behavior is not
* transactional.)
*/
makeDefault = changeVal && (source <= PGC_S_OVERRIDE) && (value != NULL);
@@ -2820,7 +3022,7 @@ set_config_option(const char *name, const char *value,
* However, if changeVal is false then plow ahead anyway since we are
* trying to find out if the value is potentially good, not actually
* use it. Also keep going if makeDefault is true, since we may want
- * to set the reset/session values even if we can't set the variable
+ * to set the reset/stacked values even if we can't set the variable
* itself.
*/
if (record->source > source)
@@ -2901,6 +3103,9 @@ set_config_option(const char *name, const char *value,
if (changeVal || makeDefault)
{
+ /* Save old value to support transaction abort */
+ if (!makeDefault)
+ push_old_value(&conf->gen);
if (changeVal)
{
*conf->variable = newval;
@@ -2908,15 +3113,20 @@ set_config_option(const char *name, const char *value,
}
if (makeDefault)
{
+ GucStack *stack;
+
if (conf->gen.reset_source <= source)
{
conf->reset_val = newval;
conf->gen.reset_source = source;
}
- if (conf->gen.session_source <= source)
+ for (stack = conf->gen.stack; stack; stack = stack->prev)
{
- conf->session_val = newval;
- conf->gen.session_source = source;
+ if (stack->source <= source)
+ {
+ stack->value.boolval = newval;
+ stack->source = source;
+ }
}
}
else if (isLocal)
@@ -3006,6 +3216,9 @@ set_config_option(const char *name, const char *value,
if (changeVal || makeDefault)
{
+ /* Save old value to support transaction abort */
+ if (!makeDefault)
+ push_old_value(&conf->gen);
if (changeVal)
{
*conf->variable = newval;
@@ -3013,15 +3226,20 @@ set_config_option(const char *name, const char *value,
}
if (makeDefault)
{
+ GucStack *stack;
+
if (conf->gen.reset_source <= source)
{
conf->reset_val = newval;
conf->gen.reset_source = source;
}
- if (conf->gen.session_source <= source)
+ for (stack = conf->gen.stack; stack; stack = stack->prev)
{
- conf->session_val = newval;
- conf->gen.session_source = source;
+ if (stack->source <= source)
+ {
+ stack->value.intval = newval;
+ stack->source = source;
+ }
}
}
else if (isLocal)
@@ -3101,6 +3319,9 @@ set_config_option(const char *name, const char *value,
if (changeVal || makeDefault)
{
+ /* Save old value to support transaction abort */
+ if (!makeDefault)
+ push_old_value(&conf->gen);
if (changeVal)
{
*conf->variable = newval;
@@ -3108,15 +3329,20 @@ set_config_option(const char *name, const char *value,
}
if (makeDefault)
{
+ GucStack *stack;
+
if (conf->gen.reset_source <= source)
{
conf->reset_val = newval;
conf->gen.reset_source = source;
}
- if (conf->gen.session_source <= source)
+ for (stack = conf->gen.stack; stack; stack = stack->prev)
{
- conf->session_val = newval;
- conf->gen.session_source = source;
+ if (stack->source <= source)
+ {
+ stack->value.realval = newval;
+ stack->source = source;
+ }
}
}
else if (isLocal)
@@ -3261,27 +3487,34 @@ set_config_option(const char *name, const char *value,
if (changeVal || makeDefault)
{
+ /* Save old value to support transaction abort */
+ if (!makeDefault)
+ push_old_value(&conf->gen);
if (changeVal)
{
- SET_STRING_VARIABLE(conf, newval);
+ set_string_field(conf, conf->variable, newval);
conf->gen.source = source;
}
if (makeDefault)
{
+ GucStack *stack;
+
if (conf->gen.reset_source <= source)
{
- SET_STRING_RESET_VAL(conf, newval);
+ set_string_field(conf, &conf->reset_val, newval);
conf->gen.reset_source = source;
}
- if (conf->gen.session_source <= source)
+ for (stack = conf->gen.stack; stack; stack = stack->prev)
{
- SET_STRING_SESSION_VAL(conf, newval);
- conf->gen.session_source = source;
+ if (stack->source <= source)
+ {
+ set_string_field(conf, &stack->value.stringval,
+ newval);
+ stack->source = source;
+ }
}
/* Perhaps we didn't install newval anywhere */
- if (newval != *conf->variable &&
- newval != conf->session_val &&
- newval != conf->reset_val)
+ if (!string_field_used(conf, newval))
free(newval);
}
else if (isLocal)
@@ -3291,7 +3524,7 @@ set_config_option(const char *name, const char *value,
}
else
{
- SET_STRING_TENTATIVE_VAL(conf, newval);
+ set_string_field(conf, &conf->tentative_val, newval);
conf->gen.tentative_source = source;
conf->gen.status |= GUC_HAVE_TENTATIVE;
guc_dirty = true;
@@ -3608,44 +3841,36 @@ define_custom_variable(struct config_generic* variable)
/* This better be a placeholder
*/
if(((*res)->flags & GUC_CUSTOM_PLACEHOLDER) == 0)
- {
ereport(ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
errmsg("attempt to redefine parameter \"%s\"", name)));
- }
- pHolder = (struct config_string*)*res;
+
+ Assert((*res)->vartype == PGC_STRING);
+ pHolder = (struct config_string*) *res;
- /* We have the same name, no sorting is necessary.
- */
+ /* We have the same name, no sorting is necessary */
*res = variable;
value = *pHolder->variable;
- /* Assign the variable stored in the placeholder to the real
- * variable.
+ /*
+ * Assign the string value stored in the placeholder to the real variable.
+ *
+ * XXX this is not really good enough --- it should be a nontransactional
+ * assignment, since we don't want it to roll back if the current xact
+ * fails later.
*/
set_config_option(name, value,
pHolder->gen.context, pHolder->gen.source,
false, true);
- /* Free up stuff occupied by the placeholder variable
+ /*
+ * Free up as much as we conveniently can of the placeholder structure
+ * (this neglects any stack items...)
*/
- if(value != NULL)
- free((void*)value);
-
- if(pHolder->reset_val != NULL && pHolder->reset_val != value)
- free(pHolder->reset_val);
-
- if(pHolder->session_val != NULL
- && pHolder->session_val != value
- && pHolder->session_val != pHolder->reset_val)
- free(pHolder->session_val);
-
- if(pHolder->tentative_val != NULL
- && pHolder->tentative_val != value
- && pHolder->tentative_val != pHolder->reset_val
- && pHolder->tentative_val != pHolder->session_val)
- free(pHolder->tentative_val);
+ set_string_field(pHolder, pHolder->variable, NULL);
+ set_string_field(pHolder, &pHolder->reset_val, NULL);
+ set_string_field(pHolder, &pHolder->tentative_val, NULL);
free(pHolder);
}
@@ -3754,7 +3979,7 @@ void DefineCustomStringVariable(
define_custom_variable(&var->gen);
}
-extern void EmittWarningsOnPlaceholders(const char* className)
+extern void EmitWarningsOnPlaceholders(const char* className)
{
struct config_generic** vars = guc_variables;
struct config_generic** last = vars + num_guc_variables;
@@ -5133,5 +5358,14 @@ assign_log_stats(bool newval, bool doit, GucSource source)
return true;
}
+static bool
+assign_transaction_read_only(bool newval, bool doit, GucSource source)
+{
+ if (doit && source >= PGC_S_INTERACTIVE && IsSubTransaction())
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("cannot set transaction read only mode inside a subtransaction")));
+ return true;
+}
#include "guc-file.c"