Thread

Commits

Same data as JSON: GET /api/v1/messages/:b64id/commits the thread's linked commits as JSON, with link sources. API reference →
  1. Only allow one recovery target setting

  1. [PATCH v3 1/2] Transaction chaining

    Peter Eisentraut <peter_e@gmx.net> — 2018-02-17T02:37:55Z

    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.
    ---
     doc/src/sgml/ref/abort.sgml                |  14 +-
     doc/src/sgml/ref/commit.sgml               |  14 +-
     doc/src/sgml/ref/end.sgml                  |  14 +-
     doc/src/sgml/ref/rollback.sgml             |  14 +-
     src/backend/access/transam/xact.c          |  73 +++++++-
     src/backend/catalog/sql_features.txt       |   2 +-
     src/backend/nodes/copyfuncs.c              |   1 +
     src/backend/nodes/equalfuncs.c             |   1 +
     src/backend/parser/gram.y                  |  19 +-
     src/backend/tcop/utility.c                 |   4 +-
     src/bin/psql/tab-complete.c                |   8 +-
     src/include/access/xact.h                  |   4 +-
     src/include/nodes/parsenodes.h             |   1 +
     src/test/regress/expected/transactions.out | 191 +++++++++++++++++++++
     src/test/regress/sql/transactions.sql      |  63 +++++++
     15 files changed, 404 insertions(+), 19 deletions(-)
    
    diff --git a/doc/src/sgml/ref/abort.sgml b/doc/src/sgml/ref/abort.sgml
    index 21799d2a83..0372913365 100644
    --- a/doc/src/sgml/ref/abort.sgml
    +++ b/doc/src/sgml/ref/abort.sgml
    @@ -21,7 +21,7 @@
     
      <refsynopsisdiv>
     <synopsis>
    -ABORT [ WORK | TRANSACTION ]
    +ABORT [ WORK | TRANSACTION ] [ AND [ NO ] CHAIN ]
     </synopsis>
      </refsynopsisdiv>
     
    @@ -51,6 +51,18 @@ <title>Parameters</title>
          </para>
         </listitem>
        </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>
      </refsect1>
     
    diff --git a/doc/src/sgml/ref/commit.sgml b/doc/src/sgml/ref/commit.sgml
    index b2e8d5d180..37c706a66f 100644
    --- a/doc/src/sgml/ref/commit.sgml
    +++ b/doc/src/sgml/ref/commit.sgml
    @@ -21,7 +21,7 @@
     
      <refsynopsisdiv>
     <synopsis>
    -COMMIT [ WORK | TRANSACTION ]
    +COMMIT [ WORK | TRANSACTION ] [ AND [ NO ] CHAIN ]
     </synopsis>
      </refsynopsisdiv>
     
    @@ -48,6 +48,18 @@ <title>Parameters</title>
          </para>
         </listitem>
        </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>
      </refsect1>
     
    diff --git a/doc/src/sgml/ref/end.sgml b/doc/src/sgml/ref/end.sgml
    index 7523315f34..8b8f4f0dbb 100644
    --- a/doc/src/sgml/ref/end.sgml
    +++ b/doc/src/sgml/ref/end.sgml
    @@ -21,7 +21,7 @@
     
      <refsynopsisdiv>
     <synopsis>
    -END [ WORK | TRANSACTION ]
    +END [ WORK | TRANSACTION ] [ AND [ NO ] CHAIN ]
     </synopsis>
      </refsynopsisdiv>
     
    @@ -50,6 +50,18 @@ <title>Parameters</title>
          </para>
         </listitem>
        </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>
      </refsect1>
     
    diff --git a/doc/src/sgml/ref/rollback.sgml b/doc/src/sgml/ref/rollback.sgml
    index 3cafb848a9..3019273a47 100644
    --- a/doc/src/sgml/ref/rollback.sgml
    +++ b/doc/src/sgml/ref/rollback.sgml
    @@ -21,7 +21,7 @@
     
      <refsynopsisdiv>
     <synopsis>
    -ROLLBACK [ WORK | TRANSACTION ]
    +ROLLBACK [ WORK | TRANSACTION ] [ AND [ NO ] CHAIN ]
     </synopsis>
      </refsynopsisdiv>
     
    @@ -47,6 +47,18 @@ <title>Parameters</title>
          </para>
         </listitem>
        </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>
      </refsect1>
     
    diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
    index d967400384..6af149d788 100644
    --- a/src/backend/access/transam/xact.c
    +++ b/src/backend/access/transam/xact.c
    @@ -189,6 +189,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;
     
    @@ -2760,6 +2761,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;
    +
    +static void
    +SaveTransactionCharacteristics(void)
    +{
    +	save_XactIsoLevel = XactIsoLevel;
    +	save_XactReadOnly = XactReadOnly;
    +	save_XactDeferrable = XactDeferrable;
    +}
    +
    +static void
    +RestoreTransactionCharacteristics(void)
    +{
    +	XactIsoLevel = save_XactIsoLevel;
    +	XactReadOnly = save_XactReadOnly;
    +	XactDeferrable = save_XactDeferrable;
    +}
    +
    +
     /*
      *	CommitTransactionCommand
      */
    @@ -2768,6 +2799,9 @@ CommitTransactionCommand(void)
     {
     	TransactionState s = CurrentTransactionState;
     
    +	if (s->chain)
    +		SaveTransactionCharacteristics();
    +
     	switch (s->blockState)
     	{
     			/*
    @@ -2819,6 +2853,13 @@ CommitTransactionCommand(void)
     		case TBLOCK_END:
     			CommitTransaction();
     			s->blockState = TBLOCK_DEFAULT;
    +			if (s->chain)
    +			{
    +				StartTransaction();
    +				s->blockState = TBLOCK_INPROGRESS;
    +				s->chain = false;
    +				RestoreTransactionCharacteristics();
    +			}
     			break;
     
     			/*
    @@ -2838,6 +2879,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;
     
     			/*
    @@ -2849,6 +2897,13 @@ CommitTransactionCommand(void)
     			AbortTransaction();
     			CleanupTransaction();
     			s->blockState = TBLOCK_DEFAULT;
    +			if (s->chain)
    +			{
    +				StartTransaction();
    +				s->blockState = TBLOCK_INPROGRESS;
    +				s->chain = false;
    +				RestoreTransactionCharacteristics();
    +			}
     			break;
     
     			/*
    @@ -3506,7 +3561,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)
    @@ -3552,7 +3607,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;
    @@ -3678,6 +3733,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;
     }
     
    @@ -3688,7 +3750,7 @@ EndTransactionBlock(void)
      * As above, we don't actually do anything here except change blockState.
      */
     void
    -UserAbortTransactionBlock(void)
    +UserAbortTransactionBlock(bool chain)
     {
     	TransactionState s = CurrentTransactionState;
     
    @@ -3786,6 +3848,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 aeb262a5b0..49df7e08c0 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/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
    index db49968409..acfb05374d 100644
    --- a/src/backend/nodes/copyfuncs.c
    +++ b/src/backend/nodes/copyfuncs.c
    @@ -3654,6 +3654,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 3a084b4d1f..7c8cc6596d 100644
    --- a/src/backend/nodes/equalfuncs.c
    +++ b/src/backend/nodes/equalfuncs.c
    @@ -1506,6 +1506,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 2c2208ffb7..94a407c981 100644
    --- a/src/backend/parser/gram.y
    +++ b/src/backend/parser/gram.y
    @@ -310,6 +310,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
    @@ -9807,11 +9808,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
    @@ -9828,25 +9830,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
    @@ -9946,6 +9951,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 970c94ee80..cd5c536563 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:
    diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
    index fa44b2820b..fe89c58a7d 100644
    --- a/src/bin/psql/tab-complete.c
    +++ b/src/bin/psql/tab-complete.c
    @@ -2050,16 +2050,18 @@ psql_completion(const char *text, int start, int end)
     		COMPLETE_WITH("WORK", "TRANSACTION", "ISOLATION LEVEL", "READ", "DEFERRABLE", "NOT DEFERRABLE");
     /* END, ABORT */
     	else if (Matches("END|ABORT"))
    -		COMPLETE_WITH("WORK", "TRANSACTION");
    +		COMPLETE_WITH("AND", "WORK", "TRANSACTION");
     /* COMMIT */
     	else if (Matches("COMMIT"))
    -		COMPLETE_WITH("WORK", "TRANSACTION", "PREPARED");
    +		COMPLETE_WITH("AND", "WORK", "TRANSACTION", "PREPARED");
     /* RELEASE SAVEPOINT */
     	else if (Matches("RELEASE"))
     		COMPLETE_WITH("SAVEPOINT");
     /* 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 */
     	else if (Matches("CALL"))
     		COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_procedures, NULL);
    diff --git a/src/include/access/xact.h b/src/include/access/xact.h
    index 689c57c592..c757146e4d 100644
    --- a/src/include/access/xact.h
    +++ b/src/include/access/xact.h
    @@ -372,9 +372,9 @@ extern void StartTransactionCommand(void);
     extern void CommitTransactionCommand(void);
     extern void AbortCurrentTransaction(void);
     extern void BeginTransactionBlock(void);
    -extern bool EndTransactionBlock(void);
    +extern bool EndTransactionBlock(bool chain);
     extern bool PrepareTransactionBlock(const char *gid);
    -extern void UserAbortTransactionBlock(void);
    +extern void UserAbortTransactionBlock(bool chain);
     extern void BeginImplicitTransactionBlock(void);
     extern void EndImplicitTransactionBlock(void);
     extern void ReleaseSavepoint(const char *name);
    diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
    index e5bdc1cec5..bcc2f1a06c 100644
    --- a/src/include/nodes/parsenodes.h
    +++ b/src/include/nodes/parsenodes.h
    @@ -2986,6 +2986,7 @@ typedef struct TransactionStmt
     	List	   *options;		/* for BEGIN/START commands */
     	char	   *savepoint_name; /* for savepoint commands */
     	char	   *gid;			/* for two-phase-commit related commands */
    +	bool		chain;			/* AND CHAIN option */
     } TransactionStmt;
     
     /* ----------------------
    diff --git a/src/test/regress/expected/transactions.out b/src/test/regress/expected/transactions.out
    index 69e176c525..4093b6fdf0 100644
    --- a/src/test/regress/expected/transactions.out
    +++ b/src/test/regress/expected/transactions.out
    @@ -659,6 +659,197 @@ ERROR:  portal "ctt" cannot be run
     COMMIT;
     DROP FUNCTION create_temp_tab();
     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 (3);
    +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 (4);
    +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 (5);
    +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
    + 3
    + 4
    +(4 rows)
    +
    +RESET default_transaction_read_only;
    +DROP TABLE abc;
     -- Test assorted behaviors around the implicit transaction block created
     -- 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
    diff --git a/src/test/regress/sql/transactions.sql b/src/test/regress/sql/transactions.sql
    index 2e3739fd6c..8e89481430 100644
    --- a/src/test/regress/sql/transactions.sql
    +++ b/src/test/regress/sql/transactions.sql
    @@ -419,6 +419,69 @@ CREATE FUNCTION create_temp_tab() RETURNS text
     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 (3);
    +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 (4);
    +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 (5);
    +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
     -- 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
    
    base-commit: f2cbffc7a6183a359b339cfed65e9d609de756f0
    -- 
    2.19.1
    
    
    --------------5FF59F88931798579DEB4489
    Content-Type: text/plain; charset=UTF-8; x-mac-type="0"; x-mac-creator="0";
     name="v3-0002-Transaction-chaining-support-in-PL-pgSQL.patch"
    Content-Transfer-Encoding: 7bit
    Content-Disposition: attachment;
     filename*0="v3-0002-Transaction-chaining-support-in-PL-pgSQL.patch"