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