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