summaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/access/transam/xact.c73
-rw-r--r--src/backend/catalog/sql_features.txt2
-rw-r--r--src/backend/executor/spi.c50
-rw-r--r--src/backend/nodes/copyfuncs.c1
-rw-r--r--src/backend/nodes/equalfuncs.c1
-rw-r--r--src/backend/parser/gram.y19
-rw-r--r--src/backend/tcop/utility.c4
7 files changed, 137 insertions, 13 deletions
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index 6e5891749b4..c3214d4f4d8 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -190,6 +190,7 @@ typedef struct TransactionStateData
bool startedInRecovery; /* did we start in recovery? */
bool didLogXid; /* has xid been included in WAL record? */
int parallelModeLevel; /* Enter/ExitParallelMode counter */
+ bool chain; /* start a new block after this one */
struct TransactionStateData *parent; /* back link to parent */
} TransactionStateData;
@@ -2775,6 +2776,36 @@ StartTransactionCommand(void)
MemoryContextSwitchTo(CurTransactionContext);
}
+
+/*
+ * Simple system for saving and restoring transaction characteristics
+ * (isolation level, read only, deferrable). We need this for transaction
+ * chaining, so that we can set the characteristics of the new transaction to
+ * be the same as the previous one. (We need something like this because the
+ * GUC system resets the characteristics at transaction end, so for example
+ * just skipping the reset in StartTransaction() won't work.)
+ */
+static int save_XactIsoLevel;
+static bool save_XactReadOnly;
+static bool save_XactDeferrable;
+
+void
+SaveTransactionCharacteristics(void)
+{
+ save_XactIsoLevel = XactIsoLevel;
+ save_XactReadOnly = XactReadOnly;
+ save_XactDeferrable = XactDeferrable;
+}
+
+void
+RestoreTransactionCharacteristics(void)
+{
+ XactIsoLevel = save_XactIsoLevel;
+ XactReadOnly = save_XactReadOnly;
+ XactDeferrable = save_XactDeferrable;
+}
+
+
/*
* CommitTransactionCommand
*/
@@ -2783,6 +2814,9 @@ CommitTransactionCommand(void)
{
TransactionState s = CurrentTransactionState;
+ if (s->chain)
+ SaveTransactionCharacteristics();
+
switch (s->blockState)
{
/*
@@ -2834,6 +2868,13 @@ CommitTransactionCommand(void)
case TBLOCK_END:
CommitTransaction();
s->blockState = TBLOCK_DEFAULT;
+ if (s->chain)
+ {
+ StartTransaction();
+ s->blockState = TBLOCK_INPROGRESS;
+ s->chain = false;
+ RestoreTransactionCharacteristics();
+ }
break;
/*
@@ -2853,6 +2894,13 @@ CommitTransactionCommand(void)
case TBLOCK_ABORT_END:
CleanupTransaction();
s->blockState = TBLOCK_DEFAULT;
+ if (s->chain)
+ {
+ StartTransaction();
+ s->blockState = TBLOCK_INPROGRESS;
+ s->chain = false;
+ RestoreTransactionCharacteristics();
+ }
break;
/*
@@ -2864,6 +2912,13 @@ CommitTransactionCommand(void)
AbortTransaction();
CleanupTransaction();
s->blockState = TBLOCK_DEFAULT;
+ if (s->chain)
+ {
+ StartTransaction();
+ s->blockState = TBLOCK_INPROGRESS;
+ s->chain = false;
+ RestoreTransactionCharacteristics();
+ }
break;
/*
@@ -3521,7 +3576,7 @@ PrepareTransactionBlock(const char *gid)
bool result;
/* Set up to commit the current transaction */
- result = EndTransactionBlock();
+ result = EndTransactionBlock(false);
/* If successful, change outer tblock state to PREPARE */
if (result)
@@ -3567,7 +3622,7 @@ PrepareTransactionBlock(const char *gid)
* resource owner, etc while executing inside a Portal.
*/
bool
-EndTransactionBlock(void)
+EndTransactionBlock(bool chain)
{
TransactionState s = CurrentTransactionState;
bool result = false;
@@ -3693,6 +3748,13 @@ EndTransactionBlock(void)
break;
}
+ Assert(s->blockState == TBLOCK_STARTED ||
+ s->blockState == TBLOCK_END ||
+ s->blockState == TBLOCK_ABORT_END ||
+ s->blockState == TBLOCK_ABORT_PENDING);
+
+ s->chain = chain;
+
return result;
}
@@ -3703,7 +3765,7 @@ EndTransactionBlock(void)
* As above, we don't actually do anything here except change blockState.
*/
void
-UserAbortTransactionBlock(void)
+UserAbortTransactionBlock(bool chain)
{
TransactionState s = CurrentTransactionState;
@@ -3801,6 +3863,11 @@ UserAbortTransactionBlock(void)
BlockStateAsString(s->blockState));
break;
}
+
+ Assert(s->blockState == TBLOCK_ABORT_END ||
+ s->blockState == TBLOCK_ABORT_PENDING);
+
+ s->chain = chain;
}
/*
diff --git a/src/backend/catalog/sql_features.txt b/src/backend/catalog/sql_features.txt
index bade0fe9aeb..6b23163929d 100644
--- a/src/backend/catalog/sql_features.txt
+++ b/src/backend/catalog/sql_features.txt
@@ -443,7 +443,7 @@ T213 INSTEAD OF triggers YES
T231 Sensitive cursors YES
T241 START TRANSACTION statement YES
T251 SET TRANSACTION statement: LOCAL option NO
-T261 Chained transactions NO
+T261 Chained transactions YES
T271 Savepoints YES
T272 Enhanced savepoint management NO
T281 SELECT privilege with column granularity YES
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index d898f4ca78d..6e262d1a3ad 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -217,8 +217,8 @@ SPI_start_transaction(void)
MemoryContextSwitchTo(oldcontext);
}
-void
-SPI_commit(void)
+static void
+_SPI_commit(bool chain)
{
MemoryContext oldcontext = CurrentMemoryContext;
@@ -250,14 +250,36 @@ SPI_commit(void)
while (ActiveSnapshotSet())
PopActiveSnapshot();
+ if (chain)
+ SaveTransactionCharacteristics();
+
CommitTransactionCommand();
+
+ if (chain)
+ {
+ StartTransactionCommand();
+ RestoreTransactionCharacteristics();
+ }
+
MemoryContextSwitchTo(oldcontext);
_SPI_current->internal_xact = false;
}
void
-SPI_rollback(void)
+SPI_commit(void)
+{
+ _SPI_commit(false);
+}
+
+void
+SPI_commit_and_chain(void)
+{
+ _SPI_commit(true);
+}
+
+static void
+_SPI_rollback(bool chain)
{
MemoryContext oldcontext = CurrentMemoryContext;
@@ -274,12 +296,34 @@ SPI_rollback(void)
_SPI_current->internal_xact = true;
+ if (chain)
+ SaveTransactionCharacteristics();
+
AbortCurrentTransaction();
+
+ if (chain)
+ {
+ StartTransactionCommand();
+ RestoreTransactionCharacteristics();
+ }
+
MemoryContextSwitchTo(oldcontext);
_SPI_current->internal_xact = false;
}
+void
+SPI_rollback(void)
+{
+ _SPI_rollback(false);
+}
+
+void
+SPI_rollback_and_chain(void)
+{
+ _SPI_rollback(true);
+}
+
/*
* Clean up SPI state. Called on transaction end (of non-SPI-internal
* transactions) and when returning to the main loop on error.
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 1ea6b845616..d97781e1cbe 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3666,6 +3666,7 @@ _copyTransactionStmt(const TransactionStmt *from)
COPY_NODE_FIELD(options);
COPY_STRING_FIELD(savepoint_name);
COPY_STRING_FIELD(gid);
+ COPY_SCALAR_FIELD(chain);
return newnode;
}
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 886e96c9b61..91c007ad5b0 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1510,6 +1510,7 @@ _equalTransactionStmt(const TransactionStmt *a, const TransactionStmt *b)
COMPARE_NODE_FIELD(options);
COMPARE_STRING_FIELD(savepoint_name);
COMPARE_STRING_FIELD(gid);
+ COMPARE_SCALAR_FIELD(chain);
return true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 502e51bb0e1..0a4822829a5 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -312,6 +312,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <boolean> opt_or_replace
opt_grant_grant_option opt_grant_admin_option
opt_nowait opt_if_exists opt_with_data
+ opt_transaction_chain
%type <ival> opt_nowait_or_skip
%type <list> OptRoleList AlterOptRoleList
@@ -9792,11 +9793,12 @@ UnlistenStmt:
*****************************************************************************/
TransactionStmt:
- ABORT_P opt_transaction
+ ABORT_P opt_transaction opt_transaction_chain
{
TransactionStmt *n = makeNode(TransactionStmt);
n->kind = TRANS_STMT_ROLLBACK;
n->options = NIL;
+ n->chain = $3;
$$ = (Node *)n;
}
| BEGIN_P opt_transaction transaction_mode_list_or_empty
@@ -9813,25 +9815,28 @@ TransactionStmt:
n->options = $3;
$$ = (Node *)n;
}
- | COMMIT opt_transaction
+ | COMMIT opt_transaction opt_transaction_chain
{
TransactionStmt *n = makeNode(TransactionStmt);
n->kind = TRANS_STMT_COMMIT;
n->options = NIL;
+ n->chain = $3;
$$ = (Node *)n;
}
- | END_P opt_transaction
+ | END_P opt_transaction opt_transaction_chain
{
TransactionStmt *n = makeNode(TransactionStmt);
n->kind = TRANS_STMT_COMMIT;
n->options = NIL;
+ n->chain = $3;
$$ = (Node *)n;
}
- | ROLLBACK opt_transaction
+ | ROLLBACK opt_transaction opt_transaction_chain
{
TransactionStmt *n = makeNode(TransactionStmt);
n->kind = TRANS_STMT_ROLLBACK;
n->options = NIL;
+ n->chain = $3;
$$ = (Node *)n;
}
| SAVEPOINT ColId
@@ -9931,6 +9936,12 @@ transaction_mode_list_or_empty:
{ $$ = NIL; }
;
+opt_transaction_chain:
+ AND CHAIN { $$ = true; }
+ | AND NO CHAIN { $$ = false; }
+ | /* EMPTY */ { $$ = false; }
+ ;
+
/*****************************************************************************
*
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 5053ef05eff..857b7a8b43f 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -440,7 +440,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
break;
case TRANS_STMT_COMMIT:
- if (!EndTransactionBlock())
+ if (!EndTransactionBlock(stmt->chain))
{
/* report unsuccessful commit in completionTag */
if (completionTag)
@@ -471,7 +471,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
break;
case TRANS_STMT_ROLLBACK:
- UserAbortTransactionBlock();
+ UserAbortTransactionBlock(stmt->chain);
break;
case TRANS_STMT_SAVEPOINT: