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 : }
|