LCOV - code coverage report
Current view: top level - src/backend/utils/cache - attoptcache.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 95.9 % 49 47
Test Date: 2026-03-27 22:16:19 Functions: 100.0 % 4 4
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-2026, 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       721906 : InvalidateAttoptCacheCallback(Datum arg, SysCacheIdentifier cacheid,
      54              :                               uint32 hashvalue)
      55              : {
      56              :     HASH_SEQ_STATUS status;
      57              :     AttoptCacheEntry *attopt;
      58              : 
      59              :     /*
      60              :      * By convention, zero hash value is passed to the callback as a sign that
      61              :      * it's time to invalidate the whole cache. See sinval.c, inval.c and
      62              :      * InvalidateSystemCachesExtended().
      63              :      */
      64       721906 :     if (hashvalue == 0)
      65           44 :         hash_seq_init(&status, AttoptCacheHash);
      66              :     else
      67       721862 :         hash_seq_init_with_hash_value(&status, AttoptCacheHash, hashvalue);
      68              : 
      69       725229 :     while ((attopt = (AttoptCacheEntry *) hash_seq_search(&status)) != NULL)
      70              :     {
      71         3323 :         if (attopt->opts)
      72            4 :             pfree(attopt->opts);
      73         3323 :         if (hash_search(AttoptCacheHash,
      74         3323 :                         &attopt->key,
      75              :                         HASH_REMOVE,
      76              :                         NULL) == NULL)
      77            0 :             elog(ERROR, "hash table corrupted");
      78              :     }
      79       721906 : }
      80              : 
      81              : /*
      82              :  * Hash function compatible with two-arg system cache hash function.
      83              :  */
      84              : static uint32
      85        98370 : relatt_cache_syshash(const void *key, Size keysize)
      86              : {
      87        98370 :     const AttoptCacheKey *ckey = key;
      88              : 
      89              :     Assert(keysize == sizeof(*ckey));
      90        98370 :     return GetSysCacheHashValue2(ATTNUM, ObjectIdGetDatum(ckey->attrelid), Int32GetDatum(ckey->attnum));
      91              : }
      92              : 
      93              : /*
      94              :  * InitializeAttoptCache
      95              :  *      Initialize the attribute options cache.
      96              :  */
      97              : static void
      98          379 : InitializeAttoptCache(void)
      99              : {
     100              :     HASHCTL     ctl;
     101              : 
     102              :     /* Initialize the hash table. */
     103          379 :     ctl.keysize = sizeof(AttoptCacheKey);
     104          379 :     ctl.entrysize = sizeof(AttoptCacheEntry);
     105              : 
     106              :     /*
     107              :      * AttoptCacheEntry takes hash value from the system cache. For
     108              :      * AttoptCacheHash we use the same hash in order to speedup search by hash
     109              :      * value. This is used by hash_seq_init_with_hash_value().
     110              :      */
     111          379 :     ctl.hash = relatt_cache_syshash;
     112              : 
     113          379 :     AttoptCacheHash =
     114          379 :         hash_create("Attopt cache", 256, &ctl,
     115              :                     HASH_ELEM | HASH_FUNCTION);
     116              : 
     117              :     /* Make sure we've initialized CacheMemoryContext. */
     118          379 :     if (!CacheMemoryContext)
     119            0 :         CreateCacheMemoryContext();
     120              : 
     121              :     /* Watch for invalidation events. */
     122          379 :     CacheRegisterSyscacheCallback(ATTNUM,
     123              :                                   InvalidateAttoptCacheCallback,
     124              :                                   (Datum) 0);
     125          379 : }
     126              : 
     127              : /*
     128              :  * get_attribute_options
     129              :  *      Fetch attribute options for a specified table OID.
     130              :  */
     131              : AttributeOpts *
     132        49115 : get_attribute_options(Oid attrelid, int attnum)
     133              : {
     134              :     AttoptCacheKey key;
     135              :     AttoptCacheEntry *attopt;
     136              :     AttributeOpts *result;
     137              :     HeapTuple   tp;
     138              : 
     139              :     /* Find existing cache entry, if any. */
     140        49115 :     if (!AttoptCacheHash)
     141          379 :         InitializeAttoptCache();
     142        49115 :     memset(&key, 0, sizeof(key));   /* make sure any padding bits are unset */
     143        49115 :     key.attrelid = attrelid;
     144        49115 :     key.attnum = attnum;
     145              :     attopt =
     146        49115 :         (AttoptCacheEntry *) hash_search(AttoptCacheHash,
     147              :                                          &key,
     148              :                                          HASH_FIND,
     149              :                                          NULL);
     150              : 
     151              :     /* Not found in Attopt cache.  Construct new cache entry. */
     152        49115 :     if (!attopt)
     153              :     {
     154              :         AttributeOpts *opts;
     155              : 
     156        45932 :         tp = SearchSysCache2(ATTNUM,
     157              :                              ObjectIdGetDatum(attrelid),
     158              :                              Int16GetDatum(attnum));
     159              : 
     160              :         /*
     161              :          * If we don't find a valid HeapTuple, it must mean someone has
     162              :          * managed to request attribute details for a non-existent attribute.
     163              :          * We treat that case as if no options were specified.
     164              :          */
     165        45932 :         if (!HeapTupleIsValid(tp))
     166           63 :             opts = NULL;
     167              :         else
     168              :         {
     169              :             Datum       datum;
     170              :             bool        isNull;
     171              : 
     172        45869 :             datum = SysCacheGetAttr(ATTNUM,
     173              :                                     tp,
     174              :                                     Anum_pg_attribute_attoptions,
     175              :                                     &isNull);
     176        45869 :             if (isNull)
     177        45865 :                 opts = NULL;
     178              :             else
     179              :             {
     180            4 :                 bytea      *bytea_opts = attribute_reloptions(datum, false);
     181              : 
     182            4 :                 opts = MemoryContextAlloc(CacheMemoryContext,
     183              :                                           VARSIZE(bytea_opts));
     184            4 :                 memcpy(opts, bytea_opts, VARSIZE(bytea_opts));
     185              :             }
     186        45869 :             ReleaseSysCache(tp);
     187              :         }
     188              : 
     189              :         /*
     190              :          * It's important to create the actual cache entry only after reading
     191              :          * pg_attribute, since the read could cause a cache flush.
     192              :          */
     193        45932 :         attopt = (AttoptCacheEntry *) hash_search(AttoptCacheHash,
     194              :                                                   &key,
     195              :                                                   HASH_ENTER,
     196              :                                                   NULL);
     197        45932 :         attopt->opts = opts;
     198              :     }
     199              : 
     200              :     /* Return results in caller's memory context. */
     201        49115 :     if (attopt->opts == NULL)
     202        49111 :         return NULL;
     203            4 :     result = palloc(VARSIZE(attopt->opts));
     204            4 :     memcpy(result, attopt->opts, VARSIZE(attopt->opts));
     205            4 :     return result;
     206              : }
        

Generated by: LCOV version 2.0-1