*** /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
***************
*** 79,85 **** int synchronous_commit = SYNCHRONOUS_COMMIT_ON;
*/
bool MyXactAccessedTempRel = false;
-
/*
* transaction states - transaction state from server perspective
*/
--- 79,84 ----
***************
*** 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;
/*
--- 122,136 ----
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
--- 156,163 ----
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);
--- 288,295 ----
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);
--- 304,313 ----
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;
--- 346,353 ----
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;
}
--- 363,371 ----
TransactionId
GetTopTransactionId(void)
{
! if (MyProc->inAutoTXLevel)
! return GetTopAutonomousTransactionID();
! else if (!TransactionIdIsValid(TopTransactionStateData.transactionId))
AssignTransactionId(&TopTransactionStateData);
return TopTransactionStateData.transactionId;
}
***************
*** 363,368 **** GetTopTransactionId(void)
--- 380,403 ----
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
--- 485,516 ----
{
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);
/*
--- 562,570 ----
* 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 */
--- 804,827 ----
{
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)
--- 841,853 ----
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);
--- 1067,1073 ----
* 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);
/*
--- 1080,1095 ----
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
--- 1117,1123 ----
* 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();
--- 1146,1158 ----
* 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.
--- 1313,1333 ----
*/
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 */
--- 1339,1346 ----
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 */
--- 1636,1670 ----
* 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;
!
! /* the top of auto TX */
! if(stateNestingLevel == autoNestingLevel)
! 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 */
***************
*** 1799,1805 **** StartTransaction(void)
/*
* Lock the virtual transaction id before we announce it in the proc array
*/
! VirtualXactLockTableInsert(vxid);
/*
* Advertise it in the proc array. We assume assignment of
--- 1917,1923 ----
/*
* Lock the virtual transaction id before we announce it in the proc array
*/
! VirtualXactLockTableInsert(vxid, false);
/*
* Advertise it in the proc array. We assume assignment of
***************
*** 1945,1951 **** CommitTransaction(void)
/*
* Here is where we really truly commit.
*/
! latestXid = RecordTransactionCommit();
TRACE_POSTGRESQL_TRANSACTION_COMMIT(MyProc->lxid);
--- 2063,2069 ----
/*
* Here is where we really truly commit.
*/
! latestXid = RecordTransactionCommit(false);
TRACE_POSTGRESQL_TRANSACTION_COMMIT(MyProc->lxid);
***************
*** 2539,2544 **** StartTransactionCommand(void)
--- 2657,2663 ----
*/
case TBLOCK_INPROGRESS:
case TBLOCK_SUBINPROGRESS:
+ case TBLOCK_AUTOINPROGRESS:
break;
/*
***************
*** 2551,2556 **** StartTransactionCommand(void)
--- 2670,2676 ----
*/
case TBLOCK_ABORT:
case TBLOCK_SUBABORT:
+ case TBLOCK_AUTOABORT:
break;
/* These cases are invalid. */
***************
*** 2567,2572 **** StartTransactionCommand(void)
--- 2687,2696 ----
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;
/*
--- 2826,2833 ----
} while (s->blockState == TBLOCK_SUBRELEASE);
Assert(s->blockState == TBLOCK_INPROGRESS ||
! s->blockState == TBLOCK_SUBINPROGRESS ||
! s->blockState == TBLOCK_AUTOINPROGRESS);
break;
/*
***************
*** 2733,2738 **** CommitTransactionCommand(void)
--- 2858,2868 ----
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)
--- 2944,2973 ----
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)
--- 3054,3060 ----
*/
case TBLOCK_ABORT:
case TBLOCK_SUBABORT:
+ case TBLOCK_AUTOABORT:
break;
/*
***************
*** 2966,2971 **** AbortCurrentTransaction(void)
--- 3121,3142 ----
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)
--- 3440,3447 ----
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)
--- 3461,3470 ----
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)
--- 3489,3498 ----
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;
--- 3568,3574 ----
* 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)
--- 3579,3587 ----
}
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;
--- 3594,3600 ----
* 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)
--- 3609,3619 ----
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)
--- 3632,3646 ----
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)
--- 3655,3664 ----
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;
--- 3705,3711 ----
*/
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)
--- 3720,3730 ----
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)
--- 3742,3754 ----
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)
--- 3764,3773 ----
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
--- 3787,3797 ----
{
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)
--- 3818,3828 ----
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)
--- 3851,3858 ----
* defined.
*/
case TBLOCK_INPROGRESS:
+ case TBLOCK_AUTOINPROGRESS:
+
ereport(ERROR,
(errcode(ERRCODE_S_E_INVALID_SPECIFICATION),
errmsg("no such savepoint")));
***************
*** 3655,3660 **** ReleaseSavepoint(List *options)
--- 3882,3893 ----
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")));
--- 3903,3917 ----
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)
--- 3962,3970 ----
*/
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)
--- 3992,4001 ----
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")));
--- 4011,4025 ----
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 */
/*
--- 4082,4090 ----
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)
--- 4109,4119 ----
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)
--- 4181,4192 ----
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)
--- 4285,4305 ----
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)
--- 4360,4368 ----
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)
--- 4372,4380 ----
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));
--- 4689,4695 ----
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.
--- 4716,4726 ----
* 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;
/*
--- 4753,4776 ----
s->gucNestLevel = NewGUCNestLevel();
s->savepointLevel = p->savepointLevel;
s->state = TRANS_DEFAULT;
GetUserIdAndSecContext(&s->prevUser, &s->prevSecContext);
s->prevXactReadOnly = XactReadOnly;
+ if (isAutoTX)
+ {
+ VirtualTransactionId vxid;
+ MyProc->inAutoTXLevel++;
+ currentautotx = GetCurrentPGAutonomousXACT();
+ currentautotx->nestingLevel = s->nestingLevel;
+ vxid.localTransactionId = currentautotx->lxid = GetNextLocalTransactionId();
+ vxid.backendId = MyProc->backendId;
+ VirtualXactLockTableInsert(vxid, true);
+ s->blockState = TBLOCK_AUTOBEGIN;
+ XactReadOnly = readOnly;
+ }
+ else
+ s->blockState = TBLOCK_SUBBEGIN;
+
CurrentTransactionState = s;
/*
***************
*** 4500,4505 **** static void
--- 4792,4798 ----
PopTransaction(void)
{
TransactionState s = CurrentTransactionState;
+ PGAutonomousXACT *currentautox = NULL;
if (s->state != TRANS_DEFAULT)
elog(WARNING, "PopTransaction while in %s state",
***************
*** 4518,4523 **** PopTransaction(void)
--- 4811,4823 ----
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)
--- 4922,4939 ----
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)
--- 5306,5880 ----
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);
+
+ /* Release auto tx VXID*/
+ VirtualAutoXactLockTableCleanup();
+ 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);
+
+ /* Release auto tx VXID*/
+ VirtualAutoXactLockTableCleanup();
+
+ 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
***************
*** 943,949 **** setval3_oid(PG_FUNCTION_ARGS)
static Relation
open_share_lock(SeqTable seq)
{
! LocalTransactionId thislxid = MyProc->lxid;
/* Get the lock if not already held in this xact */
if (seq->lxid != thislxid)
--- 943,957 ----
static Relation
open_share_lock(SeqTable seq)
{
! LocalTransactionId thislxid;
!
! if (MyProc->inAutoTXLevel)
! {
! PGAutonomousXACT *currentautotx = GetCurrentPGAutonomousXACT();
! thislxid = currentautotx->lxid;
! }
! else
! thislxid = MyProc->lxid;
/* Get the lock if not already held in this xact */
if (seq->lxid != thislxid)
***************
*** 953,959 **** open_share_lock(SeqTable seq)
currentOwner = CurrentResourceOwner;
PG_TRY();
{
! CurrentResourceOwner = TopTransactionResourceOwner;
LockRelationOid(seq->relid, AccessShareLock);
}
PG_CATCH();
--- 961,969 ----
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;
***************
*** 10735,10740 **** register_on_commit_action(Oid relid, OnCommitAction action)
--- 10739,10745 ----
oc = (OnCommitItem *) palloc(sizeof(OnCommitItem));
oc->relid = relid;
oc->oncommit = action;
+ oc->toptxid = GetTopTransactionId();
oc->creating_subid = GetCurrentSubTransactionId();
oc->deleting_subid = InvalidSubTransactionId;
***************
*** 10792,10797 **** 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
***************
*** 10805,10810 **** 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,3700 ----
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;
--- 3757,3764 ----
afterTriggerInvokeEvents(AfterTriggerEventList *events,
CommandId firing_id,
EState *estate,
! bool delete_ok,
! bool inAutoX)
{
bool all_fired = true;
AfterTriggerEventChunk *chunk;
***************
*** 3764,3769 **** afterTriggerInvokeEvents(AfterTriggerEventList *events,
--- 3770,3776 ----
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,
--- 3796,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,4197 ----
* 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)
--- 4202,4208 ----
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++;
--- 4750,4756 ----
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 */
}
--- 4775,4781 ----
* 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,2092 ----
* 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",
***************
*** 12858,12863 **** unreserved_keyword:
--- 12877,12883 ----
| 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;
+ 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,504 ----
}
}
+ 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;
+ pgautonouxact->lxid = InvalidLocalTransactionId;
+
+ /* 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 */
+
+ pgautonouxact->lxid = InvalidLocalTransactionId;
+
+ 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
--- 910,917 ----
ProcArrayStruct *arrayP = procArray;
TransactionId topxid;
int i,
! j,
! k;
/*
* Don't bother checking a transaction older than RecentXmin; it could not
***************
*** 900,906 **** TransactionIdIsInProgress(TransactionId xid)
* known-assigned list. If we later finish recovery, we no longer need
* the bigger array, but we don't bother to shrink it.
*/
! int maxxids = RecoveryInProgress() ? TOTAL_MAX_CACHED_SUBXIDS : arrayP->maxProcs;
xids = (TransactionId *) malloc(maxxids * sizeof(TransactionId));
if (xids == NULL)
--- 957,964 ----
* known-assigned list. If we later finish recovery, we no longer need
* the bigger array, but we don't bother to shrink it.
*/
! int maxxids = (RecoveryInProgress() ? TOTAL_MAX_CACHED_SUBXIDS
! : arrayP->maxProcs);
xids = (TransactionId *) malloc(maxxids * sizeof(TransactionId));
if (xids == NULL)
***************
*** 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 */
--- 984,998 ----
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)
--- 1043,1087 ----
*/
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)
--- 1467,1473 ----
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)
--- 1536,1546 ----
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. */
--- 1577,1589 ----
*/
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)
--- 1623,1669 ----
}
}
}
+
+ /* 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
***************
*** 2039,2052 **** GetVirtualXIDsDelayingChkpt(int *nvxids)
for (index = 0; index < arrayP->numProcs; index++)
{
int pgprocno = arrayP->pgprocnos[index];
volatile PGPROC *proc = &allProcs[pgprocno];
volatile PGXACT *pgxact = &allPgXact[pgprocno];
! if (pgxact->delayChkpt)
! {
! VirtualTransactionId vxid;
GET_VXID_FROM_PGPROC(vxid, *proc);
if (VirtualTransactionIdIsValid(vxid))
vxids[count++] = vxid;
--- 2192,2223 ----
for (index = 0; index < arrayP->numProcs; index++)
{
+ VirtualTransactionId vxid;
+
int pgprocno = arrayP->pgprocnos[index];
volatile PGPROC *proc = &allProcs[pgprocno];
volatile PGXACT *pgxact = &allPgXact[pgprocno];
! int pgautoxno = pgprocno * MAX_AUTOX_NESTING_LEVEL;
! volatile PGAutonomousXACT *pgautonomousxact;
+ /*
+ * If autonomous transaction is there, then only lowest autonomous
+ * transaction can be in "in commit" stage. So in that case skip
+ * checking for this stage for upper transaction.
+ */
+ if (proc->inAutoTXLevel)
+ {
+ pgautonomousxact = &allPgAutonomousXact[pgautoxno + proc->inAutoTXLevel - 1];
+ if (pgautonomousxact->delayChkpt)
+ {
+ GET_VXID_FROM_PGAUTOXACT(vxid, *proc, *pgautonomousxact);
+ if (VirtualTransactionIdIsValid(vxid))
+ vxids[count++] = vxid;
+ }
+ }
+ else if (pgxact->delayChkpt)
+ {
GET_VXID_FROM_PGPROC(vxid, *proc);
if (VirtualTransactionIdIsValid(vxid))
vxids[count++] = vxid;
***************
*** 2084,2092 **** HaveVirtualXIDsDelayingChkpt(VirtualTransactionId *vxids, int nvxids)
volatile PGXACT *pgxact = &allPgXact[pgprocno];
VirtualTransactionId vxid;
! GET_VXID_FROM_PGPROC(vxid, *proc);
! if (pgxact->delayChkpt && VirtualTransactionIdIsValid(vxid))
{
int i;
--- 2255,2278 ----
volatile PGXACT *pgxact = &allPgXact[pgprocno];
VirtualTransactionId vxid;
! int pgautoxno = pgprocno * MAX_AUTOX_NESTING_LEVEL;
! volatile PGAutonomousXACT *pgautonomousxact;
! bool delayChkpt = false;
!
! if (proc->inAutoTXLevel)
! {
! pgautonomousxact = &allPgAutonomousXact[pgautoxno + proc->inAutoTXLevel - 1];
! delayChkpt = pgautonomousxact->delayChkpt;
! GET_VXID_FROM_PGAUTOXACT(vxid, *proc, *pgautonomousxact);
! }
! else
! {
! delayChkpt = pgxact->delayChkpt;
! GET_VXID_FROM_PGPROC(vxid, *proc);
! }
!
! if (delayChkpt && VirtualTransactionIdIsValid(vxid))
{
int i;
***************
*** 2795,2800 **** XidCacheRemoveRunningXids(TransactionId xid,
--- 2981,3069 ----
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/ipc/standby.c
--- b/src/backend/storage/ipc/standby.c
***************
*** 85,91 **** InitRecoveryTransactionEnvironment(void)
*/
vxid.backendId = MyBackendId;
vxid.localTransactionId = GetNextLocalTransactionId();
! VirtualXactLockTableInsert(vxid);
standbyState = STANDBY_INITIALIZED;
}
--- 85,91 ----
*/
vxid.backendId = MyBackendId;
vxid.localTransactionId = GetNextLocalTransactionId();
! VirtualXactLockTableInsert(vxid, false);
standbyState = STANDBY_INITIALIZED;
}
*** 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
***************
*** 50,58 ****
/* This configuration variable is used to set the lock table size */
int max_locks_per_xact; /* set by guc.c */
! #define NLOCKENTS() \
! mul_size(max_locks_per_xact, add_size(MaxBackends, max_prepared_xacts))
/*
* Data structures defining the semantics of the standard lock methods.
--- 50,60 ----
/* This configuration variable is used to set the lock table size */
int max_locks_per_xact; /* set by guc.c */
! extern PGAutonomousXACT *allPgAutonomousXact;
+ #define NLOCKENTS() \
+ mul_size(max_locks_per_xact, add_size((MAX_AUTOX_NESTING_LEVEL*MaxConnections), \
+ add_size(MaxBackends, max_prepared_xacts)))
/*
* Data structures defining the semantics of the standard lock methods.
***************
*** 355,360 **** static void LockRefindAndRelease(LockMethod lockMethodTable, PGPROC *proc,
--- 357,365 ----
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,
--- 788,797 ----
*/
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;
--- 827,835 ----
* 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,
--- 1152,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,1385 ----
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,
--- 1435,1446 ----
/*
* 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);
***************
*** 2823,2844 **** GetLockConflicts(const LOCKTAG *locktag, LOCKMODE lockmode)
while (proclock)
{
! if (conflictMask & proclock->holdMask)
{
! PGPROC *proc = proclock->tag.myProc;
! /* A backend never blocks itself */
! if (proc != MyProc)
{
! VirtualTransactionId vxid;
! GET_VXID_FROM_PGPROC(vxid, *proc);
! /*
! * If we see an invalid VXID, then either the xact has already
! * committed (or aborted), or it's a prepared xact. In either
! * case we may ignore it.
! */
if (VirtualTransactionIdIsValid(vxid))
{
int i;
--- 2870,2909 ----
while (proclock)
{
! PGPROC *proc = proclock->tag.myProc;
! /* A backend never blocks itself */
! if (proc != MyProc)
{
! VirtualTransactionId vxid;
! int txLevel;
! /*
! * If we see an invalid VXID, then either the xact has already
! * committed (or aborted), or it's a prepared xact. In either
! * case we may ignore it.
! */
!
! /* First we should check for autonomous transaction*/
! for (txLevel = 0; txLevel < proc->inAutoTXLevel; txLevel++)
{
! int pgautoxno = proc->pgprocno * MAX_AUTOX_NESTING_LEVEL;
! PGAutonomousXACT *autoxact = &allPgAutonomousXact[pgautoxno
! + txLevel];
! if (conflictMask & proclock->holdMaskByAutoTX[txLevel])
! {
! GET_VXID_FROM_PGAUTOXACT(vxid, *proc, *autoxact);
! /* Auto tx will not be part of FPL, so no need to check*/
! if (VirtualTransactionIdIsValid(vxid))
! vxids[count++] = vxid;
! }
! }
!
! /* Then main transaction*/
! if (conflictMask & proclock->holdMaskByNormalTX)
! {
! GET_VXID_FROM_PGPROC(vxid, *proc);
if (VirtualTransactionIdIsValid(vxid))
{
int i;
***************
*** 3786,3791 **** lock_twophase_recover(TransactionId xid, uint16 info,
--- 3851,3861 ----
{
proclock->holdMask = 0;
proclock->releaseMask = 0;
+
+ MemSet(proclock->holdMaskByAutoTX, 0, MAX_AUTOX_NESTING_LEVEL *
+ (sizeof(LOCKMASK)));
+ proclock->holdMaskByNormalTX = 0;
+
/* Add proclock to appropriate lists */
SHMQueueInsertBefore(&lock->procLocks, &proclock->lockLink);
SHMQueueInsertBefore(&(proc->myProcLocks[partition]),
***************
*** 3925,3946 **** lock_twophase_postabort(TransactionId xid, uint16 info,
* LockReleaseAll() calls VirtualXactLockTableCleanup().
*/
void
! VirtualXactLockTableInsert(VirtualTransactionId vxid)
{
Assert(VirtualTransactionIdIsValid(vxid));
LWLockAcquire(MyProc->backendLock, LW_EXCLUSIVE);
! Assert(MyProc->backendId == vxid.backendId);
! Assert(MyProc->fpLocalTransactionId == InvalidLocalTransactionId);
! Assert(MyProc->fpVXIDLock == false);
! MyProc->fpVXIDLock = true;
! MyProc->fpLocalTransactionId = vxid.localTransactionId;
LWLockRelease(MyProc->backendLock);
}
/*
* VirtualXactLockTableCleanup
*
--- 3995,4055 ----
* LockReleaseAll() calls VirtualXactLockTableCleanup().
*/
void
! VirtualXactLockTableInsert(VirtualTransactionId vxid, bool isAutoTx)
{
Assert(VirtualTransactionIdIsValid(vxid));
LWLockAcquire(MyProc->backendLock, LW_EXCLUSIVE);
! if (!isAutoTx)
! {
! Assert(MyProc->backendId == vxid.backendId);
! Assert(MyProc->fpLocalTransactionId == InvalidLocalTransactionId);
! Assert(MyProc->fpVXIDLock == false);
! MyProc->fpVXIDLock = true;
! MyProc->fpLocalTransactionId = vxid.localTransactionId;
! }
! else
! {
! int autoLevel = MyProc->inAutoTXLevel - 1; /* 0 based index*/
! PGAutonomousXACT *autoxact = &MyPgAutonomousXact[autoLevel];
!
! autoxact->fpLocalAuto.fpAutoLxid= autoxact->lxid;
! autoxact->fpLocalAuto.fpAutoVXIDLock = true;
! }
LWLockRelease(MyProc->backendLock);
}
+ void
+ VirtualAutoXactLockTableCleanup(void)
+ {
+ LocalTransactionId lxid;
+ bool fastpath;
+ PGAutonomousXACT *myautoxact = GetCurrentPGAutonomousXACT();
+
+ LWLockAcquire(MyProc->backendLock, LW_EXCLUSIVE);
+
+ fastpath = myautoxact->fpLocalAuto.fpAutoVXIDLock;
+ lxid = myautoxact->fpLocalAuto.fpAutoLxid;
+
+ LWLockRelease(MyProc->backendLock);
+
+ if (!fastpath && LocalTransactionIdIsValid(lxid))
+ {
+ VirtualTransactionId vxid;
+ LOCKTAG locktag;
+
+ vxid.backendId = MyBackendId;
+ vxid.localTransactionId = lxid;
+ SET_LOCKTAG_VIRTUALTRANSACTION(locktag, vxid);
+
+ LockRefindAndRelease(LockMethods[DEFAULT_LOCKMETHOD], MyProc,
+ &locktag, ExclusiveLock, false);
+ }
+ }
+
/*
* VirtualXactLockTableCleanup
*
***************
*** 3985,3990 **** VirtualXactLockTableCleanup(void)
--- 4094,4115 ----
}
}
+ int VirtualXactInAutoTx(PGPROC *proc, LocalTransactionId lxid)
+ {
+ int level;
+ int pgautoxno = proc->pgprocno * MAX_AUTOX_NESTING_LEVEL;
+ PGAutonomousXACT *autoxact = &allPgAutonomousXact[pgautoxno];
+
+
+ for (level = 0; level < proc->inAutoTXLevel; level++)
+ {
+ if (autoxact[level].fpLocalAuto.fpAutoLxid == lxid)
+ return level;
+ }
+
+ return -1;
+ }
+
/*
* VirtualXactLock
*
***************
*** 3999,4004 **** VirtualXactLock(VirtualTransactionId vxid, bool wait)
--- 4124,4134 ----
{
LOCKTAG tag;
PGPROC *proc;
+ int level = -1;
+ bool lockEntryReq = false;
+ int pgautoxno;
+ PGAutonomousXACT *autoxact;
+
Assert(VirtualTransactionIdIsValid(vxid));
***************
*** 4025,4031 **** VirtualXactLock(VirtualTransactionId vxid, bool wait)
/* If the transaction has ended, our work here is done. */
if (proc->backendId != vxid.backendId
! || proc->fpLocalTransactionId != vxid.localTransactionId)
{
LWLockRelease(proc->backendLock);
return true;
--- 4155,4162 ----
/* If the transaction has ended, our work here is done. */
if (proc->backendId != vxid.backendId
! || ((proc->fpLocalTransactionId != vxid.localTransactionId)
! && (level = VirtualXactInAutoTx(proc, vxid.localTransactionId)) == -1 ))
{
LWLockRelease(proc->backendLock);
return true;
***************
*** 4041,4052 **** VirtualXactLock(VirtualTransactionId vxid, bool wait)
return false;
}
/*
* OK, we're going to need to sleep on the VXID. But first, we must set
* up the primary lock table entry, if needed (ie, convert the proc's
* fast-path lock on its VXID to a regular lock).
*/
! if (proc->fpVXIDLock)
{
PROCLOCK *proclock;
uint32 hashcode;
--- 4172,4196 ----
return false;
}
+ /* Here means transaction corresponding VXID has not ended.*/
+ if (level == -1)
+ {
+ /* Means it is main transaction*/
+ lockEntryReq = proc->fpVXIDLock;
+ }
+ else
+ {
+ pgautoxno = proc->pgprocno * MAX_AUTOX_NESTING_LEVEL;
+ autoxact = &allPgAutonomousXact[pgautoxno + level];
+ lockEntryReq = autoxact->fpLocalAuto.fpAutoVXIDLock;
+ }
+
/*
* OK, we're going to need to sleep on the VXID. But first, we must set
* up the primary lock table entry, if needed (ie, convert the proc's
* fast-path lock on its VXID to a regular lock).
*/
! if (lockEntryReq)
{
PROCLOCK *proclock;
uint32 hashcode;
***************
*** 4072,4078 **** VirtualXactLock(VirtualTransactionId vxid, bool wait)
LWLockRelease(partitionLock);
! proc->fpVXIDLock = false;
}
/* Done with proc->fpLockBits */
--- 4216,4228 ----
LWLockRelease(partitionLock);
! if (level == -1)
! {
! /* Means it is main transaction*/
! proc->fpVXIDLock = false;
! }
! else
! autoxact->fpLocalAuto.fpAutoVXIDLock = false;
}
/* Done with proc->fpLockBits */
***************
*** 4084,4086 **** VirtualXactLock(VirtualTransactionId vxid, bool wait)
--- 4234,4281 ----
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,517 ----
return false;
/*
+ * 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;
+
+ /*
* 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,125 ----
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)
--- 170,178 ----
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)
--- 221,232 ----
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)
--- 296,302 ----
{
/* 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)
--- 335,343 ----
if (MyProc != NULL)
{
+ MyProc->inAutoTXLevel = 0;
+
+
if (IsAnyAutoVacuumProcess())
procglobal->autovacFreeProcs = (PGPROC *) MyProc->links.next;
else if (IsBackgroundWorker)
***************
*** 340,345 **** InitProcess(void)
--- 361,371 ----
}
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
***************
*** 389,394 **** InitProcess(void)
--- 415,421 ----
/* Initialize fields for sync rep */
MyProc->waitLSN = 0;
MyProc->syncRepState = SYNC_REP_NOT_WAITING;
+
SHMQueueElemInit(&(MyProc->syncRepLinks));
/*
***************
*** 463,468 **** InitAuxiliaryProcess(void)
--- 490,496 ----
{
PGPROC *auxproc;
int proctype;
+ int pgautoxno;
/*
* ProcGlobal should be set up already (if we are a backend, we inherit
***************
*** 514,519 **** InitAuxiliaryProcess(void)
--- 542,551 ----
MyProc = auxproc;
MyPgXact = &ProcGlobal->allPgXact[auxproc->pgprocno];
+ pgautoxno = auxproc->pgprocno * MAX_AUTOX_NESTING_LEVEL;
+ MyPgAutonomousXact = &ProcGlobal->allPgAutonomousXact[pgautoxno];
+
+
SpinLockRelease(ProcStructLock);
/*
***************
*** 1666,1668 **** ProcSendSignal(int pid)
--- 1698,1708 ----
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
***************
*** 3853,3858 **** PostgresMain(int argc, char *argv[],
--- 3853,3863 ----
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
***************
*** 1340,1345 **** ReleaseCatCache(HeapTuple tuple)
--- 1340,1412 ----
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,1091 ----
}
/*
+ * 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,916 ----
!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,
--- 921,933 ----
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;
***************
*** 161,166 **** ResourceOwnerCreate(ResourceOwner parent, const char *name)
--- 163,170 ----
sizeof(ResourceOwnerData));
owner->name = name;
+ owner->inAutoTXLevel = MyProc->inAutoTXLevel;
+
if (parent)
{
owner->parent = parent;
***************
*** 1339,1341 **** PrintDSMLeakWarning(dsm_segment *seg)
--- 1343,1353 ----
"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
***************
*** 2371,2377 **** typedef enum TransactionStmtKind
TRANS_STMT_ROLLBACK_TO,
TRANS_STMT_PREPARE,
TRANS_STMT_COMMIT_PREPARED,
! TRANS_STMT_ROLLBACK_PREPARED
} TransactionStmtKind;
typedef struct TransactionStmt
--- 2371,2378 ----
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,87 ----
((vxid).backendId = (proc).backendId, \
(vxid).localTransactionId = (proc).lxid)
+ #define GET_VXID_FROM_PGAUTOXACT(vxid, proc, autoxact) \
+ ((vxid).backendId = (proc).backendId, \
+ (vxid).localTransactionId = (autoxact).lxid)
+
+ #define MAX_AUTOX_NESTING_LEVEL 3
/*
* LOCKMODE is an integer (1..N) indicating a lock type. LOCKMASK is a bit
***************
*** 92,98 **** typedef int LOCKMODE;
#define LOCKBIT_ON(lockmode) (1 << (lockmode))
#define LOCKBIT_OFF(lockmode) (~(1 << (lockmode)))
-
/*
* This data structure defines the locking semantics associated with a
* "lock method". The semantics specify the meaning of each lock mode
--- 97,102 ----
***************
*** 363,368 **** typedef struct PROCLOCK
--- 367,378 ----
/* data */
LOCKMASK holdMask; /* bitmask for lock types currently held */
+
+ /* bitmask for lock types currently held by autonomous TX */
+ LOCKMASK holdMaskByAutoTX[MAX_AUTOX_NESTING_LEVEL];
+
+ /* bitmask for lock types currently held by normal 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 */
***************
*** 562,569 **** extern void DumpAllLocks(void);
#endif
/* Lock a VXID (used to wait for a transaction to finish) */
! extern void VirtualXactLockTableInsert(VirtualTransactionId vxid);
extern void VirtualXactLockTableCleanup(void);
extern bool VirtualXactLock(VirtualTransactionId vxid, bool wait);
#endif /* LOCK_H */
--- 572,583 ----
#endif
/* Lock a VXID (used to wait for a transaction to finish) */
! extern void VirtualXactLockTableInsert(VirtualTransactionId vxid,
! bool isAutoTx);
extern void VirtualXactLockTableCleanup(void);
extern bool VirtualXactLock(VirtualTransactionId vxid, bool wait);
+ extern void VirtualAutoXactLockTableCleanup(void);
+ extern int VirtualXactInAutoTx(PGPROC *proc, LocalTransactionId lxid);
+
#endif /* LOCK_H */
*** 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 level 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,209 ----
uint8 nxids;
} PGXACT;
+
+ /* Structure to hold VXID FL for autonomous transaction*/
+ typedef struct AutoXactVXIDFP
+ {
+ LocalTransactionId fpAutoLxid;
+ bool fpAutoVXIDLock;
+ }AutoXactVXIDFP;
+
+ /* Structure like PGXACT but for autonomous transaction*/
+ 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 */
+ LocalTransactionId lxid; /* Local transaction id for virtual tx*/
+ AutoXactVXIDFP fpLocalAuto; /* Auto local xid*/
+ } PGAutonomousXACT;
+
/*
* There is one ProcGlobal struct for the whole database cluster.
*/
***************
*** 186,191 **** typedef struct PROC_HDR
--- 213,222 ----
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);
--- 288,293 ----
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,32 ----
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 */
--- 81,93 ----
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 */
*** a/src/pl/plpgsql/src/pl_exec.c
--- b/src/pl/plpgsql/src/pl_exec.c
***************
*** 5108,5119 **** exec_eval_simple_expr(PLpgSQL_execstate *estate,
Oid *rettype)
{
ExprContext *econtext = estate->eval_econtext;
! LocalTransactionId curlxid = MyProc->lxid;
CachedPlan *cplan;
ParamListInfo paramLI;
PLpgSQL_expr *save_cur_expr;
MemoryContext oldcontext;
/*
* Forget it if expression wasn't simple before.
*/
--- 5108,5127 ----
Oid *rettype)
{
ExprContext *econtext = estate->eval_econtext;
! LocalTransactionId curlxid;
CachedPlan *cplan;
ParamListInfo paramLI;
PLpgSQL_expr *save_cur_expr;
MemoryContext oldcontext;
+ if (MyProc->inAutoTXLevel)
+ {
+ PGAutonomousXACT *currentautotx = GetCurrentPGAutonomousXACT();
+ curlxid = currentautotx->lxid;
+ }
+ else
+ curlxid = MyProc->lxid;
+
/*
* Forget it if expression wasn't simple before.
*/