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-2016, 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
26
27 /* Hash table for informations about each attribute's options */
28 static HTAB *AttoptCacheHash = NULL;
29
30 /* attrelid and attnum form the lookup key, and must appear first */
31 typedef struct
32 {
33 Oid attrelid;
34 int attnum;
35 } AttoptCacheKey;
36
37 typedef struct
38 {
39 AttoptCacheKey key; /* lookup key - must be first */
40 AttributeOpts *opts; /* options, or NULL if none */
41 } AttoptCacheEntry;
42
43
44 /*
45 * InvalidateAttoptCacheCallback
46 * Flush all cache entries when pg_attribute is updated.
47 *
48 * When pg_attribute is updated, we must flush the cache entry at least
49 * for that attribute. Currently, we just flush them all. Since attribute
50 * options are not currently used in performance-critical paths (such as
51 * query execution), this seems OK.
52 */
53 static void
InvalidateAttoptCacheCallback(Datum arg,int cacheid,uint32 hashvalue)54 InvalidateAttoptCacheCallback(Datum arg, int cacheid, uint32 hashvalue)
55 {
56 HASH_SEQ_STATUS status;
57 AttoptCacheEntry *attopt;
58
59 hash_seq_init(&status, AttoptCacheHash);
60 while ((attopt = (AttoptCacheEntry *) hash_seq_search(&status)) != NULL)
61 {
62 if (attopt->opts)
63 pfree(attopt->opts);
64 if (hash_search(AttoptCacheHash,
65 (void *) &attopt->key,
66 HASH_REMOVE,
67 NULL) == NULL)
68 elog(ERROR, "hash table corrupted");
69 }
70 }
71
72 /*
73 * InitializeAttoptCache
74 * Initialize the tablespace cache.
75 */
76 static void
InitializeAttoptCache(void)77 InitializeAttoptCache(void)
78 {
79 HASHCTL ctl;
80
81 /* Initialize the hash table. */
82 MemSet(&ctl, 0, sizeof(ctl));
83 ctl.keysize = sizeof(AttoptCacheKey);
84 ctl.entrysize = sizeof(AttoptCacheEntry);
85 AttoptCacheHash =
86 hash_create("Attopt cache", 256, &ctl,
87 HASH_ELEM | HASH_BLOBS);
88
89 /* Make sure we've initialized CacheMemoryContext. */
90 if (!CacheMemoryContext)
91 CreateCacheMemoryContext();
92
93 /* Watch for invalidation events. */
94 CacheRegisterSyscacheCallback(ATTNUM,
95 InvalidateAttoptCacheCallback,
96 (Datum) 0);
97 }
98
99 /*
100 * get_attribute_options
101 * Fetch attribute options for a specified table OID.
102 */
103 AttributeOpts *
get_attribute_options(Oid attrelid,int attnum)104 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 if (!AttoptCacheHash)
113 InitializeAttoptCache();
114 memset(&key, 0, sizeof(key)); /* make sure any padding bits are
115 * unset */
116 key.attrelid = attrelid;
117 key.attnum = attnum;
118 attopt =
119 (AttoptCacheEntry *) hash_search(AttoptCacheHash,
120 (void *) &key,
121 HASH_FIND,
122 NULL);
123
124 /* Not found in Attopt cache. Construct new cache entry. */
125 if (!attopt)
126 {
127 AttributeOpts *opts;
128
129 tp = SearchSysCache2(ATTNUM,
130 ObjectIdGetDatum(attrelid),
131 Int16GetDatum(attnum));
132
133 /*
134 * If we don't find a valid HeapTuple, it must mean someone has
135 * managed to request attribute details for a non-existent attribute.
136 * We treat that case as if no options were specified.
137 */
138 if (!HeapTupleIsValid(tp))
139 opts = NULL;
140 else
141 {
142 Datum datum;
143 bool isNull;
144
145 datum = SysCacheGetAttr(ATTNUM,
146 tp,
147 Anum_pg_attribute_attoptions,
148 &isNull);
149 if (isNull)
150 opts = NULL;
151 else
152 {
153 bytea *bytea_opts = attribute_reloptions(datum, false);
154
155 opts = MemoryContextAlloc(CacheMemoryContext,
156 VARSIZE(bytea_opts));
157 memcpy(opts, bytea_opts, VARSIZE(bytea_opts));
158 }
159 ReleaseSysCache(tp);
160 }
161
162 /*
163 * It's important to create the actual cache entry only after reading
164 * pg_attribute, since the read could cause a cache flush.
165 */
166 attopt = (AttoptCacheEntry *) hash_search(AttoptCacheHash,
167 (void *) &key,
168 HASH_ENTER,
169 NULL);
170 attopt->opts = opts;
171 }
172
173 /* Return results in caller's memory context. */
174 if (attopt->opts == NULL)
175 return NULL;
176 result = palloc(VARSIZE(attopt->opts));
177 memcpy(result, attopt->opts, VARSIZE(attopt->opts));
178 return result;
179 }
180