*** /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. */