--- a/src/backend/access/transam/xact.c +++ b/src/backend/access/transam/xact.c @@ -3721,13 +3721,21 @@ break; /* - * In an implicit transaction block, commit, but issue a warning - * because there was no explicit BEGIN before this. + * We are in an implicit transaction block. If AND CHAIN was + * specified, we abort this transaction; otherwise commit, but + * issue a warning because there was no explicit BEGIN before this. */ case TBLOCK_IMPLICIT_INPROGRESS: - ereport(WARNING, - (errcode(ERRCODE_NO_ACTIVE_SQL_TRANSACTION), - errmsg("there is no transaction in progress"))); + if (chain) + ereport(ERROR, + (errcode(ERRCODE_NO_ACTIVE_SQL_TRANSACTION), + /* translator: %s represents an SQL statement name */ + errmsg("%s can only be used in transaction blocks", + "COMMIT AND CHAIN"))); + else + ereport(WARNING, + (errcode(ERRCODE_NO_ACTIVE_SQL_TRANSACTION), + errmsg("there is no transaction in progress"))); s->blockState = TBLOCK_END; result = true; break; @@ -3789,15 +3797,26 @@ break; /* - * The user issued COMMIT when not inside a transaction. Issue a - * WARNING, staying in TBLOCK_STARTED state. The upcoming call to + * The user issued COMMIT when not inside a transaction. For + * COMMIT without transaction chaining, we just issue a WARNING, + * staying in TBLOCK_STARTED state. The upcoming call to * CommitTransactionCommand() will then close the transaction and * put us back into the default state. + * + * We disallow transaction chaining outside an explicit transaction + * block, so issue an ERROR if the user told us to do that. */ case TBLOCK_STARTED: - ereport(WARNING, - (errcode(ERRCODE_NO_ACTIVE_SQL_TRANSACTION), - errmsg("there is no transaction in progress"))); + if (chain) + ereport(ERROR, + (errcode(ERRCODE_NO_ACTIVE_SQL_TRANSACTION), + /* translator: %s represents an SQL statement name */ + errmsg("%s can only be used in transaction blocks", + "COMMIT AND CHAIN"))); + else + ereport(WARNING, + (errcode(ERRCODE_NO_ACTIVE_SQL_TRANSACTION), + errmsg("there is no transaction in progress"))); result = true; break; @@ -3899,10 +3918,13 @@ break; /* - * The user issued ABORT when not inside a transaction. Issue a - * WARNING and go to abort state. The upcoming call to - * CommitTransactionCommand() will then put us back into the - * default state. + * The user issued ABORT when not inside a transaction. For + * ROLLBACK without transaction chaining, issue a WARNING and go + * to abort state. The upcoming call to CommitTransactionCommand() + * will then put us back into the default state. + * + * We disallow transaction chaining outside an explicit transaction + * block, so issue an ERROR if the user told us to do that. * * We do the same thing with ABORT inside an implicit transaction, * although in this case we might be rolling back actual database @@ -3911,9 +3933,16 @@ */ case TBLOCK_STARTED: case TBLOCK_IMPLICIT_INPROGRESS: - ereport(WARNING, - (errcode(ERRCODE_NO_ACTIVE_SQL_TRANSACTION), - errmsg("there is no transaction in progress"))); + if (chain) + ereport(ERROR, + (errcode(ERRCODE_NO_ACTIVE_SQL_TRANSACTION), + /* translator: %s represents an SQL statement name */ + errmsg("%s can only be used in transaction blocks", + "ROLLBACK AND CHAIN"))); + else + ereport(WARNING, + (errcode(ERRCODE_NO_ACTIVE_SQL_TRANSACTION), + errmsg("there is no transaction in progress"))); s->blockState = TBLOCK_ABORT_PENDING; break; --- a/src/test/regress/expected/transactions.out +++ b/src/test/regress/expected/transactions.out @@ -839,6 +839,28 @@ (1 row) ROLLBACK; +-- transaction chaining should not be used outside a transaction block +COMMIT AND CHAIN; -- error +ERROR: COMMIT AND CHAIN can only be used in transaction blocks +ROLLBACK AND CHAIN; -- error +ERROR: ROLLBACK AND CHAIN can only be used in transaction blocks +-- implicit transaction should not be chained as well +SET TRANSACTION READ WRITE\; COMMIT AND CHAIN; -- error +ERROR: COMMIT AND CHAIN can only be used in transaction blocks +SHOW transaction_read_only; + transaction_read_only +----------------------- + on +(1 row) + +SET TRANSACTION READ WRITE\; ROLLBACK AND CHAIN; -- error +ERROR: ROLLBACK AND CHAIN can only be used in transaction blocks +SHOW transaction_read_only; + transaction_read_only +----------------------- + on +(1 row) + SELECT * FROM abc ORDER BY 1; a --- --- a/src/test/regress/sql/transactions.sql +++ b/src/test/regress/sql/transactions.sql @@ -475,6 +475,17 @@ SHOW transaction_deferrable; ROLLBACK; +-- transaction chaining should not be used outside a transaction block +COMMIT AND CHAIN; -- error +ROLLBACK AND CHAIN; -- error + +-- implicit transaction should not be chained as well +SET TRANSACTION READ WRITE\; COMMIT AND CHAIN; -- error +SHOW transaction_read_only; + +SET TRANSACTION READ WRITE\; ROLLBACK AND CHAIN; -- error +SHOW transaction_read_only; + SELECT * FROM abc ORDER BY 1; RESET default_transaction_read_only;