diff --git a/doc/src/sgml/ref/commit.sgml b/doc/src/sgml/ref/commit.sgml index e4169cd2c6..5f244cdd3c 100644 --- a/doc/src/sgml/ref/commit.sgml +++ b/doc/src/sgml/ref/commit.sgml @@ -77,7 +77,8 @@ COMMIT [ WORK | TRANSACTION ] [ AND [ NO ] CHAIN ] Issuing COMMIT when not inside a transaction does - no harm, but it will provoke a warning message. + no harm, but it will provoke a warning message. COMMIT AND + CHAIN when not inside a transaction is an error. diff --git a/doc/src/sgml/ref/rollback.sgml b/doc/src/sgml/ref/rollback.sgml index a5bbf25221..1357eaa832 100644 --- a/doc/src/sgml/ref/rollback.sgml +++ b/doc/src/sgml/ref/rollback.sgml @@ -76,7 +76,8 @@ ROLLBACK [ WORK | TRANSACTION ] [ AND [ NO ] CHAIN ] Issuing ROLLBACK outside of a transaction - block emits a warning and otherwise has no effect. + block emits a warning and otherwise has no effect. ROLLBACK AND + CHAIN outside of a transaction block is an error. diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c index f594d33e7a..9162286c98 100644 --- a/src/backend/access/transam/xact.c +++ b/src/backend/access/transam/xact.c @@ -3721,13 +3721,21 @@ EndTransactionBlock(bool chain) break; /* - * In an implicit transaction block, commit, but issue a warning + * We are in an implicit transaction block. If AND CHAIN was + * specified, error. 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,24 @@ EndTransactionBlock(bool chain) 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 CHAIN, 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. + * put us back into the default state. For COMMIT AND CHAIN, + * error. */ 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 +3916,10 @@ UserAbortTransactionBlock(bool chain) 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 CHAIN, issue a WARNING and go to abort state. + * The upcoming call to CommitTransactionCommand() will then put + * us back into the default state. For ROLLBACK AND CHAIN, error. * * We do the same thing with ABORT inside an implicit transaction, * although in this case we might be rolling back actual database @@ -3911,9 +3928,16 @@ UserAbortTransactionBlock(bool chain) */ 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; diff --git a/src/test/regress/expected/transactions.out b/src/test/regress/expected/transactions.out index 1b316cc9b8..213de4be6b 100644 --- a/src/test/regress/expected/transactions.out +++ b/src/test/regress/expected/transactions.out @@ -839,6 +839,11 @@ SHOW transaction_deferrable; (1 row) ROLLBACK; +-- not allowed 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 SELECT * FROM abc ORDER BY 1; a --- @@ -934,6 +939,79 @@ SELECT 2\; RELEASE SAVEPOINT sp\; SELECT 3; ERROR: RELEASE SAVEPOINT can only be used in transaction blocks -- but this is OK, because the BEGIN converts it to a regular xact SELECT 1\; BEGIN\; SAVEPOINT sp\; ROLLBACK TO SAVEPOINT sp\; COMMIT; +-- Tests for AND CHAIN in implicit transaction blocks +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 +----------------------- + off +(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 +----------------------- + off +(1 row) + +CREATE TABLE abc (a int); +-- COMMIT/ROLLBACK + COMMIT/ROLLBACK AND CHAIN +INSERT INTO abc VALUES (7)\; COMMIT\; INSERT INTO abc VALUES (8)\; COMMIT AND CHAIN; -- 7 commit, 8 error +WARNING: there is no transaction in progress +ERROR: COMMIT AND CHAIN can only be used in transaction blocks +INSERT INTO abc VALUES (9)\; ROLLBACK\; INSERT INTO abc VALUES (10)\; ROLLBACK AND CHAIN; -- 9 rollback, 10 error +WARNING: there is no transaction in progress +ERROR: ROLLBACK AND CHAIN can only be used in transaction blocks +-- COMMIT/ROLLBACK AND CHAIN + COMMIT/ROLLBACK +INSERT INTO abc VALUES (11)\; COMMIT AND CHAIN\; INSERT INTO abc VALUES (12)\; COMMIT; -- 11 error, 12 not reached +ERROR: COMMIT AND CHAIN can only be used in transaction blocks +INSERT INTO abc VALUES (13)\; ROLLBACK AND CHAIN\; INSERT INTO abc VALUES (14)\; ROLLBACK; -- 13 error, 14 not reached +ERROR: ROLLBACK AND CHAIN can only be used in transaction blocks +-- START TRANSACTION + COMMIT/ROLLBACK AND CHAIN +START TRANSACTION ISOLATION LEVEL REPEATABLE READ\; INSERT INTO abc VALUES (15)\; COMMIT AND CHAIN; -- 15 ok +SHOW transaction_isolation; -- transaction is active at this point + transaction_isolation +----------------------- + repeatable read +(1 row) + +COMMIT; +START TRANSACTION ISOLATION LEVEL REPEATABLE READ\; INSERT INTO abc VALUES (16)\; ROLLBACK AND CHAIN; -- 16 ok +SHOW transaction_isolation; -- transaction is active at this point + transaction_isolation +----------------------- + repeatable read +(1 row) + +ROLLBACK; +-- START TRANSACTION + COMMIT/ROLLBACK + COMMIT/ROLLBACK AND CHAIN +START TRANSACTION ISOLATION LEVEL REPEATABLE READ\; INSERT INTO abc VALUES (17)\; COMMIT\; INSERT INTO abc VALUES (18)\; COMMIT AND CHAIN; -- 17 commit, 18 error +ERROR: COMMIT AND CHAIN can only be used in transaction blocks +SHOW transaction_isolation; -- out of transaction block + transaction_isolation +----------------------- + read committed +(1 row) + +START TRANSACTION ISOLATION LEVEL REPEATABLE READ\; INSERT INTO abc VALUES (19)\; ROLLBACK\; INSERT INTO abc VALUES (20)\; ROLLBACK AND CHAIN; -- 19 rollback, 20 error +ERROR: ROLLBACK AND CHAIN can only be used in transaction blocks +SHOW transaction_isolation; -- out of transaction block + transaction_isolation +----------------------- + read committed +(1 row) + +SELECT * FROM abc ORDER BY 1; + a +---- + 7 + 15 + 17 +(3 rows) + +DROP TABLE abc; -- Test for successful cleanup of an aborted transaction at session exit. -- THIS MUST BE THE LAST TEST IN THIS FILE. begin; diff --git a/src/test/regress/sql/transactions.sql b/src/test/regress/sql/transactions.sql index 812e40a1a3..ba96f0f36f 100644 --- a/src/test/regress/sql/transactions.sql +++ b/src/test/regress/sql/transactions.sql @@ -475,6 +475,10 @@ SHOW transaction_read_only; SHOW transaction_deferrable; ROLLBACK; +-- not allowed outside a transaction block +COMMIT AND CHAIN; -- error +ROLLBACK AND CHAIN; -- error + SELECT * FROM abc ORDER BY 1; RESET default_transaction_read_only; @@ -536,6 +540,45 @@ SELECT 2\; RELEASE SAVEPOINT sp\; SELECT 3; SELECT 1\; BEGIN\; SAVEPOINT sp\; ROLLBACK TO SAVEPOINT sp\; COMMIT; +-- Tests for AND CHAIN in implicit transaction blocks + +SET TRANSACTION READ WRITE\; COMMIT AND CHAIN; -- error +SHOW transaction_read_only; + +SET TRANSACTION READ WRITE\; ROLLBACK AND CHAIN; -- error +SHOW transaction_read_only; + +CREATE TABLE abc (a int); + +-- COMMIT/ROLLBACK + COMMIT/ROLLBACK AND CHAIN +INSERT INTO abc VALUES (7)\; COMMIT\; INSERT INTO abc VALUES (8)\; COMMIT AND CHAIN; -- 7 commit, 8 error +INSERT INTO abc VALUES (9)\; ROLLBACK\; INSERT INTO abc VALUES (10)\; ROLLBACK AND CHAIN; -- 9 rollback, 10 error + +-- COMMIT/ROLLBACK AND CHAIN + COMMIT/ROLLBACK +INSERT INTO abc VALUES (11)\; COMMIT AND CHAIN\; INSERT INTO abc VALUES (12)\; COMMIT; -- 11 error, 12 not reached +INSERT INTO abc VALUES (13)\; ROLLBACK AND CHAIN\; INSERT INTO abc VALUES (14)\; ROLLBACK; -- 13 error, 14 not reached + +-- START TRANSACTION + COMMIT/ROLLBACK AND CHAIN +START TRANSACTION ISOLATION LEVEL REPEATABLE READ\; INSERT INTO abc VALUES (15)\; COMMIT AND CHAIN; -- 15 ok +SHOW transaction_isolation; -- transaction is active at this point +COMMIT; + +START TRANSACTION ISOLATION LEVEL REPEATABLE READ\; INSERT INTO abc VALUES (16)\; ROLLBACK AND CHAIN; -- 16 ok +SHOW transaction_isolation; -- transaction is active at this point +ROLLBACK; + +-- START TRANSACTION + COMMIT/ROLLBACK + COMMIT/ROLLBACK AND CHAIN +START TRANSACTION ISOLATION LEVEL REPEATABLE READ\; INSERT INTO abc VALUES (17)\; COMMIT\; INSERT INTO abc VALUES (18)\; COMMIT AND CHAIN; -- 17 commit, 18 error +SHOW transaction_isolation; -- out of transaction block + +START TRANSACTION ISOLATION LEVEL REPEATABLE READ\; INSERT INTO abc VALUES (19)\; ROLLBACK\; INSERT INTO abc VALUES (20)\; ROLLBACK AND CHAIN; -- 19 rollback, 20 error +SHOW transaction_isolation; -- out of transaction block + +SELECT * FROM abc ORDER BY 1; + +DROP TABLE abc; + + -- Test for successful cleanup of an aborted transaction at session exit. -- THIS MUST BE THE LAST TEST IN THIS FILE.