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