@ -145,6 +145,7 @@ typedef enum TBlockState
/* transaction block states */
TBLOCK_BEGIN , /* starting transaction block */
TBLOCK_INPROGRESS , /* live transaction */
TBLOCK_IMPLICIT_INPROGRESS , /* live transaction after implicit BEGIN */
TBLOCK_PARALLEL_INPROGRESS , /* live transaction inside parallel worker */
TBLOCK_END , /* COMMIT received */
TBLOCK_ABORT , /* failed xact, awaiting ROLLBACK */
@ -2700,6 +2701,7 @@ StartTransactionCommand(void)
* previous CommitTransactionCommand . )
*/
case TBLOCK_INPROGRESS :
case TBLOCK_IMPLICIT_INPROGRESS :
case TBLOCK_SUBINPROGRESS :
break ;
@ -2790,6 +2792,7 @@ CommitTransactionCommand(void)
* counter and return .
*/
case TBLOCK_INPROGRESS :
case TBLOCK_IMPLICIT_INPROGRESS :
case TBLOCK_SUBINPROGRESS :
CommandCounterIncrement ( ) ;
break ;
@ -3014,10 +3017,12 @@ AbortCurrentTransaction(void)
break ;
/*
* if we aren ' t in a transaction block , we just do the basic abort
* & cleanup transaction .
* If we aren ' t in a transaction block , we just do the basic abort
* & cleanup transaction . For this purpose , we treat an implicit
* transaction block as if it were a simple statement .
*/
case TBLOCK_STARTED :
case TBLOCK_IMPLICIT_INPROGRESS :
AbortTransaction ( ) ;
CleanupTransaction ( ) ;
s - > blockState = TBLOCK_DEFAULT ;
@ -3148,9 +3153,8 @@ AbortCurrentTransaction(void)
* completes ) . Subtransactions are verboten too .
*
* isTopLevel : passed down from ProcessUtility to determine whether we are
* inside a function or multi - query querystring . ( We will always fail if
* this is false , but it ' s convenient to centralize the check here instead of
* making callers do it . )
* inside a function . ( We will always fail if this is false , but it ' s
* convenient to centralize the check here instead of making callers do it . )
* stmtType : statement type name , for error messages .
*/
void
@ -3183,8 +3187,7 @@ PreventTransactionChain(bool isTopLevel, const char *stmtType)
ereport ( ERROR ,
( errcode ( ERRCODE_ACTIVE_SQL_TRANSACTION ) ,
/* translator: %s represents an SQL statement name */
errmsg ( " %s cannot be executed from a function or multi-command string " ,
stmtType ) ) ) ;
errmsg ( " %s cannot be executed from a function " , stmtType ) ) ) ;
/* If we got past IsTransactionBlock test, should be in default state */
if ( CurrentTransactionState - > blockState ! = TBLOCK_DEFAULT & &
@ -3428,6 +3431,15 @@ BeginTransactionBlock(void)
s - > blockState = TBLOCK_BEGIN ;
break ;
/*
* BEGIN converts an implicit transaction block to a regular one .
* ( Note that we allow this even if we ' ve already done some
* commands , which is a bit odd but matches historical practice . )
*/
case TBLOCK_IMPLICIT_INPROGRESS :
s - > blockState = TBLOCK_BEGIN ;
break ;
/*
* Already a transaction block in progress .
*/
@ -3503,7 +3515,8 @@ PrepareTransactionBlock(char *gid)
* ignore case where we are not in a transaction ;
* EndTransactionBlock already issued a warning .
*/
Assert ( s - > blockState = = TBLOCK_STARTED ) ;
Assert ( s - > blockState = = TBLOCK_STARTED | |
s - > blockState = = TBLOCK_IMPLICIT_INPROGRESS ) ;
/* Don't send back a PREPARE result tag... */
result = false ;
}
@ -3541,6 +3554,18 @@ EndTransactionBlock(void)
result = true ;
break ;
/*
* In an implicit transaction block , commit , but issue a warning
* because there was no explicit BEGIN before this .
*/
case TBLOCK_IMPLICIT_INPROGRESS :
ereport ( WARNING ,
( errcode ( ERRCODE_NO_ACTIVE_SQL_TRANSACTION ) ,
errmsg ( " there is no transaction in progress " ) ) ) ;
s - > blockState = TBLOCK_END ;
result = true ;
break ;
/*
* We are in a failed transaction block . Tell
* CommitTransactionCommand it ' s time to exit the block .
@ -3705,8 +3730,14 @@ UserAbortTransactionBlock(void)
* WARNING and go to abort state . The upcoming call to
* CommitTransactionCommand ( ) will then put us back into the
* default state .
*
* We do the same thing with ABORT inside an implicit transaction ,
* although in this case we might be rolling back actual database
* state changes . ( It ' s debatable whether we should issue a
* WARNING in this case , but we have done so historically . )
*/
case TBLOCK_STARTED :
case TBLOCK_IMPLICIT_INPROGRESS :
ereport ( WARNING ,
( errcode ( ERRCODE_NO_ACTIVE_SQL_TRANSACTION ) ,
errmsg ( " there is no transaction in progress " ) ) ) ;
@ -3743,6 +3774,58 @@ UserAbortTransactionBlock(void)
}
}
/*
* BeginImplicitTransactionBlock
* Start an implicit transaction block if we ' re not already in one .
*
* Unlike BeginTransactionBlock , this is called directly from the main loop
* in postgres . c , not within a Portal . So we can just change blockState
* without a lot of ceremony . We do not expect caller to do
* CommitTransactionCommand / StartTransactionCommand .
*/
void
BeginImplicitTransactionBlock ( void )
{
TransactionState s = CurrentTransactionState ;
/*
* If we are in STARTED state ( that is , no transaction block is open ) ,
* switch to IMPLICIT_INPROGRESS state , creating an implicit transaction
* block .
*
* For caller convenience , we consider all other transaction states as
* legal here ; otherwise the caller would need its own state check , which
* seems rather pointless .
*/
if ( s - > blockState = = TBLOCK_STARTED )
s - > blockState = TBLOCK_IMPLICIT_INPROGRESS ;
}
/*
* EndImplicitTransactionBlock
* End an implicit transaction block , if we ' re in one .
*
* Like EndTransactionBlock , we just make any needed blockState change here .
* The real work will be done in the upcoming CommitTransactionCommand ( ) .
*/
void
EndImplicitTransactionBlock ( void )
{
TransactionState s = CurrentTransactionState ;
/*
* If we are in IMPLICIT_INPROGRESS state , switch back to STARTED state ,
* allowing CommitTransactionCommand to commit whatever happened during
* the implicit transaction block as though it were a single statement .
*
* For caller convenience , we consider all other transaction states as
* legal here ; otherwise the caller would need its own state check , which
* seems rather pointless .
*/
if ( s - > blockState = = TBLOCK_IMPLICIT_INPROGRESS )
s - > blockState = TBLOCK_STARTED ;
}
/*
* DefineSavepoint
* This executes a SAVEPOINT command .
@ -3780,6 +3863,28 @@ DefineSavepoint(char *name)
s - > name = MemoryContextStrdup ( TopTransactionContext , name ) ;
break ;
/*
* We disallow savepoint commands in implicit transaction blocks .
* There would be no great difficulty in allowing them so far as
* this module is concerned , but a savepoint seems inconsistent
* with exec_simple_query ' s behavior of abandoning the whole query
* string upon error . Also , the point of an implicit transaction
* block ( as opposed to a regular one ) is to automatically close
* after an error , so it ' s hard to see how a savepoint would fit
* into that .
*
* The error messages for this are phrased as if there were no
* active transaction block at all , which is historical but
* perhaps could be improved .
*/
case TBLOCK_IMPLICIT_INPROGRESS :
ereport ( ERROR ,
( errcode ( ERRCODE_NO_ACTIVE_SQL_TRANSACTION ) ,
/* translator: %s represents an SQL statement name */
errmsg ( " %s can only be used in transaction blocks " ,
" SAVEPOINT " ) ) ) ;
break ;
/* These cases are invalid. */
case TBLOCK_DEFAULT :
case TBLOCK_STARTED :
@ -3834,8 +3939,7 @@ ReleaseSavepoint(List *options)
switch ( s - > blockState )
{
/*
* We can ' t rollback to a savepoint if there is no savepoint
* defined .
* We can ' t release a savepoint if there is no savepoint defined .
*/
case TBLOCK_INPROGRESS :
ereport ( ERROR ,
@ -3843,6 +3947,15 @@ ReleaseSavepoint(List *options)
errmsg ( " no such savepoint " ) ) ) ;
break ;
case TBLOCK_IMPLICIT_INPROGRESS :
/* See comment about implicit transactions in DefineSavepoint */
ereport ( ERROR ,
( errcode ( ERRCODE_NO_ACTIVE_SQL_TRANSACTION ) ,
/* translator: %s represents an SQL statement name */
errmsg ( " %s can only be used in transaction blocks " ,
" RELEASE SAVEPOINT " ) ) ) ;
break ;
/*
* We are in a non - aborted subtransaction . This is the only valid
* case .
@ -3957,6 +4070,15 @@ RollbackToSavepoint(List *options)
errmsg ( " no such savepoint " ) ) ) ;
break ;
case TBLOCK_IMPLICIT_INPROGRESS :
/* See comment about implicit transactions in DefineSavepoint */
ereport ( ERROR ,
( errcode ( ERRCODE_NO_ACTIVE_SQL_TRANSACTION ) ,
/* translator: %s represents an SQL statement name */
errmsg ( " %s can only be used in transaction blocks " ,
" ROLLBACK TO SAVEPOINT " ) ) ) ;
break ;
/*
* There is at least one savepoint , so proceed .
*/
@ -4046,11 +4168,12 @@ RollbackToSavepoint(List *options)
/*
* BeginInternalSubTransaction
* This is the same as DefineSavepoint except it allows TBLOCK_STARTED ,
* TBLOCK_END , and TBLOCK_PREPARE states , and therefore it can safely be
* used in functions that might be called when not inside a BEGIN block
* or when running deferred triggers at COMMIT / PREPARE time . Also , it
* automatically does CommitTransactionCommand / StartTransactionCommand
* instead of expecting the caller to do it .
* TBLOCK_IMPLICIT_INPROGRESS , TBLOCK_END , and TBLOCK_PREPARE states ,
* and therefore it can safely be used in functions that might be called
* when not inside a BEGIN block or when running deferred triggers at
* COMMIT / PREPARE time . Also , it automatically does
* CommitTransactionCommand / StartTransactionCommand instead of expecting
* the caller to do it .
*/
void
BeginInternalSubTransaction ( char * name )
@ -4076,6 +4199,7 @@ BeginInternalSubTransaction(char *name)
{
case TBLOCK_STARTED :
case TBLOCK_INPROGRESS :
case TBLOCK_IMPLICIT_INPROGRESS :
case TBLOCK_END :
case TBLOCK_PREPARE :
case TBLOCK_SUBINPROGRESS :
@ -4180,6 +4304,7 @@ RollbackAndReleaseCurrentSubTransaction(void)
case TBLOCK_DEFAULT :
case TBLOCK_STARTED :
case TBLOCK_BEGIN :
case TBLOCK_IMPLICIT_INPROGRESS :
case TBLOCK_PARALLEL_INPROGRESS :
case TBLOCK_SUBBEGIN :
case TBLOCK_INPROGRESS :
@ -4211,6 +4336,7 @@ RollbackAndReleaseCurrentSubTransaction(void)
s = CurrentTransactionState ; /* changed by pop */
AssertState ( s - > blockState = = TBLOCK_SUBINPROGRESS | |
s - > blockState = = TBLOCK_INPROGRESS | |
s - > blockState = = TBLOCK_IMPLICIT_INPROGRESS | |
s - > blockState = = TBLOCK_STARTED ) ;
}
@ -4259,6 +4385,7 @@ AbortOutOfAnyTransaction(void)
case TBLOCK_STARTED :
case TBLOCK_BEGIN :
case TBLOCK_INPROGRESS :
case TBLOCK_IMPLICIT_INPROGRESS :
case TBLOCK_PARALLEL_INPROGRESS :
case TBLOCK_END :
case TBLOCK_ABORT_PENDING :
@ -4369,6 +4496,7 @@ TransactionBlockStatusCode(void)
case TBLOCK_BEGIN :
case TBLOCK_SUBBEGIN :
case TBLOCK_INPROGRESS :
case TBLOCK_IMPLICIT_INPROGRESS :
case TBLOCK_PARALLEL_INPROGRESS :
case TBLOCK_SUBINPROGRESS :
case TBLOCK_END :
@ -5036,6 +5164,8 @@ BlockStateAsString(TBlockState blockState)
return " BEGIN " ;
case TBLOCK_INPROGRESS :
return " INPROGRESS " ;
case TBLOCK_IMPLICIT_INPROGRESS :
return " IMPLICIT_INPROGRESS " ;
case TBLOCK_PARALLEL_INPROGRESS :
return " PARALLEL_INPROGRESS " ;
case TBLOCK_END :