@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $ PostgreSQL : pgsql / src / backend / access / transam / xact . c , v 1.171 2004 / 07 / 17 03 : 28 : 23 tgl Exp $
* $ PostgreSQL : pgsql / src / backend / access / transam / xact . c , v 1.172 2004 / 07 / 27 05 : 10 : 49 tgl Exp $
*
* NOTES
* Transaction aborts can now occur two ways :
@ -186,21 +186,26 @@ typedef enum TransState
*/
typedef enum TBlockState
{
/* not-in-transaction-block states */
TBLOCK_DEFAULT ,
TBLOCK_STARTED ,
/* transaction block states */
TBLOCK_BEGIN ,
TBLOCK_INPROGRESS ,
TBLOCK_END ,
TBLOCK_ABORT ,
TBLOCK_ENDABORT ,
/* subtransaction states */
TBLOCK_SUBBEGIN ,
TBLOCK_SUBBEGINABORT ,
TBLOCK_SUBINPROGRESS ,
TBLOCK_SUBEND ,
TBLOCK_SUBABORT ,
TBLOCK_SUBENDABORT_OK ,
TBLOCK_SUBENDABORT_ERROR
TBLOCK_SUBABORT_PENDING ,
TBLOCK_SUBENDABORT_ALL ,
TBLOCK_SUBENDABORT_RELEASE ,
TBLOCK_SUBENDABORT
} TBlockState ;
/*
@ -209,6 +214,8 @@ typedef enum TBlockState
typedef struct TransactionStateData
{
TransactionId transactionIdData ; /* my XID */
char * name ; /* savepoint name, if any */
int savepointLevel ; /* savepoint level */
CommandId commandId ; /* current CID */
TransState state ; /* low-level state */
TBlockState blockState ; /* high-level state */
@ -245,6 +252,8 @@ static void CleanupSubTransaction(void);
static void StartAbortedSubTransaction ( void ) ;
static void PushTransaction ( void ) ;
static void PopTransaction ( void ) ;
static void CommitTransactionToLevel ( int level ) ;
static char * CleanupAbortedSubTransactions ( bool returnName ) ;
static void AtSubAbort_Memory ( void ) ;
static void AtSubCleanup_Memory ( void ) ;
@ -264,6 +273,8 @@ static const char *TransStateAsString(TransState state);
*/
static TransactionStateData TopTransactionStateData = {
0 , /* transaction id */
NULL , /* savepoint name */
0 , /* savepoint level */
FirstCommandId , /* command id */
TRANS_DEFAULT , /* transaction state */
TBLOCK_DEFAULT , /* transaction block state from the client
@ -1638,11 +1649,12 @@ StartTransactionCommand(void)
case TBLOCK_STARTED :
case TBLOCK_BEGIN :
case TBLOCK_SUBBEGIN :
case TBLOCK_SUBBEGINABORT :
case TBLOCK_END :
case TBLOCK_SUBEND :
case TBLOCK_SUBENDABORT_OK :
case TBLOCK_SUBENDABORT_ERROR :
case TBLOCK_SUBENDABORT_ALL :
case TBLOCK_SUBENDABORT :
case TBLOCK_SUBABORT_PENDING :
case TBLOCK_SUBENDABORT_RELEASE :
case TBLOCK_ENDABORT :
elog ( FATAL , " StartTransactionCommand: unexpected state %s " ,
BlockStateAsString ( s - > blockState ) ) ;
@ -1670,10 +1682,13 @@ CommitTransactionCommand(void)
/*
* This shouldn ' t happen , because it means the previous
* StartTransactionCommand didn ' t set the STARTED state
* appropiately .
* appropriately , or we didn ' t manage previous pending
* abort states .
*/
case TBLOCK_DEFAULT :
elog ( FATAL , " CommitTransactionCommand: unexpected TBLOCK_DEFAULT " ) ;
case TBLOCK_SUBABORT_PENDING :
elog ( FATAL , " CommitTransactionCommand: unexpected state %s " ,
BlockStateAsString ( s - > blockState ) ) ;
break ;
/*
@ -1710,6 +1725,12 @@ CommitTransactionCommand(void)
* default state .
*/
case TBLOCK_END :
/* commit all open subtransactions */
if ( s - > nestingLevel > 1 )
CommitTransactionToLevel ( 2 ) ;
s = CurrentTransactionState ;
Assert ( s - > parent = = NULL ) ;
/* and now the outer transaction */
CommitTransaction ( ) ;
s - > blockState = TBLOCK_DEFAULT ;
break ;
@ -1734,7 +1755,17 @@ CommitTransactionCommand(void)
break ;
/*
* We were just issued a BEGIN inside a transaction block .
* Ditto , but in a subtransaction . AbortOutOfAnyTransaction
* will do the dirty work .
*/
case TBLOCK_SUBENDABORT_ALL :
AbortOutOfAnyTransaction ( ) ;
s = CurrentTransactionState ; /* changed by AbortOutOfAnyTransaction */
/* AbortOutOfAnyTransaction sets the blockState */
break ;
/*
* We were just issued a SAVEPOINT inside a transaction block .
* Start a subtransaction . ( BeginTransactionBlock already
* did PushTransaction , so as to have someplace to put the
* SUBBEGIN state . )
@ -1744,15 +1775,6 @@ CommitTransactionCommand(void)
s - > blockState = TBLOCK_SUBINPROGRESS ;
break ;
/*
* We were issued a BEGIN inside an aborted transaction block .
* Start a subtransaction , and put it in aborted state .
*/
case TBLOCK_SUBBEGINABORT :
StartAbortedSubTransaction ( ) ;
s - > blockState = TBLOCK_SUBABORT ;
break ;
/*
* Inside a subtransaction , increment the command counter .
*/
@ -1761,7 +1783,7 @@ CommitTransactionCommand(void)
break ;
/*
* We were issued a COMMIT command , so we end the current
* We were issued a RELEASE command , so we end the current
* subtransaction and return to the parent transaction .
*/
case TBLOCK_SUBEND :
@ -1777,29 +1799,80 @@ CommitTransactionCommand(void)
break ;
/*
* We are ending an aborted subtransaction via ROLLBACK ,
* so the parent can be allowed to live .
* The current subtransaction is ending . Do the equivalent
* of a ROLLBACK TO followed by a RELEASE command .
*/
case TBLOCK_SUBENDABORT_OK :
CleanupSubTransaction ( ) ;
PopTransaction ( ) ;
s = CurrentTransactionState ; /* changed by pop */
case TBLOCK_SUBENDABORT_RELEASE :
CleanupAbortedSubTransactions ( false ) ;
break ;
/*
* We are ending an aborted subtransaction via COMMIT .
* End the subtransaction , and abort the parent too .
* The current subtransaction is ending due to a ROLLBACK
* TO command , so close all savepoints up to the target
* level . When finished , recreate the savepoint .
*/
case TBLOCK_SUBENDABORT_ERROR :
CleanupSubTransaction ( ) ;
PopTransaction ( ) ;
s = CurrentTransactionState ; /* changed by pop */
Assert ( s - > blockState ! = TBLOCK_SUBENDABORT_ERROR ) ;
AbortCurrentTransaction ( ) ;
case TBLOCK_SUBENDABORT :
{
char * name = CleanupAbortedSubTransactions ( true ) ;
Assert ( PointerIsValid ( name ) ) ;
DefineSavepoint ( name ) ;
s = CurrentTransactionState ; /* changed by DefineSavepoint */
pfree ( name ) ;
/* This is the same as TBLOCK_SUBBEGIN case */
AssertState ( s - > blockState = = TBLOCK_SUBBEGIN ) ;
StartSubTransaction ( ) ;
s - > blockState = TBLOCK_SUBINPROGRESS ;
}
break ;
}
}
/*
* CleanupAbortedSubTransactions
*
* Helper function for CommitTransactionCommand . Aborts and cleans up
* dead subtransactions after a ROLLBACK TO command . Optionally returns
* the name of the last dead subtransaction so it can be reused to redefine
* the savepoint . ( Caller is responsible for pfree ' ing the result . )
*/
static char *
CleanupAbortedSubTransactions ( bool returnName )
{
TransactionState s = CurrentTransactionState ;
char * name = NULL ;
AssertState ( PointerIsValid ( s - > parent ) ) ;
Assert ( s - > parent - > blockState = = TBLOCK_SUBINPROGRESS | |
s - > parent - > blockState = = TBLOCK_INPROGRESS | |
s - > parent - > blockState = = TBLOCK_SUBABORT_PENDING ) ;
/*
* Abort everything up to the target level . The current
* subtransaction only needs cleanup . If we need to save the name ,
* look for the last subtransaction in TBLOCK_SUBABORT_PENDING state .
*/
if ( returnName & & s - > parent - > blockState ! = TBLOCK_SUBABORT_PENDING )
name = MemoryContextStrdup ( TopMemoryContext , s - > name ) ;
CleanupSubTransaction ( ) ;
PopTransaction ( ) ;
s = CurrentTransactionState ; /* changed by pop */
while ( s - > blockState = = TBLOCK_SUBABORT_PENDING )
{
AbortSubTransaction ( ) ;
if ( returnName & & s - > parent - > blockState ! = TBLOCK_SUBABORT_PENDING )
name = MemoryContextStrdup ( TopMemoryContext , s - > name ) ;
CleanupSubTransaction ( ) ;
PopTransaction ( ) ;
s = CurrentTransactionState ;
}
return name ;
}
/*
* AbortCurrentTransaction
*/
@ -1887,7 +1960,6 @@ AbortCurrentTransaction(void)
* in aborted state .
*/
case TBLOCK_SUBBEGIN :
case TBLOCK_SUBBEGINABORT :
StartAbortedSubTransaction ( ) ;
s - > blockState = TBLOCK_SUBABORT ;
break ;
@ -1902,29 +1974,36 @@ AbortCurrentTransaction(void)
* we have to abort the parent transaction too .
*/
case TBLOCK_SUBEND :
case TBLOCK_SUBABORT_PENDING :
AbortSubTransaction ( ) ;
CleanupSubTransaction ( ) ;
PopTransaction ( ) ;
s = CurrentTransactionState ; /* changed by pop */
Assert ( s - > blockState ! = TBLOCK_SUBEND & &
s - > blockState ! = TBLOCK_SUBENDABORT_OK & &
s - > blockState ! = TBLOCK_SUBENDABORT_ERROR ) ;
s - > blockState ! = TBLOCK_SUBENDABORT ) ;
AbortCurrentTransaction ( ) ;
break ;
/*
* Same as above , except the Abort ( ) was already done .
*/
case TBLOCK_SUBENDABORT_OK :
case TBLOCK_SUBENDABORT_ERROR :
case TBLOCK_SUBENDABORT :
case TBLOCK_SUBENDABORT_RELEASE :
CleanupSubTransaction ( ) ;
PopTransaction ( ) ;
s = CurrentTransactionState ; /* changed by pop */
Assert ( s - > blockState ! = TBLOCK_SUBEND & &
s - > blockState ! = TBLOCK_SUBENDABORT_OK & &
s - > blockState ! = TBLOCK_SUBENDABORT_ERROR ) ;
s - > blockState ! = TBLOCK_SUBENDABORT ) ;
AbortCurrentTransaction ( ) ;
break ;
/*
* We are already aborting the whole transaction tree .
* Do nothing , CommitTransactionCommand will call
* AbortOutOfAnyTransaction and set things straight .
*/
case TBLOCK_SUBENDABORT_ALL :
break ;
}
}
@ -2135,7 +2214,8 @@ BeginTransactionBlock(void)
{
TransactionState s = CurrentTransactionState ;
switch ( s - > blockState ) {
switch ( s - > blockState )
{
/*
* We are not inside a transaction block , so allow one
* to begin .
@ -2146,35 +2226,26 @@ BeginTransactionBlock(void)
/*
* Already a transaction block in progress .
* Start a subtransaction .
*/
case TBLOCK_INPROGRESS :
case TBLOCK_SUBINPROGRESS :
PushTransaction ( ) ;
s = CurrentTransactionState ; /* changed by push */
s - > blockState = TBLOCK_SUBBEGIN ;
break ;
/*
* An aborted transaction block should be allowed to start
* a subtransaction , but it must put it in aborted state .
*/
case TBLOCK_ABORT :
case TBLOCK_SUBABORT :
PushTransaction ( ) ;
s = CurrentTransactionState ; /* changed by push */
s - > blockState = TBLOCK_SUBBEGINABORT ;
ereport ( WARNING ,
( errcode ( ERRCODE_ACTIVE_SQL_TRANSACTION ) ,
errmsg ( " there is already a transaction in progress " ) ) ) ;
break ;
/* These cases are invalid. Reject them altogether. */
case TBLOCK_DEFAULT :
case TBLOCK_BEGIN :
case TBLOCK_SUBBEGIN :
case TBLOCK_SUBBEGINABORT :
case TBLOCK_ENDABORT :
case TBLOCK_END :
case TBLOCK_SUBENDABORT_OK :
case TBLOCK_SUBENDABORT_ERROR :
case TBLOCK_SUBENDABORT_ALL :
case TBLOCK_SUBENDABORT :
case TBLOCK_SUBABORT_PENDING :
case TBLOCK_SUBENDABORT_RELEASE :
case TBLOCK_SUBEND :
elog ( FATAL , " BeginTransactionBlock: unexpected state %s " ,
BlockStateAsString ( s - > blockState ) ) ;
@ -2185,34 +2256,32 @@ BeginTransactionBlock(void)
/*
* EndTransactionBlock
* This executes a COMMIT command .
*
* Since COMMIT may actually do a ROLLBACK , the result indicates what
* happened : TRUE for COMMIT , FALSE for ROLLBACK .
*/
void
bool
EndTransactionBlock ( void )
{
TransactionState s = CurrentTransactionState ;
bool result = false ;
switch ( s - > blockState ) {
switch ( s - > blockState )
{
/*
* here w e are in a transaction block which should commit when we
* W e are in a transaction block which should commit when we
* get to the upcoming CommitTransactionCommand ( ) so we set the
* state to " END " . CommitTransactionCommand ( ) will recognize this
* and commit the transaction and return us to the default state
* and commit the transaction and return us to the default state .
*/
case TBLOCK_INPROGRESS :
s - > blockState = TBLOCK_END ;
break ;
/*
* here we are in a subtransaction block . Signal
* CommitTransactionCommand ( ) to end it and return to the
* parent transaction .
*/
case TBLOCK_SUBINPROGRESS :
s - > blockState = TBLOCK_SUBEND ;
s - > blockState = TBLOCK_END ;
result = true ;
break ;
/*
* here , w e are in a transaction block which aborted . Since the
* We are in a transaction block which aborted . Since the
* AbortTransaction ( ) was already done , we need only
* change to the special " END ABORT " state . The upcoming
* CommitTransactionCommand ( ) will recognise this and then put us
@ -2223,13 +2292,12 @@ EndTransactionBlock(void)
break ;
/*
* here we are in an aborted subtransaction . Signal
* CommitTransactionCommand ( ) to clean up and return to the
* parent transaction . Since the user said COMMIT , we must
* fail the parent transaction .
* Here we are inside an aborted subtransaction . Go to the " abort
* the whole tree " state so that CommitTransactionCommand() calls
* AbortOutOfAnyTransaction .
*/
case TBLOCK_SUBABORT :
s - > blockState = TBLOCK_SUBENDABORT_ERROR ;
s - > blockState = TBLOCK_SUBENDABORT_ALL ;
break ;
case TBLOCK_STARTED :
@ -2252,14 +2320,17 @@ EndTransactionBlock(void)
case TBLOCK_ENDABORT :
case TBLOCK_END :
case TBLOCK_SUBBEGIN :
case TBLOCK_SUBBEGINABORT :
case TBLOCK_SUBEND :
case TBLOCK_SUBENDABORT_OK :
case TBLOCK_SUBENDABORT_ERROR :
case TBLOCK_SUBENDABORT_ALL :
case TBLOCK_SUBENDABORT :
case TBLOCK_SUBABORT_PENDING :
case TBLOCK_SUBENDABORT_RELEASE :
elog ( FATAL , " EndTransactionBlock: unexpected state %s " ,
BlockStateAsString ( s - > blockState ) ) ;
break ;
}
return result ;
}
/*
@ -2271,27 +2342,32 @@ UserAbortTransactionBlock(void)
{
TransactionState s = CurrentTransactionState ;
switch ( s - > blockState ) {
/*
* here we are inside a failed transaction block and we got an abort
* command from the user . Abort processing is already done , we just
* need to move to the ENDABORT state so we will end up in the default
* state after the upcoming CommitTransactionCommand ( ) .
*/
switch ( s - > blockState )
{
/*
* We are inside a failed transaction block and we got an
* abort command from the user . Abort processing is already
* done , we just need to move to the ENDABORT state so we will
* end up in the default state after the upcoming
* CommitTransactionCommand ( ) .
*/
case TBLOCK_ABORT :
s - > blockState = TBLOCK_ENDABORT ;
break ;
/*
* Ditto , for a subtransaction . Here it is okay to allow the
* parent transaction to continue .
* We are inside a failed subtransaction and we got an
* abort command from the user . Abort processing is already
* done , so go to the " abort all " state and
* CommitTransactionCommand will call AbortOutOfAnyTransaction
* to set things straight .
*/
case TBLOCK_SUBABORT :
s - > blockState = TBLOCK_SUBENDABORT_OK ;
s - > blockState = TBLOCK_SUBENDABORT_ALL ;
break ;
/*
* here w e are inside a transaction block and we got an abort
* W e are inside a transaction block and we got an abort
* command from the user , so we move to the ENDABORT state and
* do abort processing so we will end up in the default state
* after the upcoming CommitTransactionCommand ( ) .
@ -2301,17 +2377,22 @@ UserAbortTransactionBlock(void)
s - > blockState = TBLOCK_ENDABORT ;
break ;
/* Ditto, for a subtransaction. */
/*
* We are inside a subtransaction . Abort the current
* subtransaction and go to the " abort all " state , so
* CommitTransactionCommand will call AbortOutOfAnyTransaction
* to set things straight .
*/
case TBLOCK_SUBINPROGRESS :
AbortSubTransaction ( ) ;
s - > blockState = TBLOCK_SUBENDABORT_OK ;
s - > blockState = TBLOCK_SUBENDABORT_ALL ;
break ;
/*
* here , t he user issued ABORT when not inside a
* transaction . Issue a WARNING and go to abort state . The
* upcoming call to CommitTransactionCommand ( ) will then put us
* back into the default state .
* T he user issued ABORT when not inside a transaction . Issue
* a WARNING and go to abort state . The upcoming call to
* CommitTransactionCommand ( ) will then put us back into the
* default state .
*/
case TBLOCK_STARTED :
ereport ( WARNING ,
@ -2321,21 +2402,265 @@ UserAbortTransactionBlock(void)
s - > blockState = TBLOCK_ENDABORT ;
break ;
/* t hese cases are invalid. */
/* T hese cases are invalid. */
case TBLOCK_DEFAULT :
case TBLOCK_BEGIN :
case TBLOCK_END :
case TBLOCK_ENDABORT :
case TBLOCK_SUBEND :
case TBLOCK_SUBENDABORT_OK :
case TBLOCK_SUBENDABORT_ERROR :
case TBLOCK_SUBENDABORT_ALL :
case TBLOCK_SUBENDABORT :
case TBLOCK_SUBABORT_PENDING :
case TBLOCK_SUBENDABORT_RELEASE :
case TBLOCK_SUBBEGIN :
case TBLOCK_SUBBEGINABORT :
elog ( FATAL , " UserAbortTransactionBlock: unexpected state %s " ,
BlockStateAsString ( s - > blockState ) ) ;
break ;
}
}
/*
* DefineSavepoint
* This executes a SAVEPOINT command .
*/
void
DefineSavepoint ( char * name )
{
TransactionState s = CurrentTransactionState ;
switch ( s - > blockState )
{
case TBLOCK_INPROGRESS :
case TBLOCK_SUBINPROGRESS :
/* Normal subtransaction start */
PushTransaction ( ) ;
s = CurrentTransactionState ; /* changed by push */
/*
* Note that we are allocating the savepoint name in the
* parent transaction ' s CurTransactionContext , since we
* don ' t yet have a transaction context for the new guy .
*/
s - > name = MemoryContextStrdup ( CurTransactionContext , name ) ;
s - > blockState = TBLOCK_SUBBEGIN ;
break ;
/* These cases are invalid. Reject them altogether. */
case TBLOCK_DEFAULT :
case TBLOCK_STARTED :
case TBLOCK_BEGIN :
case TBLOCK_SUBBEGIN :
case TBLOCK_ABORT :
case TBLOCK_SUBABORT :
case TBLOCK_ENDABORT :
case TBLOCK_END :
case TBLOCK_SUBENDABORT_ALL :
case TBLOCK_SUBENDABORT :
case TBLOCK_SUBABORT_PENDING :
case TBLOCK_SUBENDABORT_RELEASE :
case TBLOCK_SUBEND :
elog ( FATAL , " BeginTransactionBlock: unexpected state %s " ,
BlockStateAsString ( s - > blockState ) ) ;
break ;
}
}
/*
* ReleaseSavepoint
* This executes a RELEASE command .
*/
void
ReleaseSavepoint ( List * options )
{
TransactionState s = CurrentTransactionState ;
TransactionState target = s ;
char * name = NULL ;
ListCell * cell ;
/*
* Check valid block state transaction status .
*/
switch ( s - > blockState )
{
case TBLOCK_INPROGRESS :
case TBLOCK_ABORT :
ereport ( ERROR ,
( errcode ( ERRCODE_S_E_INVALID_SPECIFICATION ) ,
errmsg ( " no such savepoint " ) ) ) ;
break ;
/*
* We are in a non - aborted subtransaction . This is
* the only valid case .
*/
case TBLOCK_SUBINPROGRESS :
break ;
/* these cases are invalid. */
case TBLOCK_DEFAULT :
case TBLOCK_STARTED :
case TBLOCK_BEGIN :
case TBLOCK_ENDABORT :
case TBLOCK_END :
case TBLOCK_SUBABORT :
case TBLOCK_SUBBEGIN :
case TBLOCK_SUBEND :
case TBLOCK_SUBENDABORT_ALL :
case TBLOCK_SUBENDABORT :
case TBLOCK_SUBABORT_PENDING :
case TBLOCK_SUBENDABORT_RELEASE :
elog ( FATAL , " ReleaseSavepoint: unexpected state %s " ,
BlockStateAsString ( s - > blockState ) ) ;
break ;
}
foreach ( cell , options )
{
DefElem * elem = lfirst ( cell ) ;
if ( strcmp ( elem - > defname , " savepoint_name " ) = = 0 )
name = strVal ( elem - > arg ) ;
}
Assert ( PointerIsValid ( name ) ) ;
while ( target ! = NULL )
{
if ( PointerIsValid ( target - > name ) & & strcmp ( target - > name , name ) = = 0 )
break ;
target = target - > parent ;
}
if ( ! PointerIsValid ( target ) )
ereport ( ERROR ,
( errcode ( ERRCODE_S_E_INVALID_SPECIFICATION ) ,
errmsg ( " no such savepoint " ) ) ) ;
CommitTransactionToLevel ( target - > nestingLevel ) ;
}
/*
* RollbackToSavepoint
* This executes a ROLLBACK TO < savepoint > command .
*/
void
RollbackToSavepoint ( List * options )
{
TransactionState s = CurrentTransactionState ;
TransactionState target ,
xact ;
ListCell * cell ;
char * name = NULL ;
switch ( s - > blockState )
{
/*
* We can ' t rollback to a savepoint if there is no saveopint
* defined .
*/
case TBLOCK_ABORT :
case TBLOCK_INPROGRESS :
ereport ( ERROR ,
( errcode ( ERRCODE_S_E_INVALID_SPECIFICATION ) ,
errmsg ( " no such savepoint " ) ) ) ;
break ;
/*
* There is at least one savepoint , so proceed .
*/
case TBLOCK_SUBABORT :
case TBLOCK_SUBINPROGRESS :
/*
* Have to do AbortSubTransaction , but first check
* if this is the right subtransaction
*/
break ;
/* these cases are invalid. */
case TBLOCK_DEFAULT :
case TBLOCK_STARTED :
case TBLOCK_BEGIN :
case TBLOCK_END :
case TBLOCK_ENDABORT :
case TBLOCK_SUBEND :
case TBLOCK_SUBENDABORT_ALL :
case TBLOCK_SUBENDABORT :
case TBLOCK_SUBABORT_PENDING :
case TBLOCK_SUBENDABORT_RELEASE :
case TBLOCK_SUBBEGIN :
elog ( FATAL , " RollbackToSavepoint: unexpected state %s " ,
BlockStateAsString ( s - > blockState ) ) ;
break ;
}
foreach ( cell , options )
{
DefElem * elem = lfirst ( cell ) ;
if ( strcmp ( elem - > defname , " savepoint_name " ) = = 0 )
name = strVal ( elem - > arg ) ;
}
Assert ( PointerIsValid ( name ) ) ;
target = CurrentTransactionState ;
while ( target ! = NULL )
{
if ( PointerIsValid ( target - > name ) & & strcmp ( target - > name , name ) = = 0 )
break ;
target = target - > parent ;
/* we don't cross savepoint level boundaries */
if ( target - > savepointLevel ! = s - > savepointLevel )
ereport ( ERROR ,
( errcode ( ERRCODE_S_E_INVALID_SPECIFICATION ) ,
errmsg ( " no such savepoint " ) ) ) ;
}
if ( ! PointerIsValid ( target ) )
ereport ( ERROR ,
( errcode ( ERRCODE_S_E_INVALID_SPECIFICATION ) ,
errmsg ( " no such savepoint " ) ) ) ;
/*
* Abort the current subtransaction , if needed . We can ' t Cleanup the
* savepoint yet , so signal CommitTransactionCommand to do it and
* close all savepoints up to the target level .
*/
if ( s - > blockState = = TBLOCK_SUBINPROGRESS )
AbortSubTransaction ( ) ;
s - > blockState = TBLOCK_SUBENDABORT ;
/*
* Mark " abort pending " all subtransactions up to the target
* subtransaction . ( Except the current subtransaction ! )
*/
xact = CurrentTransactionState ;
while ( xact ! = target )
{
xact = xact - > parent ;
Assert ( PointerIsValid ( xact ) ) ;
Assert ( xact - > blockState = = TBLOCK_SUBINPROGRESS ) ;
xact - > blockState = TBLOCK_SUBABORT_PENDING ;
}
}
/*
* RollbackAndReleaseSavepoint
*
* Executes a ROLLBACK TO command , immediately followed by a RELEASE
* of the same savepoint .
*/
void
RollbackAndReleaseSavepoint ( List * options )
{
TransactionState s ;
RollbackToSavepoint ( options ) ;
s = CurrentTransactionState ;
Assert ( s - > blockState = = TBLOCK_SUBENDABORT ) ;
s - > blockState = TBLOCK_SUBENDABORT_RELEASE ;
}
/*
@ -2375,7 +2700,6 @@ AbortOutOfAnyTransaction(void)
s - > blockState = TBLOCK_DEFAULT ;
break ;
case TBLOCK_SUBBEGIN :
case TBLOCK_SUBBEGINABORT :
/*
* We didn ' t get as far as starting the subxact , so there ' s
* nothing to abort . Just pop back to parent .
@ -2385,6 +2709,7 @@ AbortOutOfAnyTransaction(void)
break ;
case TBLOCK_SUBINPROGRESS :
case TBLOCK_SUBEND :
case TBLOCK_SUBABORT_PENDING :
/* In a subtransaction, so clean it up and abort parent too */
AbortSubTransaction ( ) ;
CleanupSubTransaction ( ) ;
@ -2392,8 +2717,9 @@ AbortOutOfAnyTransaction(void)
s = CurrentTransactionState ; /* changed by pop */
break ;
case TBLOCK_SUBABORT :
case TBLOCK_SUBENDABORT_OK :
case TBLOCK_SUBENDABORT_ERROR :
case TBLOCK_SUBENDABORT_ALL :
case TBLOCK_SUBENDABORT :
case TBLOCK_SUBENDABORT_RELEASE :
/* As above, but AbortSubTransaction already done */
CleanupSubTransaction ( ) ;
PopTransaction ( ) ;
@ -2406,6 +2732,28 @@ AbortOutOfAnyTransaction(void)
Assert ( s - > parent = = NULL ) ;
}
/*
* CommitTransactionToLevel
*
* Commit everything from the current transaction level
* up to the specified level ( inclusive ) .
*/
void
CommitTransactionToLevel ( int level )
{
TransactionState s = CurrentTransactionState ;
Assert ( s - > state = = TRANS_INPROGRESS ) ;
while ( s - > nestingLevel > = level )
{
CommitSubTransaction ( ) ;
PopTransaction ( ) ;
s = CurrentTransactionState ; /* changed by pop */
Assert ( s - > state = = TRANS_INPROGRESS ) ;
}
}
/*
* IsTransactionBlock - - - are we within a transaction block ?
*/
@ -2461,9 +2809,10 @@ TransactionBlockStatusCode(void)
case TBLOCK_ABORT :
case TBLOCK_ENDABORT :
case TBLOCK_SUBABORT :
case TBLOCK_SUBENDABORT_OK :
case TBLOCK_SUBENDABORT_ERROR :
case TBLOCK_SUBBEGINABORT :
case TBLOCK_SUBENDABORT_ALL :
case TBLOCK_SUBENDABORT :
case TBLOCK_SUBABORT_PENDING :
case TBLOCK_SUBENDABORT_RELEASE :
return ' E ' ; /* in failed transaction */
}
@ -2481,7 +2830,8 @@ IsSubTransaction(void)
{
TransactionState s = CurrentTransactionState ;
switch ( s - > blockState ) {
switch ( s - > blockState )
{
case TBLOCK_DEFAULT :
case TBLOCK_STARTED :
case TBLOCK_BEGIN :
@ -2491,12 +2841,13 @@ IsSubTransaction(void)
case TBLOCK_ENDABORT :
return false ;
case TBLOCK_SUBBEGIN :
case TBLOCK_SUBBEGINABORT :
case TBLOCK_SUBINPROGRESS :
case TBLOCK_SUBABORT :
case TBLOCK_SUBEND :
case TBLOCK_SUBENDABORT_OK :
case TBLOCK_SUBENDABORT_ERROR :
case TBLOCK_SUBENDABORT_ALL :
case TBLOCK_SUBENDABORT :
case TBLOCK_SUBABORT_PENDING :
case TBLOCK_SUBENDABORT_RELEASE :
return true ;
}
@ -2532,6 +2883,8 @@ StartSubTransaction(void)
SubTransSetParent ( s - > transactionIdData , s - > parent - > transactionIdData ) ;
XactLockTableInsert ( s - > transactionIdData ) ;
/*
* Finish setup of other transaction state fields .
*/
@ -2619,6 +2972,9 @@ AbortSubTransaction(void)
ShowTransactionState ( " AbortSubTransaction " ) ;
if ( s - > state ! = TRANS_INPROGRESS )
elog ( WARNING , " AbortSubTransaction and not in in-progress state " ) ;
HOLD_INTERRUPTS ( ) ;
s - > state = TRANS_ABORT ;
@ -2762,6 +3118,9 @@ StartAbortedSubTransaction(void)
/*
* PushTransaction
* Set up transaction state for a subtransaction
*
* The caller has to make sure to always reassign CurrentTransactionState
* if it has a local pointer to it after calling this function .
*/
static void
PushTransaction ( void )
@ -2777,6 +3136,7 @@ PushTransaction(void)
sizeof ( TransactionStateData ) ) ;
s - > parent = p ;
s - > nestingLevel = p - > nestingLevel + 1 ;
s - > savepointLevel = p - > savepointLevel ;
s - > state = TRANS_DEFAULT ;
s - > blockState = TBLOCK_SUBBEGIN ;
@ -2798,6 +3158,9 @@ PushTransaction(void)
/*
* PopTransaction
* Pop back to parent transaction state
*
* The caller has to make sure to always reassign CurrentTransactionState
* if it has a local pointer to it after calling this function .
*/
static void
PopTransaction ( void )
@ -2824,6 +3187,8 @@ PopTransaction(void)
CurrentResourceOwner = s - > parent - > curTransactionOwner ;
/* Free the old child structure */
if ( s - > name )
pfree ( s - > name ) ;
pfree ( s ) ;
}
@ -2854,7 +3219,8 @@ ShowTransactionStateRec(TransactionState s)
/* use ereport to suppress computation if msg will not be printed */
ereport ( DEBUG2 ,
( errmsg_internal ( " blockState: %13s; state: %7s, xid/cid: %u/%02u, nestlvl: %d, children: %s " ,
( errmsg_internal ( " name: %s; blockState: %13s; state: %7s, xid/cid: %u/%02u, nestlvl: %d, children: %s " ,
PointerIsValid ( s - > name ) ? s - > name : " unnamed " ,
BlockStateAsString ( s - > blockState ) ,
TransStateAsString ( s - > state ) ,
( unsigned int ) s - > transactionIdData ,
@ -2870,7 +3236,8 @@ ShowTransactionStateRec(TransactionState s)
static const char *
BlockStateAsString ( TBlockState blockState )
{
switch ( blockState ) {
switch ( blockState )
{
case TBLOCK_DEFAULT :
return " DEFAULT " ;
case TBLOCK_STARTED :
@ -2887,18 +3254,20 @@ BlockStateAsString(TBlockState blockState)
return " ENDABORT " ;
case TBLOCK_SUBBEGIN :
return " SUB BEGIN " ;
case TBLOCK_SUBBEGINABORT :
return " SUB BEGIN AB " ;
case TBLOCK_SUBINPROGRESS :
return " SUB INPROGRS " ;
case TBLOCK_SUBEND :
return " SUB END " ;
case TBLOCK_SUBABORT :
return " SUB ABORT " ;
case TBLOCK_SUBENDABORT_OK :
return " SUB ENDAB OK " ;
case TBLOCK_SUBENDABORT_ERROR :
return " SUB ENDAB ERR " ;
case TBLOCK_SUBENDABORT_ALL :
return " SUB ENDAB ALL " ;
case TBLOCK_SUBENDABORT :
return " SUB ENDAB " ;
case TBLOCK_SUBABORT_PENDING :
return " SUB ABRT PEND " ;
case TBLOCK_SUBENDABORT_RELEASE :
return " SUB ENDAB REL " ;
}
return " UNRECOGNIZED " ;
}
@ -2910,7 +3279,8 @@ BlockStateAsString(TBlockState blockState)
static const char *
TransStateAsString ( TransState state )
{
switch ( state ) {
switch ( state )
{
case TRANS_DEFAULT :
return " DEFAULT " ;
case TRANS_START :