void ProcessFiles(void) { int i; if (NQuotaRecs == 0) { if (NFiles != 0) AddRecord(SAM_QUOTA_ADMIN, -1); AddRecord(SAM_QUOTA_GROUP, -1); AddRecord(SAM_QUOTA_USER, -1); } if (NFiles == 0 && NQuotaOps == 0) ReadSamFSes(); for (i = 0; i < NFiles && !Err; i++) ProcessRecords(FileList[i]); }
/* * RecoverPreparedTransactions * * Scan the pg_twophase directory and reload shared-memory state for each * prepared transaction (reacquire locks, etc). This is run during database * startup. */ void RecoverPreparedTransactions(void) { char dir[MAXPGPATH]; DIR *cldir; struct dirent *clde; snprintf(dir, MAXPGPATH, "%s", TWOPHASE_DIR); cldir = AllocateDir(dir); while ((clde = ReadDir(cldir, dir)) != NULL) { if (strlen(clde->d_name) == 8 && strspn(clde->d_name, "0123456789ABCDEF") == 8) { TransactionId xid; char *buf; char *bufptr; TwoPhaseFileHeader *hdr; TransactionId *subxids; GlobalTransaction gxact; int i; xid = (TransactionId) strtoul(clde->d_name, NULL, 16); /* Already processed? */ if (TransactionIdDidCommit(xid) || TransactionIdDidAbort(xid)) { ereport(WARNING, (errmsg("removing stale two-phase state file \"%s\"", clde->d_name))); RemoveTwoPhaseFile(xid, true); continue; } /* Read and validate file */ buf = ReadTwoPhaseFile(xid); if (buf == NULL) { ereport(WARNING, (errmsg("removing corrupt two-phase state file \"%s\"", clde->d_name))); RemoveTwoPhaseFile(xid, true); continue; } ereport(LOG, (errmsg("recovering prepared transaction %u", xid))); /* Deconstruct header */ hdr = (TwoPhaseFileHeader *) buf; Assert(TransactionIdEquals(hdr->xid, xid)); bufptr = buf + MAXALIGN(sizeof(TwoPhaseFileHeader)); subxids = (TransactionId *) bufptr; bufptr += MAXALIGN(hdr->nsubxacts * sizeof(TransactionId)); bufptr += MAXALIGN(hdr->ncommitrels * sizeof(RelFileNode)); bufptr += MAXALIGN(hdr->nabortrels * sizeof(RelFileNode)); /* * Reconstruct subtrans state for the transaction --- needed * because pg_subtrans is not preserved over a restart. Note that * we are linking all the subtransactions directly to the * top-level XID; there may originally have been a more complex * hierarchy, but there's no need to restore that exactly. */ for (i = 0; i < hdr->nsubxacts; i++) SubTransSetParent(subxids[i], xid); /* * Recreate its GXACT and dummy PGPROC * * Note: since we don't have the PREPARE record's WAL location at * hand, we leave prepare_lsn zeroes. This means the GXACT will * be fsync'd on every future checkpoint. We assume this * situation is infrequent enough that the performance cost is * negligible (especially since we know the state file has already * been fsynced). */ gxact = MarkAsPreparing(xid, hdr->gid, hdr->prepared_at, hdr->owner, hdr->database); GXactLoadSubxactData(gxact, hdr->nsubxacts, subxids); MarkAsPrepared(gxact); /* * Recover other state (notably locks) using resource managers */ ProcessRecords(bufptr, xid, twophase_recover_callbacks); pfree(buf); } } FreeDir(cldir); }
/* * FinishPreparedTransaction: execute COMMIT PREPARED or ROLLBACK PREPARED */ void FinishPreparedTransaction(const char *gid, bool isCommit) { GlobalTransaction gxact; TransactionId xid; char *buf; char *bufptr; TwoPhaseFileHeader *hdr; TransactionId latestXid; TransactionId *children; RelFileNode *commitrels; RelFileNode *abortrels; RelFileNode *delrels; int ndelrels; int i; /* * Validate the GID, and lock the GXACT to ensure that two backends do not * try to commit the same GID at once. */ gxact = LockGXact(gid, GetUserId()); xid = gxact->proc.xid; /* * Read and validate the state file */ buf = ReadTwoPhaseFile(xid); if (buf == NULL) ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), errmsg("two-phase state file for transaction %u is corrupt", xid))); /* * Disassemble the header area */ hdr = (TwoPhaseFileHeader *) buf; Assert(TransactionIdEquals(hdr->xid, xid)); bufptr = buf + MAXALIGN(sizeof(TwoPhaseFileHeader)); children = (TransactionId *) bufptr; bufptr += MAXALIGN(hdr->nsubxacts * sizeof(TransactionId)); commitrels = (RelFileNode *) bufptr; bufptr += MAXALIGN(hdr->ncommitrels * sizeof(RelFileNode)); abortrels = (RelFileNode *) bufptr; bufptr += MAXALIGN(hdr->nabortrels * sizeof(RelFileNode)); /* compute latestXid among all children */ latestXid = TransactionIdLatest(xid, hdr->nsubxacts, children); /* * The order of operations here is critical: make the XLOG entry for * commit or abort, then mark the transaction committed or aborted in * pg_clog, then remove its PGPROC from the global ProcArray (which means * TransactionIdIsInProgress will stop saying the prepared xact is in * progress), then run the post-commit or post-abort callbacks. The * callbacks will release the locks the transaction held. */ if (isCommit) RecordTransactionCommitPrepared(xid, hdr->nsubxacts, children, hdr->ncommitrels, commitrels); else RecordTransactionAbortPrepared(xid, hdr->nsubxacts, children, hdr->nabortrels, abortrels); ProcArrayRemove(&gxact->proc, latestXid); /* * In case we fail while running the callbacks, mark the gxact invalid so * no one else will try to commit/rollback, and so it can be recycled * properly later. It is still locked by our XID so it won't go away yet. * * (We assume it's safe to do this without taking TwoPhaseStateLock.) */ gxact->valid = false; /* * We have to remove any files that were supposed to be dropped. For * consistency with the regular xact.c code paths, must do this before * releasing locks, so do it before running the callbacks. * * NB: this code knows that we couldn't be dropping any temp rels ... */ if (isCommit) { delrels = commitrels; ndelrels = hdr->ncommitrels; } else { delrels = abortrels; ndelrels = hdr->nabortrels; } for (i = 0; i < ndelrels; i++) { SMgrRelation srel = smgropen(delrels[i]); ForkNumber fork; for (fork = 0; fork <= MAX_FORKNUM; fork++) { if (smgrexists(srel, fork)) smgrdounlink(srel, fork, false, false); } smgrclose(srel); } /* And now do the callbacks */ if (isCommit) ProcessRecords(bufptr, xid, twophase_postcommit_callbacks); else ProcessRecords(bufptr, xid, twophase_postabort_callbacks); /* Count the prepared xact as committed or aborted */ AtEOXact_PgStat(isCommit); /* * And now we can clean up our mess. */ RemoveTwoPhaseFile(xid, true); RemoveGXact(gxact); pfree(buf); }