Commit 280a408b authored by Peter Eisentraut's avatar Peter Eisentraut

Transaction chaining

Add command variants COMMIT AND CHAIN and ROLLBACK AND CHAIN, which
start new transactions with the same transaction characteristics as the
just finished one, per SQL standard.

Support for transaction chaining in PL/pgSQL is also added.  This
functionality is especially useful when running COMMIT in a loop in
PL/pgSQL.
Reviewed-by: default avatarFabien COELHO <coelho@cri.ensmp.fr>
Discussion: https://www.postgresql.org/message-id/flat/28536681-324b-10dc-ade8-ab46f7645a5a@2ndquadrant.com
parent b2db2770
...@@ -3490,6 +3490,20 @@ CALL transaction_test1(); ...@@ -3490,6 +3490,20 @@ CALL transaction_test1();
</programlisting> </programlisting>
</para> </para>
<indexterm zone="plpgsql-transaction-chain">
<primary>chained transactions</primary>
<secondary>in PL/pgSQL</secondary>
</indexterm>
<para id="plpgsql-transaction-chain">
A new transaction starts out with default transaction characteristics such
as transaction isolation level. In cases where transactions are committed
in a loop, it might be desirable to start new transactions automatically
with the same characteristics as the previous one. The commands
<command>COMMIT AND CHAIN</command> and <command>ROLLBACK AND
CHAIN</command> accomplish this.
</para>
<para> <para>
Transaction control is only possible in <command>CALL</command> or Transaction control is only possible in <command>CALL</command> or
<command>DO</command> invocations from the top level or nested <command>DO</command> invocations from the top level or nested
......
...@@ -21,7 +21,7 @@ PostgreSQL documentation ...@@ -21,7 +21,7 @@ PostgreSQL documentation
<refsynopsisdiv> <refsynopsisdiv>
<synopsis> <synopsis>
ABORT [ WORK | TRANSACTION ] ABORT [ WORK | TRANSACTION ] [ AND [ NO ] CHAIN ]
</synopsis> </synopsis>
</refsynopsisdiv> </refsynopsisdiv>
...@@ -51,6 +51,18 @@ ABORT [ WORK | TRANSACTION ] ...@@ -51,6 +51,18 @@ ABORT [ WORK | TRANSACTION ]
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><literal>AND CHAIN</literal></term>
<listitem>
<para>
If <literal>AND CHAIN</literal> is specified, a new transaction is
immediately started with the same transaction characteristics (see <xref
linkend="sql-set-transaction"/>) as the just finished one. Otherwise,
no new transaction is started.
</para>
</listitem>
</varlistentry>
</variablelist> </variablelist>
</refsect1> </refsect1>
......
...@@ -21,7 +21,7 @@ PostgreSQL documentation ...@@ -21,7 +21,7 @@ PostgreSQL documentation
<refsynopsisdiv> <refsynopsisdiv>
<synopsis> <synopsis>
COMMIT [ WORK | TRANSACTION ] COMMIT [ WORK | TRANSACTION ] [ AND [ NO ] CHAIN ]
</synopsis> </synopsis>
</refsynopsisdiv> </refsynopsisdiv>
...@@ -38,6 +38,10 @@ COMMIT [ WORK | TRANSACTION ] ...@@ -38,6 +38,10 @@ COMMIT [ WORK | TRANSACTION ]
<refsect1> <refsect1>
<title>Parameters</title> <title>Parameters</title>
<indexterm zone="sql-commit-chain">
<primary>chained transactions</primary>
</indexterm>
<variablelist> <variablelist>
<varlistentry> <varlistentry>
<term><literal>WORK</literal></term> <term><literal>WORK</literal></term>
...@@ -48,6 +52,18 @@ COMMIT [ WORK | TRANSACTION ] ...@@ -48,6 +52,18 @@ COMMIT [ WORK | TRANSACTION ]
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry id="sql-commit-chain">
<term><literal>AND CHAIN</literal></term>
<listitem>
<para>
If <literal>AND CHAIN</literal> is specified, a new transaction is
immediately started with the same transaction characteristics (see <xref
linkend="sql-set-transaction"/>) as the just finished one. Otherwise,
no new transaction is started.
</para>
</listitem>
</varlistentry>
</variablelist> </variablelist>
</refsect1> </refsect1>
...@@ -79,9 +95,8 @@ COMMIT; ...@@ -79,9 +95,8 @@ COMMIT;
<title>Compatibility</title> <title>Compatibility</title>
<para> <para>
The SQL standard only specifies the two forms The command <command>COMMIT</command> conforms to the SQL standard. The
<literal>COMMIT</literal> and <literal>COMMIT form <literal>COMMIT TRANSACTION</literal> is a PostgreSQL extension.
WORK</literal>. Otherwise, this command is fully conforming.
</para> </para>
</refsect1> </refsect1>
......
...@@ -21,7 +21,7 @@ PostgreSQL documentation ...@@ -21,7 +21,7 @@ PostgreSQL documentation
<refsynopsisdiv> <refsynopsisdiv>
<synopsis> <synopsis>
END [ WORK | TRANSACTION ] END [ WORK | TRANSACTION ] [ AND [ NO ] CHAIN ]
</synopsis> </synopsis>
</refsynopsisdiv> </refsynopsisdiv>
...@@ -50,6 +50,18 @@ END [ WORK | TRANSACTION ] ...@@ -50,6 +50,18 @@ END [ WORK | TRANSACTION ]
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><literal>AND CHAIN</literal></term>
<listitem>
<para>
If <literal>AND CHAIN</literal> is specified, a new transaction is
immediately started with the same transaction characteristics (see <xref
linkend="sql-set-transaction"/>) as the just finished one. Otherwise,
no new transaction is started.
</para>
</listitem>
</varlistentry>
</variablelist> </variablelist>
</refsect1> </refsect1>
......
...@@ -21,7 +21,7 @@ PostgreSQL documentation ...@@ -21,7 +21,7 @@ PostgreSQL documentation
<refsynopsisdiv> <refsynopsisdiv>
<synopsis> <synopsis>
ROLLBACK [ WORK | TRANSACTION ] ROLLBACK [ WORK | TRANSACTION ] [ AND [ NO ] CHAIN ]
</synopsis> </synopsis>
</refsynopsisdiv> </refsynopsisdiv>
...@@ -37,6 +37,10 @@ ROLLBACK [ WORK | TRANSACTION ] ...@@ -37,6 +37,10 @@ ROLLBACK [ WORK | TRANSACTION ]
<refsect1> <refsect1>
<title>Parameters</title> <title>Parameters</title>
<indexterm zone="sql-rollback-chain">
<primary>chained transactions</primary>
</indexterm>
<variablelist> <variablelist>
<varlistentry> <varlistentry>
<term><literal>WORK</literal></term> <term><literal>WORK</literal></term>
...@@ -47,6 +51,18 @@ ROLLBACK [ WORK | TRANSACTION ] ...@@ -47,6 +51,18 @@ ROLLBACK [ WORK | TRANSACTION ]
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry id="sql-rollback-chain">
<term><literal>AND CHAIN</literal></term>
<listitem>
<para>
If <literal>AND CHAIN</literal> is specified, a new transaction is
immediately started with the same transaction characteristics (see <xref
linkend="sql-set-transaction"/>) as the just finished one. Otherwise,
no new transaction is started.
</para>
</listitem>
</varlistentry>
</variablelist> </variablelist>
</refsect1> </refsect1>
...@@ -78,9 +94,8 @@ ROLLBACK; ...@@ -78,9 +94,8 @@ ROLLBACK;
<title>Compatibility</title> <title>Compatibility</title>
<para> <para>
The SQL standard only specifies the two forms The command <command>ROLLBACK</command> conforms to the SQL standard. The
<literal>ROLLBACK</literal> and <literal>ROLLBACK form <literal>ROLLBACK TRANSACTION</literal> is a PostgreSQL extension.
WORK</literal>. Otherwise, this command is fully conforming.
</para> </para>
</refsect1> </refsect1>
......
...@@ -4376,6 +4376,7 @@ int SPI_freeplan(SPIPlanPtr <parameter>plan</parameter>) ...@@ -4376,6 +4376,7 @@ int SPI_freeplan(SPIPlanPtr <parameter>plan</parameter>)
<refentry id="spi-spi-commit"> <refentry id="spi-spi-commit">
<indexterm><primary>SPI_commit</primary></indexterm> <indexterm><primary>SPI_commit</primary></indexterm>
<indexterm><primary>SPI_commit_and_chain</primary></indexterm>
<refmeta> <refmeta>
<refentrytitle>SPI_commit</refentrytitle> <refentrytitle>SPI_commit</refentrytitle>
...@@ -4384,12 +4385,17 @@ int SPI_freeplan(SPIPlanPtr <parameter>plan</parameter>) ...@@ -4384,12 +4385,17 @@ int SPI_freeplan(SPIPlanPtr <parameter>plan</parameter>)
<refnamediv> <refnamediv>
<refname>SPI_commit</refname> <refname>SPI_commit</refname>
<refname>SPI_commit_and_chain</refname>
<refpurpose>commit the current transaction</refpurpose> <refpurpose>commit the current transaction</refpurpose>
</refnamediv> </refnamediv>
<refsynopsisdiv> <refsynopsisdiv>
<synopsis> <synopsis>
void SPI_commit(void) void SPI_commit(void)
</synopsis>
<synopsis>
void SPI_commit_and_chain(void)
</synopsis> </synopsis>
</refsynopsisdiv> </refsynopsisdiv>
...@@ -4406,7 +4412,14 @@ void SPI_commit(void) ...@@ -4406,7 +4412,14 @@ void SPI_commit(void)
</para> </para>
<para> <para>
This function can only be executed if the SPI connection has been set as <function>SPI_commit_and_chain</function> is the same, but a new
transaction is immediately started with the same transaction
characteristics as the just finished one, like with the SQL command
<command>COMMIT AND CHAIN</command>.
</para>
<para>
These functions can only be executed if the SPI connection has been set as
nonatomic in the call to <function>SPI_connect_ext</function>. nonatomic in the call to <function>SPI_connect_ext</function>.
</para> </para>
</refsect1> </refsect1>
...@@ -4416,6 +4429,7 @@ void SPI_commit(void) ...@@ -4416,6 +4429,7 @@ void SPI_commit(void)
<refentry id="spi-spi-rollback"> <refentry id="spi-spi-rollback">
<indexterm><primary>SPI_rollback</primary></indexterm> <indexterm><primary>SPI_rollback</primary></indexterm>
<indexterm><primary>SPI_rollback_and_chain</primary></indexterm>
<refmeta> <refmeta>
<refentrytitle>SPI_rollback</refentrytitle> <refentrytitle>SPI_rollback</refentrytitle>
...@@ -4424,12 +4438,17 @@ void SPI_commit(void) ...@@ -4424,12 +4438,17 @@ void SPI_commit(void)
<refnamediv> <refnamediv>
<refname>SPI_rollback</refname> <refname>SPI_rollback</refname>
<refname>SPI_rollback_and_chain</refname>
<refpurpose>abort the current transaction</refpurpose> <refpurpose>abort the current transaction</refpurpose>
</refnamediv> </refnamediv>
<refsynopsisdiv> <refsynopsisdiv>
<synopsis> <synopsis>
void SPI_rollback(void) void SPI_rollback(void)
</synopsis>
<synopsis>
void SPI_rollback_and_chain(void)
</synopsis> </synopsis>
</refsynopsisdiv> </refsynopsisdiv>
...@@ -4444,9 +4463,15 @@ void SPI_rollback(void) ...@@ -4444,9 +4463,15 @@ void SPI_rollback(void)
using <function>SPI_start_transaction</function> before further database using <function>SPI_start_transaction</function> before further database
actions can be executed. actions can be executed.
</para> </para>
<para>
<function>SPI_rollback_and_chain</function> is the same, but a new
transaction is immediately started with the same transaction
characteristics as the just finished one, like with the SQL command
<command>ROLLBACK AND CHAIN</command>.
</para>
<para> <para>
This function can only be executed if the SPI connection has been set as These functions can only be executed if the SPI connection has been set as
nonatomic in the call to <function>SPI_connect_ext</function>. nonatomic in the call to <function>SPI_connect_ext</function>.
</para> </para>
</refsect1> </refsect1>
......
...@@ -190,6 +190,7 @@ typedef struct TransactionStateData ...@@ -190,6 +190,7 @@ typedef struct TransactionStateData
bool startedInRecovery; /* did we start in recovery? */ bool startedInRecovery; /* did we start in recovery? */
bool didLogXid; /* has xid been included in WAL record? */ bool didLogXid; /* has xid been included in WAL record? */
int parallelModeLevel; /* Enter/ExitParallelMode counter */ int parallelModeLevel; /* Enter/ExitParallelMode counter */
bool chain; /* start a new block after this one */
struct TransactionStateData *parent; /* back link to parent */ struct TransactionStateData *parent; /* back link to parent */
} TransactionStateData; } TransactionStateData;
...@@ -2775,6 +2776,36 @@ StartTransactionCommand(void) ...@@ -2775,6 +2776,36 @@ StartTransactionCommand(void)
MemoryContextSwitchTo(CurTransactionContext); 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 * CommitTransactionCommand
*/ */
...@@ -2783,6 +2814,9 @@ CommitTransactionCommand(void) ...@@ -2783,6 +2814,9 @@ CommitTransactionCommand(void)
{ {
TransactionState s = CurrentTransactionState; TransactionState s = CurrentTransactionState;
if (s->chain)
SaveTransactionCharacteristics();
switch (s->blockState) switch (s->blockState)
{ {
/* /*
...@@ -2834,6 +2868,13 @@ CommitTransactionCommand(void) ...@@ -2834,6 +2868,13 @@ CommitTransactionCommand(void)
case TBLOCK_END: case TBLOCK_END:
CommitTransaction(); CommitTransaction();
s->blockState = TBLOCK_DEFAULT; s->blockState = TBLOCK_DEFAULT;
if (s->chain)
{
StartTransaction();
s->blockState = TBLOCK_INPROGRESS;
s->chain = false;
RestoreTransactionCharacteristics();
}
break; break;
/* /*
...@@ -2853,6 +2894,13 @@ CommitTransactionCommand(void) ...@@ -2853,6 +2894,13 @@ CommitTransactionCommand(void)
case TBLOCK_ABORT_END: case TBLOCK_ABORT_END:
CleanupTransaction(); CleanupTransaction();
s->blockState = TBLOCK_DEFAULT; s->blockState = TBLOCK_DEFAULT;
if (s->chain)
{
StartTransaction();
s->blockState = TBLOCK_INPROGRESS;
s->chain = false;
RestoreTransactionCharacteristics();
}
break; break;
/* /*
...@@ -2864,6 +2912,13 @@ CommitTransactionCommand(void) ...@@ -2864,6 +2912,13 @@ CommitTransactionCommand(void)
AbortTransaction(); AbortTransaction();
CleanupTransaction(); CleanupTransaction();
s->blockState = TBLOCK_DEFAULT; s->blockState = TBLOCK_DEFAULT;
if (s->chain)
{
StartTransaction();
s->blockState = TBLOCK_INPROGRESS;
s->chain = false;
RestoreTransactionCharacteristics();
}
break; break;
/* /*
...@@ -3521,7 +3576,7 @@ PrepareTransactionBlock(const char *gid) ...@@ -3521,7 +3576,7 @@ PrepareTransactionBlock(const char *gid)
bool result; bool result;
/* Set up to commit the current transaction */ /* Set up to commit the current transaction */
result = EndTransactionBlock(); result = EndTransactionBlock(false);
/* If successful, change outer tblock state to PREPARE */ /* If successful, change outer tblock state to PREPARE */
if (result) if (result)
...@@ -3567,7 +3622,7 @@ PrepareTransactionBlock(const char *gid) ...@@ -3567,7 +3622,7 @@ PrepareTransactionBlock(const char *gid)
* resource owner, etc while executing inside a Portal. * resource owner, etc while executing inside a Portal.
*/ */
bool bool
EndTransactionBlock(void) EndTransactionBlock(bool chain)
{ {
TransactionState s = CurrentTransactionState; TransactionState s = CurrentTransactionState;
bool result = false; bool result = false;
...@@ -3693,6 +3748,13 @@ EndTransactionBlock(void) ...@@ -3693,6 +3748,13 @@ EndTransactionBlock(void)
break; 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; return result;
} }
...@@ -3703,7 +3765,7 @@ EndTransactionBlock(void) ...@@ -3703,7 +3765,7 @@ EndTransactionBlock(void)
* As above, we don't actually do anything here except change blockState. * As above, we don't actually do anything here except change blockState.
*/ */
void void
UserAbortTransactionBlock(void) UserAbortTransactionBlock(bool chain)
{ {
TransactionState s = CurrentTransactionState; TransactionState s = CurrentTransactionState;
...@@ -3801,6 +3863,11 @@ UserAbortTransactionBlock(void) ...@@ -3801,6 +3863,11 @@ UserAbortTransactionBlock(void)
BlockStateAsString(s->blockState)); BlockStateAsString(s->blockState));
break; break;
} }
Assert(s->blockState == TBLOCK_ABORT_END ||
s->blockState == TBLOCK_ABORT_PENDING);
s->chain = chain;
} }
/* /*
......
...@@ -443,7 +443,7 @@ T213 INSTEAD OF triggers YES ...@@ -443,7 +443,7 @@ T213 INSTEAD OF triggers YES
T231 Sensitive cursors YES T231 Sensitive cursors YES
T241 START TRANSACTION statement YES T241 START TRANSACTION statement YES
T251 SET TRANSACTION statement: LOCAL option NO T251 SET TRANSACTION statement: LOCAL option NO
T261 Chained transactions NO T261 Chained transactions YES
T271 Savepoints YES T271 Savepoints YES
T272 Enhanced savepoint management NO T272 Enhanced savepoint management NO
T281 SELECT privilege with column granularity YES T281 SELECT privilege with column granularity YES
......
...@@ -217,8 +217,8 @@ SPI_start_transaction(void) ...@@ -217,8 +217,8 @@ SPI_start_transaction(void)
MemoryContextSwitchTo(oldcontext); MemoryContextSwitchTo(oldcontext);
} }
void static void
SPI_commit(void) _SPI_commit(bool chain)
{ {
MemoryContext oldcontext = CurrentMemoryContext; MemoryContext oldcontext = CurrentMemoryContext;
...@@ -250,14 +250,36 @@ SPI_commit(void) ...@@ -250,14 +250,36 @@ SPI_commit(void)
while (ActiveSnapshotSet()) while (ActiveSnapshotSet())
PopActiveSnapshot(); PopActiveSnapshot();
if (chain)
SaveTransactionCharacteristics();
CommitTransactionCommand(); CommitTransactionCommand();
if (chain)
{
StartTransactionCommand();
RestoreTransactionCharacteristics();
}
MemoryContextSwitchTo(oldcontext); MemoryContextSwitchTo(oldcontext);
_SPI_current->internal_xact = false; _SPI_current->internal_xact = false;
} }
void 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; MemoryContext oldcontext = CurrentMemoryContext;
...@@ -274,12 +296,34 @@ SPI_rollback(void) ...@@ -274,12 +296,34 @@ SPI_rollback(void)
_SPI_current->internal_xact = true; _SPI_current->internal_xact = true;
if (chain)
SaveTransactionCharacteristics();
AbortCurrentTransaction(); AbortCurrentTransaction();
if (chain)
{
StartTransactionCommand();
RestoreTransactionCharacteristics();
}
MemoryContextSwitchTo(oldcontext); MemoryContextSwitchTo(oldcontext);
_SPI_current->internal_xact = false; _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 * Clean up SPI state. Called on transaction end (of non-SPI-internal
* transactions) and when returning to the main loop on error. * transactions) and when returning to the main loop on error.
......
...@@ -3666,6 +3666,7 @@ _copyTransactionStmt(const TransactionStmt *from) ...@@ -3666,6 +3666,7 @@ _copyTransactionStmt(const TransactionStmt *from)
COPY_NODE_FIELD(options); COPY_NODE_FIELD(options);
COPY_STRING_FIELD(savepoint_name); COPY_STRING_FIELD(savepoint_name);
COPY_STRING_FIELD(gid); COPY_STRING_FIELD(gid);
COPY_SCALAR_FIELD(chain);
return newnode; return newnode;
} }
......
...@@ -1510,6 +1510,7 @@ _equalTransactionStmt(const TransactionStmt *a, const TransactionStmt *b) ...@@ -1510,6 +1510,7 @@ _equalTransactionStmt(const TransactionStmt *a, const TransactionStmt *b)
COMPARE_NODE_FIELD(options); COMPARE_NODE_FIELD(options);
COMPARE_STRING_FIELD(savepoint_name); COMPARE_STRING_FIELD(savepoint_name);
COMPARE_STRING_FIELD(gid); COMPARE_STRING_FIELD(gid);
COMPARE_SCALAR_FIELD(chain);
return true; return true;
} }
......
...@@ -312,6 +312,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); ...@@ -312,6 +312,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <boolean> opt_or_replace %type <boolean> opt_or_replace
opt_grant_grant_option opt_grant_admin_option opt_grant_grant_option opt_grant_admin_option
opt_nowait opt_if_exists opt_with_data opt_nowait opt_if_exists opt_with_data
opt_transaction_chain
%type <ival> opt_nowait_or_skip %type <ival> opt_nowait_or_skip
%type <list> OptRoleList AlterOptRoleList %type <list> OptRoleList AlterOptRoleList
...@@ -9792,11 +9793,12 @@ UnlistenStmt: ...@@ -9792,11 +9793,12 @@ UnlistenStmt:
*****************************************************************************/ *****************************************************************************/
TransactionStmt: TransactionStmt:
ABORT_P opt_transaction ABORT_P opt_transaction opt_transaction_chain
{ {
TransactionStmt *n = makeNode(TransactionStmt); TransactionStmt *n = makeNode(TransactionStmt);
n->kind = TRANS_STMT_ROLLBACK; n->kind = TRANS_STMT_ROLLBACK;
n->options = NIL; n->options = NIL;
n->chain = $3;
$$ = (Node *)n; $$ = (Node *)n;
} }
| BEGIN_P opt_transaction transaction_mode_list_or_empty | BEGIN_P opt_transaction transaction_mode_list_or_empty
...@@ -9813,25 +9815,28 @@ TransactionStmt: ...@@ -9813,25 +9815,28 @@ TransactionStmt:
n->options = $3; n->options = $3;
$$ = (Node *)n; $$ = (Node *)n;
} }
| COMMIT opt_transaction | COMMIT opt_transaction opt_transaction_chain
{ {
TransactionStmt *n = makeNode(TransactionStmt); TransactionStmt *n = makeNode(TransactionStmt);
n->kind = TRANS_STMT_COMMIT; n->kind = TRANS_STMT_COMMIT;
n->options = NIL; n->options = NIL;
n->chain = $3;
$$ = (Node *)n; $$ = (Node *)n;
} }
| END_P opt_transaction | END_P opt_transaction opt_transaction_chain
{ {
TransactionStmt *n = makeNode(TransactionStmt); TransactionStmt *n = makeNode(TransactionStmt);
n->kind = TRANS_STMT_COMMIT; n->kind = TRANS_STMT_COMMIT;
n->options = NIL; n->options = NIL;
n->chain = $3;
$$ = (Node *)n; $$ = (Node *)n;
} }
| ROLLBACK opt_transaction | ROLLBACK opt_transaction opt_transaction_chain
{ {
TransactionStmt *n = makeNode(TransactionStmt); TransactionStmt *n = makeNode(TransactionStmt);
n->kind = TRANS_STMT_ROLLBACK; n->kind = TRANS_STMT_ROLLBACK;
n->options = NIL; n->options = NIL;
n->chain = $3;
$$ = (Node *)n; $$ = (Node *)n;
} }
| SAVEPOINT ColId | SAVEPOINT ColId
...@@ -9931,6 +9936,12 @@ transaction_mode_list_or_empty: ...@@ -9931,6 +9936,12 @@ transaction_mode_list_or_empty:
{ $$ = NIL; } { $$ = NIL; }
; ;
opt_transaction_chain:
AND CHAIN { $$ = true; }
| AND NO CHAIN { $$ = false; }
| /* EMPTY */ { $$ = false; }
;
/***************************************************************************** /*****************************************************************************
* *
......
...@@ -440,7 +440,7 @@ standard_ProcessUtility(PlannedStmt *pstmt, ...@@ -440,7 +440,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
break; break;
case TRANS_STMT_COMMIT: case TRANS_STMT_COMMIT:
if (!EndTransactionBlock()) if (!EndTransactionBlock(stmt->chain))
{ {
/* report unsuccessful commit in completionTag */ /* report unsuccessful commit in completionTag */
if (completionTag) if (completionTag)
...@@ -471,7 +471,7 @@ standard_ProcessUtility(PlannedStmt *pstmt, ...@@ -471,7 +471,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
break; break;
case TRANS_STMT_ROLLBACK: case TRANS_STMT_ROLLBACK:
UserAbortTransactionBlock(); UserAbortTransactionBlock(stmt->chain);
break; break;
case TRANS_STMT_SAVEPOINT: case TRANS_STMT_SAVEPOINT:
......
...@@ -2091,16 +2091,18 @@ psql_completion(const char *text, int start, int end) ...@@ -2091,16 +2091,18 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH("WORK", "TRANSACTION", "ISOLATION LEVEL", "READ", "DEFERRABLE", "NOT DEFERRABLE"); COMPLETE_WITH("WORK", "TRANSACTION", "ISOLATION LEVEL", "READ", "DEFERRABLE", "NOT DEFERRABLE");
/* END, ABORT */ /* END, ABORT */
else if (Matches("END|ABORT")) else if (Matches("END|ABORT"))
COMPLETE_WITH("WORK", "TRANSACTION"); COMPLETE_WITH("AND", "WORK", "TRANSACTION");
/* COMMIT */ /* COMMIT */
else if (Matches("COMMIT")) else if (Matches("COMMIT"))
COMPLETE_WITH("WORK", "TRANSACTION", "PREPARED"); COMPLETE_WITH("AND", "WORK", "TRANSACTION", "PREPARED");
/* RELEASE SAVEPOINT */ /* RELEASE SAVEPOINT */
else if (Matches("RELEASE")) else if (Matches("RELEASE"))
COMPLETE_WITH("SAVEPOINT"); COMPLETE_WITH("SAVEPOINT");
/* ROLLBACK */ /* ROLLBACK */
else if (Matches("ROLLBACK")) else if (Matches("ROLLBACK"))
COMPLETE_WITH("WORK", "TRANSACTION", "TO SAVEPOINT", "PREPARED"); COMPLETE_WITH("AND", "WORK", "TRANSACTION", "TO SAVEPOINT", "PREPARED");
else if (Matches("ABORT|END|COMMIT|ROLLBACK", "AND"))
COMPLETE_WITH("CHAIN");
/* CALL */ /* CALL */
else if (Matches("CALL")) else if (Matches("CALL"))
COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_procedures, NULL); COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_procedures, NULL);
......
...@@ -368,12 +368,14 @@ extern bool TransactionIdIsCurrentTransactionId(TransactionId xid); ...@@ -368,12 +368,14 @@ extern bool TransactionIdIsCurrentTransactionId(TransactionId xid);
extern void CommandCounterIncrement(void); extern void CommandCounterIncrement(void);
extern void ForceSyncCommit(void); extern void ForceSyncCommit(void);
extern void StartTransactionCommand(void); extern void StartTransactionCommand(void);
extern void SaveTransactionCharacteristics(void);
extern void RestoreTransactionCharacteristics(void);
extern void CommitTransactionCommand(void); extern void CommitTransactionCommand(void);
extern void AbortCurrentTransaction(void); extern void AbortCurrentTransaction(void);
extern void BeginTransactionBlock(void); extern void BeginTransactionBlock(void);
extern bool EndTransactionBlock(void); extern bool EndTransactionBlock(bool chain);
extern bool PrepareTransactionBlock(const char *gid); extern bool PrepareTransactionBlock(const char *gid);
extern void UserAbortTransactionBlock(void); extern void UserAbortTransactionBlock(bool chain);
extern void BeginImplicitTransactionBlock(void); extern void BeginImplicitTransactionBlock(void);
extern void EndImplicitTransactionBlock(void); extern void EndImplicitTransactionBlock(void);
extern void ReleaseSavepoint(const char *name); extern void ReleaseSavepoint(const char *name);
......
...@@ -160,7 +160,9 @@ extern int SPI_register_trigger_data(TriggerData *tdata); ...@@ -160,7 +160,9 @@ extern int SPI_register_trigger_data(TriggerData *tdata);
extern void SPI_start_transaction(void); extern void SPI_start_transaction(void);
extern void SPI_commit(void); extern void SPI_commit(void);
extern void SPI_commit_and_chain(void);
extern void SPI_rollback(void); extern void SPI_rollback(void);
extern void SPI_rollback_and_chain(void);
extern void SPICleanup(void); extern void SPICleanup(void);
extern void AtEOXact_SPI(bool isCommit); extern void AtEOXact_SPI(bool isCommit);
......
...@@ -2998,6 +2998,7 @@ typedef struct TransactionStmt ...@@ -2998,6 +2998,7 @@ typedef struct TransactionStmt
List *options; /* for BEGIN/START commands */ List *options; /* for BEGIN/START commands */
char *savepoint_name; /* for savepoint commands */ char *savepoint_name; /* for savepoint commands */
char *gid; /* for two-phase-commit related commands */ char *gid; /* for two-phase-commit related commands */
bool chain; /* AND CHAIN option */
} TransactionStmt; } TransactionStmt;
/* ---------------------- /* ----------------------
......
...@@ -523,6 +523,34 @@ BEGIN ...@@ -523,6 +523,34 @@ BEGIN
END; END;
$$; $$;
CALL transaction_test11(); CALL transaction_test11();
-- transaction chain
TRUNCATE test1;
DO LANGUAGE plpgsql $$
BEGIN
ROLLBACK;
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
FOR i IN 0..3 LOOP
RAISE INFO 'transaction_isolation = %', current_setting('transaction_isolation');
INSERT INTO test1 (a) VALUES (i);
IF i % 2 = 0 THEN
COMMIT AND CHAIN;
ELSE
ROLLBACK AND CHAIN;
END IF;
END LOOP;
END
$$;
INFO: transaction_isolation = repeatable read
INFO: transaction_isolation = repeatable read
INFO: transaction_isolation = repeatable read
INFO: transaction_isolation = repeatable read
SELECT * FROM test1;
a | b
---+---
0 |
2 |
(2 rows)
DROP TABLE test1; DROP TABLE test1;
DROP TABLE test2; DROP TABLE test2;
DROP TABLE test3; DROP TABLE test3;
...@@ -4773,8 +4773,13 @@ exec_stmt_commit(PLpgSQL_execstate *estate, PLpgSQL_stmt_commit *stmt) ...@@ -4773,8 +4773,13 @@ exec_stmt_commit(PLpgSQL_execstate *estate, PLpgSQL_stmt_commit *stmt)
{ {
HoldPinnedPortals(); HoldPinnedPortals();
SPI_commit(); if (stmt->chain)
SPI_start_transaction(); SPI_commit_and_chain();
else
{
SPI_commit();
SPI_start_transaction();
}
estate->simple_eval_estate = NULL; estate->simple_eval_estate = NULL;
plpgsql_create_econtext(estate); plpgsql_create_econtext(estate);
...@@ -4792,8 +4797,13 @@ exec_stmt_rollback(PLpgSQL_execstate *estate, PLpgSQL_stmt_rollback *stmt) ...@@ -4792,8 +4797,13 @@ exec_stmt_rollback(PLpgSQL_execstate *estate, PLpgSQL_stmt_rollback *stmt)
{ {
HoldPinnedPortals(); HoldPinnedPortals();
SPI_rollback(); if (stmt->chain)
SPI_start_transaction(); SPI_rollback_and_chain();
else
{
SPI_rollback();
SPI_start_transaction();
}
estate->simple_eval_estate = NULL; estate->simple_eval_estate = NULL;
plpgsql_create_econtext(estate); plpgsql_create_econtext(estate);
......
...@@ -1318,14 +1318,20 @@ static void ...@@ -1318,14 +1318,20 @@ static void
dump_commit(PLpgSQL_stmt_commit *stmt) dump_commit(PLpgSQL_stmt_commit *stmt)
{ {
dump_ind(); dump_ind();
printf("COMMIT\n"); if (stmt->chain)
printf("COMMIT AND CHAIN\n");
else
printf("COMMIT\n");
} }
static void static void
dump_rollback(PLpgSQL_stmt_rollback *stmt) dump_rollback(PLpgSQL_stmt_rollback *stmt)
{ {
dump_ind(); dump_ind();
printf("ROLLBACK\n"); if (stmt->chain)
printf("ROLLBACK AND CHAIN\n");
else
printf("ROLLBACK\n");
} }
static void static void
......
...@@ -219,6 +219,8 @@ static void check_raise_parameters(PLpgSQL_stmt_raise *stmt); ...@@ -219,6 +219,8 @@ static void check_raise_parameters(PLpgSQL_stmt_raise *stmt);
%type <ival> opt_scrollable %type <ival> opt_scrollable
%type <fetch> opt_fetch_direction %type <fetch> opt_fetch_direction
%type <ival> opt_transaction_chain
%type <keyword> unreserved_keyword %type <keyword> unreserved_keyword
...@@ -252,6 +254,7 @@ static void check_raise_parameters(PLpgSQL_stmt_raise *stmt); ...@@ -252,6 +254,7 @@ static void check_raise_parameters(PLpgSQL_stmt_raise *stmt);
%token <keyword> K_ABSOLUTE %token <keyword> K_ABSOLUTE
%token <keyword> K_ALIAS %token <keyword> K_ALIAS
%token <keyword> K_ALL %token <keyword> K_ALL
%token <keyword> K_AND
%token <keyword> K_ARRAY %token <keyword> K_ARRAY
%token <keyword> K_ASSERT %token <keyword> K_ASSERT
%token <keyword> K_BACKWARD %token <keyword> K_BACKWARD
...@@ -259,6 +262,7 @@ static void check_raise_parameters(PLpgSQL_stmt_raise *stmt); ...@@ -259,6 +262,7 @@ static void check_raise_parameters(PLpgSQL_stmt_raise *stmt);
%token <keyword> K_BY %token <keyword> K_BY
%token <keyword> K_CALL %token <keyword> K_CALL
%token <keyword> K_CASE %token <keyword> K_CASE
%token <keyword> K_CHAIN
%token <keyword> K_CLOSE %token <keyword> K_CLOSE
%token <keyword> K_COLLATE %token <keyword> K_COLLATE
%token <keyword> K_COLUMN %token <keyword> K_COLUMN
...@@ -2199,7 +2203,7 @@ stmt_null : K_NULL ';' ...@@ -2199,7 +2203,7 @@ stmt_null : K_NULL ';'
} }
; ;
stmt_commit : K_COMMIT ';' stmt_commit : K_COMMIT opt_transaction_chain ';'
{ {
PLpgSQL_stmt_commit *new; PLpgSQL_stmt_commit *new;
...@@ -2207,12 +2211,13 @@ stmt_commit : K_COMMIT ';' ...@@ -2207,12 +2211,13 @@ stmt_commit : K_COMMIT ';'
new->cmd_type = PLPGSQL_STMT_COMMIT; new->cmd_type = PLPGSQL_STMT_COMMIT;
new->lineno = plpgsql_location_to_lineno(@1); new->lineno = plpgsql_location_to_lineno(@1);
new->stmtid = ++plpgsql_curr_compile->nstatements; new->stmtid = ++plpgsql_curr_compile->nstatements;
new->chain = $2;
$$ = (PLpgSQL_stmt *)new; $$ = (PLpgSQL_stmt *)new;
} }
; ;
stmt_rollback : K_ROLLBACK ';' stmt_rollback : K_ROLLBACK opt_transaction_chain ';'
{ {
PLpgSQL_stmt_rollback *new; PLpgSQL_stmt_rollback *new;
...@@ -2220,11 +2225,18 @@ stmt_rollback : K_ROLLBACK ';' ...@@ -2220,11 +2225,18 @@ stmt_rollback : K_ROLLBACK ';'
new->cmd_type = PLPGSQL_STMT_ROLLBACK; new->cmd_type = PLPGSQL_STMT_ROLLBACK;
new->lineno = plpgsql_location_to_lineno(@1); new->lineno = plpgsql_location_to_lineno(@1);
new->stmtid = ++plpgsql_curr_compile->nstatements; new->stmtid = ++plpgsql_curr_compile->nstatements;
new->chain = $2;
$$ = (PLpgSQL_stmt *)new; $$ = (PLpgSQL_stmt *)new;
} }
; ;
opt_transaction_chain:
K_AND K_CHAIN { $$ = true; }
| K_AND K_NO K_CHAIN { $$ = false; }
| /* EMPTY */ { $$ = false; }
;
stmt_set : K_SET stmt_set : K_SET
{ {
PLpgSQL_stmt_set *new; PLpgSQL_stmt_set *new;
...@@ -2482,10 +2494,12 @@ any_identifier : T_WORD ...@@ -2482,10 +2494,12 @@ any_identifier : T_WORD
unreserved_keyword : unreserved_keyword :
K_ABSOLUTE K_ABSOLUTE
| K_ALIAS | K_ALIAS
| K_AND
| K_ARRAY | K_ARRAY
| K_ASSERT | K_ASSERT
| K_BACKWARD | K_BACKWARD
| K_CALL | K_CALL
| K_CHAIN
| K_CLOSE | K_CLOSE
| K_COLLATE | K_COLLATE
| K_COLUMN | K_COLUMN
......
...@@ -29,10 +29,12 @@ ...@@ -29,10 +29,12 @@
/* name, value */ /* name, value */
PG_KEYWORD("absolute", K_ABSOLUTE) PG_KEYWORD("absolute", K_ABSOLUTE)
PG_KEYWORD("alias", K_ALIAS) PG_KEYWORD("alias", K_ALIAS)
PG_KEYWORD("and", K_AND)
PG_KEYWORD("array", K_ARRAY) PG_KEYWORD("array", K_ARRAY)
PG_KEYWORD("assert", K_ASSERT) PG_KEYWORD("assert", K_ASSERT)
PG_KEYWORD("backward", K_BACKWARD) PG_KEYWORD("backward", K_BACKWARD)
PG_KEYWORD("call", K_CALL) PG_KEYWORD("call", K_CALL)
PG_KEYWORD("chain", K_CHAIN)
PG_KEYWORD("close", K_CLOSE) PG_KEYWORD("close", K_CLOSE)
PG_KEYWORD("collate", K_COLLATE) PG_KEYWORD("collate", K_COLLATE)
PG_KEYWORD("column", K_COLUMN) PG_KEYWORD("column", K_COLUMN)
......
...@@ -545,6 +545,7 @@ typedef struct PLpgSQL_stmt_commit ...@@ -545,6 +545,7 @@ typedef struct PLpgSQL_stmt_commit
PLpgSQL_stmt_type cmd_type; PLpgSQL_stmt_type cmd_type;
int lineno; int lineno;
unsigned int stmtid; unsigned int stmtid;
bool chain;
} PLpgSQL_stmt_commit; } PLpgSQL_stmt_commit;
/* /*
...@@ -555,6 +556,7 @@ typedef struct PLpgSQL_stmt_rollback ...@@ -555,6 +556,7 @@ typedef struct PLpgSQL_stmt_rollback
PLpgSQL_stmt_type cmd_type; PLpgSQL_stmt_type cmd_type;
int lineno; int lineno;
unsigned int stmtid; unsigned int stmtid;
bool chain;
} PLpgSQL_stmt_rollback; } PLpgSQL_stmt_rollback;
/* /*
......
...@@ -445,6 +445,29 @@ $$; ...@@ -445,6 +445,29 @@ $$;
CALL transaction_test11(); CALL transaction_test11();
-- transaction chain
TRUNCATE test1;
DO LANGUAGE plpgsql $$
BEGIN
ROLLBACK;
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
FOR i IN 0..3 LOOP
RAISE INFO 'transaction_isolation = %', current_setting('transaction_isolation');
INSERT INTO test1 (a) VALUES (i);
IF i % 2 = 0 THEN
COMMIT AND CHAIN;
ELSE
ROLLBACK AND CHAIN;
END IF;
END LOOP;
END
$$;
SELECT * FROM test1;
DROP TABLE test1; DROP TABLE test1;
DROP TABLE test2; DROP TABLE test2;
DROP TABLE test3; DROP TABLE test3;
...@@ -659,6 +659,197 @@ ERROR: portal "ctt" cannot be run ...@@ -659,6 +659,197 @@ ERROR: portal "ctt" cannot be run
COMMIT; COMMIT;
DROP FUNCTION create_temp_tab(); DROP FUNCTION create_temp_tab();
DROP FUNCTION invert(x float8); DROP FUNCTION invert(x float8);
-- Tests for AND CHAIN
CREATE TABLE abc (a int);
-- set nondefault value so we have something to override below
SET default_transaction_read_only = on;
START TRANSACTION ISOLATION LEVEL REPEATABLE READ, READ WRITE, DEFERRABLE;
SHOW transaction_isolation;
transaction_isolation
-----------------------
repeatable read
(1 row)
SHOW transaction_read_only;
transaction_read_only
-----------------------
off
(1 row)
SHOW transaction_deferrable;
transaction_deferrable
------------------------
on
(1 row)
INSERT INTO abc VALUES (1);
INSERT INTO abc VALUES (2);
COMMIT AND CHAIN; -- TBLOCK_END
SHOW transaction_isolation;
transaction_isolation
-----------------------
repeatable read
(1 row)
SHOW transaction_read_only;
transaction_read_only
-----------------------
off
(1 row)
SHOW transaction_deferrable;
transaction_deferrable
------------------------
on
(1 row)
INSERT INTO abc VALUES ('error');
ERROR: invalid input syntax for type integer: "error"
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
SHOW transaction_isolation;
transaction_isolation
-----------------------
repeatable read
(1 row)
SHOW transaction_read_only;
transaction_read_only
-----------------------
off
(1 row)
SHOW transaction_deferrable;
transaction_deferrable
------------------------
on
(1 row)
INSERT INTO abc VALUES (4);
COMMIT;
START TRANSACTION ISOLATION LEVEL REPEATABLE READ, READ WRITE, DEFERRABLE;
SHOW transaction_isolation;
transaction_isolation
-----------------------
repeatable read
(1 row)
SHOW transaction_read_only;
transaction_read_only
-----------------------
off
(1 row)
SHOW transaction_deferrable;
transaction_deferrable
------------------------
on
(1 row)
SAVEPOINT x;
INSERT INTO abc VALUES ('error');
ERROR: invalid input syntax for type integer: "error"
LINE 1: INSERT INTO abc VALUES ('error');
^
COMMIT AND CHAIN; -- TBLOCK_ABORT_PENDING
SHOW transaction_isolation;
transaction_isolation
-----------------------
repeatable read
(1 row)
SHOW transaction_read_only;
transaction_read_only
-----------------------
off
(1 row)
SHOW transaction_deferrable;
transaction_deferrable
------------------------
on
(1 row)
INSERT INTO abc VALUES (5);
COMMIT;
-- different mix of options just for fun
START TRANSACTION ISOLATION LEVEL SERIALIZABLE, READ WRITE, NOT DEFERRABLE;
SHOW transaction_isolation;
transaction_isolation
-----------------------
serializable
(1 row)
SHOW transaction_read_only;
transaction_read_only
-----------------------
off
(1 row)
SHOW transaction_deferrable;
transaction_deferrable
------------------------
off
(1 row)
INSERT INTO abc VALUES (6);
ROLLBACK AND CHAIN; -- TBLOCK_ABORT_PENDING
SHOW transaction_isolation;
transaction_isolation
-----------------------
serializable
(1 row)
SHOW transaction_read_only;
transaction_read_only
-----------------------
off
(1 row)
SHOW transaction_deferrable;
transaction_deferrable
------------------------
off
(1 row)
INSERT INTO abc VALUES ('error');
ERROR: invalid input syntax for type integer: "error"
LINE 1: INSERT INTO abc VALUES ('error');
^
ROLLBACK AND CHAIN; -- TBLOCK_ABORT_END
SHOW transaction_isolation;
transaction_isolation
-----------------------
serializable
(1 row)
SHOW transaction_read_only;
transaction_read_only
-----------------------
off
(1 row)
SHOW transaction_deferrable;
transaction_deferrable
------------------------
off
(1 row)
ROLLBACK;
SELECT * FROM abc ORDER BY 1;
a
---
1
2
4
5
(4 rows)
RESET default_transaction_read_only;
DROP TABLE abc;
-- Test assorted behaviors around the implicit transaction block created -- Test assorted behaviors around the implicit transaction block created
-- when multiple SQL commands are sent in a single Query message. These -- when multiple SQL commands are sent in a single Query message. These
-- tests rely on the fact that psql will not break SQL commands apart at a -- tests rely on the fact that psql will not break SQL commands apart at a
......
...@@ -419,6 +419,69 @@ DROP FUNCTION create_temp_tab(); ...@@ -419,6 +419,69 @@ DROP FUNCTION create_temp_tab();
DROP FUNCTION invert(x float8); DROP FUNCTION invert(x float8);
-- Tests for AND CHAIN
CREATE TABLE abc (a int);
-- set nondefault value so we have something to override below
SET default_transaction_read_only = on;
START TRANSACTION ISOLATION LEVEL REPEATABLE READ, READ WRITE, DEFERRABLE;
SHOW transaction_isolation;
SHOW transaction_read_only;
SHOW transaction_deferrable;
INSERT INTO abc VALUES (1);
INSERT INTO abc VALUES (2);
COMMIT AND CHAIN; -- TBLOCK_END
SHOW transaction_isolation;
SHOW transaction_read_only;
SHOW transaction_deferrable;
INSERT INTO abc VALUES ('error');
INSERT INTO abc VALUES (3); -- check it's really aborted
COMMIT AND CHAIN; -- TBLOCK_ABORT_END
SHOW transaction_isolation;
SHOW transaction_read_only;
SHOW transaction_deferrable;
INSERT INTO abc VALUES (4);
COMMIT;
START TRANSACTION ISOLATION LEVEL REPEATABLE READ, READ WRITE, DEFERRABLE;
SHOW transaction_isolation;
SHOW transaction_read_only;
SHOW transaction_deferrable;
SAVEPOINT x;
INSERT INTO abc VALUES ('error');
COMMIT AND CHAIN; -- TBLOCK_ABORT_PENDING
SHOW transaction_isolation;
SHOW transaction_read_only;
SHOW transaction_deferrable;
INSERT INTO abc VALUES (5);
COMMIT;
-- different mix of options just for fun
START TRANSACTION ISOLATION LEVEL SERIALIZABLE, READ WRITE, NOT DEFERRABLE;
SHOW transaction_isolation;
SHOW transaction_read_only;
SHOW transaction_deferrable;
INSERT INTO abc VALUES (6);
ROLLBACK AND CHAIN; -- TBLOCK_ABORT_PENDING
SHOW transaction_isolation;
SHOW transaction_read_only;
SHOW transaction_deferrable;
INSERT INTO abc VALUES ('error');
ROLLBACK AND CHAIN; -- TBLOCK_ABORT_END
SHOW transaction_isolation;
SHOW transaction_read_only;
SHOW transaction_deferrable;
ROLLBACK;
SELECT * FROM abc ORDER BY 1;
RESET default_transaction_read_only;
DROP TABLE abc;
-- Test assorted behaviors around the implicit transaction block created -- Test assorted behaviors around the implicit transaction block created
-- when multiple SQL commands are sent in a single Query message. These -- when multiple SQL commands are sent in a single Query message. These
-- tests rely on the fact that psql will not break SQL commands apart at a -- tests rely on the fact that psql will not break SQL commands apart at a
......
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