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