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:
parent
b366562e43
commit
82a47982f3
@ -1,5 +1,5 @@
|
|||||||
<!--
|
<!--
|
||||||
$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) ...
|
|||||||
</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
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<!--
|
<!--
|
||||||
$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>
|
|||||||
</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
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<!--
|
<!--
|
||||||
$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
|
|||||||
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
|
|||||||
<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
|
|||||||
<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;
|
|||||||
<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>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<!--
|
<!--
|
||||||
$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
|
|||||||
</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
|
|||||||
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
|
|||||||
<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>);
|
|||||||
<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>);
|
|||||||
|
|
||||||
<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>
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<!--
|
<!--
|
||||||
$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
|
|||||||
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
|
|||||||
|
|
||||||
<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 @@
|
|||||||
*
|
*
|
||||||
* 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)
|
|||||||
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)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* 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 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* 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)
|
|||||||
|
|
||||||
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)
|
|||||||
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 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* 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)
|
|||||||
* 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 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* 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)
|
|||||||
* 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
GUC IMPLEMENTATION NOTES
|
||||||
@ -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
|
||||||
|
|
||||||
* 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
|
In addition to the primary values, there is a stack of former effective
|
||||||
that when a transaction does SET followed by SET LOCAL, the actual value
|
values that might need to be restored in future. Stacking and unstacking
|
||||||
will now be the LOCAL value, but we want to remember the prior SET so that
|
is controlled by the GUC "nest level", which is zero when outside any
|
||||||
that value is restored at transaction commit.
|
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
|
A stack entry has a state, a prior value of the GUC variable, a remembered
|
||||||
remember the transaction-entry-time actual and tentative values, in case
|
source of that prior value, and depending on the state may also have a
|
||||||
we need to restore them at transaction end. (The RESET value is essentially
|
"masked" value. The masked value is needed when SET followed by SET LOCAL
|
||||||
non-transactional, so it doesn't have to be stacked.) For efficiency these
|
occur at the same nest level: the SET's value is masked but must be
|
||||||
stack entries are not constructed until/unless the variable is actually SET
|
remembered to restore after transaction commit.
|
||||||
within a particular transaction.
|
|
||||||
|
|
||||||
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
|
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
|
The possible transactional operations on a GUC value are:
|
||||||
if this hasn't already been done within the current transaction. Then:
|
|
||||||
|
|
||||||
A SET LOCAL command sets the actual variable (and nothing else). At
|
Entry to a function with a SET option:
|
||||||
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,
|
Push a stack entry with the prior variable value and state SAVE,
|
||||||
then sets the tentative_value. If the transaction commits, the
|
then set the variable.
|
||||||
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.
|
|
||||||
|
|
||||||
In the case of SET within nested subtransactions, at each commit the
|
Plain SET command:
|
||||||
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.
|
|
||||||
|
|
||||||
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
|
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
|
|||||||
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 @@
|
|||||||
*
|
*
|
||||||
* 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)
|
|||||||
}
|
}
|
||||||
|
|
||||||
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)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* 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)
|
|||||||
* 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 @@
|
|||||||
* 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[] =
|
|||||||
* 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 */
|
|||||||
|
|
||||||
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)
|
|||||||
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)
|
|||||||
|
|
||||||
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)
|
|||||||
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)
|
|||||||
|
|
||||||
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)
|
|||||||
|
|
||||||
*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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
}
|
}
|
||||||
|
|
||||||
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,83 +3390,83 @@ 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)
|
||||||
{
|
{
|
||||||
/* Done if we already pushed it at this nesting depth */
|
/* Yes, so adjust its state if necessary */
|
||||||
if (gconf->stack && gconf->stack->nest_level >= GUCNestLevel)
|
Assert(stack->nest_level == GUCNestLevel);
|
||||||
|
switch (action)
|
||||||
|
{
|
||||||
|
case GUC_ACTION_SET:
|
||||||
|
/* 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;
|
break;
|
||||||
|
case GUC_ACTION_LOCAL:
|
||||||
case PGC_INT:
|
stack->state = GUC_LOCAL;
|
||||||
stack->tentative_val.intval =
|
|
||||||
((struct config_int *) gconf)->tentative_val;
|
|
||||||
stack->value.intval =
|
|
||||||
*((struct config_int *) gconf)->variable;
|
|
||||||
break;
|
break;
|
||||||
|
case GUC_ACTION_SAVE:
|
||||||
case PGC_REAL:
|
stack->state = GUC_SAVE;
|
||||||
stack->tentative_val.realval =
|
|
||||||
((struct config_real *) gconf)->tentative_val;
|
|
||||||
stack->value.realval =
|
|
||||||
*((struct config_real *) gconf)->variable;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PGC_STRING:
|
|
||||||
stack->tentative_val.stringval =
|
|
||||||
((struct config_string *) gconf)->tentative_val;
|
|
||||||
stack->value.stringval =
|
|
||||||
*((struct config_string *) gconf)->variable;
|
|
||||||
break;
|
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)
|
|||||||
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,78 +3518,127 @@ 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 */
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
else /* must be GUC_LOCAL */
|
||||||
|
restorePrior = true;
|
||||||
|
}
|
||||||
|
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 that we stacked old value before changing it */
|
else
|
||||||
Assert(stack != NULL && (my_status & GUC_HAVE_STACK));
|
{
|
||||||
/* However, the last change may have been at an outer xact level */
|
|
||||||
if (stack->nest_level < nestLevel)
|
|
||||||
continue;
|
|
||||||
Assert(stack->nest_level == nestLevel);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 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;
|
||||||
|
|
||||||
|
if (restorePrior || restoreMasked)
|
||||||
|
{
|
||||||
|
/* Perform appropriate restoration of the stacked value */
|
||||||
|
union config_var_value newvalue;
|
||||||
|
GucSource newsource;
|
||||||
|
|
||||||
|
if (restoreMasked)
|
||||||
|
{
|
||||||
|
newvalue = stack->masked;
|
||||||
|
newsource = PGC_S_SESSION;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
newvalue = stack->prior;
|
||||||
|
newsource = stack->source;
|
||||||
|
}
|
||||||
|
|
||||||
switch (gconf->vartype)
|
switch (gconf->vartype)
|
||||||
{
|
{
|
||||||
case PGC_BOOL:
|
case PGC_BOOL:
|
||||||
{
|
{
|
||||||
struct config_bool *conf = (struct config_bool *) gconf;
|
struct config_bool *conf = (struct config_bool *) gconf;
|
||||||
bool newval;
|
bool newval = newvalue.boolval;
|
||||||
GucSource newsource;
|
|
||||||
|
|
||||||
if (useTentative)
|
|
||||||
{
|
|
||||||
newval = conf->tentative_val;
|
|
||||||
newsource = conf->gen.tentative_source;
|
|
||||||
conf->gen.status |= GUC_HAVE_TENTATIVE;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
newval = stack->value.boolval;
|
|
||||||
newsource = stack->source;
|
|
||||||
conf->tentative_val = stack->tentative_val.boolval;
|
|
||||||
conf->gen.tentative_source = stack->tentative_source;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*conf->variable != newval)
|
if (*conf->variable != newval)
|
||||||
{
|
{
|
||||||
@ -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)
|
|||||||
*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)
|
|||||||
*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)
|
|||||||
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Finish popping the state stack */
|
gconf->source = newsource;
|
||||||
gconf->stack = stack->prev;
|
|
||||||
pfree(stack);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If we're now out of all xact levels, forget TENTATIVE status bit;
|
|
||||||
* there's nothing tentative about the value anymore.
|
|
||||||
*/
|
|
||||||
if (nestLevel == 1)
|
|
||||||
{
|
|
||||||
Assert(gconf->stack == NULL);
|
|
||||||
gconf->status = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Finish popping the state stack */
|
||||||
|
gconf->stack = prev;
|
||||||
|
pfree(stack);
|
||||||
|
|
||||||
/* 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,
|
|||||||
* 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,
|
|||||||
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,
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* 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,
|
|||||||
{
|
{
|
||||||
/* 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,
|
|||||||
{
|
{
|
||||||
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,
|
|||||||
{
|
{
|
||||||
/* 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,
|
|||||||
{
|
{
|
||||||
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,
|
|||||||
{
|
{
|
||||||
/* 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,
|
|||||||
{
|
{
|
||||||
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,
|
|||||||
{
|
{
|
||||||
/* 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,
|
|||||||
{
|
{
|
||||||
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,
|
|||||||
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
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
*/
|
*/
|
||||||
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)
|
|||||||
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)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* 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,
|
|||||||
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)
|
|||||||
/* 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)
|
|||||||
/* 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 @@
|
|||||||
* 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);
|
|||||||
|
|
||||||
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);
|
|||||||
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);
|
|||||||
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 @@
|
|||||||
*
|
*
|
||||||
* 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
|
|||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 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
|
|||||||
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
|
|||||||
#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
|
|||||||
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
|
|||||||
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
|
|||||||
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
|
|||||||
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;
|
|||||||
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');
|
|||||||
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;
|
|||||||
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');
|
|||||||
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');
|
||||||
|
Loading…
x
Reference in New Issue
Block a user