LCOV - code coverage report
Current view: top level - src/backend/utils/cache - attoptcache.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 42 44 95.5 %
Date: 2024-02-28 23:11:08 Functions: 3 3 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 all cache 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.  Currently, we just flush them all.  Since attribute
      51             :  * options are not currently used in performance-critical paths (such as
      52             :  * query execution), this seems OK.
      53             :  */
      54             : static void
      55      714836 : InvalidateAttoptCacheCallback(Datum arg, int cacheid, uint32 hashvalue)
      56             : {
      57             :     HASH_SEQ_STATUS status;
      58             :     AttoptCacheEntry *attopt;
      59             : 
      60      714836 :     hash_seq_init(&status, AttoptCacheHash);
      61      724384 :     while ((attopt = (AttoptCacheEntry *) hash_seq_search(&status)) != NULL)
      62             :     {
      63        9548 :         if (attopt->opts)
      64           6 :             pfree(attopt->opts);
      65        9548 :         if (hash_search(AttoptCacheHash,
      66        9548 :                         &attopt->key,
      67             :                         HASH_REMOVE,
      68             :                         NULL) == NULL)
      69           0 :             elog(ERROR, "hash table corrupted");
      70             :     }
      71      714836 : }
      72             : 
      73             : /*
      74             :  * InitializeAttoptCache
      75             :  *      Initialize the attribute options cache.
      76             :  */
      77             : static void
      78         438 : InitializeAttoptCache(void)
      79             : {
      80             :     HASHCTL     ctl;
      81             : 
      82             :     /* Initialize the hash table. */
      83         438 :     ctl.keysize = sizeof(AttoptCacheKey);
      84         438 :     ctl.entrysize = sizeof(AttoptCacheEntry);
      85         438 :     AttoptCacheHash =
      86         438 :         hash_create("Attopt cache", 256, &ctl,
      87             :                     HASH_ELEM | HASH_BLOBS);
      88             : 
      89             :     /* Make sure we've initialized CacheMemoryContext. */
      90         438 :     if (!CacheMemoryContext)
      91           0 :         CreateCacheMemoryContext();
      92             : 
      93             :     /* Watch for invalidation events. */
      94         438 :     CacheRegisterSyscacheCallback(ATTNUM,
      95             :                                   InvalidateAttoptCacheCallback,
      96             :                                   (Datum) 0);
      97         438 : }
      98             : 
      99             : /*
     100             :  * get_attribute_options
     101             :  *      Fetch attribute options for a specified table OID.
     102             :  */
     103             : AttributeOpts *
     104       57624 : get_attribute_options(Oid attrelid, int attnum)
     105             : {
     106             :     AttoptCacheKey key;
     107             :     AttoptCacheEntry *attopt;
     108             :     AttributeOpts *result;
     109             :     HeapTuple   tp;
     110             : 
     111             :     /* Find existing cache entry, if any. */
     112       57624 :     if (!AttoptCacheHash)
     113         438 :         InitializeAttoptCache();
     114       57624 :     memset(&key, 0, sizeof(key));   /* make sure any padding bits are unset */
     115       57624 :     key.attrelid = attrelid;
     116       57624 :     key.attnum = attnum;
     117             :     attopt =
     118       57624 :         (AttoptCacheEntry *) hash_search(AttoptCacheHash,
     119             :                                          &key,
     120             :                                          HASH_FIND,
     121             :                                          NULL);
     122             : 
     123             :     /* Not found in Attopt cache.  Construct new cache entry. */
     124       57624 :     if (!attopt)
     125             :     {
     126             :         AttributeOpts *opts;
     127             : 
     128       56224 :         tp = SearchSysCache2(ATTNUM,
     129             :                              ObjectIdGetDatum(attrelid),
     130             :                              Int16GetDatum(attnum));
     131             : 
     132             :         /*
     133             :          * If we don't find a valid HeapTuple, it must mean someone has
     134             :          * managed to request attribute details for a non-existent attribute.
     135             :          * We treat that case as if no options were specified.
     136             :          */
     137       56224 :         if (!HeapTupleIsValid(tp))
     138          94 :             opts = NULL;
     139             :         else
     140             :         {
     141             :             Datum       datum;
     142             :             bool        isNull;
     143             : 
     144       56130 :             datum = SysCacheGetAttr(ATTNUM,
     145             :                                     tp,
     146             :                                     Anum_pg_attribute_attoptions,
     147             :                                     &isNull);
     148       56130 :             if (isNull)
     149       56124 :                 opts = NULL;
     150             :             else
     151             :             {
     152           6 :                 bytea      *bytea_opts = attribute_reloptions(datum, false);
     153             : 
     154           6 :                 opts = MemoryContextAlloc(CacheMemoryContext,
     155           6 :                                           VARSIZE(bytea_opts));
     156           6 :                 memcpy(opts, bytea_opts, VARSIZE(bytea_opts));
     157             :             }
     158       56130 :             ReleaseSysCache(tp);
     159             :         }
     160             : 
     161             :         /*
     162             :          * It's important to create the actual cache entry only after reading
     163             :          * pg_attribute, since the read could cause a cache flush.
     164             :          */
     165       56224 :         attopt = (AttoptCacheEntry *) hash_search(AttoptCacheHash,
     166             :                                                   &key,
     167             :                                                   HASH_ENTER,
     168             :                                                   NULL);
     169       56224 :         attopt->opts = opts;
     170             :     }
     171             : 
     172             :     /* Return results in caller's memory context. */
     173       57624 :     if (attopt->opts == NULL)
     174       57618 :         return NULL;
     175           6 :     result = palloc(VARSIZE(attopt->opts));
     176           6 :     memcpy(result, attopt->opts, VARSIZE(attopt->opts));
     177           6 :     return result;
     178             : }

Generated by: LCOV version 1.14