1
0
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:
Tom Lane
2007-09-11 00:06:42 +00:00
parent b366562e43
commit 82a47982f3
16 changed files with 814 additions and 510 deletions

View File

@ -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;
}

View File

@ -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();

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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.

View File

@ -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

View File

@ -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);

View File

@ -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 */

View File

@ -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)

View File

@ -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');