--- a/src/backend/access/transam/xact.c +++ b/src/backend/access/transam/xact.c @@ -3725,9 +3725,17 @@ * 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"))); + /* We disallow transaction chaining outside an explicit transaction block */ + 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; @@ -3795,9 +3803,17 @@ * put us back into the default state. */ case TBLOCK_STARTED: - ereport(WARNING, - (errcode(ERRCODE_NO_ACTIVE_SQL_TRANSACTION), - errmsg("there is no transaction in progress"))); + /* We disallow transaction chaining outside an explicit transaction block */ + 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; @@ -3911,9 +3927,17 @@ */ case TBLOCK_STARTED: case TBLOCK_IMPLICIT_INPROGRESS: - ereport(WARNING, - (errcode(ERRCODE_NO_ACTIVE_SQL_TRANSACTION), - errmsg("there is no transaction in progress"))); + /* We disallow transaction chaining outside an explicit transaction block */ + 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;