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 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 798018 : InvalidateAttoptCacheCallback(Datum arg, int cacheid, uint32 hashvalue)
54 : {
55 : HASH_SEQ_STATUS status;
56 : AttoptCacheEntry *attopt;
57 :
58 : /*
59 : * By convention, zero hash value is passed to the callback as a sign that
60 : * it's time to invalidate the whole cache. See sinval.c, inval.c and
61 : * InvalidateSystemCachesExtended().
62 : */
63 798018 : if (hashvalue == 0)
64 40 : hash_seq_init(&status, AttoptCacheHash);
65 : else
66 797978 : hash_seq_init_with_hash_value(&status, AttoptCacheHash, hashvalue);
67 :
68 801882 : while ((attopt = (AttoptCacheEntry *) hash_seq_search(&status)) != NULL)
69 : {
70 3864 : if (attopt->opts)
71 6 : pfree(attopt->opts);
72 3864 : if (hash_search(AttoptCacheHash,
73 3864 : &attopt->key,
74 : HASH_REMOVE,
75 : NULL) == NULL)
76 0 : elog(ERROR, "hash table corrupted");
77 : }
78 798018 : }
79 :
80 : /*
81 : * Hash function compatible with two-arg system cache hash function.
82 : */
83 : static uint32
84 139890 : relatt_cache_syshash(const void *key, Size keysize)
85 : {
86 139890 : const AttoptCacheKey *ckey = key;
87 :
88 : Assert(keysize == sizeof(*ckey));
89 139890 : return GetSysCacheHashValue2(ATTNUM, ckey->attrelid, ckey->attnum);
90 : }
91 :
92 : /*
93 : * InitializeAttoptCache
94 : * Initialize the attribute options cache.
95 : */
96 : static void
97 518 : InitializeAttoptCache(void)
98 : {
99 : HASHCTL ctl;
100 :
101 : /* Initialize the hash table. */
102 518 : ctl.keysize = sizeof(AttoptCacheKey);
103 518 : ctl.entrysize = sizeof(AttoptCacheEntry);
104 :
105 : /*
106 : * AttoptCacheEntry takes hash value from the system cache. For
107 : * AttoptCacheHash we use the same hash in order to speedup search by hash
108 : * value. This is used by hash_seq_init_with_hash_value().
109 : */
110 518 : ctl.hash = relatt_cache_syshash;
111 :
112 518 : AttoptCacheHash =
113 518 : hash_create("Attopt cache", 256, &ctl,
114 : HASH_ELEM | HASH_FUNCTION);
115 :
116 : /* Make sure we've initialized CacheMemoryContext. */
117 518 : if (!CacheMemoryContext)
118 0 : CreateCacheMemoryContext();
119 :
120 : /* Watch for invalidation events. */
121 518 : CacheRegisterSyscacheCallback(ATTNUM,
122 : InvalidateAttoptCacheCallback,
123 : (Datum) 0);
124 518 : }
125 :
126 : /*
127 : * get_attribute_options
128 : * Fetch attribute options for a specified table OID.
129 : */
130 : AttributeOpts *
131 70274 : get_attribute_options(Oid attrelid, int attnum)
132 : {
133 : AttoptCacheKey key;
134 : AttoptCacheEntry *attopt;
135 : AttributeOpts *result;
136 : HeapTuple tp;
137 :
138 : /* Find existing cache entry, if any. */
139 70274 : if (!AttoptCacheHash)
140 518 : InitializeAttoptCache();
141 70274 : memset(&key, 0, sizeof(key)); /* make sure any padding bits are unset */
142 70274 : key.attrelid = attrelid;
143 70274 : key.attnum = attnum;
144 : attopt =
145 70274 : (AttoptCacheEntry *) hash_search(AttoptCacheHash,
146 : &key,
147 : HASH_FIND,
148 : NULL);
149 :
150 : /* Not found in Attopt cache. Construct new cache entry. */
151 70274 : if (!attopt)
152 : {
153 : AttributeOpts *opts;
154 :
155 65752 : tp = SearchSysCache2(ATTNUM,
156 : ObjectIdGetDatum(attrelid),
157 : Int16GetDatum(attnum));
158 :
159 : /*
160 : * If we don't find a valid HeapTuple, it must mean someone has
161 : * managed to request attribute details for a non-existent attribute.
162 : * We treat that case as if no options were specified.
163 : */
164 65752 : if (!HeapTupleIsValid(tp))
165 54 : opts = NULL;
166 : else
167 : {
168 : Datum datum;
169 : bool isNull;
170 :
171 65698 : datum = SysCacheGetAttr(ATTNUM,
172 : tp,
173 : Anum_pg_attribute_attoptions,
174 : &isNull);
175 65698 : if (isNull)
176 65692 : opts = NULL;
177 : else
178 : {
179 6 : bytea *bytea_opts = attribute_reloptions(datum, false);
180 :
181 6 : opts = MemoryContextAlloc(CacheMemoryContext,
182 6 : VARSIZE(bytea_opts));
183 6 : memcpy(opts, bytea_opts, VARSIZE(bytea_opts));
184 : }
185 65698 : ReleaseSysCache(tp);
186 : }
187 :
188 : /*
189 : * It's important to create the actual cache entry only after reading
190 : * pg_attribute, since the read could cause a cache flush.
191 : */
192 65752 : attopt = (AttoptCacheEntry *) hash_search(AttoptCacheHash,
193 : &key,
194 : HASH_ENTER,
195 : NULL);
196 65752 : attopt->opts = opts;
197 : }
198 :
199 : /* Return results in caller's memory context. */
200 70274 : if (attopt->opts == NULL)
201 70268 : return NULL;
202 6 : result = palloc(VARSIZE(attopt->opts));
203 6 : memcpy(result, attopt->opts, VARSIZE(attopt->opts));
204 6 : return result;
205 : }
|