LCOV - code coverage report
Current view: top level - src/backend/catalog - catalog.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18beta1 Lines: 130 167 77.8 %
Date: 2025-05-17 06:15:42 Functions: 17 18 94.4 %
Legend: Lines: hit not hit

          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-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      284062 : IsSystemRelation(Relation relation)
      75             : {
      76      284062 :     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     1211950 : IsSystemClass(Oid relid, Form_pg_class reltuple)
      87             : {
      88             :     /* IsCatalogRelationOid is a bit faster, so test that first */
      89     1211950 :     return (IsCatalogRelationOid(relid) || IsToastClass(reltuple));
      90             : }
      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    56115296 : IsCatalogRelation(Relation relation)
     105             : {
     106    56115296 :     return IsCatalogRelationOid(RelationGetRelid(relation));
     107             : }
     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    96704772 : IsCatalogRelationOid(Oid relid)
     122             : {
     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    96704772 :     return (relid < (Oid) FirstUnpinnedObjectId);
     137             : }
     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        1254 : IsCatalogTextUniqueIndexOid(Oid relid)
     157             : {
     158        1254 :     switch (relid)
     159             :     {
     160          24 :         case ParameterAclParnameIndexId:
     161             :         case ReplicationOriginNameIndex:
     162             :         case SecLabelObjectIndexId:
     163             :         case SharedSecLabelObjectIndexId:
     164          24 :             return true;
     165             :     }
     166        1230 :     return false;
     167             : }
     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      441910 : IsInplaceUpdateRelation(Relation relation)
     184             : {
     185      441910 :     return IsInplaceUpdateOid(RelationGetRelid(relation));
     186             : }
     187             : 
     188             : /*
     189             :  * IsInplaceUpdateOid
     190             :  *      Like the above, but takes an OID as argument.
     191             :  */
     192             : bool
     193      441910 : IsInplaceUpdateOid(Oid relid)
     194             : {
     195      441910 :     return (relid == RelationRelationId ||
     196             :             relid == DatabaseRelationId);
     197             : }
     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     5404962 : IsToastRelation(Relation relation)
     207             : {
     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     5404962 :     return IsToastNamespace(RelationGetNamespace(relation));
     217             : }
     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     1137996 : IsToastClass(Form_pg_class reltuple)
     227             : {
     228     1137996 :     Oid         relnamespace = reltuple->relnamespace;
     229             : 
     230     1137996 :     return IsToastNamespace(relnamespace);
     231             : }
     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      215946 : IsCatalogNamespace(Oid namespaceId)
     244             : {
     245      215946 :     return namespaceId == PG_CATALOG_NAMESPACE;
     246             : }
     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     6638198 : IsToastNamespace(Oid namespaceId)
     262             : {
     263    13180548 :     return (namespaceId == PG_TOAST_NAMESPACE) ||
     264     6542350 :         isTempToastNamespace(namespaceId);
     265             : }
     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        3462 : IsReservedName(const char *name)
     279             : {
     280             :     /* ugly coding for speed */
     281        3532 :     return (name[0] == 'p' &&
     282        3480 :             name[1] == 'g' &&
     283          18 :             name[2] == '_');
     284             : }
     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    39923958 : IsSharedRelation(Oid relationId)
     305             : {
     306             :     /* These are the shared catalogs (look for BKI_SHARED_RELATION) */
     307    39923958 :     if (relationId == AuthIdRelationId ||
     308    39709438 :         relationId == AuthMemRelationId ||
     309    39553010 :         relationId == DatabaseRelationId ||
     310    39511132 :         relationId == DbRoleSettingRelationId ||
     311    39487378 :         relationId == ParameterAclRelationId ||
     312    39457132 :         relationId == ReplicationOriginRelationId ||
     313    39007704 :         relationId == SharedDependRelationId ||
     314    39001942 :         relationId == SharedDescriptionRelationId ||
     315    38991780 :         relationId == SharedSecLabelRelationId ||
     316    38956508 :         relationId == SubscriptionRelationId ||
     317             :         relationId == TableSpaceRelationId)
     318     1014470 :         return true;
     319             :     /* These are their indexes */
     320    38909488 :     if (relationId == AuthIdOidIndexId ||
     321    38802596 :         relationId == AuthIdRolnameIndexId ||
     322    38786228 :         relationId == AuthMemMemRoleIndexId ||
     323    38777264 :         relationId == AuthMemRoleMemIndexId ||
     324    38773414 :         relationId == AuthMemOidIndexId ||
     325    38770790 :         relationId == AuthMemGrantorIndexId ||
     326    38733750 :         relationId == DatabaseNameIndexId ||
     327    38654260 :         relationId == DatabaseOidIndexId ||
     328    38526552 :         relationId == DbRoleSettingDatidRolidIndexId ||
     329    38521064 :         relationId == ParameterAclOidIndexId ||
     330    38515646 :         relationId == ParameterAclParnameIndexId ||
     331    38507772 :         relationId == ReplicationOriginIdentIndex ||
     332    38500166 :         relationId == ReplicationOriginNameIndex ||
     333    38264748 :         relationId == SharedDependDependerIndexId ||
     334    38252048 :         relationId == SharedDependReferenceIndexId ||
     335    38247894 :         relationId == SharedDescriptionObjIndexId ||
     336    38239342 :         relationId == SharedSecLabelObjectIndexId ||
     337    38231548 :         relationId == SubscriptionNameIndexId ||
     338    38223046 :         relationId == SubscriptionObjectIndexId ||
     339    38219280 :         relationId == TablespaceNameIndexId ||
     340             :         relationId == TablespaceOidIndexId)
     341      713486 :         return true;
     342             :     /* These are their toast tables and toast indexes */
     343    38196002 :     if (relationId == PgDatabaseToastTable ||
     344    38192764 :         relationId == PgDatabaseToastIndex ||
     345    38191214 :         relationId == PgDbRoleSettingToastTable ||
     346    38189946 :         relationId == PgDbRoleSettingToastIndex ||
     347    38188398 :         relationId == PgParameterAclToastTable ||
     348    38187132 :         relationId == PgParameterAclToastIndex ||
     349    38185520 :         relationId == PgShdescriptionToastTable ||
     350    38184194 :         relationId == PgShdescriptionToastIndex ||
     351    38182646 :         relationId == PgShseclabelToastTable ||
     352    38181380 :         relationId == PgShseclabelToastIndex ||
     353    38179836 :         relationId == PgSubscriptionToastTable ||
     354    38178574 :         relationId == PgSubscriptionToastIndex ||
     355    38176974 :         relationId == PgTablespaceToastTable ||
     356             :         relationId == PgTablespaceToastIndex)
     357       20346 :         return true;
     358    38175656 :     return false;
     359             : }
     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     2587244 : IsPinnedObject(Oid classId, Oid objectId)
     371             : {
     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     2587244 :     if (objectId >= FirstUnpinnedObjectId)
     378      757590 :         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     1829654 :     if (classId == LargeObjectRelationId)
     385          54 :         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     1829600 :     if (classId == NamespaceRelationId &&
     401             :         objectId == PG_PUBLIC_NAMESPACE)
     402       58526 :         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     1771074 :     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     1771074 :     return true;
     421             : }
     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      972090 : GetNewOidWithIndex(Relation relation, Oid indexId, AttrNumber oidcolumn)
     449             : {
     450             :     Oid         newOid;
     451             :     SysScanDesc scan;
     452             :     ScanKeyData key;
     453             :     bool        collides;
     454      972090 :     uint64      retries = 0;
     455      972090 :     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      972090 :     if (IsBootstrapProcessingMode())
     462       11270 :         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      960820 :         CHECK_FOR_INTERRUPTS();
     476             : 
     477      960820 :         newOid = GetNewObjectId();
     478             : 
     479      960820 :         ScanKeyInit(&key,
     480             :                     oidcolumn,
     481             :                     BTEqualStrategyNumber, F_OIDEQ,
     482             :                     ObjectIdGetDatum(newOid));
     483             : 
     484             :         /* see notes above about using SnapshotAny */
     485      960820 :         scan = systable_beginscan(relation, indexId, true,
     486             :                                   SnapshotAny, 1, &key);
     487             : 
     488      960820 :         collides = HeapTupleIsValid(systable_getnext(scan));
     489             : 
     490      960820 :         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      960820 :         if (retries >= retries_before_log)
     502             :         {
     503           0 :             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           0 :             if (retries_before_log * 2 <= GETNEWOID_LOG_MAX_INTERVAL)
     516           0 :                 retries_before_log *= 2;
     517             :             else
     518           0 :                 retries_before_log += GETNEWOID_LOG_MAX_INTERVAL;
     519             :         }
     520             : 
     521      960820 :         retries++;
     522      960820 :     } while (collides);
     523             : 
     524             :     /*
     525             :      * If at least one log message is emitted, also log the completion of OID
     526             :      * assignment.
     527             :      */
     528      960820 :     if (retries > GETNEWOID_LOG_THRESHOLD)
     529             :     {
     530           0 :         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      960820 :     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      123546 : GetNewRelFileNumber(Oid reltablespace, Relation pg_class, char relpersistence)
     558             : {
     559             :     RelFileLocatorBackend rlocator;
     560             :     RelPathStr  rpath;
     561             :     bool        collides;
     562             :     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             :     Assert(!IsBinaryUpgrade);
     570             : 
     571      123546 :     switch (relpersistence)
     572             :     {
     573        6712 :         case RELPERSISTENCE_TEMP:
     574        6712 :             procNumber = ProcNumberForTempRelations();
     575        6712 :             break;
     576      116834 :         case RELPERSISTENCE_UNLOGGED:
     577             :         case RELPERSISTENCE_PERMANENT:
     578      116834 :             procNumber = INVALID_PROC_NUMBER;
     579      116834 :             break;
     580           0 :         default:
     581           0 :             elog(ERROR, "invalid relpersistence: %c", relpersistence);
     582             :             return InvalidRelFileNumber;    /* placate compiler */
     583             :     }
     584             : 
     585             :     /* This logic should match RelationInitPhysicalAddr */
     586      123546 :     rlocator.locator.spcOid = reltablespace ? reltablespace : MyDatabaseTableSpace;
     587      123546 :     rlocator.locator.dbOid =
     588      123546 :         (rlocator.locator.spcOid == GLOBALTABLESPACE_OID) ?
     589      123546 :         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      123546 :     rlocator.backend = procNumber;
     597             : 
     598             :     do
     599             :     {
     600      123546 :         CHECK_FOR_INTERRUPTS();
     601             : 
     602             :         /* Generate the OID */
     603      123546 :         if (pg_class)
     604      108594 :             rlocator.locator.relNumber = GetNewOidWithIndex(pg_class, ClassOidIndexId,
     605             :                                                             Anum_pg_class_oid);
     606             :         else
     607       14952 :             rlocator.locator.relNumber = GetNewObjectId();
     608             : 
     609             :         /* Check for existing file of same name */
     610      123546 :         rpath = relpath(rlocator, MAIN_FORKNUM);
     611             : 
     612      123546 :         if (access(rpath.str, F_OK) == 0)
     613             :         {
     614             :             /* definite collision */
     615           0 :             collides = true;
     616             :         }
     617             :         else
     618             :         {
     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      123546 :             collides = false;
     627             :         }
     628      123546 :     } while (collides);
     629             : 
     630      123546 :     return rlocator.locator.relNumber;
     631             : }
     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           0 : pg_nextoid(PG_FUNCTION_ARGS)
     642             : {
     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             :     Relation    rel;
     647             :     Relation    idx;
     648             :     HeapTuple   atttuple;
     649             :     Form_pg_attribute attform;
     650             :     AttrNumber  attno;
     651             :     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             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     662             :                  errmsg("must be superuser to call %s()",
     663             :                         "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             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     671             :                  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             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     676             :                  errmsg("index \"%s\" does not belong to table \"%s\"",
     677             :                         RelationGetRelationName(idx),
     678             :                         RelationGetRelationName(rel))));
     679             : 
     680           0 :     atttuple = SearchSysCacheAttName(reloid, NameStr(*attname));
     681           0 :     if (!HeapTupleIsValid(atttuple))
     682           0 :         ereport(ERROR,
     683             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
     684             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
     685             :                         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             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     693             :                  errmsg("column \"%s\" is not of type oid",
     694             :                         NameStr(*attname))));
     695             : 
     696           0 :     if (IndexRelationGetNumberOfKeyAttributes(idx) != 1 ||
     697           0 :         idx->rd_index->indkey.values[0] != attno)
     698           0 :         ereport(ERROR,
     699             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     700             :                  errmsg("index \"%s\" is not the index for column \"%s\"",
     701             :                         RelationGetRelationName(idx),
     702             :                         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             : }
     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          94 : pg_stop_making_pinned_objects(PG_FUNCTION_ARGS)
     721             : {
     722             :     /*
     723             :      * Belt-and-suspenders check, since StopGeneratingPinnedObjectIds will
     724             :      * fail anyway in non-single-user mode.
     725             :      */
     726          94 :     if (!superuser())
     727           0 :         ereport(ERROR,
     728             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     729             :                  errmsg("must be superuser to call %s()",
     730             :                         "pg_stop_making_pinned_objects")));
     731             : 
     732          94 :     StopGeneratingPinnedObjectIds();
     733             : 
     734          94 :     PG_RETURN_VOID();
     735             : }

Generated by: LCOV version 1.14