diff --git a/doc/src/sgml/ref/abort.sgml b/doc/src/sgml/ref/abort.sgml
index 246e8f81268..f3a2fa88ff1 100644
--- a/doc/src/sgml/ref/abort.sgml
+++ b/doc/src/sgml/ref/abort.sgml
@@ -63,8 +63,7 @@ ABORT [ WORK | TRANSACTION ]
- Issuing ABORT> when not inside a transaction does
- no harm, but it will provoke a warning message.
+ Issuing ABORT> outside of a transaction block has no effect.
diff --git a/doc/src/sgml/ref/rollback.sgml b/doc/src/sgml/ref/rollback.sgml
index b26554567db..4f7962117c8 100644
--- a/doc/src/sgml/ref/rollback.sgml
+++ b/doc/src/sgml/ref/rollback.sgml
@@ -59,8 +59,8 @@ ROLLBACK [ WORK | TRANSACTION ]
- Issuing ROLLBACK> when not inside a transaction does
- no harm, but it will provoke a warning message.
+ Issuing ROLLBACK> outside of a transaction
+ block has no effect.
diff --git a/doc/src/sgml/ref/set.sgml b/doc/src/sgml/ref/set.sgml
index 6290c9de708..5a84f697e67 100644
--- a/doc/src/sgml/ref/set.sgml
+++ b/doc/src/sgml/ref/set.sgml
@@ -110,9 +110,8 @@ 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.
- PostgreSQL reports an error if
- SET LOCAL> is used outside a transaction block.
+ the session-level setting takes effect again. This has no effect
+ outside of a transaction block.
diff --git a/doc/src/sgml/ref/set_constraints.sgml b/doc/src/sgml/ref/set_constraints.sgml
index 895a5fdbc0e..a33190cca81 100644
--- a/doc/src/sgml/ref/set_constraints.sgml
+++ b/doc/src/sgml/ref/set_constraints.sgml
@@ -99,10 +99,7 @@ SET CONSTRAINTS { ALL | name [, ...
This command only alters the behavior of constraints within the
- current transaction. Thus, if you execute this command outside of a
- transaction block
- (BEGIN/COMMIT pair), it will
- generate an error.
+ current transaction. This has no effect outside of a transaction block.
diff --git a/doc/src/sgml/ref/set_transaction.sgml b/doc/src/sgml/ref/set_transaction.sgml
index 391464ade83..e90ff4af725 100644
--- a/doc/src/sgml/ref/set_transaction.sgml
+++ b/doc/src/sgml/ref/set_transaction.sgml
@@ -185,7 +185,7 @@ SET SESSION CHARACTERISTICS AS TRANSACTION transa
If SET TRANSACTION is executed without a prior
START TRANSACTION or BEGIN,
- it will generate an error.
+ it will have no effect.
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index 0591f3fd562..bab048d38a1 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -265,6 +265,8 @@ static void CallSubXactCallbacks(SubXactEvent event,
SubTransactionId mySubid,
SubTransactionId parentSubid);
static void CleanupTransaction(void);
+static void CheckTransactionChain(bool isTopLevel, bool throwError,
+ const char *stmtType);
static void CommitTransaction(void);
static TransactionId RecordTransactionAbort(bool isSubXact);
static void StartTransaction(void);
@@ -2948,6 +2950,26 @@ PreventTransactionChain(bool isTopLevel, const char *stmtType)
/* all okay */
}
+/*
+ * These two functions allow for warnings or errors if a command is
+ * executed outside of a transaction block.
+ *
+ * While top-level transaction control commands (BEGIN/COMMIT/ABORT) and
+ * SET that have no effect issue warnings, all other no-effect commands
+ * generate errors.
+ */
+void
+WarnNoTransactionChain(bool isTopLevel, const char *stmtType)
+{
+ CheckTransactionChain(isTopLevel, false, stmtType);
+}
+
+void
+RequireTransactionChain(bool isTopLevel, const char *stmtType)
+{
+ CheckTransactionChain(isTopLevel, true, stmtType);
+}
+
/*
* RequireTransactionChain
*
@@ -2957,16 +2979,16 @@ PreventTransactionChain(bool isTopLevel, const char *stmtType)
* is presumably an error). DECLARE CURSOR is an example.
*
* If we appear to be running inside a user-defined function, we do not
- * issue an error, since the function could issue more commands that make
+ * issue anything, since the function could issue more commands that make
* use of the current statement's results. Likewise subtransactions.
* Thus this is an inverse for PreventTransactionChain.
*
* isTopLevel: passed down from ProcessUtility to determine whether we are
* inside a function.
- * stmtType: statement type name, for error messages.
+ * stmtType: statement type name, for warning or error messages.
*/
-void
-RequireTransactionChain(bool isTopLevel, const char *stmtType)
+static void
+CheckTransactionChain(bool isTopLevel, bool throwError, const char *stmtType)
{
/*
* xact block already started?
@@ -2986,11 +3008,12 @@ RequireTransactionChain(bool isTopLevel, const char *stmtType)
if (!isTopLevel)
return;
- ereport(ERROR,
+ ereport(throwError ? ERROR : WARNING,
(errcode(ERRCODE_NO_ACTIVE_SQL_TRANSACTION),
/* translator: %s represents an SQL statement name */
errmsg("%s can only be used in transaction blocks",
stmtType)));
+ return;
}
/*
@@ -3425,12 +3448,12 @@ UserAbortTransactionBlock(void)
/*
* The user issued ABORT when not inside a transaction. Issue a
- * NOTICE and go to abort state. The upcoming call to
+ * WARNING and go to abort state. The upcoming call to
* CommitTransactionCommand() will then put us back into the
* default state.
*/
case TBLOCK_STARTED:
- ereport(NOTICE,
+ ereport(WARNING,
(errcode(ERRCODE_NO_ACTIVE_SQL_TRANSACTION),
errmsg("there is no transaction in progress")));
s->blockState = TBLOCK_ABORT_PENDING;
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 6a7bf0de7d7..5895102b518 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -754,7 +754,7 @@ standard_ProcessUtility(Node *parsetree,
break;
case T_ConstraintsSetStmt:
- RequireTransactionChain(isTopLevel, "SET CONSTRAINTS");
+ WarnNoTransactionChain(isTopLevel, "SET CONSTRAINTS");
AfterTriggerSetState((ConstraintsSetStmt *) parsetree);
break;
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 54d8078fe7a..cbf3186789c 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -6274,7 +6274,7 @@ ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel)
case VAR_SET_VALUE:
case VAR_SET_CURRENT:
if (stmt->is_local)
- RequireTransactionChain(isTopLevel, "SET LOCAL");
+ WarnNoTransactionChain(isTopLevel, "SET LOCAL");
(void) set_config_option(stmt->name,
ExtractSetVariableArgs(stmt),
(superuser() ? PGC_SUSET : PGC_USERSET),
@@ -6295,7 +6295,7 @@ ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel)
{
ListCell *head;
- RequireTransactionChain(isTopLevel, "SET TRANSACTION");
+ WarnNoTransactionChain(isTopLevel, "SET TRANSACTION");
foreach(head, stmt->args)
{
@@ -6346,7 +6346,7 @@ ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel)
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("SET LOCAL TRANSACTION SNAPSHOT is not implemented")));
- RequireTransactionChain(isTopLevel, "SET TRANSACTION");
+ WarnNoTransactionChain(isTopLevel, "SET TRANSACTION");
Assert(IsA(con, A_Const));
Assert(nodeTag(&con->val) == T_String);
ImportSnapshot(strVal(&con->val));
@@ -6357,11 +6357,11 @@ ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel)
break;
case VAR_SET_DEFAULT:
if (stmt->is_local)
- RequireTransactionChain(isTopLevel, "SET LOCAL");
+ WarnNoTransactionChain(isTopLevel, "SET LOCAL");
/* fall through */
case VAR_RESET:
if (strcmp(stmt->name, "transaction_isolation") == 0)
- RequireTransactionChain(isTopLevel, "RESET TRANSACTION");
+ WarnNoTransactionChain(isTopLevel, "RESET TRANSACTION");
(void) set_config_option(stmt->name,
NULL,
diff --git a/src/include/access/xact.h b/src/include/access/xact.h
index 835f6acbee0..1d3e7d8938a 100644
--- a/src/include/access/xact.h
+++ b/src/include/access/xact.h
@@ -245,6 +245,7 @@ extern char TransactionBlockStatusCode(void);
extern void AbortOutOfAnyTransaction(void);
extern void PreventTransactionChain(bool isTopLevel, const char *stmtType);
extern void RequireTransactionChain(bool isTopLevel, const char *stmtType);
+extern void WarnNoTransactionChain(bool isTopLevel, const char *stmtType);
extern bool IsInTransactionChain(bool isTopLevel);
extern void RegisterXactCallback(XactCallback callback, void *arg);
extern void UnregisterXactCallback(XactCallback callback, void *arg);
diff --git a/src/test/regress/expected/errors.out b/src/test/regress/expected/errors.out
index fa0bd82819a..40615129778 100644
--- a/src/test/regress/expected/errors.out
+++ b/src/test/regress/expected/errors.out
@@ -114,7 +114,7 @@ ERROR: column name "oid" conflicts with a system column name
-- TRANSACTION STUFF
-- not in a xact
abort;
-NOTICE: there is no transaction in progress
+WARNING: there is no transaction in progress
-- not in a xact
end;
WARNING: there is no transaction in progress
diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out
index 203fa6ef8ea..4f0065cb7ef 100644
--- a/src/test/regress/expected/guc.out
+++ b/src/test/regress/expected/guc.out
@@ -29,7 +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
+WARNING: SET LOCAL can only be used in transaction blocks
SHOW vacuum_cost_delay;
vacuum_cost_delay
-------------------
@@ -37,7 +37,7 @@ SHOW vacuum_cost_delay;
(1 row)
SET LOCAL datestyle = 'SQL';
-ERROR: SET LOCAL can only be used in transaction blocks
+WARNING: SET LOCAL can only be used in transaction blocks
SHOW datestyle;
DateStyle
-----------