mirror of
https://github.com/postgres/postgres.git
synced 2025-07-27 12:41:57 +03:00
Arrange for SET LOCAL's effects to persist until the end of the current top
transaction, unless rolled back or overridden by a SET clause for the same variable attached to a surrounding function call. Per discussion, these seem the best semantics. Note that this is an INCOMPATIBLE CHANGE: in 8.0 through 8.2, SET LOCAL's effects disappeared at subtransaction commit (leading to behavior that made little sense at the SQL level). I took advantage of the opportunity to rewrite and simplify the GUC variable save/restore logic a little bit. The old idea of a "tentative" value is gone; it was a hangover from before we had a stack. Also, we no longer need a stack entry for every nesting level, but only for those in which a variable's value actually changed.
This commit is contained in:
@ -15,7 +15,7 @@
|
||||
*
|
||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/backend/utils/adt/ri_triggers.c,v 1.96 2007/08/15 19:15:46 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/utils/adt/ri_triggers.c,v 1.97 2007/09/11 00:06:42 tgl Exp $
|
||||
*
|
||||
* ----------
|
||||
*/
|
||||
@ -2749,7 +2749,7 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
|
||||
snprintf(workmembuf, sizeof(workmembuf), "%d", maintenance_work_mem);
|
||||
(void) set_config_option("work_mem", workmembuf,
|
||||
PGC_USERSET, PGC_S_SESSION,
|
||||
true, true);
|
||||
GUC_ACTION_LOCAL, true);
|
||||
|
||||
if (SPI_connect() != SPI_OK_CONNECT)
|
||||
elog(ERROR, "SPI_connect failed");
|
||||
@ -2832,13 +2832,12 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
|
||||
|
||||
/*
|
||||
* Restore work_mem for the remainder of the current transaction. This is
|
||||
* another SET LOCAL, so it won't affect the session value, nor any
|
||||
* tentative value if there is one.
|
||||
* another SET LOCAL, so it won't affect the session value.
|
||||
*/
|
||||
snprintf(workmembuf, sizeof(workmembuf), "%d", old_work_mem);
|
||||
(void) set_config_option("work_mem", workmembuf,
|
||||
PGC_USERSET, PGC_S_SESSION,
|
||||
true, true);
|
||||
GUC_ACTION_LOCAL, true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/utils/fmgr/fmgr.c,v 1.109 2007/09/03 00:39:18 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/utils/fmgr/fmgr.c,v 1.110 2007/09/11 00:06:42 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -925,11 +925,10 @@ fmgr_security_definer(PG_FUNCTION_ARGS)
|
||||
|
||||
if (fcache->proconfig)
|
||||
{
|
||||
/* The options are processed as if by SET LOCAL var = val */
|
||||
ProcessGUCArray(fcache->proconfig,
|
||||
(superuser() ? PGC_SUSET : PGC_USERSET),
|
||||
PGC_S_SESSION,
|
||||
true);
|
||||
GUC_ACTION_SAVE);
|
||||
}
|
||||
|
||||
result = FunctionCallInvoke(fcinfo);
|
||||
@ -937,8 +936,7 @@ fmgr_security_definer(PG_FUNCTION_ARGS)
|
||||
PG_CATCH();
|
||||
{
|
||||
fcinfo->flinfo = save_flinfo;
|
||||
if (fcache->proconfig)
|
||||
AtEOXact_GUC(false, save_nestlevel);
|
||||
/* We don't need to restore GUC settings, outer xact abort will */
|
||||
if (OidIsValid(fcache->userid))
|
||||
SetUserId(save_userid);
|
||||
PG_RE_THROW();
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/utils/init/miscinit.c,v 1.163 2007/09/03 00:39:18 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/utils/init/miscinit.c,v 1.164 2007/09/11 00:06:42 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -453,7 +453,7 @@ InitializeSessionUserId(const char *rolename)
|
||||
* right to insert an option into pg_authid was checked when it was
|
||||
* inserted.
|
||||
*/
|
||||
ProcessGUCArray(a, PGC_SUSET, PGC_S_USER, false);
|
||||
ProcessGUCArray(a, PGC_SUSET, PGC_S_USER, GUC_ACTION_SET);
|
||||
}
|
||||
|
||||
ReleaseSysCache(roleTup);
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/utils/init/postinit.c,v 1.177 2007/09/03 00:39:18 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/utils/init/postinit.c,v 1.178 2007/09/11 00:06:42 tgl Exp $
|
||||
*
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
@ -255,7 +255,7 @@ CheckMyDatabase(const char *name, bool am_superuser)
|
||||
* right to insert an option into pg_database was checked when it
|
||||
* was inserted.
|
||||
*/
|
||||
ProcessGUCArray(a, PGC_SUSET, PGC_S_DATABASE, false);
|
||||
ProcessGUCArray(a, PGC_SUSET, PGC_S_DATABASE, GUC_ACTION_SET);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
$PostgreSQL: pgsql/src/backend/utils/misc/README,v 1.6 2007/09/10 00:57:21 tgl Exp $
|
||||
$PostgreSQL: pgsql/src/backend/utils/misc/README,v 1.7 2007/09/11 00:06:42 tgl Exp $
|
||||
|
||||
|
||||
GUC IMPLEMENTATION NOTES
|
||||
@ -69,74 +69,133 @@ by SHOW.
|
||||
|
||||
SAVING/RESTORING GUC VARIABLE VALUES
|
||||
|
||||
Prior values of configuration variables must be remembered in order to
|
||||
deal with three special cases: RESET (a/k/a SET TO DEFAULT), rollback of
|
||||
SET on transaction abort, and rollback of SET LOCAL at transaction end
|
||||
(either commit or abort). RESET is defined as selecting the value that
|
||||
would be effective had there never been any SET commands in the current
|
||||
session.
|
||||
Prior values of configuration variables must be remembered in order to deal
|
||||
with several special cases: RESET (a/k/a SET TO DEFAULT), rollback of SET
|
||||
on transaction abort, rollback of SET LOCAL at transaction end (either
|
||||
commit or abort), and save/restore around a function that has a SET option.
|
||||
RESET is defined as selecting the value that would be effective had there
|
||||
never been any SET commands in the current session.
|
||||
|
||||
To handle these cases we must keep track of many distinct values for each
|
||||
variable. The primary values are:
|
||||
|
||||
* actual variable contents always the current effective value
|
||||
|
||||
* reset_value the value to use for RESET
|
||||
* reset_val the value to use for RESET
|
||||
|
||||
* tentative_value the uncommitted result of SET
|
||||
(Each GUC entry also has a boot_val which is the wired-in default value.
|
||||
This is assigned to the reset_val and the actual variable during
|
||||
InitializeGUCOptions(). The boot_val is also consulted to restore the
|
||||
correct reset_val if SIGHUP processing discovers that a variable formerly
|
||||
specified in postgresql.conf is no longer set there.)
|
||||
|
||||
The reason we need a tentative_value separate from the actual value is
|
||||
that when a transaction does SET followed by SET LOCAL, the actual value
|
||||
will now be the LOCAL value, but we want to remember the prior SET so that
|
||||
that value is restored at transaction commit.
|
||||
In addition to the primary values, there is a stack of former effective
|
||||
values that might need to be restored in future. Stacking and unstacking
|
||||
is controlled by the GUC "nest level", which is zero when outside any
|
||||
transaction, one at top transaction level, and incremented for each
|
||||
open subtransaction or function call with a SET option. A stack entry
|
||||
is made whenever a GUC variable is first modified at a given nesting level.
|
||||
(Note: the reset_val need not be stacked because it is only changed by
|
||||
non-transactional operations.)
|
||||
|
||||
In addition, for each level of transaction (possibly nested) we have to
|
||||
remember the transaction-entry-time actual and tentative values, in case
|
||||
we need to restore them at transaction end. (The RESET value is essentially
|
||||
non-transactional, so it doesn't have to be stacked.) For efficiency these
|
||||
stack entries are not constructed until/unless the variable is actually SET
|
||||
within a particular transaction.
|
||||
A stack entry has a state, a prior value of the GUC variable, a remembered
|
||||
source of that prior value, and depending on the state may also have a
|
||||
"masked" value. The masked value is needed when SET followed by SET LOCAL
|
||||
occur at the same nest level: the SET's value is masked but must be
|
||||
remembered to restore after transaction commit.
|
||||
|
||||
During initialization we set the actual value and reset_value based on
|
||||
During initialization we set the actual value and reset_val based on
|
||||
whichever non-interactive source has the highest priority. They will
|
||||
have the same value. The tentative_value is not meaningful at this point.
|
||||
have the same value.
|
||||
|
||||
A SET command starts by stacking the existing actual and tentative values
|
||||
if this hasn't already been done within the current transaction. Then:
|
||||
The possible transactional operations on a GUC value are:
|
||||
|
||||
A SET LOCAL command sets the actual variable (and nothing else). At
|
||||
transaction end, the stacked values are used to restore the GUC entry
|
||||
to its pre-transaction state.
|
||||
Entry to a function with a SET option:
|
||||
|
||||
A SET (or SET SESSION) command sets the actual variable, and if no error,
|
||||
then sets the tentative_value. If the transaction commits, the
|
||||
tentative_value is assigned again to the actual variable (which could by
|
||||
now be different, if the SET was followed by SET LOCAL). If the
|
||||
transaction aborts, the stacked values are used to restore the GUC entry
|
||||
to its pre-transaction state.
|
||||
Push a stack entry with the prior variable value and state SAVE,
|
||||
then set the variable.
|
||||
|
||||
In the case of SET within nested subtransactions, at each commit the
|
||||
tentative_value propagates out to the next transaction level. It will
|
||||
be thrown away at abort of any level, or after exiting the top transaction.
|
||||
Plain SET command:
|
||||
|
||||
RESET is executed like a SET, but using the reset_value as the desired new
|
||||
If no stack entry of current level:
|
||||
Push new stack entry w/prior value and state SET
|
||||
else if stack entry's state is SAVE, SET, or LOCAL:
|
||||
change stack state to SET, don't change saved value
|
||||
(here we are forgetting effects of prior set action)
|
||||
else (entry must have state SET+LOCAL):
|
||||
discard its masked value, change state to SET
|
||||
(here we are forgetting effects of prior SET and SET LOCAL)
|
||||
Now set new value.
|
||||
|
||||
SET LOCAL command:
|
||||
|
||||
If no stack entry of current level:
|
||||
Push new stack entry w/prior value and state LOCAL
|
||||
else if stack entry's state is SAVE or LOCAL or SET+LOCAL:
|
||||
no change to stack entry
|
||||
(in SAVE case, SET LOCAL will be forgotten at func exit)
|
||||
else (entry must have state SET):
|
||||
put current active into its masked slot, set state SET+LOCAL
|
||||
Now set new value.
|
||||
|
||||
Transaction or subtransaction abort:
|
||||
|
||||
Pop stack entries, restoring prior value, until top < subxact depth
|
||||
|
||||
Transaction or subtransaction commit (incl. successful function exit):
|
||||
|
||||
While stack entry level >= subxact depth
|
||||
|
||||
if entry's state is SAVE:
|
||||
pop, restoring prior value
|
||||
else if level is 1 and entry's state is SET+LOCAL:
|
||||
pop, restoring *masked* value
|
||||
else if level is 1 and entry's state is SET:
|
||||
pop, discarding old value
|
||||
else if level is 1 and entry's state is LOCAL:
|
||||
pop, restoring prior value
|
||||
else if there is no entry of exactly level N-1:
|
||||
decrement entry's level, no other state change
|
||||
else
|
||||
merge entries of level N-1 and N as specified below
|
||||
|
||||
The merged entry will have level N-1 and prior = older prior, so easiest
|
||||
to keep older entry and free newer. There are 12 possibilities since
|
||||
we already handled level N state = SAVE:
|
||||
|
||||
N-1 N
|
||||
|
||||
SAVE SET discard top prior, set state SET
|
||||
SAVE LOCAL discard top prior, no change to stack entry
|
||||
SAVE SET+LOCAL discard top prior, copy masked, state S+L
|
||||
|
||||
SET SET discard top prior, no change to stack entry
|
||||
SET LOCAL copy top prior to masked, state S+L
|
||||
SET SET+LOCAL discard top prior, copy masked, state S+L
|
||||
|
||||
LOCAL SET discard top prior, set state SET
|
||||
LOCAL LOCAL discard top prior, no change to stack entry
|
||||
LOCAL SET+LOCAL discard top prior, copy masked, state S+L
|
||||
|
||||
SET+LOCAL SET discard top prior and second masked, state SET
|
||||
SET+LOCAL LOCAL discard top prior, no change to stack entry
|
||||
SET+LOCAL SET+LOCAL discard top prior, copy masked, state S+L
|
||||
|
||||
|
||||
RESET is executed like a SET, but using the reset_val as the desired new
|
||||
value. (We do not provide a RESET LOCAL command, but SET LOCAL TO DEFAULT
|
||||
has the same behavior that RESET LOCAL would.) The source associated with
|
||||
the reset_value also becomes associated with the actual and tentative values.
|
||||
the reset_val also becomes associated with the actual value.
|
||||
|
||||
If SIGHUP is received, the GUC code rereads the postgresql.conf
|
||||
configuration file (this does not happen in the signal handler, but at
|
||||
next return to main loop; note that it can be executed while within a
|
||||
transaction). New values from postgresql.conf are assigned to actual
|
||||
variable, reset_value, and stacked actual values, but only if each of
|
||||
variable, reset_val, and stacked actual values, but only if each of
|
||||
these has a current source priority <= PGC_S_FILE. (It is thus possible
|
||||
for reset_value to track the config-file setting even if there is
|
||||
for reset_val to track the config-file setting even if there is
|
||||
currently a different interactive value of the actual variable.)
|
||||
|
||||
Note that tentative_value is unused and undefined except between a SET
|
||||
command and the end of the transaction. Also notice that we must track
|
||||
the source associated with each one of the values.
|
||||
|
||||
The assign_hook and show_hook routines work only with the actual variable,
|
||||
and are not directly aware of the additional values maintained by GUC.
|
||||
This is not a problem for normal usage, since we can assign first to the
|
||||
@ -154,9 +213,9 @@ pstrdup/palloc mechanisms. We would need to keep them in a permanent
|
||||
context anyway, and strdup gives us more control over handling
|
||||
out-of-memory failures.
|
||||
|
||||
We allow a string variable's actual value, reset_val, tentative_val, and
|
||||
stacked copies of same to point at the same storage. This makes it
|
||||
slightly harder to free space (must test whether a value to be freed isn't
|
||||
equal to any of the other pointers in the GUC entry or associated stack
|
||||
items). The main advantage is that we never need to strdup during
|
||||
transaction commit/abort, so cannot cause an out-of-memory failure there.
|
||||
We allow a string variable's actual value, reset_val, boot_val, and stacked
|
||||
values to point at the same storage. This makes it slightly harder to free
|
||||
space (we must test whether a value to be freed isn't equal to any of the
|
||||
other pointers in the GUC entry or associated stack items). The main
|
||||
advantage is that we never need to strdup during transaction commit/abort,
|
||||
so cannot cause an out-of-memory failure there.
|
||||
|
@ -4,7 +4,7 @@
|
||||
*
|
||||
* Copyright (c) 2000-2007, PostgreSQL Global Development Group
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/backend/utils/misc/guc-file.l,v 1.51 2007/09/10 00:57:21 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/utils/misc/guc-file.l,v 1.52 2007/09/11 00:06:42 tgl Exp $
|
||||
*/
|
||||
|
||||
%{
|
||||
@ -231,7 +231,7 @@ ProcessConfigFile(GucContext context)
|
||||
}
|
||||
|
||||
if (!set_config_option(item->name, item->value, context,
|
||||
PGC_S_FILE, false, false))
|
||||
PGC_S_FILE, GUC_ACTION_SET, false))
|
||||
goto cleanup_list;
|
||||
}
|
||||
|
||||
@ -264,24 +264,21 @@ ProcessConfigFile(GucContext context)
|
||||
|
||||
/*
|
||||
* Reset any "file" sources to "default", else set_config_option
|
||||
* will not override those settings. tentative_source should
|
||||
* never be "file".
|
||||
* will not override those settings.
|
||||
*/
|
||||
if (gconf->reset_source == PGC_S_FILE)
|
||||
gconf->reset_source = PGC_S_DEFAULT;
|
||||
Assert(gconf->tentative_source != PGC_S_FILE);
|
||||
if (gconf->source == PGC_S_FILE)
|
||||
gconf->source = PGC_S_DEFAULT;
|
||||
for (stack = gconf->stack; stack; stack = stack->prev)
|
||||
{
|
||||
Assert(stack->tentative_source != PGC_S_FILE);
|
||||
if (stack->source == PGC_S_FILE)
|
||||
stack->source = PGC_S_DEFAULT;
|
||||
}
|
||||
|
||||
/* Now we can re-apply the wired-in default */
|
||||
set_config_option(gconf->name, NULL, context, PGC_S_DEFAULT,
|
||||
false, true);
|
||||
GUC_ACTION_SET, true);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -289,25 +286,27 @@ ProcessConfigFile(GucContext context)
|
||||
* is a no-op except in the case where one of these had been in the
|
||||
* config file and is now removed. PGC_S_ENV_VAR will override the
|
||||
* wired-in default we just applied, but cannot override any other source.
|
||||
* PGPORT can be ignored, because it cannot be changed without restart.
|
||||
*
|
||||
* Keep this list in sync with InitializeGUCOptions()!
|
||||
* PGPORT can be ignored, because it cannot be changed without restart.
|
||||
* We assume rlimit hasn't changed, either.
|
||||
*/
|
||||
envvar = getenv("PGDATESTYLE");
|
||||
if (envvar != NULL)
|
||||
set_config_option("datestyle", envvar, PGC_POSTMASTER,
|
||||
PGC_S_ENV_VAR, false, true);
|
||||
PGC_S_ENV_VAR, GUC_ACTION_SET, true);
|
||||
|
||||
envvar = getenv("PGCLIENTENCODING");
|
||||
if (envvar != NULL)
|
||||
set_config_option("client_encoding", envvar, PGC_POSTMASTER,
|
||||
PGC_S_ENV_VAR, false, true);
|
||||
PGC_S_ENV_VAR, GUC_ACTION_SET, true);
|
||||
|
||||
|
||||
/* If we got here all the options checked out okay, so apply them. */
|
||||
for (item = head; item; item = item->next)
|
||||
{
|
||||
set_config_option(item->name, item->value, context,
|
||||
PGC_S_FILE, false, true);
|
||||
PGC_S_FILE, GUC_ACTION_SET, true);
|
||||
}
|
||||
|
||||
cleanup_list:
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -7,7 +7,7 @@
|
||||
* Copyright (c) 2000-2007, PostgreSQL Global Development Group
|
||||
* Written by Peter Eisentraut <peter_e@gmx.net>.
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/include/utils/guc.h,v 1.85 2007/09/03 18:46:30 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/include/utils/guc.h,v 1.86 2007/09/11 00:06:42 tgl Exp $
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef GUC_H
|
||||
@ -100,6 +100,14 @@ typedef bool (*GucRealAssignHook) (double newval, bool doit, GucSource source);
|
||||
|
||||
typedef const char *(*GucShowHook) (void);
|
||||
|
||||
typedef enum
|
||||
{
|
||||
/* Types of set_config_option actions */
|
||||
GUC_ACTION_SET, /* regular SET command */
|
||||
GUC_ACTION_LOCAL, /* SET LOCAL command */
|
||||
GUC_ACTION_SAVE /* function SET option */
|
||||
} GucAction;
|
||||
|
||||
#define GUC_QUALIFIER_SEPARATOR '.'
|
||||
|
||||
/* GUC vars that are actually declared in guc.c, rather than elsewhere */
|
||||
@ -196,7 +204,7 @@ extern void BeginReportingGUCOptions(void);
|
||||
extern void ParseLongOption(const char *string, char **name, char **value);
|
||||
extern bool set_config_option(const char *name, const char *value,
|
||||
GucContext context, GucSource source,
|
||||
bool isLocal, bool changeVal);
|
||||
GucAction action, bool changeVal);
|
||||
extern char *GetConfigOptionByName(const char *name, const char **varname);
|
||||
extern void GetConfigOptionByNum(int varnum, const char **values, bool *noshow);
|
||||
extern int GetNumConfigOptions(void);
|
||||
@ -209,7 +217,7 @@ extern void ExecSetVariableStmt(VariableSetStmt *stmt);
|
||||
extern char *ExtractSetVariableArgs(VariableSetStmt *stmt);
|
||||
|
||||
extern void ProcessGUCArray(ArrayType *array,
|
||||
GucContext context, GucSource source, bool isLocal);
|
||||
GucContext context, GucSource source, GucAction action);
|
||||
extern ArrayType *GUCArrayAdd(ArrayType *array, const char *name, const char *value);
|
||||
extern ArrayType *GUCArrayDelete(ArrayType *array, const char *name);
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
*
|
||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/include/utils/guc_tables.h,v 1.34 2007/09/10 00:57:22 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/include/utils/guc_tables.h,v 1.35 2007/09/11 00:06:42 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -79,18 +79,27 @@ enum config_group
|
||||
};
|
||||
|
||||
/*
|
||||
* Stack entry for saving the state of a variable prior to the current
|
||||
* transaction
|
||||
* Stack entry for saving the state a variable had prior to an uncommitted
|
||||
* transactional change
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
/* This is almost GucAction, but we need a fourth state for SET+LOCAL */
|
||||
GUC_SAVE, /* entry caused by function SET option */
|
||||
GUC_SET, /* entry caused by plain SET command */
|
||||
GUC_LOCAL, /* entry caused by SET LOCAL command */
|
||||
GUC_SET_LOCAL /* entry caused by SET then SET LOCAL */
|
||||
} GucStackState;
|
||||
|
||||
typedef struct guc_stack
|
||||
{
|
||||
struct guc_stack *prev; /* previous stack item, if any */
|
||||
int nest_level; /* nesting depth of cur transaction */
|
||||
int status; /* previous status bits, see below */
|
||||
GucSource tentative_source; /* source of the tentative_value */
|
||||
GucSource source; /* source of the actual value */
|
||||
union config_var_value tentative_val; /* previous tentative val */
|
||||
union config_var_value value; /* previous actual value */
|
||||
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 */
|
||||
/* masked value's source must be PGC_S_SESSION, so no need to store it */
|
||||
} GucStack;
|
||||
|
||||
/*
|
||||
@ -113,9 +122,8 @@ struct config_generic
|
||||
enum config_type vartype; /* type of variable (set only at startup) */
|
||||
int status; /* status bits, see below */
|
||||
GucSource reset_source; /* source of the reset_value */
|
||||
GucSource tentative_source; /* source of the tentative_value */
|
||||
GucSource source; /* source of the current actual value */
|
||||
GucStack *stack; /* stacked outside-of-transaction states */
|
||||
GucStack *stack; /* stacked prior values */
|
||||
};
|
||||
|
||||
/* bit values in flags field */
|
||||
@ -141,10 +149,7 @@ struct config_generic
|
||||
#define GUC_UNIT_TIME 0x7000 /* mask for MS, S, MIN */
|
||||
|
||||
/* bit values in status field */
|
||||
#define GUC_HAVE_TENTATIVE 0x0001 /* tentative value is defined */
|
||||
#define GUC_HAVE_LOCAL 0x0002 /* a SET LOCAL has been executed */
|
||||
#define GUC_HAVE_STACK 0x0004 /* we have stacked prior value(s) */
|
||||
#define GUC_IS_IN_FILE 0x0008 /* found it in config file */
|
||||
#define GUC_IS_IN_FILE 0x0001 /* found it in config file */
|
||||
/*
|
||||
* Caution: the GUC_IS_IN_FILE bit is transient state for ProcessConfigFile.
|
||||
* Do not assume that its value represents useful information elsewhere.
|
||||
@ -163,7 +168,6 @@ struct config_bool
|
||||
GucShowHook show_hook;
|
||||
/* variable fields, initialized at runtime: */
|
||||
bool reset_val;
|
||||
bool tentative_val;
|
||||
};
|
||||
|
||||
struct config_int
|
||||
@ -178,7 +182,6 @@ struct config_int
|
||||
GucShowHook show_hook;
|
||||
/* variable fields, initialized at runtime: */
|
||||
int reset_val;
|
||||
int tentative_val;
|
||||
};
|
||||
|
||||
struct config_real
|
||||
@ -193,7 +196,6 @@ struct config_real
|
||||
GucShowHook show_hook;
|
||||
/* variable fields, initialized at runtime: */
|
||||
double reset_val;
|
||||
double tentative_val;
|
||||
};
|
||||
|
||||
struct config_string
|
||||
@ -206,7 +208,6 @@ struct config_string
|
||||
GucShowHook show_hook;
|
||||
/* variable fields, initialized at runtime: */
|
||||
char *reset_val;
|
||||
char *tentative_val;
|
||||
};
|
||||
|
||||
/* constant tables corresponding to enums above and in guc.h */
|
||||
|
@ -352,6 +352,85 @@ SELECT '2006-08-13 12:34:56'::timestamptz;
|
||||
2006-08-13 12:34:56-07
|
||||
(1 row)
|
||||
|
||||
-- SET LOCAL persists through RELEASE (which was not true in 8.0-8.2)
|
||||
BEGIN;
|
||||
SHOW vacuum_cost_delay;
|
||||
vacuum_cost_delay
|
||||
-------------------
|
||||
400ms
|
||||
(1 row)
|
||||
|
||||
SHOW datestyle;
|
||||
DateStyle
|
||||
-----------
|
||||
ISO, YMD
|
||||
(1 row)
|
||||
|
||||
SELECT '2006-08-13 12:34:56'::timestamptz;
|
||||
timestamptz
|
||||
------------------------
|
||||
2006-08-13 12:34:56-07
|
||||
(1 row)
|
||||
|
||||
SAVEPOINT sp;
|
||||
SET LOCAL vacuum_cost_delay TO 300;
|
||||
SHOW vacuum_cost_delay;
|
||||
vacuum_cost_delay
|
||||
-------------------
|
||||
300ms
|
||||
(1 row)
|
||||
|
||||
SET LOCAL datestyle = 'Postgres, MDY';
|
||||
SHOW datestyle;
|
||||
DateStyle
|
||||
---------------
|
||||
Postgres, MDY
|
||||
(1 row)
|
||||
|
||||
SELECT '2006-08-13 12:34:56'::timestamptz;
|
||||
timestamptz
|
||||
------------------------------
|
||||
Sun Aug 13 12:34:56 2006 PDT
|
||||
(1 row)
|
||||
|
||||
RELEASE SAVEPOINT sp;
|
||||
SHOW vacuum_cost_delay;
|
||||
vacuum_cost_delay
|
||||
-------------------
|
||||
300ms
|
||||
(1 row)
|
||||
|
||||
SHOW datestyle;
|
||||
DateStyle
|
||||
---------------
|
||||
Postgres, MDY
|
||||
(1 row)
|
||||
|
||||
SELECT '2006-08-13 12:34:56'::timestamptz;
|
||||
timestamptz
|
||||
------------------------------
|
||||
Sun Aug 13 12:34:56 2006 PDT
|
||||
(1 row)
|
||||
|
||||
ROLLBACK;
|
||||
SHOW vacuum_cost_delay;
|
||||
vacuum_cost_delay
|
||||
-------------------
|
||||
400ms
|
||||
(1 row)
|
||||
|
||||
SHOW datestyle;
|
||||
DateStyle
|
||||
-----------
|
||||
ISO, YMD
|
||||
(1 row)
|
||||
|
||||
SELECT '2006-08-13 12:34:56'::timestamptz;
|
||||
timestamptz
|
||||
------------------------
|
||||
2006-08-13 12:34:56-07
|
||||
(1 row)
|
||||
|
||||
-- SET followed by SET LOCAL
|
||||
BEGIN;
|
||||
SET vacuum_cost_delay TO 400;
|
||||
@ -558,3 +637,65 @@ select report_guc('regex_flavor'), current_setting('regex_flavor');
|
||||
advanced | advanced
|
||||
(1 row)
|
||||
|
||||
-- SET LOCAL is restricted by a function SET option
|
||||
create or replace function myfunc(int) returns text as $$
|
||||
begin
|
||||
set local regex_flavor = extended;
|
||||
return current_setting('regex_flavor');
|
||||
end $$
|
||||
language plpgsql
|
||||
set regex_flavor = basic;
|
||||
select myfunc(0), current_setting('regex_flavor');
|
||||
myfunc | current_setting
|
||||
----------+-----------------
|
||||
extended | advanced
|
||||
(1 row)
|
||||
|
||||
alter function myfunc(int) reset all;
|
||||
select myfunc(0), current_setting('regex_flavor');
|
||||
myfunc | current_setting
|
||||
----------+-----------------
|
||||
extended | extended
|
||||
(1 row)
|
||||
|
||||
set regex_flavor = advanced;
|
||||
-- but SET isn't
|
||||
create or replace function myfunc(int) returns text as $$
|
||||
begin
|
||||
set regex_flavor = extended;
|
||||
return current_setting('regex_flavor');
|
||||
end $$
|
||||
language plpgsql
|
||||
set regex_flavor = basic;
|
||||
select myfunc(0), current_setting('regex_flavor');
|
||||
myfunc | current_setting
|
||||
----------+-----------------
|
||||
extended | extended
|
||||
(1 row)
|
||||
|
||||
set regex_flavor = advanced;
|
||||
-- it should roll back on error, though
|
||||
create or replace function myfunc(int) returns text as $$
|
||||
begin
|
||||
set regex_flavor = extended;
|
||||
perform 1/$1;
|
||||
return current_setting('regex_flavor');
|
||||
end $$
|
||||
language plpgsql
|
||||
set regex_flavor = basic;
|
||||
select myfunc(0);
|
||||
ERROR: division by zero
|
||||
CONTEXT: SQL statement "SELECT 1/ $1 "
|
||||
PL/pgSQL function "myfunc" line 3 at PERFORM
|
||||
select current_setting('regex_flavor');
|
||||
current_setting
|
||||
-----------------
|
||||
advanced
|
||||
(1 row)
|
||||
|
||||
select myfunc(1), current_setting('regex_flavor');
|
||||
myfunc | current_setting
|
||||
----------+-----------------
|
||||
extended | extended
|
||||
(1 row)
|
||||
|
||||
|
@ -99,6 +99,26 @@ SHOW vacuum_cost_delay;
|
||||
SHOW datestyle;
|
||||
SELECT '2006-08-13 12:34:56'::timestamptz;
|
||||
|
||||
-- SET LOCAL persists through RELEASE (which was not true in 8.0-8.2)
|
||||
BEGIN;
|
||||
SHOW vacuum_cost_delay;
|
||||
SHOW datestyle;
|
||||
SELECT '2006-08-13 12:34:56'::timestamptz;
|
||||
SAVEPOINT sp;
|
||||
SET LOCAL vacuum_cost_delay TO 300;
|
||||
SHOW vacuum_cost_delay;
|
||||
SET LOCAL datestyle = 'Postgres, MDY';
|
||||
SHOW datestyle;
|
||||
SELECT '2006-08-13 12:34:56'::timestamptz;
|
||||
RELEASE SAVEPOINT sp;
|
||||
SHOW vacuum_cost_delay;
|
||||
SHOW datestyle;
|
||||
SELECT '2006-08-13 12:34:56'::timestamptz;
|
||||
ROLLBACK;
|
||||
SHOW vacuum_cost_delay;
|
||||
SHOW datestyle;
|
||||
SELECT '2006-08-13 12:34:56'::timestamptz;
|
||||
|
||||
-- SET followed by SET LOCAL
|
||||
BEGIN;
|
||||
SET vacuum_cost_delay TO 400;
|
||||
@ -187,3 +207,47 @@ select report_guc('regex_flavor'), current_setting('regex_flavor');
|
||||
alter function report_guc(text) reset all;
|
||||
|
||||
select report_guc('regex_flavor'), current_setting('regex_flavor');
|
||||
|
||||
-- SET LOCAL is restricted by a function SET option
|
||||
create or replace function myfunc(int) returns text as $$
|
||||
begin
|
||||
set local regex_flavor = extended;
|
||||
return current_setting('regex_flavor');
|
||||
end $$
|
||||
language plpgsql
|
||||
set regex_flavor = basic;
|
||||
|
||||
select myfunc(0), current_setting('regex_flavor');
|
||||
|
||||
alter function myfunc(int) reset all;
|
||||
|
||||
select myfunc(0), current_setting('regex_flavor');
|
||||
|
||||
set regex_flavor = advanced;
|
||||
|
||||
-- but SET isn't
|
||||
create or replace function myfunc(int) returns text as $$
|
||||
begin
|
||||
set regex_flavor = extended;
|
||||
return current_setting('regex_flavor');
|
||||
end $$
|
||||
language plpgsql
|
||||
set regex_flavor = basic;
|
||||
|
||||
select myfunc(0), current_setting('regex_flavor');
|
||||
|
||||
set regex_flavor = advanced;
|
||||
|
||||
-- it should roll back on error, though
|
||||
create or replace function myfunc(int) returns text as $$
|
||||
begin
|
||||
set regex_flavor = extended;
|
||||
perform 1/$1;
|
||||
return current_setting('regex_flavor');
|
||||
end $$
|
||||
language plpgsql
|
||||
set regex_flavor = basic;
|
||||
|
||||
select myfunc(0);
|
||||
select current_setting('regex_flavor');
|
||||
select myfunc(1), current_setting('regex_flavor');
|
||||
|
Reference in New Issue
Block a user