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-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
4625 tgl@sss.pgh.pa.us 55 :CBC 451472 : InvalidateAttoptCacheCallback(Datum arg, int cacheid, uint32 hashvalue)
56 : : {
57 : : HASH_SEQ_STATUS status;
58 : : AttoptCacheEntry *attopt;
59 : :
5196 rhaas@postgresql.org 60 : 451472 : hash_seq_init(&status, AttoptCacheHash);
61 [ + + ]: 457760 : while ((attopt = (AttoptCacheEntry *) hash_seq_search(&status)) != NULL)
62 : : {
63 [ + + ]: 6288 : if (attopt->opts)
64 : 3 : pfree(attopt->opts);
65 [ - + ]: 6288 : if (hash_search(AttoptCacheHash,
433 peter@eisentraut.org 66 : 6288 : &attopt->key,
67 : : HASH_REMOVE,
68 : : NULL) == NULL)
5196 rhaas@postgresql.org 69 [ # # ]:UBC 0 : elog(ERROR, "hash table corrupted");
70 : : }
5196 rhaas@postgresql.org 71 :CBC 451472 : }
72 : :
73 : : /*
74 : : * InitializeAttoptCache
75 : : * Initialize the attribute options cache.
76 : : */
77 : : static void
78 : 239 : InitializeAttoptCache(void)
79 : : {
80 : : HASHCTL ctl;
81 : :
82 : : /* Initialize the hash table. */
83 : 239 : ctl.keysize = sizeof(AttoptCacheKey);
84 : 239 : ctl.entrysize = sizeof(AttoptCacheEntry);
85 : 239 : AttoptCacheHash =
86 : 239 : hash_create("Attopt cache", 256, &ctl,
87 : : HASH_ELEM | HASH_BLOBS);
88 : :
89 : : /* Make sure we've initialized CacheMemoryContext. */
90 [ - + ]: 239 : if (!CacheMemoryContext)
5196 rhaas@postgresql.org 91 :UBC 0 : CreateCacheMemoryContext();
92 : :
93 : : /* Watch for invalidation events. */
5196 rhaas@postgresql.org 94 :CBC 239 : CacheRegisterSyscacheCallback(ATTNUM,
95 : : InvalidateAttoptCacheCallback,
96 : : (Datum) 0);
97 : 239 : }
98 : :
99 : : /*
100 : : * get_attribute_options
101 : : * Fetch attribute options for a specified table OID.
102 : : */
103 : : AttributeOpts *
104 : 32919 : 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 [ + + ]: 32919 : if (!AttoptCacheHash)
113 : 239 : InitializeAttoptCache();
2489 tgl@sss.pgh.pa.us 114 : 32919 : memset(&key, 0, sizeof(key)); /* make sure any padding bits are unset */
5196 rhaas@postgresql.org 115 : 32919 : key.attrelid = attrelid;
116 : 32919 : key.attnum = attnum;
117 : : attopt =
118 : 32919 : (AttoptCacheEntry *) hash_search(AttoptCacheHash,
119 : : &key,
120 : : HASH_FIND,
121 : : NULL);
122 : :
123 : : /* Not found in Attopt cache. Construct new cache entry. */
124 [ + + ]: 32919 : if (!attopt)
125 : : {
126 : : AttributeOpts *opts;
127 : :
5173 128 : 32248 : 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 : : */
5196 137 [ + + ]: 32248 : if (!HeapTupleIsValid(tp))
138 : 50 : opts = NULL;
139 : : else
140 : : {
141 : : Datum datum;
142 : : bool isNull;
143 : :
144 : 32198 : datum = SysCacheGetAttr(ATTNUM,
145 : : tp,
146 : : Anum_pg_attribute_attoptions,
147 : : &isNull);
148 [ + + ]: 32198 : if (isNull)
149 : 32195 : opts = NULL;
150 : : else
151 : : {
5161 bruce@momjian.us 152 : 3 : bytea *bytea_opts = attribute_reloptions(datum, false);
153 : :
5196 rhaas@postgresql.org 154 : 3 : opts = MemoryContextAlloc(CacheMemoryContext,
155 : 3 : VARSIZE(bytea_opts));
156 : 3 : memcpy(opts, bytea_opts, VARSIZE(bytea_opts));
157 : : }
158 : 32198 : 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 : 32248 : attopt = (AttoptCacheEntry *) hash_search(AttoptCacheHash,
166 : : &key,
167 : : HASH_ENTER,
168 : : NULL);
169 : 32248 : attopt->opts = opts;
170 : : }
171 : :
172 : : /* Return results in caller's memory context. */
173 [ + + ]: 32919 : if (attopt->opts == NULL)
174 : 32916 : return NULL;
175 : 3 : result = palloc(VARSIZE(attopt->opts));
176 : 3 : memcpy(result, attopt->opts, VARSIZE(attopt->opts));
177 : 3 : return result;
178 : : }
|