1 /*-------------------------------------------------------------------------
4 * postgres OID & XID variables support routines
6 * Copyright (c) 2000-2005, PostgreSQL Global Development Group
9 * $PostgreSQL: pgsql/src/backend/access/transam/varsup.c,v 1.60 2005/01/01 05:43:06 momjian Exp $
11 *-------------------------------------------------------------------------
16 #include "access/clog.h"
17 #include "access/subtrans.h"
18 #include "access/transam.h"
19 #include "storage/ipc.h"
20 #include "storage/proc.h"
23 /* Number of OIDs to prefetch (preallocate) per XLOG write */
24 #define VAR_OID_PREFETCH 8192
26 /* pointer to "variable cache" in shared memory (set up by shmem.c) */
27 VariableCache ShmemVariableCache = NULL;
31 * Allocate the next XID for my new transaction.
34 GetNewTransactionId(bool isSubXact)
39 * During bootstrap initialization, we return the special bootstrap
43 return BootstrapTransactionId;
45 LWLockAcquire(XidGenLock, LW_EXCLUSIVE);
47 xid = ShmemVariableCache->nextXid;
50 * If we are allocating the first XID of a new page of the commit log,
51 * zero out that commit-log page before returning. We must do this
52 * while holding XidGenLock, else another xact could acquire and
53 * commit a later XID before we zero the page. Fortunately, a page of
54 * the commit log holds 32K or more transactions, so we don't have to
57 * Extend pg_subtrans too.
63 * Now advance the nextXid counter. This must not happen until after
64 * we have successfully completed ExtendCLOG() --- if that routine
65 * fails, we want the next incoming transaction to try it again. We
66 * cannot assign more XIDs until there is CLOG space for them.
68 TransactionIdAdvance(ShmemVariableCache->nextXid);
71 * We must store the new XID into the shared PGPROC array before
72 * releasing XidGenLock. This ensures that when GetSnapshotData calls
73 * ReadNewTransactionId, all active XIDs before the returned value of
74 * nextXid are already present in PGPROC. Else we have a race
77 * XXX by storing xid into MyProc without acquiring SInvalLock, we are
78 * relying on fetch/store of an xid to be atomic, else other backends
79 * might see a partially-set xid here. But holding both locks at once
80 * would be a nasty concurrency hit (and in fact could cause a
81 * deadlock against GetSnapshotData). So for now, assume atomicity.
82 * Note that readers of PGPROC xid field should be careful to fetch
83 * the value only once, rather than assume they can read it multiple
84 * times and get the same answer each time.
86 * The same comments apply to the subxact xid count and overflow fields.
88 * A solution to the atomic-store problem would be to give each PGPROC
89 * its own spinlock used only for fetching/storing that PGPROC's xid
90 * and related fields. (SInvalLock would then mean primarily that
91 * PGPROCs couldn't be added/removed while holding the lock.)
93 * If there's no room to fit a subtransaction XID into PGPROC, set the
94 * cache-overflowed flag instead. This forces readers to look in
95 * pg_subtrans to map subtransaction XIDs up to top-level XIDs. There
96 * is a race-condition window, in that the new XID will not appear as
97 * running until its parent link has been placed into pg_subtrans.
98 * However, that will happen before anyone could possibly have a
99 * reason to inquire about the status of the XID, so it seems OK.
100 * (Snapshots taken during this window *will* include the parent XID,
101 * so they will deliver the correct answer later on when someone does
102 * have a reason to inquire.)
110 if (MyProc->subxids.nxids < PGPROC_MAX_CACHED_SUBXIDS)
112 MyProc->subxids.xids[MyProc->subxids.nxids] = xid;
113 MyProc->subxids.nxids++;
116 MyProc->subxids.overflowed = true;
120 LWLockRelease(XidGenLock);
126 * Read nextXid but don't allocate it.
129 ReadNewTransactionId(void)
133 LWLockAcquire(XidGenLock, LW_SHARED);
134 xid = ShmemVariableCache->nextXid;
135 LWLockRelease(XidGenLock);
140 /* ----------------------------------------------------------------
141 * object id generation support
142 * ----------------------------------------------------------------
145 static Oid lastSeenOid = InvalidOid;
152 LWLockAcquire(OidGenLock, LW_EXCLUSIVE);
155 * Check for wraparound of the OID counter. We *must* not return 0
156 * (InvalidOid); and as long as we have to check that, it seems a good
157 * idea to skip over everything below BootstrapObjectIdData too. (This
158 * basically just reduces the odds of OID collision right after a wrap
159 * occurs.) Note we are relying on unsigned comparison here.
161 if (ShmemVariableCache->nextOid < ((Oid) BootstrapObjectIdData))
163 ShmemVariableCache->nextOid = BootstrapObjectIdData;
164 ShmemVariableCache->oidCount = 0;
167 /* If we run out of logged for use oids then we must log more */
168 if (ShmemVariableCache->oidCount == 0)
170 XLogPutNextOid(ShmemVariableCache->nextOid + VAR_OID_PREFETCH);
171 ShmemVariableCache->oidCount = VAR_OID_PREFETCH;
174 result = ShmemVariableCache->nextOid;
176 (ShmemVariableCache->nextOid)++;
177 (ShmemVariableCache->oidCount)--;
179 LWLockRelease(OidGenLock);
181 lastSeenOid = result;
187 CheckMaxObjectId(Oid assigned_oid)
189 if (lastSeenOid != InvalidOid && assigned_oid < lastSeenOid)
192 LWLockAcquire(OidGenLock, LW_EXCLUSIVE);
194 if (assigned_oid < ShmemVariableCache->nextOid)
196 lastSeenOid = ShmemVariableCache->nextOid - 1;
197 LWLockRelease(OidGenLock);
201 /* If we are in the logged oid range, just bump nextOid up */
202 if (assigned_oid <= ShmemVariableCache->nextOid +
203 ShmemVariableCache->oidCount - 1)
205 ShmemVariableCache->oidCount -=
206 assigned_oid - ShmemVariableCache->nextOid + 1;
207 ShmemVariableCache->nextOid = assigned_oid + 1;
208 LWLockRelease(OidGenLock);
213 * We have exceeded the logged oid range. We should lock the database
214 * and kill all other backends but we are loading oid's that we can
215 * not guarantee are unique anyway, so we must rely on the user.
218 XLogPutNextOid(assigned_oid + VAR_OID_PREFETCH);
219 ShmemVariableCache->nextOid = assigned_oid + 1;
220 ShmemVariableCache->oidCount = VAR_OID_PREFETCH - 1;
222 LWLockRelease(OidGenLock);