Commit cc813fc2 authored by Tom Lane's avatar Tom Lane

Replace nested-BEGIN syntax for subtransactions with spec-compliant

SAVEPOINT/RELEASE/ROLLBACK-TO syntax.  (Alvaro)
Cause COMMIT of a failed transaction to report ROLLBACK instead of
COMMIT in its command tag.  (Tom)
Fix a few loose ends in the nested-transactions stuff.
parent b1ee9388
This diff is collapsed.
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.120 2004/07/01 21:17:13 tgl Exp $
* $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.121 2004/07/27 05:10:51 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -1181,18 +1181,16 @@ _SPI_execute(const char *src, int tcount, _SPI_plan *plan)
res = SPI_ERROR_CURSOR;
goto fail;
}
else if (IsA(queryTree->utilityStmt, TransactionStmt))
{
res = SPI_ERROR_TRANSACTION;
goto fail;
}
res = SPI_OK_UTILITY;
if (plan == NULL)
{
ProcessUtility(queryTree->utilityStmt, dest, NULL);
if (IsA(queryTree->utilityStmt, TransactionStmt))
{
CommitTransactionCommand();
StartTransactionCommand();
}
else
CommandCounterIncrement();
CommandCounterIncrement();
}
}
else if (plan == NULL)
......@@ -1308,14 +1306,7 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
{
ProcessUtility(queryTree->utilityStmt, dest, NULL);
res = SPI_OK_UTILITY;
if (IsA(queryTree->utilityStmt, TransactionStmt))
{
CommitTransactionCommand();
StartTransactionCommand();
}
else
CommandCounterIncrement();
CommandCounterIncrement();
}
else
{
......
......@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.467 2004/07/12 05:37:44 tgl Exp $
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.468 2004/07/27 05:10:55 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
......@@ -386,11 +386,11 @@ static void doNegateFloat(Value *v);
QUOTE
READ REAL RECHECK REFERENCES REINDEX RELATIVE_P RENAME REPEATABLE REPLACE
RESET RESTART RESTRICT RETURNS REVOKE RIGHT ROLLBACK ROW ROWS
RULE
READ REAL RECHECK REFERENCES REINDEX RELATIVE_P RELEASE RENAME
REPEATABLE REPLACE RESET RESTART RESTRICT RETURNS REVOKE RIGHT
ROLLBACK ROW ROWS RULE
SCHEMA SCROLL SECOND_P SECURITY SELECT SEQUENCE
SAVEPOINT SCHEMA SCROLL SECOND_P SECURITY SELECT SEQUENCE
SERIALIZABLE SESSION SESSION_USER SET SETOF SHARE
SHOW SIMILAR SIMPLE SMALLINT SOME STABLE START STATEMENT
STATISTICS STDIN STDOUT STORAGE STRICT_P SUBSTRING SYSID
......@@ -3961,6 +3961,30 @@ TransactionStmt:
n->options = NIL;
$$ = (Node *)n;
}
| SAVEPOINT ColId
{
TransactionStmt *n = makeNode(TransactionStmt);
n->kind = TRANS_STMT_SAVEPOINT;
n->options = list_make1(makeDefElem("savepoint_name",
(Node *)makeString($2)));
$$ = (Node *)n;
}
| RELEASE ColId
{
TransactionStmt *n = makeNode(TransactionStmt);
n->kind = TRANS_STMT_RELEASE;
n->options = list_make1(makeDefElem("savepoint_name",
(Node *)makeString($2)));
$$ = (Node *)n;
}
| ROLLBACK TO ColId
{
TransactionStmt *n = makeNode(TransactionStmt);
n->kind = TRANS_STMT_ROLLBACK_TO;
n->options = list_make1(makeDefElem("savepoint_name",
(Node *)makeString($3)));
$$ = (Node *)n;
}
;
opt_transaction: WORK {}
......@@ -7688,6 +7712,7 @@ unreserved_keyword:
| RECHECK
| REINDEX
| RELATIVE_P
| RELEASE
| RENAME
| REPEATABLE
| REPLACE
......@@ -7699,6 +7724,7 @@ unreserved_keyword:
| ROLLBACK
| ROWS
| RULE
| SAVEPOINT
| SCHEMA
| SCROLL
| SECOND_P
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.151 2004/07/12 05:37:44 tgl Exp $
* $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.152 2004/07/27 05:10:55 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -254,6 +254,7 @@ static const ScanKeyword ScanKeywords[] = {
{"references", REFERENCES},
{"reindex", REINDEX},
{"relative", RELATIVE_P},
{"release", RELEASE},
{"rename", RENAME},
{"repeatable", REPEATABLE},
{"replace", REPLACE},
......@@ -267,6 +268,7 @@ static const ScanKeyword ScanKeywords[] = {
{"row", ROW},
{"rows", ROWS},
{"rule", RULE},
{"savepoint", SAVEPOINT},
{"schema", SCHEMA},
{"scroll", SCROLL},
{"second", SECOND_P},
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/storage/lmgr/lmgr.c,v 1.64 2004/07/01 00:50:59 tgl Exp $
* $PostgreSQL: pgsql/src/backend/storage/lmgr/lmgr.c,v 1.65 2004/07/27 05:10:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -334,21 +334,23 @@ XactLockTableInsert(TransactionId xid)
* XactLockTableWait
*
* Wait for the specified transaction to commit or abort.
* We actually wait on the topmost transaction of the transaction tree.
*
* Note that this does the right thing for subtransactions: if we
* wait on a subtransaction, we will be awakened as soon as it aborts
* or its parent commits.
*/
void
XactLockTableWait(TransactionId xid)
{
LOCKTAG tag;
TransactionId myxid = GetCurrentTransactionId();
TransactionId waitXid = SubTransGetTopmostTransaction(xid);
Assert(!SubTransXidsHaveCommonAncestor(waitXid, myxid));
Assert(!SubTransXidsHaveCommonAncestor(xid, myxid));
MemSet(&tag, 0, sizeof(tag));
tag.relId = XactLockTableId;
tag.dbId = InvalidOid;
tag.objId.xid = waitXid;
tag.objId.xid = xid;
if (!LockAcquire(LockTableId, &tag, myxid,
ShareLock, false))
......@@ -358,13 +360,8 @@ XactLockTableWait(TransactionId xid)
/*
* Transaction was committed/aborted/crashed - we have to update
* pg_clog if transaction is still marked as running. If it's a
* subtransaction, we can update the parent status too.
* pg_clog if transaction is still marked as running.
*/
if (!TransactionIdDidCommit(waitXid) && !TransactionIdDidAbort(waitXid))
{
TransactionIdAbort(waitXid);
if (waitXid != xid)
TransactionIdAbort(xid);
}
if (!TransactionIdDidCommit(xid) && !TransactionIdDidAbort(xid))
TransactionIdAbort(xid);
}
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.424 2004/07/17 03:29:00 tgl Exp $
* $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.425 2004/07/27 05:11:03 tgl Exp $
*
* NOTES
* this is the "main" module of the postgres backend and
......@@ -841,8 +841,8 @@ exec_simple_query(const char *query_string)
TransactionStmt *stmt = (TransactionStmt *) parsetree;
if (stmt->kind == TRANS_STMT_COMMIT ||
stmt->kind == TRANS_STMT_BEGIN ||
stmt->kind == TRANS_STMT_ROLLBACK)
stmt->kind == TRANS_STMT_ROLLBACK ||
stmt->kind == TRANS_STMT_ROLLBACK_TO)
allowit = true;
}
......@@ -1162,8 +1162,8 @@ exec_parse_message(const char *query_string, /* string to execute */
TransactionStmt *stmt = (TransactionStmt *) parsetree;
if (stmt->kind == TRANS_STMT_COMMIT ||
stmt->kind == TRANS_STMT_BEGIN ||
stmt->kind == TRANS_STMT_ROLLBACK)
stmt->kind == TRANS_STMT_ROLLBACK ||
stmt->kind == TRANS_STMT_ROLLBACK_TO)
allowit = true;
}
......@@ -1625,8 +1625,8 @@ exec_execute_message(const char *portal_name, long max_rows)
is_trans_stmt = true;
if (stmt->kind == TRANS_STMT_COMMIT ||
stmt->kind == TRANS_STMT_BEGIN ||
stmt->kind == TRANS_STMT_ROLLBACK)
stmt->kind == TRANS_STMT_ROLLBACK ||
stmt->kind == TRANS_STMT_ROLLBACK_TO)
is_trans_exit = true;
}
}
......@@ -2810,6 +2810,9 @@ PostgresMain(int argc, char *argv[], const char *username)
*/
MemoryContextSwitchTo(ErrorContext);
/* Make sure we are using a sane ResourceOwner, too */
CurrentResourceOwner = CurTransactionResourceOwner;
/* Do the recovery */
ereport(DEBUG2,
(errmsg_internal("AbortCurrentTransaction")));
......
......@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.220 2004/06/25 21:55:57 tgl Exp $
* $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.221 2004/07/27 05:11:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -354,12 +354,51 @@ ProcessUtility(Node *parsetree,
break;
case TRANS_STMT_COMMIT:
EndTransactionBlock();
if (!EndTransactionBlock())
{
/* report unsuccessful commit in completionTag */
if (completionTag)
strcpy(completionTag, "ROLLBACK");
}
break;
case TRANS_STMT_ROLLBACK:
UserAbortTransactionBlock();
break;
case TRANS_STMT_SAVEPOINT:
{
ListCell *cell;
char *name = NULL;
RequireTransactionChain((void *)stmt, "SAVEPOINT");
foreach (cell, stmt->options)
{
DefElem *elem = lfirst(cell);
if (strcmp(elem->defname, "savepoint_name") == 0)
name = strVal(elem->arg);
}
Assert(PointerIsValid(name));
DefineSavepoint(name);
}
break;
case TRANS_STMT_RELEASE:
RequireTransactionChain((void *)stmt, "RELEASE");
ReleaseSavepoint(stmt->options);
break;
case TRANS_STMT_ROLLBACK_TO:
RequireTransactionChain((void *)stmt, "ROLLBACK TO");
RollbackToSavepoint(stmt->options);
/*
* CommitTransactionCommand is in charge
* of re-defining the savepoint again
*/
break;
}
}
break;
......@@ -1114,9 +1153,18 @@ CreateCommandTag(Node *parsetree)
break;
case TRANS_STMT_ROLLBACK:
case TRANS_STMT_ROLLBACK_TO:
tag = "ROLLBACK";
break;
case TRANS_STMT_SAVEPOINT:
tag = "SAVEPOINT";
break;
case TRANS_STMT_RELEASE:
tag = "RELEASE";
break;
default:
tag = "???";
break;
......
......@@ -3,7 +3,7 @@
*
* Copyright (c) 2000-2003, PostgreSQL Global Development Group
*
* $PostgreSQL: pgsql/src/bin/psql/tab-complete.c,v 1.107 2004/05/26 13:56:55 momjian Exp $
* $PostgreSQL: pgsql/src/bin/psql/tab-complete.c,v 1.108 2004/07/27 05:11:11 tgl Exp $
*/
/*----------------------------------------------------------------------
......@@ -463,8 +463,8 @@ psql_completion(char *text, int start, int end)
"ABORT", "ALTER", "ANALYZE", "BEGIN", "CHECKPOINT", "CLOSE", "CLUSTER", "COMMENT",
"COMMIT", "COPY", "CREATE", "DEALLOCATE", "DECLARE", "DELETE", "DROP", "EXECUTE",
"EXPLAIN", "FETCH", "GRANT", "INSERT", "LISTEN", "LOAD", "LOCK", "MOVE", "NOTIFY",
"PREPARE", "REINDEX", "RESET", "REVOKE", "ROLLBACK", "SELECT", "SET", "SHOW", "START",
"TRUNCATE", "UNLISTEN", "UPDATE", "VACUUM", NULL
"PREPARE", "REINDEX", "RELEASE", "RESET", "REVOKE", "ROLLBACK", "SAVEPOINT",
"SELECT", "SET", "SHOW", "START", "TRUNCATE", "UNLISTEN", "UPDATE", "VACUUM", NULL
};
static const char * const pgsql_variables[] = {
......@@ -722,16 +722,23 @@ psql_completion(char *text, int start, int end)
else if (pg_strcasecmp(prev2_wd, "ANALYZE") == 0)
COMPLETE_WITH_CONST(";");
/* BEGIN, COMMIT, ROLLBACK, ABORT, */
/* BEGIN, COMMIT, ABORT */
else if (pg_strcasecmp(prev_wd, "BEGIN") == 0 ||
pg_strcasecmp(prev_wd, "END") == 0 ||
pg_strcasecmp(prev_wd, "COMMIT") == 0 ||
pg_strcasecmp(prev_wd, "ROLLBACK") == 0 ||
pg_strcasecmp(prev_wd, "ABORT") == 0)
{
static const char * const list_TRANS[] =
{"WORK", "TRANSACTION", NULL};
COMPLETE_WITH_LIST(list_TRANS);
}
/* ROLLBACK*/
else if ( pg_strcasecmp(prev_wd, "ROLLBACK") == 0 )
{
static const char * const list_TRANS[] =
{"WORK", "TRANSACTION", "TO", NULL};
COMPLETE_WITH_LIST(list_TRANS);
}
/* CLUSTER */
......
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/access/xact.h,v 1.66 2004/07/21 22:31:25 tgl Exp $
* $PostgreSQL: pgsql/src/include/access/xact.h,v 1.67 2004/07/27 05:11:24 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -16,6 +16,7 @@
#include "access/xlog.h"
#include "storage/relfilenode.h"
#include "nodes/pg_list.h"
#include "utils/nabstime.h"
......@@ -101,12 +102,16 @@ extern void StartTransactionCommand(void);
extern void CommitTransactionCommand(void);
extern void AbortCurrentTransaction(void);
extern void BeginTransactionBlock(void);
extern void EndTransactionBlock(void);
extern bool EndTransactionBlock(void);
extern void UserAbortTransactionBlock(void);
extern void ReleaseSavepoint(List *options);
extern void DefineSavepoint(char *name);
extern void RollbackToSavepoint(List *options);
extern void RollbackAndReleaseSavepoint(List *options);
extern bool IsSubTransaction(void);
extern bool IsTransactionBlock(void);
extern bool IsTransactionOrTransactionBlock(void);
extern char TransactionBlockStatusCode(void);
extern void UserAbortTransactionBlock(void);
extern void AbortOutOfAnyTransaction(void);
extern void PreventTransactionChain(void *stmtNode, const char *stmtType);
extern void RequireTransactionChain(void *stmtNode, const char *stmtType);
......
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.262 2004/07/12 05:38:11 tgl Exp $
* $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.263 2004/07/27 05:11:30 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -1514,14 +1514,17 @@ typedef enum TransactionStmtKind
TRANS_STMT_BEGIN,
TRANS_STMT_START, /* semantically identical to BEGIN */
TRANS_STMT_COMMIT,
TRANS_STMT_ROLLBACK
TRANS_STMT_ROLLBACK,
TRANS_STMT_SAVEPOINT,
TRANS_STMT_RELEASE,
TRANS_STMT_ROLLBACK_TO
} TransactionStmtKind;
typedef struct TransactionStmt
{
NodeTag type;
TransactionStmtKind kind; /* see above */
List *options; /* for BEGIN/START only */
List *options; /* for BEGIN/START and savepoint commands */
} TransactionStmt;
/* ----------------------
......
......@@ -11,7 +11,7 @@
*
* Copyright (c) 2003, PostgreSQL Global Development Group
*
* $PostgreSQL: pgsql/src/include/utils/errcodes.h,v 1.12 2004/06/01 21:49:22 tgl Exp $
* $PostgreSQL: pgsql/src/include/utils/errcodes.h,v 1.13 2004/07/27 05:11:35 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -213,6 +213,10 @@
#define ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED MAKE_SQLSTATE('3','9', 'P','0','1')
#define ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED MAKE_SQLSTATE('3','9', 'P','0','2')
/* Class 3B - Savepoint Exception */
#define ERRCODE_SAVEPOINT_EXCEPTION MAKE_SQLSTATE('3','B', '0','0','0')
#define ERRCODE_S_E_INVALID_SPECIFICATION MAKE_SQLSTATE('3','B', '0','0','1')
/* Class 3D - Invalid Catalog Name */
#define ERRCODE_INVALID_CATALOG_NAME MAKE_SQLSTATE('3','D', '0','0','0')
......
......@@ -74,13 +74,14 @@ SET SESSION CHARACTERISTICS AS TRANSACTION READ WRITE;
CREATE TABLE foobar (a int);
BEGIN;
CREATE TABLE foo (a int);
BEGIN;
SAVEPOINT one;
DROP TABLE foo;
CREATE TABLE bar (a int);
ROLLBACK;
BEGIN;
ROLLBACK TO one;
RELEASE one;
SAVEPOINT two;
CREATE TABLE baz (a int);
COMMIT;
RELEASE two;
drop TABLE foobar;
CREATE TABLE barbaz (a int);
COMMIT;
......@@ -105,18 +106,20 @@ SELECT * FROM baz; -- should be empty
-- inserts
BEGIN;
INSERT INTO foo VALUES (1);
BEGIN;
SAVEPOINT one;
INSERT into bar VALUES (1);
ERROR: relation "bar" does not exist
ROLLBACK;
BEGIN;
ROLLBACK TO one;
RELEASE one;
SAVEPOINT two;
INSERT into barbaz VALUES (1);
COMMIT;
BEGIN;
BEGIN;
RELEASE two;
SAVEPOINT three;
SAVEPOINT four;
INSERT INTO foo VALUES (2);
COMMIT;
ROLLBACK;
RELEASE four;
ROLLBACK TO three;
RELEASE three;
INSERT INTO foo VALUES (3);
COMMIT;
SELECT * FROM foo; -- should have 1 and 3
......@@ -132,53 +135,168 @@ SELECT * FROM barbaz; -- should have 1
1
(1 row)
-- check that starting a subxact in a failed xact or subxact works
-- test whole-tree commit
BEGIN;
SELECT 0/0; -- fail the outer xact
ERROR: division by zero
BEGIN;
SELECT 1; -- this should NOT work
ERROR: current transaction is aborted, commands ignored until end of transaction block
COMMIT;
SELECT 1; -- this should NOT work
ERROR: current transaction is aborted, commands ignored until end of transaction block
BEGIN;
SELECT 1; -- this should NOT work
ERROR: current transaction is aborted, commands ignored until end of transaction block
ROLLBACK;
SELECT 1; -- this should NOT work
ERROR: current transaction is aborted, commands ignored until end of transaction block
SAVEPOINT one;
SELECT foo;
ERROR: column "foo" does not exist
ROLLBACK TO one;
RELEASE one;
SAVEPOINT two;
CREATE TABLE savepoints (a int);
SAVEPOINT three;
INSERT INTO savepoints VALUES (1);
SAVEPOINT four;
INSERT INTO savepoints VALUES (2);
SAVEPOINT five;
INSERT INTO savepoints VALUES (3);
ROLLBACK TO five;
COMMIT;
SELECT 1; -- this should work
COMMIT; -- should not be in a transaction block
WARNING: there is no transaction in progress
SELECT * FROM savepoints;
a
---
1
2
(2 rows)
-- test whole-tree rollback
BEGIN;
SAVEPOINT one;
DELETE FROM savepoints WHERE a=1;
RELEASE one;
SAVEPOINT two;
DELETE FROM savepoints WHERE a=1;
SAVEPOINT three;
DELETE FROM savepoints WHERE a=2;
ROLLBACK;
COMMIT; -- should not be in a transaction block
WARNING: there is no transaction in progress
SELECT * FROM savepoints;
a
---
1
2
(2 rows)
-- test whole-tree commit on an aborted subtransaction
BEGIN;
INSERT INTO savepoints VALUES (4);
SAVEPOINT one;
INSERT INTO savepoints VALUES (5);
SELECT foo;
ERROR: column "foo" does not exist
COMMIT;
SELECT * FROM savepoints;
a
---
1
2
(2 rows)
BEGIN;
INSERT INTO savepoints VALUES (6);
SAVEPOINT one;
INSERT INTO savepoints VALUES (7);
RELEASE one;
INSERT INTO savepoints VALUES (8);
COMMIT;
-- rows 6 and 8 should have been created by the same xact
SELECT a.xmin = b.xmin FROM savepoints a, savepoints b WHERE a.a=6 AND b.a=8;
?column?
----------
1
t
(1 row)
-- rows 6 and 7 should have been created by different xacts
SELECT a.xmin = b.xmin FROM savepoints a, savepoints b WHERE a.a=6 AND b.a=7;
?column?
----------
f
(1 row)
BEGIN;
BEGIN;
SELECT 1; -- this should work
INSERT INTO savepoints VALUES (9);
SAVEPOINT one;
INSERT INTO savepoints VALUES (10);
ROLLBACK TO one;
INSERT INTO savepoints VALUES (11);
COMMIT;
SELECT a FROM savepoints WHERE a in (9, 10, 11);
a
----
9
11
(2 rows)
-- rows 9 and 11 should have been created by different xacts
SELECT a.xmin = b.xmin FROM savepoints a, savepoints b WHERE a.a=9 AND b.a=11;
?column?
----------
1
f
(1 row)
SELECT 0/0; -- fail the subxact
BEGIN;
INSERT INTO savepoints VALUES (12);
SAVEPOINT one;
INSERT INTO savepoints VALUES (13);
SAVEPOINT two;
INSERT INTO savepoints VALUES (14);
ROLLBACK TO one;
INSERT INTO savepoints VALUES (15);
SAVEPOINT two;
INSERT INTO savepoints VALUES (16);
SAVEPOINT three;
INSERT INTO savepoints VALUES (17);
COMMIT;
SELECT a FROM savepoints WHERE a BETWEEN 12 AND 17;
a
----
12
15
16
17
(4 rows)
BEGIN;
INSERT INTO savepoints VALUES (18);
SAVEPOINT one;
INSERT INTO savepoints VALUES (19);
SAVEPOINT two;
INSERT INTO savepoints VALUES (20);
ROLLBACK TO one;
INSERT INTO savepoints VALUES (21);
ROLLBACK TO one;
INSERT INTO savepoints VALUES (22);
COMMIT;
SELECT a FROM savepoints WHERE a BETWEEN 18 AND 22;
a
----
18
22
(2 rows)
DROP TABLE savepoints;
-- only in a transaction block:
SAVEPOINT one;
ERROR: SAVEPOINT may only be used in transaction blocks
ROLLBACK TO one;
ERROR: ROLLBACK TO may only be used in transaction blocks
RELEASE one;
ERROR: RELEASE may only be used in transaction blocks
-- Only "rollback to" allowed in aborted state
BEGIN;
SAVEPOINT one;
SELECT 0/0;
ERROR: division by zero
SELECT 1; -- this should NOT work
ERROR: current transaction is aborted, commands ignored until end of transaction block
BEGIN;
SELECT 1; -- this should NOT work
ERROR: current transaction is aborted, commands ignored until end of transaction block
ROLLBACK;
BEGIN;
SELECT 1; -- this should NOT work
SAVEPOINT two; -- ignored till the end of ...
ERROR: current transaction is aborted, commands ignored until end of transaction block
COMMIT;
SELECT 1; -- this should NOT work
RELEASE one; -- ignored till the end of ...
ERROR: current transaction is aborted, commands ignored until end of transaction block
ROLLBACK;
SELECT 1; -- this should work
ROLLBACK TO one;
SELECT 1;
?column?
----------
1
......@@ -194,7 +312,7 @@ SELECT 1; -- this should work
-- check non-transactional behavior of cursors
BEGIN;
DECLARE c CURSOR FOR SELECT unique2 FROM tenk1;
BEGIN;
SAVEPOINT one;
FETCH 10 FROM c;
unique2
---------
......@@ -210,8 +328,7 @@ BEGIN;
9
(10 rows)
ROLLBACK;
BEGIN;
ROLLBACK TO one;
FETCH 10 FROM c;
unique2
---------
......@@ -227,7 +344,7 @@ BEGIN;
19
(10 rows)
COMMIT;
RELEASE one;
FETCH 10 FROM c;
unique2
---------
......@@ -245,15 +362,15 @@ BEGIN;
CLOSE c;
DECLARE c CURSOR FOR SELECT unique2/0 FROM tenk1;
BEGIN;
SAVEPOINT two;
FETCH 10 FROM c;
ERROR: division by zero
ROLLBACK;
ROLLBACK TO two;
-- c is now dead to the world ...
BEGIN;
FETCH 10 FROM c;
ERROR: portal "c" cannot be run
ROLLBACK;
ROLLBACK TO two;
RELEASE two;
FETCH 10 FROM c;
ERROR: portal "c" cannot be run
COMMIT;
......
......@@ -61,13 +61,14 @@ SET SESSION CHARACTERISTICS AS TRANSACTION READ WRITE;
CREATE TABLE foobar (a int);
BEGIN;
CREATE TABLE foo (a int);
BEGIN;
SAVEPOINT one;
DROP TABLE foo;
CREATE TABLE bar (a int);
ROLLBACK;
BEGIN;
ROLLBACK TO one;
RELEASE one;
SAVEPOINT two;
CREATE TABLE baz (a int);
COMMIT;
RELEASE two;
drop TABLE foobar;
CREATE TABLE barbaz (a int);
COMMIT;
......@@ -80,76 +81,156 @@ SELECT * FROM baz; -- should be empty
-- inserts
BEGIN;
INSERT INTO foo VALUES (1);
BEGIN;
SAVEPOINT one;
INSERT into bar VALUES (1);
ROLLBACK;
BEGIN;
ROLLBACK TO one;
RELEASE one;
SAVEPOINT two;
INSERT into barbaz VALUES (1);
COMMIT;
BEGIN;
BEGIN;
RELEASE two;
SAVEPOINT three;
SAVEPOINT four;
INSERT INTO foo VALUES (2);
COMMIT;
ROLLBACK;
RELEASE four;
ROLLBACK TO three;
RELEASE three;
INSERT INTO foo VALUES (3);
COMMIT;
SELECT * FROM foo; -- should have 1 and 3
SELECT * FROM barbaz; -- should have 1
-- check that starting a subxact in a failed xact or subxact works
-- test whole-tree commit
BEGIN;
SELECT 0/0; -- fail the outer xact
BEGIN;
SELECT 1; -- this should NOT work
COMMIT;
SELECT 1; -- this should NOT work
BEGIN;
SELECT 1; -- this should NOT work
ROLLBACK;
SELECT 1; -- this should NOT work
SAVEPOINT one;
SELECT foo;
ROLLBACK TO one;
RELEASE one;
SAVEPOINT two;
CREATE TABLE savepoints (a int);
SAVEPOINT three;
INSERT INTO savepoints VALUES (1);
SAVEPOINT four;
INSERT INTO savepoints VALUES (2);
SAVEPOINT five;
INSERT INTO savepoints VALUES (3);
ROLLBACK TO five;
COMMIT;
SELECT 1; -- this should work
COMMIT; -- should not be in a transaction block
SELECT * FROM savepoints;
-- test whole-tree rollback
BEGIN;
SAVEPOINT one;
DELETE FROM savepoints WHERE a=1;
RELEASE one;
SAVEPOINT two;
DELETE FROM savepoints WHERE a=1;
SAVEPOINT three;
DELETE FROM savepoints WHERE a=2;
ROLLBACK;
COMMIT; -- should not be in a transaction block
SELECT * FROM savepoints;
-- test whole-tree commit on an aborted subtransaction
BEGIN;
INSERT INTO savepoints VALUES (4);
SAVEPOINT one;
INSERT INTO savepoints VALUES (5);
SELECT foo;
COMMIT;
SELECT * FROM savepoints;
BEGIN;
INSERT INTO savepoints VALUES (6);
SAVEPOINT one;
INSERT INTO savepoints VALUES (7);
RELEASE one;
INSERT INTO savepoints VALUES (8);
COMMIT;
-- rows 6 and 8 should have been created by the same xact
SELECT a.xmin = b.xmin FROM savepoints a, savepoints b WHERE a.a=6 AND b.a=8;
-- rows 6 and 7 should have been created by different xacts
SELECT a.xmin = b.xmin FROM savepoints a, savepoints b WHERE a.a=6 AND b.a=7;
BEGIN;
INSERT INTO savepoints VALUES (9);
SAVEPOINT one;
INSERT INTO savepoints VALUES (10);
ROLLBACK TO one;
INSERT INTO savepoints VALUES (11);
COMMIT;
SELECT a FROM savepoints WHERE a in (9, 10, 11);
-- rows 9 and 11 should have been created by different xacts
SELECT a.xmin = b.xmin FROM savepoints a, savepoints b WHERE a.a=9 AND b.a=11;
BEGIN;
INSERT INTO savepoints VALUES (12);
SAVEPOINT one;
INSERT INTO savepoints VALUES (13);
SAVEPOINT two;
INSERT INTO savepoints VALUES (14);
ROLLBACK TO one;
INSERT INTO savepoints VALUES (15);
SAVEPOINT two;
INSERT INTO savepoints VALUES (16);
SAVEPOINT three;
INSERT INTO savepoints VALUES (17);
COMMIT;
SELECT a FROM savepoints WHERE a BETWEEN 12 AND 17;
BEGIN;
INSERT INTO savepoints VALUES (18);
SAVEPOINT one;
INSERT INTO savepoints VALUES (19);
SAVEPOINT two;
INSERT INTO savepoints VALUES (20);
ROLLBACK TO one;
INSERT INTO savepoints VALUES (21);
ROLLBACK TO one;
INSERT INTO savepoints VALUES (22);
COMMIT;
SELECT a FROM savepoints WHERE a BETWEEN 18 AND 22;
DROP TABLE savepoints;
-- only in a transaction block:
SAVEPOINT one;
ROLLBACK TO one;
RELEASE one;
-- Only "rollback to" allowed in aborted state
BEGIN;
BEGIN;
SELECT 1; -- this should work
SELECT 0/0; -- fail the subxact
SELECT 1; -- this should NOT work
BEGIN;
SELECT 1; -- this should NOT work
ROLLBACK;
BEGIN;
SELECT 1; -- this should NOT work
COMMIT;
SELECT 1; -- this should NOT work
ROLLBACK;
SELECT 1; -- this should work
SAVEPOINT one;
SELECT 0/0;
SAVEPOINT two; -- ignored till the end of ...
RELEASE one; -- ignored till the end of ...
ROLLBACK TO one;
SELECT 1;
COMMIT;
SELECT 1; -- this should work
-- check non-transactional behavior of cursors
BEGIN;
DECLARE c CURSOR FOR SELECT unique2 FROM tenk1;
BEGIN;
SAVEPOINT one;
FETCH 10 FROM c;
ROLLBACK;
BEGIN;
ROLLBACK TO one;
FETCH 10 FROM c;
COMMIT;
RELEASE one;
FETCH 10 FROM c;
CLOSE c;
DECLARE c CURSOR FOR SELECT unique2/0 FROM tenk1;
BEGIN;
SAVEPOINT two;
FETCH 10 FROM c;
ROLLBACK;
ROLLBACK TO two;
-- c is now dead to the world ...
BEGIN;
FETCH 10 FROM c;
ROLLBACK;
ROLLBACK TO two;
RELEASE two;
FETCH 10 FROM c;
COMMIT;
DROP TABLE foo;
DROP TABLE baz;
DROP TABLE barbaz;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment