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-10 05:14:54 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       510737 : 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       510737 :     if (hashvalue == 0)
      65           34 :         hash_seq_init(&status, AttoptCacheHash);
      66              :     else
      67       510703 :         hash_seq_init_with_hash_value(&status, AttoptCacheHash, hashvalue);
      68              : 
      69       513156 :     while ((attopt = (AttoptCacheEntry *) hash_seq_search(&status)) != NULL)
      70              :     {
      71         2419 :         if (attopt->opts)
      72            3 :             pfree(attopt->opts);
      73         2419 :         if (hash_search(AttoptCacheHash,
      74         2419 :                         &attopt->key,
      75              :                         HASH_REMOVE,
      76              :                         NULL) == NULL)
      77            0 :             elog(ERROR, "hash table corrupted");
      78              :     }
      79       510737 : }
      80              : 
      81              : /*
      82              :  * Hash function compatible with two-arg system cache hash function.
      83              :  */
      84              : static uint32
      85        89400 : relatt_cache_syshash(const void *key, Size keysize)
      86              : {
      87        89400 :     const AttoptCacheKey *ckey = key;
      88              : 
      89              :     Assert(keysize == sizeof(*ckey));
      90        89400 :     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          304 : InitializeAttoptCache(void)
      99              : {
     100              :     HASHCTL     ctl;
     101              : 
     102              :     /* Initialize the hash table. */
     103          304 :     ctl.keysize = sizeof(AttoptCacheKey);
     104          304 :     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          304 :     ctl.hash = relatt_cache_syshash;
     112              : 
     113          304 :     AttoptCacheHash =
     114          304 :         hash_create("Attopt cache", 256, &ctl,
     115              :                     HASH_ELEM | HASH_FUNCTION);
     116              : 
     117              :     /* Make sure we've initialized CacheMemoryContext. */
     118          304 :     if (!CacheMemoryContext)
     119            0 :         CreateCacheMemoryContext();
     120              : 
     121              :     /* Watch for invalidation events. */
     122          304 :     CacheRegisterSyscacheCallback(ATTNUM,
     123              :                                   InvalidateAttoptCacheCallback,
     124              :                                   (Datum) 0);
     125          304 : }
     126              : 
     127              : /*
     128              :  * get_attribute_options
     129              :  *      Fetch attribute options for a specified table OID.
     130              :  */
     131              : AttributeOpts *
     132        44679 : 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        44679 :     if (!AttoptCacheHash)
     141          304 :         InitializeAttoptCache();
     142        44679 :     memset(&key, 0, sizeof(key));   /* make sure any padding bits are unset */
     143        44679 :     key.attrelid = attrelid;
     144        44679 :     key.attnum = attnum;
     145              :     attopt =
     146        44679 :         (AttoptCacheEntry *) hash_search(AttoptCacheHash,
     147              :                                          &key,
     148              :                                          HASH_FIND,
     149              :                                          NULL);
     150              : 
     151              :     /* Not found in Attopt cache.  Construct new cache entry. */
     152        44679 :     if (!attopt)
     153              :     {
     154              :         AttributeOpts *opts;
     155              : 
     156        42302 :         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        42302 :         if (!HeapTupleIsValid(tp))
     166           48 :             opts = NULL;
     167              :         else
     168              :         {
     169              :             Datum       datum;
     170              :             bool        isNull;
     171              : 
     172        42254 :             datum = SysCacheGetAttr(ATTNUM,
     173              :                                     tp,
     174              :                                     Anum_pg_attribute_attoptions,
     175              :                                     &isNull);
     176        42254 :             if (isNull)
     177        42251 :                 opts = NULL;
     178              :             else
     179              :             {
     180            3 :                 bytea      *bytea_opts = attribute_reloptions(datum, false);
     181              : 
     182            3 :                 opts = MemoryContextAlloc(CacheMemoryContext,
     183              :                                           VARSIZE(bytea_opts));
     184            3 :                 memcpy(opts, bytea_opts, VARSIZE(bytea_opts));
     185              :             }
     186        42254 :             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        42302 :         attopt = (AttoptCacheEntry *) hash_search(AttoptCacheHash,
     194              :                                                   &key,
     195              :                                                   HASH_ENTER,
     196              :                                                   NULL);
     197        42302 :         attopt->opts = opts;
     198              :     }
     199              : 
     200              :     /* Return results in caller's memory context. */
     201        44679 :     if (attopt->opts == NULL)
     202        44676 :         return NULL;
     203            3 :     result = palloc(VARSIZE(attopt->opts));
     204            3 :     memcpy(result, attopt->opts, VARSIZE(attopt->opts));
     205            3 :     return result;
     206              : }
        

Generated by: LCOV version 2.0-1