1/*-------------------------------------------------------------------------
2 *
3 * spccache.c
4 * Tablespace cache management.
5 *
6 * We cache the parsed version of spcoptions for each tablespace to avoid
7 * needing to reparse on every lookup. Right now, there doesn't appear to
8 * be a measurable performance gain from doing this, but that might change
9 * in the future as we add more options.
10 *
11 * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
12 * Portions Copyright (c) 1994, Regents of the University of California
13 *
14 * IDENTIFICATION
15 * src/backend/utils/cache/spccache.c
16 *
17 *-------------------------------------------------------------------------
18 */
19#include "postgres.h"
20
21#include "access/reloptions.h"
22#include "catalog/pg_tablespace.h"
23#include "commands/tablespace.h"
24#include "miscadmin.h"
25#include "optimizer/optimizer.h"
26#include "storage/bufmgr.h"
27#include "utils/catcache.h"
28#include "utils/hsearch.h"
29#include "utils/inval.h"
30#include "utils/spccache.h"
31#include "utils/syscache.h"
32
33
34/* Hash table for information about each tablespace */
35static HTAB *TableSpaceCacheHash = NULL;
36
37typedef struct
38{
39 Oid oid; /* lookup key - must be first */
40 TableSpaceOpts *opts; /* options, or NULL if none */
41} TableSpaceCacheEntry;
42
43
44/*
45 * InvalidateTableSpaceCacheCallback
46 * Flush all cache entries when pg_tablespace is updated.
47 *
48 * When pg_tablespace is updated, we must flush the cache entry at least
49 * for that tablespace. Currently, we just flush them all. This is quick
50 * and easy and doesn't cost much, since there shouldn't be terribly many
51 * tablespaces, nor do we expect them to be frequently modified.
52 */
53static void
54InvalidateTableSpaceCacheCallback(Datum arg, int cacheid, uint32 hashvalue)
55{
56 HASH_SEQ_STATUS status;
57 TableSpaceCacheEntry *spc;
58
59 hash_seq_init(&status, TableSpaceCacheHash);
60 while ((spc = (TableSpaceCacheEntry *) hash_seq_search(&status)) != NULL)
61 {
62 if (spc->opts)
63 pfree(spc->opts);
64 if (hash_search(TableSpaceCacheHash,
65 (void *) &spc->oid,
66 HASH_REMOVE,
67 NULL) == NULL)
68 elog(ERROR, "hash table corrupted");
69 }
70}
71
72/*
73 * InitializeTableSpaceCache
74 * Initialize the tablespace cache.
75 */
76static void
77InitializeTableSpaceCache(void)
78{
79 HASHCTL ctl;
80
81 /* Initialize the hash table. */
82 MemSet(&ctl, 0, sizeof(ctl));
83 ctl.keysize = sizeof(Oid);
84 ctl.entrysize = sizeof(TableSpaceCacheEntry);
85 TableSpaceCacheHash =
86 hash_create("TableSpace cache", 16, &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(TABLESPACEOID,
95 InvalidateTableSpaceCacheCallback,
96 (Datum) 0);
97}
98
99/*
100 * get_tablespace
101 * Fetch TableSpaceCacheEntry structure for a specified table OID.
102 *
103 * Pointers returned by this function should not be stored, since a cache
104 * flush will invalidate them.
105 */
106static TableSpaceCacheEntry *
107get_tablespace(Oid spcid)
108{
109 TableSpaceCacheEntry *spc;
110 HeapTuple tp;
111 TableSpaceOpts *opts;
112
113 /*
114 * Since spcid is always from a pg_class tuple, InvalidOid implies the
115 * default.
116 */
117 if (spcid == InvalidOid)
118 spcid = MyDatabaseTableSpace;
119
120 /* Find existing cache entry, if any. */
121 if (!TableSpaceCacheHash)
122 InitializeTableSpaceCache();
123 spc = (TableSpaceCacheEntry *) hash_search(TableSpaceCacheHash,
124 (void *) &spcid,
125 HASH_FIND,
126 NULL);
127 if (spc)
128 return spc;
129
130 /*
131 * Not found in TableSpace cache. Check catcache. If we don't find a
132 * valid HeapTuple, it must mean someone has managed to request tablespace
133 * details for a non-existent tablespace. We'll just treat that case as
134 * if no options were specified.
135 */
136 tp = SearchSysCache1(TABLESPACEOID, ObjectIdGetDatum(spcid));
137 if (!HeapTupleIsValid(tp))
138 opts = NULL;
139 else
140 {
141 Datum datum;
142 bool isNull;
143
144 datum = SysCacheGetAttr(TABLESPACEOID,
145 tp,
146 Anum_pg_tablespace_spcoptions,
147 &isNull);
148 if (isNull)
149 opts = NULL;
150 else
151 {
152 bytea *bytea_opts = tablespace_reloptions(datum, false);
153
154 opts = MemoryContextAlloc(CacheMemoryContext, VARSIZE(bytea_opts));
155 memcpy(opts, bytea_opts, VARSIZE(bytea_opts));
156 }
157 ReleaseSysCache(tp);
158 }
159
160 /*
161 * Now create the cache entry. It's important to do this only after
162 * reading the pg_tablespace entry, since doing so could cause a cache
163 * flush.
164 */
165 spc = (TableSpaceCacheEntry *) hash_search(TableSpaceCacheHash,
166 (void *) &spcid,
167 HASH_ENTER,
168 NULL);
169 spc->opts = opts;
170 return spc;
171}
172
173/*
174 * get_tablespace_page_costs
175 * Return random and/or sequential page costs for a given tablespace.
176 *
177 * This value is not locked by the transaction, so this value may
178 * be changed while a SELECT that has used these values for planning
179 * is still executing.
180 */
181void
182get_tablespace_page_costs(Oid spcid,
183 double *spc_random_page_cost,
184 double *spc_seq_page_cost)
185{
186 TableSpaceCacheEntry *spc = get_tablespace(spcid);
187
188 Assert(spc != NULL);
189
190 if (spc_random_page_cost)
191 {
192 if (!spc->opts || spc->opts->random_page_cost < 0)
193 *spc_random_page_cost = random_page_cost;
194 else
195 *spc_random_page_cost = spc->opts->random_page_cost;
196 }
197
198 if (spc_seq_page_cost)
199 {
200 if (!spc->opts || spc->opts->seq_page_cost < 0)
201 *spc_seq_page_cost = seq_page_cost;
202 else
203 *spc_seq_page_cost = spc->opts->seq_page_cost;
204 }
205}
206
207/*
208 * get_tablespace_io_concurrency
209 *
210 * This value is not locked by the transaction, so this value may
211 * be changed while a SELECT that has used these values for planning
212 * is still executing.
213 */
214int
215get_tablespace_io_concurrency(Oid spcid)
216{
217 TableSpaceCacheEntry *spc = get_tablespace(spcid);
218
219 if (!spc->opts || spc->opts->effective_io_concurrency < 0)
220 return effective_io_concurrency;
221 else
222 return spc->opts->effective_io_concurrency;
223}
224