From a54141aebcafa02cba5204596758dadbbeb0f78e Mon Sep 17 00:00:00 2001 From: Bruce Momjian Date: Fri, 4 Oct 2013 13:50:28 -0400 Subject: [PATCH] Issue error on SET outside transaction block in some cases Issue error for SET LOCAL/CONSTRAINTS/TRANSACTION outside a transaction block, as they have no effect. Per suggestion from Morten Hustveit --- doc/src/sgml/ref/set.sgml | 7 +++---- doc/src/sgml/ref/set_constraints.sgml | 2 +- doc/src/sgml/ref/set_transaction.sgml | 5 ++--- src/backend/tcop/utility.c | 3 ++- src/backend/utils/misc/guc.c | 15 +++++++++++++-- src/include/utils/guc.h | 2 +- src/test/regress/expected/guc.out | 2 ++ 7 files changed, 24 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/set.sgml b/doc/src/sgml/ref/set.sgml index 21745db462..d108dd4831 100644 --- a/doc/src/sgml/ref/set.sgml +++ b/doc/src/sgml/ref/set.sgml @@ -110,10 +110,9 @@ SET [ SESSION | LOCAL ] TIME ZONE { timezone Specifies that the command takes effect for only the current transaction. After COMMIT or ROLLBACK, - the session-level setting takes effect again. Note that - SET LOCAL will appear to have no effect if it is - executed outside a BEGIN block, since the - transaction will end immediately. + the session-level setting takes effect again. + PostgreSQL reports an error if + SET LOCAL is used outside a transaction block. diff --git a/doc/src/sgml/ref/set_constraints.sgml b/doc/src/sgml/ref/set_constraints.sgml index 8098b7b667..895a5fdbc0 100644 --- a/doc/src/sgml/ref/set_constraints.sgml +++ b/doc/src/sgml/ref/set_constraints.sgml @@ -102,7 +102,7 @@ SET CONSTRAINTS { ALL | name [, ... current transaction. Thus, if you execute this command outside of a transaction block (BEGIN/COMMIT pair), it will - not appear to have any effect. + generate an error. diff --git a/doc/src/sgml/ref/set_transaction.sgml b/doc/src/sgml/ref/set_transaction.sgml index f060729680..391464ade8 100644 --- a/doc/src/sgml/ref/set_transaction.sgml +++ b/doc/src/sgml/ref/set_transaction.sgml @@ -184,9 +184,8 @@ SET SESSION CHARACTERISTICS AS TRANSACTION transa If SET TRANSACTION is executed without a prior - START TRANSACTION or BEGIN, - it will appear to have no effect, since the transaction will immediately - end. + START TRANSACTION or BEGIN, + it will generate an error. diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index fffaa35d34..6a7bf0de7d 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -688,7 +688,7 @@ standard_ProcessUtility(Node *parsetree, break; case T_VariableSetStmt: - ExecSetVariableStmt((VariableSetStmt *) parsetree); + ExecSetVariableStmt((VariableSetStmt *) parsetree, isTopLevel); break; case T_VariableShowStmt: @@ -754,6 +754,7 @@ standard_ProcessUtility(Node *parsetree, break; case T_ConstraintsSetStmt: + RequireTransactionChain(isTopLevel, "SET CONSTRAINTS"); AfterTriggerSetState((ConstraintsSetStmt *) parsetree); break; diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 3107f9cf02..d9a06b4d88 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -6252,7 +6252,7 @@ flatten_set_variable_args(const char *name, List *args) * SET command */ void -ExecSetVariableStmt(VariableSetStmt *stmt) +ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel) { GucAction action = stmt->is_local ? GUC_ACTION_LOCAL : GUC_ACTION_SET; @@ -6260,6 +6260,8 @@ ExecSetVariableStmt(VariableSetStmt *stmt) { case VAR_SET_VALUE: case VAR_SET_CURRENT: + if (stmt->is_local) + RequireTransactionChain(isTopLevel, "SET LOCAL"); (void) set_config_option(stmt->name, ExtractSetVariableArgs(stmt), (superuser() ? PGC_SUSET : PGC_USERSET), @@ -6269,7 +6271,6 @@ ExecSetVariableStmt(VariableSetStmt *stmt) 0); break; case VAR_SET_MULTI: - /* * Special-case SQL syntaxes. The TRANSACTION and SESSION * CHARACTERISTICS cases effectively set more than one variable @@ -6281,6 +6282,8 @@ ExecSetVariableStmt(VariableSetStmt *stmt) { ListCell *head; + RequireTransactionChain(isTopLevel, "SET TRANSACTION"); + foreach(head, stmt->args) { DefElem *item = (DefElem *) lfirst(head); @@ -6329,6 +6332,8 @@ ExecSetVariableStmt(VariableSetStmt *stmt) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("SET LOCAL TRANSACTION SNAPSHOT is not implemented"))); + + RequireTransactionChain(isTopLevel, "SET TRANSACTION"); Assert(IsA(con, A_Const)); Assert(nodeTag(&con->val) == T_String); ImportSnapshot(strVal(&con->val)); @@ -6338,7 +6343,13 @@ ExecSetVariableStmt(VariableSetStmt *stmt) stmt->name); break; case VAR_SET_DEFAULT: + if (stmt->is_local) + RequireTransactionChain(isTopLevel, "SET LOCAL"); + /* fall through */ case VAR_RESET: + if (strcmp(stmt->name, "transaction_isolation") == 0) + RequireTransactionChain(isTopLevel, "RESET TRANSACTION"); + (void) set_config_option(stmt->name, NULL, (superuser() ? PGC_SUSET : PGC_USERSET), diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h index 99211c1f6c..89ee40c334 100644 --- a/src/include/utils/guc.h +++ b/src/include/utils/guc.h @@ -334,7 +334,7 @@ extern void SetPGVariable(const char *name, List *args, bool is_local); extern void GetPGVariable(const char *name, DestReceiver *dest); extern TupleDesc GetPGVariableResultDesc(const char *name); -extern void ExecSetVariableStmt(VariableSetStmt *stmt); +extern void ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel); extern char *ExtractSetVariableArgs(VariableSetStmt *stmt); extern void ProcessGUCArray(ArrayType *array, diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 7b5a624eb8..203fa6ef8e 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -29,6 +29,7 @@ SELECT '2006-08-13 12:34:56'::timestamptz; -- SET LOCAL has no effect outside of a transaction SET LOCAL vacuum_cost_delay TO 50; +ERROR: SET LOCAL can only be used in transaction blocks SHOW vacuum_cost_delay; vacuum_cost_delay ------------------- @@ -36,6 +37,7 @@ SHOW vacuum_cost_delay; (1 row) SET LOCAL datestyle = 'SQL'; +ERROR: SET LOCAL can only be used in transaction blocks SHOW datestyle; DateStyle -----------