Commit 82a47982 authored by Tom Lane's avatar Tom Lane

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.
parent b366562e
<!-- <!--
$PostgreSQL: pgsql/doc/src/sgml/ref/create_function.sgml,v 1.77 2007/09/03 18:46:29 tgl Exp $ $PostgreSQL: pgsql/doc/src/sgml/ref/create_function.sgml,v 1.78 2007/09/11 00:06:41 tgl Exp $
--> -->
<refentry id="SQL-CREATEFUNCTION"> <refentry id="SQL-CREATEFUNCTION">
...@@ -475,11 +475,11 @@ CREATE FUNCTION foo(int, out text) ... ...@@ -475,11 +475,11 @@ CREATE FUNCTION foo(int, out text) ...
</para> </para>
<para> <para>
If any <literal>SET</> clauses are attached to a function, then If a <literal>SET</> clause is attached to a function, then
the effects of a <command>SET LOCAL</> command executed inside the the effects of a <command>SET LOCAL</> command executed inside the
function are restricted to the function: the configuration parameter's function for the same variable are restricted to the function: the
value is restored at function exit. This is true even for parameters configuration parameter's prior value is still restored at function exit.
not mentioned in the <literal>SET</> clause(s). However, an ordinary However, an ordinary
<command>SET</> command (without <literal>LOCAL</>) overrides the <command>SET</> command (without <literal>LOCAL</>) overrides the
<literal>SET</> clause, much as it would do for a previous <command>SET <literal>SET</> clause, much as it would do for a previous <command>SET
LOCAL</> command: the effects of such a command will persist after LOCAL</> command: the effects of such a command will persist after
......
<!-- <!--
$PostgreSQL: pgsql/doc/src/sgml/ref/prepare_transaction.sgml,v 1.5 2006/09/16 00:30:19 momjian Exp $ $PostgreSQL: pgsql/doc/src/sgml/ref/prepare_transaction.sgml,v 1.6 2007/09/11 00:06:41 tgl Exp $
PostgreSQL documentation PostgreSQL documentation
--> -->
...@@ -94,7 +94,8 @@ PREPARE TRANSACTION <replaceable class="PARAMETER">transaction_id</replaceable> ...@@ -94,7 +94,8 @@ PREPARE TRANSACTION <replaceable class="PARAMETER">transaction_id</replaceable>
</para> </para>
<para> <para>
If the transaction modified any run-time parameters with <command>SET</>, If the transaction modified any run-time parameters with <command>SET</>
(without the <literal>LOCAL</> option),
those effects persist after <command>PREPARE TRANSACTION</>, and will not those effects persist after <command>PREPARE TRANSACTION</>, and will not
be affected by any later <command>COMMIT PREPARED</command> or be affected by any later <command>COMMIT PREPARED</command> or
<command>ROLLBACK PREPARED</command>. Thus, in this one respect <command>ROLLBACK PREPARED</command>. Thus, in this one respect
......
<!-- <!--
$PostgreSQL: pgsql/doc/src/sgml/ref/reset.sgml,v 1.35 2007/04/26 16:13:09 neilc Exp $ $PostgreSQL: pgsql/doc/src/sgml/ref/reset.sgml,v 1.36 2007/09/11 00:06:41 tgl Exp $
PostgreSQL documentation PostgreSQL documentation
--> -->
...@@ -44,13 +44,16 @@ SET <replaceable class="parameter">configuration_parameter</replaceable> TO DEFA ...@@ -44,13 +44,16 @@ SET <replaceable class="parameter">configuration_parameter</replaceable> TO DEFA
have had, if no <command>SET</> had ever been issued for it in the have had, if no <command>SET</> had ever been issued for it in the
current session. The actual source of this value might be a current session. The actual source of this value might be a
compiled-in default, the configuration file, command-line options, compiled-in default, the configuration file, command-line options,
or per-database or per-user default settings. See <xref or per-database or per-user default settings. This is subtly different
linkend="runtime-config"> for details. from defining it as <quote>the value that the parameter had at session
start</>, because if the value came from the configuration file, it
will be reset to whatever is specified by the configuration file now.
See <xref linkend="runtime-config"> for details.
</para> </para>
<para> <para>
See the <command>SET</> reference page for details on the The transactional behavior of <command>RESET</> is the same as
transaction behavior of <command>RESET</>. <command>SET</>: its effects will be undone by transaction rollback.
</para> </para>
</refsect1> </refsect1>
...@@ -62,8 +65,9 @@ SET <replaceable class="parameter">configuration_parameter</replaceable> TO DEFA ...@@ -62,8 +65,9 @@ SET <replaceable class="parameter">configuration_parameter</replaceable> TO DEFA
<term><replaceable class="PARAMETER">configuration_parameter</replaceable></term> <term><replaceable class="PARAMETER">configuration_parameter</replaceable></term>
<listitem> <listitem>
<para> <para>
The name of a run-time parameter. See <xref linkend="sql-set" Name of a settable run-time parameter. Available parameters are
endterm="sql-set-title"> for a list. documented in <xref linkend="runtime-config"> and on the
<xref linkend="sql-set" endterm="sql-set-title"> reference page.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
...@@ -83,9 +87,9 @@ SET <replaceable class="parameter">configuration_parameter</replaceable> TO DEFA ...@@ -83,9 +87,9 @@ SET <replaceable class="parameter">configuration_parameter</replaceable> TO DEFA
<title>Examples</title> <title>Examples</title>
<para> <para>
Set the <varname>geqo</> configuration variable to its default value: Set the <varname>timezone</> configuration variable to its default value:
<screen> <screen>
RESET geqo; RESET timezone;
</screen> </screen>
</para> </para>
</refsect1> </refsect1>
...@@ -97,4 +101,13 @@ RESET geqo; ...@@ -97,4 +101,13 @@ RESET geqo;
<command>RESET</command> is a <productname>PostgreSQL</productname> extension. <command>RESET</command> is a <productname>PostgreSQL</productname> extension.
</para> </para>
</refsect1> </refsect1>
<refsect1>
<title>See Also</title>
<simplelist type="inline">
<member><xref linkend="SQL-SET" endterm="SQL-SET-title"></member>
<member><xref linkend="SQL-SHOW" endterm="SQL-SHOW-title"></member>
</simplelist>
</refsect1>
</refentry> </refentry>
<!-- <!--
$PostgreSQL: pgsql/doc/src/sgml/ref/set.sgml,v 1.90 2006/09/22 16:20:00 tgl Exp $ $PostgreSQL: pgsql/doc/src/sgml/ref/set.sgml,v 1.91 2007/09/11 00:06:41 tgl Exp $
PostgreSQL documentation PostgreSQL documentation
--> -->
...@@ -40,13 +40,10 @@ SET [ SESSION | LOCAL ] TIME ZONE { <replaceable class="PARAMETER">timezone</rep ...@@ -40,13 +40,10 @@ SET [ SESSION | LOCAL ] TIME ZONE { <replaceable class="PARAMETER">timezone</rep
</para> </para>
<para> <para>
If <command>SET</command> or <command>SET SESSION</command> is issued If <command>SET</command> (or equivalently <command>SET SESSION</command>)
within a transaction that is later aborted, the effects of the is issued within a transaction that is later aborted, the effects of the
<command>SET</command> command disappear when the transaction is rolled <command>SET</command> command disappear when the transaction is rolled
back. (This behavior represents a change from back. Once the surrounding transaction is committed, the effects
<productname>PostgreSQL</productname> versions prior to 7.3, where
the effects of <command>SET</command> would not roll back after a later
error.) Once the surrounding transaction is committed, the effects
will persist until the end of the session, unless overridden by another will persist until the end of the session, unless overridden by another
<command>SET</command>. <command>SET</command>.
</para> </para>
...@@ -59,6 +56,36 @@ SET [ SESSION | LOCAL ] TIME ZONE { <replaceable class="PARAMETER">timezone</rep ...@@ -59,6 +56,36 @@ SET [ SESSION | LOCAL ] TIME ZONE { <replaceable class="PARAMETER">timezone</rep
seen until the end of the transaction, but afterwards (if the transaction seen until the end of the transaction, but afterwards (if the transaction
is committed) the <command>SET</command> value will take effect. is committed) the <command>SET</command> value will take effect.
</para> </para>
<para>
The effects of <command>SET</command> or <command>SET LOCAL</command> are
also canceled by rolling back to a savepoint that is earlier than the
command.
</para>
<para>
If <command>SET LOCAL</command> is used within a function that has a
<literal>SET</> option for the same variable (see
<xref linkend="sql-createfunction" endterm="sql-createfunction-title">),
the effects of the <command>SET LOCAL</command> command disappear at
function exit; that is, the value in effect when the function was called is
restored anyway. This allows <command>SET LOCAL</command> to be used for
dynamic or repeated changes of a parameter within a function, while still
having the convenience of using the <literal>SET</> option to save and
restore the caller's value. However, a regular <command>SET</> command
overrides any surrounding function's <literal>SET</> option; its effects
will persist unless rolled back.
</para>
<note>
<para>
In <productname>PostgreSQL</productname> versions 8.0 through 8.2,
the effects of a <command>SET LOCAL</command> would be canceled by
releasing an earlier savepoint, or by successful exit from a
<application>PL/pgSQL</application> exception block. This behavior
has been changed because it was deemed unintuitive.
</para>
</note>
</refsect1> </refsect1>
<refsect1> <refsect1>
...@@ -106,8 +133,11 @@ SET [ SESSION | LOCAL ] TIME ZONE { <replaceable class="PARAMETER">timezone</rep ...@@ -106,8 +133,11 @@ SET [ SESSION | LOCAL ] TIME ZONE { <replaceable class="PARAMETER">timezone</rep
<para> <para>
New value of parameter. Values can be specified as string New value of parameter. Values can be specified as string
constants, identifiers, numbers, or comma-separated lists of constants, identifiers, numbers, or comma-separated lists of
these. <literal>DEFAULT</literal> can be used to specify these, as appropriate for the particular parameter.
resetting the parameter to its default value. <literal>DEFAULT</literal> can be written to specify
resetting the parameter to its default value (that is, whatever
value it would have had if no <command>SET</> had been executed
in the current session).
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
...@@ -200,7 +230,9 @@ SELECT setseed(<replaceable>value</replaceable>); ...@@ -200,7 +230,9 @@ SELECT setseed(<replaceable>value</replaceable>);
<term><literal>DEFAULT</literal></term> <term><literal>DEFAULT</literal></term>
<listitem> <listitem>
<para> <para>
Set the time zone to your local time zone (the one that Set the time zone to your local time zone (that is, the
server's default value of <varname>timezone</>; if this
has not been explicitly set anywhere, it will be the zone that
the server's operating system defaults to). the server's operating system defaults to).
</para> </para>
</listitem> </listitem>
...@@ -221,7 +253,10 @@ SELECT setseed(<replaceable>value</replaceable>); ...@@ -221,7 +253,10 @@ SELECT setseed(<replaceable>value</replaceable>);
<para> <para>
The function <function>set_config</function> provides equivalent The function <function>set_config</function> provides equivalent
functionality. See <xref linkend="functions-admin">. functionality; see <xref linkend="functions-admin">.
Also, it is possible to UPDATE the
<link linkend="view-pg-settings"><structname>pg_settings</structname></link>
system view to perform the equivalent of <command>SET</>.
</para> </para>
</refsect1> </refsect1>
......
<!-- <!--
$PostgreSQL: pgsql/doc/src/sgml/ref/show.sgml,v 1.42 2006/09/16 00:30:20 momjian Exp $ $PostgreSQL: pgsql/doc/src/sgml/ref/show.sgml,v 1.43 2007/09/11 00:06:41 tgl Exp $
PostgreSQL documentation PostgreSQL documentation
--> -->
...@@ -36,7 +36,7 @@ SHOW ALL ...@@ -36,7 +36,7 @@ SHOW ALL
the <envar>PGOPTIONS</envar> environmental variable (when using the <envar>PGOPTIONS</envar> environmental variable (when using
<application>libpq</> or a <application>libpq</>-based <application>libpq</> or a <application>libpq</>-based
application), or through command-line flags when starting the application), or through command-line flags when starting the
<command>postgres</command>. See <xref <command>postgres</command> server. See <xref
linkend="runtime-config"> for details. linkend="runtime-config"> for details.
</para> </para>
</refsect1> </refsect1>
...@@ -130,7 +130,11 @@ SHOW ALL ...@@ -130,7 +130,11 @@ SHOW ALL
<para> <para>
The function <function>current_setting</function> produces The function <function>current_setting</function> produces
equivalent output. See <xref linkend="functions-admin">. equivalent output; see <xref linkend="functions-admin">.
Also, the
<link linkend="view-pg-settings"><structname>pg_settings</structname></link>
system view produces the same information.
</para> </para>
</refsect1> </refsect1>
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* *
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * 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) ...@@ -2749,7 +2749,7 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
snprintf(workmembuf, sizeof(workmembuf), "%d", maintenance_work_mem); snprintf(workmembuf, sizeof(workmembuf), "%d", maintenance_work_mem);
(void) set_config_option("work_mem", workmembuf, (void) set_config_option("work_mem", workmembuf,
PGC_USERSET, PGC_S_SESSION, PGC_USERSET, PGC_S_SESSION,
true, true); GUC_ACTION_LOCAL, true);
if (SPI_connect() != SPI_OK_CONNECT) if (SPI_connect() != SPI_OK_CONNECT)
elog(ERROR, "SPI_connect failed"); elog(ERROR, "SPI_connect failed");
...@@ -2832,13 +2832,12 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) ...@@ -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 * 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 * another SET LOCAL, so it won't affect the session value.
* tentative value if there is one.
*/ */
snprintf(workmembuf, sizeof(workmembuf), "%d", old_work_mem); snprintf(workmembuf, sizeof(workmembuf), "%d", old_work_mem);
(void) set_config_option("work_mem", workmembuf, (void) set_config_option("work_mem", workmembuf,
PGC_USERSET, PGC_S_SESSION, PGC_USERSET, PGC_S_SESSION,
true, true); GUC_ACTION_LOCAL, true);
return true; return true;
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -925,11 +925,10 @@ fmgr_security_definer(PG_FUNCTION_ARGS)
if (fcache->proconfig) if (fcache->proconfig)
{ {
/* The options are processed as if by SET LOCAL var = val */
ProcessGUCArray(fcache->proconfig, ProcessGUCArray(fcache->proconfig,
(superuser() ? PGC_SUSET : PGC_USERSET), (superuser() ? PGC_SUSET : PGC_USERSET),
PGC_S_SESSION, PGC_S_SESSION,
true); GUC_ACTION_SAVE);
} }
result = FunctionCallInvoke(fcinfo); result = FunctionCallInvoke(fcinfo);
...@@ -937,8 +936,7 @@ fmgr_security_definer(PG_FUNCTION_ARGS) ...@@ -937,8 +936,7 @@ fmgr_security_definer(PG_FUNCTION_ARGS)
PG_CATCH(); PG_CATCH();
{ {
fcinfo->flinfo = save_flinfo; fcinfo->flinfo = save_flinfo;
if (fcache->proconfig) /* We don't need to restore GUC settings, outer xact abort will */
AtEOXact_GUC(false, save_nestlevel);
if (OidIsValid(fcache->userid)) if (OidIsValid(fcache->userid))
SetUserId(save_userid); SetUserId(save_userid);
PG_RE_THROW(); PG_RE_THROW();
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -453,7 +453,7 @@ InitializeSessionUserId(const char *rolename)
* right to insert an option into pg_authid was checked when it was * right to insert an option into pg_authid was checked when it was
* inserted. * inserted.
*/ */
ProcessGUCArray(a, PGC_SUSET, PGC_S_USER, false); ProcessGUCArray(a, PGC_SUSET, PGC_S_USER, GUC_ACTION_SET);
} }
ReleaseSysCache(roleTup); ReleaseSysCache(roleTup);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -255,7 +255,7 @@ CheckMyDatabase(const char *name, bool am_superuser)
* right to insert an option into pg_database was checked when it * right to insert an option into pg_database was checked when it
* was inserted. * was inserted.
*/ */
ProcessGUCArray(a, PGC_SUSET, PGC_S_DATABASE, false); ProcessGUCArray(a, PGC_SUSET, PGC_S_DATABASE, GUC_ACTION_SET);
} }
} }
......
$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 GUC IMPLEMENTATION NOTES
...@@ -69,74 +69,133 @@ by SHOW. ...@@ -69,74 +69,133 @@ by SHOW.
SAVING/RESTORING GUC VARIABLE VALUES SAVING/RESTORING GUC VARIABLE VALUES
Prior values of configuration variables must be remembered in order to Prior values of configuration variables must be remembered in order to deal
deal with three special cases: RESET (a/k/a SET TO DEFAULT), rollback of with several special cases: RESET (a/k/a SET TO DEFAULT), rollback of SET
SET on transaction abort, and rollback of SET LOCAL at transaction end on transaction abort, rollback of SET LOCAL at transaction end (either
(either commit or abort). RESET is defined as selecting the value that commit or abort), and save/restore around a function that has a SET option.
would be effective had there never been any SET commands in the current RESET is defined as selecting the value that would be effective had there
session. never been any SET commands in the current session.
To handle these cases we must keep track of many distinct values for each To handle these cases we must keep track of many distinct values for each
variable. The primary values are: variable. The primary values are:
* actual variable contents always the current effective value * actual variable contents always the current effective value
* reset_value the value to use for RESET * reset_val the value to use for RESET
(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.)
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.)
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_val based on
whichever non-interactive source has the highest priority. They will
have the same value.
* tentative_value the uncommitted result of SET The possible transactional operations on a GUC value are:
The reason we need a tentative_value separate from the actual value is Entry to a function with a SET option:
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, for each level of transaction (possibly nested) we have to Push a stack entry with the prior variable value and state SAVE,
remember the transaction-entry-time actual and tentative values, in case then set the variable.
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.
During initialization we set the actual value and reset_value based on Plain SET command:
whichever non-interactive source has the highest priority. They will
have the same value. The tentative_value is not meaningful at this point. 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
A SET command starts by stacking the existing actual and tentative values Transaction or subtransaction commit (incl. successful function exit):
if this hasn't already been done within the current transaction. Then:
A SET LOCAL command sets the actual variable (and nothing else). At While stack entry level >= subxact depth
transaction end, the stacked values are used to restore the GUC entry
to its pre-transaction state.
A SET (or SET SESSION) command sets the actual variable, and if no error, if entry's state is SAVE:
then sets the tentative_value. If the transaction commits, the pop, restoring prior value
tentative_value is assigned again to the actual variable (which could by else if level is 1 and entry's state is SET+LOCAL:
now be different, if the SET was followed by SET LOCAL). If the pop, restoring *masked* value
transaction aborts, the stacked values are used to restore the GUC entry else if level is 1 and entry's state is SET:
to its pre-transaction state. 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
In the case of SET within nested subtransactions, at each commit the The merged entry will have level N-1 and prior = older prior, so easiest
tentative_value propagates out to the next transaction level. It will to keep older entry and free newer. There are 12 possibilities since
be thrown away at abort of any level, or after exiting the top transaction. we already handled level N state = SAVE:
RESET is executed like a SET, but using the reset_value as the desired new 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 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 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 If SIGHUP is received, the GUC code rereads the postgresql.conf
configuration file (this does not happen in the signal handler, but at 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 next return to main loop; note that it can be executed while within a
transaction). New values from postgresql.conf are assigned to actual 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 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.) 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, 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. 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 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 ...@@ -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 context anyway, and strdup gives us more control over handling
out-of-memory failures. out-of-memory failures.
We allow a string variable's actual value, reset_val, tentative_val, and We allow a string variable's actual value, reset_val, boot_val, and stacked
stacked copies of same to point at the same storage. This makes it values to point at the same storage. This makes it slightly harder to free
slightly harder to free space (must test whether a value to be freed isn't space (we must test whether a value to be freed isn't equal to any of the
equal to any of the other pointers in the GUC entry or associated stack other pointers in the GUC entry or associated stack items). The main
items). The main advantage is that we never need to strdup during advantage is that we never need to strdup during transaction commit/abort,
transaction commit/abort, so cannot cause an out-of-memory failure there. so cannot cause an out-of-memory failure there.
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
* *
* Copyright (c) 2000-2007, PostgreSQL Global Development Group * 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) ...@@ -231,7 +231,7 @@ ProcessConfigFile(GucContext context)
} }
if (!set_config_option(item->name, item->value, 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; goto cleanup_list;
} }
...@@ -264,24 +264,21 @@ ProcessConfigFile(GucContext context) ...@@ -264,24 +264,21 @@ ProcessConfigFile(GucContext context)
/* /*
* Reset any "file" sources to "default", else set_config_option * Reset any "file" sources to "default", else set_config_option
* will not override those settings. tentative_source should * will not override those settings.
* never be "file".
*/ */
if (gconf->reset_source == PGC_S_FILE) if (gconf->reset_source == PGC_S_FILE)
gconf->reset_source = PGC_S_DEFAULT; gconf->reset_source = PGC_S_DEFAULT;
Assert(gconf->tentative_source != PGC_S_FILE);
if (gconf->source == PGC_S_FILE) if (gconf->source == PGC_S_FILE)
gconf->source = PGC_S_DEFAULT; gconf->source = PGC_S_DEFAULT;
for (stack = gconf->stack; stack; stack = stack->prev) for (stack = gconf->stack; stack; stack = stack->prev)
{ {
Assert(stack->tentative_source != PGC_S_FILE);
if (stack->source == PGC_S_FILE) if (stack->source == PGC_S_FILE)
stack->source = PGC_S_DEFAULT; stack->source = PGC_S_DEFAULT;
} }
/* Now we can re-apply the wired-in default */ /* Now we can re-apply the wired-in default */
set_config_option(gconf->name, NULL, context, PGC_S_DEFAULT, set_config_option(gconf->name, NULL, context, PGC_S_DEFAULT,
false, true); GUC_ACTION_SET, true);
} }
/* /*
...@@ -289,25 +286,27 @@ ProcessConfigFile(GucContext context) ...@@ -289,25 +286,27 @@ ProcessConfigFile(GucContext context)
* is a no-op except in the case where one of these had been in the * 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 * 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. * 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()! * 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"); envvar = getenv("PGDATESTYLE");
if (envvar != NULL) if (envvar != NULL)
set_config_option("datestyle", envvar, PGC_POSTMASTER, set_config_option("datestyle", envvar, PGC_POSTMASTER,
PGC_S_ENV_VAR, false, true); PGC_S_ENV_VAR, GUC_ACTION_SET, true);
envvar = getenv("PGCLIENTENCODING"); envvar = getenv("PGCLIENTENCODING");
if (envvar != NULL) if (envvar != NULL)
set_config_option("client_encoding", envvar, PGC_POSTMASTER, 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. */ /* If we got here all the options checked out okay, so apply them. */
for (item = head; item; item = item->next) for (item = head; item; item = item->next)
{ {
set_config_option(item->name, item->value, context, set_config_option(item->name, item->value, context,
PGC_S_FILE, false, true); PGC_S_FILE, GUC_ACTION_SET, true);
} }
cleanup_list: cleanup_list:
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* Written by Peter Eisentraut <peter_e@gmx.net>. * Written by Peter Eisentraut <peter_e@gmx.net>.
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.419 2007/09/10 02:01:19 tgl Exp $ * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.420 2007/09/11 00:06:42 tgl Exp $
* *
*-------------------------------------------------------------------- *--------------------------------------------------------------------
*/ */
...@@ -422,9 +422,9 @@ const char *const config_type_names[] = ...@@ -422,9 +422,9 @@ const char *const config_type_names[] =
* 4. Add a record below. * 4. Add a record below.
* *
* 5. Add it to src/backend/utils/misc/postgresql.conf.sample, if * 5. Add it to src/backend/utils/misc/postgresql.conf.sample, if
* appropriate * appropriate.
* *
* 6. Don't forget to document the option. * 6. Don't forget to document the option (at least in config.sgml).
* *
* 7. If it's a new GUC_LIST option you must edit pg_dumpall.c to ensure * 7. If it's a new GUC_LIST option you must edit pg_dumpall.c to ensure
* it is not single quoted at dump time. * it is not single quoted at dump time.
...@@ -2499,7 +2499,7 @@ static int GUCNestLevel = 0; /* 1 when in main transaction */ ...@@ -2499,7 +2499,7 @@ static int GUCNestLevel = 0; /* 1 when in main transaction */
static int guc_var_compare(const void *a, const void *b); static int guc_var_compare(const void *a, const void *b);
static int guc_name_compare(const char *namea, const char *nameb); static int guc_name_compare(const char *namea, const char *nameb);
static void push_old_value(struct config_generic * gconf); static void push_old_value(struct config_generic * gconf, GucAction action);
static void ReportGUCOption(struct config_generic * record); static void ReportGUCOption(struct config_generic * record);
static void ShowGUCConfigOption(const char *name, DestReceiver *dest); static void ShowGUCConfigOption(const char *name, DestReceiver *dest);
static void ShowAllGUCConfig(DestReceiver *dest); static void ShowAllGUCConfig(DestReceiver *dest);
...@@ -2568,13 +2568,12 @@ set_string_field(struct config_string * conf, char **field, char *newval) ...@@ -2568,13 +2568,12 @@ set_string_field(struct config_string * conf, char **field, char *newval)
if (oldval == NULL || if (oldval == NULL ||
oldval == *(conf->variable) || oldval == *(conf->variable) ||
oldval == conf->reset_val || oldval == conf->reset_val ||
oldval == conf->tentative_val ||
oldval == conf->boot_val) oldval == conf->boot_val)
return; return;
for (stack = conf->gen.stack; stack; stack = stack->prev) for (stack = conf->gen.stack; stack; stack = stack->prev)
{ {
if (oldval == stack->tentative_val.stringval || if (oldval == stack->prior.stringval ||
oldval == stack->value.stringval) oldval == stack->masked.stringval)
return; return;
} }
...@@ -2592,19 +2591,71 @@ string_field_used(struct config_string * conf, char *strval) ...@@ -2592,19 +2591,71 @@ string_field_used(struct config_string * conf, char *strval)
if (strval == *(conf->variable) || if (strval == *(conf->variable) ||
strval == conf->reset_val || strval == conf->reset_val ||
strval == conf->tentative_val ||
strval == conf->boot_val) strval == conf->boot_val)
return true; return true;
for (stack = conf->gen.stack; stack; stack = stack->prev) for (stack = conf->gen.stack; stack; stack = stack->prev)
{ {
if (strval == stack->tentative_val.stringval || if (strval == stack->prior.stringval ||
strval == stack->value.stringval) strval == stack->masked.stringval)
return true; return true;
} }
return false; return false;
} }
/*
* Support for copying a variable's active value into a stack entry
*/
static void
set_stack_value(struct config_generic * gconf, union config_var_value * val)
{
switch (gconf->vartype)
{
case PGC_BOOL:
val->boolval =
*((struct config_bool *) gconf)->variable;
break;
case PGC_INT:
val->intval =
*((struct config_int *) gconf)->variable;
break;
case PGC_REAL:
val->realval =
*((struct config_real *) gconf)->variable;
break;
case PGC_STRING:
/* we assume stringval is NULL if not valid */
set_string_field((struct config_string *) gconf,
&(val->stringval),
*((struct config_string *) gconf)->variable);
break;
}
}
/*
* Support for discarding a no-longer-needed value in a stack entry
*/
static void
discard_stack_value(struct config_generic *gconf, union config_var_value *val)
{
switch (gconf->vartype)
{
case PGC_BOOL:
case PGC_INT:
case PGC_REAL:
/* no need to do anything */
break;
case PGC_STRING:
set_string_field((struct config_string *) gconf,
&(val->stringval),
NULL);
break;
}
}
/*
* Fetch the sorted array pointer (exported for help_config.c's use ONLY)
*/
struct config_generic ** struct config_generic **
get_guc_variables(void) get_guc_variables(void)
{ {
...@@ -2878,7 +2929,9 @@ guc_var_compare(const void *a, const void *b) ...@@ -2878,7 +2929,9 @@ guc_var_compare(const void *a, const void *b)
return guc_name_compare(confa->name, confb->name); return guc_name_compare(confa->name, confb->name);
} }
/*
* the bare comparison function for GUC names
*/
static int static int
guc_name_compare(const char *namea, const char *nameb) guc_name_compare(const char *namea, const char *nameb)
{ {
...@@ -2941,7 +2994,6 @@ InitializeGUCOptions(void) ...@@ -2941,7 +2994,6 @@ InitializeGUCOptions(void)
gconf->status = 0; gconf->status = 0;
gconf->reset_source = PGC_S_DEFAULT; gconf->reset_source = PGC_S_DEFAULT;
gconf->tentative_source = PGC_S_DEFAULT;
gconf->source = PGC_S_DEFAULT; gconf->source = PGC_S_DEFAULT;
gconf->stack = NULL; gconf->stack = NULL;
...@@ -2994,7 +3046,6 @@ InitializeGUCOptions(void) ...@@ -2994,7 +3046,6 @@ InitializeGUCOptions(void)
*conf->variable = NULL; *conf->variable = NULL;
conf->reset_val = NULL; conf->reset_val = NULL;
conf->tentative_val = NULL;
if (conf->boot_val == NULL) if (conf->boot_val == NULL)
{ {
...@@ -3260,7 +3311,7 @@ ResetAllOptions(void) ...@@ -3260,7 +3311,7 @@ ResetAllOptions(void)
continue; continue;
/* Save old value to support transaction abort */ /* Save old value to support transaction abort */
push_old_value(gconf); push_old_value(gconf, GUC_ACTION_SET);
switch (gconf->vartype) switch (gconf->vartype)
{ {
...@@ -3273,11 +3324,7 @@ ResetAllOptions(void) ...@@ -3273,11 +3324,7 @@ ResetAllOptions(void)
PGC_S_SESSION)) PGC_S_SESSION))
elog(ERROR, "failed to reset %s", conf->gen.name); elog(ERROR, "failed to reset %s", conf->gen.name);
*conf->variable = conf->reset_val; *conf->variable = conf->reset_val;
conf->tentative_val = conf->reset_val;
conf->gen.source = conf->gen.reset_source; conf->gen.source = conf->gen.reset_source;
conf->gen.tentative_source = conf->gen.reset_source;
conf->gen.status |= GUC_HAVE_TENTATIVE;
guc_dirty = true;
break; break;
} }
case PGC_INT: case PGC_INT:
...@@ -3289,11 +3336,7 @@ ResetAllOptions(void) ...@@ -3289,11 +3336,7 @@ ResetAllOptions(void)
PGC_S_SESSION)) PGC_S_SESSION))
elog(ERROR, "failed to reset %s", conf->gen.name); elog(ERROR, "failed to reset %s", conf->gen.name);
*conf->variable = conf->reset_val; *conf->variable = conf->reset_val;
conf->tentative_val = conf->reset_val;
conf->gen.source = conf->gen.reset_source; conf->gen.source = conf->gen.reset_source;
conf->gen.tentative_source = conf->gen.reset_source;
conf->gen.status |= GUC_HAVE_TENTATIVE;
guc_dirty = true;
break; break;
} }
case PGC_REAL: case PGC_REAL:
...@@ -3305,11 +3348,7 @@ ResetAllOptions(void) ...@@ -3305,11 +3348,7 @@ ResetAllOptions(void)
PGC_S_SESSION)) PGC_S_SESSION))
elog(ERROR, "failed to reset %s", conf->gen.name); elog(ERROR, "failed to reset %s", conf->gen.name);
*conf->variable = conf->reset_val; *conf->variable = conf->reset_val;
conf->tentative_val = conf->reset_val;
conf->gen.source = conf->gen.reset_source; conf->gen.source = conf->gen.reset_source;
conf->gen.tentative_source = conf->gen.reset_source;
conf->gen.status |= GUC_HAVE_TENTATIVE;
guc_dirty = true;
break; break;
} }
case PGC_STRING: case PGC_STRING:
...@@ -3338,11 +3377,7 @@ ResetAllOptions(void) ...@@ -3338,11 +3377,7 @@ ResetAllOptions(void)
} }
set_string_field(conf, conf->variable, 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.source = conf->gen.reset_source;
conf->gen.tentative_source = conf->gen.reset_source;
conf->gen.status |= GUC_HAVE_TENTATIVE;
guc_dirty = true;
break; break;
} }
} }
...@@ -3355,84 +3390,84 @@ ResetAllOptions(void) ...@@ -3355,84 +3390,84 @@ ResetAllOptions(void)
/* /*
* push_old_value * push_old_value
* Push previous state during first assignment to a GUC variable * Push previous state during transactional 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 static void
push_old_value(struct config_generic * gconf) push_old_value(struct config_generic * gconf, GucAction action)
{ {
GucStack *stack; GucStack *stack;
/* If we're not inside a transaction, do nothing */ /* If we're not inside a nest level, do nothing */
if (GUCNestLevel == 0) if (GUCNestLevel == 0)
return; return;
for (;;) /* Do we already have a stack entry of the current nest level? */
stack = gconf->stack;
if (stack && stack->nest_level >= GUCNestLevel)
{
/* Yes, so adjust its state if necessary */
Assert(stack->nest_level == GUCNestLevel);
switch (action)
{ {
/* Done if we already pushed it at this nesting depth */ case GUC_ACTION_SET:
if (gconf->stack && gconf->stack->nest_level >= GUCNestLevel) /* SET overrides any prior action at same nest level */
if (stack->state == GUC_SET_LOCAL)
{
/* must discard old masked value */
discard_stack_value(gconf, &stack->masked);
}
stack->state = GUC_SET;
break;
case GUC_ACTION_LOCAL:
if (stack->state == GUC_SET)
{
/* SET followed by SET LOCAL, remember SET's value */
set_stack_value(gconf, &stack->masked);
stack->state = GUC_SET_LOCAL;
}
/* in all other cases, no change to stack entry */
break;
case GUC_ACTION_SAVE:
/* Could only have a prior SAVE of same variable */
Assert(stack->state == GUC_SAVE);
break;
}
Assert(guc_dirty); /* must be set already */
return; return;
}
/* /*
* We keep all the stack entries in TopTransactionContext so as to * Push a new stack entry
* avoid allocation problems when a subtransaction back-fills stack *
* entries for upper transaction levels. * We keep all the stack entries in TopTransactionContext for simplicity.
*/ */
stack = (GucStack *) MemoryContextAlloc(TopTransactionContext, stack = (GucStack *) MemoryContextAllocZero(TopTransactionContext,
sizeof(GucStack)); sizeof(GucStack));
stack->prev = gconf->stack; stack->prev = gconf->stack;
stack->nest_level = stack->prev ? stack->prev->nest_level + 1 : 1; stack->nest_level = GUCNestLevel;
stack->status = gconf->status; switch (action)
stack->tentative_source = gconf->tentative_source;
stack->source = gconf->source;
switch (gconf->vartype)
{ {
case PGC_BOOL: case GUC_ACTION_SET:
stack->tentative_val.boolval = stack->state = GUC_SET;
((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; break;
case GUC_ACTION_LOCAL:
case PGC_REAL: stack->state = GUC_LOCAL;
stack->tentative_val.realval =
((struct config_real *) gconf)->tentative_val;
stack->value.realval =
*((struct config_real *) gconf)->variable;
break; break;
case GUC_ACTION_SAVE:
case PGC_STRING: stack->state = GUC_SAVE;
stack->tentative_val.stringval =
((struct config_string *) gconf)->tentative_val;
stack->value.stringval =
*((struct config_string *) gconf)->variable;
break; break;
} }
stack->source = gconf->source;
set_stack_value(gconf, &stack->prior);
gconf->stack = stack; 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 */ /* Ensure we remember to pop at end of xact */
guc_dirty = true; guc_dirty = true;
}
} }
/* /*
* Do GUC processing at main transaction start. * Do GUC processing at main transaction start.
*/ */
...@@ -3471,6 +3506,7 @@ NewGUCNestLevel(void) ...@@ -3471,6 +3506,7 @@ NewGUCNestLevel(void)
void void
AtEOXact_GUC(bool isCommit, int nestLevel) AtEOXact_GUC(bool isCommit, int nestLevel)
{ {
bool still_dirty;
int i; int i;
Assert(nestLevel > 0 && nestLevel <= GUCNestLevel); Assert(nestLevel > 0 && nestLevel <= GUCNestLevel);
...@@ -3482,79 +3518,128 @@ AtEOXact_GUC(bool isCommit, int nestLevel) ...@@ -3482,79 +3518,128 @@ AtEOXact_GUC(bool isCommit, int nestLevel)
return; return;
} }
still_dirty = false;
for (i = 0; i < num_guc_variables; i++) for (i = 0; i < num_guc_variables; i++)
{ {
struct config_generic *gconf = guc_variables[i]; struct config_generic *gconf = guc_variables[i];
int my_status = gconf->status; GucStack *stack;
GucStack *stack = gconf->stack;
bool useTentative; /*
* Process and pop each stack entry within the nest level. To
* simplify fmgr_security_definer(), we allow failure exit from
* a function-with-SET-options to be recovered at the surrounding
* transaction or subtransaction abort; so there could be more than
* one stack entry to pop.
*/
while ((stack = gconf->stack) != NULL &&
stack->nest_level >= nestLevel)
{
GucStack *prev = stack->prev;
bool restorePrior = false;
bool restoreMasked = false;
bool changed; bool changed;
/* /*
* Skip if nothing's happened to this var in this transaction * In this next bit, if we don't set either restorePrior or
* restoreMasked, we must "discard" any unwanted fields of the
* stack entries to avoid leaking memory. If we do set one of
* those flags, unused fields will be cleaned up after restoring.
*/ */
if ((my_status & (GUC_HAVE_TENTATIVE | if (!isCommit) /* if abort, always restore prior value */
GUC_HAVE_LOCAL | restorePrior = true;
GUC_HAVE_STACK)) == 0) else if (stack->state == GUC_SAVE)
restorePrior = true;
else if (stack->nest_level == 1)
{ {
Assert(stack == NULL); /* transaction commit */
continue; if (stack->state == GUC_SET_LOCAL)
restoreMasked = true;
else if (stack->state == GUC_SET)
{
/* we keep the current active value */
discard_stack_value(gconf, &stack->prior);
} }
/* Assert that we stacked old value before changing it */ else /* must be GUC_LOCAL */
Assert(stack != NULL && (my_status & GUC_HAVE_STACK)); restorePrior = true;
/* However, the last change may have been at an outer xact level */ }
if (stack->nest_level < nestLevel) else if (prev == NULL ||
prev->nest_level < stack->nest_level - 1)
{
/* decrement entry's level and do not pop it */
stack->nest_level--;
continue; continue;
Assert(stack->nest_level == nestLevel); }
else
{
/* /*
* We will pop the stack entry. Start by restoring outer xact status * We have to merge this stack entry into prev.
* (since we may want to modify it below). Be careful to use * See README for discussion of this bit.
* my_status to reference the inner xact status below this point...
*/ */
gconf->status = stack->status; switch (stack->state)
{
case GUC_SAVE:
Assert(false); /* can't get here */
/* case GUC_SET:
* We have two cases: /* next level always becomes SET */
* discard_stack_value(gconf, &stack->prior);
* If commit and HAVE_TENTATIVE, set actual value to tentative (this if (prev->state == GUC_SET_LOCAL)
* is to override a SET LOCAL if one occurred later than SET). We keep discard_stack_value(gconf, &prev->masked);
* the tentative value and propagate HAVE_TENTATIVE to the parent prev->state = GUC_SET;
* status, allowing the SET's effect to percolate up. (But if we're break;
* exiting the outermost transaction, we'll drop the HAVE_TENTATIVE
* bit below.) case GUC_LOCAL:
* if (prev->state == GUC_SET)
* 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 /* LOCAL migrates down */
* effect, so restore both tentative and actual values from the stack prev->masked = stack->prior;
* entry. prev->state = GUC_SET_LOCAL;
*/ }
else
{
/* else just forget this stack level */
discard_stack_value(gconf, &stack->prior);
}
break;
case GUC_SET_LOCAL:
/* prior state at this level no longer wanted */
discard_stack_value(gconf, &stack->prior);
/* copy down the masked state */
if (prev->state == GUC_SET_LOCAL)
discard_stack_value(gconf, &prev->masked);
prev->masked = stack->masked;
prev->state = GUC_SET_LOCAL;
break;
}
}
useTentative = isCommit && (my_status & GUC_HAVE_TENTATIVE) != 0;
changed = false; changed = false;
switch (gconf->vartype) if (restorePrior || restoreMasked)
{
case PGC_BOOL:
{ {
struct config_bool *conf = (struct config_bool *) gconf; /* Perform appropriate restoration of the stacked value */
bool newval; union config_var_value newvalue;
GucSource newsource; GucSource newsource;
if (useTentative) if (restoreMasked)
{ {
newval = conf->tentative_val; newvalue = stack->masked;
newsource = conf->gen.tentative_source; newsource = PGC_S_SESSION;
conf->gen.status |= GUC_HAVE_TENTATIVE;
} }
else else
{ {
newval = stack->value.boolval; newvalue = stack->prior;
newsource = stack->source; newsource = stack->source;
conf->tentative_val = stack->tentative_val.boolval;
conf->gen.tentative_source = stack->tentative_source;
} }
switch (gconf->vartype)
{
case PGC_BOOL:
{
struct config_bool *conf = (struct config_bool *) gconf;
bool newval = newvalue.boolval;
if (*conf->variable != newval) if (*conf->variable != newval)
{ {
if (conf->assign_hook) if (conf->assign_hook)
...@@ -3565,28 +3650,12 @@ AtEOXact_GUC(bool isCommit, int nestLevel) ...@@ -3565,28 +3650,12 @@ AtEOXact_GUC(bool isCommit, int nestLevel)
*conf->variable = newval; *conf->variable = newval;
changed = true; changed = true;
} }
conf->gen.source = newsource;
break; break;
} }
case PGC_INT: case PGC_INT:
{ {
struct config_int *conf = (struct config_int *) gconf; struct config_int *conf = (struct config_int *) gconf;
int newval; int newval = newvalue.intval;
GucSource newsource;
if (useTentative)
{
newval = conf->tentative_val;
newsource = conf->gen.tentative_source;
conf->gen.status |= GUC_HAVE_TENTATIVE;
}
else
{
newval = stack->value.intval;
newsource = stack->source;
conf->tentative_val = stack->tentative_val.intval;
conf->gen.tentative_source = stack->tentative_source;
}
if (*conf->variable != newval) if (*conf->variable != newval)
{ {
...@@ -3598,28 +3667,12 @@ AtEOXact_GUC(bool isCommit, int nestLevel) ...@@ -3598,28 +3667,12 @@ AtEOXact_GUC(bool isCommit, int nestLevel)
*conf->variable = newval; *conf->variable = newval;
changed = true; changed = true;
} }
conf->gen.source = newsource;
break; break;
} }
case PGC_REAL: case PGC_REAL:
{ {
struct config_real *conf = (struct config_real *) gconf; struct config_real *conf = (struct config_real *) gconf;
double newval; double newval = newvalue.realval;
GucSource newsource;
if (useTentative)
{
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 != newval) if (*conf->variable != newval)
{ {
...@@ -3631,29 +3684,12 @@ AtEOXact_GUC(bool isCommit, int nestLevel) ...@@ -3631,29 +3684,12 @@ AtEOXact_GUC(bool isCommit, int nestLevel)
*conf->variable = newval; *conf->variable = newval;
changed = true; changed = true;
} }
conf->gen.source = newsource;
break; break;
} }
case PGC_STRING: case PGC_STRING:
{ {
struct config_string *conf = (struct config_string *) gconf; struct config_string *conf = (struct config_string *) gconf;
char *newval; char *newval = newvalue.stringval;
GucSource newsource;
if (useTentative)
{
newval = conf->tentative_val;
newsource = conf->gen.tentative_source;
conf->gen.status |= GUC_HAVE_TENTATIVE;
}
else
{
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->variable != newval)
{ {
...@@ -3682,46 +3718,36 @@ AtEOXact_GUC(bool isCommit, int nestLevel) ...@@ -3682,46 +3718,36 @@ AtEOXact_GUC(bool isCommit, int nestLevel)
set_string_field(conf, conf->variable, newval); set_string_field(conf, conf->variable, newval);
changed = true; changed = true;
} }
conf->gen.source = newsource; /*
/* Release stacked values if not used anymore */ * Release stacked values if not used anymore.
set_string_field(conf, &stack->value.stringval, * We could use discard_stack_value() here, but since
NULL); * we have type-specific code anyway, might as well
set_string_field(conf, &stack->tentative_val.stringval, * inline it.
NULL); */
/* Don't store tentative value separately after commit */ set_string_field(conf, &stack->prior.stringval, NULL);
if (nestLevel == 1) set_string_field(conf, &stack->masked.stringval, NULL);
set_string_field(conf, &conf->tentative_val, NULL);
break; break;
} }
} }
gconf->source = newsource;
}
/* Finish popping the state stack */ /* Finish popping the state stack */
gconf->stack = stack->prev; gconf->stack = prev;
pfree(stack); pfree(stack);
/*
* If we're now out of all xact levels, forget TENTATIVE status bit;
* there's nothing tentative about the value anymore.
*/
if (nestLevel == 1)
{
Assert(gconf->stack == NULL);
gconf->status = 0;
}
/* Report new value if we changed it */ /* Report new value if we changed it */
if (changed && (gconf->flags & GUC_REPORT)) if (changed && (gconf->flags & GUC_REPORT))
ReportGUCOption(gconf); ReportGUCOption(gconf);
} /* end of stack-popping loop */
if (stack != NULL)
still_dirty = true;
} }
/* /* If there are no remaining stack entries, we can reset guc_dirty */
* If we're now out of all xact levels, we can clear guc_dirty. (Note: we guc_dirty = still_dirty;
* cannot reset guc_dirty when exiting a subtransaction, because we know
* that all outer transaction levels will have stacked values to deal
* with.)
*/
if (nestLevel == 1)
guc_dirty = false;
/* Update nesting level */ /* Update nesting level */
GUCNestLevel = nestLevel - 1; GUCNestLevel = nestLevel - 1;
...@@ -4123,8 +4149,13 @@ call_string_assign_hook(GucStringAssignHook assign_hook, ...@@ -4123,8 +4149,13 @@ call_string_assign_hook(GucStringAssignHook assign_hook,
* function is being called so it can apply the access restrictions * function is being called so it can apply the access restrictions
* properly. * properly.
* *
* If value is NULL, set the option to its default value. If the * If value is NULL, set the option to its default value (normally the
* parameter changeVal is false then don't really set the option but do all * reset_val, but if source == PGC_S_DEFAULT we instead use the boot_val).
*
* action indicates whether to set the value globally in the session, locally
* to the current top transaction, or just for the duration of a function call.
*
* If changeVal is false then don't really set the option but do all
* the checks to see if it would work. * the checks to see if it would work.
* *
* If there is an error (non-existing option, invalid value) then an * If there is an error (non-existing option, invalid value) then an
...@@ -4141,7 +4172,7 @@ call_string_assign_hook(GucStringAssignHook assign_hook, ...@@ -4141,7 +4172,7 @@ call_string_assign_hook(GucStringAssignHook assign_hook,
bool bool
set_config_option(const char *name, const char *value, set_config_option(const char *name, const char *value,
GucContext context, GucSource source, GucContext context, GucSource source,
bool isLocal, bool changeVal) GucAction action, bool changeVal)
{ {
struct config_generic *record; struct config_generic *record;
int elevel; int elevel;
...@@ -4306,9 +4337,6 @@ set_config_option(const char *name, const char *value, ...@@ -4306,9 +4337,6 @@ set_config_option(const char *name, const char *value,
/* /*
* Evaluate value and set variable. * Evaluate value and set variable.
*
* Note: if value == NULL then we are supposed to set to the reset_val,
* except when source == PGC_S_DEFAULT; then we set to the boot_val.
*/ */
switch (record->vartype) switch (record->vartype)
{ {
...@@ -4350,7 +4378,7 @@ set_config_option(const char *name, const char *value, ...@@ -4350,7 +4378,7 @@ set_config_option(const char *name, const char *value,
{ {
/* Save old value to support transaction abort */ /* Save old value to support transaction abort */
if (!makeDefault) if (!makeDefault)
push_old_value(&conf->gen); push_old_value(&conf->gen, action);
if (changeVal) if (changeVal)
{ {
*conf->variable = newval; *conf->variable = newval;
...@@ -4369,23 +4397,11 @@ set_config_option(const char *name, const char *value, ...@@ -4369,23 +4397,11 @@ set_config_option(const char *name, const char *value,
{ {
if (stack->source <= source) if (stack->source <= source)
{ {
stack->value.boolval = newval; stack->prior.boolval = newval;
stack->source = source; stack->source = source;
} }
} }
} }
else if (isLocal)
{
conf->gen.status |= GUC_HAVE_LOCAL;
guc_dirty = true;
}
else
{
conf->tentative_val = newval;
conf->gen.tentative_source = source;
conf->gen.status |= GUC_HAVE_TENTATIVE;
guc_dirty = true;
}
} }
break; break;
} }
...@@ -4439,7 +4455,7 @@ set_config_option(const char *name, const char *value, ...@@ -4439,7 +4455,7 @@ set_config_option(const char *name, const char *value,
{ {
/* Save old value to support transaction abort */ /* Save old value to support transaction abort */
if (!makeDefault) if (!makeDefault)
push_old_value(&conf->gen); push_old_value(&conf->gen, action);
if (changeVal) if (changeVal)
{ {
*conf->variable = newval; *conf->variable = newval;
...@@ -4458,23 +4474,11 @@ set_config_option(const char *name, const char *value, ...@@ -4458,23 +4474,11 @@ set_config_option(const char *name, const char *value,
{ {
if (stack->source <= source) if (stack->source <= source)
{ {
stack->value.intval = newval; stack->prior.intval = newval;
stack->source = source; stack->source = source;
} }
} }
} }
else if (isLocal)
{
conf->gen.status |= GUC_HAVE_LOCAL;
guc_dirty = true;
}
else
{
conf->tentative_val = newval;
conf->gen.tentative_source = source;
conf->gen.status |= GUC_HAVE_TENTATIVE;
guc_dirty = true;
}
} }
break; break;
} }
...@@ -4525,7 +4529,7 @@ set_config_option(const char *name, const char *value, ...@@ -4525,7 +4529,7 @@ set_config_option(const char *name, const char *value,
{ {
/* Save old value to support transaction abort */ /* Save old value to support transaction abort */
if (!makeDefault) if (!makeDefault)
push_old_value(&conf->gen); push_old_value(&conf->gen, action);
if (changeVal) if (changeVal)
{ {
*conf->variable = newval; *conf->variable = newval;
...@@ -4544,23 +4548,11 @@ set_config_option(const char *name, const char *value, ...@@ -4544,23 +4548,11 @@ set_config_option(const char *name, const char *value,
{ {
if (stack->source <= source) if (stack->source <= source)
{ {
stack->value.realval = newval; stack->prior.realval = newval;
stack->source = source; stack->source = source;
} }
} }
} }
else if (isLocal)
{
conf->gen.status |= GUC_HAVE_LOCAL;
guc_dirty = true;
}
else
{
conf->tentative_val = newval;
conf->gen.tentative_source = source;
conf->gen.status |= GUC_HAVE_TENTATIVE;
guc_dirty = true;
}
} }
break; break;
} }
...@@ -4653,7 +4645,7 @@ set_config_option(const char *name, const char *value, ...@@ -4653,7 +4645,7 @@ set_config_option(const char *name, const char *value,
{ {
/* Save old value to support transaction abort */ /* Save old value to support transaction abort */
if (!makeDefault) if (!makeDefault)
push_old_value(&conf->gen); push_old_value(&conf->gen, action);
if (changeVal) if (changeVal)
{ {
set_string_field(conf, conf->variable, newval); set_string_field(conf, conf->variable, newval);
...@@ -4672,7 +4664,7 @@ set_config_option(const char *name, const char *value, ...@@ -4672,7 +4664,7 @@ set_config_option(const char *name, const char *value,
{ {
if (stack->source <= source) if (stack->source <= source)
{ {
set_string_field(conf, &stack->value.stringval, set_string_field(conf, &stack->prior.stringval,
newval); newval);
stack->source = source; stack->source = source;
} }
...@@ -4681,18 +4673,6 @@ set_config_option(const char *name, const char *value, ...@@ -4681,18 +4673,6 @@ set_config_option(const char *name, const char *value,
if (newval && !string_field_used(conf, newval)) if (newval && !string_field_used(conf, newval))
free(newval); free(newval);
} }
else if (isLocal)
{
conf->gen.status |= GUC_HAVE_LOCAL;
guc_dirty = true;
}
else
{
set_string_field(conf, &conf->tentative_val, newval);
conf->gen.tentative_source = source;
conf->gen.status |= GUC_HAVE_TENTATIVE;
guc_dirty = true;
}
} }
else if (newval) else if (newval)
free(newval); free(newval);
...@@ -4716,7 +4696,8 @@ void ...@@ -4716,7 +4696,8 @@ void
SetConfigOption(const char *name, const char *value, SetConfigOption(const char *name, const char *value,
GucContext context, GucSource source) GucContext context, GucSource source)
{ {
(void) set_config_option(name, value, context, source, false, true); (void) set_config_option(name, value, context, source,
GUC_ACTION_SET, true);
} }
...@@ -4942,6 +4923,8 @@ flatten_set_variable_args(const char *name, List *args) ...@@ -4942,6 +4923,8 @@ flatten_set_variable_args(const char *name, List *args)
void void
ExecSetVariableStmt(VariableSetStmt *stmt) ExecSetVariableStmt(VariableSetStmt *stmt)
{ {
GucAction action = stmt->is_local ? GUC_ACTION_LOCAL : GUC_ACTION_SET;
switch (stmt->kind) switch (stmt->kind)
{ {
case VAR_SET_VALUE: case VAR_SET_VALUE:
...@@ -4950,7 +4933,7 @@ ExecSetVariableStmt(VariableSetStmt *stmt) ...@@ -4950,7 +4933,7 @@ ExecSetVariableStmt(VariableSetStmt *stmt)
ExtractSetVariableArgs(stmt), ExtractSetVariableArgs(stmt),
(superuser() ? PGC_SUSET : PGC_USERSET), (superuser() ? PGC_SUSET : PGC_USERSET),
PGC_S_SESSION, PGC_S_SESSION,
stmt->is_local, action,
true); true);
break; break;
case VAR_SET_MULTI: case VAR_SET_MULTI:
...@@ -5006,7 +4989,7 @@ ExecSetVariableStmt(VariableSetStmt *stmt) ...@@ -5006,7 +4989,7 @@ ExecSetVariableStmt(VariableSetStmt *stmt)
NULL, NULL,
(superuser() ? PGC_SUSET : PGC_USERSET), (superuser() ? PGC_SUSET : PGC_USERSET),
PGC_S_SESSION, PGC_S_SESSION,
stmt->is_local, action,
true); true);
break; break;
case VAR_RESET_ALL: case VAR_RESET_ALL:
...@@ -5051,7 +5034,7 @@ SetPGVariable(const char *name, List *args, bool is_local) ...@@ -5051,7 +5034,7 @@ SetPGVariable(const char *name, List *args, bool is_local)
argstring, argstring,
(superuser() ? PGC_SUSET : PGC_USERSET), (superuser() ? PGC_SUSET : PGC_USERSET),
PGC_S_SESSION, PGC_S_SESSION,
is_local, is_local ? GUC_ACTION_LOCAL : GUC_ACTION_SET,
true); true);
} }
...@@ -5095,7 +5078,7 @@ set_config_by_name(PG_FUNCTION_ARGS) ...@@ -5095,7 +5078,7 @@ set_config_by_name(PG_FUNCTION_ARGS)
value, value,
(superuser() ? PGC_SUSET : PGC_USERSET), (superuser() ? PGC_SUSET : PGC_USERSET),
PGC_S_SESSION, PGC_S_SESSION,
is_local, is_local ? GUC_ACTION_LOCAL : GUC_ACTION_SET,
true); true);
/* get the new current value */ /* get the new current value */
...@@ -5190,7 +5173,7 @@ define_custom_variable(struct config_generic *variable) ...@@ -5190,7 +5173,7 @@ define_custom_variable(struct config_generic *variable)
if (value) if (value)
set_config_option(name, value, set_config_option(name, value,
pHolder->gen.context, pHolder->gen.source, pHolder->gen.context, pHolder->gen.source,
false, true); GUC_ACTION_SET, true);
/* /*
* Free up as much as we conveniently can of the placeholder structure * Free up as much as we conveniently can of the placeholder structure
...@@ -5198,7 +5181,6 @@ define_custom_variable(struct config_generic *variable) ...@@ -5198,7 +5181,6 @@ define_custom_variable(struct config_generic *variable)
*/ */
set_string_field(pHolder, pHolder->variable, NULL); set_string_field(pHolder, pHolder->variable, NULL);
set_string_field(pHolder, &pHolder->reset_val, NULL); set_string_field(pHolder, &pHolder->reset_val, NULL);
set_string_field(pHolder, &pHolder->tentative_val, NULL);
free(pHolder); free(pHolder);
} }
...@@ -6145,7 +6127,7 @@ read_nondefault_variables(void) ...@@ -6145,7 +6127,7 @@ read_nondefault_variables(void)
elog(FATAL, "invalid format of exec config params file"); elog(FATAL, "invalid format of exec config params file");
(void) set_config_option(varname, varvalue, record->context, (void) set_config_option(varname, varvalue, record->context,
varsource, false, true); varsource, GUC_ACTION_SET, true);
free(varname); free(varname);
free(varvalue); free(varvalue);
} }
...@@ -6196,13 +6178,13 @@ ParseLongOption(const char *string, char **name, char **value) ...@@ -6196,13 +6178,13 @@ ParseLongOption(const char *string, char **name, char **value)
/* /*
* Handle options fetched from pg_database.datconfig, pg_authid.rolconfig, * Handle options fetched from pg_database.datconfig, pg_authid.rolconfig,
* pg_proc.proconfig, etc. Caller must specify proper context/source/local. * pg_proc.proconfig, etc. Caller must specify proper context/source/action.
* *
* The array parameter must be an array of TEXT (it must not be NULL). * The array parameter must be an array of TEXT (it must not be NULL).
*/ */
void void
ProcessGUCArray(ArrayType *array, ProcessGUCArray(ArrayType *array,
GucContext context, GucSource source, bool isLocal) GucContext context, GucSource source, GucAction action)
{ {
int i; int i;
...@@ -6242,7 +6224,7 @@ ProcessGUCArray(ArrayType *array, ...@@ -6242,7 +6224,7 @@ ProcessGUCArray(ArrayType *array,
continue; continue;
} }
(void) set_config_option(name, value, context, source, isLocal, true); (void) set_config_option(name, value, context, source, action, true);
free(name); free(name);
if (value) if (value)
...@@ -6269,7 +6251,7 @@ GUCArrayAdd(ArrayType *array, const char *name, const char *value) ...@@ -6269,7 +6251,7 @@ GUCArrayAdd(ArrayType *array, const char *name, const char *value)
/* test if the option is valid */ /* test if the option is valid */
set_config_option(name, value, set_config_option(name, value,
superuser() ? PGC_SUSET : PGC_USERSET, superuser() ? PGC_SUSET : PGC_USERSET,
PGC_S_TEST, false, false); PGC_S_TEST, GUC_ACTION_SET, false);
/* convert name to canonical spelling, so we can use plain strcmp */ /* convert name to canonical spelling, so we can use plain strcmp */
(void) GetConfigOptionByName(name, &varname); (void) GetConfigOptionByName(name, &varname);
...@@ -6347,7 +6329,7 @@ GUCArrayDelete(ArrayType *array, const char *name) ...@@ -6347,7 +6329,7 @@ GUCArrayDelete(ArrayType *array, const char *name)
/* test if the option is valid */ /* test if the option is valid */
set_config_option(name, NULL, set_config_option(name, NULL,
superuser() ? PGC_SUSET : PGC_USERSET, superuser() ? PGC_SUSET : PGC_USERSET,
PGC_S_TEST, false, false); PGC_S_TEST, GUC_ACTION_SET, false);
/* convert name to canonical spelling, so we can use plain strcmp */ /* convert name to canonical spelling, so we can use plain strcmp */
(void) GetConfigOptionByName(name, &varname); (void) GetConfigOptionByName(name, &varname);
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Copyright (c) 2000-2007, PostgreSQL Global Development Group * Copyright (c) 2000-2007, PostgreSQL Global Development Group
* Written by Peter Eisentraut <peter_e@gmx.net>. * 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 #ifndef GUC_H
...@@ -100,6 +100,14 @@ typedef bool (*GucRealAssignHook) (double newval, bool doit, GucSource source); ...@@ -100,6 +100,14 @@ typedef bool (*GucRealAssignHook) (double newval, bool doit, GucSource source);
typedef const char *(*GucShowHook) (void); 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 '.' #define GUC_QUALIFIER_SEPARATOR '.'
/* GUC vars that are actually declared in guc.c, rather than elsewhere */ /* GUC vars that are actually declared in guc.c, rather than elsewhere */
...@@ -196,7 +204,7 @@ extern void BeginReportingGUCOptions(void); ...@@ -196,7 +204,7 @@ extern void BeginReportingGUCOptions(void);
extern void ParseLongOption(const char *string, char **name, char **value); extern void ParseLongOption(const char *string, char **name, char **value);
extern bool set_config_option(const char *name, const char *value, extern bool set_config_option(const char *name, const char *value,
GucContext context, GucSource source, GucContext context, GucSource source,
bool isLocal, bool changeVal); GucAction action, bool changeVal);
extern char *GetConfigOptionByName(const char *name, const char **varname); extern char *GetConfigOptionByName(const char *name, const char **varname);
extern void GetConfigOptionByNum(int varnum, const char **values, bool *noshow); extern void GetConfigOptionByNum(int varnum, const char **values, bool *noshow);
extern int GetNumConfigOptions(void); extern int GetNumConfigOptions(void);
...@@ -209,7 +217,7 @@ extern void ExecSetVariableStmt(VariableSetStmt *stmt); ...@@ -209,7 +217,7 @@ extern void ExecSetVariableStmt(VariableSetStmt *stmt);
extern char *ExtractSetVariableArgs(VariableSetStmt *stmt); extern char *ExtractSetVariableArgs(VariableSetStmt *stmt);
extern void ProcessGUCArray(ArrayType *array, 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 *GUCArrayAdd(ArrayType *array, const char *name, const char *value);
extern ArrayType *GUCArrayDelete(ArrayType *array, const char *name); extern ArrayType *GUCArrayDelete(ArrayType *array, const char *name);
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* *
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * 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 ...@@ -79,18 +79,27 @@ enum config_group
}; };
/* /*
* Stack entry for saving the state of a variable prior to the current * Stack entry for saving the state a variable had prior to an uncommitted
* transaction * 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 typedef struct guc_stack
{ {
struct guc_stack *prev; /* previous stack item, if any */ struct guc_stack *prev; /* previous stack item, if any */
int nest_level; /* nesting depth of cur transaction */ int nest_level; /* nesting depth at which we made entry */
int status; /* previous status bits, see below */ GucStackState state; /* see enum above */
GucSource tentative_source; /* source of the tentative_value */ GucSource source; /* source of the prior value */
GucSource source; /* source of the actual value */ union config_var_value prior; /* previous value of variable */
union config_var_value tentative_val; /* previous tentative val */ union config_var_value masked; /* SET value in a GUC_SET_LOCAL entry */
union config_var_value value; /* previous actual value */ /* masked value's source must be PGC_S_SESSION, so no need to store it */
} GucStack; } GucStack;
/* /*
...@@ -113,9 +122,8 @@ struct config_generic ...@@ -113,9 +122,8 @@ struct config_generic
enum config_type vartype; /* type of variable (set only at startup) */ enum config_type vartype; /* type of variable (set only at startup) */
int status; /* status bits, see below */ int status; /* status bits, see below */
GucSource reset_source; /* source of the reset_value */ GucSource reset_source; /* source of the reset_value */
GucSource tentative_source; /* source of the tentative_value */
GucSource source; /* source of the current actual 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 */ /* bit values in flags field */
...@@ -141,10 +149,7 @@ struct config_generic ...@@ -141,10 +149,7 @@ struct config_generic
#define GUC_UNIT_TIME 0x7000 /* mask for MS, S, MIN */ #define GUC_UNIT_TIME 0x7000 /* mask for MS, S, MIN */
/* bit values in status field */ /* bit values in status field */
#define GUC_HAVE_TENTATIVE 0x0001 /* tentative value is defined */ #define GUC_IS_IN_FILE 0x0001 /* found it in config file */
#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 */
/* /*
* Caution: the GUC_IS_IN_FILE bit is transient state for ProcessConfigFile. * Caution: the GUC_IS_IN_FILE bit is transient state for ProcessConfigFile.
* Do not assume that its value represents useful information elsewhere. * Do not assume that its value represents useful information elsewhere.
...@@ -163,7 +168,6 @@ struct config_bool ...@@ -163,7 +168,6 @@ struct config_bool
GucShowHook show_hook; GucShowHook show_hook;
/* variable fields, initialized at runtime: */ /* variable fields, initialized at runtime: */
bool reset_val; bool reset_val;
bool tentative_val;
}; };
struct config_int struct config_int
...@@ -178,7 +182,6 @@ struct config_int ...@@ -178,7 +182,6 @@ struct config_int
GucShowHook show_hook; GucShowHook show_hook;
/* variable fields, initialized at runtime: */ /* variable fields, initialized at runtime: */
int reset_val; int reset_val;
int tentative_val;
}; };
struct config_real struct config_real
...@@ -193,7 +196,6 @@ struct config_real ...@@ -193,7 +196,6 @@ struct config_real
GucShowHook show_hook; GucShowHook show_hook;
/* variable fields, initialized at runtime: */ /* variable fields, initialized at runtime: */
double reset_val; double reset_val;
double tentative_val;
}; };
struct config_string struct config_string
...@@ -206,7 +208,6 @@ struct config_string ...@@ -206,7 +208,6 @@ struct config_string
GucShowHook show_hook; GucShowHook show_hook;
/* variable fields, initialized at runtime: */ /* variable fields, initialized at runtime: */
char *reset_val; char *reset_val;
char *tentative_val;
}; };
/* constant tables corresponding to enums above and in guc.h */ /* constant tables corresponding to enums above and in guc.h */
......
...@@ -352,6 +352,85 @@ SELECT '2006-08-13 12:34:56'::timestamptz; ...@@ -352,6 +352,85 @@ SELECT '2006-08-13 12:34:56'::timestamptz;
2006-08-13 12:34:56-07 2006-08-13 12:34:56-07
(1 row) (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 -- SET followed by SET LOCAL
BEGIN; BEGIN;
SET vacuum_cost_delay TO 400; SET vacuum_cost_delay TO 400;
...@@ -558,3 +637,65 @@ select report_guc('regex_flavor'), current_setting('regex_flavor'); ...@@ -558,3 +637,65 @@ select report_guc('regex_flavor'), current_setting('regex_flavor');
advanced | advanced advanced | advanced
(1 row) (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; ...@@ -99,6 +99,26 @@ SHOW vacuum_cost_delay;
SHOW datestyle; SHOW datestyle;
SELECT '2006-08-13 12:34:56'::timestamptz; 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 -- SET followed by SET LOCAL
BEGIN; BEGIN;
SET vacuum_cost_delay TO 400; SET vacuum_cost_delay TO 400;
...@@ -187,3 +207,47 @@ select report_guc('regex_flavor'), current_setting('regex_flavor'); ...@@ -187,3 +207,47 @@ select report_guc('regex_flavor'), current_setting('regex_flavor');
alter function report_guc(text) reset all; alter function report_guc(text) reset all;
select report_guc('regex_flavor'), current_setting('regex_flavor'); 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');
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment