/* * AtSubCommit_Notify() --- Take care of subtransaction commit. * * Reassign all items in the pending notifies list to the parent transaction. */ void AtSubCommit_Notify(void) { List *parentPendingNotifies; parentPendingNotifies = (List *) linitial(upperPendingNotifies); upperPendingNotifies = list_delete_first(upperPendingNotifies); Assert(list_length(upperPendingNotifies) == GetCurrentTransactionNestLevel() - 2); /* * We could try to eliminate duplicates here, but it seems not worthwhile. */ pendingNotifies = list_concat(parentPendingNotifies, pendingNotifies); }
/* * smgrDoPendingDeletes() -- Take care of relation deletes at end of xact. * * This also runs when aborting a subxact; we want to clean up a failed * subxact immediately. */ void smgrDoPendingDeletes(bool isCommit) { int nestLevel = GetCurrentTransactionNestLevel(); PendingRelDelete *pending; PendingRelDelete *prev; PendingRelDelete *next; prev = NULL; for (pending = pendingDeletes; pending != NULL; pending = next) { next = pending->next; if (pending->nestLevel < nestLevel) { /* outer-level entries should not be processed yet */ prev = pending; } else { /* unlink list entry first, so we don't retry on failure */ if (prev) prev->next = next; else pendingDeletes = next; /* do deletion if called for */ if (pending->atCommit == isCommit) { SMgrRelation srel; int i; srel = smgropen(pending->relnode); for (i = 0; i <= MAX_FORKNUM; i++) { if (smgrexists(srel, i)) smgrdounlink(srel, i, pending->isTemp, false); } smgrclose(srel); } /* must explicitly free the list entry */ pfree(pending); /* prev does not change */ } } }
/* * AtSubStart_Notify() --- Take care of subtransaction start. * * Push empty state for the new subtransaction. */ void AtSubStart_Notify(void) { MemoryContext old_cxt; /* Keep the list-of-lists in TopTransactionContext for simplicity */ old_cxt = MemoryContextSwitchTo(TopTransactionContext); upperPendingNotifies = lcons(pendingNotifies, upperPendingNotifies); Assert(list_length(upperPendingNotifies) == GetCurrentTransactionNestLevel() - 1); pendingNotifies = NIL; MemoryContextSwitchTo(old_cxt); }
static void subxact_callback(SubXactEvent event, SubTransactionId mySubid, SubTransactionId parentSubid, void *arg) { char sql[1024]; //fixme HASH_SEQ_STATUS scan; PlxConnHashEntry *entry = NULL; int curlevel; /* Nothing to do at subxact start, nor after commit. */ if (!is_remote_subtransaction || !(event == SUBXACT_EVENT_PRE_COMMIT_SUB || event == SUBXACT_EVENT_ABORT_SUB)) return; curlevel = GetCurrentTransactionNestLevel(); if (event == SUBXACT_EVENT_PRE_COMMIT_SUB) snprintf(sql, sizeof(sql), "release savepoint s%d", curlevel); else snprintf(sql, sizeof(sql), "rollback to savepoint s%d; release savepoint s%d", curlevel, curlevel); hash_seq_init(&scan, plx_conn_cache); while ((entry = (PlxConnHashEntry *) hash_seq_search(&scan))) { PlxConn *plx_conn = entry->plx_conn; if (plx_conn->xlevel < curlevel) continue; if (plx_conn->xlevel > curlevel) ereport(ERROR, (errcode(ERRCODE_RAISE_EXCEPTION), errmsg("missed cleaning up remote subtransaction at level"))); if (!PQexec(plx_conn->pq_conn, sql)) ereport(ERROR, (errcode(ERRCODE_RAISE_EXCEPTION), errmsg("error on %s", sql))); plx_conn->xlevel--; } }
/* * RelationMapUpdateMap * * Install a new relfilenode mapping for the specified relation. * * If immediate is true (or we're bootstrapping), the mapping is activated * immediately. Otherwise it is made pending until CommandCounterIncrement. */ void RelationMapUpdateMap(Oid relationId, Oid fileNode, bool shared, bool immediate) { RelMapFile *map; if (IsBootstrapProcessingMode()) { /* * In bootstrap mode, the mapping gets installed in permanent map. */ if (shared) map = &shared_map; else map = &local_map; } else { /* * We don't currently support map changes within subtransactions. This * could be done with more bookkeeping infrastructure, but it doesn't * presently seem worth it. */ if (GetCurrentTransactionNestLevel() > 1) elog(ERROR, "cannot change relation mapping within subtransaction"); if (immediate) { /* Make it active, but only locally */ if (shared) map = &active_shared_updates; else map = &active_local_updates; } else { /* Make it pending */ if (shared) map = &pending_shared_updates; else map = &pending_local_updates; } } apply_map_update(map, relationId, fileNode, true); }
static void KtBeginTransactionIfNeeded(KtConnCacheEntry* entry) { int curlevel = GetCurrentTransactionNestLevel(); //I dont think it should ever be less than 1; if(curlevel < 1) //I dont get something elog(ERROR, "Transaction level should not be less than one"); // I don't understand if(curlevel > 1) //kt does not support savepoints ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("Kyoto Tycoon does not support savepoints"))); if (entry->xact_depth < curlevel){ if(!ktbegin_transaction(entry->db)) elog(ERROR,"Could not begin transaction from KT %s %s", ktgeterror(entry->db), ktgeterrormsg(entry->db)); entry->xact_depth = curlevel; //1 xact_got_connection = true; } }
/* * AtEOSubXact_Inval * Process queued-up invalidation messages at end of subtransaction. * * If isCommit, process CurrentCmdInvalidMsgs if any (there probably aren't), * and then attach both CurrentCmdInvalidMsgs and PriorCmdInvalidMsgs to the * parent's PriorCmdInvalidMsgs list. * * If not isCommit, we are aborting, and must locally process the messages * in PriorCmdInvalidMsgs. No messages need be sent to other backends. * 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 AtEOSubXact_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); /* If CurrentCmdInvalidMsgs still has anything, fix it */ CommandEndInvalidationMessages(); /* Pass up my inval messages to parent */ AppendInvalidationMessages(&myInfo->parent->PriorCmdInvalidMsgs, &myInfo->PriorCmdInvalidMsgs); /* Pending relcache inval becomes parent's problem too */ if (myInfo->RelcacheInitFileInval) myInfo->parent->RelcacheInitFileInval = true; /* 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); } }
/* * RelationCreateStorage * Create physical storage for a relation. * * Create the underlying disk file storage for the relation. This only * creates the main fork; additional forks are created lazily by the * modules that need them. * * This function is transactional. The creation is WAL-logged, and if the * transaction aborts later on, the storage will be destroyed. */ void RelationCreateStorage(RelFileNode rnode, char relpersistence) { PendingRelDelete *pending; SMgrRelation srel; BackendId backend; bool needs_wal; switch (relpersistence) { case RELPERSISTENCE_TEMP: backend = MyBackendId; needs_wal = false; break; case RELPERSISTENCE_UNLOGGED: backend = InvalidBackendId; needs_wal = false; break; case RELPERSISTENCE_PERMANENT: backend = InvalidBackendId; needs_wal = true; break; default: elog(ERROR, "invalid relpersistence: %c", relpersistence); return; /* placate compiler */ } srel = smgropen(rnode, backend); smgrcreate(srel, MAIN_FORKNUM, false); if (needs_wal) log_smgrcreate(&srel->smgr_rnode.node, MAIN_FORKNUM); /* Add the relation to the list of stuff to delete at abort */ pending = (PendingRelDelete *) MemoryContextAlloc(TopMemoryContext, sizeof(PendingRelDelete)); pending->relnode = rnode; pending->backend = backend; pending->atCommit = false; /* delete if abort */ pending->nestLevel = GetCurrentTransactionNestLevel(); pending->next = pendingDeletes; pendingDeletes = pending; }
/* * Request worker for specified sub/rel to be stopped on commit. */ void logicalrep_worker_stop_at_commit(Oid subid, Oid relid) { int nestDepth = GetCurrentTransactionNestLevel(); LogicalRepWorkerId *wid; MemoryContext oldctx; /* Make sure we store the info in context that survives until commit. */ oldctx = MemoryContextSwitchTo(TopTransactionContext); /* Check that previous transactions were properly cleaned up. */ Assert(on_commit_stop_workers == NULL || nestDepth >= on_commit_stop_workers->nestDepth); /* * Push a new stack element if we don't already have one for the current * nestDepth. */ if (on_commit_stop_workers == NULL || nestDepth > on_commit_stop_workers->nestDepth) { StopWorkersData *newdata = palloc(sizeof(StopWorkersData)); newdata->nestDepth = nestDepth; newdata->workers = NIL; newdata->parent = on_commit_stop_workers; on_commit_stop_workers = newdata; } /* * Finally add a new worker into the worker list of the current * subtransaction. */ wid = palloc(sizeof(LogicalRepWorkerId)); wid->subid = subid; wid->relid = relid; on_commit_stop_workers->workers = lappend(on_commit_stop_workers->workers, wid); MemoryContextSwitchTo(oldctx); }
/* * AtSubAbort_Notify() --- Take care of subtransaction abort. */ void AtSubAbort_Notify(void) { int my_level = GetCurrentTransactionNestLevel(); /* * All we have to do is pop the stack --- the notifies made in this * subxact are no longer interesting, and the space will be freed when * CurTransactionContext is recycled. * * This routine could be called more than once at a given nesting level if * there is trouble during subxact abort. Avoid dumping core by using * GetCurrentTransactionNestLevel as the indicator of how far we need to * prune the list. */ while (list_length(upperPendingNotifies) > my_level - 2) { pendingNotifies = (List *) linitial(upperPendingNotifies); upperPendingNotifies = list_delete_first(upperPendingNotifies); } }
void smgrAppendOnlySubTransAbort(void) { int nestLevel = GetCurrentTransactionNestLevel(); HASH_SEQ_STATUS iterateStatus; AppendOnlyMirrorResyncEofs *entry; /* * Throw away sub-transaction Append-Only mirror resync EOFs. */ hash_seq_init(&iterateStatus, AppendOnlyMirrorResyncEofsTable); while ((entry = hash_seq_search(&iterateStatus)) != NULL) { if (entry->key.nestLevel >= nestLevel) { AppendOnlyMirrorResyncEofs_Remove("smgrSubTransAbort", entry); } } }
/* * RelationCreateStorage * Create physical storage for a relation. * * Create the underlying disk file storage for the relation. This only * creates the main fork; additional forks are created lazily by the * modules that need them. * * This function is transactional. The creation is WAL-logged, and if the * transaction aborts later on, the storage will be destroyed. */ void RelationCreateStorage(RelFileNode rnode, bool istemp) { PendingRelDelete *pending; XLogRecPtr lsn; XLogRecData rdata; xl_smgr_create xlrec; SMgrRelation srel; srel = smgropen(rnode); smgrcreate(srel, MAIN_FORKNUM, false); if (!istemp) { /* * Make an XLOG entry showing the file creation. If we abort, the * file will be dropped at abort time. */ xlrec.rnode = rnode; rdata.data = (char *) &xlrec; rdata.len = sizeof(xlrec); rdata.buffer = InvalidBuffer; rdata.next = NULL; lsn = XLogInsert(RM_SMGR_ID, XLOG_SMGR_CREATE, &rdata); } /* Add the relation to the list of stuff to delete at abort */ pending = (PendingRelDelete *) MemoryContextAlloc(TopMemoryContext, sizeof(PendingRelDelete)); pending->relnode = rnode; pending->isTemp = istemp; pending->atCommit = false; /* delete if abort */ pending->nestLevel = GetCurrentTransactionNestLevel(); pending->next = pendingDeletes; pendingDeletes = pending; }
/* * Start remote transaction or subtransaction, if needed. * * Note that we always use at least REPEATABLE READ in the remote session. * This is so that, if a query initiates multiple scans of the same or * different foreign tables, we will get snapshot-consistent results from * those scans. A disadvantage is that we can't provide sane emulation of * READ COMMITTED behavior --- it would be nice if we had some other way to * control which remote queries share a snapshot. */ static void begin_remote_xact(ConnCacheEntry *entry) { int curlevel = GetCurrentTransactionNestLevel(); //TODO: Replace this code with JDBC Connection call return; /* Start main transaction if we haven't yet */ if (entry->xact_depth <= 0) { const char *sql; elog(DEBUG3, "starting remote transaction on connection %p", entry->conn); if (IsolationIsSerializable()) sql = "START TRANSACTION ISOLATION LEVEL SERIALIZABLE"; else sql = "START TRANSACTION ISOLATION LEVEL REPEATABLE READ"; do_sql_command(entry->conn, sql); entry->xact_depth = 1; } /* * If we're in a subtransaction, stack up savepoints to match our level. * This ensures we can rollback just the desired effects when a * subtransaction aborts. */ while (entry->xact_depth < curlevel) { char sql[64]; snprintf(sql, sizeof(sql), "SAVEPOINT s%d", entry->xact_depth + 1); do_sql_command(entry->conn, sql); entry->xact_depth++; } }
void smgrappendonlymirrorresynceofs(RelFileNode *relFileNode, int32 segmentFileNum, char *relationName, ItemPointer persistentTid, int64 persistentSerialNum, bool mirrorCatchupRequired, MirrorDataLossTrackingState mirrorDataLossTrackingState, int64 mirrorDataLossTrackingSessionNum, int64 mirrorNewEof) { Assert(mirrorNewEof > 0); AppendOnlyMirrorResyncEofs_Merge(relFileNode, segmentFileNum, GetCurrentTransactionNestLevel(), relationName, persistentTid, persistentSerialNum, mirrorCatchupRequired, mirrorDataLossTrackingState, mirrorDataLossTrackingSessionNum, mirrorNewEof); }
/* * smgrDoPendingDeletes() -- Take care of relation deletes at end of xact. * * This also runs when aborting a subxact; we want to clean up a failed * subxact immediately. */ void smgrDoPendingDeletes(bool isCommit) { int nestLevel = GetCurrentTransactionNestLevel(); PendingRelDelete *pending; PendingRelDelete *prev; PendingRelDelete *next; prev = NULL; for (pending = pendingDeletes; pending != NULL; pending = next) { next = pending->next; if (pending->nestLevel < nestLevel) { /* outer-level entries should not be processed yet */ prev = pending; } else { /* unlink list entry first, so we don't retry on failure */ if (prev) prev->next = next; else pendingDeletes = next; /* do deletion if called for */ if (pending->atCommit == isCommit) smgr_internal_unlink(pending->relnode, pending->which, pending->isTemp, false); /* must explicitly free the list entry */ pfree(pending); /* prev does not change */ } } }
void start_transaction(PlxConn *plx_conn) { int curlevel; StringInfo sql = NULL; if (!strcmp(plx_conn->plx_cluster->isolation_level, "auto commit")) return; curlevel = GetCurrentTransactionNestLevel(); if (!is_remote_transaction) { RegisterXactCallback(xact_callback, NULL); RegisterSubXactCallback(subxact_callback, NULL); is_remote_transaction = true; } if (plx_conn->xlevel == 0) { sql = makeStringInfo(); appendStringInfo(sql, "start transaction isolation level %s;", plx_conn->plx_cluster->isolation_level); plx_conn->xlevel = 1; } while (plx_conn->xlevel < curlevel) { if (!sql) sql = makeStringInfo(); appendStringInfo(sql, "savepoint s%d; ", (int) ++(plx_conn->xlevel)); is_remote_subtransaction = true; } if (sql) PQclear(PQexec(plx_conn->pq_conn, sql->data)); }
/* * pgfdw_subxact_callback --- cleanup at subtransaction end. */ static void pgfdw_subxact_callback(SubXactEvent event, SubTransactionId mySubid, SubTransactionId parentSubid, void *arg) { HASH_SEQ_STATUS scan; ConnCacheEntry *entry; int curlevel; /* Nothing to do at subxact start, nor after commit. */ if (!(event == SUBXACT_EVENT_PRE_COMMIT_SUB || event == SUBXACT_EVENT_ABORT_SUB)) return; /* Quick exit if no connections were touched in this transaction. */ if (!xact_got_connection) return; /* * Scan all connection cache entries to find open remote subtransactions * of the current level, and close them. */ curlevel = GetCurrentTransactionNestLevel(); hash_seq_init(&scan, ConnectionHash); while ((entry = (ConnCacheEntry *) hash_seq_search(&scan))) { PGresult *res; char sql[100]; /* * We only care about connections with open remote subtransactions of * the current level. */ if (entry->conn == NULL || entry->xact_depth < curlevel) continue; if (entry->xact_depth > curlevel) elog(ERROR, "missed cleaning up remote subtransaction at level %d", entry->xact_depth); if (event == SUBXACT_EVENT_PRE_COMMIT_SUB) { /* Commit all remote subtransactions during pre-commit */ snprintf(sql, sizeof(sql), "RELEASE SAVEPOINT s%d", curlevel); do_sql_command(entry->conn, sql); } else { /* Assume we might have lost track of prepared statements */ entry->have_error = true; /* * If a command has been submitted to the remote server by using * an asynchronous execution function, the command might not have * yet completed. Check to see if a command is still being * processed by the remote server, and if so, request cancellation * of the command. */ if (PQtransactionStatus(entry->conn) == PQTRANS_ACTIVE) { PGcancel *cancel; char errbuf[256]; if ((cancel = PQgetCancel(entry->conn))) { if (!PQcancel(cancel, errbuf, sizeof(errbuf))) ereport(WARNING, (errcode(ERRCODE_CONNECTION_FAILURE), errmsg("could not send cancel request: %s", errbuf))); PQfreeCancel(cancel); } } /* Rollback all remote subtransactions during abort */ snprintf(sql, sizeof(sql), "ROLLBACK TO SAVEPOINT s%d; RELEASE SAVEPOINT s%d", curlevel, curlevel); res = PQexec(entry->conn, sql); if (PQresultStatus(res) != PGRES_COMMAND_OK) pgfdw_report_error(WARNING, res, entry->conn, true, sql); else PQclear(res); } /* OK, we're outta that level of subtransaction */ entry->xact_depth--; } }
void DtxContextInfo_CreateOnMaster(DtxContextInfo *dtxContextInfo, DistributedSnapshotWithLocalMapping *dslm, CommandId curcid, int txnOptions) { int i; DtxContextInfo_Reset(dtxContextInfo); dtxContextInfo->distributedXid = getDistributedTransactionId(); if (dtxContextInfo->distributedXid != InvalidDistributedTransactionId) { if (syncCacheXid == dtxContextInfo->distributedXid) dtxContextInfo->segmateSync = ++syncCount; else { syncCacheXid = dtxContextInfo->distributedXid; dtxContextInfo->segmateSync = syncCount = 1; } dtxContextInfo->distributedTimeStamp = getDtxStartTime(); getDistributedTransactionIdentifier(dtxContextInfo->distributedId); dtxContextInfo->curcid = curcid; } else { elog((Debug_print_full_dtm ? LOG : DEBUG5), "DtxContextInfo_CreateOnMaster Gp_role is DISPATCH and distributed transaction is InvalidDistributedTransactionId"); syncCacheXid = dtxContextInfo->distributedXid; dtxContextInfo->segmateSync = syncCount = 1; } dtxContextInfo->nestingLevel = GetCurrentTransactionNestLevel(); elog((Debug_print_full_dtm ? LOG : DEBUG5), "DtxContextInfo_CreateOnMaster: created dtxcontext with dxid %u/%u nestingLevel %d segmateSync %u/%u (current/cached)", dtxContextInfo->distributedXid, syncCacheXid, dtxContextInfo->nestingLevel, dtxContextInfo->segmateSync, syncCount); if (dslm == NULL) dtxContextInfo->haveDistributedSnapshot = false; else { DtxContextInfo_CopyDistributedSnapshot(&dtxContextInfo->distributedSnapshot, dslm); dtxContextInfo->haveDistributedSnapshot = true; } dtxContextInfo->distributedTxnOptions = txnOptions; if (DEBUG5 >= log_min_messages || Debug_print_full_dtm) { char gid[TMGIDSIZE]; DistributedSnapshot *ds = &dtxContextInfo->distributedSnapshot; if (!getDistributedTransactionIdentifier(gid)) memcpy(gid, "<empty>", 8); elog((Debug_print_full_dtm ? LOG : DEBUG5), "DtxContextInfo_CreateOnMaster Gp_role is DISPATCH and have currentGxact = %s, gxid = %u --> have distributed snapshot", gid, getDistributedTransactionId()); elog((Debug_print_full_dtm ? LOG : DEBUG5), "DtxContextInfo_CreateOnMaster distributedXid = %u, " "distributedSnapshotHeader (xminAllDistributedSnapshots %u, xmin = %u, xmax = %u, count = %d, maxCount %d)", dtxContextInfo->distributedXid, ds->header.xminAllDistributedSnapshots, ds->header.xmin, ds->header.xmax, ds->header.count, ds->header.maxCount); for (i = 0; i < ds->header.count; i++) { elog((Debug_print_full_dtm ? LOG : DEBUG5), ".... distributedSnapshotData->xip[%d] = %u", i, ds->inProgressXidArray[i]); } elog((Debug_print_full_dtm ? LOG : DEBUG5), "DtxContextInfo_CreateOnMaster curcid = %u", dtxContextInfo->curcid); elog((Debug_print_full_dtm ? LOG : DEBUG5), "DtxContextInfo_CreateOnMaster txnOptions = 0x%x, needTwoPhase = %s, explicitBegin = %s, isoLevel = %s, readOnly = %s.", txnOptions, (isMppTxOptions_NeedTwoPhase(txnOptions) ? "true" : "false"), (isMppTxOptions_ExplicitBegin(txnOptions) ? "true" : "false"), IsoLevelAsUpperString(mppTxOptions_IsoLevel(txnOptions)), (isMppTxOptions_ReadOnly(txnOptions) ? "true" : "false")); } }
int smgrGetAppendOnlyMirrorResyncEofs(EndXactRecKind endXactRecKind, PersistentEndXactAppendOnlyMirrorResyncEofs **ptr) { int nestLevel = GetCurrentTransactionNestLevel(); int nentries; PersistentEndXactAppendOnlyMirrorResyncEofs *rptr; HASH_SEQ_STATUS iterateStatus; AppendOnlyMirrorResyncEofs *entry; int entryIndex; if (endXactRecKind == EndXactRecKind_Abort) { /* * No Append-Only Mirror Resync EOF information needed on abort. */ *ptr = NULL; return 0; } nentries = 0; if (AppendOnlyMirrorResyncEofsTable != NULL) { hash_seq_init(&iterateStatus, AppendOnlyMirrorResyncEofsTable); while ((entry = hash_seq_search(&iterateStatus)) != NULL) { if (entry->key.nestLevel >= nestLevel) nentries++; } } if (nentries == 0) { *ptr = NULL; return 0; } if (Debug_persistent_print || Debug_persistent_appendonly_commit_count_print) elog(Persistent_DebugPrintLevel(), "Storage Manager: Get Append-Only mirror resync eofs list entries (current transaction nest level %d, Append-Only commit work system count %d)", nestLevel, FileRepPrimary_GetAppendOnlyCommitWorkCount()); rptr = (PersistentEndXactAppendOnlyMirrorResyncEofs *) palloc(nentries * sizeof(PersistentEndXactAppendOnlyMirrorResyncEofs)); *ptr = rptr; entryIndex = 0; hash_seq_init(&iterateStatus, AppendOnlyMirrorResyncEofsTable); while ((entry = hash_seq_search(&iterateStatus)) != NULL) { MIRRORED_LOCK_DECLARE; bool returned; int resultSystemAppendOnlyCommitCount; returned = false; if (entry->key.nestLevel >= nestLevel) { MIRRORED_LOCK; MirroredAppendOnly_EndXactCatchup(entryIndex, &entry->key.relFileNode, entry->key.segmentFileNum, entry->key.nestLevel, entry->relationName, &entry->persistentTid, entry->persistentSerialNum, &mirroredLockLocalVars, entry->mirrorCatchupRequired, entry->mirrorDataLossTrackingState, entry->mirrorDataLossTrackingSessionNum, entry->mirrorNewEof); /* * See if the mirror situation for this Append-Only segment file * has changed since we flushed it to disk. */ rptr->relFileNode = entry->key.relFileNode; rptr->segmentFileNum = entry->key.segmentFileNum; rptr->persistentTid = entry->persistentTid; rptr->persistentSerialNum = entry->persistentSerialNum; if (entry->mirrorCatchupRequired) { rptr->mirrorLossEof = INT64CONST(-1); } else { rptr->mirrorLossEof = entry->mirrorNewEof; } rptr->mirrorNewEof = entry->mirrorNewEof; rptr++; returned = true; START_CRIT_SECTION(); LWLockAcquire(FileRepAppendOnlyCommitCountLock, LW_EXCLUSIVE); resultSystemAppendOnlyCommitCount = FileRepPrimary_IntentAppendOnlyCommitWork(); /* Set this inside the Critical Section. */ entry->didIncrementCommitCount = true; if (endXactRecKind == EndXactRecKind_Prepare) { char gid[TMGIDSIZE]; if (!getDistributedTransactionIdentifier(gid)) elog(ERROR, "Unable to obtain gid during prepare"); PrepareIntentAppendOnlyCommitWork(gid); entry->isDistributedTransaction = true; memcpy(entry->gid, gid, TMGIDSIZE); } pendingAppendOnlyMirrorResyncIntentCount++; } else { MIRRORED_LOCK; START_CRIT_SECTION(); LWLockAcquire(FileRepAppendOnlyCommitCountLock, LW_EXCLUSIVE); resultSystemAppendOnlyCommitCount = FileRepPrimary_GetAppendOnlyCommitWorkCount(); } if (Debug_persistent_print || Debug_persistent_appendonly_commit_count_print) { if (entry->relationName == NULL) elog(Persistent_DebugPrintLevel(), "Storage Manager: Get Append-Only mirror resync eofs list entry #%d: %u/%u/%u, segment file #%d " "(returned %s, result system Append-Only commit count %d, transaction nest level %d, persistent TID %s, persistent serial number " INT64_FORMAT ", mirror catchup required %s, mirror new EOF " INT64_FORMAT ")", entryIndex, entry->key.relFileNode.spcNode, entry->key.relFileNode.dbNode, entry->key.relFileNode.relNode, entry->key.segmentFileNum, (returned ? "true" : "false"), resultSystemAppendOnlyCommitCount, entry->key.nestLevel, ItemPointerToString(&entry->persistentTid), entry->persistentSerialNum, (entry->mirrorCatchupRequired ? "true" : "false"), entry->mirrorNewEof); else elog(Persistent_DebugPrintLevel(), "Storage Manager: Get Append-Only mirror resync eofs list entry #%d: %u/%u/%u, segment file #%d, relation name '%s' " "(returned %s, result system Append-Only commit count %d, transaction nest level %d, persistent TID %s, persistent serial number " INT64_FORMAT ", mirror catchup required %s, mirror new EOF " INT64_FORMAT ")", entryIndex, entry->key.relFileNode.spcNode, entry->key.relFileNode.dbNode, entry->key.relFileNode.relNode, entry->key.segmentFileNum, entry->relationName, (returned ? "true" : "false"), resultSystemAppendOnlyCommitCount, entry->key.nestLevel, ItemPointerToString(&entry->persistentTid), entry->persistentSerialNum, (entry->mirrorCatchupRequired ? "true" : "false"), entry->mirrorNewEof); } LWLockRelease(FileRepAppendOnlyCommitCountLock); END_CRIT_SECTION(); MIRRORED_UNLOCK; entryIndex++; } return nentries; }
/* * pgfdw_subxact_callback --- cleanup at subtransaction end. */ static void pgfdw_subxact_callback(SubXactEvent event, SubTransactionId mySubid, SubTransactionId parentSubid, void *arg) { HASH_SEQ_STATUS scan; ConnCacheEntry *entry; int curlevel; /* Nothing to do at subxact start, nor after commit. */ if (!(event == SUBXACT_EVENT_PRE_COMMIT_SUB || event == SUBXACT_EVENT_ABORT_SUB)) return; /* Quick exit if no connections were touched in this transaction. */ if (!xact_got_connection) return; /* * Scan all connection cache entries to find open remote subtransactions * of the current level, and close them. */ curlevel = GetCurrentTransactionNestLevel(); hash_seq_init(&scan, ConnectionHash); while ((entry = (ConnCacheEntry *) hash_seq_search(&scan))) { PGresult *res; char sql[100]; /* * We only care about connections with open remote subtransactions of * the current level. */ if (entry->conn == NULL || entry->xact_depth < curlevel) continue; if (entry->xact_depth > curlevel) elog(ERROR, "missed cleaning up remote subtransaction at level %d", entry->xact_depth); if (event == SUBXACT_EVENT_PRE_COMMIT_SUB) { /* Commit all remote subtransactions during pre-commit */ snprintf(sql, sizeof(sql), "RELEASE SAVEPOINT s%d", curlevel); do_sql_command(entry->conn, sql); } else { /* Assume we might have lost track of prepared statements */ entry->have_error = true; /* Rollback all remote subtransactions during abort */ snprintf(sql, sizeof(sql), "ROLLBACK TO SAVEPOINT s%d; RELEASE SAVEPOINT s%d", curlevel, curlevel); res = PQexec(entry->conn, sql); if (PQresultStatus(res) != PGRES_COMMAND_OK) pgfdw_report_error(WARNING, res, true, sql); else PQclear(res); } /* OK, we're outta that level of subtransaction */ entry->xact_depth--; } }
/* * smgrDoPendingDeletes() -- Take care of relation deletes at end of xact. * * This also runs when aborting a subxact; we want to clean up a failed * subxact immediately. * * Note: It's possible that we're being asked to remove a relation that has * no physical storage in any fork. In particular, it's possible that we're * cleaning up an old temporary relation for which RemovePgTempFiles has * already recovered the physical storage. */ void smgrDoPendingDeletes(bool isCommit) { int nestLevel = GetCurrentTransactionNestLevel(); PendingRelDelete *pending; PendingRelDelete *prev; PendingRelDelete *next; int nrels = 0, i = 0, maxrels = 0; SMgrRelation *srels = NULL; prev = NULL; for (pending = pendingDeletes; pending != NULL; pending = next) { next = pending->next; if (pending->nestLevel < nestLevel) { /* outer-level entries should not be processed yet */ prev = pending; } else { /* unlink list entry first, so we don't retry on failure */ if (prev) prev->next = next; else pendingDeletes = next; /* do deletion if called for */ if (pending->atCommit == isCommit) { SMgrRelation srel; srel = smgropen(pending->relnode, pending->backend); /* allocate the initial array, or extend it, if needed */ if (maxrels == 0) { maxrels = 8; srels = palloc(sizeof(SMgrRelation) * maxrels); } else if (maxrels <= nrels) { maxrels *= 2; srels = repalloc(srels, sizeof(SMgrRelation) * maxrels); } srels[nrels++] = srel; } /* must explicitly free the list entry */ pfree(pending); /* prev does not change */ } } if (nrels > 0) { smgrdounlinkall(srels, nrels, false); for (i = 0; i < nrels; i++) smgrclose(srels[i]); pfree(srels); } }
/* * AtEOSubXact_Inval * Process queued-up invalidation messages at end of subtransaction. * * If isCommit, process CurrentCmdInvalidMsgs if any (there probably aren't), * and then attach both CurrentCmdInvalidMsgs and PriorCmdInvalidMsgs to the * parent's PriorCmdInvalidMsgs list. * * If not isCommit, we are aborting, and must locally process the messages * in PriorCmdInvalidMsgs. No messages need be sent to other backends. * 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 AtEOSubXact_Inval(bool isCommit) { int my_level; TransInvalidationInfo *myInfo = transInvalInfo; /* Quick exit if no messages. */ if (myInfo == NULL) return; /* Also bail out quickly if messages are not for this level. */ my_level = GetCurrentTransactionNestLevel(); if (myInfo->my_level != my_level) { Assert(myInfo->my_level < my_level); return; } if (isCommit) { /* If CurrentCmdInvalidMsgs still has anything, fix it */ CommandEndInvalidationMessages(); /* * We create invalidation stack entries lazily, so the parent might * not have one. Instead of creating one, moving all the data over, * and then freeing our own, we can just adjust the level of our own * entry. */ if (myInfo->parent == NULL || myInfo->parent->my_level < my_level - 1) { myInfo->my_level--; return; } /* Pass up my inval messages to parent */ AppendInvalidationMessages(&myInfo->parent->PriorCmdInvalidMsgs, &myInfo->PriorCmdInvalidMsgs); /* Pending relcache inval becomes parent's problem too */ if (myInfo->RelcacheInitFileInval) myInfo->parent->RelcacheInitFileInval = true; /* Pop the transaction state stack */ transInvalInfo = myInfo->parent; /* Need not free anything else explicitly */ pfree(myInfo); } else { ProcessInvalidationMessages(&myInfo->PriorCmdInvalidMsgs, LocalExecuteInvalidationMessage); /* Pop the transaction state stack */ transInvalInfo = myInfo->parent; /* Need not free anything else explicitly */ pfree(myInfo); } }