LCOV - differential code coverage report
Current view: top level - src/backend/utils/cache - relfilenumbermap.c (source / functions) Coverage Total Hit UIC GIC GNC
Current: Differential Code Coverage HEAD vs 15 Lines: 93.9 % 66 62 4 33 29
Current Date: 2023-04-08 15:15:32 Functions: 100.0 % 3 3 3
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

           TLA  Line data    Source code
       1                 : /*-------------------------------------------------------------------------
       2                 :  *
       3                 :  * relfilenumbermap.c
       4                 :  *    relfilenumber to oid mapping cache.
       5                 :  *
       6                 :  * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
       7                 :  * Portions Copyright (c) 1994, Regents of the University of California
       8                 :  *
       9                 :  * IDENTIFICATION
      10                 :  *    src/backend/utils/cache/relfilenumbermap.c
      11                 :  *
      12                 :  *-------------------------------------------------------------------------
      13                 :  */
      14                 : #include "postgres.h"
      15                 : 
      16                 : #include "access/genam.h"
      17                 : #include "access/htup_details.h"
      18                 : #include "access/table.h"
      19                 : #include "catalog/pg_class.h"
      20                 : #include "catalog/pg_tablespace.h"
      21                 : #include "miscadmin.h"
      22                 : #include "utils/builtins.h"
      23                 : #include "utils/catcache.h"
      24                 : #include "utils/fmgroids.h"
      25                 : #include "utils/hsearch.h"
      26                 : #include "utils/inval.h"
      27                 : #include "utils/rel.h"
      28                 : #include "utils/relfilenumbermap.h"
      29                 : #include "utils/relmapper.h"
      30                 : 
      31                 : /* Hash table for information about each relfilenumber <-> oid pair */
      32                 : static HTAB *RelfilenumberMapHash = NULL;
      33                 : 
      34                 : /* built first time through in InitializeRelfilenumberMap */
      35                 : static ScanKeyData relfilenumber_skey[2];
      36                 : 
      37                 : typedef struct
      38                 : {
      39                 :     Oid         reltablespace;
      40                 :     RelFileNumber relfilenumber;
      41                 : } RelfilenumberMapKey;
      42                 : 
      43                 : typedef struct
      44                 : {
      45                 :     RelfilenumberMapKey key;    /* lookup key - must be first */
      46                 :     Oid         relid;          /* pg_class.oid */
      47                 : } RelfilenumberMapEntry;
      48                 : 
      49                 : /*
      50                 :  * RelfilenumberMapInvalidateCallback
      51                 :  *      Flush mapping entries when pg_class is updated in a relevant fashion.
      52                 :  */
      53                 : static void
      54 GNC       17468 : RelfilenumberMapInvalidateCallback(Datum arg, Oid relid)
      55                 : {
      56                 :     HASH_SEQ_STATUS status;
      57                 :     RelfilenumberMapEntry *entry;
      58                 : 
      59                 :     /* callback only gets registered after creating the hash */
      60           17468 :     Assert(RelfilenumberMapHash != NULL);
      61                 : 
      62           17468 :     hash_seq_init(&status, RelfilenumberMapHash);
      63         9429596 :     while ((entry = (RelfilenumberMapEntry *) hash_seq_search(&status)) != NULL)
      64                 :     {
      65                 :         /*
      66                 :          * If relid is InvalidOid, signaling a complete reset, we must remove
      67                 :          * all entries, otherwise just remove the specific relation's entry.
      68                 :          * Always remove negative cache entries.
      69                 :          */
      70 GIC     9412128 :         if (relid == InvalidOid ||  /* complete reset */
      71         9411735 :             entry->relid == InvalidOid ||    /* negative cache entry */
      72         9411682 :             entry->relid == relid)   /* individual flushed relation */
      73                 :         {
      74 GNC         670 :             if (hash_search(RelfilenumberMapHash,
      75             670 :                             &entry->key,
      76                 :                             HASH_REMOVE,
      77                 :                             NULL) == NULL)
      78 UIC           0 :                 elog(ERROR, "hash table corrupted");
      79                 :         }
      80                 :     }
      81 GIC       17468 : }
      82                 : 
      83                 : /*
      84                 :  * InitializeRelfilenumberMap
      85                 :  *      Initialize cache, either on first use or after a reset.
      86                 :  */
      87                 : static void
      88 GNC         163 : InitializeRelfilenumberMap(void)
      89                 : {
      90                 :     HASHCTL     ctl;
      91                 :     int         i;
      92                 : 
      93                 :     /* Make sure we've initialized CacheMemoryContext. */
      94 GIC         163 :     if (CacheMemoryContext == NULL)
      95 UIC           0 :         CreateCacheMemoryContext();
      96                 : 
      97                 :     /* build skey */
      98 GNC        3097 :     MemSet(&relfilenumber_skey, 0, sizeof(relfilenumber_skey));
      99                 : 
     100 GIC         489 :     for (i = 0; i < 2; i++)
     101                 :     {
     102             326 :         fmgr_info_cxt(F_OIDEQ,
     103                 :                       &relfilenumber_skey[i].sk_func,
     104                 :                       CacheMemoryContext);
     105 GNC         326 :         relfilenumber_skey[i].sk_strategy = BTEqualStrategyNumber;
     106             326 :         relfilenumber_skey[i].sk_subtype = InvalidOid;
     107             326 :         relfilenumber_skey[i].sk_collation = InvalidOid;
     108                 :     }
     109                 : 
     110             163 :     relfilenumber_skey[0].sk_attno = Anum_pg_class_reltablespace;
     111             163 :     relfilenumber_skey[1].sk_attno = Anum_pg_class_relfilenode;
     112                 : 
     113                 :     /*
     114                 :      * Only create the RelfilenumberMapHash now, so we don't end up partially
     115                 :      * initialized when fmgr_info_cxt() above ERRORs out with an out of memory
     116                 :      * error.
     117                 :      */
     118             163 :     ctl.keysize = sizeof(RelfilenumberMapKey);
     119             163 :     ctl.entrysize = sizeof(RelfilenumberMapEntry);
     120 GIC         163 :     ctl.hcxt = CacheMemoryContext;
     121                 : 
     122 GNC         163 :     RelfilenumberMapHash =
     123             163 :         hash_create("RelfilenumberMap cache", 64, &ctl,
     124                 :                     HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
     125                 : 
     126                 :     /* Watch for invalidation events. */
     127             163 :     CacheRegisterRelcacheCallback(RelfilenumberMapInvalidateCallback,
     128                 :                                   (Datum) 0);
     129 GIC         163 : }
     130                 : 
     131                 : /*
     132                 :  * Map a relation's (tablespace, relfilenumber) to a relation's oid and cache
     133                 :  * the result.
     134                 :  *
     135                 :  * Returns InvalidOid if no relation matching the criteria could be found.
     136                 :  */
     137                 : Oid
     138 GNC      343084 : RelidByRelfilenumber(Oid reltablespace, RelFileNumber relfilenumber)
     139                 : {
     140                 :     RelfilenumberMapKey key;
     141                 :     RelfilenumberMapEntry *entry;
     142                 :     bool        found;
     143                 :     SysScanDesc scandesc;
     144                 :     Relation    relation;
     145                 :     HeapTuple   ntp;
     146                 :     ScanKeyData skey[2];
     147                 :     Oid         relid;
     148                 : 
     149          343084 :     if (RelfilenumberMapHash == NULL)
     150             163 :         InitializeRelfilenumberMap();
     151                 : 
     152                 :     /* pg_class will show 0 when the value is actually MyDatabaseTableSpace */
     153 GIC      343084 :     if (reltablespace == MyDatabaseTableSpace)
     154          339354 :         reltablespace = 0;
     155                 : 
     156          686168 :     MemSet(&key, 0, sizeof(key));
     157          343084 :     key.reltablespace = reltablespace;
     158 GNC      343084 :     key.relfilenumber = relfilenumber;
     159                 : 
     160                 :     /*
     161                 :      * Check cache and return entry if one is found. Even if no target
     162                 :      * relation can be found later on we store the negative match and return a
     163                 :      * InvalidOid from cache. That's not really necessary for performance
     164                 :      * since querying invalid values isn't supposed to be a frequent thing,
     165                 :      * but it's basically free.
     166                 :      */
     167          343084 :     entry = hash_search(RelfilenumberMapHash, &key, HASH_FIND, &found);
     168                 : 
     169 GIC      343084 :     if (found)
     170          338349 :         return entry->relid;
     171                 : 
     172                 :     /* ok, no previous cache entry, do it the hard way */
     173                 : 
     174                 :     /* initialize empty/negative cache entry before doing the actual lookups */
     175            4735 :     relid = InvalidOid;
     176                 : 
     177            4735 :     if (reltablespace == GLOBALTABLESPACE_OID)
     178                 :     {
     179                 :         /*
     180                 :          * Ok, shared table, check relmapper.
     181                 :          */
     182 GNC         157 :         relid = RelationMapFilenumberToOid(relfilenumber, true);
     183                 :     }
     184                 :     else
     185                 :     {
     186                 :         /*
     187                 :          * Not a shared table, could either be a plain relation or a
     188                 :          * non-shared, nailed one, like e.g. pg_class.
     189                 :          */
     190                 : 
     191                 :         /* check for plain relations by looking in pg_class */
     192 GIC        4578 :         relation = table_open(RelationRelationId, AccessShareLock);
     193                 : 
     194                 :         /* copy scankey to local copy, it will be modified during the scan */
     195 GNC        4575 :         memcpy(skey, relfilenumber_skey, sizeof(skey));
     196                 : 
     197                 :         /* set scan arguments */
     198 GIC        4575 :         skey[0].sk_argument = ObjectIdGetDatum(reltablespace);
     199 GNC        4575 :         skey[1].sk_argument = ObjectIdGetDatum(relfilenumber);
     200                 : 
     201 GIC        4575 :         scandesc = systable_beginscan(relation,
     202                 :                                       ClassTblspcRelfilenodeIndexId,
     203                 :                                       true,
     204                 :                                       NULL,
     205                 :                                       2,
     206                 :                                       skey);
     207                 : 
     208            4575 :         found = false;
     209                 : 
     210            8790 :         while (HeapTupleIsValid(ntp = systable_getnext(scandesc)))
     211                 :         {
     212            4215 :             Form_pg_class classform = (Form_pg_class) GETSTRUCT(ntp);
     213                 : 
     214            4215 :             if (found)
     215 UIC           0 :                 elog(ERROR,
     216                 :                      "unexpected duplicate for tablespace %u, relfilenumber %u",
     217                 :                      reltablespace, relfilenumber);
     218 GIC        4215 :             found = true;
     219                 : 
     220            4215 :             Assert(classform->reltablespace == reltablespace);
     221 GNC        4215 :             Assert(classform->relfilenode == relfilenumber);
     222 GIC        4215 :             relid = classform->oid;
     223                 :         }
     224                 : 
     225            4571 :         systable_endscan(scandesc);
     226            4571 :         table_close(relation, AccessShareLock);
     227                 : 
     228                 :         /* check for tables that are mapped but not shared */
     229            4571 :         if (!found)
     230 GNC         356 :             relid = RelationMapFilenumberToOid(relfilenumber, false);
     231                 :     }
     232                 : 
     233                 :     /*
     234                 :      * Only enter entry into cache now, our opening of pg_class could have
     235                 :      * caused cache invalidations to be executed which would have deleted a
     236                 :      * new entry if we had entered it above.
     237                 :      */
     238            4728 :     entry = hash_search(RelfilenumberMapHash, &key, HASH_ENTER, &found);
     239 GIC        4728 :     if (found)
     240 UIC           0 :         elog(ERROR, "corrupted hashtable");
     241 GIC        4728 :     entry->relid = relid;
     242                 : 
     243            4728 :     return relid;
     244                 : }
        

Generated by: LCOV version v1.16-55-g56c0a2a