Age Owner Branch data TLA 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
25 michael@paquier.xyz 53 :GNC 531483 : 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 : : */
585 akorotkov@postgresql 64 [ + + ]:CBC 531483 : if (hashvalue == 0)
65 : 31 : hash_seq_init(&status, AttoptCacheHash);
66 : : else
67 : 531452 : hash_seq_init_with_hash_value(&status, AttoptCacheHash, hashvalue);
68 : :
5896 rhaas@postgresql.org 69 [ + + ]: 533857 : while ((attopt = (AttoptCacheEntry *) hash_seq_search(&status)) != NULL)
70 : : {
71 [ + + ]: 2374 : if (attopt->opts)
72 : 3 : pfree(attopt->opts);
73 [ - + ]: 2374 : if (hash_search(AttoptCacheHash,
1133 peter@eisentraut.org 74 : 2374 : &attopt->key,
75 : : HASH_REMOVE,
76 : : NULL) == NULL)
5896 rhaas@postgresql.org 77 [ # # ]:UBC 0 : elog(ERROR, "hash table corrupted");
78 : : }
5896 rhaas@postgresql.org 79 :CBC 531483 : }
80 : :
81 : : /*
82 : : * Hash function compatible with two-arg system cache hash function.
83 : : */
84 : : static uint32
585 akorotkov@postgresql 85 : 90548 : relatt_cache_syshash(const void *key, Size keysize)
86 : : {
87 : 90548 : const AttoptCacheKey *ckey = key;
88 : :
89 [ - + ]: 90548 : Assert(keysize == sizeof(*ckey));
219 peter@eisentraut.org 90 :GNC 90548 : return GetSysCacheHashValue2(ATTNUM, ObjectIdGetDatum(ckey->attrelid), Int32GetDatum(ckey->attnum));
91 : : }
92 : :
93 : : /*
94 : : * InitializeAttoptCache
95 : : * Initialize the attribute options cache.
96 : : */
97 : : static void
5896 rhaas@postgresql.org 98 :CBC 313 : InitializeAttoptCache(void)
99 : : {
100 : : HASHCTL ctl;
101 : :
102 : : /* Initialize the hash table. */
103 : 313 : ctl.keysize = sizeof(AttoptCacheKey);
104 : 313 : 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 : : */
585 akorotkov@postgresql 111 : 313 : ctl.hash = relatt_cache_syshash;
112 : :
5896 rhaas@postgresql.org 113 : 313 : AttoptCacheHash =
114 : 313 : hash_create("Attopt cache", 256, &ctl,
115 : : HASH_ELEM | HASH_FUNCTION);
116 : :
117 : : /* Make sure we've initialized CacheMemoryContext. */
118 [ - + ]: 313 : if (!CacheMemoryContext)
5896 rhaas@postgresql.org 119 :UBC 0 : CreateCacheMemoryContext();
120 : :
121 : : /* Watch for invalidation events. */
5896 rhaas@postgresql.org 122 :CBC 313 : CacheRegisterSyscacheCallback(ATTNUM,
123 : : InvalidateAttoptCacheCallback,
124 : : (Datum) 0);
125 : 313 : }
126 : :
127 : : /*
128 : : * get_attribute_options
129 : : * Fetch attribute options for a specified table OID.
130 : : */
131 : : AttributeOpts *
132 : 45287 : 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 [ + + ]: 45287 : if (!AttoptCacheHash)
141 : 313 : InitializeAttoptCache();
3189 tgl@sss.pgh.pa.us 142 : 45287 : memset(&key, 0, sizeof(key)); /* make sure any padding bits are unset */
5896 rhaas@postgresql.org 143 : 45287 : key.attrelid = attrelid;
144 : 45287 : key.attnum = attnum;
145 : : attopt =
146 : 45287 : (AttoptCacheEntry *) hash_search(AttoptCacheHash,
147 : : &key,
148 : : HASH_FIND,
149 : : NULL);
150 : :
151 : : /* Not found in Attopt cache. Construct new cache entry. */
152 [ + + ]: 45287 : if (!attopt)
153 : : {
154 : : AttributeOpts *opts;
155 : :
5873 156 : 42887 : 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 : : */
5896 165 [ + + ]: 42887 : if (!HeapTupleIsValid(tp))
166 : 48 : opts = NULL;
167 : : else
168 : : {
169 : : Datum datum;
170 : : bool isNull;
171 : :
172 : 42839 : datum = SysCacheGetAttr(ATTNUM,
173 : : tp,
174 : : Anum_pg_attribute_attoptions,
175 : : &isNull);
176 [ + + ]: 42839 : if (isNull)
177 : 42836 : opts = NULL;
178 : : else
179 : : {
5861 bruce@momjian.us 180 : 3 : bytea *bytea_opts = attribute_reloptions(datum, false);
181 : :
5896 rhaas@postgresql.org 182 : 3 : opts = MemoryContextAlloc(CacheMemoryContext,
5896 rhaas@postgresql.org 183 :ECB (3) : VARSIZE(bytea_opts));
5896 rhaas@postgresql.org 184 :CBC 3 : memcpy(opts, bytea_opts, VARSIZE(bytea_opts));
185 : : }
186 : 42839 : 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 : 42887 : attopt = (AttoptCacheEntry *) hash_search(AttoptCacheHash,
194 : : &key,
195 : : HASH_ENTER,
196 : : NULL);
197 : 42887 : attopt->opts = opts;
198 : : }
199 : :
200 : : /* Return results in caller's memory context. */
201 [ + + ]: 45287 : if (attopt->opts == NULL)
202 : 45284 : return NULL;
203 : 3 : result = palloc(VARSIZE(attopt->opts));
204 : 3 : memcpy(result, attopt->opts, VARSIZE(attopt->opts));
205 : 3 : return result;
206 : : }
|