LCOV - code coverage report
Current view: top level - src/backend/catalog - catalog.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 78.7 % 174 137
Test Date: 2026-07-03 19:57:34 Functions: 95.0 % 20 19
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 75.1 % 189 142

             Branch data     Line data    Source code
       1                 :             : /*-------------------------------------------------------------------------
       2                 :             :  *
       3                 :             :  * catalog.c
       4                 :             :  *      routines concerned with catalog naming conventions and other
       5                 :             :  *      bits of hard-wired knowledge
       6                 :             :  *
       7                 :             :  *
       8                 :             :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       9                 :             :  * Portions Copyright (c) 1994, Regents of the University of California
      10                 :             :  *
      11                 :             :  *
      12                 :             :  * IDENTIFICATION
      13                 :             :  *    src/backend/catalog/catalog.c
      14                 :             :  *
      15                 :             :  *-------------------------------------------------------------------------
      16                 :             :  */
      17                 :             : 
      18                 :             : #include "postgres.h"
      19                 :             : 
      20                 :             : #include <fcntl.h>
      21                 :             : #include <unistd.h>
      22                 :             : 
      23                 :             : #include "access/genam.h"
      24                 :             : #include "access/htup_details.h"
      25                 :             : #include "access/table.h"
      26                 :             : #include "access/transam.h"
      27                 :             : #include "catalog/catalog.h"
      28                 :             : #include "catalog/namespace.h"
      29                 :             : #include "catalog/pg_auth_members.h"
      30                 :             : #include "catalog/pg_authid.h"
      31                 :             : #include "catalog/pg_database.h"
      32                 :             : #include "catalog/pg_db_role_setting.h"
      33                 :             : #include "catalog/pg_largeobject.h"
      34                 :             : #include "catalog/pg_namespace.h"
      35                 :             : #include "catalog/pg_parameter_acl.h"
      36                 :             : #include "catalog/pg_replication_origin.h"
      37                 :             : #include "catalog/pg_seclabel.h"
      38                 :             : #include "catalog/pg_shdepend.h"
      39                 :             : #include "catalog/pg_shdescription.h"
      40                 :             : #include "catalog/pg_shseclabel.h"
      41                 :             : #include "catalog/pg_subscription.h"
      42                 :             : #include "catalog/pg_tablespace.h"
      43                 :             : #include "catalog/pg_type.h"
      44                 :             : #include "miscadmin.h"
      45                 :             : #include "utils/fmgroids.h"
      46                 :             : #include "utils/fmgrprotos.h"
      47                 :             : #include "utils/rel.h"
      48                 :             : #include "utils/snapmgr.h"
      49                 :             : #include "utils/syscache.h"
      50                 :             : 
      51                 :             : /*
      52                 :             :  * Parameters to determine when to emit a log message in
      53                 :             :  * GetNewOidWithIndex()
      54                 :             :  */
      55                 :             : #define GETNEWOID_LOG_THRESHOLD 1000000
      56                 :             : #define GETNEWOID_LOG_MAX_INTERVAL 128000000
      57                 :             : 
      58                 :             : /*
      59                 :             :  * IsSystemRelation
      60                 :             :  *      True iff the relation is either a system catalog or a toast table.
      61                 :             :  *      See IsCatalogRelation for the exact definition of a system catalog.
      62                 :             :  *
      63                 :             :  *      We treat toast tables of user relations as "system relations" for
      64                 :             :  *      protection purposes, e.g. you can't change their schemas without
      65                 :             :  *      special permissions.  Therefore, most uses of this function are
      66                 :             :  *      checking whether allow_system_table_mods restrictions apply.
      67                 :             :  *      For other purposes, consider whether you shouldn't be using
      68                 :             :  *      IsCatalogRelation instead.
      69                 :             :  *
      70                 :             :  *      This function does not perform any catalog accesses.
      71                 :             :  *      Some callers rely on that!
      72                 :             :  */
      73                 :             : bool
      74                 :      189175 : IsSystemRelation(Relation relation)
      75                 :             : {
      76                 :      189175 :     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                 :      254425 : IsSystemClass(Oid relid, Form_pg_class reltuple)
      87                 :             : {
      88                 :             :     /* IsCatalogRelationOid is a bit faster, so test that first */
      89         [ +  + ]:      464288 :     return (IsCatalogRelationOid(relid) ||
      90   [ +  +  +  + ]:      464288 :             IsToastClass(reltuple) ||
      91                 :      177027 :             IsConflictLogTableClass(reltuple));
      92                 :             : }
      93                 :             : 
      94                 :             : /*
      95                 :             :  * IsCatalogRelation
      96                 :             :  *      True iff the relation is a system catalog.
      97                 :             :  *
      98                 :             :  *      By a system catalog, we mean one that is created during the bootstrap
      99                 :             :  *      phase of initdb.  That includes not just the catalogs per se, but
     100                 :             :  *      also their indexes, and TOAST tables and indexes if any.
     101                 :             :  *
     102                 :             :  *      This function does not perform any catalog accesses.
     103                 :             :  *      Some callers rely on that!
     104                 :             :  */
     105                 :             : bool
     106                 :    41275210 : IsCatalogRelation(Relation relation)
     107                 :             : {
     108                 :    41275210 :     return IsCatalogRelationOid(RelationGetRelid(relation));
     109                 :             : }
     110                 :             : 
     111                 :             : /*
     112                 :             :  * IsCatalogRelationOid
     113                 :             :  *      True iff the relation identified by this OID is a system catalog.
     114                 :             :  *
     115                 :             :  *      By a system catalog, we mean one that is created during the bootstrap
     116                 :             :  *      phase of initdb.  That includes not just the catalogs per se, but
     117                 :             :  *      also their indexes, and TOAST tables and indexes if any.
     118                 :             :  *
     119                 :             :  *      This function does not perform any catalog accesses.
     120                 :             :  *      Some callers rely on that!
     121                 :             :  */
     122                 :             : bool
     123                 :    66495424 : IsCatalogRelationOid(Oid relid)
     124                 :             : {
     125                 :             :     /*
     126                 :             :      * We consider a relation to be a system catalog if it has a pinned OID.
     127                 :             :      * This includes all the defined catalogs, their indexes, and their TOAST
     128                 :             :      * tables and indexes.
     129                 :             :      *
     130                 :             :      * This rule excludes the relations in information_schema, which are not
     131                 :             :      * integral to the system and can be treated the same as user relations.
     132                 :             :      * (Since it's valid to drop and recreate information_schema, any rule
     133                 :             :      * that did not act this way would be wrong.)
     134                 :             :      *
     135                 :             :      * This test is reliable since an OID wraparound will skip this range of
     136                 :             :      * OIDs; see GetNewObjectId().
     137                 :             :      */
     138                 :    66495424 :     return (relid < (Oid) FirstUnpinnedObjectId);
     139                 :             : }
     140                 :             : 
     141                 :             : /*
     142                 :             :  * IsCatalogTextUniqueIndexOid
     143                 :             :  *      True iff the relation identified by this OID is a catalog UNIQUE index
     144                 :             :  *      having a column of type "text".
     145                 :             :  *
     146                 :             :  *      The relcache must not use these indexes.  Inserting into any UNIQUE
     147                 :             :  *      index compares index keys while holding BUFFER_LOCK_EXCLUSIVE.
     148                 :             :  *      bttextcmp() can search the COLLOID catcache.  Depending on concurrent
     149                 :             :  *      invalidation traffic, catcache can reach relcache builds.  A backend
     150                 :             :  *      would self-deadlock on LWLocks if the relcache build read the
     151                 :             :  *      exclusive-locked buffer.
     152                 :             :  *
     153                 :             :  *      To avoid being itself the cause of self-deadlock, this doesn't read
     154                 :             :  *      catalogs.  Instead, it uses a hard-coded list with a supporting
     155                 :             :  *      regression test.
     156                 :             :  */
     157                 :             : bool
     158                 :         886 : IsCatalogTextUniqueIndexOid(Oid relid)
     159                 :             : {
     160         [ +  + ]:         886 :     switch (relid)
     161                 :             :     {
     162                 :          16 :         case ParameterAclParnameIndexId:
     163                 :             :         case ReplicationOriginNameIndex:
     164                 :             :         case SecLabelObjectIndexId:
     165                 :             :         case SharedSecLabelObjectIndexId:
     166                 :          16 :             return true;
     167                 :             :     }
     168                 :         870 :     return false;
     169                 :             : }
     170                 :             : 
     171                 :             : /*
     172                 :             :  * IsInplaceUpdateRelation
     173                 :             :  *      True iff core code performs inplace updates on the relation.
     174                 :             :  *
     175                 :             :  *      This is used for assertions and for making the executor follow the
     176                 :             :  *      locking protocol described at README.tuplock section "Locking to write
     177                 :             :  *      inplace-updated tables".  Extensions may inplace-update other heap
     178                 :             :  *      tables, but concurrent SQL UPDATE on the same table may overwrite
     179                 :             :  *      those modifications.
     180                 :             :  *
     181                 :             :  *      The executor can assume these are not partitions or partitioned and
     182                 :             :  *      have no triggers.
     183                 :             :  */
     184                 :             : bool
     185                 :      256165 : IsInplaceUpdateRelation(Relation relation)
     186                 :             : {
     187                 :      256165 :     return IsInplaceUpdateOid(RelationGetRelid(relation));
     188                 :             : }
     189                 :             : 
     190                 :             : /*
     191                 :             :  * IsInplaceUpdateOid
     192                 :             :  *      Like the above, but takes an OID as argument.
     193                 :             :  */
     194                 :             : bool
     195                 :      256165 : IsInplaceUpdateOid(Oid relid)
     196                 :             : {
     197   [ +  +  +  + ]:      256165 :     return (relid == RelationRelationId ||
     198                 :             :             relid == DatabaseRelationId);
     199                 :             : }
     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                 :     3468295 : IsToastRelation(Relation relation)
     209                 :             : {
     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                 :     3468295 :     return IsToastNamespace(RelationGetNamespace(relation));
     219                 :             : }
     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                 :      209863 : IsToastClass(Form_pg_class reltuple)
     229                 :             : {
     230                 :      209863 :     Oid         relnamespace = reltuple->relnamespace;
     231                 :             : 
     232                 :      209863 :     return IsToastNamespace(relnamespace);
     233                 :             : }
     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                 :      582353 : IsConflictLogTableClass(Form_pg_class reltuple)
     243                 :             : {
     244                 :      582353 :     Oid         relnamespace = reltuple->relnamespace;
     245                 :             : 
     246                 :      582353 :     return IsConflictLogTableNamespace(relnamespace);
     247                 :             : }
     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                 :      143985 : IsCatalogNamespace(Oid namespaceId)
     260                 :             : {
     261                 :      143985 :     return namespaceId == PG_CATALOG_NAMESPACE;
     262                 :             : }
     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                 :     3741048 : IsToastNamespace(Oid namespaceId)
     278                 :             : {
     279   [ +  +  +  + ]:     7421191 :     return (namespaceId == PG_TOAST_NAMESPACE) ||
     280                 :     3680143 :         isTempToastNamespace(namespaceId);
     281                 :             : }
     282                 :             : 
     283                 :             : /*
     284                 :             :  * IsConflictLogTableNamespace
     285                 :             :  *      True iff namespace is pg_conflict.
     286                 :             :  *
     287                 :             :  *      Does not perform any catalog accesses.
     288                 :             :  */
     289                 :             : bool
     290                 :      795182 : IsConflictLogTableNamespace(Oid namespaceId)
     291                 :             : {
     292                 :      795182 :     return namespaceId == PG_CONFLICT_NAMESPACE;
     293                 :             : }
     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                 :        2446 : IsReservedName(const char *name)
     306                 :             : {
     307                 :             :     /* ugly coding for speed */
     308                 :        2519 :     return (name[0] == 'p' &&
     309   [ +  +  +  + ]:        2460 :             name[1] == 'g' &&
     310         [ +  + ]:          14 :             name[2] == '_');
     311                 :             : }
     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                 :    28093109 : IsSharedRelation(Oid relationId)
     332                 :             : {
     333                 :             :     /* These are the shared catalogs (look for BKI_SHARED_RELATION) */
     334   [ +  +  +  + ]:    28093109 :     if (relationId == AuthIdRelationId ||
     335         [ +  + ]:    27962246 :         relationId == AuthMemRelationId ||
     336         [ +  + ]:    27865178 :         relationId == DatabaseRelationId ||
     337         [ +  + ]:    27839212 :         relationId == DbRoleSettingRelationId ||
     338         [ +  + ]:    27818277 :         relationId == ParameterAclRelationId ||
     339         [ +  + ]:    27799422 :         relationId == ReplicationOriginRelationId ||
     340         [ +  + ]:    27495387 :         relationId == SharedDependRelationId ||
     341         [ +  + ]:    27491687 :         relationId == SharedDescriptionRelationId ||
     342         [ +  + ]:    27484538 :         relationId == SharedSecLabelRelationId ||
     343         [ +  + ]:    27461228 :         relationId == SubscriptionRelationId ||
     344                 :             :         relationId == TableSpaceRelationId)
     345                 :      661566 :         return true;
     346                 :             :     /* These are their indexes */
     347   [ +  +  +  + ]:    27431543 :     if (relationId == AuthIdOidIndexId ||
     348         [ +  + ]:    27363126 :         relationId == AuthIdRolnameIndexId ||
     349         [ +  + ]:    27352382 :         relationId == AuthMemMemRoleIndexId ||
     350         [ +  + ]:    27346577 :         relationId == AuthMemRoleMemIndexId ||
     351         [ +  + ]:    27344116 :         relationId == AuthMemOidIndexId ||
     352         [ +  + ]:    27342255 :         relationId == AuthMemGrantorIndexId ||
     353         [ +  + ]:    27319068 :         relationId == DatabaseNameIndexId ||
     354         [ +  + ]:    27269022 :         relationId == DatabaseOidIndexId ||
     355         [ +  + ]:    27188956 :         relationId == DbRoleSettingDatidRolidIndexId ||
     356         [ +  + ]:    27182257 :         relationId == ParameterAclOidIndexId ||
     357         [ +  + ]:    27175594 :         relationId == ParameterAclParnameIndexId ||
     358         [ +  + ]:    27170375 :         relationId == ReplicationOriginIdentIndex ||
     359         [ +  + ]:    27165339 :         relationId == ReplicationOriginNameIndex ||
     360         [ +  + ]:    26996641 :         relationId == SharedDependDependerIndexId ||
     361         [ +  + ]:    26988018 :         relationId == SharedDependReferenceIndexId ||
     362         [ +  + ]:    26985038 :         relationId == SharedDescriptionObjIndexId ||
     363         [ +  + ]:    26978622 :         relationId == SharedSecLabelObjectIndexId ||
     364         [ +  + ]:    26972892 :         relationId == SubscriptionNameIndexId ||
     365         [ +  + ]:    26966670 :         relationId == SubscriptionObjectIndexId ||
     366         [ +  + ]:    26963899 :         relationId == TablespaceNameIndexId ||
     367                 :             :         relationId == TablespaceOidIndexId)
     368                 :      482661 :         return true;
     369                 :             :     /* These are their toast tables and toast indexes */
     370   [ +  +  +  + ]:    26948882 :     if (relationId == PgDatabaseToastTable ||
     371         [ +  + ]:    26946085 :         relationId == PgDatabaseToastIndex ||
     372         [ +  + ]:    26944731 :         relationId == PgDbRoleSettingToastTable ||
     373         [ +  + ]:    26943558 :         relationId == PgDbRoleSettingToastIndex ||
     374         [ +  + ]:    26942201 :         relationId == PgParameterAclToastTable ||
     375         [ +  + ]:    26941025 :         relationId == PgParameterAclToastIndex ||
     376         [ +  + ]:    26939637 :         relationId == PgShdescriptionToastTable ||
     377         [ +  + ]:    26938432 :         relationId == PgShdescriptionToastIndex ||
     378         [ +  + ]:    26937075 :         relationId == PgShseclabelToastTable ||
     379         [ +  + ]:    26935899 :         relationId == PgShseclabelToastIndex ||
     380         [ +  + ]:    26934542 :         relationId == PgSubscriptionToastTable ||
     381         [ +  + ]:    26933366 :         relationId == PgSubscriptionToastIndex ||
     382         [ +  + ]:    26931983 :         relationId == PgTablespaceToastTable ||
     383                 :             :         relationId == PgTablespaceToastIndex)
     384                 :       18102 :         return true;
     385                 :    26930780 :     return false;
     386                 :             : }
     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                 :     1730922 : IsPinnedObject(Oid classId, Oid objectId)
     398                 :             : {
     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         [ +  + ]:     1730922 :     if (objectId >= FirstUnpinnedObjectId)
     405                 :      533490 :         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         [ +  + ]:     1197432 :     if (classId == LargeObjectRelationId)
     412                 :          36 :         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   [ +  +  +  + ]:     1197396 :     if (classId == NamespaceRelationId &&
     428                 :             :         objectId == PG_PUBLIC_NAMESPACE)
     429                 :       37343 :         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         [ -  + ]:     1160053 :     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                 :     1160053 :     return true;
     448                 :             : }
     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                 :      570443 : GetNewOidWithIndex(Relation relation, Oid indexId, AttrNumber oidcolumn)
     476                 :             : {
     477                 :             :     Oid         newOid;
     478                 :             :     SysScanDesc scan;
     479                 :             :     ScanKeyData key;
     480                 :             :     bool        collides;
     481                 :      570443 :     uint64      retries = 0;
     482                 :      570443 :     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         [ +  + ]:      570443 :     if (IsBootstrapProcessingMode())
     489                 :        7068 :         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         [ -  + ]:      563375 :         CHECK_FOR_INTERRUPTS();
     503                 :             : 
     504                 :      563375 :         newOid = GetNewObjectId();
     505                 :             : 
     506                 :      563375 :         ScanKeyInit(&key,
     507                 :             :                     oidcolumn,
     508                 :             :                     BTEqualStrategyNumber, F_OIDEQ,
     509                 :             :                     ObjectIdGetDatum(newOid));
     510                 :             : 
     511                 :             :         /* see notes above about using SnapshotAny */
     512                 :      563375 :         scan = systable_beginscan(relation, indexId, true,
     513                 :             :                                   SnapshotAny, 1, &key);
     514                 :             : 
     515                 :      563375 :         collides = HeapTupleIsValid(systable_getnext(scan));
     516                 :             : 
     517                 :      563375 :         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         [ -  + ]:      563375 :         if (retries >= retries_before_log)
     529                 :             :         {
     530         [ #  # ]:           0 :             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         [ #  # ]:           0 :             if (retries_before_log * 2 <= GETNEWOID_LOG_MAX_INTERVAL)
     543                 :           0 :                 retries_before_log *= 2;
     544                 :             :             else
     545                 :           0 :                 retries_before_log += GETNEWOID_LOG_MAX_INTERVAL;
     546                 :             :         }
     547                 :             : 
     548                 :      563375 :         retries++;
     549         [ -  + ]:      563375 :     } while (collides);
     550                 :             : 
     551                 :             :     /*
     552                 :             :      * If at least one log message is emitted, also log the completion of OID
     553                 :             :      * assignment.
     554                 :             :      */
     555         [ -  + ]:      563375 :     if (retries > GETNEWOID_LOG_THRESHOLD)
     556                 :             :     {
     557         [ #  # ]:           0 :         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                 :      563375 :     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                 :       81314 : GetNewRelFileNumber(Oid reltablespace, Relation pg_class, char relpersistence)
     585                 :             : {
     586                 :             :     RelFileLocatorBackend rlocator;
     587                 :             :     RelPathStr  rpath;
     588                 :             :     bool        collides;
     589                 :             :     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                 :             :     Assert(!IsBinaryUpgrade);
     597                 :             : 
     598      [ +  +  - ]:       81314 :     switch (relpersistence)
     599                 :             :     {
     600                 :        4828 :         case RELPERSISTENCE_TEMP:
     601         [ +  - ]:        4828 :             procNumber = ProcNumberForTempRelations();
     602                 :        4828 :             break;
     603                 :       76486 :         case RELPERSISTENCE_UNLOGGED:
     604                 :             :         case RELPERSISTENCE_PERMANENT:
     605                 :       76486 :             procNumber = INVALID_PROC_NUMBER;
     606                 :       76486 :             break;
     607                 :           0 :         default:
     608         [ #  # ]:           0 :             elog(ERROR, "invalid relpersistence: %c", relpersistence);
     609                 :             :             return InvalidRelFileNumber;    /* placate compiler */
     610                 :             :     }
     611                 :             : 
     612                 :             :     /* This logic should match RelationInitPhysicalAddr */
     613         [ +  + ]:       81314 :     rlocator.locator.spcOid = reltablespace ? reltablespace : MyDatabaseTableSpace;
     614                 :       81314 :     rlocator.locator.dbOid =
     615                 :       81314 :         (rlocator.locator.spcOid == GLOBALTABLESPACE_OID) ?
     616         [ +  + ]:       81314 :         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                 :       81314 :     rlocator.backend = procNumber;
     624                 :             : 
     625                 :             :     do
     626                 :             :     {
     627         [ -  + ]:       81314 :         CHECK_FOR_INTERRUPTS();
     628                 :             : 
     629                 :             :         /* Generate the OID */
     630         [ +  + ]:       81314 :         if (pg_class)
     631                 :       73021 :             rlocator.locator.relNumber = GetNewOidWithIndex(pg_class, ClassOidIndexId,
     632                 :             :                                                             Anum_pg_class_oid);
     633                 :             :         else
     634                 :        8293 :             rlocator.locator.relNumber = GetNewObjectId();
     635                 :             : 
     636                 :             :         /* Check for existing file of same name */
     637                 :       81314 :         rpath = relpath(rlocator, MAIN_FORKNUM);
     638                 :             : 
     639         [ -  + ]:       81314 :         if (access(rpath.str, F_OK) == 0)
     640                 :             :         {
     641                 :             :             /* definite collision */
     642                 :           0 :             collides = true;
     643                 :             :         }
     644                 :             :         else
     645                 :             :         {
     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                 :       81314 :             collides = false;
     654                 :             :         }
     655         [ -  + ]:       81314 :     } while (collides);
     656                 :             : 
     657                 :       81314 :     return rlocator.locator.relNumber;
     658                 :             : }
     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                 :           0 : pg_nextoid(PG_FUNCTION_ARGS)
     669                 :             : {
     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                 :             :     Relation    rel;
     674                 :             :     Relation    idx;
     675                 :             :     HeapTuple   atttuple;
     676                 :             :     Form_pg_attribute attform;
     677                 :             :     AttrNumber  attno;
     678                 :             :     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                 :             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     689                 :             :                  errmsg("must be superuser to call %s()",
     690                 :             :                         "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                 :             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     698                 :             :                  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                 :             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     703                 :             :                  errmsg("index \"%s\" does not belong to table \"%s\"",
     704                 :             :                         RelationGetRelationName(idx),
     705                 :             :                         RelationGetRelationName(rel))));
     706                 :             : 
     707                 :           0 :     atttuple = SearchSysCacheAttName(reloid, NameStr(*attname));
     708         [ #  # ]:           0 :     if (!HeapTupleIsValid(atttuple))
     709         [ #  # ]:           0 :         ereport(ERROR,
     710                 :             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
     711                 :             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
     712                 :             :                         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                 :             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     720                 :             :                  errmsg("column \"%s\" is not of type oid",
     721                 :             :                         NameStr(*attname))));
     722                 :             : 
     723         [ #  # ]:           0 :     if (IndexRelationGetNumberOfKeyAttributes(idx) != 1 ||
     724         [ #  # ]:           0 :         idx->rd_index->indkey.values[0] != attno)
     725         [ #  # ]:           0 :         ereport(ERROR,
     726                 :             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     727                 :             :                  errmsg("index \"%s\" is not the index for column \"%s\"",
     728                 :             :                         RelationGetRelationName(idx),
     729                 :             :                         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                 :             : }
     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                 :          55 : pg_stop_making_pinned_objects(PG_FUNCTION_ARGS)
     748                 :             : {
     749                 :             :     /*
     750                 :             :      * Belt-and-suspenders check, since StopGeneratingPinnedObjectIds will
     751                 :             :      * fail anyway in non-single-user mode.
     752                 :             :      */
     753         [ -  + ]:          55 :     if (!superuser())
     754         [ #  # ]:           0 :         ereport(ERROR,
     755                 :             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     756                 :             :                  errmsg("must be superuser to call %s()",
     757                 :             :                         "pg_stop_making_pinned_objects")));
     758                 :             : 
     759                 :          55 :     StopGeneratingPinnedObjectIds();
     760                 :             : 
     761                 :          55 :     PG_RETURN_VOID();
     762                 :             : }
        

Generated by: LCOV version 2.0-1