From 8b1b213204b72d731a8dd3374749d3e6862e073e Mon Sep 17 00:00:00 2001 From: Dave Cramer Date: Tue, 4 Aug 2020 12:15:38 -0400 Subject: [PATCH] Throw error and rollback on a failed transaction instead of silently rolling back --- src/backend/tcop/utility.c | 9 +++++++++ src/backend/utils/error/elog.c | 1 + src/include/utils/elog.h | 9 +++++---- src/test/regress/expected/copy2.out | 2 ++ src/test/regress/expected/create_index.out | 6 ++++++ src/test/regress/expected/foreign_key.out | 7 +++++++ src/test/regress/expected/insert_conflict.out | 3 +++ src/test/regress/expected/portals.out | 1 + src/test/regress/expected/prepared_xacts.out | 1 + src/test/regress/expected/privileges.out | 2 ++ src/test/regress/expected/subscription.out | 2 ++ src/test/regress/expected/transactions.out | 9 +++++++++ src/test/regress/output/constraints.source | 2 ++ 13 files changed, 50 insertions(+), 4 deletions(-) diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index 9b0c376c8c..ad462f00a6 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -621,6 +621,11 @@ standard_ProcessUtility(PlannedStmt *pstmt, /* report unsuccessful commit in qc */ if (qc) SetQueryCompletion(qc, CMDTAG_ROLLBACK, 0); + /* report USER_ERROR so that we don't completely exit the context */ + ereport(USER_ERROR, + (errcode(ERRCODE_TRANSACTION_ROLLBACK), + errmsg("current transaction failed, " + "rolling back"))); } break; @@ -630,6 +635,10 @@ standard_ProcessUtility(PlannedStmt *pstmt, /* report unsuccessful commit in qc */ if (qc) SetQueryCompletion(qc, CMDTAG_ROLLBACK, 0); + ereport(ERROR, + (errcode(ERRCODE_TRANSACTION_ROLLBACK), + errmsg("current transaction failed, " + "rolling back"))); } break; diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c index d0b368530e..b02a2dd20a 100644 --- a/src/backend/utils/error/elog.c +++ b/src/backend/utils/error/elog.c @@ -3414,6 +3414,7 @@ error_severity(int elevel) case WARNING: prefix = gettext_noop("WARNING"); break; + case USER_ERROR: case ERROR: prefix = gettext_noop("ERROR"); break; diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h index 1e09ee0541..d3acbeb420 100644 --- a/src/include/utils/elog.h +++ b/src/include/utils/elog.h @@ -40,17 +40,18 @@ #define WARNING 19 /* Warnings. NOTICE is for expected messages * like implicit sequence creation by SERIAL. * WARNING is for unexpected messages. */ -#define ERROR 20 /* user error - abort transaction; return to +#define USER_ERROR 20 +#define ERROR 21 /* user error - abort transaction; return to * known state */ /* Save ERROR value in PGERROR so it can be restored when Win32 includes * modify it. We have to use a constant rather than ERROR because macros * are expanded only when referenced outside macros. */ #ifdef WIN32 -#define PGERROR 20 +#define PGERROR 21 #endif -#define FATAL 21 /* fatal error - abort process */ -#define PANIC 22 /* take down the other backends with me */ +#define FATAL 22 /* fatal error - abort process */ +#define PANIC 23 /* take down the other backends with me */ /* #define DEBUG DEBUG1 */ /* Backward compatibility with pre-7.3 */ diff --git a/src/test/regress/expected/copy2.out b/src/test/regress/expected/copy2.out index e40287d25a..59a9246f0c 100644 --- a/src/test/regress/expected/copy2.out +++ b/src/test/regress/expected/copy2.out @@ -367,6 +367,7 @@ SAVEPOINT s1; COPY vistest FROM stdin CSV FREEZE; ERROR: cannot perform COPY FREEZE because the table was not created or truncated in the current subtransaction COMMIT; +ERROR: current transaction failed, rolling back BEGIN; INSERT INTO vistest VALUES ('z'); SAVEPOINT s1; @@ -375,6 +376,7 @@ ROLLBACK TO SAVEPOINT s1; COPY vistest FROM stdin CSV FREEZE; ERROR: cannot perform COPY FREEZE because the table was not created or truncated in the current subtransaction COMMIT; +ERROR: current transaction failed, rolling back CREATE FUNCTION truncate_in_subxact() RETURNS VOID AS $$ BEGIN diff --git a/src/test/regress/expected/create_index.out b/src/test/regress/expected/create_index.out index e3e6634d7e..92db8aa7b7 100644 --- a/src/test/regress/expected/create_index.out +++ b/src/test/regress/expected/create_index.out @@ -1393,6 +1393,7 @@ BEGIN; CREATE INDEX CONCURRENTLY concur_index7 ON concur_heap(f1); ERROR: CREATE INDEX CONCURRENTLY cannot run inside a transaction block COMMIT; +ERROR: current transaction failed, rolling back -- But you can do a regular index build in a transaction BEGIN; CREATE INDEX std_index on concur_heap(f2); @@ -1453,6 +1454,7 @@ INSERT INTO concur_temp VALUES (1, 'foo'), (2, 'bar'); CREATE INDEX CONCURRENTLY concur_temp_ind ON concur_temp(f1); ERROR: CREATE INDEX CONCURRENTLY cannot run inside a transaction block COMMIT; +ERROR: current transaction failed, rolling back -- ON COMMIT DELETE ROWS CREATE TEMP TABLE concur_temp (f1 int, f2 text) ON COMMIT DELETE ROWS; @@ -2327,6 +2329,7 @@ BEGIN; REINDEX TABLE CONCURRENTLY concur_reindex_tab; ERROR: REINDEX CONCURRENTLY cannot run inside a transaction block COMMIT; +ERROR: current transaction failed, rolling back REINDEX TABLE CONCURRENTLY pg_class; -- no catalog relation ERROR: cannot reindex system catalogs concurrently REINDEX INDEX CONCURRENTLY pg_class_oid_index; -- no catalog index @@ -2490,6 +2493,7 @@ BEGIN; REINDEX INDEX CONCURRENTLY concur_temp_ind_1; ERROR: REINDEX CONCURRENTLY cannot run inside a transaction block COMMIT; +ERROR: current transaction failed, rolling back -- ON COMMIT DELETE ROWS CREATE TEMP TABLE concur_temp_tab_2 (c1 int, c2 text) ON COMMIT DELETE ROWS; @@ -2506,6 +2510,7 @@ CREATE INDEX concur_temp_ind_3 ON concur_temp_tab_3(c2); REINDEX INDEX CONCURRENTLY concur_temp_ind_3; ERROR: REINDEX CONCURRENTLY cannot run inside a transaction block COMMIT; +ERROR: current transaction failed, rolling back -- REINDEX SCHEMA processes all temporary relations CREATE TABLE reindex_temp_before AS SELECT oid, relname, relfilenode, relkind, reltoastrelid @@ -2585,6 +2590,7 @@ BEGIN; REINDEX SCHEMA schema_to_reindex; -- failure, cannot run in a transaction ERROR: REINDEX SCHEMA cannot run inside a transaction block END; +ERROR: current transaction failed, rolling back -- concurrently REINDEX SCHEMA CONCURRENTLY schema_to_reindex; -- Failure for unauthorized user diff --git a/src/test/regress/expected/foreign_key.out b/src/test/regress/expected/foreign_key.out index 07bd5b6434..e6ef96670c 100644 --- a/src/test/regress/expected/foreign_key.out +++ b/src/test/regress/expected/foreign_key.out @@ -1052,6 +1052,7 @@ INSERT INTO fktable VALUES (500, 1000); ERROR: insert or update on table "fktable" violates foreign key constraint "fktable_fk_fkey" DETAIL: Key (fk)=(1000) is not present in table "pktable". COMMIT; +ERROR: current transaction failed, rolling back DROP TABLE fktable, pktable; -- tricky behavior: according to SQL99, if a deferred constraint is set -- to 'immediate' mode, it should be checked for validity *immediately*, @@ -1076,6 +1077,7 @@ DETAIL: Key (fk)=(2000) is not present in table "pktable". INSERT INTO pktable VALUES (2000, 3); -- too late ERROR: current transaction is aborted, commands ignored until end of transaction block COMMIT; +ERROR: current transaction failed, rolling back DROP TABLE fktable, pktable; -- deferrable, initially deferred CREATE TABLE pktable ( @@ -1231,12 +1233,14 @@ UPDATE pktable SET id = 10 WHERE id = 5; ERROR: update or delete on table "pktable" violates foreign key constraint "fktable_fk_fkey" on table "fktable" DETAIL: Key (id)=(5) is still referenced from table "fktable". COMMIT; +ERROR: current transaction failed, rolling back BEGIN; -- doesn't match PK, should throw error now INSERT INTO fktable VALUES (0, 20); ERROR: insert or update on table "fktable" violates foreign key constraint "fktable_fk_fkey" DETAIL: Key (fk)=(20) is not present in table "pktable". COMMIT; +ERROR: current transaction failed, rolling back -- try additional syntax ALTER TABLE fktable ALTER CONSTRAINT fktable_fk_fkey NOT DEFERRABLE; -- illegal option @@ -1489,11 +1493,13 @@ insert into fktable2 values(2); alter table fktable2 drop constraint fktable2_f1_fkey; ERROR: cannot ALTER TABLE "fktable2" because it has pending trigger events commit; +ERROR: current transaction failed, rolling back begin; delete from pktable2 where f1 = 1; alter table fktable2 drop constraint fktable2_f1_fkey; ERROR: cannot ALTER TABLE "pktable2" because it has pending trigger events commit; +ERROR: current transaction failed, rolling back drop table pktable2, fktable2; -- -- Test keys that "look" different but compare as equal @@ -2440,6 +2446,7 @@ INSERT INTO fkpart8.tbl2 VALUES(1); ALTER TABLE fkpart8.tbl2 DROP CONSTRAINT tbl2_f1_fkey; ERROR: cannot ALTER TABLE "tbl2_p1" because it has pending trigger events COMMIT; +ERROR: current transaction failed, rolling back DROP SCHEMA fkpart8 CASCADE; NOTICE: drop cascades to 2 other objects DETAIL: drop cascades to table fkpart8.tbl1 diff --git a/src/test/regress/expected/insert_conflict.out b/src/test/regress/expected/insert_conflict.out index 1338b2b23e..1ce531270d 100644 --- a/src/test/regress/expected/insert_conflict.out +++ b/src/test/regress/expected/insert_conflict.out @@ -704,16 +704,19 @@ insert into selfconflict values (4,1), (4,2) on conflict(f1) do update set f2 = ERROR: ON CONFLICT DO UPDATE command cannot affect row a second time HINT: Ensure that no rows proposed for insertion within the same command have duplicate constrained values. commit; +ERROR: current transaction failed, rolling back begin transaction isolation level repeatable read; insert into selfconflict values (5,1), (5,2) on conflict(f1) do update set f2 = 0; ERROR: ON CONFLICT DO UPDATE command cannot affect row a second time HINT: Ensure that no rows proposed for insertion within the same command have duplicate constrained values. commit; +ERROR: current transaction failed, rolling back begin transaction isolation level serializable; insert into selfconflict values (6,1), (6,2) on conflict(f1) do update set f2 = 0; ERROR: ON CONFLICT DO UPDATE command cannot affect row a second time HINT: Ensure that no rows proposed for insertion within the same command have duplicate constrained values. commit; +ERROR: current transaction failed, rolling back select * from selfconflict; f1 | f2 ----+---- diff --git a/src/test/regress/expected/portals.out b/src/test/regress/expected/portals.out index dc0d2ef7dd..6092d6acf7 100644 --- a/src/test/regress/expected/portals.out +++ b/src/test/regress/expected/portals.out @@ -715,6 +715,7 @@ FETCH BACKWARD 1 FROM foo24; -- should fail ERROR: cursor can only scan forward HINT: Declare it with SCROLL option to enable backward scan. END; +ERROR: current transaction failed, rolling back -- -- Cursors outside transaction blocks -- diff --git a/src/test/regress/expected/prepared_xacts.out b/src/test/regress/expected/prepared_xacts.out index eb77c18788..41e6bfd9b3 100644 --- a/src/test/regress/expected/prepared_xacts.out +++ b/src/test/regress/expected/prepared_xacts.out @@ -137,6 +137,7 @@ ERROR: could not serialize access due to read/write dependencies among transact DETAIL: Reason code: Canceled on identification as a pivot, during write. HINT: The transaction might succeed if retried. PREPARE TRANSACTION 'foo5'; +ERROR: current transaction failed, rolling back SELECT gid FROM pg_prepared_xacts; gid ------ diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out index 3ec22c20ea..fd4ed9d95b 100644 --- a/src/test/regress/expected/privileges.out +++ b/src/test/regress/expected/privileges.out @@ -114,6 +114,7 @@ BEGIN; LOCK atest2 IN ACCESS EXCLUSIVE MODE; -- fail ERROR: permission denied for table atest2 COMMIT; +ERROR: current transaction failed, rolling back COPY atest2 FROM stdin; -- fail ERROR: permission denied for table atest2 GRANT ALL ON atest1 TO PUBLIC; -- fail @@ -727,6 +728,7 @@ BEGIN; LOCK atestc; ERROR: permission denied for table atestc END; +ERROR: current transaction failed, rolling back -- privileges on functions, languages -- switch to superuser \c - diff --git a/src/test/regress/expected/subscription.out b/src/test/regress/expected/subscription.out index d71db0d520..4b215f0c8c 100644 --- a/src/test/regress/expected/subscription.out +++ b/src/test/regress/expected/subscription.out @@ -20,6 +20,7 @@ BEGIN; CREATE SUBSCRIPTION regress_testsub CONNECTION 'testconn' PUBLICATION testpub WITH (create_slot); ERROR: CREATE SUBSCRIPTION ... WITH (create_slot = true) cannot run inside a transaction block COMMIT; +ERROR: current transaction failed, rolling back -- fail - invalid connection string CREATE SUBSCRIPTION regress_testsub CONNECTION 'testconn' PUBLICATION testpub; ERROR: invalid connection string syntax: missing "=" after "testconn" in connection info string @@ -146,6 +147,7 @@ BEGIN; DROP SUBSCRIPTION regress_testsub; ERROR: DROP SUBSCRIPTION cannot run inside a transaction block COMMIT; +ERROR: current transaction failed, rolling back ALTER SUBSCRIPTION regress_testsub SET (slot_name = NONE); -- now it works BEGIN; diff --git a/src/test/regress/expected/transactions.out b/src/test/regress/expected/transactions.out index 1b03310029..89ca57709b 100644 --- a/src/test/regress/expected/transactions.out +++ b/src/test/regress/expected/transactions.out @@ -53,6 +53,7 @@ SELECT * FROM writetest; -- ok SET TRANSACTION READ WRITE; --fail ERROR: transaction read-write mode must be set before any query COMMIT; +ERROR: current transaction failed, rolling back BEGIN; SET TRANSACTION READ ONLY; -- ok SET TRANSACTION READ WRITE; -- ok @@ -73,6 +74,7 @@ SET TRANSACTION READ ONLY; -- ok SET TRANSACTION READ WRITE; --fail ERROR: cannot set transaction read-write mode inside a read-only transaction COMMIT; +ERROR: current transaction failed, rolling back BEGIN; SET TRANSACTION READ WRITE; -- ok SAVEPOINT x; @@ -87,6 +89,7 @@ SET TRANSACTION READ ONLY; -- ok SET TRANSACTION READ WRITE; --fail ERROR: cannot set transaction read-write mode inside a read-only transaction COMMIT; +ERROR: current transaction failed, rolling back BEGIN; SET TRANSACTION READ WRITE; -- ok SAVEPOINT x; @@ -271,6 +274,7 @@ ERROR: column "trans_foo" does not exist LINE 1: SELECT trans_foo; ^ COMMIT; +ERROR: current transaction failed, rolling back SELECT * FROM savepoints; a --- @@ -456,6 +460,7 @@ ERROR: portal "c" cannot be run FETCH 10 FROM c; ERROR: portal "c" cannot be run COMMIT; +ERROR: current transaction failed, rolling back -- -- Check that "stable" functions are really stable. They should not be -- able to see the partial results of the calling query. (Ideally we would @@ -586,6 +591,7 @@ rollback to x; fetch from foo; ERROR: cursor "foo" does not exist commit; +ERROR: current transaction failed, rolling back begin; create table abc (a int); insert into abc values (5); @@ -657,6 +663,7 @@ FETCH ok; -- should work FETCH ctt; -- must be rejected ERROR: portal "ctt" cannot be run COMMIT; +ERROR: current transaction failed, rolling back DROP FUNCTION create_temp_tab(); DROP FUNCTION invert(x float8); -- Tests for AND CHAIN @@ -710,6 +717,7 @@ LINE 1: INSERT INTO abc VALUES ('error'); INSERT INTO abc VALUES (3); -- check it's really aborted ERROR: current transaction is aborted, commands ignored until end of transaction block COMMIT AND CHAIN; -- TBLOCK_ABORT_END +ERROR: current transaction failed, rolling back SHOW transaction_isolation; transaction_isolation ----------------------- @@ -755,6 +763,7 @@ ERROR: invalid input syntax for type integer: "error" LINE 1: INSERT INTO abc VALUES ('error'); ^ COMMIT AND CHAIN; -- TBLOCK_ABORT_PENDING +ERROR: current transaction failed, rolling back SHOW transaction_isolation; transaction_isolation ----------------------- diff --git a/src/test/regress/output/constraints.source b/src/test/regress/output/constraints.source index b727c6150a..d0bcf962a8 100644 --- a/src/test/regress/output/constraints.source +++ b/src/test/regress/output/constraints.source @@ -541,6 +541,7 @@ INSERT INTO unique_tbl VALUES (3, 'Three'); -- should fail ERROR: duplicate key value violates unique constraint "unique_tbl_i_key" DETAIL: Key (i)=(3) already exists. COMMIT; +ERROR: current transaction failed, rolling back -- forced check when SET CONSTRAINTS is called BEGIN; SET CONSTRAINTS ALL DEFERRED; @@ -549,6 +550,7 @@ SET CONSTRAINTS ALL IMMEDIATE; -- should fail ERROR: duplicate key value violates unique constraint "unique_tbl_i_key" DETAIL: Key (i)=(3) already exists. COMMIT; +ERROR: current transaction failed, rolling back -- test deferrable UNIQUE with a partitioned table CREATE TABLE parted_uniq_tbl (i int UNIQUE DEFERRABLE) partition by range (i); CREATE TABLE parted_uniq_tbl_1 PARTITION OF parted_uniq_tbl FOR VALUES FROM (0) TO (10); -- 2.20.1 (Apple Git-117)