Branch data Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * catalog.c
4 : : * routines concerned with catalog naming conventions and other
5 : : * bits of hard-wired knowledge
6 : : *
7 : : *
8 : : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
9 : : * Portions Copyright (c) 1994, Regents of the University of California
10 : : *
11 : : *
12 : : * IDENTIFICATION
13 : : * src/backend/catalog/catalog.c
14 : : *
15 : : *-------------------------------------------------------------------------
16 : : */
17 : :
18 : : #include "postgres.h"
19 : :
20 : : #include <fcntl.h>
21 : : #include <unistd.h>
22 : :
23 : : #include "access/genam.h"
24 : : #include "access/htup_details.h"
25 : : #include "access/table.h"
26 : : #include "access/transam.h"
27 : : #include "catalog/catalog.h"
28 : : #include "catalog/namespace.h"
29 : : #include "catalog/pg_auth_members.h"
30 : : #include "catalog/pg_authid.h"
31 : : #include "catalog/pg_database.h"
32 : : #include "catalog/pg_db_role_setting.h"
33 : : #include "catalog/pg_largeobject.h"
34 : : #include "catalog/pg_namespace.h"
35 : : #include "catalog/pg_parameter_acl.h"
36 : : #include "catalog/pg_replication_origin.h"
37 : : #include "catalog/pg_seclabel.h"
38 : : #include "catalog/pg_shdepend.h"
39 : : #include "catalog/pg_shdescription.h"
40 : : #include "catalog/pg_shseclabel.h"
41 : : #include "catalog/pg_subscription.h"
42 : : #include "catalog/pg_tablespace.h"
43 : : #include "catalog/pg_type.h"
44 : : #include "miscadmin.h"
45 : : #include "utils/fmgroids.h"
46 : : #include "utils/fmgrprotos.h"
47 : : #include "utils/rel.h"
48 : : #include "utils/snapmgr.h"
49 : : #include "utils/syscache.h"
50 : :
51 : : /*
52 : : * Parameters to determine when to emit a log message in
53 : : * GetNewOidWithIndex()
54 : : */
55 : : #define GETNEWOID_LOG_THRESHOLD 1000000
56 : : #define GETNEWOID_LOG_MAX_INTERVAL 128000000
57 : :
58 : : /*
59 : : * IsSystemRelation
60 : : * True iff the relation is either a system catalog or a toast table.
61 : : * See IsCatalogRelation for the exact definition of a system catalog.
62 : : *
63 : : * We treat toast tables of user relations as "system relations" for
64 : : * protection purposes, e.g. you can't change their schemas without
65 : : * special permissions. Therefore, most uses of this function are
66 : : * checking whether allow_system_table_mods restrictions apply.
67 : : * For other purposes, consider whether you shouldn't be using
68 : : * IsCatalogRelation instead.
69 : : *
70 : : * This function does not perform any catalog accesses.
71 : : * Some callers rely on that!
72 : : */
73 : : bool
74 : 114495 : IsSystemRelation(Relation relation)
75 : : {
76 : 114495 : return IsSystemClass(RelationGetRelid(relation), relation->rd_rel);
77 : : }
78 : :
79 : : /*
80 : : * IsSystemClass
81 : : * Like the above, but takes a Form_pg_class as argument.
82 : : * Used when we do not want to open the relation and have to
83 : : * search pg_class directly.
84 : : */
85 : : bool
86 : 182244 : IsSystemClass(Oid relid, Form_pg_class reltuple)
87 : : {
88 : : /* IsCatalogRelationOid is a bit faster, so test that first */
89 [ + + ]: 345510 : return (IsCatalogRelationOid(relid) ||
90 [ + + + + ]: 345510 : IsToastClass(reltuple) ||
91 : 151940 : IsConflictLogTableClass(reltuple));
92 : : }
93 : :
94 : : /*
95 : : * IsCatalogRelation
96 : : * True iff the relation is a system catalog.
97 : : *
98 : : * By a system catalog, we mean one that is created during the bootstrap
99 : : * phase of initdb. That includes not just the catalogs per se, but
100 : : * also their indexes, and TOAST tables and indexes if any.
101 : : *
102 : : * This function does not perform any catalog accesses.
103 : : * Some callers rely on that!
104 : : */
105 : : bool
106 : 34404307 : IsCatalogRelation(Relation relation)
107 : : {
108 : 34404307 : return IsCatalogRelationOid(RelationGetRelid(relation));
109 : : }
110 : :
111 : : /*
112 : : * IsCatalogRelationOid
113 : : * True iff the relation identified by this OID is a system catalog.
114 : : *
115 : : * By a system catalog, we mean one that is created during the bootstrap
116 : : * phase of initdb. That includes not just the catalogs per se, but
117 : : * also their indexes, and TOAST tables and indexes if any.
118 : : *
119 : : * This function does not perform any catalog accesses.
120 : : * Some callers rely on that!
121 : : */
122 : : bool
123 : 60646375 : IsCatalogRelationOid(Oid relid)
124 : : {
125 : : /*
126 : : * We consider a relation to be a system catalog if it has a pinned OID.
127 : : * This includes all the defined catalogs, their indexes, and their TOAST
128 : : * tables and indexes.
129 : : *
130 : : * This rule excludes the relations in information_schema, which are not
131 : : * integral to the system and can be treated the same as user relations.
132 : : * (Since it's valid to drop and recreate information_schema, any rule
133 : : * that did not act this way would be wrong.)
134 : : *
135 : : * This test is reliable since an OID wraparound will skip this range of
136 : : * OIDs; see GetNewObjectId().
137 : : */
138 : 60646375 : return (relid < (Oid) FirstUnpinnedObjectId);
139 : : }
140 : :
141 : : /*
142 : : * IsCatalogTextUniqueIndexOid
143 : : * True iff the relation identified by this OID is a catalog UNIQUE index
144 : : * having a column of type "text".
145 : : *
146 : : * The relcache must not use these indexes. Inserting into any UNIQUE
147 : : * index compares index keys while holding BUFFER_LOCK_EXCLUSIVE.
148 : : * bttextcmp() can search the COLLOID catcache. Depending on concurrent
149 : : * invalidation traffic, catcache can reach relcache builds. A backend
150 : : * would self-deadlock on LWLocks if the relcache build read the
151 : : * exclusive-locked buffer.
152 : : *
153 : : * To avoid being itself the cause of self-deadlock, this doesn't read
154 : : * catalogs. Instead, it uses a hard-coded list with a supporting
155 : : * regression test.
156 : : */
157 : : bool
158 : 841 : IsCatalogTextUniqueIndexOid(Oid relid)
159 : : {
160 [ + + ]: 841 : switch (relid)
161 : : {
162 : 16 : case ParameterAclParnameIndexId:
163 : : case ReplicationOriginNameIndex:
164 : : case SecLabelObjectIndexId:
165 : : case SharedSecLabelObjectIndexId:
166 : 16 : return true;
167 : : }
168 : 825 : return false;
169 : : }
170 : :
171 : : /*
172 : : * IsInplaceUpdateRelation
173 : : * True iff core code performs inplace updates on the relation.
174 : : *
175 : : * This is used for assertions and for making the executor follow the
176 : : * locking protocol described at README.tuplock section "Locking to write
177 : : * inplace-updated tables". Extensions may inplace-update other heap
178 : : * tables, but concurrent SQL UPDATE on the same table may overwrite
179 : : * those modifications.
180 : : *
181 : : * The executor can assume these are not partitions or partitioned and
182 : : * have no triggers.
183 : : */
184 : : bool
185 : 268399 : IsInplaceUpdateRelation(Relation relation)
186 : : {
187 : 268399 : return IsInplaceUpdateOid(RelationGetRelid(relation));
188 : : }
189 : :
190 : : /*
191 : : * IsInplaceUpdateOid
192 : : * Like the above, but takes an OID as argument.
193 : : */
194 : : bool
195 : 268399 : IsInplaceUpdateOid(Oid relid)
196 : : {
197 [ + + + + ]: 268399 : return (relid == RelationRelationId ||
198 : : relid == DatabaseRelationId);
199 : : }
200 : :
201 : : /*
202 : : * HasVersionedAttributes
203 : : * Return whether the relation has versioned attributes.
204 : : */
205 : : bool
206 : 1141828 : HasVersionedAttributes(Relation relation)
207 : : {
208 : : Assert(RelationGetRelid(relation) != InvalidOid);
209 : :
210 : : /*
211 : : * If the relation does not have a rowtype, its attributes obviously are
212 : : * not versioned, since attribute versioning exists to protect against
213 : : * deforming composite datums incompatible with the current type
214 : : * definition.
215 : : */
216 [ + + + + : 1141828 : if (!RELKIND_HAS_ROWTYPE(relation->rd_rel->relkind))
+ + + + +
+ + + ]
217 : : {
218 : : Assert(relation->rd_rel->reltype == InvalidOid);
219 : 579835 : return false;
220 : : }
221 : :
222 : : Assert(relation->rd_rel->reltype != InvalidOid);
223 : :
224 : : /*
225 : : * Bootstrapped relations are not versioned, for two reasons:
226 : : *
227 : : * First, they can't ever change, i.e. we never need to detect mismatches.
228 : : *
229 : : * Second, to determine an attribute's version, we might need to do
230 : : * syscache lookups, which could lead to recursion dangers (this is not
231 : : * the case right now, but might again be at a later point). As there's no
232 : : * reason to version, it's not worth analyzing further.
233 : : */
234 [ + + ]: 561993 : if (RelationGetRelid(relation) < FirstUnpinnedObjectId)
235 : 212093 : return false;
236 : :
237 : : Assert(!IsBootstrapProcessingMode());
238 : :
239 : 349900 : return true;
240 : : }
241 : :
242 : : /*
243 : : * IsToastRelation
244 : : * True iff relation is a TOAST support relation (or index).
245 : : *
246 : : * Does not perform any catalog accesses.
247 : : */
248 : : bool
249 : 3480934 : IsToastRelation(Relation relation)
250 : : {
251 : : /*
252 : : * What we actually check is whether the relation belongs to a pg_toast
253 : : * namespace. This should be equivalent because of restrictions that are
254 : : * enforced elsewhere against creating user relations in, or moving
255 : : * relations into/out of, a pg_toast namespace. Notice also that this
256 : : * will not say "true" for toast tables belonging to other sessions' temp
257 : : * tables; we expect that other mechanisms will prevent access to those.
258 : : */
259 : 3480934 : return IsToastNamespace(RelationGetNamespace(relation));
260 : : }
261 : :
262 : : /*
263 : : * IsToastClass
264 : : * Like the above, but takes a Form_pg_class as argument.
265 : : * Used when we do not want to open the relation and have to
266 : : * search pg_class directly.
267 : : */
268 : : bool
269 : 163266 : IsToastClass(Form_pg_class reltuple)
270 : : {
271 : 163266 : Oid relnamespace = reltuple->relnamespace;
272 : :
273 : 163266 : return IsToastNamespace(relnamespace);
274 : : }
275 : :
276 : : /*
277 : : * IsConflictLogTableClass
278 : : * True iff pg_class tuple represents a Conflict Log Table.
279 : : *
280 : : * Does not perform any catalog accesses.
281 : : */
282 : : bool
283 : 559881 : IsConflictLogTableClass(Form_pg_class reltuple)
284 : : {
285 : 559881 : Oid relnamespace = reltuple->relnamespace;
286 : :
287 : 559881 : return IsConflictLogTableNamespace(relnamespace);
288 : : }
289 : :
290 : : /*
291 : : * IsCatalogNamespace
292 : : * True iff namespace is pg_catalog.
293 : : *
294 : : * Does not perform any catalog accesses.
295 : : *
296 : : * NOTE: the reason this isn't a macro is to avoid having to include
297 : : * catalog/pg_namespace.h in a lot of places.
298 : : */
299 : : bool
300 : 143780 : IsCatalogNamespace(Oid namespaceId)
301 : : {
302 : 143780 : return namespaceId == PG_CATALOG_NAMESPACE;
303 : : }
304 : :
305 : : /*
306 : : * IsToastNamespace
307 : : * True iff namespace is pg_toast or my temporary-toast-table namespace.
308 : : *
309 : : * Does not perform any catalog accesses.
310 : : *
311 : : * Note: this will return false for temporary-toast-table namespaces belonging
312 : : * to other backends. Those are treated the same as other backends' regular
313 : : * temp table namespaces, and access is prevented where appropriate.
314 : : * If you need to check for those, you may be able to use isAnyTempNamespace,
315 : : * but beware that that does involve a catalog access.
316 : : */
317 : : bool
318 : 3706452 : IsToastNamespace(Oid namespaceId)
319 : : {
320 [ + + + + ]: 7372457 : return (namespaceId == PG_TOAST_NAMESPACE) ||
321 : 3666005 : isTempToastNamespace(namespaceId);
322 : : }
323 : :
324 : : /*
325 : : * IsConflictLogTableNamespace
326 : : * True iff namespace is pg_conflict.
327 : : *
328 : : * Does not perform any catalog accesses.
329 : : */
330 : : bool
331 : 787917 : IsConflictLogTableNamespace(Oid namespaceId)
332 : : {
333 : 787917 : return namespaceId == PG_CONFLICT_NAMESPACE;
334 : : }
335 : :
336 : : /*
337 : : * IsReservedName
338 : : * True iff name starts with the pg_ prefix.
339 : : *
340 : : * For some classes of objects, the prefix pg_ is reserved for
341 : : * system objects only. As of 8.0, this was only true for
342 : : * schema and tablespace names. With 9.6, this is also true
343 : : * for roles.
344 : : */
345 : : bool
346 : 2480 : IsReservedName(const char *name)
347 : : {
348 : : /* ugly coding for speed */
349 : 2544 : return (name[0] == 'p' &&
350 [ + + + + ]: 2494 : name[1] == 'g' &&
351 [ + + ]: 14 : name[2] == '_');
352 : : }
353 : :
354 : :
355 : : /*
356 : : * IsSharedRelation
357 : : * Given the OID of a relation, determine whether it's supposed to be
358 : : * shared across an entire database cluster.
359 : : *
360 : : * In older releases, this had to be hard-wired so that we could compute the
361 : : * locktag for a relation and lock it before examining its catalog entry.
362 : : * Since we now have MVCC catalog access, the race conditions that made that
363 : : * a hard requirement are gone, so we could look at relaxing this restriction.
364 : : * However, if we scanned the pg_class entry to find relisshared, and only
365 : : * then locked the relation, pg_class could get updated in the meantime,
366 : : * forcing us to scan the relation again, which would definitely be complex
367 : : * and might have undesirable performance consequences. Fortunately, the set
368 : : * of shared relations is fairly static, so a hand-maintained list of their
369 : : * OIDs isn't completely impractical.
370 : : */
371 : : bool
372 : 29079715 : IsSharedRelation(Oid relationId)
373 : : {
374 : : /* These are the shared catalogs (look for BKI_SHARED_RELATION) */
375 [ + + + + ]: 29079715 : if (relationId == AuthIdRelationId ||
376 [ + + ]: 28940102 : relationId == AuthMemRelationId ||
377 [ + + ]: 28835638 : relationId == DatabaseRelationId ||
378 [ + + ]: 28807937 : relationId == DbRoleSettingRelationId ||
379 [ + + ]: 28785643 : relationId == ParameterAclRelationId ||
380 [ + + ]: 28765436 : relationId == ReplicationOriginRelationId ||
381 [ + + ]: 28455730 : relationId == SharedDependRelationId ||
382 [ + + ]: 28451812 : relationId == SharedDescriptionRelationId ||
383 [ + + ]: 28444175 : relationId == SharedSecLabelRelationId ||
384 [ + + ]: 28418752 : relationId == SubscriptionRelationId ||
385 : : relationId == TableSpaceRelationId)
386 : 692399 : return true;
387 : : /* These are their indexes */
388 [ + + + + ]: 28387316 : if (relationId == AuthIdOidIndexId ||
389 [ + + ]: 28307936 : relationId == AuthIdRolnameIndexId ||
390 [ + + ]: 28296643 : relationId == AuthMemMemRoleIndexId ||
391 [ + + ]: 28290581 : relationId == AuthMemRoleMemIndexId ||
392 [ + + ]: 28288003 : relationId == AuthMemOidIndexId ||
393 [ + + ]: 28286038 : relationId == AuthMemGrantorIndexId ||
394 [ + + ]: 28260876 : relationId == DatabaseNameIndexId ||
395 [ + + ]: 28207035 : relationId == DatabaseOidIndexId ||
396 [ + + ]: 28120857 : relationId == DbRoleSettingDatidRolidIndexId ||
397 [ + + ]: 28113714 : relationId == ParameterAclOidIndexId ||
398 [ + + ]: 28106603 : relationId == ParameterAclParnameIndexId ||
399 [ + + ]: 28101013 : relationId == ReplicationOriginIdentIndex ||
400 [ + + ]: 28095556 : relationId == ReplicationOriginNameIndex ||
401 [ + + ]: 27925979 : relationId == SharedDependDependerIndexId ||
402 [ + + ]: 27917131 : relationId == SharedDependReferenceIndexId ||
403 [ + + ]: 27913985 : relationId == SharedDescriptionObjIndexId ||
404 [ + + ]: 27907113 : relationId == SharedSecLabelObjectIndexId ||
405 [ + + ]: 27900876 : relationId == SubscriptionNameIndexId ||
406 [ + + ]: 27894029 : relationId == SubscriptionObjectIndexId ||
407 [ + + ]: 27891061 : relationId == TablespaceNameIndexId ||
408 : : relationId == TablespaceOidIndexId)
409 : 512142 : return true;
410 : : /* These are their toast tables and toast indexes */
411 [ + + + + ]: 27875174 : if (relationId == PgDatabaseToastTable ||
412 [ + + ]: 27872205 : relationId == PgDatabaseToastIndex ||
413 [ + + ]: 27870764 : relationId == PgDbRoleSettingToastTable ||
414 [ + + ]: 27869510 : relationId == PgDbRoleSettingToastIndex ||
415 [ + + ]: 27868074 : relationId == PgParameterAclToastTable ||
416 [ + + ]: 27866825 : relationId == PgParameterAclToastIndex ||
417 [ + + ]: 27865353 : relationId == PgShdescriptionToastTable ||
418 [ + + ]: 27864070 : relationId == PgShdescriptionToastIndex ||
419 [ + + ]: 27862633 : relationId == PgShseclabelToastTable ||
420 [ + + ]: 27861383 : relationId == PgShseclabelToastIndex ||
421 [ + + ]: 27859948 : relationId == PgSubscriptionToastTable ||
422 [ + + ]: 27858700 : relationId == PgSubscriptionToastIndex ||
423 [ + + ]: 27857233 : relationId == PgTablespaceToastTable ||
424 : : relationId == PgTablespaceToastIndex)
425 : 19222 : return true;
426 : 27855952 : return false;
427 : : }
428 : :
429 : : /*
430 : : * IsPinnedObject
431 : : * Given the class + OID identity of a database object, report whether
432 : : * it is "pinned", that is not droppable because the system requires it.
433 : : *
434 : : * We used to represent this explicitly in pg_depend, but that proved to be
435 : : * an undesirable amount of overhead, so now we rely on an OID range test.
436 : : */
437 : : bool
438 : 1780882 : IsPinnedObject(Oid classId, Oid objectId)
439 : : {
440 : : /*
441 : : * Objects with OIDs above FirstUnpinnedObjectId are never pinned. Since
442 : : * the OID generator skips this range when wrapping around, this check
443 : : * guarantees that user-defined objects are never considered pinned.
444 : : */
445 [ + + ]: 1780882 : if (objectId >= FirstUnpinnedObjectId)
446 : 527038 : return false;
447 : :
448 : : /*
449 : : * Large objects are never pinned. We need this special case because
450 : : * their OIDs can be user-assigned.
451 : : */
452 [ + + ]: 1253844 : if (classId == LargeObjectRelationId)
453 : 36 : return false;
454 : :
455 : : /*
456 : : * There are a few objects defined in the catalog .dat files that, as a
457 : : * matter of policy, we prefer not to treat as pinned. We used to handle
458 : : * that by excluding them from pg_depend, but it's just as easy to
459 : : * hard-wire their OIDs here. (If the user does indeed drop and recreate
460 : : * them, they'll have new but certainly-unpinned OIDs, so no problem.)
461 : : *
462 : : * Checking both classId and objectId is overkill, since OIDs below
463 : : * FirstGenbkiObjectId should be globally unique, but do it anyway for
464 : : * robustness.
465 : : */
466 : :
467 : : /* the public namespace is not pinned */
468 [ + + + + ]: 1253808 : if (classId == NamespaceRelationId &&
469 : : objectId == PG_PUBLIC_NAMESPACE)
470 : 40375 : return false;
471 : :
472 : : /*
473 : : * Databases are never pinned. It might seem that it'd be prudent to pin
474 : : * at least template0; but we do this intentionally so that template0 and
475 : : * template1 can be rebuilt from each other, thus letting them serve as
476 : : * mutual backups (as long as you've not modified template1, anyway).
477 : : */
478 [ - + ]: 1213433 : if (classId == DatabaseRelationId)
479 : 0 : return false;
480 : :
481 : : /*
482 : : * All other initdb-created objects are pinned. This is overkill (the
483 : : * system doesn't really depend on having every last weird datatype, for
484 : : * instance) but generating only the minimum required set of dependencies
485 : : * seems hard, and enforcing an accurate list would be much more expensive
486 : : * than the simple range test used here.
487 : : */
488 : 1213433 : return true;
489 : : }
490 : :
491 : :
492 : : /*
493 : : * GetNewOidWithIndex
494 : : * Generate a new OID that is unique within the system relation.
495 : : *
496 : : * Since the OID is not immediately inserted into the table, there is a
497 : : * race condition here; but a problem could occur only if someone else
498 : : * managed to cycle through 2^32 OIDs and generate the same OID before we
499 : : * finish inserting our row. This seems unlikely to be a problem. Note
500 : : * that if we had to *commit* the row to end the race condition, the risk
501 : : * would be rather higher; therefore we use SnapshotAny in the test, so that
502 : : * we will see uncommitted rows. (We used to use SnapshotDirty, but that has
503 : : * the disadvantage that it ignores recently-deleted rows, creating a risk
504 : : * of transient conflicts for as long as our own MVCC snapshots think a
505 : : * recently-deleted row is live. The risk is far higher when selecting TOAST
506 : : * OIDs, because SnapshotToast considers dead rows as active indefinitely.)
507 : : *
508 : : * Note that we are effectively assuming that the table has a relatively small
509 : : * number of entries (much less than 2^32) and there aren't very long runs of
510 : : * consecutive existing OIDs. This is a mostly reasonable assumption for
511 : : * system catalogs.
512 : : *
513 : : * Caller must have a suitable lock on the relation.
514 : : */
515 : : Oid
516 : 570948 : GetNewOidWithIndex(Relation relation, Oid indexId, AttrNumber oidcolumn)
517 : : {
518 : : Oid newOid;
519 : : SysScanDesc scan;
520 : : ScanKeyData key;
521 : : bool collides;
522 : 570948 : uint64 retries = 0;
523 : 570948 : uint64 retries_before_log = GETNEWOID_LOG_THRESHOLD;
524 : :
525 : : /* Only system relations are supported */
526 : : Assert(IsSystemRelation(relation));
527 : :
528 : : /* In bootstrap mode, we don't have any indexes to use */
529 [ + + ]: 570948 : if (IsBootstrapProcessingMode())
530 : 6840 : return GetNewObjectId();
531 : :
532 : : /*
533 : : * We should never be asked to generate a new pg_type OID during
534 : : * pg_upgrade; doing so would risk collisions with the OIDs it wants to
535 : : * assign. Hitting this assert means there's some path where we failed to
536 : : * ensure that a type OID is determined by commands in the dump script.
537 : : */
538 : : Assert(!IsBinaryUpgrade || RelationGetRelid(relation) != TypeRelationId);
539 : :
540 : : /* Generate new OIDs until we find one not in the table */
541 : : do
542 : : {
543 [ - + ]: 564108 : CHECK_FOR_INTERRUPTS();
544 : :
545 : 564108 : newOid = GetNewObjectId();
546 : :
547 : 564108 : ScanKeyInit(&key,
548 : : oidcolumn,
549 : : BTEqualStrategyNumber, F_OIDEQ,
550 : : ObjectIdGetDatum(newOid));
551 : :
552 : : /* see notes above about using SnapshotAny */
553 : 564108 : scan = systable_beginscan(relation, indexId, true,
554 : : SnapshotAny, 1, &key);
555 : :
556 : 564108 : collides = HeapTupleIsValid(systable_getnext(scan));
557 : :
558 : 564108 : systable_endscan(scan);
559 : :
560 : : /*
561 : : * Log that we iterate more than GETNEWOID_LOG_THRESHOLD but have not
562 : : * yet found OID unused in the relation. Then repeat logging with
563 : : * exponentially increasing intervals until we iterate more than
564 : : * GETNEWOID_LOG_MAX_INTERVAL. Finally repeat logging every
565 : : * GETNEWOID_LOG_MAX_INTERVAL unless an unused OID is found. This
566 : : * logic is necessary not to fill up the server log with the similar
567 : : * messages.
568 : : */
569 [ - + ]: 564108 : if (retries >= retries_before_log)
570 : : {
571 [ # # ]: 0 : ereport(LOG,
572 : : (errmsg("still searching for an unused OID in relation \"%s\"",
573 : : RelationGetRelationName(relation)),
574 : : errdetail_plural("OID candidates have been checked %" PRIu64 " time, but no unused OID has been found yet.",
575 : : "OID candidates have been checked %" PRIu64 " times, but no unused OID has been found yet.",
576 : : retries,
577 : : retries)));
578 : :
579 : : /*
580 : : * Double the number of retries to do before logging next until it
581 : : * reaches GETNEWOID_LOG_MAX_INTERVAL.
582 : : */
583 [ # # ]: 0 : if (retries_before_log * 2 <= GETNEWOID_LOG_MAX_INTERVAL)
584 : 0 : retries_before_log *= 2;
585 : : else
586 : 0 : retries_before_log += GETNEWOID_LOG_MAX_INTERVAL;
587 : : }
588 : :
589 : 564108 : retries++;
590 [ - + ]: 564108 : } while (collides);
591 : :
592 : : /*
593 : : * If at least one log message is emitted, also log the completion of OID
594 : : * assignment.
595 : : */
596 [ - + ]: 564108 : if (retries > GETNEWOID_LOG_THRESHOLD)
597 : : {
598 [ # # ]: 0 : ereport(LOG,
599 : : (errmsg_plural("new OID has been assigned in relation \"%s\" after %" PRIu64 " retry",
600 : : "new OID has been assigned in relation \"%s\" after %" PRIu64 " retries",
601 : : retries,
602 : : RelationGetRelationName(relation), retries)));
603 : : }
604 : :
605 : 564108 : return newOid;
606 : : }
607 : :
608 : : /*
609 : : * GetNewRelFileNumber
610 : : * Generate a new relfilenumber that is unique within the
611 : : * database of the given tablespace.
612 : : *
613 : : * If the relfilenumber will also be used as the relation's OID, pass the
614 : : * opened pg_class catalog, and this routine will guarantee that the result
615 : : * is also an unused OID within pg_class. If the result is to be used only
616 : : * as a relfilenumber for an existing relation, pass NULL for pg_class.
617 : : *
618 : : * As with GetNewOidWithIndex(), there is some theoretical risk of a race
619 : : * condition, but it doesn't seem worth worrying about.
620 : : *
621 : : * Note: we don't support using this in bootstrap mode. All relations
622 : : * created by bootstrap have preassigned OIDs, so there's no need.
623 : : */
624 : : RelFileNumber
625 : 82048 : GetNewRelFileNumber(Oid reltablespace, Relation pg_class, char relpersistence)
626 : : {
627 : : RelFileLocatorBackend rlocator;
628 : : RelPathStr rpath;
629 : : bool collides;
630 : : ProcNumber procNumber;
631 : :
632 : : /*
633 : : * If we ever get here during pg_upgrade, there's something wrong; all
634 : : * relfilenumber assignments during a binary-upgrade run should be
635 : : * determined by commands in the dump script.
636 : : */
637 : : Assert(!IsBinaryUpgrade);
638 : :
639 [ + + - ]: 82048 : switch (relpersistence)
640 : : {
641 : 4959 : case RELPERSISTENCE_TEMP:
642 [ + - ]: 4959 : procNumber = ProcNumberForTempRelations();
643 : 4959 : break;
644 : 77089 : case RELPERSISTENCE_UNLOGGED:
645 : : case RELPERSISTENCE_PERMANENT:
646 : 77089 : procNumber = INVALID_PROC_NUMBER;
647 : 77089 : break;
648 : 0 : default:
649 [ # # ]: 0 : elog(ERROR, "invalid relpersistence: %c", relpersistence);
650 : : return InvalidRelFileNumber; /* placate compiler */
651 : : }
652 : :
653 : : /* This logic should match RelationInitPhysicalAddr */
654 [ + + ]: 82048 : rlocator.locator.spcOid = reltablespace ? reltablespace : MyDatabaseTableSpace;
655 : 82048 : rlocator.locator.dbOid =
656 : 82048 : (rlocator.locator.spcOid == GLOBALTABLESPACE_OID) ?
657 [ + + ]: 82048 : InvalidOid : MyDatabaseId;
658 : :
659 : : /*
660 : : * The relpath will vary based on the backend number, so we must
661 : : * initialize that properly here to make sure that any collisions based on
662 : : * filename are properly detected.
663 : : */
664 : 82048 : rlocator.backend = procNumber;
665 : :
666 : : do
667 : : {
668 [ - + ]: 82048 : CHECK_FOR_INTERRUPTS();
669 : :
670 : : /* Generate the OID */
671 [ + + ]: 82048 : if (pg_class)
672 : 73806 : rlocator.locator.relNumber = GetNewOidWithIndex(pg_class, ClassOidIndexId,
673 : : Anum_pg_class_oid);
674 : : else
675 : 8242 : rlocator.locator.relNumber = GetNewObjectId();
676 : :
677 : : /* Check for existing file of same name */
678 : 82048 : rpath = relpath(rlocator, MAIN_FORKNUM);
679 : :
680 [ - + ]: 82048 : if (access(rpath.str, F_OK) == 0)
681 : : {
682 : : /* definite collision */
683 : 0 : collides = true;
684 : : }
685 : : else
686 : : {
687 : : /*
688 : : * Here we have a little bit of a dilemma: if errno is something
689 : : * other than ENOENT, should we declare a collision and loop? In
690 : : * practice it seems best to go ahead regardless of the errno. If
691 : : * there is a colliding file we will get an smgr failure when we
692 : : * attempt to create the new relation file.
693 : : */
694 : 82048 : collides = false;
695 : : }
696 [ - + ]: 82048 : } while (collides);
697 : :
698 : 82048 : return rlocator.locator.relNumber;
699 : : }
700 : :
701 : : /*
702 : : * SQL callable interface for GetNewOidWithIndex(). Outside of initdb's
703 : : * direct insertions into catalog tables, and recovering from corruption, this
704 : : * should rarely be needed.
705 : : *
706 : : * Function is intentionally not documented in the user facing docs.
707 : : */
708 : : Datum
709 : 0 : pg_nextoid(PG_FUNCTION_ARGS)
710 : : {
711 : 0 : Oid reloid = PG_GETARG_OID(0);
712 : 0 : Name attname = PG_GETARG_NAME(1);
713 : 0 : Oid idxoid = PG_GETARG_OID(2);
714 : : Relation rel;
715 : : Relation idx;
716 : : HeapTuple atttuple;
717 : : Form_pg_attribute attform;
718 : : AttrNumber attno;
719 : : Oid newoid;
720 : :
721 : : /*
722 : : * As this function is not intended to be used during normal running, and
723 : : * only supports system catalogs (which require superuser permissions to
724 : : * modify), just checking for superuser ought to not obstruct valid
725 : : * usecases.
726 : : */
727 [ # # ]: 0 : if (!superuser())
728 [ # # ]: 0 : ereport(ERROR,
729 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
730 : : errmsg("must be superuser to call %s()",
731 : : "pg_nextoid")));
732 : :
733 : 0 : rel = table_open(reloid, RowExclusiveLock);
734 : 0 : idx = index_open(idxoid, RowExclusiveLock);
735 : :
736 [ # # ]: 0 : if (!IsSystemRelation(rel))
737 [ # # ]: 0 : ereport(ERROR,
738 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
739 : : errmsg("pg_nextoid() can only be used on system catalogs")));
740 : :
741 [ # # ]: 0 : if (idx->rd_index->indrelid != RelationGetRelid(rel))
742 [ # # ]: 0 : ereport(ERROR,
743 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
744 : : errmsg("index \"%s\" does not belong to table \"%s\"",
745 : : RelationGetRelationName(idx),
746 : : RelationGetRelationName(rel))));
747 : :
748 : 0 : atttuple = SearchSysCacheAttName(reloid, NameStr(*attname));
749 [ # # ]: 0 : if (!HeapTupleIsValid(atttuple))
750 [ # # ]: 0 : ereport(ERROR,
751 : : (errcode(ERRCODE_UNDEFINED_COLUMN),
752 : : errmsg("column \"%s\" of relation \"%s\" does not exist",
753 : : NameStr(*attname), RelationGetRelationName(rel))));
754 : :
755 : 0 : attform = ((Form_pg_attribute) GETSTRUCT(atttuple));
756 : 0 : attno = attform->attnum;
757 : :
758 [ # # ]: 0 : if (attform->atttypid != OIDOID)
759 [ # # ]: 0 : ereport(ERROR,
760 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
761 : : errmsg("column \"%s\" is not of type %s",
762 : : NameStr(*attname), "oid")));
763 : :
764 [ # # ]: 0 : if (IndexRelationGetNumberOfKeyAttributes(idx) != 1 ||
765 [ # # ]: 0 : idx->rd_index->indkey.values[0] != attno)
766 [ # # ]: 0 : ereport(ERROR,
767 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
768 : : errmsg("index \"%s\" is not the index for column \"%s\"",
769 : : RelationGetRelationName(idx),
770 : : NameStr(*attname))));
771 : :
772 : 0 : newoid = GetNewOidWithIndex(rel, idxoid, attno);
773 : :
774 : 0 : ReleaseSysCache(atttuple);
775 : 0 : table_close(rel, RowExclusiveLock);
776 : 0 : index_close(idx, RowExclusiveLock);
777 : :
778 : 0 : PG_RETURN_OID(newoid);
779 : : }
780 : :
781 : : /*
782 : : * SQL callable interface for StopGeneratingPinnedObjectIds().
783 : : *
784 : : * This is only to be used by initdb, so it's intentionally not documented in
785 : : * the user facing docs.
786 : : */
787 : : Datum
788 : 58 : pg_stop_making_pinned_objects(PG_FUNCTION_ARGS)
789 : : {
790 : : /*
791 : : * Belt-and-suspenders check, since StopGeneratingPinnedObjectIds will
792 : : * fail anyway in non-single-user mode.
793 : : */
794 [ - + ]: 58 : if (!superuser())
795 [ # # ]: 0 : ereport(ERROR,
796 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
797 : : errmsg("must be superuser to call %s()",
798 : : "pg_stop_making_pinned_objects")));
799 : :
800 : 58 : StopGeneratingPinnedObjectIds();
801 : :
802 : 58 : PG_RETURN_VOID();
803 : : }
|