/src/postgres/src/backend/catalog/catalog.c
Line | Count | Source |
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 | | IsSystemRelation(Relation relation) |
75 | 0 | { |
76 | 0 | return IsSystemClass(RelationGetRelid(relation), relation->rd_rel); |
77 | 0 | } |
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 | | IsSystemClass(Oid relid, Form_pg_class reltuple) |
87 | 0 | { |
88 | | /* IsCatalogRelationOid is a bit faster, so test that first */ |
89 | 0 | return (IsCatalogRelationOid(relid) || |
90 | 0 | IsToastClass(reltuple) || |
91 | 0 | IsConflictLogTableClass(reltuple)); |
92 | 0 | } |
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 | | IsCatalogRelation(Relation relation) |
107 | 0 | { |
108 | 0 | return IsCatalogRelationOid(RelationGetRelid(relation)); |
109 | 0 | } |
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 | | IsCatalogRelationOid(Oid relid) |
124 | 0 | { |
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 | 0 | return (relid < (Oid) FirstUnpinnedObjectId); |
139 | 0 | } |
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 | | IsCatalogTextUniqueIndexOid(Oid relid) |
159 | 0 | { |
160 | 0 | switch (relid) |
161 | 0 | { |
162 | 0 | case ParameterAclParnameIndexId: |
163 | 0 | case ReplicationOriginNameIndex: |
164 | 0 | case SecLabelObjectIndexId: |
165 | 0 | case SharedSecLabelObjectIndexId: |
166 | 0 | return true; |
167 | 0 | } |
168 | 0 | return false; |
169 | 0 | } |
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 | | IsInplaceUpdateRelation(Relation relation) |
186 | 0 | { |
187 | 0 | return IsInplaceUpdateOid(RelationGetRelid(relation)); |
188 | 0 | } |
189 | | |
190 | | /* |
191 | | * IsInplaceUpdateOid |
192 | | * Like the above, but takes an OID as argument. |
193 | | */ |
194 | | bool |
195 | | IsInplaceUpdateOid(Oid relid) |
196 | 0 | { |
197 | 0 | return (relid == RelationRelationId || |
198 | 0 | relid == DatabaseRelationId); |
199 | 0 | } |
200 | | |
201 | | /* |
202 | | * IsToastRelation |
203 | | * True iff relation is a TOAST support relation (or index). |
204 | | * |
205 | | * Does not perform any catalog accesses. |
206 | | */ |
207 | | bool |
208 | | IsToastRelation(Relation relation) |
209 | 0 | { |
210 | | /* |
211 | | * What we actually check is whether the relation belongs to a pg_toast |
212 | | * namespace. This should be equivalent because of restrictions that are |
213 | | * enforced elsewhere against creating user relations in, or moving |
214 | | * relations into/out of, a pg_toast namespace. Notice also that this |
215 | | * will not say "true" for toast tables belonging to other sessions' temp |
216 | | * tables; we expect that other mechanisms will prevent access to those. |
217 | | */ |
218 | 0 | return IsToastNamespace(RelationGetNamespace(relation)); |
219 | 0 | } |
220 | | |
221 | | /* |
222 | | * IsToastClass |
223 | | * Like the above, but takes a Form_pg_class as argument. |
224 | | * Used when we do not want to open the relation and have to |
225 | | * search pg_class directly. |
226 | | */ |
227 | | bool |
228 | | IsToastClass(Form_pg_class reltuple) |
229 | 0 | { |
230 | 0 | Oid relnamespace = reltuple->relnamespace; |
231 | |
|
232 | 0 | return IsToastNamespace(relnamespace); |
233 | 0 | } |
234 | | |
235 | | /* |
236 | | * IsConflictLogTableClass |
237 | | * True iff pg_class tuple represents a Conflict Log Table. |
238 | | * |
239 | | * Does not perform any catalog accesses. |
240 | | */ |
241 | | bool |
242 | | IsConflictLogTableClass(Form_pg_class reltuple) |
243 | 0 | { |
244 | 0 | Oid relnamespace = reltuple->relnamespace; |
245 | |
|
246 | 0 | return IsConflictLogTableNamespace(relnamespace); |
247 | 0 | } |
248 | | |
249 | | /* |
250 | | * IsCatalogNamespace |
251 | | * True iff namespace is pg_catalog. |
252 | | * |
253 | | * Does not perform any catalog accesses. |
254 | | * |
255 | | * NOTE: the reason this isn't a macro is to avoid having to include |
256 | | * catalog/pg_namespace.h in a lot of places. |
257 | | */ |
258 | | bool |
259 | | IsCatalogNamespace(Oid namespaceId) |
260 | 0 | { |
261 | 0 | return namespaceId == PG_CATALOG_NAMESPACE; |
262 | 0 | } |
263 | | |
264 | | /* |
265 | | * IsToastNamespace |
266 | | * True iff namespace is pg_toast or my temporary-toast-table namespace. |
267 | | * |
268 | | * Does not perform any catalog accesses. |
269 | | * |
270 | | * Note: this will return false for temporary-toast-table namespaces belonging |
271 | | * to other backends. Those are treated the same as other backends' regular |
272 | | * temp table namespaces, and access is prevented where appropriate. |
273 | | * If you need to check for those, you may be able to use isAnyTempNamespace, |
274 | | * but beware that that does involve a catalog access. |
275 | | */ |
276 | | bool |
277 | | IsToastNamespace(Oid namespaceId) |
278 | 0 | { |
279 | 0 | return (namespaceId == PG_TOAST_NAMESPACE) || |
280 | 0 | isTempToastNamespace(namespaceId); |
281 | 0 | } |
282 | | |
283 | | /* |
284 | | * IsConflictLogTableNamespace |
285 | | * True iff namespace is pg_conflict. |
286 | | * |
287 | | * Does not perform any catalog accesses. |
288 | | */ |
289 | | bool |
290 | | IsConflictLogTableNamespace(Oid namespaceId) |
291 | 0 | { |
292 | 0 | return namespaceId == PG_CONFLICT_NAMESPACE; |
293 | 0 | } |
294 | | |
295 | | /* |
296 | | * IsReservedName |
297 | | * True iff name starts with the pg_ prefix. |
298 | | * |
299 | | * For some classes of objects, the prefix pg_ is reserved for |
300 | | * system objects only. As of 8.0, this was only true for |
301 | | * schema and tablespace names. With 9.6, this is also true |
302 | | * for roles. |
303 | | */ |
304 | | bool |
305 | | IsReservedName(const char *name) |
306 | 0 | { |
307 | | /* ugly coding for speed */ |
308 | 0 | return (name[0] == 'p' && |
309 | 0 | name[1] == 'g' && |
310 | 0 | name[2] == '_'); |
311 | 0 | } |
312 | | |
313 | | |
314 | | /* |
315 | | * IsSharedRelation |
316 | | * Given the OID of a relation, determine whether it's supposed to be |
317 | | * shared across an entire database cluster. |
318 | | * |
319 | | * In older releases, this had to be hard-wired so that we could compute the |
320 | | * locktag for a relation and lock it before examining its catalog entry. |
321 | | * Since we now have MVCC catalog access, the race conditions that made that |
322 | | * a hard requirement are gone, so we could look at relaxing this restriction. |
323 | | * However, if we scanned the pg_class entry to find relisshared, and only |
324 | | * then locked the relation, pg_class could get updated in the meantime, |
325 | | * forcing us to scan the relation again, which would definitely be complex |
326 | | * and might have undesirable performance consequences. Fortunately, the set |
327 | | * of shared relations is fairly static, so a hand-maintained list of their |
328 | | * OIDs isn't completely impractical. |
329 | | */ |
330 | | bool |
331 | | IsSharedRelation(Oid relationId) |
332 | 0 | { |
333 | | /* These are the shared catalogs (look for BKI_SHARED_RELATION) */ |
334 | 0 | if (relationId == AuthIdRelationId || |
335 | 0 | relationId == AuthMemRelationId || |
336 | 0 | relationId == DatabaseRelationId || |
337 | 0 | relationId == DbRoleSettingRelationId || |
338 | 0 | relationId == ParameterAclRelationId || |
339 | 0 | relationId == ReplicationOriginRelationId || |
340 | 0 | relationId == SharedDependRelationId || |
341 | 0 | relationId == SharedDescriptionRelationId || |
342 | 0 | relationId == SharedSecLabelRelationId || |
343 | 0 | relationId == SubscriptionRelationId || |
344 | 0 | relationId == TableSpaceRelationId) |
345 | 0 | return true; |
346 | | /* These are their indexes */ |
347 | 0 | if (relationId == AuthIdOidIndexId || |
348 | 0 | relationId == AuthIdRolnameIndexId || |
349 | 0 | relationId == AuthMemMemRoleIndexId || |
350 | 0 | relationId == AuthMemRoleMemIndexId || |
351 | 0 | relationId == AuthMemOidIndexId || |
352 | 0 | relationId == AuthMemGrantorIndexId || |
353 | 0 | relationId == DatabaseNameIndexId || |
354 | 0 | relationId == DatabaseOidIndexId || |
355 | 0 | relationId == DbRoleSettingDatidRolidIndexId || |
356 | 0 | relationId == ParameterAclOidIndexId || |
357 | 0 | relationId == ParameterAclParnameIndexId || |
358 | 0 | relationId == ReplicationOriginIdentIndex || |
359 | 0 | relationId == ReplicationOriginNameIndex || |
360 | 0 | relationId == SharedDependDependerIndexId || |
361 | 0 | relationId == SharedDependReferenceIndexId || |
362 | 0 | relationId == SharedDescriptionObjIndexId || |
363 | 0 | relationId == SharedSecLabelObjectIndexId || |
364 | 0 | relationId == SubscriptionNameIndexId || |
365 | 0 | relationId == SubscriptionObjectIndexId || |
366 | 0 | relationId == TablespaceNameIndexId || |
367 | 0 | relationId == TablespaceOidIndexId) |
368 | 0 | return true; |
369 | | /* These are their toast tables and toast indexes */ |
370 | 0 | if (relationId == PgDatabaseToastTable || |
371 | 0 | relationId == PgDatabaseToastIndex || |
372 | 0 | relationId == PgDbRoleSettingToastTable || |
373 | 0 | relationId == PgDbRoleSettingToastIndex || |
374 | 0 | relationId == PgParameterAclToastTable || |
375 | 0 | relationId == PgParameterAclToastIndex || |
376 | 0 | relationId == PgShdescriptionToastTable || |
377 | 0 | relationId == PgShdescriptionToastIndex || |
378 | 0 | relationId == PgShseclabelToastTable || |
379 | 0 | relationId == PgShseclabelToastIndex || |
380 | 0 | relationId == PgSubscriptionToastTable || |
381 | 0 | relationId == PgSubscriptionToastIndex || |
382 | 0 | relationId == PgTablespaceToastTable || |
383 | 0 | relationId == PgTablespaceToastIndex) |
384 | 0 | return true; |
385 | 0 | return false; |
386 | 0 | } |
387 | | |
388 | | /* |
389 | | * IsPinnedObject |
390 | | * Given the class + OID identity of a database object, report whether |
391 | | * it is "pinned", that is not droppable because the system requires it. |
392 | | * |
393 | | * We used to represent this explicitly in pg_depend, but that proved to be |
394 | | * an undesirable amount of overhead, so now we rely on an OID range test. |
395 | | */ |
396 | | bool |
397 | | IsPinnedObject(Oid classId, Oid objectId) |
398 | 0 | { |
399 | | /* |
400 | | * Objects with OIDs above FirstUnpinnedObjectId are never pinned. Since |
401 | | * the OID generator skips this range when wrapping around, this check |
402 | | * guarantees that user-defined objects are never considered pinned. |
403 | | */ |
404 | 0 | if (objectId >= FirstUnpinnedObjectId) |
405 | 0 | return false; |
406 | | |
407 | | /* |
408 | | * Large objects are never pinned. We need this special case because |
409 | | * their OIDs can be user-assigned. |
410 | | */ |
411 | 0 | if (classId == LargeObjectRelationId) |
412 | 0 | return false; |
413 | | |
414 | | /* |
415 | | * There are a few objects defined in the catalog .dat files that, as a |
416 | | * matter of policy, we prefer not to treat as pinned. We used to handle |
417 | | * that by excluding them from pg_depend, but it's just as easy to |
418 | | * hard-wire their OIDs here. (If the user does indeed drop and recreate |
419 | | * them, they'll have new but certainly-unpinned OIDs, so no problem.) |
420 | | * |
421 | | * Checking both classId and objectId is overkill, since OIDs below |
422 | | * FirstGenbkiObjectId should be globally unique, but do it anyway for |
423 | | * robustness. |
424 | | */ |
425 | | |
426 | | /* the public namespace is not pinned */ |
427 | 0 | if (classId == NamespaceRelationId && |
428 | 0 | objectId == PG_PUBLIC_NAMESPACE) |
429 | 0 | return false; |
430 | | |
431 | | /* |
432 | | * Databases are never pinned. It might seem that it'd be prudent to pin |
433 | | * at least template0; but we do this intentionally so that template0 and |
434 | | * template1 can be rebuilt from each other, thus letting them serve as |
435 | | * mutual backups (as long as you've not modified template1, anyway). |
436 | | */ |
437 | 0 | if (classId == DatabaseRelationId) |
438 | 0 | return false; |
439 | | |
440 | | /* |
441 | | * All other initdb-created objects are pinned. This is overkill (the |
442 | | * system doesn't really depend on having every last weird datatype, for |
443 | | * instance) but generating only the minimum required set of dependencies |
444 | | * seems hard, and enforcing an accurate list would be much more expensive |
445 | | * than the simple range test used here. |
446 | | */ |
447 | 0 | return true; |
448 | 0 | } |
449 | | |
450 | | |
451 | | /* |
452 | | * GetNewOidWithIndex |
453 | | * Generate a new OID that is unique within the system relation. |
454 | | * |
455 | | * Since the OID is not immediately inserted into the table, there is a |
456 | | * race condition here; but a problem could occur only if someone else |
457 | | * managed to cycle through 2^32 OIDs and generate the same OID before we |
458 | | * finish inserting our row. This seems unlikely to be a problem. Note |
459 | | * that if we had to *commit* the row to end the race condition, the risk |
460 | | * would be rather higher; therefore we use SnapshotAny in the test, so that |
461 | | * we will see uncommitted rows. (We used to use SnapshotDirty, but that has |
462 | | * the disadvantage that it ignores recently-deleted rows, creating a risk |
463 | | * of transient conflicts for as long as our own MVCC snapshots think a |
464 | | * recently-deleted row is live. The risk is far higher when selecting TOAST |
465 | | * OIDs, because SnapshotToast considers dead rows as active indefinitely.) |
466 | | * |
467 | | * Note that we are effectively assuming that the table has a relatively small |
468 | | * number of entries (much less than 2^32) and there aren't very long runs of |
469 | | * consecutive existing OIDs. This is a mostly reasonable assumption for |
470 | | * system catalogs. |
471 | | * |
472 | | * Caller must have a suitable lock on the relation. |
473 | | */ |
474 | | Oid |
475 | | GetNewOidWithIndex(Relation relation, Oid indexId, AttrNumber oidcolumn) |
476 | | { |
477 | | Oid newOid; |
478 | | SysScanDesc scan; |
479 | | ScanKeyData key; |
480 | | bool collides; |
481 | | uint64 retries = 0; |
482 | | uint64 retries_before_log = GETNEWOID_LOG_THRESHOLD; |
483 | | |
484 | | /* Only system relations are supported */ |
485 | | Assert(IsSystemRelation(relation)); |
486 | | |
487 | | /* In bootstrap mode, we don't have any indexes to use */ |
488 | | if (IsBootstrapProcessingMode()) |
489 | | return GetNewObjectId(); |
490 | | |
491 | | /* |
492 | | * We should never be asked to generate a new pg_type OID during |
493 | | * pg_upgrade; doing so would risk collisions with the OIDs it wants to |
494 | | * assign. Hitting this assert means there's some path where we failed to |
495 | | * ensure that a type OID is determined by commands in the dump script. |
496 | | */ |
497 | | Assert(!IsBinaryUpgrade || RelationGetRelid(relation) != TypeRelationId); |
498 | | |
499 | | /* Generate new OIDs until we find one not in the table */ |
500 | | do |
501 | | { |
502 | | CHECK_FOR_INTERRUPTS(); |
503 | | |
504 | | newOid = GetNewObjectId(); |
505 | | |
506 | | ScanKeyInit(&key, |
507 | | oidcolumn, |
508 | | BTEqualStrategyNumber, F_OIDEQ, |
509 | | ObjectIdGetDatum(newOid)); |
510 | | |
511 | | /* see notes above about using SnapshotAny */ |
512 | | scan = systable_beginscan(relation, indexId, true, |
513 | | SnapshotAny, 1, &key); |
514 | | |
515 | | collides = HeapTupleIsValid(systable_getnext(scan)); |
516 | | |
517 | | systable_endscan(scan); |
518 | | |
519 | | /* |
520 | | * Log that we iterate more than GETNEWOID_LOG_THRESHOLD but have not |
521 | | * yet found OID unused in the relation. Then repeat logging with |
522 | | * exponentially increasing intervals until we iterate more than |
523 | | * GETNEWOID_LOG_MAX_INTERVAL. Finally repeat logging every |
524 | | * GETNEWOID_LOG_MAX_INTERVAL unless an unused OID is found. This |
525 | | * logic is necessary not to fill up the server log with the similar |
526 | | * messages. |
527 | | */ |
528 | | if (retries >= retries_before_log) |
529 | | { |
530 | | ereport(LOG, |
531 | | (errmsg("still searching for an unused OID in relation \"%s\"", |
532 | | RelationGetRelationName(relation)), |
533 | | errdetail_plural("OID candidates have been checked %" PRIu64 " time, but no unused OID has been found yet.", |
534 | | "OID candidates have been checked %" PRIu64 " times, but no unused OID has been found yet.", |
535 | | retries, |
536 | | retries))); |
537 | | |
538 | | /* |
539 | | * Double the number of retries to do before logging next until it |
540 | | * reaches GETNEWOID_LOG_MAX_INTERVAL. |
541 | | */ |
542 | | if (retries_before_log * 2 <= GETNEWOID_LOG_MAX_INTERVAL) |
543 | | retries_before_log *= 2; |
544 | | else |
545 | | retries_before_log += GETNEWOID_LOG_MAX_INTERVAL; |
546 | | } |
547 | | |
548 | | retries++; |
549 | | } while (collides); |
550 | | |
551 | | /* |
552 | | * If at least one log message is emitted, also log the completion of OID |
553 | | * assignment. |
554 | | */ |
555 | | if (retries > GETNEWOID_LOG_THRESHOLD) |
556 | | { |
557 | | ereport(LOG, |
558 | | (errmsg_plural("new OID has been assigned in relation \"%s\" after %" PRIu64 " retry", |
559 | | "new OID has been assigned in relation \"%s\" after %" PRIu64 " retries", |
560 | | retries, |
561 | | RelationGetRelationName(relation), retries))); |
562 | | } |
563 | | |
564 | | return newOid; |
565 | | } |
566 | | |
567 | | /* |
568 | | * GetNewRelFileNumber |
569 | | * Generate a new relfilenumber that is unique within the |
570 | | * database of the given tablespace. |
571 | | * |
572 | | * If the relfilenumber will also be used as the relation's OID, pass the |
573 | | * opened pg_class catalog, and this routine will guarantee that the result |
574 | | * is also an unused OID within pg_class. If the result is to be used only |
575 | | * as a relfilenumber for an existing relation, pass NULL for pg_class. |
576 | | * |
577 | | * As with GetNewOidWithIndex(), there is some theoretical risk of a race |
578 | | * condition, but it doesn't seem worth worrying about. |
579 | | * |
580 | | * Note: we don't support using this in bootstrap mode. All relations |
581 | | * created by bootstrap have preassigned OIDs, so there's no need. |
582 | | */ |
583 | | RelFileNumber |
584 | | GetNewRelFileNumber(Oid reltablespace, Relation pg_class, char relpersistence) |
585 | 0 | { |
586 | 0 | RelFileLocatorBackend rlocator; |
587 | 0 | RelPathStr rpath; |
588 | 0 | bool collides; |
589 | 0 | ProcNumber procNumber; |
590 | | |
591 | | /* |
592 | | * If we ever get here during pg_upgrade, there's something wrong; all |
593 | | * relfilenumber assignments during a binary-upgrade run should be |
594 | | * determined by commands in the dump script. |
595 | | */ |
596 | 0 | Assert(!IsBinaryUpgrade); |
597 | |
|
598 | 0 | switch (relpersistence) |
599 | 0 | { |
600 | 0 | case RELPERSISTENCE_TEMP: |
601 | 0 | procNumber = ProcNumberForTempRelations(); |
602 | 0 | break; |
603 | 0 | case RELPERSISTENCE_UNLOGGED: |
604 | 0 | case RELPERSISTENCE_PERMANENT: |
605 | 0 | procNumber = INVALID_PROC_NUMBER; |
606 | 0 | break; |
607 | 0 | default: |
608 | 0 | elog(ERROR, "invalid relpersistence: %c", relpersistence); |
609 | 0 | return InvalidRelFileNumber; /* placate compiler */ |
610 | 0 | } |
611 | | |
612 | | /* This logic should match RelationInitPhysicalAddr */ |
613 | 0 | rlocator.locator.spcOid = reltablespace ? reltablespace : MyDatabaseTableSpace; |
614 | 0 | rlocator.locator.dbOid = |
615 | 0 | (rlocator.locator.spcOid == GLOBALTABLESPACE_OID) ? |
616 | 0 | InvalidOid : MyDatabaseId; |
617 | | |
618 | | /* |
619 | | * The relpath will vary based on the backend number, so we must |
620 | | * initialize that properly here to make sure that any collisions based on |
621 | | * filename are properly detected. |
622 | | */ |
623 | 0 | rlocator.backend = procNumber; |
624 | |
|
625 | 0 | do |
626 | 0 | { |
627 | 0 | CHECK_FOR_INTERRUPTS(); |
628 | | |
629 | | /* Generate the OID */ |
630 | 0 | if (pg_class) |
631 | 0 | rlocator.locator.relNumber = GetNewOidWithIndex(pg_class, ClassOidIndexId, |
632 | 0 | Anum_pg_class_oid); |
633 | 0 | else |
634 | 0 | rlocator.locator.relNumber = GetNewObjectId(); |
635 | | |
636 | | /* Check for existing file of same name */ |
637 | 0 | rpath = relpath(rlocator, MAIN_FORKNUM); |
638 | |
|
639 | 0 | if (access(rpath.str, F_OK) == 0) |
640 | 0 | { |
641 | | /* definite collision */ |
642 | 0 | collides = true; |
643 | 0 | } |
644 | 0 | else |
645 | 0 | { |
646 | | /* |
647 | | * Here we have a little bit of a dilemma: if errno is something |
648 | | * other than ENOENT, should we declare a collision and loop? In |
649 | | * practice it seems best to go ahead regardless of the errno. If |
650 | | * there is a colliding file we will get an smgr failure when we |
651 | | * attempt to create the new relation file. |
652 | | */ |
653 | 0 | collides = false; |
654 | 0 | } |
655 | 0 | } while (collides); |
656 | |
|
657 | 0 | return rlocator.locator.relNumber; |
658 | 0 | } |
659 | | |
660 | | /* |
661 | | * SQL callable interface for GetNewOidWithIndex(). Outside of initdb's |
662 | | * direct insertions into catalog tables, and recovering from corruption, this |
663 | | * should rarely be needed. |
664 | | * |
665 | | * Function is intentionally not documented in the user facing docs. |
666 | | */ |
667 | | Datum |
668 | | pg_nextoid(PG_FUNCTION_ARGS) |
669 | 0 | { |
670 | 0 | Oid reloid = PG_GETARG_OID(0); |
671 | 0 | Name attname = PG_GETARG_NAME(1); |
672 | 0 | Oid idxoid = PG_GETARG_OID(2); |
673 | 0 | Relation rel; |
674 | 0 | Relation idx; |
675 | 0 | HeapTuple atttuple; |
676 | 0 | Form_pg_attribute attform; |
677 | 0 | AttrNumber attno; |
678 | 0 | Oid newoid; |
679 | | |
680 | | /* |
681 | | * As this function is not intended to be used during normal running, and |
682 | | * only supports system catalogs (which require superuser permissions to |
683 | | * modify), just checking for superuser ought to not obstruct valid |
684 | | * usecases. |
685 | | */ |
686 | 0 | if (!superuser()) |
687 | 0 | ereport(ERROR, |
688 | 0 | (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), |
689 | 0 | errmsg("must be superuser to call %s()", |
690 | 0 | "pg_nextoid"))); |
691 | | |
692 | 0 | rel = table_open(reloid, RowExclusiveLock); |
693 | 0 | idx = index_open(idxoid, RowExclusiveLock); |
694 | |
|
695 | 0 | if (!IsSystemRelation(rel)) |
696 | 0 | ereport(ERROR, |
697 | 0 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
698 | 0 | errmsg("pg_nextoid() can only be used on system catalogs"))); |
699 | | |
700 | 0 | if (idx->rd_index->indrelid != RelationGetRelid(rel)) |
701 | 0 | ereport(ERROR, |
702 | 0 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
703 | 0 | errmsg("index \"%s\" does not belong to table \"%s\"", |
704 | 0 | RelationGetRelationName(idx), |
705 | 0 | RelationGetRelationName(rel)))); |
706 | | |
707 | 0 | atttuple = SearchSysCacheAttName(reloid, NameStr(*attname)); |
708 | 0 | if (!HeapTupleIsValid(atttuple)) |
709 | 0 | ereport(ERROR, |
710 | 0 | (errcode(ERRCODE_UNDEFINED_COLUMN), |
711 | 0 | errmsg("column \"%s\" of relation \"%s\" does not exist", |
712 | 0 | NameStr(*attname), RelationGetRelationName(rel)))); |
713 | | |
714 | 0 | attform = ((Form_pg_attribute) GETSTRUCT(atttuple)); |
715 | 0 | attno = attform->attnum; |
716 | |
|
717 | 0 | if (attform->atttypid != OIDOID) |
718 | 0 | ereport(ERROR, |
719 | 0 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
720 | 0 | errmsg("column \"%s\" is not of type %s", |
721 | 0 | NameStr(*attname), "oid"))); |
722 | | |
723 | 0 | if (IndexRelationGetNumberOfKeyAttributes(idx) != 1 || |
724 | 0 | idx->rd_index->indkey.values[0] != attno) |
725 | 0 | ereport(ERROR, |
726 | 0 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
727 | 0 | errmsg("index \"%s\" is not the index for column \"%s\"", |
728 | 0 | RelationGetRelationName(idx), |
729 | 0 | NameStr(*attname)))); |
730 | | |
731 | 0 | newoid = GetNewOidWithIndex(rel, idxoid, attno); |
732 | |
|
733 | 0 | ReleaseSysCache(atttuple); |
734 | 0 | table_close(rel, RowExclusiveLock); |
735 | 0 | index_close(idx, RowExclusiveLock); |
736 | |
|
737 | 0 | PG_RETURN_OID(newoid); |
738 | 0 | } |
739 | | |
740 | | /* |
741 | | * SQL callable interface for StopGeneratingPinnedObjectIds(). |
742 | | * |
743 | | * This is only to be used by initdb, so it's intentionally not documented in |
744 | | * the user facing docs. |
745 | | */ |
746 | | Datum |
747 | | pg_stop_making_pinned_objects(PG_FUNCTION_ARGS) |
748 | 0 | { |
749 | | /* |
750 | | * Belt-and-suspenders check, since StopGeneratingPinnedObjectIds will |
751 | | * fail anyway in non-single-user mode. |
752 | | */ |
753 | 0 | if (!superuser()) |
754 | 0 | ereport(ERROR, |
755 | 0 | (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), |
756 | 0 | errmsg("must be superuser to call %s()", |
757 | 0 | "pg_stop_making_pinned_objects"))); |
758 | | |
759 | 0 | StopGeneratingPinnedObjectIds(); |
760 | |
|
761 | 0 | PG_RETURN_VOID(); |
762 | 0 | } |