Tom Lane <[email protected]> wrote:
> The main thing I would be worried about is whether you're sure
> that you have separated the RESET-as-a-command case from the cases
> where we actually are rolling back to a previous state.
It looks good to me. I added a few regression tests for that.
Robert Haas <[email protected]> wrote:
> +1 for such a comment.
Added.
The attached patch includes these. If it seems close, I'd be happy
to come up with a version for the 9.1 branch. Basically it looks
like that means omitting the changes to variable.c (which only
changed message text and made the order of steps on related GUCs
more consistent), and capturing a different out file for the
expected directory.
-Kevin
*** a/src/backend/commands/variable.c
--- b/src/backend/commands/variable.c
***************
*** 600,616 **** check_XactIsoLevel(char **newval, void **extra, GucSource
source)
if (newXactIsoLevel != XactIsoLevel)
{
! if (FirstSnapshotSet)
{
GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION);
! GUC_check_errmsg("SET TRANSACTION ISOLATION LEVEL must
be called before any query");
return false;
}
! /* We ignore a subtransaction setting it to the existing value.
*/
! if (IsSubTransaction())
{
GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION);
! GUC_check_errmsg("SET TRANSACTION ISOLATION LEVEL must
not be called in a subtransaction");
return false;
}
/* Can't go to serializable mode while recovery is still active
*/
--- 600,616 ----
if (newXactIsoLevel != XactIsoLevel)
{
! /* We ignore a subtransaction setting it to the existing value.
*/
! if (IsSubTransaction())
{
GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION);
! GUC_check_errmsg("cannot set transaction isolation
level in a subtransaction");
return false;
}
! if (FirstSnapshotSet)
{
GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION);
! GUC_check_errmsg("transaction isolation level must be
set before any query");
return false;
}
/* Can't go to serializable mode while recovery is still active
*/
***************
*** 665,677 **** check_transaction_deferrable(bool *newval, void **extra,
GucSource source)
if (IsSubTransaction())
{
GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION);
! GUC_check_errmsg("SET TRANSACTION [NOT] DEFERRABLE cannot be
called within a subtransaction");
return false;
}
if (FirstSnapshotSet)
{
GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION);
! GUC_check_errmsg("SET TRANSACTION [NOT] DEFERRABLE must be
called before any query");
return false;
}
--- 665,677 ----
if (IsSubTransaction())
{
GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION);
! GUC_check_errmsg("cannot set transaction deferrable state
within a subtransaction");
return false;
}
if (FirstSnapshotSet)
{
GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION);
! GUC_check_errmsg("transaction deferrable state must be set
before any query");
return false;
}
*** a/src/backend/utils/misc/guc.c
--- b/src/backend/utils/misc/guc.c
***************
*** 5042,5047 **** config_enum_get_options(struct config_enum * record, const
char *prefix,
--- 5042,5051 ----
*
* If value is NULL, set the option to its default value (normally the
* reset_val, but if source == PGC_S_DEFAULT we instead use the boot_val).
+ * When we reset a value we can't assume that the old value is valid, but must
+ * call the check hook if present. This is because the validity of a change
+ * might depend on context. For example, transaction isolation may not be
+ * changed after the transaction has run a query, even by a RESET command.
*
* 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.
***************
*** 5287,5302 **** set_config_option(const char *name, const char *value,
name)));
return 0;
}
- if (!call_bool_check_hook(conf,
&newval, &newextra,
-
source, elevel))
- return 0;
}
else if (source == PGC_S_DEFAULT)
{
newval = conf->boot_val;
- if (!call_bool_check_hook(conf,
&newval, &newextra,
-
source, elevel))
- return 0;
}
else
{
--- 5291,5300 ----
***************
*** 5306,5311 **** set_config_option(const char *name, const char *value,
--- 5304,5313 ----
context = conf->gen.reset_scontext;
}
+ if (!call_bool_check_hook(conf, &newval,
&newextra,
+
source, elevel))
+ return 0;
+
if (prohibitValueChange)
{
if (*conf->variable != newval)
***************
*** 5391,5406 **** set_config_option(const char *name, const char *value,
newval, name, conf->min, conf->max)));
return 0;
}
- if (!call_int_check_hook(conf, &newval,
&newextra,
-
source, elevel))
- return 0;
}
else if (source == PGC_S_DEFAULT)
{
newval = conf->boot_val;
- if (!call_int_check_hook(conf, &newval,
&newextra,
-
source, elevel))
- return 0;
}
else
{
--- 5393,5402 ----
***************
*** 5410,5415 **** set_config_option(const char *name, const char *value,
--- 5406,5415 ----
context = conf->gen.reset_scontext;
}
+ if (!call_int_check_hook(conf, &newval,
&newextra,
+
source, elevel))
+ return 0;
+
if (prohibitValueChange)
{
if (*conf->variable != newval)
***************
*** 5492,5507 **** set_config_option(const char *name, const char *value,
newval, name, conf->min, conf->max)));
return 0;
}
- if (!call_real_check_hook(conf,
&newval, &newextra,
-
source, elevel))
- return 0;
}
else if (source == PGC_S_DEFAULT)
{
newval = conf->boot_val;
- if (!call_real_check_hook(conf,
&newval, &newextra,
-
source, elevel))
- return 0;
}
else
{
--- 5492,5501 ----
***************
*** 5511,5516 **** set_config_option(const char *name, const char *value,
--- 5505,5514 ----
context = conf->gen.reset_scontext;
}
+ if (!call_real_check_hook(conf, &newval,
&newextra,
+
source, elevel))
+ return 0;
+
if (prohibitValueChange)
{
if (*conf->variable != newval)
***************
*** 5591,5603 **** set_config_option(const char *name, const char *value,
*/
if (conf->gen.flags & GUC_IS_NAME)
truncate_identifier(newval,
strlen(newval), true);
-
- if (!call_string_check_hook(conf,
&newval, &newextra,
-
source, elevel))
- {
- free(newval);
- return 0;
- }
}
else if (source == PGC_S_DEFAULT)
{
--- 5589,5594 ----
***************
*** 5610,5635 **** set_config_option(const char *name, const char *value,
}
else
newval = NULL;
-
- if (!call_string_check_hook(conf,
&newval, &newextra,
-
source, elevel))
- {
- free(newval);
- return 0;
- }
}
else
{
! /*
! * strdup not needed, since reset_val
is already under
! * guc.c's control
! */
! newval = conf->reset_val;
newextra = conf->reset_extra;
source = conf->gen.reset_source;
context = conf->gen.reset_scontext;
}
if (prohibitValueChange)
{
/* newval shouldn't be NULL, so we're a
bit sloppy here */
--- 5601,5630 ----
}
else
newval = NULL;
}
else
{
! /* non-NULL reset_val must always get
strdup'd */
! if (conf->reset_val != NULL)
! {
! newval = guc_strdup(elevel,
conf->reset_val);
! if (newval == NULL)
! return 0;
! }
! else
! newval = NULL;
newextra = conf->reset_extra;
source = conf->gen.reset_source;
context = conf->gen.reset_scontext;
}
+ if (!call_string_check_hook(conf, &newval,
&newextra,
+
source, elevel))
+ {
+ free(newval);
+ return 0;
+ }
+
if (prohibitValueChange)
{
/* newval shouldn't be NULL, so we're a
bit sloppy here */
***************
*** 5721,5736 **** set_config_option(const char *name, const char *value,
pfree(hintmsg);
return 0;
}
- if (!call_enum_check_hook(conf,
&newval, &newextra,
-
source, elevel))
- return 0;
}
else if (source == PGC_S_DEFAULT)
{
newval = conf->boot_val;
- if (!call_enum_check_hook(conf,
&newval, &newextra,
-
source, elevel))
- return 0;
}
else
{
--- 5716,5725 ----
***************
*** 5740,5745 **** set_config_option(const char *name, const char *value,
--- 5729,5738 ----
context = conf->gen.reset_scontext;
}
+ if (!call_enum_check_hook(conf, &newval,
&newextra,
+
source, elevel))
+ return 0;
+
if (prohibitValueChange)
{
if (*conf->variable != newval)
*** a/src/test/regress/expected/transactions.out
--- b/src/test/regress/expected/transactions.out
***************
*** 53,58 **** SELECT * FROM writetest; -- ok
--- 53,90 ----
SET TRANSACTION READ WRITE; --fail
ERROR: transaction read-write mode must be set before any query
COMMIT;
+ SET default_transaction_read_only = FALSE;
+ SET default_transaction_isolation = 'read committed';
+ BEGIN;
+ SET TRANSACTION ISOLATION LEVEL SERIALIZABLE, READ ONLY; -- ok
+ SELECT * FROM writetest; -- ok
+ a
+ ---
+ (0 rows)
+
+ RESET transaction_read_only; --fail
+ ERROR: transaction read-write mode must be set before any query
+ COMMIT;
+ BEGIN;
+ SET TRANSACTION ISOLATION LEVEL SERIALIZABLE, READ ONLY; -- ok
+ SELECT * FROM writetest; -- ok
+ a
+ ---
+ (0 rows)
+
+ RESET transaction_isolation; --fail
+ ERROR: transaction isolation level must be set before any query
+ COMMIT;
+ BEGIN;
+ SET TRANSACTION ISOLATION LEVEL SERIALIZABLE, READ ONLY; -- ok
+ SELECT * FROM writetest; -- ok
+ a
+ ---
+ (0 rows)
+
+ ROLLBACK; -- ok
+ RESET default_transaction_read_only;
+ RESET default_transaction_isolation;
BEGIN;
SET TRANSACTION READ ONLY; -- ok
SET TRANSACTION READ WRITE; -- ok
***************
*** 391,396 **** SELECT 1; -- this should work
--- 423,480 ----
1
(1 row)
+ -- test rollbacks and resets of GUC in transactions
+ CREATE SCHEMA albion;
+ SET search_path = "$user",public,albion;
+ DROP SCHEMA albion;
+ SHOW search_path;
+ search_path
+ -------------------------
+ "$user", public, albion
+ (1 row)
+
+ BEGIN;
+ CREATE SCHEMA camelot;
+ SET search_path = "$user",public,camelot;
+ DROP SCHEMA camelot;
+ SAVEPOINT bedivere;
+ CREATE SCHEMA avalon;
+ SET search_path = "$user",public,avalon;
+ DROP SCHEMA avalon;
+ SHOW search_path;
+ search_path
+ -------------------------
+ "$user", public, avalon
+ (1 row)
+
+ ROLLBACK TO SAVEPOINT bedivere;
+ SHOW search_path;
+ search_path
+ --------------------------
+ "$user", public, camelot
+ (1 row)
+
+ RESET search_path;
+ SHOW search_path;
+ search_path
+ ----------------
+ "$user",public
+ (1 row)
+
+ ROLLBACK;
+ SHOW search_path;
+ search_path
+ -------------------------
+ "$user", public, albion
+ (1 row)
+
+ RESET search_path;
+ SHOW search_path;
+ search_path
+ ----------------
+ "$user",public
+ (1 row)
+
-- check non-transactional behavior of cursors
BEGIN;
DECLARE c CURSOR FOR SELECT unique2 FROM tenk1 ORDER BY unique2;
*** a/src/test/regress/sql/transactions.sql
--- b/src/test/regress/sql/transactions.sql
***************
*** 45,50 **** SELECT * FROM writetest; -- ok
--- 45,73 ----
SET TRANSACTION READ WRITE; --fail
COMMIT;
+ SET default_transaction_read_only = FALSE;
+ SET default_transaction_isolation = 'read committed';
+
+ BEGIN;
+ SET TRANSACTION ISOLATION LEVEL SERIALIZABLE, READ ONLY; -- ok
+ SELECT * FROM writetest; -- ok
+ RESET transaction_read_only; --fail
+ COMMIT;
+
+ BEGIN;
+ SET TRANSACTION ISOLATION LEVEL SERIALIZABLE, READ ONLY; -- ok
+ SELECT * FROM writetest; -- ok
+ RESET transaction_isolation; --fail
+ COMMIT;
+
+ BEGIN;
+ SET TRANSACTION ISOLATION LEVEL SERIALIZABLE, READ ONLY; -- ok
+ SELECT * FROM writetest; -- ok
+ ROLLBACK; -- ok
+
+ RESET default_transaction_read_only;
+ RESET default_transaction_isolation;
+
BEGIN;
SET TRANSACTION READ ONLY; -- ok
SET TRANSACTION READ WRITE; -- ok
***************
*** 252,257 **** BEGIN;
--- 275,303 ----
COMMIT;
SELECT 1; -- this should work
+ -- test rollbacks and resets of GUC in transactions
+ CREATE SCHEMA albion;
+ SET search_path = "$user",public,albion;
+ DROP SCHEMA albion;
+ SHOW search_path;
+ BEGIN;
+ CREATE SCHEMA camelot;
+ SET search_path = "$user",public,camelot;
+ DROP SCHEMA camelot;
+ SAVEPOINT bedivere;
+ CREATE SCHEMA avalon;
+ SET search_path = "$user",public,avalon;
+ DROP SCHEMA avalon;
+ SHOW search_path;
+ ROLLBACK TO SAVEPOINT bedivere;
+ SHOW search_path;
+ RESET search_path;
+ SHOW search_path;
+ ROLLBACK;
+ SHOW search_path;
+ RESET search_path;
+ SHOW search_path;
+
-- check non-transactional behavior of cursors
BEGIN;
DECLARE c CURSOR FOR SELECT unique2 FROM tenk1 ORDER BY unique2;
--
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers