LCOV - code coverage report
Current view: top level - src/backend/utils/cache - attoptcache.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 48 50 96.0 %
Date: 2024-12-02 21:15:08 Functions: 4 4 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * attoptcache.c
       4             :  *    Attribute options cache management.
       5             :  *
       6             :  * Attribute options are cached separately from the fixed-size portion of
       7             :  * pg_attribute entries, which are handled by the relcache.
       8             :  *
       9             :  * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
      10             :  * Portions Copyright (c) 1994, Regents of the University of California
      11             :  *
      12             :  * IDENTIFICATION
      13             :  *    src/backend/utils/cache/attoptcache.c
      14             :  *
      15             :  *-------------------------------------------------------------------------
      16             :  */
      17             : #include "postgres.h"
      18             : 
      19             : #include "access/reloptions.h"
      20             : #include "utils/attoptcache.h"
      21             : #include "utils/catcache.h"
      22             : #include "utils/hsearch.h"
      23             : #include "utils/inval.h"
      24             : #include "utils/syscache.h"
      25             : #include "varatt.h"
      26             : 
      27             : 
      28             : /* Hash table for information about each attribute's options */
      29             : static HTAB *AttoptCacheHash = NULL;
      30             : 
      31             : /* attrelid and attnum form the lookup key, and must appear first */
      32             : typedef struct
      33             : {
      34             :     Oid         attrelid;
      35             :     int         attnum;
      36             : } AttoptCacheKey;
      37             : 
      38             : typedef struct
      39             : {
      40             :     AttoptCacheKey key;         /* lookup key - must be first */
      41             :     AttributeOpts *opts;        /* options, or NULL if none */
      42             : } AttoptCacheEntry;
      43             : 
      44             : 
      45             : /*
      46             :  * InvalidateAttoptCacheCallback
      47             :  *      Flush cache entry (or entries) when pg_attribute is updated.
      48             :  *
      49             :  * When pg_attribute is updated, we must flush the cache entry at least
      50             :  * for that attribute.
      51             :  */
      52             : static void
      53      798018 : InvalidateAttoptCacheCallback(Datum arg, int cacheid, uint32 hashvalue)
      54             : {
      55             :     HASH_SEQ_STATUS status;
      56             :     AttoptCacheEntry *attopt;
      57             : 
      58             :     /*
      59             :      * By convention, zero hash value is passed to the callback as a sign that
      60             :      * it's time to invalidate the whole cache. See sinval.c, inval.c and
      61             :      * InvalidateSystemCachesExtended().
      62             :      */
      63      798018 :     if (hashvalue == 0)
      64          40 :         hash_seq_init(&status, AttoptCacheHash);
      65             :     else
      66      797978 :         hash_seq_init_with_hash_value(&status, AttoptCacheHash, hashvalue);
      67             : 
      68      801882 :     while ((attopt = (AttoptCacheEntry *) hash_seq_search(&status)) != NULL)
      69             :     {
      70        3864 :         if (attopt->opts)
      71           6 :             pfree(attopt->opts);
      72        3864 :         if (hash_search(AttoptCacheHash,
      73        3864 :                         &attopt->key,
      74             :                         HASH_REMOVE,
      75             :                         NULL) == NULL)
      76           0 :             elog(ERROR, "hash table corrupted");
      77             :     }
      78      798018 : }
      79             : 
      80             : /*
      81             :  * Hash function compatible with two-arg system cache hash function.
      82             :  */
      83             : static uint32
      84      139890 : relatt_cache_syshash(const void *key, Size keysize)
      85             : {
      86      139890 :     const AttoptCacheKey *ckey = key;
      87             : 
      88             :     Assert(keysize == sizeof(*ckey));
      89      139890 :     return GetSysCacheHashValue2(ATTNUM, ckey->attrelid, ckey->attnum);
      90             : }
      91             : 
      92             : /*
      93             :  * InitializeAttoptCache
      94             :  *      Initialize the attribute options cache.
      95             :  */
      96             : static void
      97         518 : InitializeAttoptCache(void)
      98             : {
      99             :     HASHCTL     ctl;
     100             : 
     101             :     /* Initialize the hash table. */
     102         518 :     ctl.keysize = sizeof(AttoptCacheKey);
     103         518 :     ctl.entrysize = sizeof(AttoptCacheEntry);
     104             : 
     105             :     /*
     106             :      * AttoptCacheEntry takes hash value from the system cache. For
     107             :      * AttoptCacheHash we use the same hash in order to speedup search by hash
     108             :      * value. This is used by hash_seq_init_with_hash_value().
     109             :      */
     110         518 :     ctl.hash = relatt_cache_syshash;
     111             : 
     112         518 :     AttoptCacheHash =
     113         518 :         hash_create("Attopt cache", 256, &ctl,
     114             :                     HASH_ELEM | HASH_FUNCTION);
     115             : 
     116             :     /* Make sure we've initialized CacheMemoryContext. */
     117         518 :     if (!CacheMemoryContext)
     118           0 :         CreateCacheMemoryContext();
     119             : 
     120             :     /* Watch for invalidation events. */
     121         518 :     CacheRegisterSyscacheCallback(ATTNUM,
     122             :                                   InvalidateAttoptCacheCallback,
     123             :                                   (Datum) 0);
     124         518 : }
     125             : 
     126             : /*
     127             :  * get_attribute_options
     128             :  *      Fetch attribute options for a specified table OID.
     129             :  */
     130             : AttributeOpts *
     131       70274 : get_attribute_options(Oid attrelid, int attnum)
     132             : {
     133             :     AttoptCacheKey key;
     134             :     AttoptCacheEntry *attopt;
     135             :     AttributeOpts *result;
     136             :     HeapTuple   tp;
     137             : 
     138             :     /* Find existing cache entry, if any. */
     139       70274 :     if (!AttoptCacheHash)
     140         518 :         InitializeAttoptCache();
     141       70274 :     memset(&key, 0, sizeof(key));   /* make sure any padding bits are unset */
     142       70274 :     key.attrelid = attrelid;
     143       70274 :     key.attnum = attnum;
     144             :     attopt =
     145       70274 :         (AttoptCacheEntry *) hash_search(AttoptCacheHash,
     146             :                                          &key,
     147             :                                          HASH_FIND,
     148             :                                          NULL);
     149             : 
     150             :     /* Not found in Attopt cache.  Construct new cache entry. */
     151       70274 :     if (!attopt)
     152             :     {
     153             :         AttributeOpts *opts;
     154             : 
     155       65752 :         tp = SearchSysCache2(ATTNUM,
     156             :                              ObjectIdGetDatum(attrelid),
     157             :                              Int16GetDatum(attnum));
     158             : 
     159             :         /*
     160             :          * If we don't find a valid HeapTuple, it must mean someone has
     161             :          * managed to request attribute details for a non-existent attribute.
     162             :          * We treat that case as if no options were specified.
     163             :          */
     164       65752 :         if (!HeapTupleIsValid(tp))
     165          54 :             opts = NULL;
     166             :         else
     167             :         {
     168             :             Datum       datum;
     169             :             bool        isNull;
     170             : 
     171       65698 :             datum = SysCacheGetAttr(ATTNUM,
     172             :                                     tp,
     173             :                                     Anum_pg_attribute_attoptions,
     174             :                                     &isNull);
     175       65698 :             if (isNull)
     176       65692 :                 opts = NULL;
     177             :             else
     178             :             {
     179           6 :                 bytea      *bytea_opts = attribute_reloptions(datum, false);
     180             : 
     181           6 :                 opts = MemoryContextAlloc(CacheMemoryContext,
     182           6 :                                           VARSIZE(bytea_opts));
     183           6 :                 memcpy(opts, bytea_opts, VARSIZE(bytea_opts));
     184             :             }
     185       65698 :             ReleaseSysCache(tp);
     186             :         }
     187             : 
     188             :         /*
     189             :          * It's important to create the actual cache entry only after reading
     190             :          * pg_attribute, since the read could cause a cache flush.
     191             :          */
     192       65752 :         attopt = (AttoptCacheEntry *) hash_search(AttoptCacheHash,
     193             :                                                   &key,
     194             :                                                   HASH_ENTER,
     195             :                                                   NULL);
     196       65752 :         attopt->opts = opts;
     197             :     }
     198             : 
     199             :     /* Return results in caller's memory context. */
     200       70274 :     if (attopt->opts == NULL)
     201       70268 :         return NULL;
     202           6 :     result = palloc(VARSIZE(attopt->opts));
     203           6 :     memcpy(result, attopt->opts, VARSIZE(attopt->opts));
     204           6 :     return result;
     205             : }

Generated by: LCOV version 1.14