*** /dev/null
--- b/doc/src/sgml/ref/start_autonomous_transaction.sgml
***************
*** 0 ****
--- 1,51 ----
+
+
+
+
+ START AUTONOMOUS TRANSACTION
+
+
+
+ START AUTONOMOUS TRANSACTION
+ 7
+ SQL - Language Statements
+
+
+
+ START AUTONOMOUS TRANSACTION
+ start an autonomous transaction block
+
+
+
+
+ START AUTONOMOUS TRANSACTION [ transaction_mode]
+
+ where transaction_mode is one of:
+
+ READ WRITE | READ ONLY
+
+
+
+
+
+ Description
+
+
+ This command begins a new autonomous transaction block. This can be started
+ only with in already running transaction block.
+
+
+
+ An autonomous transaction has its own and
+ scope to ensure
+ that its outcome does not effect the caller's uncommitted changes.
+ Additionally, the and
+ in the calling transaction should not effect the changes that were finalized
+ on the completion of autonomous transaction itself. If read/write mode is
+ specified, the new transaction has those characteristics.
+
+
+
*** a/src/backend/access/transam/twophase.c
--- b/src/backend/access/transam/twophase.c
***************
*** 973,979 **** StartPrepare(GlobalTransaction gxact)
hdr.ncommitrels = smgrGetPendingDeletes(true, &commitrels);
hdr.nabortrels = smgrGetPendingDeletes(false, &abortrels);
hdr.ninvalmsgs = xactGetCommittedInvalidationMessages(&invalmsgs,
! &hdr.initfileinval);
StrNCpy(hdr.gid, gxact->gid, GIDSIZE);
save_state_data(&hdr, sizeof(TwoPhaseFileHeader));
--- 973,979 ----
hdr.ncommitrels = smgrGetPendingDeletes(true, &commitrels);
hdr.nabortrels = smgrGetPendingDeletes(false, &abortrels);
hdr.ninvalmsgs = xactGetCommittedInvalidationMessages(&invalmsgs,
! &hdr.initfileinval, false);
StrNCpy(hdr.gid, gxact->gid, GIDSIZE);
save_state_data(&hdr, sizeof(TwoPhaseFileHeader));
*** a/src/backend/access/transam/varsup.c
--- b/src/backend/access/transam/varsup.c
***************
*** 43,49 **** VariableCache ShmemVariableCache = NULL;
* issue a warning about XID wrap.
*/
TransactionId
! GetNewTransactionId(bool isSubXact)
{
TransactionId xid;
--- 43,49 ----
* issue a warning about XID wrap.
*/
TransactionId
! GetNewTransactionId(bool isSubXact, int stateNestinglevel, int autotxlevel)
{
TransactionId xid;
***************
*** 212,217 **** GetNewTransactionId(bool isSubXact)
--- 212,249 ----
volatile PGPROC *myproc = MyProc;
volatile PGXACT *mypgxact = MyPgXact;
+ if (autotxlevel > 0)
+ {
+ int nxids = 0;
+ int autoNestingLevel = 0;
+ volatile PGAutonomousXACT *mypgautonomoustx;
+ mypgautonomoustx = &MyPgAutonomousXact[autotxlevel-1];
+ autoNestingLevel = mypgautonomoustx->nestingLevel;
+
+ /* In top auto tx*/
+ if (stateNestinglevel == autoNestingLevel)
+ {
+ mypgautonomoustx->xid = xid;
+ LWLockRelease(XidGenLock);
+ return xid;
+ }
+
+ /* Subtransaction in auto tx */
+ Assert(autoNestingLevel < stateNestinglevel);
+
+ nxids = mypgautonomoustx->nxids;
+ if (nxids < PGPROC_MAX_CACHED_SUBXIDS)
+ {
+ mypgautonomoustx->subxids.xids[nxids] = xid;
+ mypgautonomoustx->nxids++;
+ }
+ else
+ mypgautonomoustx->overflowed = true;
+
+ LWLockRelease(XidGenLock);
+ return xid;
+ }
+
if (!isSubXact)
mypgxact->xid = xid;
else
*** a/src/backend/access/transam/xact.c
--- b/src/backend/access/transam/xact.c
***************
*** 123,129 **** typedef enum TBlockState
TBLOCK_SUBABORT_END, /* failed subxact, ROLLBACK received */
TBLOCK_SUBABORT_PENDING, /* live subxact, ROLLBACK received */
TBLOCK_SUBRESTART, /* live subxact, ROLLBACK TO received */
! TBLOCK_SUBABORT_RESTART /* failed subxact, ROLLBACK TO received */
} TBlockState;
/*
--- 123,137 ----
TBLOCK_SUBABORT_END, /* failed subxact, ROLLBACK received */
TBLOCK_SUBABORT_PENDING, /* live subxact, ROLLBACK received */
TBLOCK_SUBRESTART, /* live subxact, ROLLBACK TO received */
! TBLOCK_SUBABORT_RESTART, /* failed subxact, ROLLBACK TO received */
!
! TBLOCK_AUTOBEGIN,
! TBLOCK_AUTOINPROGRESS,
! TBLOCK_AUTOCOMMIT,
! TBLOCK_AUTOABORT,
! TBLOCK_AUTOABORT_END,
! TBLOCK_AUTOABORT_PENDING
!
} TBlockState;
/*
***************
*** 149,154 **** typedef struct TransactionStateData
--- 157,164 ----
bool prevXactReadOnly; /* entry-time xact r/o state */
bool startedInRecovery; /* did we start in recovery? */
bool didLogXid; /* has xid been included in WAL record? */
+ MemoryContext preMemoryContext; /* previous memory context */
+ ResourceOwner preResourceOwner; /* previous resource owner */
struct TransactionStateData *parent; /* back link to parent */
} TransactionStateData;
***************
*** 279,285 **** static void StartSubTransaction(void);
static void CommitSubTransaction(void);
static void AbortSubTransaction(void);
static void CleanupSubTransaction(void);
! static void PushTransaction(void);
static void PopTransaction(void);
static void AtSubAbort_Memory(void);
--- 289,296 ----
static void CommitSubTransaction(void);
static void AbortSubTransaction(void);
static void CleanupSubTransaction(void);
! static void PushTransaction(bool isAutoTX, bool readOnly);
! static TransactionId GetTopAutonomousTransactionID(void);
static void PopTransaction(void);
static void AtSubAbort_Memory(void);
***************
*** 294,299 **** static void ShowTransactionStateRec(TransactionState state);
--- 305,314 ----
static const char *BlockStateAsString(TBlockState blockState);
static const char *TransStateAsString(TransState state);
+ extern void GetPreContextAndResource(MemoryContext *preContext,
+ ResourceOwner *preOwner);
+ extern void SetPreContextAndResource(MemoryContext preContext,
+ ResourceOwner preOwner);
/* ----------------------------------------------------------------
* transaction state accessors
***************
*** 332,338 **** IsAbortedTransactionBlockState(void)
TransactionState s = CurrentTransactionState;
if (s->blockState == TBLOCK_ABORT ||
! s->blockState == TBLOCK_SUBABORT)
return true;
return false;
--- 347,354 ----
TransactionState s = CurrentTransactionState;
if (s->blockState == TBLOCK_ABORT ||
! s->blockState == TBLOCK_SUBABORT ||
! s->blockState == TBLOCK_AUTOABORT)
return true;
return false;
***************
*** 348,354 **** IsAbortedTransactionBlockState(void)
TransactionId
GetTopTransactionId(void)
{
! if (!TransactionIdIsValid(TopTransactionStateData.transactionId))
AssignTransactionId(&TopTransactionStateData);
return TopTransactionStateData.transactionId;
}
--- 364,374 ----
TransactionId
GetTopTransactionId(void)
{
! if (MyProc->inAutoTXLevel)
! {
! return GetTopAutonomousTransactionID();
! }
! else if (!TransactionIdIsValid(TopTransactionStateData.transactionId))
AssignTransactionId(&TopTransactionStateData);
return TopTransactionStateData.transactionId;
}
***************
*** 363,368 **** GetTopTransactionId(void)
--- 383,406 ----
TransactionId
GetTopTransactionIdIfAny(void)
{
+ if ((MyProc != NULL) && (MyProc->inAutoTXLevel)
+ && (MyPgAutonomousXact != NULL))
+ {
+ TransactionId result = InvalidTransactionId;
+ TransactionState s = CurrentTransactionState;
+ TransactionState target = s;
+
+ for (;PointerIsValid(target) ; target=target->parent)
+ {
+ if (IS_TOP_AUTO_TX_STATE(target))
+ {
+ result = target->transactionId;
+ break;
+ }
+ }
+ return result;
+ }
+
return TopTransactionStateData.transactionId;
}
***************
*** 450,461 **** AssignTransactionId(TransactionState s)
{
bool isSubXact = (s->parent != NULL);
ResourceOwner currentOwner;
! bool log_unknown_top = false;
/* Assert that caller didn't screw up */
Assert(!TransactionIdIsValid(s->transactionId));
Assert(s->state == TRANS_INPROGRESS);
/*
* Ensure parent(s) have XIDs, so that a child always has an XID later
* than its parent. Musn't recurse here, or we might get a stack overflow
--- 488,519 ----
{
bool isSubXact = (s->parent != NULL);
ResourceOwner currentOwner;
! bool log_unknown_top = false;
! int autotxlevel = 0;
! bool inAutoTx = false;
! PGAutonomousXACT *currentautotx = NULL;
! int i = 0;
!
/* Assert that caller didn't screw up */
Assert(!TransactionIdIsValid(s->transactionId));
Assert(s->state == TRANS_INPROGRESS);
+
+ for (i=0; i < MyProc->inAutoTXLevel; i++)
+ {
+ currentautotx = &MyPgAutonomousXact[i];
+ if (currentautotx->nestingLevel <= s->nestingLevel)
+ {
+ autotxlevel = i+1;
+ if (currentautotx->nestingLevel == s->nestingLevel)
+ {
+ inAutoTx = true;
+ break;
+ }
+ }
+ }
+
/*
* Ensure parent(s) have XIDs, so that a child always has an XID later
* than its parent. Musn't recurse here, or we might get a stack overflow
***************
*** 507,515 **** AssignTransactionId(TransactionState s)
* PG_PROC, the subtrans entry is needed to ensure that other backends see
* the Xid as "running". See GetNewTransactionId.
*/
! s->transactionId = GetNewTransactionId(isSubXact);
! if (isSubXact)
SubTransSetParent(s->transactionId, s->parent->transactionId, false);
/*
--- 565,573 ----
* PG_PROC, the subtrans entry is needed to ensure that other backends see
* the Xid as "running". See GetNewTransactionId.
*/
! s->transactionId = GetNewTransactionId(isSubXact, s->nestingLevel, autotxlevel);
! if (isSubXact && inAutoTx)
SubTransSetParent(s->transactionId, s->parent->transactionId, false);
/*
***************
*** 749,759 **** TransactionIdIsCurrentTransactionId(TransactionId xid)
{
int low,
high;
if (s->state == TRANS_ABORT)
! continue;
if (!TransactionIdIsValid(s->transactionId))
! continue; /* it can't have any child XIDs either */
if (TransactionIdEquals(xid, s->transactionId))
return true;
/* As the childXids array is ordered, we can use binary search */
--- 807,830 ----
{
int low,
high;
+ int isTopAutoTx = IS_TOP_AUTO_TX_STATE(s);
if (s->state == TRANS_ABORT)
! {
! if (isTopAutoTx)
! break;
! else
! continue;
! }
!
if (!TransactionIdIsValid(s->transactionId))
! {
! if (isTopAutoTx)
! break;
! else
! continue; /* it can't have any child XIDs either */
! }
!
if (TransactionIdEquals(xid, s->transactionId))
return true;
/* As the childXids array is ordered, we can use binary search */
***************
*** 773,778 **** TransactionIdIsCurrentTransactionId(TransactionId xid)
--- 844,856 ----
else
high = middle - 1;
}
+
+ /*
+ * If it was auto-tx and till now it did not match, then no need to
+ * search further.
+ */
+ if (isTopAutoTx)
+ break;
}
return false;
***************
*** 992,998 **** AtSubStart_ResourceOwner(void)
* if the xact has no XID. (We compute that here just because it's easier.)
*/
static TransactionId
! RecordTransactionCommit(void)
{
TransactionId xid = GetTopTransactionIdIfAny();
bool markXidCommitted = TransactionIdIsValid(xid);
--- 1070,1076 ----
* if the xact has no XID. (We compute that here just because it's easier.)
*/
static TransactionId
! RecordTransactionCommit(bool isAutoXact)
{
TransactionId xid = GetTopTransactionIdIfAny();
bool markXidCommitted = TransactionIdIsValid(xid);
***************
*** 1005,1017 **** RecordTransactionCommit(void)
SharedInvalidationMessage *invalMessages = NULL;
bool RelcacheInitFileInval = false;
bool wrote_xlog;
/* Get data needed for commit record */
nrels = smgrGetPendingDeletes(true, &rels);
nchildren = xactGetCommittedChildren(&children);
if (XLogStandbyInfoActive())
nmsgs = xactGetCommittedInvalidationMessages(&invalMessages,
! &RelcacheInitFileInval);
wrote_xlog = (XactLastRecEnd != 0);
/*
--- 1083,1098 ----
SharedInvalidationMessage *invalMessages = NULL;
bool RelcacheInitFileInval = false;
bool wrote_xlog;
+ PGAutonomousXACT * currentautox = NULL;
+
/* Get data needed for commit record */
nrels = smgrGetPendingDeletes(true, &rels);
nchildren = xactGetCommittedChildren(&children);
if (XLogStandbyInfoActive())
nmsgs = xactGetCommittedInvalidationMessages(&invalMessages,
! &RelcacheInitFileInval,
! isAutoXact);
wrote_xlog = (XactLastRecEnd != 0);
/*
***************
*** 1039,1045 **** RecordTransactionCommit(void)
* assigned is a sequence advance record due to nextval() --- we want
* to flush that to disk before reporting commit.)
*/
! if (!wrote_xlog)
goto cleanup;
}
else
--- 1120,1126 ----
* assigned is a sequence advance record due to nextval() --- we want
* to flush that to disk before reporting commit.)
*/
! if (!wrote_xlog || isAutoXact)
goto cleanup;
}
else
***************
*** 1068,1074 **** RecordTransactionCommit(void)
* a bit fuzzy, but it doesn't matter.
*/
START_CRIT_SECTION();
! MyPgXact->delayChkpt = true;
SetCurrentTransactionStopTimestamp();
--- 1149,1163 ----
* a bit fuzzy, but it doesn't matter.
*/
START_CRIT_SECTION();
! if(isAutoXact)
! {
! currentautox = GetCurrentPGAutonomousXACT();
! currentautox->delayChkpt = true;
! }
! else
! {
! MyPgXact->delayChkpt = true;
! }
SetCurrentTransactionStopTimestamp();
***************
*** 1229,1240 **** RecordTransactionCommit(void)
*/
if (markXidCommitted)
{
! MyPgXact->delayChkpt = false;
END_CRIT_SECTION();
}
/* Compute latestXid while we have the child XIDs handy */
latestXid = TransactionIdLatest(xid, nchildren, children);
/*
* Wait for synchronous replication, if required.
--- 1318,1339 ----
*/
if (markXidCommitted)
{
! if(isAutoXact)
! {
! currentautox = GetCurrentPGAutonomousXACT();
! currentautox->delayChkpt = false;
! }
! else
! {
! MyPgXact->delayChkpt = false;
! }
END_CRIT_SECTION();
}
/* Compute latestXid while we have the child XIDs handy */
latestXid = TransactionIdLatest(xid, nchildren, children);
+ if (isAutoXact)
+ XidCacheRemoveAutoRunningXids(xid, nchildren, children, latestXid, true);
/*
* Wait for synchronous replication, if required.
***************
*** 1246,1252 **** RecordTransactionCommit(void)
SyncRepWaitForLSN(XactLastRecEnd);
/* Reset XactLastRecEnd until the next transaction writes something */
! XactLastRecEnd = 0;
cleanup:
/* Clean up local data */
--- 1345,1352 ----
SyncRepWaitForLSN(XactLastRecEnd);
/* Reset XactLastRecEnd until the next transaction writes something */
! if (!isAutoXact)
! XactLastRecEnd = 0;
cleanup:
/* Clean up local data */
***************
*** 1542,1552 **** RecordTransactionAbort(bool isSubXact)
* subxacts, because we already have the child XID array at hand. For
* main xacts, the equivalent happens just after this function returns.
*/
! if (isSubXact)
! XidCacheRemoveRunningXids(xid, nchildren, children, latestXid);
/* Reset XactLastRecEnd until the next transaction writes something */
! if (!isSubXact)
XactLastRecEnd = 0;
/* And clean up local data */
--- 1642,1683 ----
* subxacts, because we already have the child XID array at hand. For
* main xacts, the equivalent happens just after this function returns.
*/
! {
! uint8 isAutoTX = MyProc->inAutoTXLevel;
! PGAutonomousXACT *currentautox = NULL;
!
! int autoNestingLevel = 0;
! int stateNestingLevel = CurrentTransactionState->nestingLevel;
!
! if (isSubXact)
! {
! if (isAutoTX)
! {
! currentautox = GetCurrentPGAutonomousXACT();
! autoNestingLevel = currentautox->nestingLevel;
!
! if(stateNestingLevel == autoNestingLevel)
! {
! /* the top of auto TX */
! XidCacheRemoveAutoRunningXids(xid, nchildren, children,
! latestXid, true);
! }
! else
! {
! /* sub TX in auto TX */
! XidCacheRemoveAutoRunningXids(xid, nchildren, children,
! latestXid, false);
! }
! }
! else
! {
! XidCacheRemoveRunningXids(xid, nchildren, children, latestXid);
! }
! }
! }
/* Reset XactLastRecEnd until the next transaction writes something */
! if (!isSubXact )
XactLastRecEnd = 0;
/* And clean up local data */
***************
*** 1945,1951 **** CommitTransaction(void)
/*
* Here is where we really truly commit.
*/
! latestXid = RecordTransactionCommit();
TRACE_POSTGRESQL_TRANSACTION_COMMIT(MyProc->lxid);
--- 2076,2082 ----
/*
* Here is where we really truly commit.
*/
! latestXid = RecordTransactionCommit(false);
TRACE_POSTGRESQL_TRANSACTION_COMMIT(MyProc->lxid);
***************
*** 2539,2544 **** StartTransactionCommand(void)
--- 2670,2676 ----
*/
case TBLOCK_INPROGRESS:
case TBLOCK_SUBINPROGRESS:
+ case TBLOCK_AUTOINPROGRESS:
break;
/*
***************
*** 2551,2556 **** StartTransactionCommand(void)
--- 2683,2689 ----
*/
case TBLOCK_ABORT:
case TBLOCK_SUBABORT:
+ case TBLOCK_AUTOABORT:
break;
/* These cases are invalid. */
***************
*** 2567,2572 **** StartTransactionCommand(void)
--- 2700,2709 ----
case TBLOCK_SUBRESTART:
case TBLOCK_SUBABORT_RESTART:
case TBLOCK_PREPARE:
+ case TBLOCK_AUTOBEGIN:
+ case TBLOCK_AUTOCOMMIT:
+ case TBLOCK_AUTOABORT_PENDING:
+ case TBLOCK_AUTOABORT_END:
elog(ERROR, "StartTransactionCommand: unexpected state %s",
BlockStateAsString(s->blockState));
break;
***************
*** 2702,2708 **** CommitTransactionCommand(void)
} while (s->blockState == TBLOCK_SUBRELEASE);
Assert(s->blockState == TBLOCK_INPROGRESS ||
! s->blockState == TBLOCK_SUBINPROGRESS);
break;
/*
--- 2839,2846 ----
} while (s->blockState == TBLOCK_SUBRELEASE);
Assert(s->blockState == TBLOCK_INPROGRESS ||
! s->blockState == TBLOCK_SUBINPROGRESS ||
! s->blockState == TBLOCK_AUTOINPROGRESS);
break;
/*
***************
*** 2733,2738 **** CommitTransactionCommand(void)
--- 2871,2881 ----
PrepareTransaction();
s->blockState = TBLOCK_DEFAULT;
}
+ else if (s->blockState == TBLOCK_AUTOCOMMIT)
+ {
+ Assert(s->parent != NULL);
+ CommitAutonomousTransaction();
+ }
else
elog(ERROR, "CommitTransactionCommand: unexpected state %s",
BlockStateAsString(s->blockState));
***************
*** 2814,2819 **** CommitTransactionCommand(void)
--- 2957,2986 ----
s->blockState = TBLOCK_SUBINPROGRESS;
}
break;
+ case TBLOCK_AUTOBEGIN:
+ BeginAutonomousTransaction();
+ s->blockState = TBLOCK_AUTOINPROGRESS;
+ break;
+
+ case TBLOCK_AUTOCOMMIT:
+ CommitAutonomousTransaction();
+ break;
+
+ case TBLOCK_AUTOABORT:
+ break;
+
+ case TBLOCK_AUTOABORT_PENDING:
+ AbortAutonomousTransaction();
+ CleanupSubTransaction();
+ break;
+
+ case TBLOCK_AUTOINPROGRESS:
+ CommandCounterIncrement();
+ break;
+
+ case TBLOCK_AUTOABORT_END:
+ CleanupSubTransaction();
+ break;
}
}
***************
*** 2900,2905 **** AbortCurrentTransaction(void)
--- 3067,3073 ----
*/
case TBLOCK_ABORT:
case TBLOCK_SUBABORT:
+ case TBLOCK_AUTOABORT:
break;
/*
***************
*** 2966,2971 **** AbortCurrentTransaction(void)
--- 3134,3155 ----
CleanupSubTransaction();
AbortCurrentTransaction();
break;
+ case TBLOCK_AUTOBEGIN:
+ case TBLOCK_AUTOCOMMIT:
+ case TBLOCK_AUTOABORT_PENDING:
+ AbortAutonomousTransaction();
+ CleanupSubTransaction();
+ break;
+
+ case TBLOCK_AUTOINPROGRESS:
+ AbortAutonomousTransaction();
+ s->blockState = TBLOCK_AUTOABORT;
+ break;
+
+
+ case TBLOCK_AUTOABORT_END:
+ CleanupSubTransaction();
+ break;
}
}
***************
*** 3269,3274 **** BeginTransactionBlock(void)
--- 3453,3460 ----
case TBLOCK_SUBINPROGRESS:
case TBLOCK_ABORT:
case TBLOCK_SUBABORT:
+ case TBLOCK_AUTOINPROGRESS:
+ case TBLOCK_AUTOABORT:
ereport(WARNING,
(errcode(ERRCODE_ACTIVE_SQL_TRANSACTION),
errmsg("there is already a transaction in progress")));
***************
*** 3288,3293 **** BeginTransactionBlock(void)
--- 3474,3483 ----
case TBLOCK_SUBRESTART:
case TBLOCK_SUBABORT_RESTART:
case TBLOCK_PREPARE:
+ case TBLOCK_AUTOBEGIN:
+ case TBLOCK_AUTOCOMMIT:
+ case TBLOCK_AUTOABORT_PENDING:
+ case TBLOCK_AUTOABORT_END:
elog(FATAL, "BeginTransactionBlock: unexpected state %s",
BlockStateAsString(s->blockState));
break;
***************
*** 3312,3317 **** PrepareTransactionBlock(char *gid)
--- 3502,3512 ----
TransactionState s;
bool result;
+ if(MyProc->inAutoTXLevel)
+ {
+ elog(ERROR, "Can't support twophase transaction in autonomous transaction.");
+ }
+
/* Set up to commit the current transaction */
result = EndTransactionBlock();
***************
*** 3387,3393 **** EndTransactionBlock(void)
* open subtransactions and then commit the main transaction.
*/
case TBLOCK_SUBINPROGRESS:
! while (s->parent != NULL)
{
if (s->blockState == TBLOCK_SUBINPROGRESS)
s->blockState = TBLOCK_SUBCOMMIT;
--- 3582,3588 ----
* open subtransactions and then commit the main transaction.
*/
case TBLOCK_SUBINPROGRESS:
! while (s->parent != NULL && !IS_TOP_AUTO_TX_STATE(s))
{
if (s->blockState == TBLOCK_SUBINPROGRESS)
s->blockState = TBLOCK_SUBCOMMIT;
***************
*** 3398,3403 **** EndTransactionBlock(void)
--- 3593,3601 ----
}
if (s->blockState == TBLOCK_INPROGRESS)
s->blockState = TBLOCK_END;
+ else if(s->blockState == TBLOCK_AUTOINPROGRESS)
+ s->blockState = TBLOCK_AUTOCOMMIT;
+
else
elog(FATAL, "EndTransactionBlock: unexpected state %s",
BlockStateAsString(s->blockState));
***************
*** 3410,3416 **** EndTransactionBlock(void)
* transaction.
*/
case TBLOCK_SUBABORT:
! while (s->parent != NULL)
{
if (s->blockState == TBLOCK_SUBINPROGRESS)
s->blockState = TBLOCK_SUBABORT_PENDING;
--- 3608,3614 ----
* transaction.
*/
case TBLOCK_SUBABORT:
! while (s->parent != NULL && !IS_TOP_AUTO_TX_STATE(s))
{
if (s->blockState == TBLOCK_SUBINPROGRESS)
s->blockState = TBLOCK_SUBABORT_PENDING;
***************
*** 3425,3430 **** EndTransactionBlock(void)
--- 3623,3633 ----
s->blockState = TBLOCK_ABORT_PENDING;
else if (s->blockState == TBLOCK_ABORT)
s->blockState = TBLOCK_ABORT_END;
+ else if(s->blockState == TBLOCK_AUTOINPROGRESS)
+ s->blockState = TBLOCK_AUTOABORT_PENDING;
+ else if(s->blockState == TBLOCK_AUTOABORT)
+ s->blockState = TBLOCK_AUTOABORT_END;
+
else
elog(FATAL, "EndTransactionBlock: unexpected state %s",
BlockStateAsString(s->blockState));
***************
*** 3443,3448 **** EndTransactionBlock(void)
--- 3646,3660 ----
result = true;
break;
+ case TBLOCK_AUTOABORT:
+ s->blockState = TBLOCK_AUTOABORT_END;
+ break;
+
+ case TBLOCK_AUTOINPROGRESS:
+ s->blockState = TBLOCK_AUTOCOMMIT;
+ result = true;
+ break;
+
/* These cases are invalid. */
case TBLOCK_DEFAULT:
case TBLOCK_BEGIN:
***************
*** 3457,3462 **** EndTransactionBlock(void)
--- 3669,3678 ----
case TBLOCK_SUBRESTART:
case TBLOCK_SUBABORT_RESTART:
case TBLOCK_PREPARE:
+ case TBLOCK_AUTOBEGIN:
+ case TBLOCK_AUTOCOMMIT:
+ case TBLOCK_AUTOABORT_END:
+ case TBLOCK_AUTOABORT_PENDING:
elog(FATAL, "EndTransactionBlock: unexpected state %s",
BlockStateAsString(s->blockState));
break;
***************
*** 3503,3509 **** UserAbortTransactionBlock(void)
*/
case TBLOCK_SUBINPROGRESS:
case TBLOCK_SUBABORT:
! while (s->parent != NULL)
{
if (s->blockState == TBLOCK_SUBINPROGRESS)
s->blockState = TBLOCK_SUBABORT_PENDING;
--- 3719,3725 ----
*/
case TBLOCK_SUBINPROGRESS:
case TBLOCK_SUBABORT:
! while (s->parent != NULL && !IS_TOP_AUTO_TX_STATE(s))
{
if (s->blockState == TBLOCK_SUBINPROGRESS)
s->blockState = TBLOCK_SUBABORT_PENDING;
***************
*** 3518,3523 **** UserAbortTransactionBlock(void)
--- 3734,3744 ----
s->blockState = TBLOCK_ABORT_PENDING;
else if (s->blockState == TBLOCK_ABORT)
s->blockState = TBLOCK_ABORT_END;
+ else if(s->blockState == TBLOCK_AUTOINPROGRESS)
+ s->blockState = TBLOCK_AUTOABORT_PENDING;
+ else if(s->blockState == TBLOCK_AUTOABORT)
+ s->blockState = TBLOCK_AUTOABORT_END;
+
else
elog(FATAL, "UserAbortTransactionBlock: unexpected state %s",
BlockStateAsString(s->blockState));
***************
*** 3535,3540 **** UserAbortTransactionBlock(void)
--- 3756,3768 ----
errmsg("there is no transaction in progress")));
s->blockState = TBLOCK_ABORT_PENDING;
break;
+ case TBLOCK_AUTOABORT:
+ s->blockState = TBLOCK_AUTOABORT_END;
+ break;
+
+ case TBLOCK_AUTOINPROGRESS:
+ s->blockState = TBLOCK_AUTOABORT_PENDING;
+ break;
/* These cases are invalid. */
case TBLOCK_DEFAULT:
***************
*** 3550,3555 **** UserAbortTransactionBlock(void)
--- 3778,3787 ----
case TBLOCK_SUBRESTART:
case TBLOCK_SUBABORT_RESTART:
case TBLOCK_PREPARE:
+ case TBLOCK_AUTOBEGIN:
+ case TBLOCK_AUTOABORT_PENDING:
+ case TBLOCK_AUTOABORT_END:
+ case TBLOCK_AUTOCOMMIT:
elog(FATAL, "UserAbortTransactionBlock: unexpected state %s",
BlockStateAsString(s->blockState));
break;
***************
*** 3569,3577 **** DefineSavepoint(char *name)
{
case TBLOCK_INPROGRESS:
case TBLOCK_SUBINPROGRESS:
/* Normal subtransaction start */
! PushTransaction();
! s = CurrentTransactionState; /* changed by push */
/*
* Savepoint names, like the TransactionState block itself, live
--- 3801,3811 ----
{
case TBLOCK_INPROGRESS:
case TBLOCK_SUBINPROGRESS:
+ case TBLOCK_AUTOINPROGRESS:
/* Normal subtransaction start */
! PushTransaction(false, XactReadOnly);
!
! s = CurrentTransactionState; /* changed by push */
/*
* Savepoint names, like the TransactionState block itself, live
***************
*** 3598,3603 **** DefineSavepoint(char *name)
--- 3832,3842 ----
case TBLOCK_SUBRESTART:
case TBLOCK_SUBABORT_RESTART:
case TBLOCK_PREPARE:
+ case TBLOCK_AUTOBEGIN:
+ case TBLOCK_AUTOCOMMIT:
+ case TBLOCK_AUTOABORT:
+ case TBLOCK_AUTOABORT_PENDING:
+ case TBLOCK_AUTOABORT_END:
elog(FATAL, "DefineSavepoint: unexpected state %s",
BlockStateAsString(s->blockState));
break;
***************
*** 3626,3631 **** ReleaseSavepoint(List *options)
--- 3865,3872 ----
* defined.
*/
case TBLOCK_INPROGRESS:
+ case TBLOCK_AUTOINPROGRESS:
+
ereport(ERROR,
(errcode(ERRCODE_S_E_INVALID_SPECIFICATION),
errmsg("no such savepoint")));
***************
*** 3655,3660 **** ReleaseSavepoint(List *options)
--- 3896,3907 ----
case TBLOCK_SUBRESTART:
case TBLOCK_SUBABORT_RESTART:
case TBLOCK_PREPARE:
+ case TBLOCK_AUTOBEGIN:
+ case TBLOCK_AUTOCOMMIT:
+ case TBLOCK_AUTOABORT:
+ case TBLOCK_AUTOABORT_PENDING:
+ case TBLOCK_AUTOABORT_END:
+
elog(FATAL, "ReleaseSavepoint: unexpected state %s",
BlockStateAsString(s->blockState));
break;
***************
*** 3670,3682 **** ReleaseSavepoint(List *options)
Assert(PointerIsValid(name));
! for (target = s; PointerIsValid(target); target = target->parent)
{
if (PointerIsValid(target->name) && strcmp(target->name, name) == 0)
break;
}
! if (!PointerIsValid(target))
ereport(ERROR,
(errcode(ERRCODE_S_E_INVALID_SPECIFICATION),
errmsg("no such savepoint")));
--- 3917,3929 ----
Assert(PointerIsValid(name));
! for (target = s; PointerIsValid(target) && !IS_TOP_AUTO_TX_STATE(target); target = target->parent)
{
if (PointerIsValid(target->name) && strcmp(target->name, name) == 0)
break;
}
! if (!PointerIsValid(target) || (IS_TOP_AUTO_TX_STATE(target) && target->name == NULL))
ereport(ERROR,
(errcode(ERRCODE_S_E_INVALID_SPECIFICATION),
errmsg("no such savepoint")));
***************
*** 3727,3732 **** RollbackToSavepoint(List *options)
--- 3974,3982 ----
*/
case TBLOCK_INPROGRESS:
case TBLOCK_ABORT:
+ case TBLOCK_AUTOINPROGRESS:
+ case TBLOCK_AUTOABORT:
+
ereport(ERROR,
(errcode(ERRCODE_S_E_INVALID_SPECIFICATION),
errmsg("no such savepoint")));
***************
*** 3754,3759 **** RollbackToSavepoint(List *options)
--- 4004,4013 ----
case TBLOCK_SUBRESTART:
case TBLOCK_SUBABORT_RESTART:
case TBLOCK_PREPARE:
+ case TBLOCK_AUTOBEGIN:
+ case TBLOCK_AUTOCOMMIT:
+ case TBLOCK_AUTOABORT_PENDING:
+ case TBLOCK_AUTOABORT_END:
elog(FATAL, "RollbackToSavepoint: unexpected state %s",
BlockStateAsString(s->blockState));
break;
***************
*** 3769,3781 **** RollbackToSavepoint(List *options)
Assert(PointerIsValid(name));
! for (target = s; PointerIsValid(target); target = target->parent)
{
if (PointerIsValid(target->name) && strcmp(target->name, name) == 0)
break;
}
! if (!PointerIsValid(target))
ereport(ERROR,
(errcode(ERRCODE_S_E_INVALID_SPECIFICATION),
errmsg("no such savepoint")));
--- 4023,4035 ----
Assert(PointerIsValid(name));
! for (target = s; PointerIsValid(target) && !IS_TOP_AUTO_TX_STATE(target); target = target->parent)
{
if (PointerIsValid(target->name) && strcmp(target->name, name) == 0)
break;
}
! if (!PointerIsValid(target)||(IS_TOP_AUTO_TX_STATE(target)&& target->name == NULL))
ereport(ERROR,
(errcode(ERRCODE_S_E_INVALID_SPECIFICATION),
errmsg("no such savepoint")));
***************
*** 3838,3845 **** BeginInternalSubTransaction(char *name)
case TBLOCK_END:
case TBLOCK_PREPARE:
case TBLOCK_SUBINPROGRESS:
/* Normal subtransaction start */
! PushTransaction();
s = CurrentTransactionState; /* changed by push */
/*
--- 4092,4100 ----
case TBLOCK_END:
case TBLOCK_PREPARE:
case TBLOCK_SUBINPROGRESS:
+ case TBLOCK_AUTOINPROGRESS:
/* Normal subtransaction start */
! PushTransaction(false, XactReadOnly);
s = CurrentTransactionState; /* changed by push */
/*
***************
*** 3864,3869 **** BeginInternalSubTransaction(char *name)
--- 4119,4129 ----
case TBLOCK_SUBABORT_PENDING:
case TBLOCK_SUBRESTART:
case TBLOCK_SUBABORT_RESTART:
+ case TBLOCK_AUTOBEGIN:
+ case TBLOCK_AUTOCOMMIT:
+ case TBLOCK_AUTOABORT:
+ case TBLOCK_AUTOABORT_END:
+ case TBLOCK_AUTOABORT_PENDING:
elog(FATAL, "BeginInternalSubTransaction: unexpected state %s",
BlockStateAsString(s->blockState));
break;
***************
*** 3931,3936 **** RollbackAndReleaseCurrentSubTransaction(void)
--- 4191,4202 ----
case TBLOCK_SUBRESTART:
case TBLOCK_SUBABORT_RESTART:
case TBLOCK_PREPARE:
+ case TBLOCK_AUTOINPROGRESS:
+ case TBLOCK_AUTOBEGIN:
+ case TBLOCK_AUTOCOMMIT:
+ case TBLOCK_AUTOABORT:
+ case TBLOCK_AUTOABORT_END:
+ case TBLOCK_AUTOABORT_PENDING:
elog(FATAL, "RollbackAndReleaseCurrentSubTransaction: unexpected state %s",
BlockStateAsString(s->blockState));
break;
***************
*** 4029,4034 **** AbortOutOfAnyTransaction(void)
--- 4295,4315 ----
CleanupSubTransaction();
s = CurrentTransactionState; /* changed by pop */
break;
+ case TBLOCK_AUTOBEGIN:
+ case TBLOCK_AUTOINPROGRESS:
+ case TBLOCK_AUTOCOMMIT:
+ case TBLOCK_AUTOABORT_PENDING:
+ AbortAutonomousTransaction();
+ CleanupSubTransaction();
+ s = CurrentTransactionState; /* changed by pop */
+ break;
+
+ case TBLOCK_AUTOABORT:
+ case TBLOCK_AUTOABORT_END:
+ /* As above, but AbortSubTransaction already done */
+ CleanupSubTransaction();
+ s = CurrentTransactionState; /* changed by pop */
+ break;
}
} while (s->blockState != TBLOCK_DEFAULT);
***************
*** 4089,4094 **** TransactionBlockStatusCode(void)
--- 4370,4378 ----
case TBLOCK_SUBRELEASE:
case TBLOCK_SUBCOMMIT:
case TBLOCK_PREPARE:
+ case TBLOCK_AUTOBEGIN:
+ case TBLOCK_AUTOCOMMIT:
+ case TBLOCK_AUTOINPROGRESS:
return 'T'; /* in transaction */
case TBLOCK_ABORT:
case TBLOCK_SUBABORT:
***************
*** 4098,4103 **** TransactionBlockStatusCode(void)
--- 4382,4390 ----
case TBLOCK_SUBABORT_PENDING:
case TBLOCK_SUBRESTART:
case TBLOCK_SUBABORT_RESTART:
+ case TBLOCK_AUTOABORT:
+ case TBLOCK_AUTOABORT_PENDING:
+ case TBLOCK_AUTOABORT_END:
return 'E'; /* in failed transaction */
}
***************
*** 4412,4418 **** CleanupSubTransaction(void)
ShowTransactionState("CleanupSubTransaction");
! if (s->state != TRANS_ABORT)
elog(WARNING, "CleanupSubTransaction while in %s state",
TransStateAsString(s->state));
--- 4699,4705 ----
ShowTransactionState("CleanupSubTransaction");
! if (s->state != TRANS_ABORT && !IS_TOP_AUTO_TX_STATE(s))
elog(WARNING, "CleanupSubTransaction while in %s state",
TransStateAsString(s->state));
***************
*** 4439,4448 **** CleanupSubTransaction(void)
* if it has a local pointer to it after calling this function.
*/
static void
! PushTransaction(void)
{
TransactionState p = CurrentTransactionState;
TransactionState s;
/*
* We keep subtransaction state nodes in TopTransactionContext.
--- 4726,4736 ----
* if it has a local pointer to it after calling this function.
*/
static void
! PushTransaction(bool isAutoTX, bool readOnly)
{
TransactionState p = CurrentTransactionState;
TransactionState s;
+ PGAutonomousXACT *currentautotx = NULL;
/*
* We keep subtransaction state nodes in TopTransactionContext.
***************
*** 4475,4484 **** PushTransaction(void)
s->gucNestLevel = NewGUCNestLevel();
s->savepointLevel = p->savepointLevel;
s->state = TRANS_DEFAULT;
- s->blockState = TBLOCK_SUBBEGIN;
GetUserIdAndSecContext(&s->prevUser, &s->prevSecContext);
s->prevXactReadOnly = XactReadOnly;
CurrentTransactionState = s;
/*
--- 4763,4785 ----
s->gucNestLevel = NewGUCNestLevel();
s->savepointLevel = p->savepointLevel;
s->state = TRANS_DEFAULT;
GetUserIdAndSecContext(&s->prevUser, &s->prevSecContext);
s->prevXactReadOnly = XactReadOnly;
+ if (isAutoTX)
+ {
+ MyProc->inAutoTXLevel++;
+ currentautotx = GetCurrentPGAutonomousXACT();
+ currentautotx->nestingLevel = s->nestingLevel;
+ s->blockState = TBLOCK_AUTOBEGIN;
+ XactReadOnly = readOnly;
+ }
+ else
+ {
+ s->blockState = TBLOCK_SUBBEGIN;
+ }
+
+
CurrentTransactionState = s;
/*
***************
*** 4500,4505 **** static void
--- 4801,4807 ----
PopTransaction(void)
{
TransactionState s = CurrentTransactionState;
+ PGAutonomousXACT *currentautox = NULL;
if (s->state != TRANS_DEFAULT)
elog(WARNING, "PopTransaction while in %s state",
***************
*** 4518,4523 **** PopTransaction(void)
--- 4820,4832 ----
CurTransactionResourceOwner = s->parent->curTransactionOwner;
CurrentResourceOwner = s->parent->curTransactionOwner;
+ if(IS_TOP_AUTO_TX_STATE(s))
+ {
+ currentautox = GetCurrentPGAutonomousXACT();
+ MemSet(currentautox, 0, sizeof(PGAutonomousXACT));
+ MyProc->inAutoTXLevel--;
+ }
+
/* Free the old child structure */
if (s->name)
pfree(s->name);
***************
*** 4622,4627 **** BlockStateAsString(TBlockState blockState)
--- 4931,4948 ----
return "SUB RESTART";
case TBLOCK_SUBABORT_RESTART:
return "SUB AB RESTRT";
+ case TBLOCK_AUTOINPROGRESS:
+ return "AUTO INPROGRESS";
+ case TBLOCK_AUTOBEGIN:
+ return "AUTO BEGIN";
+ case TBLOCK_AUTOCOMMIT:
+ return "AUTO COMMIT";
+ case TBLOCK_AUTOABORT:
+ return "AUTO ABORT";
+ case TBLOCK_AUTOABORT_END:
+ return "AUTO ABORT END";
+ case TBLOCK_AUTOABORT_PENDING:
+ return "AUTO ABORT PENDING";
}
return "UNRECOGNIZED";
}
***************
*** 4994,4996 **** xact_redo(XLogRecPtr lsn, XLogRecord *record)
--- 5315,5885 ----
else
elog(PANIC, "xact_redo: unknown op code %u", info);
}
+
+ /*
+ * DefineAutonomousTransaction:
+ * This fuction creates an autonomous transaction.
+ * readOnly: This argumet indicates, whether it is read-only or read-write
+ * transaction.
+ * Note: In future, if we are supporting more independent properties to auto tx,
+ * the we can pass a structure containig all properties instead of bool.
+ */
+ void
+ DefineAutonomousTransaction(bool readOnly)
+ {
+ TransactionState s = CurrentTransactionState;
+
+ if (MyProc->inAutoTXLevel >= MAX_AUTOX_NESTING_LEVEL)
+ ereport(ERROR,
+ (errmsg("Has reach the max autonomous nesting level.")));
+ switch (s->blockState)
+ {
+ case TBLOCK_INPROGRESS:
+ case TBLOCK_SUBINPROGRESS:
+ case TBLOCK_STARTED:
+ case TBLOCK_AUTOINPROGRESS:
+ /* Normal subtransaction start */
+ PushTransaction(true, readOnly);
+ break;
+
+ /* These cases are invalid. */
+
+ default:
+ ereport(FATAL,
+ (errmsg("DefineAutonomousTransaction: unexpected state %s",
+ BlockStateAsString(s->blockState))));
+ break;
+ }
+ }
+
+ void
+ BeginAutonomousTransaction(void)
+ {
+ TransactionState s = CurrentTransactionState;
+
+ if (s->state != TRANS_DEFAULT)
+ ereport(WARNING,
+ (errmsg("BeginAutonomousTransaction while in %s state",
+ TransStateAsString(s->state))));
+
+ s->state = TRANS_START;
+
+ /*
+ * Initialize subsystems for new subtransaction
+ *
+ * must initialize resource-management stuff first
+ */
+ AtSubStart_Memory();
+ AtSubStart_ResourceOwner();
+ AtSubStart_Inval();
+ AtSubStart_Notify();
+ AfterTriggerBeginSubXact();
+
+ s->state = TRANS_INPROGRESS;
+
+ /*
+ * Call start-of-subxact callbacks
+ */
+ CallSubXactCallbacks(SUBXACT_EVENT_START_SUB, s->subTransactionId,
+ s->parent->subTransactionId);
+
+ ShowTransactionState("BeginAutonomousTransaction");
+ }
+
+ void
+ CommitAutonomousTransaction(void)
+ {
+ TransactionState s = CurrentTransactionState;
+ TransactionId latestXid;
+ ShowTransactionState("CommitAutonomousTransaction");
+
+ if (s->state != TRANS_INPROGRESS)
+ ereport(WARNING,
+ (errmsg("CommitAutonomousTransaction while in %s state",
+ TransStateAsString(s->state))));
+
+ /*
+ * Prior to 8.4 we marked subcommit in clog at this point. We now only
+ * perform that step, if required, as part of the atomic update of the
+ * whole transaction tree at top level commit or abort.
+ */
+ for (;;)
+ {
+ /*
+ * Fire all currently pending deferred triggers.
+ */
+ AfterTriggerFireDeferredForAutoX();
+
+ /*
+ * Close open portals (converting holdable ones into static portals).
+ * If there weren't any, we are done ... otherwise loop back to check
+ * if they queued deferred triggers. Lather, rinse, repeat.
+ */
+ if (!AutoPreCommit_Portals(s->subTransactionId))
+ break;
+ }
+
+
+
+ AfterTriggerEndSubXact(false);
+
+ AtSubCommit_Portals(s->subTransactionId,
+ s->parent->subTransactionId,
+ s->parent->curTransactionOwner);
+
+ PreCommit_on_commit_actions();
+ AtEOSubXact_LargeObject(false, s->subTransactionId,
+ s->parent->subTransactionId);
+ AtSubAbort_Notify();
+
+ /* Prevent cancel/die interrupt while cleaning up */
+ HOLD_INTERRUPTS();
+
+ s->state = TRANS_COMMIT;
+ /* Advertise the fact that we aborted in pg_clog. */
+ latestXid = RecordTransactionCommit(true);
+ ProcArrayEndAutonomousTransaction(MyProc, latestXid);
+ /* Post-commit cleanup */
+ if (TransactionIdIsValid(s->transactionId))
+ AtSubAbort_childXids();
+
+ CallSubXactCallbacks(SUBXACT_EVENT_COMMIT_SUB, s->subTransactionId,
+ s->parent->subTransactionId);
+
+ ResourceOwnerRelease(s->curTransactionOwner,
+ RESOURCE_RELEASE_BEFORE_LOCKS,
+ false, false);
+ AtEOSubXact_RelationCache(false, s->subTransactionId,
+ s->parent->subTransactionId);
+ AtEOAutoXact_Inval(true);
+ smgrDoPendingDeletes(true);
+ /*
+ * The only lock we actually release here is the subtransaction XID lock.
+ */
+ CurrentResourceOwner = s->curTransactionOwner;
+ if (TransactionIdIsValid(s->transactionId))
+ XactLockTableDelete(s->transactionId);
+
+ /*
+ * Other locks should get transferred to their parent resource owner.
+ */
+ ResourceOwnerRelease(s->curTransactionOwner,
+ RESOURCE_RELEASE_LOCKS,
+ false, false);
+ ResourceOwnerRelease(s->curTransactionOwner,
+ RESOURCE_RELEASE_AFTER_LOCKS,
+ false, false);
+
+ AtEOXact_GUC(true, s->gucNestLevel);
+ AtEOSubXact_SPI(true, s->subTransactionId);
+ AtEOSubXact_on_commit_actions(false, s->subTransactionId,
+ s->parent->subTransactionId);
+ AtEOAutoXact_Namespace(true, s->subTransactionId,
+ s->parent->subTransactionId);
+ AtEOSubXact_Files(false, s->subTransactionId,
+ s->parent->subTransactionId);
+ AtEOSubXact_HashTables(true, s->nestingLevel);
+ AtEOSubXact_PgStat(false, s->nestingLevel);
+ AtSubAbort_Snapshot(s->nestingLevel);
+
+ /*
+ * We need to restore the upper transaction's read-only state, in case the
+ * upper is read-write while the child is read-only; GUC will incorrectly
+ * think it should leave the child state in place.
+ */
+ XactReadOnly = s->prevXactReadOnly;
+ CleanupSubTransaction();
+
+ RESUME_INTERRUPTS();
+ }
+
+ void
+ AbortAutonomousTransaction(void)
+ {
+ TransactionState s = CurrentTransactionState;
+ TransactionId latestXid;
+
+ /* Prevent cancel/die interrupt while cleaning up */
+ HOLD_INTERRUPTS();
+
+ /* Make sure we have a valid memory context and resource owner */
+ AtSubAbort_Memory();
+ AtSubAbort_ResourceOwner();
+
+ /*
+ * Release any LW locks we might be holding as quickly as possible.
+ * (Regular locks, however, must be held till we finish aborting.)
+ * Releasing LW locks is critical since we might try to grab them again
+ * while cleaning up!
+ *
+ * FIXME This may be incorrect --- Are there some locks we should keep?
+ * Buffer locks, for example? I don't think so but I'm not sure.
+ */
+ LWLockReleaseAll();
+
+ AbortBufferIO();
+ UnlockBuffers();
+
+ LockErrorCleanup();
+
+ /*
+ * check the current transaction state
+ */
+ ShowTransactionState("AbortInternalAutonomousTransaction");
+
+ if (s->state != TRANS_INPROGRESS)
+ ereport(WARNING,
+ (errmsg("AbortInternalAutonomousTransaction while in %s state",
+ TransStateAsString(s->state))));
+ s->state = TRANS_ABORT;
+
+ /*
+ * Reset user ID which might have been changed transiently. (See notes in
+ * AbortTransaction.)
+ */
+ SetUserIdAndSecContext(s->prevUser, s->prevSecContext);
+
+ /*
+ * We can skip all this stuff if the subxact failed before creating a
+ * ResourceOwner...
+ */
+ if (s->curTransactionOwner)
+ {
+ AfterTriggerEndSubXact(false);
+ AtSubAbort_Portals(s->subTransactionId,
+ s->parent->subTransactionId,
+ s->parent->curTransactionOwner);
+ AtEOSubXact_LargeObject(false, s->subTransactionId,
+ s->parent->subTransactionId);
+ AtSubAbort_Notify();
+
+ /* Advertise the fact that we aborted in pg_clog. */
+ latestXid = RecordTransactionAbort(true);
+ ProcArrayEndAutonomousTransaction(MyProc, latestXid);
+ /* Post-abort cleanup */
+ if (TransactionIdIsValid(s->transactionId))
+ AtSubAbort_childXids();
+
+ CallSubXactCallbacks(SUBXACT_EVENT_ABORT_SUB, s->subTransactionId,
+ s->parent->subTransactionId);
+
+ ResourceOwnerRelease(s->curTransactionOwner,
+ RESOURCE_RELEASE_BEFORE_LOCKS,
+ false, false);
+ AtEOSubXact_RelationCache(false, s->subTransactionId,
+ s->parent->subTransactionId);
+ AtEOAutoXact_Inval(false);
+ AtSubAbort_smgr();
+ ResourceOwnerRelease(s->curTransactionOwner,
+ RESOURCE_RELEASE_LOCKS,
+ false, false);
+ ResourceOwnerRelease(s->curTransactionOwner,
+ RESOURCE_RELEASE_AFTER_LOCKS,
+ false, false);
+
+ AtEOXact_GUC(false, s->gucNestLevel);
+ AtEOSubXact_SPI(true, s->subTransactionId);
+ AtEOSubXact_on_commit_actions(false, s->subTransactionId,
+ s->parent->subTransactionId);
+ AtEOAutoXact_Namespace(false, s->subTransactionId,
+ s->parent->subTransactionId);
+ AtEOSubXact_Files(false, s->subTransactionId,
+ s->parent->subTransactionId);
+ AtEOSubXact_HashTables(false, s->nestingLevel);
+ AtEOSubXact_PgStat(false, s->nestingLevel);
+ AtSubAbort_Snapshot(s->nestingLevel);
+ }
+
+ /*
+ * Restore the upper transaction's read-only state, too. This should be
+ * redundant with GUC's cleanup but we may as well do it for consistency
+ * with the commit case.
+ */
+ XactReadOnly = s->prevXactReadOnly;
+
+ RESUME_INTERRUPTS();
+ }
+
+ /*
+ * Brief: SetPreContextAndResource
+ * save the previous memory context and resource owner
+ * Param: preContext
+ * Param: preOwner
+ */
+ void
+ SetPreContextAndResource(MemoryContext preContext,ResourceOwner preOwner)
+ {
+ TransactionState s = CurrentTransactionState;
+
+ if (s)
+ {
+ s->preMemoryContext = preContext;
+ s->preResourceOwner = preOwner;
+ }
+ }
+
+ /*
+ * Brief: GetPreContextAndResource
+ * get the previous memory context and resource owner
+ * Param: preContext
+ * Param: preOwner
+ */
+ void
+ GetPreContextAndResource(MemoryContext *preContext,ResourceOwner *preOwner)
+ {
+ TransactionState s = CurrentTransactionState;
+
+ if (s)
+ {
+ *preContext = s->preMemoryContext;
+ *preOwner = s->preResourceOwner;
+ }
+ else
+ {
+ *preContext = NULL;
+ *preOwner = NULL;
+ }
+ }
+
+ /*****************************************************************************
+ Description : BeginInternalAutonomousTransaction()
+ CommitInternalAutonomousTransaction()
+ AbortInternalAutonomousTransaction()
+ There fuctions are used to manage a internal autonomous
+ transaction.
+ Input :
+ Output :
+ Return Value : void
+ Notes : when use autonomous transaction internal, you should use
+ those functions with a block around PG_TRY()PG_CATCH()
+ example:
+ BeginInternalAutonomousTransaction();
+ PG_TRY();
+ {
+ ...
+ CommitInternalAutonomousTransaction();
+ }
+ PG_CATCH();
+ {
+ ...
+
+ *Notice:
+ *if use PG_RE_THROW() to throw the error to the next outer
+ *setjmp handler, we shouldn't call EmitErrorReport()and
+ *FlushErrorState().
+
+ EmitErrorReport();
+
+ AbortInternalAutonomousTransaction();
+
+ FlushErrorState();
+ ...
+ *PG_RE_THROW();*
+ }
+ PG_END_TRY();
+ History :
+ Modification :
+ *****************************************************************************/
+ void
+ BeginInternalAutonomousTransaction(void)
+ {
+ TransactionState s = CurrentTransactionState;
+ MemoryContext oldContext = CurrentMemoryContext;
+ ResourceOwner oldOwner = CurrentResourceOwner;
+
+ switch (s->blockState)
+ {
+ case TBLOCK_STARTED:
+ case TBLOCK_INPROGRESS:
+ case TBLOCK_SUBINPROGRESS:
+ case TBLOCK_AUTOINPROGRESS:
+ /* Normal subtransaction start */
+ PushTransaction(true, XactReadOnly);
+ break;
+
+ /* These cases are invalid. */
+
+ default:
+ ereport(FATAL,
+ (errmsg("DefineAutonomousTransaction: unexpected state %s",
+ BlockStateAsString(s->blockState))));
+ break;
+ }
+ CommitTransactionCommand();
+ StartTransactionCommand();
+ SetPreContextAndResource(oldContext, oldOwner);
+ MyProc->isIntAutoTx = true;
+ (void)MemoryContextSwitchTo(oldContext);
+ }
+
+ /*****************************************************************************
+ Description : When commit the autonomous transaction, it would not transfer
+ resources taken previously to its parent transaction, but
+ release all of them.
+ Input :
+ Output :
+ Return Value : TransactionId
+ Notes :
+ History :
+ Modification :
+ *****************************************************************************/
+ void
+ CommitInternalAutonomousTransaction(void)
+ {
+ TransactionState s = NULL;
+ MemoryContext preContext = NULL;
+ ResourceOwner preOwner = NULL;
+ s = CurrentTransactionState;
+ GetPreContextAndResource(&preContext,&preOwner);
+
+ switch (s->blockState)
+ {
+ /*
+ * We are in a live subtransaction block. Set up to subcommit all
+ * open subtransactions and then commit the main transaction.
+ */
+ case TBLOCK_SUBINPROGRESS:
+ while (s->parent != NULL && !IS_TOP_AUTO_TX_STATE(s))
+ {
+ if (s->blockState == TBLOCK_SUBINPROGRESS)
+ s->blockState = TBLOCK_SUBCOMMIT;
+ else
+ ereport(FATAL,
+ (errmsg("EndAutonomousTransactionBlock: unexpected state %s",
+ BlockStateAsString(s->blockState))));
+ s = s->parent;
+ }
+ if(s->blockState == TBLOCK_AUTOINPROGRESS)
+ s->blockState = TBLOCK_AUTOCOMMIT;
+ else
+ ereport(FATAL,
+ (errmsg("EndAutonomousTransactionBlock: unexpected state %s",
+ BlockStateAsString(s->blockState))));
+ break;
+
+ /*
+ * Here we are inside an aborted subtransaction. Treat the COMMIT
+ * as ROLLBACK: set up to abort everything and exit the main
+ * transaction.
+ */
+ case TBLOCK_SUBABORT:
+ while (s->parent != NULL && !IS_TOP_AUTO_TX_STATE(s))
+ {
+ if (s->blockState == TBLOCK_SUBINPROGRESS)
+ s->blockState = TBLOCK_SUBABORT_PENDING;
+ else if (s->blockState == TBLOCK_SUBABORT)
+ s->blockState = TBLOCK_SUBABORT_END;
+ else
+ ereport(FATAL,
+ (errmsg("EndAutonomousTransactionBlock: unexpected state %s",
+ BlockStateAsString(s->blockState))));
+ s = s->parent;
+ }
+ if(s->blockState == TBLOCK_AUTOINPROGRESS)
+ s->blockState = TBLOCK_AUTOABORT_PENDING;
+ else if(s->blockState == TBLOCK_AUTOABORT)
+ s->blockState = TBLOCK_AUTOABORT_END;
+ else
+ ereport(FATAL,
+ (errmsg("EndAutonomousTransactionBlock: unexpected state %s",
+ BlockStateAsString(s->blockState))));
+ break;
+
+ case TBLOCK_AUTOABORT:
+ s->blockState = TBLOCK_AUTOABORT_END;
+ break;
+
+ case TBLOCK_AUTOINPROGRESS:
+ s->blockState = TBLOCK_AUTOCOMMIT;
+ break;
+
+ /* These cases are invalid. */
+
+ default:
+ ereport(FATAL,
+ (errmsg("EndAutonomousTransactionBlock: unexpected state %s",
+ BlockStateAsString(s->blockState))));
+ break;
+ }
+
+ CommitTransactionCommand();
+ if (preContext)
+ (void)MemoryContextSwitchTo(preContext);
+
+ /* if exist previous resource owner , restore it */
+ if (preOwner)
+ CurrentResourceOwner = preOwner;
+
+ MyProc->isIntAutoTx = false;
+ }
+
+ void
+ AbortInternalAutonomousTransaction(void)
+ {
+
+ TransactionState s = CurrentTransactionState;
+ MemoryContext preContext = NULL;
+ ResourceOwner preOwner = NULL;
+ GetPreContextAndResource(&preContext,&preOwner);
+
+ switch (s->blockState)
+ {
+ case TBLOCK_AUTOBEGIN:
+ case TBLOCK_AUTOCOMMIT:
+ case TBLOCK_AUTOABORT_PENDING:
+ case TBLOCK_AUTOINPROGRESS:
+ AbortAutonomousTransaction();
+ break;
+
+ case TBLOCK_AUTOABORT:
+ case TBLOCK_AUTOABORT_END:
+ break;
+
+ default:
+ ereport(FATAL,
+ (errmsg("AbortautonomousTransactionBlock: unexpected state %s",
+ BlockStateAsString(s->blockState))));
+ }
+ CleanupSubTransaction();
+
+ /* if exist previous memory context , restore it */
+ if (preContext)
+ (void)MemoryContextSwitchTo(preContext);
+
+ /* if exist previous resource owner , restore it */
+ if (preOwner)
+ CurrentResourceOwner = preOwner;
+ MyProc->isIntAutoTx = false;
+ }
+
+ TransactionId GetTopAutonomousTransactionID(void)
+ {
+ TransactionId result = InvalidTransactionId;
+ TransactionState s = CurrentTransactionState;
+ TransactionState target;
+ for (target = s; PointerIsValid(target); target = target->parent)
+ {
+ if(IS_TOP_AUTO_TX_STATE(target))
+ {
+ if (!TransactionIdIsValid(target->transactionId))
+ {
+ AssignTransactionId(target);
+ }
+ result = target->transactionId;
+ return result;
+ }
+
+ }
+ if (!TransactionIdIsValid(result))
+ ereport(ERROR,
+ (errmsg("Not in a autonomous transaction")));
+
+ return result;
+ }
+
+ bool IsCurrentAutoTx()
+ {
+ return IS_TOP_AUTO_TX_STATE(CurrentTransactionState);
+ }
+
+
*** a/src/backend/catalog/namespace.c
--- b/src/backend/catalog/namespace.c
***************
*** 3789,3794 **** AtEOSubXact_Namespace(bool isCommit, SubTransactionId mySubid,
--- 3789,3843 ----
}
}
+ void
+ AtEOAutoXact_Namespace(bool isCommit, SubTransactionId mySubid,
+ SubTransactionId parentSubid)
+ {
+ OverrideStackEntry *entry;
+
+ if ((myTempNamespaceSubID == mySubid) && !isCommit)
+ {
+ myTempNamespaceSubID = InvalidSubTransactionId;
+ /* TEMP namespace creation failed, so reset state */
+ myTempNamespace = InvalidOid;
+ myTempToastNamespace = InvalidOid;
+ baseSearchPathValid = false; /* need to rebuild list */
+ }
+
+ /*
+ * Clean up if someone failed to do PopOverrideSearchPath
+ */
+ while (overrideStack)
+ {
+ entry = (OverrideStackEntry *) linitial(overrideStack);
+ if (entry->nestLevel < GetCurrentTransactionNestLevel())
+ break;
+ if (isCommit)
+ ereport(WARNING,
+ (errmsg("leaked override search path")));
+ overrideStack = list_delete_first(overrideStack);
+ list_free(entry->searchPath);
+ pfree(entry);
+ }
+
+ /* Activate the next level down. */
+ if (overrideStack)
+ {
+ entry = (OverrideStackEntry *) linitial(overrideStack);
+ activeSearchPath = entry->searchPath;
+ activeCreationNamespace = entry->creationNamespace;
+ activeTempCreationPending = false; /* XXX is this OK? */
+ }
+ else
+ {
+ /* If not baseSearchPathValid, this is useless but harmless */
+ activeSearchPath = baseSearchPath;
+ activeCreationNamespace = baseCreationNamespace;
+ activeTempCreationPending = baseTempCreationPending;
+ }
+ }
+
+
/*
* Remove all relations in the specified temp namespace.
*
*** a/src/backend/commands/sequence.c
--- b/src/backend/commands/sequence.c
***************
*** 953,959 **** open_share_lock(SeqTable seq)
currentOwner = CurrentResourceOwner;
PG_TRY();
{
! CurrentResourceOwner = TopTransactionResourceOwner;
LockRelationOid(seq->relid, AccessShareLock);
}
PG_CATCH();
--- 953,963 ----
currentOwner = CurrentResourceOwner;
PG_TRY();
{
! if(!MyProc->inAutoTXLevel)
! {
! CurrentResourceOwner = TopTransactionResourceOwner;
! }
!
LockRelationOid(seq->relid, AccessShareLock);
}
PG_CATCH();
*** a/src/backend/commands/tablecmds.c
--- b/src/backend/commands/tablecmds.c
***************
*** 87,92 ****
--- 87,93 ----
#include "utils/syscache.h"
#include "utils/tqual.h"
#include "utils/typcache.h"
+ #include "storage/proc.h"
/*
***************
*** 106,111 **** typedef struct OnCommitItem
--- 107,115 ----
*/
SubTransactionId creating_subid;
SubTransactionId deleting_subid;
+
+ TransactionId toptxid; /* top tx id */
+
} OnCommitItem;
static List *on_commits = NIL;
***************
*** 10793,10798 **** PreCommit_on_commit_actions(void)
--- 10797,10804 ----
/* Do nothing (there shouldn't be such entries, actually) */
break;
case ONCOMMIT_DELETE_ROWS:
+ if (MyProc->inAutoTXLevel)
+ break;
/*
* If this transaction hasn't accessed any temporary
***************
*** 10806,10811 **** PreCommit_on_commit_actions(void)
--- 10812,10820 ----
{
ObjectAddress object;
+ if (GetTopTransactionId() != oc->toptxid)
+ break;
+
object.classId = RelationRelationId;
object.objectId = oc->relid;
object.objectSubId = 0;
*** a/src/backend/commands/trigger.c
--- b/src/backend/commands/trigger.c
***************
*** 57,63 ****
#include "utils/syscache.h"
#include "utils/tqual.h"
#include "utils/tuplestore.h"
!
/* GUC variables */
int SessionReplicationRole = SESSION_REPLICATION_ROLE_ORIGIN;
--- 57,63 ----
#include "utils/syscache.h"
#include "utils/tqual.h"
#include "utils/tuplestore.h"
! #include "storage/proc.h"
/* GUC variables */
int SessionReplicationRole = SESSION_REPLICATION_ROLE_ORIGIN;
***************
*** 3679,3695 **** AfterTriggerExecute(AfterTriggerEvent event,
static bool
afterTriggerMarkEvents(AfterTriggerEventList *events,
AfterTriggerEventList *move_list,
! bool immediate_only)
{
bool found = false;
AfterTriggerEvent event;
AfterTriggerEventChunk *chunk;
for_each_event_chunk(event, chunk, *events)
{
AfterTriggerShared evtshared = GetTriggerSharedData(event);
bool defer_it = false;
if (!(event->ate_flags &
(AFTER_TRIGGER_DONE | AFTER_TRIGGER_IN_PROGRESS)))
{
--- 3679,3701 ----
static bool
afterTriggerMarkEvents(AfterTriggerEventList *events,
AfterTriggerEventList *move_list,
! bool immediate_only, bool inAutoX)
{
bool found = false;
AfterTriggerEvent event;
AfterTriggerEventChunk *chunk;
+ int my_level = GetCurrentTransactionNestLevel();
for_each_event_chunk(event, chunk, *events)
{
AfterTriggerShared evtshared = GetTriggerSharedData(event);
bool defer_it = false;
+ if ((inAutoX) && (chunk == events->head) && ((char *)event < afterTriggers->events_stack[my_level].tailfree))
+ {
+ continue;
+ }
+
if (!(event->ate_flags &
(AFTER_TRIGGER_DONE | AFTER_TRIGGER_IN_PROGRESS)))
{
***************
*** 3752,3758 **** static bool
afterTriggerInvokeEvents(AfterTriggerEventList *events,
CommandId firing_id,
EState *estate,
! bool delete_ok)
{
bool all_fired = true;
AfterTriggerEventChunk *chunk;
--- 3758,3765 ----
afterTriggerInvokeEvents(AfterTriggerEventList *events,
CommandId firing_id,
EState *estate,
! bool delete_ok,
! bool inAutoX)
{
bool all_fired = true;
AfterTriggerEventChunk *chunk;
***************
*** 3764,3769 **** afterTriggerInvokeEvents(AfterTriggerEventList *events,
--- 3771,3777 ----
Instrumentation *instr = NULL;
TupleTableSlot *slot1 = NULL,
*slot2 = NULL;
+ int my_level = GetCurrentTransactionNestLevel();
/* Make a local EState if need be */
if (estate == NULL)
***************
*** 3789,3794 **** afterTriggerInvokeEvents(AfterTriggerEventList *events,
--- 3797,3806 ----
{
AfterTriggerShared evtshared = GetTriggerSharedData(event);
+ if ((inAutoX) && (chunk == events->head) && ((char *)event < afterTriggers->events_stack[my_level].tailfree))
+ {
+ continue;
+ }
/*
* Is it one for me to fire?
*/
***************
*** 4033,4044 **** AfterTriggerEndQuery(EState *estate)
for (;;)
{
events = &afterTriggers->query_stack[afterTriggers->query_depth];
! if (afterTriggerMarkEvents(events, &afterTriggers->events, true))
{
CommandId firing_id = afterTriggers->firing_counter++;
/* OK to delete the immediate events after processing them */
! if (afterTriggerInvokeEvents(events, firing_id, estate, true))
break; /* all fired */
}
else
--- 4045,4056 ----
for (;;)
{
events = &afterTriggers->query_stack[afterTriggers->query_depth];
! if (afterTriggerMarkEvents(events, &afterTriggers->events, true, false))
{
CommandId firing_id = afterTriggers->firing_counter++;
/* OK to delete the immediate events after processing them */
! if (afterTriggerInvokeEvents(events, firing_id, estate, true, false))
break; /* all fired */
}
else
***************
*** 4097,4107 **** AfterTriggerFireDeferred(void)
* Run all the remaining triggers. Loop until they are all gone, in case
* some trigger queues more for us to do.
*/
! while (afterTriggerMarkEvents(events, NULL, false))
{
CommandId firing_id = afterTriggers->firing_counter++;
! if (afterTriggerInvokeEvents(events, firing_id, NULL, true))
break; /* all fired */
}
--- 4109,4201 ----
* Run all the remaining triggers. Loop until they are all gone, in case
* some trigger queues more for us to do.
*/
! while (afterTriggerMarkEvents(events, NULL, false, false))
! {
! CommandId firing_id = afterTriggers->firing_counter++;
!
! if (afterTriggerInvokeEvents(events, firing_id, NULL, true, false))
! break; /* all fired */
! }
!
! /*
! * We don't bother freeing the event list, since it will go away anyway
! * (and more efficiently than via pfree) in AfterTriggerEndXact.
! */
!
! if (snap_pushed)
! PopActiveSnapshot();
! }
!
! /* ----------
! * AfterTriggerFireDeferredForAutoX()
! * Called when autonomous transaction commit.
! * It is different from AfterTriggerFireDeferred that it would
! * only check below chunks in afterTriggers->events.
! * We can ensure it would only mark and invoke after trigger
! * events in current autonomous transaction in this way.
! * ------
! */
! void
! AfterTriggerFireDeferredForAutoX(void)
! {
! AfterTriggerEventList *events;
! bool snap_pushed = false;
! int my_level = GetCurrentTransactionNestLevel();
! MemoryContext old_cxt;
! AfterTriggerEventChunk *chunk;
! /* Must be inside a transaction */
! Assert(afterTriggers != NULL);
!
! /* ... but not inside a query */
! Assert(afterTriggers->query_depth ==
! afterTriggers->depth_stack[my_level]);
!
! /*
! * If there are any triggers to fire, make sure we have set a snapshot for
! * them to use. (Since PortalRunUtility doesn't set a snap for COMMIT, we
! * can't assume ActiveSnapshot is valid on entry.)
! */
!
! if (NULL != afterTriggers->events_stack[my_level].tail)
! {
! chunk = afterTriggers->events_stack[my_level].tail;
! }
! else
! {
! chunk = afterTriggers->events.head;
! }
!
! if (afterTriggers->events.tail == NULL || afterTriggers->events_stack[my_level].tailfree == afterTriggers->events.tailfree)
! {
! return;
! }
! else
! {
! old_cxt = MemoryContextSwitchTo(TopTransactionContext);
!
! events = (AfterTriggerEventList *)palloc(sizeof(AfterTriggerEventList));
! events->head = chunk;
! events->tail = afterTriggers->events.tail;
! events->tailfree = afterTriggers->events.tailfree;
!
! (void)MemoryContextSwitchTo(old_cxt);
! }
!
! if (events->head != NULL)
! {
! PushActiveSnapshot(GetTransactionSnapshot());
! snap_pushed = true;
! }
!
! /*
! * Run all the remaining triggers. Loop until they are all gone, in case
! * some trigger queues more for us to do.
! */
! while (afterTriggerMarkEvents(events, NULL, false, true))
{
CommandId firing_id = afterTriggers->firing_counter++;
! if (afterTriggerInvokeEvents(events, firing_id, NULL, true, true))
break; /* all fired */
}
***************
*** 4112,4117 **** AfterTriggerFireDeferred(void)
--- 4206,4212 ----
if (snap_pushed)
PopActiveSnapshot();
+ pfree(events);
}
***************
*** 4659,4665 **** AfterTriggerSetState(ConstraintsSetStmt *stmt)
AfterTriggerEventList *events = &afterTriggers->events;
bool snapshot_set = false;
! while (afterTriggerMarkEvents(events, NULL, true))
{
CommandId firing_id = afterTriggers->firing_counter++;
--- 4754,4760 ----
AfterTriggerEventList *events = &afterTriggers->events;
bool snapshot_set = false;
! while (afterTriggerMarkEvents(events, NULL, true, false))
{
CommandId firing_id = afterTriggers->firing_counter++;
***************
*** 4684,4690 **** AfterTriggerSetState(ConstraintsSetStmt *stmt)
* subtransaction could later get rolled back.
*/
if (afterTriggerInvokeEvents(events, firing_id, NULL,
! !IsSubTransaction()))
break; /* all fired */
}
--- 4779,4785 ----
* subtransaction could later get rolled back.
*/
if (afterTriggerInvokeEvents(events, firing_id, NULL,
! !IsSubTransaction(), false))
break; /* all fired */
}
*** a/src/backend/executor/spi.c
--- b/src/backend/executor/spi.c
***************
*** 2069,2074 **** _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
--- 2069,2091 ----
* Replan if needed, and increment plan refcount. If it's a saved
* plan, the refcount must be backed by the CurrentResourceOwner.
*/
+ if ((plansource)->raw_parse_tree &&
+ IsA((plansource)->raw_parse_tree, TransactionStmt))
+
+ {
+ if (((TransactionStmt*)(plansource->raw_parse_tree))->kind == TRANS_STMT_AUTONOMOUS)
+ {
+ BeginInternalAutonomousTransaction();
+ continue;
+ }
+
+ if (IsCurrentAutoTx())
+ {
+ CommitInternalAutonomousTransaction();
+ continue;
+ }
+ }
+
cplan = GetCachedPlan(plansource, paramLI, plan->saved);
stmt_list = cplan->stmt_list;
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 349,355 **** static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
execute_param_clause using_clause returning_clause
opt_enum_val_list enum_val_list table_func_column_list
create_generic_options alter_generic_options
! relation_expr_list dostmt_opt_list
%type opt_fdw_options fdw_options
%type fdw_option
--- 349,355 ----
execute_param_clause using_clause returning_clause
opt_enum_val_list enum_val_list table_func_column_list
create_generic_options alter_generic_options
! relation_expr_list dostmt_opt_list opt_auto_transaction_mode
%type opt_fdw_options fdw_options
%type fdw_option
***************
*** 527,532 **** static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
--- 527,533 ----
%token ABORT_P ABSOLUTE_P ACCESS ACTION ADD_P ADMIN AFTER
AGGREGATE ALL ALSO ALTER ALWAYS ANALYSE ANALYZE AND ANY ARRAY AS ASC
ASSERTION ASSIGNMENT ASYMMETRIC AT ATTRIBUTE AUTHORIZATION
+ AUTONOMOUS
BACKWARD BEFORE BEGIN_P BETWEEN BIGINT BINARY BIT
BOOLEAN_P BOTH BY
***************
*** 8166,8171 **** TransactionStmt:
--- 8167,8179 ----
n->gid = $3;
$$ = (Node *)n;
}
+ | START AUTONOMOUS TRANSACTION opt_auto_transaction_mode
+ {
+ TransactionStmt *n = makeNode(TransactionStmt);
+ n->kind = TRANS_STMT_AUTONOMOUS;
+ n->options = $4;
+ $$ = (Node *)n;
+ }
;
opt_transaction: WORK {}
***************
*** 8173,8178 **** opt_transaction: WORK {}
--- 8181,8197 ----
| /*EMPTY*/ {}
;
+ opt_auto_transaction_mode:
+ READ ONLY
+ { $$ = list_make1(makeDefElem("transaction_read_only",
+ (Node *)makeInteger(TRUE))); }
+ | READ WRITE
+ { $$ = list_make1(makeDefElem("transaction_read_only",
+ (Node *)makeInteger(FALSE))); }
+ | /* EMPTY */
+ { $$ = NIL; }
+ ;
+
transaction_mode_item:
ISOLATION LEVEL iso_level
{ $$ = makeDefElem("transaction_isolation",
***************
*** 12812,12817 **** unreserved_keyword:
--- 12831,12837 ----
| ASSIGNMENT
| AT
| ATTRIBUTE
+ | AUTONOMOUS
| BACKWARD
| BEFORE
| BEGIN_P
*** a/src/backend/storage/ipc/procarray.c
--- b/src/backend/storage/ipc/procarray.c
***************
*** 101,106 **** static ProcArrayStruct *procArray;
--- 101,109 ----
static PGPROC *allProcs;
static PGXACT *allPgXact;
+ static PGAutonomousXACT *allPgAutonomousXact;
+
+
/*
* Bookkeeping for tracking emulated transactions in recovery
*/
***************
*** 246,251 **** CreateSharedProcArray(void)
--- 249,255 ----
allProcs = ProcGlobal->allProcs;
allPgXact = ProcGlobal->allPgXact;
+ allPgAutonomousXact = ProcGlobal->allPgAutonomousXact;
/* Create or attach to the KnownAssignedXids arrays too, if needed */
if (EnableHotStandby)
***************
*** 443,448 **** ProcArrayEndTransaction(PGPROC *proc, TransactionId latestXid)
--- 447,501 ----
}
}
+ void
+ ProcArrayEndAutonomousTransaction(PGPROC *proc, TransactionId latestXid)
+ {
+ PGAutonomousXACT *pgautonouxact = GetCurrentPGAutonomousXACT();
+ if (TransactionIdIsValid(latestXid))
+ {
+ /*
+ * We must lock ProcArrayLock while clearing our advertised XID, so
+ * that we do not exit the set of "running" transactions while someone
+ * else is taking a snapshot. See discussion in
+ * src/backend/access/transam/README.
+ */
+
+
+ LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
+
+ pgautonouxact->xid = InvalidTransactionId;
+ pgautonouxact->xmin = InvalidTransactionId;
+ /* must be cleared with xid/xmin: */
+ pgautonouxact->delayChkpt = false; /* be sure this is cleared in abort */
+
+ /* Clear the subtransaction-XID cache too while holding the lock */
+ pgautonouxact->nxids = 0;
+ pgautonouxact->overflowed = false;
+
+ /* Also advance global latestCompletedXid while holding the lock */
+ if (TransactionIdPrecedes(ShmemVariableCache->latestCompletedXid,
+ latestXid))
+ ShmemVariableCache->latestCompletedXid = latestXid;
+
+ LWLockRelease(ProcArrayLock);
+ }
+ else
+ {
+ /*
+ * If we have no XID, we don't need to lock, since we won't affect
+ * anyone else's calculation of a snapshot. We might change their
+ * estimate of global xmin, but that's OK.
+ */
+ Assert(!TransactionIdIsValid(allPgAutonomousXact[proc->pgprocno].xid));
+ pgautonouxact->xmin = InvalidTransactionId;
+ /* must be cleared with xid/xmin: */
+ pgautonouxact->delayChkpt = false;
+ /* be sure this is cleared in abort */
+
+ Assert(pgautonouxact->nxids == 0);
+ Assert(pgautonouxact->overflowed == false);
+ }
+ }
/*
* ProcArrayClearTransaction -- clear the transaction fields
***************
*** 854,860 **** TransactionIdIsInProgress(TransactionId xid)
ProcArrayStruct *arrayP = procArray;
TransactionId topxid;
int i,
! j;
/*
* Don't bother checking a transaction older than RecentXmin; it could not
--- 907,914 ----
ProcArrayStruct *arrayP = procArray;
TransactionId topxid;
int i,
! j,
! k;
/*
* Don't bother checking a transaction older than RecentXmin; it could not
***************
*** 926,937 **** TransactionIdIsInProgress(TransactionId xid)
for (i = 0; i < arrayP->numProcs; i++)
{
int pgprocno = arrayP->pgprocnos[i];
volatile PGPROC *proc = &allProcs[pgprocno];
volatile PGXACT *pgxact = &allPgXact[pgprocno];
TransactionId pxid;
/* Ignore my own proc --- dealt with it above */
! if (proc == MyProc)
continue;
/* Fetch xid just once - see GetNewTransactionId */
--- 980,994 ----
for (i = 0; i < arrayP->numProcs; i++)
{
int pgprocno = arrayP->pgprocnos[i];
+ int pgautoxno = pgprocno * MAX_AUTOX_NESTING_LEVEL;
volatile PGPROC *proc = &allProcs[pgprocno];
volatile PGXACT *pgxact = &allPgXact[pgprocno];
+ volatile PGAutonomousXACT *pgautonomousxact = &allPgAutonomousXact[pgautoxno];
TransactionId pxid;
+ TransactionId pautoxid = InvalidTransactionId;
/* Ignore my own proc --- dealt with it above */
! if (proc == MyProc && !MyProc->inAutoTXLevel)
continue;
/* Fetch xid just once - see GetNewTransactionId */
***************
*** 982,987 **** TransactionIdIsInProgress(TransactionId xid)
--- 1039,1083 ----
*/
if (pgxact->overflowed)
xids[nxids++] = pxid;
+
+ /*
+ * check autonomous transaction
+ */
+ for (j = 0; j < proc->inAutoTXLevel; j++)
+ {
+ /*check top level autoTX*/
+ pautoxid = pgautonomousxact[j].xid;
+ if (!TransactionIdIsValid(pautoxid))
+ break;
+
+ if (TransactionIdEquals(pautoxid, xid))
+ {
+ LWLockRelease(ProcArrayLock);
+ xc_by_main_xid_inc();
+ return true;
+ }
+
+ /*if the xid logically < pautoxid, we don't need check lower TX*/
+ if (TransactionIdPrecedes(xid, pautoxid))
+ break;
+
+ /*check sub transactions of this autoTX*/
+ for (k = pgautonomousxact[j].nxids - 1; k >= 0; k--)
+ {
+ /* Fetch xid just once - see GetNewTransactionId */
+ TransactionId cxid = pgautonomousxact[j].subxids.xids[k];
+
+ if (TransactionIdEquals(cxid, xid))
+ {
+ LWLockRelease(ProcArrayLock);
+ xc_by_child_xid_inc();
+ return true;
+ }
+ }
+
+ if (pgautonomousxact[j].overflowed)
+ xids[nxids++] = pautoxid;
+ }
}
/*
***************
*** 1367,1372 **** GetSnapshotData(Snapshot snapshot)
--- 1463,1469 ----
bool suboverflowed = false;
volatile TransactionId replication_slot_xmin = InvalidTransactionId;
volatile TransactionId replication_slot_catalog_xmin = InvalidTransactionId;
+ int autoTxIndex = 0;
Assert(snapshot != NULL);
***************
*** 1435,1440 **** GetSnapshotData(Snapshot snapshot)
--- 1532,1542 ----
volatile PGXACT *pgxact = &allPgXact[pgprocno];
TransactionId xid;
+ int pgautoxno = pgprocno * MAX_AUTOX_NESTING_LEVEL;
+ volatile PGPROC *pgproc = &allProcs[pgprocno];
+ volatile PGAutonomousXACT *pgAutonomousxact =
+ &allPgAutonomousXact[pgautoxno];
+
/*
* Backend is doing logical decoding which manages xmin
* separately, check below.
***************
*** 1471,1477 **** GetSnapshotData(Snapshot snapshot)
*/
if (NormalTransactionIdPrecedes(xid, xmin))
xmin = xid;
! if (pgxact == MyPgXact)
continue;
/* Add XID to snapshot. */
--- 1573,1585 ----
*/
if (NormalTransactionIdPrecedes(xid, xmin))
xmin = xid;
!
! /*
! * When pgAutonomousxact->inAutoTX is true, there is a autonomous
! * transaction in this proc, it still need add the main transaction
! * to the snapshot.
! */
! if (pgxact == MyPgXact && !MyProc->inAutoTXLevel)
continue;
/* Add XID to snapshot. */
***************
*** 1511,1516 **** GetSnapshotData(Snapshot snapshot)
--- 1619,1664 ----
}
}
}
+
+ /* Add auto tx to snapshot */
+ for (autoTxIndex = 0; autoTxIndex < pgproc->inAutoTXLevel; autoTxIndex++)
+ {
+ xid = pgAutonomousxact[autoTxIndex].xid;
+
+ /* If auto tx is myself, skip it */
+ if (&pgAutonomousxact[autoTxIndex] == &MyPgAutonomousXact[MyProc->inAutoTXLevel - 1])
+ break;
+
+ /*
+ * If the auto tx has no XID assigned, we can skip it; it
+ * won't have sub-XIDs either. If the XID is >= xmax, we can also
+ * skip it; such tx will be treated as running anyway
+ * (and any sub-XIDs will also be >= xmax).
+ */
+ if (!TransactionIdIsNormal(xid)
+ || !NormalTransactionIdPrecedes(xid, xmax))
+ break;
+
+ snapshot->xip[count++] = xid;
+
+ if (!suboverflowed)
+ {
+ if (pgAutonomousxact[autoTxIndex].overflowed)
+ suboverflowed = true;
+ else
+ {
+ int nxids = pgAutonomousxact[autoTxIndex].nxids;
+
+ if (nxids > 0)
+ {
+ memcpy(snapshot->subxip + subcount,
+ (void *) pgAutonomousxact[autoTxIndex].subxids.xids,
+ nxids * sizeof(TransactionId));
+ subcount += nxids;
+ }
+ }
+ }
+ }
}
}
else
***************
*** 2795,2800 **** XidCacheRemoveRunningXids(TransactionId xid,
--- 2943,3028 ----
LWLockRelease(ProcArrayLock);
}
+ /*
+ * XidCacheRemoveAutoRunningXids
+ *
+ * Remove a bunch of TransactionIds from the list of known-running
+ * subtransactions of auto transaction for my backend. Both the specified xid and those in
+ * the xids[] array (of length nxids) are removed from the subxids cache.
+ * latestXid must be the latest XID among the group.
+ */
+ void
+ XidCacheRemoveAutoRunningXids(TransactionId xid,
+ int nxids, const TransactionId *xids,
+ TransactionId latestXid, bool isTopAutoTX)
+ {
+ int i,
+ j;
+ PGAutonomousXACT * currentautox = GetCurrentPGAutonomousXACT();
+ Assert(TransactionIdIsValid(xid));
+
+ /*
+ * Under normal circumstances xid and xids[] will be in increasing order,
+ * as will be the entries in subxids. Scan backwards to avoid O(N^2)
+ * behavior when removing a lot of xids.
+ */
+ for (i = nxids - 1; i >= 0; i--)
+ {
+ TransactionId anxid = xids[i];
+
+ for (j = currentautox->nxids - 1; j >= 0; j--)
+ {
+ if (TransactionIdEquals(currentautox->subxids.xids[j], anxid))
+ {
+ currentautox->subxids.xids[j] = currentautox->subxids.xids[currentautox->nxids - 1];
+ currentautox->nxids--;
+ break;
+ }
+
+ }
+ /*
+ * Ordinarily we should have found it, unless the cache has
+ * overflowed. However it's also possible for this routine to be
+ * invoked multiple times for the same subtransaction, in case of an
+ * error during AbortSubTransaction. So instead of Assert, emit a
+ * debug warning.
+ */
+ if (j < 0 && !currentautox->overflowed)
+ ereport(WARNING,
+ (errmsg("did not find subXID %u in MyPgAutonomousXact",
+ anxid)));
+
+ }
+
+ /* top level in auto TX, PopTransaction will MemSet MyPgAutonomousXact */
+ if(!isTopAutoTX)
+ {
+ for (j = currentautox->nxids - 1; j >= 0; j--)
+ {
+ if (TransactionIdEquals(currentautox->subxids.xids[j], xid))
+ {
+ currentautox->subxids.xids[j] = currentautox->subxids.xids[currentautox->nxids - 1];
+ currentautox->nxids--; ;
+ break;
+ }
+ }
+ /* Ordinarily we should have found it, unless the cache has overflowed */
+ if (j < 0 && !currentautox->overflowed)
+ ereport(WARNING,
+ (errmsg("did not find subXID %u in MyPgAutonomousXact",
+ xid)));
+ }
+
+ LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
+
+ /* Also advance global latestCompletedXid while holding the lock */
+ if (TransactionIdPrecedes(ShmemVariableCache->latestCompletedXid,
+ latestXid))
+ ShmemVariableCache->latestCompletedXid = latestXid;
+
+ LWLockRelease(ProcArrayLock);
+ }
+
#ifdef XIDCACHE_DEBUG
/*
*** a/src/backend/storage/lmgr/lmgr.c
--- b/src/backend/storage/lmgr/lmgr.c
***************
*** 23,29 ****
#include "storage/lmgr.h"
#include "storage/procarray.h"
#include "utils/inval.h"
!
/*
* Struct to hold context info for transaction lock waits.
--- 23,29 ----
#include "storage/lmgr.h"
#include "storage/procarray.h"
#include "utils/inval.h"
! #include "storage/proc.h"
/*
* Struct to hold context info for transaction lock waits.
***************
*** 524,529 **** XactLockTableWait(TransactionId xid, Relation rel, ItemPointer ctid,
--- 524,534 ----
error_context_stack = &callback;
}
+ #ifdef USE_ASSERT_CHECKING
+ if(!MyProc->inAutoTXLevel)
+ Assert(!TransactionIdEquals(xid, GetTopTransactionIdIfAny()));
+ #endif
+
for (;;)
{
Assert(TransactionIdIsValid(xid));
*** a/src/backend/storage/lmgr/lock.c
--- b/src/backend/storage/lmgr/lock.c
***************
*** 51,57 ****
int max_locks_per_xact; /* set by guc.c */
#define NLOCKENTS() \
! mul_size(max_locks_per_xact, add_size(MaxBackends, max_prepared_xacts))
/*
--- 51,58 ----
int max_locks_per_xact; /* set by guc.c */
#define NLOCKENTS() \
! mul_size(max_locks_per_xact, add_size((MAX_AUTOX_NESTING_LEVEL*MaxConnections), \
! add_size(MaxBackends, max_prepared_xacts)))
/*
***************
*** 355,360 **** static void LockRefindAndRelease(LockMethod lockMethodTable, PGPROC *proc,
--- 356,364 ----
LOCKTAG *locktag, LOCKMODE lockmode,
bool decrement_strong_lock_count);
+ static void InternalDeadLockCheckforAutoX(LockMethod lockMethodTable,
+ LOCKMODE lockmode,
+ LOCK *lock, PROCLOCK *proclock);
/*
* InitLocks -- Initialize the lock manager's data structures.
***************
*** 783,788 **** LockAcquireExtended(const LOCKTAG *locktag,
--- 787,798 ----
*/
if (locallock->nLocks > 0)
{
+ if(MyProc->inAutoTXLevel && locallock->proclock != NULL)
+ {
+ InternalDeadLockCheckforAutoX(lockMethodTable, lockmode, locallock->lock, locallock->proclock);
+ }
+
+
GrantLockLocal(locallock, owner);
return LOCKACQUIRE_ALREADY_HELD;
}
***************
*** 818,825 **** LockAcquireExtended(const LOCKTAG *locktag,
* lock type on a relation we have already locked using the fast-path, but
* for now we don't worry about that case either.
*/
! if (EligibleForRelationFastPath(locktag, lockmode) &&
! FastPathLocalUseCount < FP_LOCK_SLOTS_PER_BACKEND)
{
uint32 fasthashcode = FastPathStrongLockHashPartition(hashcode);
bool acquired;
--- 828,836 ----
* lock type on a relation we have already locked using the fast-path, but
* for now we don't worry about that case either.
*/
! if (EligibleForRelationFastPath(locktag, lockmode)
! && FastPathLocalUseCount < FP_LOCK_SLOTS_PER_BACKEND
! && !MyProc->inAutoTXLevel)
{
uint32 fasthashcode = FastPathStrongLockHashPartition(hashcode);
bool acquired;
***************
*** 1142,1147 **** SetupLockInTable(LockMethod lockMethodTable, PGPROC *proc,
--- 1153,1161 ----
uint32 partition = LockHashPartition(hashcode);
proclock->holdMask = 0;
+ MemSet(proclock->holdMaskByAutoTX, 0, MAX_AUTOX_NESTING_LEVEL * (sizeof(LOCKMASK)));
+
+ proclock->holdMaskByNormalTX = 0;
proclock->releaseMask = 0;
/* Add proclock to appropriate lists */
SHMQueueInsertBefore(&lock->procLocks, &proclock->lockLink);
***************
*** 1272,1277 **** LockCheckConflicts(LockMethod lockMethodTable,
--- 1286,1293 ----
LOCKMASK myLocks;
LOCKMASK otherLocks;
int i;
+ LOCKMASK myLocksByAutoTX;
+
/*
* first check for global conflicts: If no locks conflict with my request,
***************
*** 1295,1300 **** LockCheckConflicts(LockMethod lockMethodTable,
--- 1311,1327 ----
*/
myLocks = proclock->holdMask;
otherLocks = 0;
+
+ /* In autonomous TX, check whether lock conflict with parent TX */
+ if(MyProc->inAutoTXLevel)
+ {
+ myLocksByAutoTX = proclock->holdMaskByAutoTX[MyProc->inAutoTXLevel - 1];
+ /* if conflict with parent TX, It's a dead lock */
+ InternalDeadLockCheckforAutoX(lockMethodTable, lockmode, lock, proclock);
+ /* Something conflicts. But it could still be autonomous own lock. */
+ myLocks = myLocksByAutoTX;
+ }
+
for (i = 1; i <= numLockModes; i++)
{
int myHolding = (myLocks & LOCKBIT_ON(i)) ? 1 : 0;
***************
*** 1333,1344 **** LockCheckConflicts(LockMethod lockMethodTable,
--- 1360,1393 ----
void
GrantLock(LOCK *lock, PROCLOCK *proclock, LOCKMODE lockmode)
{
+ uint8 autoTXLevel;
+
lock->nGranted++;
lock->granted[lockmode]++;
lock->grantMask |= LOCKBIT_ON(lockmode);
if (lock->granted[lockmode] == lock->requested[lockmode])
lock->waitMask &= LOCKBIT_OFF(lockmode);
proclock->holdMask |= LOCKBIT_ON(lockmode);
+
+
+ if (MyProc == proclock->tag.myProc)
+ {
+ autoTXLevel = GetCurrentResourceOwnerAutoTXLevel();
+ }
+ else
+ {
+ autoTXLevel = proclock->tag.myProc->inAutoTXLevel;
+ }
+
+ if (autoTXLevel)
+ {
+ proclock->holdMaskByAutoTX[autoTXLevel - 1] |= LOCKBIT_ON(lockmode);
+ }
+ else
+ {
+ proclock->holdMaskByNormalTX |= LOCKBIT_ON(lockmode);
+ }
+
LOCK_PRINT("GrantLock", lock, lockmode);
Assert((lock->nGranted > 0) && (lock->granted[lockmode] > 0));
Assert(lock->nGranted <= lock->nRequested);
***************
*** 1394,1399 **** UnGrantLock(LOCK *lock, LOCKMODE lockmode,
--- 1443,1457 ----
/*
* Now fix the per-proclock state.
*/
+ if (MyProc->inAutoTXLevel)
+ {
+ proclock->holdMaskByAutoTX[MyProc->inAutoTXLevel - 1] &= LOCKBIT_OFF(lockmode);
+ }
+ else
+ {
+ proclock->holdMaskByNormalTX &= LOCKBIT_OFF(lockmode);
+
+ }
proclock->holdMask &= LOCKBIT_OFF(lockmode);
PROCLOCK_PRINT("UnGrantLock: updated", proclock);
***************
*** 4084,4086 **** VirtualXactLock(VirtualTransactionId vxid, bool wait)
--- 4142,4185 ----
LockRelease(&tag, ShareLock, false);
return true;
}
+
+ static void
+ InternalDeadLockCheckforAutoX(LockMethod lockMethodTable, LOCKMODE lockmode,
+ LOCK *lock, PROCLOCK *proclock)
+ {
+ int i = 0;
+ /*check deadlock with main xact*/
+ if (lockMethodTable->conflictTab[lockmode] & proclock->holdMaskByNormalTX)
+ {
+ lock->nRequested--;
+ Assert(lock->requested[lockmode] > 0);
+ lock->requested[lockmode]--;
+ PROCLOCK_PRINT("LockCheckConflicts: auto TX conflict with parent TX", proclock);
+ ereport(ERROR,
+ (errmsg("lock %s on object %u/%u/%u/%u required by auto TX is conflict with parent TX",
+ lockMethodTable->lockModeNames[lockmode],
+ lock->tag.locktag_field1,
+ lock->tag.locktag_field2,
+ lock->tag.locktag_field3,
+ lock->tag.locktag_field4)));
+ }
+ /*check deadlock with upper autox*/
+ for (i = 0; i < MyProc->inAutoTXLevel - 1; i++)
+ {
+ if (lockMethodTable->conflictTab[lockmode] & proclock->holdMaskByAutoTX[i])
+ {
+ lock->nRequested--;
+ Assert(lock->requested[lockmode] > 0);
+ lock->requested[lockmode]--;
+ PROCLOCK_PRINT("LockCheckConflicts: auto TX conflict with parent AutoX", proclock);
+ ereport(ERROR,
+ (errmsg("lock %s on object %u/%u/%u/%u required by auto TX is conflict with parent AutoX",
+ lockMethodTable->lockModeNames[lockmode],
+ lock->tag.locktag_field1,
+ lock->tag.locktag_field2,
+ lock->tag.locktag_field3,
+ lock->tag.locktag_field4)));
+ }
+ }
+ }
+
*** a/src/backend/storage/lmgr/predicate.c
--- b/src/backend/storage/lmgr/predicate.c
***************
*** 199,204 ****
--- 199,205 ----
#include "utils/rel.h"
#include "utils/snapmgr.h"
#include "utils/tqual.h"
+ #include "storage/proc.h"
/* Uncomment the next line to test the graceful degradation code. */
/* #define TEST_OLDSERXID */
***************
*** 502,507 **** SerializationNeededForRead(Relation relation, Snapshot snapshot)
--- 503,518 ----
return false;
/*
+ * BEGIN
+ * Don't acquire locks or conflict if it is an autonomous transaction. Autonomous
+ * transaction always does simple work like create partition or create undo segement,
+ * it should not faild because of serializable isolation.
+ */
+ if (MyProc->inAutoTXLevel)
+ return false;
+ /* END */
+
+ /*
* Check if we have just become "RO-safe". If we have, immediately release
* all locks as they're not needed anymore. This also resets
* MySerializableXact, so that subsequent calls to this function can exit
*** a/src/backend/storage/lmgr/proc.c
--- b/src/backend/storage/lmgr/proc.c
***************
*** 62,67 **** bool log_lock_waits = false;
--- 62,68 ----
/* Pointer to this process's PGPROC and PGXACT structs, if any */
PGPROC *MyProc = NULL;
PGXACT *MyPgXact = NULL;
+ PGAutonomousXACT *MyPgAutonomousXact = NULL;
/*
* This spinlock protects the freelist of recycled PGPROC structures.
***************
*** 112,117 **** ProcGlobalShmemSize(void)
--- 113,122 ----
size = add_size(size, mul_size(NUM_AUXILIARY_PROCS, sizeof(PGXACT)));
size = add_size(size, mul_size(max_prepared_xacts, sizeof(PGXACT)));
+ size = add_size(size, mul_size(MaxBackends * MAX_AUTOX_NESTING_LEVEL, sizeof(PGAutonomousXACT)));
+ size = add_size(size, mul_size(NUM_AUXILIARY_PROCS * MAX_AUTOX_NESTING_LEVEL, sizeof(PGAutonomousXACT)));
+ size = add_size(size, mul_size(max_prepared_xacts * MAX_AUTOX_NESTING_LEVEL, sizeof(PGAutonomousXACT)));
+
return size;
}
***************
*** 162,167 **** InitProcGlobal(void)
--- 167,175 ----
bool found;
uint32 TotalProcs = MaxBackends + NUM_AUXILIARY_PROCS + max_prepared_xacts;
+ PGAutonomousXACT *pgautonomousxacts;
+
+
/* Create the ProcGlobal shared structure */
ProcGlobal = (PROC_HDR *)
ShmemInitStruct("Proc Header", sizeof(PROC_HDR), &found);
***************
*** 210,215 **** InitProcGlobal(void)
--- 218,227 ----
MemSet(pgxacts, 0, TotalProcs * sizeof(PGXACT));
ProcGlobal->allPgXact = pgxacts;
+ pgautonomousxacts = (PGAutonomousXACT *) ShmemAlloc(TotalProcs * MAX_AUTOX_NESTING_LEVEL * sizeof(PGAutonomousXACT));
+ MemSet(pgautonomousxacts, 0, TotalProcs * MAX_AUTOX_NESTING_LEVEL * sizeof(PGAutonomousXACT));
+ ProcGlobal->allPgAutonomousXact = pgautonomousxacts;
+
for (i = 0; i < TotalProcs; i++)
{
/* Common initialization for all PGPROCs, regardless of type. */
***************
*** 279,284 **** InitProcess(void)
--- 291,297 ----
{
/* use volatile pointer to prevent code rearrangement */
volatile PROC_HDR *procglobal = ProcGlobal;
+ int pgautotxno = 0;
/*
* ProcGlobal should be set up already (if we are a backend, we inherit
***************
*** 317,322 **** InitProcess(void)
--- 330,338 ----
if (MyProc != NULL)
{
+ MyProc->inAutoTXLevel = 0;
+
+
if (IsAnyAutoVacuumProcess())
procglobal->autovacFreeProcs = (PGPROC *) MyProc->links.next;
else if (IsBackgroundWorker)
***************
*** 340,345 **** InitProcess(void)
--- 356,366 ----
}
MyPgXact = &ProcGlobal->allPgXact[MyProc->pgprocno];
+ pgautotxno = MyProc->pgprocno*MAX_AUTOX_NESTING_LEVEL;
+ MyPgAutonomousXact = &ProcGlobal->allPgAutonomousXact[pgautotxno];
+ MemSet(MyPgAutonomousXact, 0, MAX_AUTOX_NESTING_LEVEL*sizeof(PGAutonomousXACT));
+
+
/*
* Now that we have a PGPROC, mark ourselves as an active postmaster
* child; this is so that the postmaster can detect it if we exit without
***************
*** 390,395 **** InitProcess(void)
--- 411,417 ----
/* Initialize fields for sync rep */
MyProc->waitLSN = 0;
MyProc->syncRepState = SYNC_REP_NOT_WAITING;
+
SHMQueueElemInit(&(MyProc->syncRepLinks));
/*
***************
*** 464,469 **** InitAuxiliaryProcess(void)
--- 486,492 ----
{
PGPROC *auxproc;
int proctype;
+ int pgautoxno;
/*
* ProcGlobal should be set up already (if we are a backend, we inherit
***************
*** 515,520 **** InitAuxiliaryProcess(void)
--- 538,547 ----
MyProc = auxproc;
MyPgXact = &ProcGlobal->allPgXact[auxproc->pgprocno];
+ pgautoxno = auxproc->pgprocno * MAX_AUTOX_NESTING_LEVEL;
+ MyPgAutonomousXact = &ProcGlobal->allPgAutonomousXact[pgautoxno];
+
+
SpinLockRelease(ProcStructLock);
/*
***************
*** 1669,1671 **** ProcSendSignal(int pid)
--- 1696,1706 ----
if (proc != NULL)
PGSemaphoreUnlock(&proc->sem);
}
+
+ PGAutonomousXACT *GetCurrentPGAutonomousXACT(void)
+ {
+ Assert(MyProc->inAutoTXLevel);
+
+ return &MyPgAutonomousXact[MyProc->inAutoTXLevel - 1];
+ }
+
*** a/src/backend/tcop/postgres.c
--- b/src/backend/tcop/postgres.c
***************
*** 3857,3862 **** PostgresMain(int argc, char *argv[],
--- 3857,3867 ----
debug_query_string = NULL;
/*
+ * Abort internal autonomous transaction, if started.
+ */
+ if (MyProc->isIntAutoTx)
+ AbortInternalAutonomousTransaction();
+ /*
* Abort the current transaction in order to recover.
*/
AbortCurrentTransaction();
*** a/src/backend/tcop/utility.c
--- b/src/backend/tcop/utility.c
***************
*** 414,419 **** standard_ProcessUtility(Node *parsetree,
--- 414,440 ----
UserAbortTransactionBlock();
break;
+ case TRANS_STMT_AUTONOMOUS:
+ {
+ ListCell *cell;
+ bool readOnly = false; /* by default read-write*/
+
+ RequireTransactionChain(isTopLevel, "AUTONOMOUS TRANSACTION");
+ /*
+ * As of now only one element in list is expected.
+ * If more elements added to it, handing might need to
+ * be relooked.
+ */
+ foreach(cell, stmt->options)
+ {
+ DefElem *elem = lfirst(cell);
+ if (strcmp(elem->defname, "transaction_read_only") == 0)
+ readOnly = (bool)intVal(elem->arg);
+ }
+ DefineAutonomousTransaction(readOnly);
+ }
+ break;
+
case TRANS_STMT_SAVEPOINT:
{
ListCell *cell;
***************
*** 1751,1756 **** CreateCommandTag(Node *parsetree)
--- 1772,1781 ----
tag = "ROLLBACK PREPARED";
break;
+ case TRANS_STMT_AUTONOMOUS:
+ tag = "START AUTONOMOUS TRANSACTION";
+ break;
+
default:
tag = "???";
break;
*** a/src/backend/utils/cache/catcache.c
--- b/src/backend/utils/cache/catcache.c
***************
*** 1327,1332 **** ReleaseCatCache(HeapTuple tuple)
--- 1327,1400 ----
CatCacheRemoveCTup(ct->my_cache, ct);
}
+ /*if it is a autonomous transaction, it will not use syscache*/
+ HeapTuple
+ SearchSystableForAutoX(CatCache *cache,
+ Datum v1,
+ Datum v2,
+ Datum v3,
+ Datum v4)
+ {
+ ScanKeyData cur_skey[CATCACHE_MAXKEYS];
+ Relation relation;
+ SysScanDesc scandesc;
+ HeapTuple ntp = NULL;
+ HeapTuple dtp = NULL;
+
+ /*
+ * one-time startup overhead for each cache
+ */
+ if (cache->cc_tupdesc == NULL)
+ CatalogCacheInitializeCache(cache);
+
+ #ifdef CATCACHE_STATS
+ cache->cc_searches++;
+ #endif
+
+ /*
+ * initialize the search key information
+ */
+ memcpy(cur_skey, cache->cc_skey, sizeof(cur_skey));
+ cur_skey[0].sk_argument = v1;
+ cur_skey[1].sk_argument = v2;
+ cur_skey[2].sk_argument = v3;
+ cur_skey[3].sk_argument = v4;
+
+ relation = heap_open(cache->cc_reloid, AccessShareLock);
+ scandesc = systable_beginscan(relation,
+ cache->cc_indexoid,
+ IndexScanOK(cache, cur_skey),
+ NULL,
+ cache->cc_nkeys,
+ cur_skey);
+
+ ntp = systable_getnext(scandesc);
+ if (NULL != ntp)
+ {
+ dtp = (HeapTupleData *) palloc(sizeof(HeapTupleData));
+
+ heap_copytuple_with_tuple(ntp, dtp);
+ }
+
+ systable_endscan(scandesc);
+
+ heap_close(relation, AccessShareLock);
+
+ return dtp;
+ }
+
+ void
+ ReleaseHeapTupleforAutoX(HeapTuple tuple)
+ {
+ if(NULL != tuple)
+ {
+ if(NULL != tuple->t_data)
+ {
+ pfree(tuple->t_data);
+ }
+ pfree(tuple);
+ }
+ }
/*
* GetCatCacheHashValue
*** a/src/backend/utils/cache/inval.c
--- b/src/backend/utils/cache/inval.c
***************
*** 799,810 **** MakeSharedInvalidMessagesArray(const SharedInvalidationMessage *msgs, int n)
*/
int
xactGetCommittedInvalidationMessages(SharedInvalidationMessage **msgs,
! bool *RelcacheInitFileInval)
{
MemoryContext oldcontext;
/* Must be at top of stack */
! Assert(transInvalInfo != NULL && transInvalInfo->parent == NULL);
/*
* Relcache init file invalidation requires processing both before and
--- 799,812 ----
*/
int
xactGetCommittedInvalidationMessages(SharedInvalidationMessage **msgs,
! bool *RelcacheInitFileInval,
! bool isAutoXact)
{
MemoryContext oldcontext;
/* Must be at top of stack */
! if (!isAutoXact)
! Assert(transInvalInfo != NULL && transInvalInfo->parent == NULL);
/*
* Relcache init file invalidation requires processing both before and
***************
*** 1003,1008 **** AtEOSubXact_Inval(bool isCommit)
--- 1005,1088 ----
}
/*
+ * AtEOAutoXact_Inval
+ * Process queued-up invalidation messages at end of autonomous transaction.
+ *
+ * If isCommit, we must send out the messages in our PriorCmdInvalidMsgs list
+ * to the shared invalidation message queue. Note that these will be read
+ * not only by other backends, but also by our own backend at the next
+ * transaction start (via AcceptInvalidationMessages). This means that
+ * we can skip immediate local processing of anything that's still in
+ * CurrentCmdInvalidMsgs, and just send that list out too.
+ *
+ * If not isCommit, we are aborting, and must locally process the messages
+ * in PriorCmdInvalidMsgs. No messages need be sent to other backends,
+ * since they'll not have seen our changed tuples anyway. We can forget
+ * about CurrentCmdInvalidMsgs too, since those changes haven't touched
+ * the caches yet.
+ *
+ * In any case, pop the transaction stack. We need not physically free memory
+ * here, since CurTransactionContext is about to be emptied anyway
+ * (if aborting). Beware of the possibility of aborting the same nesting
+ * level twice, though.
+ */
+ void
+ AtEOAutoXact_Inval(bool isCommit)
+ {
+ int my_level = GetCurrentTransactionNestLevel();
+ TransInvalidationInfo *myInfo = transInvalInfo;
+
+ if (isCommit)
+ {
+ /* Must be at non-top of stack */
+ Assert(myInfo != NULL && myInfo->parent != NULL);
+ Assert(myInfo->my_level == my_level);
+
+ /*
+ * Relcache init file invalidation requires processing both before and
+ * after we send the SI messages. However, we need not do anything
+ * unless we committed.
+ */
+ if (myInfo->RelcacheInitFileInval)
+ RelationCacheInitFilePreInvalidate();
+
+ AppendInvalidationMessages(&myInfo->PriorCmdInvalidMsgs,
+ &myInfo->CurrentCmdInvalidMsgs);
+
+ ProcessInvalidationMessagesMulti(&myInfo->PriorCmdInvalidMsgs,
+ SendSharedInvalidMessages);
+
+
+ if (myInfo->RelcacheInitFileInval)
+ RelationCacheInitFilePostInvalidate();
+
+ /* Pop the transaction state stack */
+ transInvalInfo = myInfo->parent;
+
+ /* Need not free anything else explicitly */
+ pfree(myInfo);
+ }
+ else if (myInfo != NULL && myInfo->my_level == my_level)
+ {
+ /* Must be at non-top of stack */
+ Assert(myInfo->parent != NULL);
+
+ ProcessInvalidationMessages(&myInfo->PriorCmdInvalidMsgs,
+ LocalExecuteInvalidationMessage);
+
+ /* Pop the transaction state stack */
+ transInvalInfo = myInfo->parent;
+
+ /* Need not free anything else explicitly */
+ pfree(myInfo);
+ }
+
+ /* when auto transaction end, unset SharedInvalidMessagesArray and numSharedInvalidMessagesArray */
+ SharedInvalidMessagesArray = NULL;
+ numSharedInvalidMessagesArray = 0;
+ }
+
+ /*
* CommandEndInvalidationMessages
* Process queued-up invalidation messages at end of one command
* in a transaction.
*** a/src/backend/utils/cache/syscache.c
--- b/src/backend/utils/cache/syscache.c
***************
*** 66,71 ****
--- 66,72 ----
#include "utils/rel.h"
#include "utils/catcache.h"
#include "utils/syscache.h"
+ #include "storage/proc.h"
/*---------------------------------------------------------------------------
***************
*** 907,912 **** SearchSysCache(int cacheId,
--- 908,918 ----
!PointerIsValid(SysCache[cacheId]))
elog(ERROR, "invalid cache ID: %d", cacheId);
+ if (MyProc->inAutoTXLevel)
+ {
+ return SearchSystableForAutoX(SysCache[cacheId], key1, key2, key3, key4);
+ }
+
return SearchCatCache(SysCache[cacheId], key1, key2, key3, key4);
}
***************
*** 917,922 **** SearchSysCache(int cacheId,
--- 923,935 ----
void
ReleaseSysCache(HeapTuple tuple)
{
+
+ if (MyProc->inAutoTXLevel)
+ {
+ ReleaseHeapTupleforAutoX(tuple);
+ return;
+ }
+
ReleaseCatCache(tuple);
}
*** a/src/backend/utils/mmgr/portalmem.c
--- b/src/backend/utils/mmgr/portalmem.c
***************
*** 25,30 ****
--- 25,31 ----
#include "utils/builtins.h"
#include "utils/memutils.h"
#include "utils/timestamp.h"
+ #include "storage/proc.h"
/*
* Estimate of the maximum number of open portals a user would have,
***************
*** 724,729 **** PreCommit_Portals(bool isPrepare)
--- 725,839 ----
}
/*
+ * Pre-commit processing for portals in Autonomous Transaction.
+ *
+ * Holdable cursors created in this transaction need to be converted to
+ * materialized form, since we are going to close down the executor and
+ * release locks. Non-holdable portals created in this transaction are
+ * simply removed. Portals remaining from prior transactions should be
+ * left untouched.
+ *
+ * Returns TRUE if any portals changed state (possibly causing user-defined
+ * code to be run), FALSE if not.
+ */
+ bool
+ AutoPreCommit_Portals(uint32 createSubid)
+ {
+ bool result = false;
+ HASH_SEQ_STATUS status;
+ PortalHashEnt *hentry;
+
+ hash_seq_init(&status, PortalHashTable);
+
+ while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
+ {
+ Portal portal = hentry->portal;
+
+ /* portal created in auto TX? */
+ if (createSubid > portal->createSubid)
+ continue;
+
+ /*
+ * There should be no pinned portals anymore. Complain if someone
+ * leaked one.
+ */
+ if (portal->portalPinned)
+ ereport(ERROR,(errmsg("cannot commit while a portal is pinned")));
+
+ /*
+ * Do not touch active portals --- this can only happen in the case of
+ * a multi-transaction utility command, such as VACUUM.
+ *
+ * Note however that any resource owner attached to such a portal is
+ * still going to go away, so don't leave a dangling pointer.
+ */
+ if (portal->status == PORTAL_ACTIVE )
+ {
+ portal->resowner = NULL;
+ continue;
+ }
+
+ /* Is it a holdable portal created in the auto xact? */
+ if ((portal->cursorOptions & CURSOR_OPT_HOLD) &&
+ portal->createSubid != InvalidSubTransactionId &&
+ portal->status == PORTAL_READY)
+ {
+ /*
+ * Note that PersistHoldablePortal() must release all resources
+ * used by the portal that are local to the creating transaction.
+ */
+ PortalCreateHoldStore(portal);
+ PersistHoldablePortal(portal);
+
+ /* drop cached plan reference, if any */
+ PortalReleaseCachedPlan(portal);
+
+ /*
+ * Any resources belonging to the portal will be released in the
+ * upcoming transaction-wide cleanup; the portal will no longer
+ * have its own resources.
+ */
+ portal->resowner = NULL;
+
+ /*
+ * Having successfully exported the holdable cursor, mark it as
+ * not belonging to this transaction.
+ */
+ portal->createSubid = InvalidSubTransactionId;
+
+ /* Report we changed state */
+ result = true;
+ }
+ else if (portal->createSubid == InvalidSubTransactionId)
+ {
+ /*
+ * Do nothing to cursors held over from a previous transaction
+ * (including ones we just froze in a previous cycle of this loop)
+ */
+ continue;
+ }
+ else
+ {
+ /* Zap all non-holdable portals */
+ PortalDrop(portal, true);
+
+ /* Report we changed state */
+ result = true;
+ }
+
+ /*
+ * After either freezing or dropping a portal, we have to restart the
+ * iteration, because we could have invoked user-defined code that
+ * caused a drop of the next portal in the hash chain.
+ */
+ hash_seq_term(&status);
+ hash_seq_init(&status, PortalHashTable);
+ }
+
+ return result;
+ }
+
+ /*
* Abort processing for portals.
*
* At this point we reset "active" status and run the cleanup hook if
*** a/src/backend/utils/resowner/resowner.c
--- b/src/backend/utils/resowner/resowner.c
***************
*** 103,108 **** typedef struct ResourceOwnerData
--- 103,110 ----
int ndsms; /* number of owned shmem segments */
dsm_segment **dsms; /* dynamically allocated array */
int maxdsms; /* currently allocated array size */
+
+ uint8 inAutoTXLevel;
} ResourceOwnerData;
***************
*** 1339,1341 **** PrintDSMLeakWarning(dsm_segment *seg)
--- 1341,1351 ----
"dynamic shared memory leak: segment %u still referenced",
dsm_segment_handle(seg));
}
+
+ uint8
+ GetCurrentResourceOwnerAutoTXLevel()
+ {
+ Assert(CurrentResourceOwner != NULL);
+
+ return CurrentResourceOwner->inAutoTXLevel;
+ }
*** a/src/include/access/transam.h
--- b/src/include/access/transam.h
***************
*** 163,169 **** extern TransactionId TransactionIdLatest(TransactionId mainxid,
extern XLogRecPtr TransactionIdGetCommitLSN(TransactionId xid);
/* in transam/varsup.c */
! extern TransactionId GetNewTransactionId(bool isSubXact);
extern TransactionId ReadNewTransactionId(void);
extern void SetTransactionIdLimit(TransactionId oldest_datfrozenxid,
Oid oldest_datoid);
--- 163,170 ----
extern XLogRecPtr TransactionIdGetCommitLSN(TransactionId xid);
/* in transam/varsup.c */
! TransactionId GetNewTransactionId(bool isSubXact, int stateNestinglevel,
! int autotxlevel);
extern TransactionId ReadNewTransactionId(void);
extern void SetTransactionIdLimit(TransactionId oldest_datfrozenxid,
Oid oldest_datoid);
*** a/src/include/access/xact.h
--- b/src/include/access/xact.h
***************
*** 27,32 ****
--- 27,38 ----
#define XACT_REPEATABLE_READ 2
#define XACT_SERIALIZABLE 3
+ #define IS_TOP_AUTO_TX_STATE(s) \
+ ( \
+ ((s)->blockState >= TBLOCK_AUTOBEGIN) \
+ && ((s)->blockState <= TBLOCK_AUTOABORT_PENDING) \
+ )
+
extern int DefaultXactIsoLevel;
extern PGDLLIMPORT int XactIsoLevel;
***************
*** 258,261 **** extern int xactGetCommittedChildren(TransactionId **ptr);
--- 264,276 ----
extern void xact_redo(XLogRecPtr lsn, XLogRecord *record);
extern void xact_desc(StringInfo buf, XLogRecord *record);
+ extern void DefineAutonomousTransaction(bool readOnly);
+ extern void BeginInternalAutonomousTransaction(void);
+ extern void CommitInternalAutonomousTransaction(void);
+ extern void AbortInternalAutonomousTransaction(void);
+ extern void BeginAutonomousTransaction(void);
+ extern void CommitAutonomousTransaction(void);
+ extern void AbortAutonomousTransaction(void);
+ extern bool IsCurrentAutoTx(void);
+
#endif /* XACT_H */
*** a/src/include/catalog/namespace.h
--- b/src/include/catalog/namespace.h
***************
*** 141,146 **** extern Oid FindDefaultConversionProc(int32 for_encoding, int32 to_encoding);
--- 141,148 ----
/* initialization & transaction cleanup code */
extern void InitializeSearchPath(void);
extern void AtEOXact_Namespace(bool isCommit);
+ extern void AtEOAutoXact_Namespace(bool isCommit, SubTransactionId mySubid,
+ SubTransactionId parentSubid);
extern void AtEOSubXact_Namespace(bool isCommit, SubTransactionId mySubid,
SubTransactionId parentSubid);
*** a/src/include/commands/trigger.h
--- b/src/include/commands/trigger.h
***************
*** 185,190 **** extern void AfterTriggerBeginXact(void);
--- 185,191 ----
extern void AfterTriggerBeginQuery(void);
extern void AfterTriggerEndQuery(EState *estate);
extern void AfterTriggerFireDeferred(void);
+ extern void AfterTriggerFireDeferredForAutoX(void);
extern void AfterTriggerEndXact(bool isCommit);
extern void AfterTriggerBeginSubXact(void);
extern void AfterTriggerEndSubXact(bool isCommit);
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
***************
*** 2354,2360 **** typedef enum TransactionStmtKind
TRANS_STMT_ROLLBACK_TO,
TRANS_STMT_PREPARE,
TRANS_STMT_COMMIT_PREPARED,
! TRANS_STMT_ROLLBACK_PREPARED
} TransactionStmtKind;
typedef struct TransactionStmt
--- 2354,2361 ----
TRANS_STMT_ROLLBACK_TO,
TRANS_STMT_PREPARE,
TRANS_STMT_COMMIT_PREPARED,
! TRANS_STMT_ROLLBACK_PREPARED,
! TRANS_STMT_AUTONOMOUS
} TransactionStmtKind;
typedef struct TransactionStmt
*** a/src/include/parser/kwlist.h
--- b/src/include/parser/kwlist.h
***************
*** 51,56 **** PG_KEYWORD("asymmetric", ASYMMETRIC, RESERVED_KEYWORD)
--- 51,57 ----
PG_KEYWORD("at", AT, UNRESERVED_KEYWORD)
PG_KEYWORD("attribute", ATTRIBUTE, UNRESERVED_KEYWORD)
PG_KEYWORD("authorization", AUTHORIZATION, TYPE_FUNC_NAME_KEYWORD)
+ PG_KEYWORD("autonomous", AUTONOMOUS, UNRESERVED_KEYWORD)
PG_KEYWORD("backward", BACKWARD, UNRESERVED_KEYWORD)
PG_KEYWORD("before", BEFORE, UNRESERVED_KEYWORD)
PG_KEYWORD("begin", BEGIN_P, UNRESERVED_KEYWORD)
*** a/src/include/storage/lock.h
--- b/src/include/storage/lock.h
***************
*** 77,82 **** typedef struct
--- 77,83 ----
((vxid).backendId = (proc).backendId, \
(vxid).localTransactionId = (proc).lxid)
+ #define MAX_AUTOX_NESTING_LEVEL 3
/*
* LOCKMODE is an integer (1..N) indicating a lock type. LOCKMASK is a bit
***************
*** 363,368 **** typedef struct PROCLOCK
--- 364,371 ----
/* data */
LOCKMASK holdMask; /* bitmask for lock types currently held */
+ LOCKMASK holdMaskByAutoTX[MAX_AUTOX_NESTING_LEVEL]; /* bitmask for lock types currently held by autonomous TX */
+ LOCKMASK holdMaskByNormalTX;
LOCKMASK releaseMask; /* bitmask for lock types to be released */
SHM_QUEUE lockLink; /* list link in LOCK's list of proclocks */
SHM_QUEUE procLink; /* list link in PGPROC's list of proclocks */
*** a/src/include/storage/proc.h
--- b/src/include/storage/proc.h
***************
*** 142,147 **** struct PGPROC
--- 142,150 ----
bool fpVXIDLock; /* are we holding a fast-path VXID lock? */
LocalTransactionId fpLocalTransactionId; /* lxid for fast-path VXID
* lock */
+
+ uint8 inAutoTXLevel; /* At what leve of autonomous tx*/
+ bool isIntAutoTx; /* Is any internal autonomous tx started by proc*/
};
/* NOTE: "typedef struct PGPROC PGPROC" appears in storage/lock.h. */
***************
*** 149,154 **** struct PGPROC
--- 152,158 ----
extern PGDLLIMPORT PGPROC *MyProc;
extern PGDLLIMPORT struct PGXACT *MyPgXact;
+ extern PGDLLIMPORT struct PGAutonomousXACT *MyPgAutonomousXact;
/*
* Prior to PostgreSQL 9.2, the fields below were stored as part of the
***************
*** 177,182 **** typedef struct PGXACT
--- 181,198 ----
uint8 nxids;
} PGXACT;
+ typedef struct PGAutonomousXACT
+ {
+ TransactionId xid;
+ TransactionId xmin;
+
+ int nestingLevel; /* transaction nesting depth */
+ struct XidCache subxids; /* cache for subtransaction XIDs */
+ bool overflowed;
+ bool delayChkpt; /* true if this proc delays checkpoint start*/
+ uint8 nxids; /* number of subtransactions */
+ } PGAutonomousXACT;
+
/*
* There is one ProcGlobal struct for the whole database cluster.
*/
***************
*** 186,191 **** typedef struct PROC_HDR
--- 202,211 ----
PGPROC *allProcs;
/* Array of PGXACT structures (not including dummies for prepared txns) */
PGXACT *allPgXact;
+
+ /* Array of PGAutonomous transaction structure*/
+ PGAutonomousXACT *allPgAutonomousXact;
+
/* Length of allProcs array */
uint32 allProcCount;
/* Head of list of free PGPROC structures */
***************
*** 257,260 **** extern void LockErrorCleanup(void);
--- 277,282 ----
extern void ProcWaitForSignal(void);
extern void ProcSendSignal(int pid);
+ extern PGAutonomousXACT *GetCurrentPGAutonomousXACT(void);
+
#endif /* PROC_H */
*** a/src/include/storage/procarray.h
--- b/src/include/storage/procarray.h
***************
*** 25,30 **** extern void ProcArrayAdd(PGPROC *proc);
--- 25,31 ----
extern void ProcArrayRemove(PGPROC *proc, TransactionId latestXid);
extern void ProcArrayEndTransaction(PGPROC *proc, TransactionId latestXid);
+ extern void ProcArrayEndAutonomousTransaction(PGPROC *proc, TransactionId latestXid);
extern void ProcArrayClearTransaction(PGPROC *proc);
extern void ProcArrayInitRecovery(TransactionId initializedUptoXID);
***************
*** 79,88 **** extern void XidCacheRemoveRunningXids(TransactionId xid,
int nxids, const TransactionId *xids,
TransactionId latestXid);
extern void ProcArraySetReplicationSlotXmin(TransactionId xmin,
TransactionId catalog_xmin, bool already_locked);
extern void ProcArrayGetReplicationSlotXmin(TransactionId *xmin,
! TransactionId *catalog_xmin);
!
#endif /* PROCARRAY_H */
--- 80,92 ----
int nxids, const TransactionId *xids,
TransactionId latestXid);
+ extern void XidCacheRemoveAutoRunningXids(TransactionId xid,
+ int nxids, const TransactionId *xids,
+ TransactionId latestXid, bool isTopAutoTX);
+
extern void ProcArraySetReplicationSlotXmin(TransactionId xmin,
TransactionId catalog_xmin, bool already_locked);
extern void ProcArrayGetReplicationSlotXmin(TransactionId *xmin,
! TransactionId *catalog_xmin);
#endif /* PROCARRAY_H */
*** a/src/include/storage/sinval.h
--- b/src/include/storage/sinval.h
***************
*** 142,148 **** extern void EnableCatchupInterrupt(void);
extern bool DisableCatchupInterrupt(void);
extern int xactGetCommittedInvalidationMessages(SharedInvalidationMessage **msgs,
! bool *RelcacheInitFileInval);
extern void ProcessCommittedInvalidationMessages(SharedInvalidationMessage *msgs,
int nmsgs, bool RelcacheInitFileInval,
Oid dbid, Oid tsid);
--- 142,148 ----
extern bool DisableCatchupInterrupt(void);
extern int xactGetCommittedInvalidationMessages(SharedInvalidationMessage **msgs,
! bool *RelcacheInitFileInval, bool isAutoXact);
extern void ProcessCommittedInvalidationMessages(SharedInvalidationMessage *msgs,
int nmsgs, bool RelcacheInitFileInval,
Oid dbid, Oid tsid);
*** a/src/include/utils/catcache.h
--- b/src/include/utils/catcache.h
***************
*** 174,179 **** extern HeapTuple SearchCatCache(CatCache *cache,
--- 174,183 ----
Datum v3, Datum v4);
extern void ReleaseCatCache(HeapTuple tuple);
+ extern HeapTuple SearchSystableForAutoX(CatCache *cache, Datum v1, Datum v2,
+ Datum v3, Datum v4);
+ extern void ReleaseHeapTupleforAutoX(HeapTuple tuple);
+
extern uint32 GetCatCacheHashValue(CatCache *cache,
Datum v1, Datum v2,
Datum v3, Datum v4);
*** a/src/include/utils/inval.h
--- b/src/include/utils/inval.h
***************
*** 33,38 **** extern void AtEOXact_Inval(bool isCommit);
--- 33,40 ----
extern void AtEOSubXact_Inval(bool isCommit);
+ extern void AtEOAutoXact_Inval(bool isCommit);
+
extern void AtPrepare_Inval(void);
extern void PostPrepare_Inval(void);
*** a/src/include/utils/portal.h
--- b/src/include/utils/portal.h
***************
*** 194,199 **** typedef struct PortalData
--- 194,200 ----
/* Prototypes for functions in utils/mmgr/portalmem.c */
extern void EnablePortalManager(void);
extern bool PreCommit_Portals(bool isPrepare);
+ extern bool AutoPreCommit_Portals(uint32 createSubid);
extern void AtAbort_Portals(void);
extern void AtCleanup_Portals(void);
extern void AtSubCommit_Portals(SubTransactionId mySubid,
*** a/src/include/utils/resowner.h
--- b/src/include/utils/resowner.h
***************
*** 78,82 **** extern void RegisterResourceReleaseCallback(ResourceReleaseCallback callback,
--- 78,83 ----
void *arg);
extern void UnregisterResourceReleaseCallback(ResourceReleaseCallback callback,
void *arg);
+ extern uint8 GetCurrentResourceOwnerAutoTXLevel(void);
#endif /* RESOWNER_H */