Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * varsup.c
4 : * postgres OID & XID variables support routines
5 : *
6 : * Copyright (c) 2000-2026, PostgreSQL Global Development Group
7 : *
8 : * IDENTIFICATION
9 : * src/backend/access/transam/varsup.c
10 : *
11 : *-------------------------------------------------------------------------
12 : */
13 :
14 : #include "postgres.h"
15 :
16 : #include "access/clog.h"
17 : #include "access/commit_ts.h"
18 : #include "access/subtrans.h"
19 : #include "access/transam.h"
20 : #include "access/xact.h"
21 : #include "access/xlogutils.h"
22 : #include "miscadmin.h"
23 : #include "postmaster/autovacuum.h"
24 : #include "storage/pmsignal.h"
25 : #include "storage/proc.h"
26 : #include "storage/subsystems.h"
27 : #include "utils/lsyscache.h"
28 : #include "utils/syscache.h"
29 :
30 :
31 : /* Number of OIDs to prefetch (preallocate) per XLOG write */
32 : #define VAR_OID_PREFETCH 8192
33 :
34 : static void VarsupShmemRequest(void *arg);
35 :
36 : /* pointer to variables struct in shared memory */
37 : TransamVariablesData *TransamVariables = NULL;
38 :
39 : const ShmemCallbacks VarsupShmemCallbacks = {
40 : .request_fn = VarsupShmemRequest,
41 : };
42 :
43 : /*
44 : * Request shared memory for TransamVariables.
45 : */
46 : static void
47 1238 : VarsupShmemRequest(void *arg)
48 : {
49 1238 : ShmemRequestStruct(.name = "TransamVariables",
50 : .size = sizeof(TransamVariablesData),
51 : .ptr = (void **) &TransamVariables,
52 : );
53 1238 : }
54 :
55 : /*
56 : * Allocate the next FullTransactionId for a new transaction or
57 : * subtransaction.
58 : *
59 : * The new XID is also stored into MyProc->xid/ProcGlobal->xids[] before
60 : * returning.
61 : *
62 : * Note: when this is called, we are actually already inside a valid
63 : * transaction, since XIDs are now not allocated until the transaction
64 : * does something. So it is safe to do a database lookup if we want to
65 : * issue a warning about XID wrap.
66 : */
67 : FullTransactionId
68 24542299 : GetNewTransactionId(bool isSubXact)
69 : {
70 : FullTransactionId full_xid;
71 : TransactionId xid;
72 :
73 : /*
74 : * Workers synchronize transaction state at the beginning of each parallel
75 : * operation, so we can't account for new XIDs after that point.
76 : */
77 24542299 : if (IsInParallelMode())
78 0 : elog(ERROR, "cannot assign TransactionIds during a parallel operation");
79 :
80 : /*
81 : * During bootstrap initialization, we return the special bootstrap
82 : * transaction id.
83 : */
84 24542299 : if (IsBootstrapProcessingMode())
85 : {
86 : Assert(!isSubXact);
87 57 : MyProc->xid = BootstrapTransactionId;
88 57 : ProcGlobal->xids[MyProc->pgxactoff] = BootstrapTransactionId;
89 57 : return FullTransactionIdFromEpochAndXid(0, BootstrapTransactionId);
90 : }
91 :
92 : /* safety check, we should never get this far in a HS standby */
93 24542242 : if (RecoveryInProgress())
94 0 : elog(ERROR, "cannot assign TransactionIds during recovery");
95 :
96 24542242 : LWLockAcquire(XidGenLock, LW_EXCLUSIVE);
97 :
98 24542242 : full_xid = TransamVariables->nextXid;
99 24542242 : xid = XidFromFullTransactionId(full_xid);
100 :
101 : /*----------
102 : * Check to see if it's safe to assign another XID. This protects against
103 : * catastrophic data loss due to XID wraparound. The basic rules are:
104 : *
105 : * If we're past xidVacLimit, start trying to force autovacuum cycles.
106 : * If we're past xidWarnLimit, start issuing warnings.
107 : * If we're past xidStopLimit, refuse to execute transactions, unless
108 : * we are running in single-user mode (which gives an escape hatch
109 : * to the DBA who somehow got past the earlier defenses).
110 : *
111 : * Note that this coding also appears in GetNewMultiXactId.
112 : *----------
113 : */
114 24542242 : if (TransactionIdFollowsOrEquals(xid, TransamVariables->xidVacLimit))
115 : {
116 : /*
117 : * For safety's sake, we release XidGenLock while sending signals,
118 : * warnings, etc. This is not so much because we care about
119 : * preserving concurrency in this situation, as to avoid any
120 : * possibility of deadlock while doing get_database_name(). First,
121 : * copy all the shared values we'll need in this path.
122 : */
123 14411134 : TransactionId xidWarnLimit = TransamVariables->xidWarnLimit;
124 14411134 : TransactionId xidStopLimit = TransamVariables->xidStopLimit;
125 14411134 : TransactionId xidWrapLimit = TransamVariables->xidWrapLimit;
126 14411134 : Oid oldest_datoid = TransamVariables->oldestXidDB;
127 :
128 14411134 : LWLockRelease(XidGenLock);
129 :
130 : /*
131 : * To avoid swamping the postmaster with signals, we issue the autovac
132 : * request only once per 64K transaction starts. This still gives
133 : * plenty of chances before we get into real trouble.
134 : */
135 14411134 : if (IsUnderPostmaster && (xid % 65536) == 0)
136 128491 : SendPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER);
137 :
138 28822268 : if (IsUnderPostmaster &&
139 14411134 : TransactionIdFollowsOrEquals(xid, xidStopLimit))
140 : {
141 7 : char *oldest_datname = get_database_name(oldest_datoid);
142 :
143 : /* complain even if that DB has disappeared */
144 7 : if (oldest_datname)
145 7 : ereport(ERROR,
146 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
147 : errmsg("database is not accepting commands that assign new transaction IDs to avoid wraparound data loss in database \"%s\"",
148 : oldest_datname),
149 : errhint("Execute a database-wide VACUUM in that database.\n"
150 : "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
151 : else
152 0 : ereport(ERROR,
153 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
154 : errmsg("database is not accepting commands that assign new transaction IDs to avoid wraparound data loss in database with OID %u",
155 : oldest_datoid),
156 : errhint("Execute a database-wide VACUUM in that database.\n"
157 : "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
158 : }
159 14411127 : else if (TransactionIdFollowsOrEquals(xid, xidWarnLimit))
160 : {
161 167753 : char *oldest_datname = get_database_name(oldest_datoid);
162 :
163 : /* complain even if that DB has disappeared */
164 167753 : if (oldest_datname)
165 167753 : ereport(WARNING,
166 : (errmsg("database \"%s\" must be vacuumed within %u transactions",
167 : oldest_datname,
168 : xidWrapLimit - xid),
169 : errdetail("Approximately %.2f%% of transaction IDs are available for use.",
170 : (double) (xidWrapLimit - xid) / (MaxTransactionId / 2) * 100),
171 : errhint("To avoid transaction ID assignment failures, execute a database-wide VACUUM in that database.\n"
172 : "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
173 : else
174 0 : ereport(WARNING,
175 : (errmsg("database with OID %u must be vacuumed within %u transactions",
176 : oldest_datoid,
177 : xidWrapLimit - xid),
178 : errdetail("Approximately %.2f%% of transaction IDs are available for use.",
179 : (double) (xidWrapLimit - xid) / (MaxTransactionId / 2) * 100),
180 : errhint("To avoid XID assignment failures, execute a database-wide VACUUM in that database.\n"
181 : "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
182 : }
183 :
184 : /* Re-acquire lock and start over */
185 14411127 : LWLockAcquire(XidGenLock, LW_EXCLUSIVE);
186 14411127 : full_xid = TransamVariables->nextXid;
187 14411127 : xid = XidFromFullTransactionId(full_xid);
188 : }
189 :
190 : /*
191 : * If we are allocating the first XID of a new page of the commit log,
192 : * zero out that commit-log page before returning. We must do this while
193 : * holding XidGenLock, else another xact could acquire and commit a later
194 : * XID before we zero the page. Fortunately, a page of the commit log
195 : * holds 32K or more transactions, so we don't have to do this very often.
196 : *
197 : * Extend pg_subtrans and pg_commit_ts too.
198 : */
199 24542235 : ExtendCLOG(xid);
200 24542235 : ExtendCommitTs(xid);
201 24542235 : ExtendSUBTRANS(xid);
202 :
203 : /*
204 : * Now advance the nextXid counter. This must not happen until after we
205 : * have successfully completed ExtendCLOG() --- if that routine fails, we
206 : * want the next incoming transaction to try it again. We cannot assign
207 : * more XIDs until there is CLOG space for them.
208 : */
209 24542235 : FullTransactionIdAdvance(&TransamVariables->nextXid);
210 :
211 : /*
212 : * We must store the new XID into the shared ProcArray before releasing
213 : * XidGenLock. This ensures that every active XID older than
214 : * latestCompletedXid is present in the ProcArray, which is essential for
215 : * correct OldestXmin tracking; see src/backend/access/transam/README.
216 : *
217 : * Note that readers of ProcGlobal->xids/PGPROC->xid should be careful to
218 : * fetch the value for each proc only once, rather than assume they can
219 : * read a value multiple times and get the same answer each time. Note we
220 : * are assuming that TransactionId and int fetch/store are atomic.
221 : *
222 : * The same comments apply to the subxact xid count and overflow fields.
223 : *
224 : * Use of a write barrier prevents dangerous code rearrangement in this
225 : * function; other backends could otherwise e.g. be examining my subxids
226 : * info concurrently, and we don't want them to see an invalid
227 : * intermediate state, such as an incremented nxids before the array entry
228 : * is filled.
229 : *
230 : * Other processes that read nxids should do so before reading xids
231 : * elements with a pg_read_barrier() in between, so that they can be sure
232 : * not to read an uninitialized array element; see
233 : * src/backend/storage/lmgr/README.barrier.
234 : *
235 : * If there's no room to fit a subtransaction XID into PGPROC, set the
236 : * cache-overflowed flag instead. This forces readers to look in
237 : * pg_subtrans to map subtransaction XIDs up to top-level XIDs. There is a
238 : * race-condition window, in that the new XID will not appear as running
239 : * until its parent link has been placed into pg_subtrans. However, that
240 : * will happen before anyone could possibly have a reason to inquire about
241 : * the status of the XID, so it seems OK. (Snapshots taken during this
242 : * window *will* include the parent XID, so they will deliver the correct
243 : * answer later on when someone does have a reason to inquire.)
244 : */
245 24542235 : if (!isSubXact)
246 : {
247 : Assert(ProcGlobal->subxidStates[MyProc->pgxactoff].count == 0);
248 : Assert(!ProcGlobal->subxidStates[MyProc->pgxactoff].overflowed);
249 : Assert(MyProc->subxidStatus.count == 0);
250 : Assert(!MyProc->subxidStatus.overflowed);
251 :
252 : /* LWLockRelease acts as barrier */
253 165431 : MyProc->xid = xid;
254 165431 : ProcGlobal->xids[MyProc->pgxactoff] = xid;
255 : }
256 : else
257 : {
258 24376804 : XidCacheStatus *substat = &ProcGlobal->subxidStates[MyProc->pgxactoff];
259 24376804 : int nxids = MyProc->subxidStatus.count;
260 :
261 : Assert(substat->count == MyProc->subxidStatus.count);
262 : Assert(substat->overflowed == MyProc->subxidStatus.overflowed);
263 :
264 24376804 : if (nxids < PGPROC_MAX_CACHED_SUBXIDS)
265 : {
266 9372 : MyProc->subxids.xids[nxids] = xid;
267 9372 : pg_write_barrier();
268 9372 : MyProc->subxidStatus.count = substat->count = nxids + 1;
269 : }
270 : else
271 24367432 : MyProc->subxidStatus.overflowed = substat->overflowed = true;
272 : }
273 :
274 24542235 : LWLockRelease(XidGenLock);
275 :
276 24542235 : return full_xid;
277 : }
278 :
279 : /*
280 : * Read nextXid but don't allocate it.
281 : */
282 : FullTransactionId
283 344929 : ReadNextFullTransactionId(void)
284 : {
285 : FullTransactionId fullXid;
286 :
287 344929 : LWLockAcquire(XidGenLock, LW_SHARED);
288 344929 : fullXid = TransamVariables->nextXid;
289 344929 : LWLockRelease(XidGenLock);
290 :
291 344929 : return fullXid;
292 : }
293 :
294 : /*
295 : * Advance nextXid to the value after a given xid. The epoch is inferred.
296 : * This must only be called during recovery or from two-phase start-up code.
297 : */
298 : void
299 2993996 : AdvanceNextFullTransactionIdPastXid(TransactionId xid)
300 : {
301 : FullTransactionId newNextFullXid;
302 : TransactionId next_xid;
303 : uint32 epoch;
304 :
305 : /*
306 : * It is safe to read nextXid without a lock, because this is only called
307 : * from the startup process or single-process mode, meaning that no other
308 : * process can modify it.
309 : */
310 : Assert(AmStartupProcess() || !IsUnderPostmaster);
311 :
312 : /* Fast return if this isn't an xid high enough to move the needle. */
313 2993996 : next_xid = XidFromFullTransactionId(TransamVariables->nextXid);
314 2993996 : if (!TransactionIdFollowsOrEquals(xid, next_xid))
315 2967421 : return;
316 :
317 : /*
318 : * Compute the FullTransactionId that comes after the given xid. To do
319 : * this, we preserve the existing epoch, but detect when we've wrapped
320 : * into a new epoch. This is necessary because WAL records and 2PC state
321 : * currently contain 32 bit xids. The wrap logic is safe in those cases
322 : * because the span of active xids cannot exceed one epoch at any given
323 : * point in the WAL stream.
324 : */
325 26575 : TransactionIdAdvance(xid);
326 26575 : epoch = EpochFromFullTransactionId(TransamVariables->nextXid);
327 26575 : if (unlikely(xid < next_xid))
328 0 : ++epoch;
329 26575 : newNextFullXid = FullTransactionIdFromEpochAndXid(epoch, xid);
330 :
331 : /*
332 : * We still need to take a lock to modify the value when there are
333 : * concurrent readers.
334 : */
335 26575 : LWLockAcquire(XidGenLock, LW_EXCLUSIVE);
336 26575 : TransamVariables->nextXid = newNextFullXid;
337 26575 : LWLockRelease(XidGenLock);
338 : }
339 :
340 : /*
341 : * Advance the cluster-wide value for the oldest valid clog entry.
342 : *
343 : * We must acquire XactTruncationLock to advance the oldestClogXid. It's not
344 : * necessary to hold the lock during the actual clog truncation, only when we
345 : * advance the limit, as code looking up arbitrary xids is required to hold
346 : * XactTruncationLock from when it tests oldestClogXid through to when it
347 : * completes the clog lookup.
348 : */
349 : void
350 1231 : AdvanceOldestClogXid(TransactionId oldest_datfrozenxid)
351 : {
352 1231 : LWLockAcquire(XactTruncationLock, LW_EXCLUSIVE);
353 1231 : if (TransactionIdPrecedes(TransamVariables->oldestClogXid,
354 : oldest_datfrozenxid))
355 : {
356 1174 : TransamVariables->oldestClogXid = oldest_datfrozenxid;
357 : }
358 1231 : LWLockRelease(XactTruncationLock);
359 1231 : }
360 :
361 : /*
362 : * Determine the last safe XID to allocate using the currently oldest
363 : * datfrozenxid (ie, the oldest XID that might exist in any database
364 : * of our cluster), and the OID of the (or a) database with that value.
365 : */
366 : void
367 2237 : SetTransactionIdLimit(TransactionId oldest_datfrozenxid, Oid oldest_datoid)
368 : {
369 : TransactionId xidVacLimit;
370 : TransactionId xidWarnLimit;
371 : TransactionId xidStopLimit;
372 : TransactionId xidWrapLimit;
373 : TransactionId curXid;
374 :
375 : Assert(TransactionIdIsNormal(oldest_datfrozenxid));
376 :
377 : /*
378 : * The place where we actually get into deep trouble is halfway around
379 : * from the oldest potentially-existing XID. (This calculation is
380 : * probably off by one or two counts, because the special XIDs reduce the
381 : * size of the loop a little bit. But we throw in plenty of slop below,
382 : * so it doesn't matter.)
383 : */
384 2237 : xidWrapLimit = oldest_datfrozenxid + (MaxTransactionId >> 1);
385 2237 : if (xidWrapLimit < FirstNormalTransactionId)
386 0 : xidWrapLimit += FirstNormalTransactionId;
387 :
388 : /*
389 : * We'll refuse to continue assigning XIDs in interactive mode once we get
390 : * within 3M transactions of data loss. This leaves lots of room for the
391 : * DBA to fool around fixing things in a standalone backend, while not
392 : * being significant compared to total XID space. (VACUUM requires an XID
393 : * if it truncates at wal_level!=minimal. "VACUUM (ANALYZE)", which a DBA
394 : * might do by reflex, assigns an XID. Hence, we had better be sure
395 : * there's lots of XIDs left...) Also, at default BLCKSZ, this leaves two
396 : * completely-idle segments. In the event of edge-case bugs involving
397 : * page or segment arithmetic, idle segments render the bugs unreachable
398 : * outside of single-user mode.
399 : */
400 2237 : xidStopLimit = xidWrapLimit - 3000000;
401 2237 : if (xidStopLimit < FirstNormalTransactionId)
402 0 : xidStopLimit -= FirstNormalTransactionId;
403 :
404 : /*
405 : * We'll start complaining loudly when we get within 100M transactions of
406 : * data loss. This is kind of arbitrary, but if you let your gas gauge
407 : * get down to 5% of full, would you be looking for the next gas station?
408 : * We need to be fairly liberal about this number because there are lots
409 : * of scenarios where most transactions are done by automatic clients that
410 : * won't pay attention to warnings. (No, we're not gonna make this
411 : * configurable. If you know enough to configure it, you know enough to
412 : * not get in this kind of trouble in the first place.)
413 : */
414 2237 : xidWarnLimit = xidWrapLimit - 100000000;
415 2237 : if (xidWarnLimit < FirstNormalTransactionId)
416 0 : xidWarnLimit -= FirstNormalTransactionId;
417 :
418 : /*
419 : * We'll start trying to force autovacuums when oldest_datfrozenxid gets
420 : * to be more than autovacuum_freeze_max_age transactions old.
421 : *
422 : * Note: guc.c ensures that autovacuum_freeze_max_age is in a sane range,
423 : * so that xidVacLimit will be well before xidWarnLimit.
424 : *
425 : * Note: autovacuum_freeze_max_age is a PGC_POSTMASTER parameter so that
426 : * we don't have to worry about dealing with on-the-fly changes in its
427 : * value. It doesn't look practical to update shared state from a GUC
428 : * assign hook (too many processes would try to execute the hook,
429 : * resulting in race conditions as well as crashes of those not connected
430 : * to shared memory). Perhaps this can be improved someday. See also
431 : * SetMultiXactIdLimit.
432 : */
433 2237 : xidVacLimit = oldest_datfrozenxid + autovacuum_freeze_max_age;
434 2237 : if (xidVacLimit < FirstNormalTransactionId)
435 0 : xidVacLimit += FirstNormalTransactionId;
436 :
437 : /* Grab lock for just long enough to set the new limit values */
438 2237 : LWLockAcquire(XidGenLock, LW_EXCLUSIVE);
439 2237 : TransamVariables->oldestXid = oldest_datfrozenxid;
440 2237 : TransamVariables->xidVacLimit = xidVacLimit;
441 2237 : TransamVariables->xidWarnLimit = xidWarnLimit;
442 2237 : TransamVariables->xidStopLimit = xidStopLimit;
443 2237 : TransamVariables->xidWrapLimit = xidWrapLimit;
444 2237 : TransamVariables->oldestXidDB = oldest_datoid;
445 2237 : curXid = XidFromFullTransactionId(TransamVariables->nextXid);
446 2237 : LWLockRelease(XidGenLock);
447 :
448 : /* Log the info */
449 2237 : ereport(DEBUG1,
450 : (errmsg_internal("transaction ID wrap limit is %u, limited by database with OID %u",
451 : xidWrapLimit, oldest_datoid)));
452 :
453 : /*
454 : * If past the autovacuum force point, immediately signal an autovac
455 : * request. The reason for this is that autovac only processes one
456 : * database per invocation. Once it's finished cleaning up the oldest
457 : * database, it'll call here, and we'll signal the postmaster to start
458 : * another iteration immediately if there are still any old databases.
459 : */
460 2237 : if (TransactionIdFollowsOrEquals(curXid, xidVacLimit) &&
461 849 : IsUnderPostmaster && !InRecovery)
462 849 : SendPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER);
463 :
464 : /* Give an immediate warning if past the wrap warn point */
465 2237 : if (TransactionIdFollowsOrEquals(curXid, xidWarnLimit) && !InRecovery)
466 : {
467 : char *oldest_datname;
468 :
469 : /*
470 : * We can be called when not inside a transaction, for example during
471 : * StartupXLOG(). In such a case we cannot do database access, so we
472 : * must just report the oldest DB's OID.
473 : *
474 : * Note: it's also possible that get_database_name fails and returns
475 : * NULL, for example because the database just got dropped. We'll
476 : * still warn, even though the warning might now be unnecessary.
477 : */
478 36 : if (IsTransactionState())
479 36 : oldest_datname = get_database_name(oldest_datoid);
480 : else
481 0 : oldest_datname = NULL;
482 :
483 36 : if (oldest_datname)
484 36 : ereport(WARNING,
485 : (errmsg("database \"%s\" must be vacuumed within %u transactions",
486 : oldest_datname,
487 : xidWrapLimit - curXid),
488 : errdetail("Approximately %.2f%% of transaction IDs are available for use.",
489 : (double) (xidWrapLimit - curXid) / (MaxTransactionId / 2) * 100),
490 : errhint("To avoid XID assignment failures, execute a database-wide VACUUM in that database.\n"
491 : "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
492 : else
493 0 : ereport(WARNING,
494 : (errmsg("database with OID %u must be vacuumed within %u transactions",
495 : oldest_datoid,
496 : xidWrapLimit - curXid),
497 : errdetail("Approximately %.2f%% of transaction IDs are available for use.",
498 : (double) (xidWrapLimit - curXid) / (MaxTransactionId / 2) * 100),
499 : errhint("To avoid XID assignment failures, execute a database-wide VACUUM in that database.\n"
500 : "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
501 : }
502 2237 : }
503 :
504 :
505 : /*
506 : * ForceTransactionIdLimitUpdate -- does the XID wrap-limit data need updating?
507 : *
508 : * We primarily check whether oldestXidDB is valid. The cases we have in
509 : * mind are that that database was dropped, or the field was reset to zero
510 : * by pg_resetwal. In either case we should force recalculation of the
511 : * wrap limit. Also do it if oldestXid is old enough to be forcing
512 : * autovacuums or other actions; this ensures we update our state as soon
513 : * as possible once extra overhead is being incurred.
514 : */
515 : bool
516 2116 : ForceTransactionIdLimitUpdate(void)
517 : {
518 : TransactionId nextXid;
519 : TransactionId xidVacLimit;
520 : TransactionId oldestXid;
521 : Oid oldestXidDB;
522 :
523 : /* Locking is probably not really necessary, but let's be careful */
524 2116 : LWLockAcquire(XidGenLock, LW_SHARED);
525 2116 : nextXid = XidFromFullTransactionId(TransamVariables->nextXid);
526 2116 : xidVacLimit = TransamVariables->xidVacLimit;
527 2116 : oldestXid = TransamVariables->oldestXid;
528 2116 : oldestXidDB = TransamVariables->oldestXidDB;
529 2116 : LWLockRelease(XidGenLock);
530 :
531 2116 : if (!TransactionIdIsNormal(oldestXid))
532 0 : return true; /* shouldn't happen, but just in case */
533 2116 : if (!TransactionIdIsValid(xidVacLimit))
534 0 : return true; /* this shouldn't happen anymore either */
535 2116 : if (TransactionIdFollowsOrEquals(nextXid, xidVacLimit))
536 694 : return true; /* past xidVacLimit, don't delay updating */
537 1422 : if (!SearchSysCacheExists1(DATABASEOID, ObjectIdGetDatum(oldestXidDB)))
538 0 : return true; /* could happen, per comments above */
539 1422 : return false;
540 : }
541 :
542 :
543 : /*
544 : * GetNewObjectId -- allocate a new OID
545 : *
546 : * OIDs are generated by a cluster-wide counter. Since they are only 32 bits
547 : * wide, counter wraparound will occur eventually, and therefore it is unwise
548 : * to assume they are unique unless precautions are taken to make them so.
549 : * Hence, this routine should generally not be used directly. The only direct
550 : * callers should be GetNewOidWithIndex() and GetNewRelFileNumber() in
551 : * catalog/catalog.c.
552 : */
553 : Oid
554 575362 : GetNewObjectId(void)
555 : {
556 : Oid result;
557 :
558 : /* safety check, we should never get this far in a HS standby */
559 575362 : if (RecoveryInProgress())
560 0 : elog(ERROR, "cannot assign OIDs during recovery");
561 :
562 575362 : LWLockAcquire(OidGenLock, LW_EXCLUSIVE);
563 :
564 : /*
565 : * Check for wraparound of the OID counter. We *must* not return 0
566 : * (InvalidOid), and in normal operation we mustn't return anything below
567 : * FirstNormalObjectId since that range is reserved for initdb (see
568 : * IsCatalogRelationOid()). Note we are relying on unsigned comparison.
569 : *
570 : * During initdb, we start the OID generator at FirstGenbkiObjectId, so we
571 : * only wrap if before that point when in bootstrap or standalone mode.
572 : * The first time through this routine after normal postmaster start, the
573 : * counter will be forced up to FirstNormalObjectId. This mechanism
574 : * leaves the OIDs between FirstGenbkiObjectId and FirstNormalObjectId
575 : * available for automatic assignment during initdb, while ensuring they
576 : * will never conflict with user-assigned OIDs.
577 : */
578 575362 : if (TransamVariables->nextOid < ((Oid) FirstNormalObjectId))
579 : {
580 115991 : if (IsPostmasterEnvironment)
581 : {
582 : /* wraparound, or first post-initdb assignment, in normal mode */
583 406 : TransamVariables->nextOid = FirstNormalObjectId;
584 406 : TransamVariables->oidCount = 0;
585 : }
586 : else
587 : {
588 : /* we may be bootstrapping, so don't enforce the full range */
589 115585 : if (TransamVariables->nextOid < ((Oid) FirstGenbkiObjectId))
590 : {
591 : /* wraparound in standalone mode (unlikely but possible) */
592 0 : TransamVariables->nextOid = FirstNormalObjectId;
593 0 : TransamVariables->oidCount = 0;
594 : }
595 : }
596 : }
597 :
598 : /* If we run out of logged for use oids then we must log more */
599 575362 : if (TransamVariables->oidCount == 0)
600 : {
601 692 : XLogPutNextOid(TransamVariables->nextOid + VAR_OID_PREFETCH);
602 692 : TransamVariables->oidCount = VAR_OID_PREFETCH;
603 : }
604 :
605 575362 : result = TransamVariables->nextOid;
606 :
607 575362 : (TransamVariables->nextOid)++;
608 575362 : (TransamVariables->oidCount)--;
609 :
610 575362 : LWLockRelease(OidGenLock);
611 :
612 575362 : return result;
613 : }
614 :
615 : /*
616 : * SetNextObjectId
617 : *
618 : * This may only be called during initdb; it advances the OID counter
619 : * to the specified value.
620 : */
621 : static void
622 55 : SetNextObjectId(Oid nextOid)
623 : {
624 : /* Safety check, this is only allowable during initdb */
625 55 : if (IsPostmasterEnvironment)
626 0 : elog(ERROR, "cannot advance OID counter anymore");
627 :
628 : /* Taking the lock is, therefore, just pro forma; but do it anyway */
629 55 : LWLockAcquire(OidGenLock, LW_EXCLUSIVE);
630 :
631 55 : if (TransamVariables->nextOid > nextOid)
632 0 : elog(ERROR, "too late to advance OID counter to %u, it is now %u",
633 : nextOid, TransamVariables->nextOid);
634 :
635 55 : TransamVariables->nextOid = nextOid;
636 55 : TransamVariables->oidCount = 0;
637 :
638 55 : LWLockRelease(OidGenLock);
639 55 : }
640 :
641 : /*
642 : * StopGeneratingPinnedObjectIds
643 : *
644 : * This is called once during initdb to force the OID counter up to
645 : * FirstUnpinnedObjectId. This supports letting initdb's post-bootstrap
646 : * processing create some pinned objects early on. Once it's done doing
647 : * so, it calls this (via pg_stop_making_pinned_objects()) so that the
648 : * remaining objects it makes will be considered un-pinned.
649 : */
650 : void
651 55 : StopGeneratingPinnedObjectIds(void)
652 : {
653 55 : SetNextObjectId(FirstUnpinnedObjectId);
654 55 : }
655 :
656 :
657 : #ifdef USE_ASSERT_CHECKING
658 :
659 : /*
660 : * Assert that xid is between [oldestXid, nextXid], which is the range we
661 : * expect XIDs coming from tables etc to be in.
662 : *
663 : * As TransamVariables->oldestXid could change just after this call without
664 : * further precautions, and as a wrapped-around xid could again fall within
665 : * the valid range, this assertion can only detect if something is definitely
666 : * wrong, but not establish correctness.
667 : *
668 : * This intentionally does not expose a return value, to avoid code being
669 : * introduced that depends on the return value.
670 : */
671 : void
672 : AssertTransactionIdInAllowableRange(TransactionId xid)
673 : {
674 : TransactionId oldest_xid;
675 : TransactionId next_xid;
676 :
677 : Assert(TransactionIdIsValid(xid));
678 :
679 : /* we may see bootstrap / frozen */
680 : if (!TransactionIdIsNormal(xid))
681 : return;
682 :
683 : /*
684 : * We can't acquire XidGenLock, as this may be called with XidGenLock
685 : * already held (or with other locks that don't allow XidGenLock to be
686 : * nested). That's ok for our purposes though, since we already rely on
687 : * 32bit reads to be atomic. While nextXid is 64 bit, we only look at the
688 : * lower 32bit, so a skewed read doesn't hurt.
689 : *
690 : * There's no increased danger of falling outside [oldest, next] by
691 : * accessing them without a lock. xid needs to have been created with
692 : * GetNewTransactionId() in the originating session, and the locks there
693 : * pair with the memory barrier below. We do however accept xid to be <=
694 : * to next_xid, instead of just <, as xid could be from the procarray,
695 : * before we see the updated nextXid value.
696 : */
697 : pg_memory_barrier();
698 : oldest_xid = TransamVariables->oldestXid;
699 : next_xid = XidFromFullTransactionId(TransamVariables->nextXid);
700 :
701 : Assert(TransactionIdFollowsOrEquals(xid, oldest_xid) ||
702 : TransactionIdPrecedesOrEquals(xid, next_xid));
703 : }
704 : #endif
|